diff --git a/.eslintrc b/.eslintrc old mode 100755 new mode 100644 index 55b51f5793..46ed2af494 --- a/.eslintrc +++ b/.eslintrc @@ -19,6 +19,17 @@ "jest": true }, "rules": { + "no-unused-vars": "off", + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "warn", + { + "vars": "all", + "varsIgnorePattern": "^_", + "args": "after-used", + "argsIgnorePattern": "^_" + } + ], "arrow-body-style": 0, "class-methods-use-this": 0, "consistent-return": "warn", @@ -27,6 +38,7 @@ "jsx-a11y/alt-text": 0, "lines-between-class-members": 0, "global-require": 0, + "react/function-component-definition": 0, "import/extensions": 0, "import/no-cycle": 0, "import/no-dynamic-require": 0, @@ -87,13 +99,12 @@ "react/static-property-placement": 0, "react/jsx-no-useless-fragment": 0, "prefer-regex-literals": 0, - "no-unused-vars": "warn", "react/no-unused-class-component-methods": "warn", "react/no-unstable-nested-components": "warn", "no-promise-executor-return": "warn", "default-param-last": "warn" }, - "plugins": ["@typescript-eslint", "import", "promise", "react", "jest"], + "plugins": ["@typescript-eslint", "import", "promise", "react", "jest", "unused-imports"], "globals": { "API": true, "CARDANO_WALLET_VERSION": true, diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index eb60c469db..eed54adfa7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -50,7 +50,6 @@ Open a thread on #daedalus-qa on Slack, mention `@daedalusqa` and `@daedalusteam - [ ] There are no missing translations (running `yarn manage:translations` produces no changes) - [ ] Text changes are proofread and approved (Jane Wild / Amy Reeve) - [ ] Japanese text changes are proofread and approved (Junko Oda) -- [ ] Storybook works and no stories are broken (`yarn storybook`) - [ ] In case of dependency changes `yarn.lock` file is updated ### Code Quality diff --git a/.gitignore b/.gitignore index 3cbcdbcf4e..19e642b9b8 100755 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,8 @@ yalc.lock # Typescript *.scss.d.ts +/source/**/*.js +/source/**/*.jsx +/source/**/*.js.map +!/source/main/webpack.config.js +!/source/renderer/webpack.config.js \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 169b64d262..b268a9a4d3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,7 +4,6 @@ # But not the following folders !source/ !features/ -!storybook/ !hardware-wallet-tests/ !tests/ diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100755 index 01bc875f91..0000000000 --- a/gulpfile.js +++ /dev/null @@ -1,67 +0,0 @@ -const gulp = require('gulp'); -const shell = require('gulp-shell'); - -gulp.task('prepare:themes:utils', () => - gulp - .src([ - 'source/renderer/app/themes/utils/checkCreateTheme.ts', - 'source/renderer/app/themes/utils/constants.ts', - 'source/renderer/app/themes/utils/createShades.ts', - 'source/renderer/app/themes/utils/createTheme.ts', - 'source/renderer/app/themes/utils/findUpdates.ts', - 'source/renderer/app/themes/utils/updateThemes.ts', - 'source/renderer/app/themes/utils/updateThemesCLI.ts', - 'source/renderer/app/themes/utils/writeThemeUpdate.ts', - ]) - .pipe(gulp.dest('dist/utils')) -); - -gulp.task('prepare:themes:daedalus', () => - gulp - .src([ - 'source/renderer/app/themes/daedalus/cardano.ts', - 'source/renderer/app/themes/daedalus/dark-blue.ts', - 'source/renderer/app/themes/daedalus/dark-cardano.ts', - 'source/renderer/app/themes/daedalus/flight-candidate.ts', - 'source/renderer/app/themes/daedalus/incentivized-testnet.ts', - 'source/renderer/app/themes/daedalus/index.ts', - 'source/renderer/app/themes/daedalus/light-blue.ts', - 'source/renderer/app/themes/daedalus/shelley-testnet.ts', - 'source/renderer/app/themes/daedalus/white.ts', - 'source/renderer/app/themes/daedalus/yellow.ts', - ]) - .pipe(gulp.dest('dist/daedalus')) -); - -gulp.task('prepare:themes:scripts', () => - gulp - .src([ - 'source/renderer/app/themes/scripts/check.ts', - 'source/renderer/app/themes/scripts/update.ts', - ]) - .pipe(gulp.dest('dist/scripts')) -); - -gulp.task( - 'prepare:themes', - gulp.series( - 'prepare:themes:utils', - 'prepare:themes:daedalus', - 'prepare:themes:scripts' - ) -); - -gulp.task('clean:dist', shell.task('rimraf ./dist')); - -gulp.task('build:themes', gulp.series('clean:dist', 'prepare:themes')); - -gulp.task( - 'test:e2e:nodemon', - shell.task( - 'nodemon --watch dist --watch tests --exec "yarn test:e2e --tags \'@e2e and @watch\'"' - ) -); - -gulp.task('e2e:watch', gulp.series('clean:dist', shell.task('yarn dev'))); - -gulp.task('test:e2e:watch', gulp.series('e2e:watch', 'test:e2e:nodemon')); diff --git a/hardware-wallet-tests/cardano-app-already-launched.js b/hardware-wallet-tests/cardano-app-already-launched.js new file mode 100644 index 0000000000..fe5d074e23 --- /dev/null +++ b/hardware-wallet-tests/cardano-app-already-launched.js @@ -0,0 +1,67 @@ +'use strict'; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.run = void 0; +const expect_1 = __importDefault(require('expect')); +const utils_1 = require('./utils'); +const run = () => { + expect_1.default.assertions(3); + (0, utils_1.createTestInstructions)([ + 'Plug Ledger Nano S to your computer', + 'Launch Cardano APP on Nano S/Nano X', + 'Run the test again with Cardano App opened', + 'Export the public key', + ]); + (0, utils_1.createAndRegisterHardwareWalletChannels)(); + const cardanoAppChannel = (0, utils_1.createCardanoAppChannel)(); + const publicKeyChannel = (0, utils_1.createGetPublicKeyChannel)(); + const hardwareWalletConnectionChannel = (0, + utils_1.createHardwareWalletConnectionChannel)(); + return new Promise((resolve) => { + hardwareWalletConnectionChannel.onReceive(async (params) => { + (0, expect_1.default)(params).toEqual({ + disconnected: expect_1.default.any(Boolean), + deviceType: expect_1.default.any(String), + deviceId: null, + deviceModel: expect_1.default.any(String), + deviceName: expect_1.default.any(String), + path: expect_1.default.any(String), + product: expect_1.default.any(String), + }); + const cardanoAppChannelReply = await cardanoAppChannel.request( + { path: params.path }, + utils_1.ipcRenderer, + utils_1.ipcRenderer + ); + (0, expect_1.default)(cardanoAppChannelReply).toEqual({ + minor: expect_1.default.any(Number), + major: expect_1.default.any(Number), + patch: expect_1.default.any(Number), + deviceId: expect_1.default.any(String), + }); + const extendedPublicKey = await publicKeyChannel.request( + { + path: "1852'/1815'/0'", + // Shelley 1852 ADA 1815 indicator for account '0' + isTrezor: false, + devicePath: params.path, + }, + utils_1.ipcRenderer, + utils_1.ipcRenderer + ); + (0, expect_1.default)(extendedPublicKey).toEqual({ + chainCodeHex: expect_1.default.any(String), + publicKeyHex: expect_1.default.any(String), + deviceId: expect_1.default.any(String), + }); + resolve(); + }); + (0, utils_1.initLedgerChannel)(); + }); +}; +exports.run = run; +//# sourceMappingURL=cardano-app-already-launched.js.map diff --git a/hardware-wallet-tests/cardano-app-already-launched.js.map b/hardware-wallet-tests/cardano-app-already-launched.js.map new file mode 100644 index 0000000000..4b346d2d9e --- /dev/null +++ b/hardware-wallet-tests/cardano-app-already-launched.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cardano-app-already-launched.js","sourceRoot":"","sources":["cardano-app-already-launched.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,mCAQiB;AAEV,MAAM,GAAG,GAAG,GAAG,EAAE;IACtB,gBAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAErB,IAAA,8BAAsB,EAAC;QACrB,qCAAqC;QACrC,qCAAqC;QACrC,4CAA4C;QAC5C,uBAAuB;KACxB,CAAC,CAAC;IAEH,IAAA,+CAAuC,GAAE,CAAC;IAE1C,MAAM,iBAAiB,GAAG,IAAA,+BAAuB,GAAE,CAAC;IACpD,MAAM,gBAAgB,GAAG,IAAA,iCAAyB,GAAE,CAAC;IACrD,MAAM,+BAA+B,GAAG,IAAA,6CAAqC,GAAE,CAAC;IAEhF,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,+BAA+B,CAAC,SAAS,CACvC,KAAK,EAAE,MAAwB,EAAE,EAAE;YACjC,IAAA,gBAAM,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,YAAY,EAAE,gBAAM,CAAC,GAAG,CAAC,OAAO,CAAC;gBACjC,UAAU,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC9B,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC/B,UAAU,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC9B,IAAI,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBACxB,OAAO,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;aAC5B,CAAC,CAAC;YAEH,MAAM,sBAAsB,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAC5D,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,EACrB,mBAAW,EACX,mBAAW,CACZ,CAAC;YAEF,IAAA,gBAAM,EAAC,sBAAsB,CAAC,CAAC,OAAO,CAAC;gBACrC,KAAK,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBACzB,KAAK,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBACzB,KAAK,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBACzB,QAAQ,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;YAEH,MAAM,iBAAiB,GAAG,MAAM,gBAAgB,CAAC,OAAO,CACtD;gBACE,IAAI,EAAE,gBAAgB;gBACtB,kDAAkD;gBAClD,QAAQ,EAAE,KAAK;gBACf,UAAU,EAAE,MAAM,CAAC,IAAI;aACxB,EACD,mBAAW,EACX,mBAAW,CACZ,CAAC;YAEF,IAAA,gBAAM,EAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;gBAChC,YAAY,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAChC,YAAY,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAChC,QAAQ,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;YAEH,OAAO,EAAE,CAAC;QACZ,CAAC,CACF,CAAC;QAEF,IAAA,yBAAiB,GAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAjEW,QAAA,GAAG,OAiEd"} \ No newline at end of file diff --git a/hardware-wallet-tests/cardano-app-not-started.js b/hardware-wallet-tests/cardano-app-not-started.js new file mode 100644 index 0000000000..439610a390 --- /dev/null +++ b/hardware-wallet-tests/cardano-app-not-started.js @@ -0,0 +1,64 @@ +'use strict'; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.run = void 0; +const expect_1 = __importDefault(require('expect')); +const utils_1 = require('./utils'); +const run = () => { + expect_1.default.assertions(3); + (0, utils_1.createTestInstructions)([ + 'Start test runner', + 'Plug Ledger Nano S to your computer', + 'Start Cardano APP on Nano S', + 'Nano S will prompt to export the public key', + 'Export the public key', + ]); + (0, utils_1.createAndRegisterHardwareWalletChannels)(); + const publicKeyChannel = (0, utils_1.createGetPublicKeyChannel)(); + const hardwareWalletConnectionChannel = (0, + utils_1.createHardwareWalletConnectionChannel)(); + return new Promise((resolve) => { + hardwareWalletConnectionChannel.onReceive(async (params) => { + (0, expect_1.default)(params).toEqual({ + disconnected: expect_1.default.any(Boolean), + deviceType: expect_1.default.any(String), + deviceId: null, + deviceModel: expect_1.default.any(String), + deviceName: expect_1.default.any(String), + path: expect_1.default.any(String), + product: expect_1.default.any(String), + }); + const cardanoAppChannelResponse = await (0, + utils_1.requestLaunchingCardanoAppOnLedger)(params.path); + (0, expect_1.default)(cardanoAppChannelResponse).toEqual({ + minor: expect_1.default.any(Number), + major: expect_1.default.any(Number), + patch: expect_1.default.any(Number), + deviceId: expect_1.default.any(String), + }); + const extendedPublicKey = await publicKeyChannel.request( + { + path: "1852'/1815'/0'", + // Shelley 1852 ADA 1815 indicator for account '0' + isTrezor: false, + devicePath: params.path, + }, + utils_1.ipcRenderer, + utils_1.ipcRenderer + ); + (0, expect_1.default)(extendedPublicKey).toEqual({ + chainCodeHex: expect_1.default.any(String), + publicKeyHex: expect_1.default.any(String), + deviceId: expect_1.default.any(String), + }); + resolve(); + }); + (0, utils_1.initLedgerChannel)(); + }); +}; +exports.run = run; +//# sourceMappingURL=cardano-app-not-started.js.map diff --git a/hardware-wallet-tests/cardano-app-not-started.js.map b/hardware-wallet-tests/cardano-app-not-started.js.map new file mode 100644 index 0000000000..06e0b5ef6a --- /dev/null +++ b/hardware-wallet-tests/cardano-app-not-started.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cardano-app-not-started.js","sourceRoot":"","sources":["cardano-app-not-started.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,mCAQiB;AAEV,MAAM,GAAG,GAAG,GAAG,EAAE;IACtB,gBAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAErB,IAAA,8BAAsB,EAAC;QACrB,mBAAmB;QACnB,qCAAqC;QACrC,6BAA6B;QAC7B,6CAA6C;QAC7C,uBAAuB;KACxB,CAAC,CAAC;IAEH,IAAA,+CAAuC,GAAE,CAAC;IAE1C,MAAM,gBAAgB,GAAG,IAAA,iCAAyB,GAAE,CAAC;IACrD,MAAM,+BAA+B,GAAG,IAAA,6CAAqC,GAAE,CAAC;IAEhF,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,+BAA+B,CAAC,SAAS,CACvC,KAAK,EAAE,MAAwB,EAAE,EAAE;YACjC,IAAA,gBAAM,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,YAAY,EAAE,gBAAM,CAAC,GAAG,CAAC,OAAO,CAAC;gBACjC,UAAU,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC9B,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC/B,UAAU,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC9B,IAAI,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBACxB,OAAO,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;aAC5B,CAAC,CAAC;YAEH,MAAM,yBAAyB,GAAG,MAAM,IAAA,0CAAkC,EACxE,MAAM,CAAC,IAAI,CACZ,CAAC;YAEF,IAAA,gBAAM,EAAC,yBAAyB,CAAC,CAAC,OAAO,CAAC;gBACxC,KAAK,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBACzB,KAAK,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBACzB,KAAK,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBACzB,QAAQ,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;YAEH,MAAM,iBAAiB,GAAG,MAAM,gBAAgB,CAAC,OAAO,CACtD;gBACE,IAAI,EAAE,gBAAgB;gBACtB,kDAAkD;gBAClD,QAAQ,EAAE,KAAK;gBACf,UAAU,EAAE,MAAM,CAAC,IAAI;aACxB,EACD,mBAAW,EACX,mBAAW,CACZ,CAAC;YAEF,IAAA,gBAAM,EAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;gBAChC,YAAY,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAChC,YAAY,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAChC,QAAQ,EAAE,gBAAM,CAAC,GAAG,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;YAEH,OAAO,EAAE,CAAC;QACZ,CAAC,CACF,CAAC;QAEF,IAAA,yBAAiB,GAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AA/DW,QAAA,GAAG,OA+Dd"} \ No newline at end of file diff --git a/hardware-wallet-tests/connect-multiple-hardware-wallets.js b/hardware-wallet-tests/connect-multiple-hardware-wallets.js new file mode 100644 index 0000000000..9d0c65b450 --- /dev/null +++ b/hardware-wallet-tests/connect-multiple-hardware-wallets.js @@ -0,0 +1,49 @@ +'use strict'; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.run = void 0; +const expect_1 = __importDefault(require('expect')); +const utils_1 = require('./utils'); +const run = () => { + expect_1.default.assertions(2); + (0, utils_1.createAndRegisterHardwareWalletChannels)(); + const hardwareWalletConnectionChannel = (0, + utils_1.createHardwareWalletConnectionChannel)(); + (0, utils_1.createTestInstructions)([ + 'Start test runner', + 'Plug Ledger Nano S to your computer', + 'Plug Ledger Nano S Plus to your computer', + 'Plug Ledger Nano X to your computer', + ]); + const getNextExpectedSequence = (0, utils_1.createSequentialResult)([ + { + disconnected: false, + deviceModel: 'nanoS', + }, + { + disconnected: false, + deviceModel: 'nanoSP', + }, + { + disconnected: false, + deviceModel: 'nanoX', + }, + ]); + return new Promise((resolve) => { + hardwareWalletConnectionChannel.onReceive(async (message) => { + const [expectedValue, isOver] = getNextExpectedSequence(); + (0, expect_1.default)(message).toEqual(expectedValue); + if (isOver) { + await (0, utils_1.waitForZombieMessages)(); + resolve(); + } + }); + (0, utils_1.initLedgerChannel)(); + }); +}; +exports.run = run; +//# sourceMappingURL=connect-multiple-hardware-wallets.js.map diff --git a/hardware-wallet-tests/connect-multiple-hardware-wallets.js.map b/hardware-wallet-tests/connect-multiple-hardware-wallets.js.map new file mode 100644 index 0000000000..830f4706b0 --- /dev/null +++ b/hardware-wallet-tests/connect-multiple-hardware-wallets.js.map @@ -0,0 +1 @@ +{"version":3,"file":"connect-multiple-hardware-wallets.js","sourceRoot":"","sources":["connect-multiple-hardware-wallets.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,mCAOiB;AAEV,MAAM,GAAG,GAAG,GAAG,EAAE;IACtB,gBAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAErB,IAAA,+CAAuC,GAAE,CAAC;IAE1C,MAAM,+BAA+B,GAAG,IAAA,6CAAqC,GAAE,CAAC;IAEhF,IAAA,8BAAsB,EAAC;QACrB,mBAAmB;QACnB,qCAAqC;QACrC,0CAA0C;QAC1C,qCAAqC;KACtC,CAAC,CAAC;IAEH,MAAM,uBAAuB,GAAG,IAAA,8BAAsB,EAAC;QACrD;YACE,YAAY,EAAE,KAAK;YACnB,WAAW,EAAE,OAAO;SACrB;QACD;YACE,YAAY,EAAE,KAAK;YACnB,WAAW,EAAE,QAAQ;SACtB;QACD;YACE,YAAY,EAAE,KAAK;YACnB,WAAW,EAAE,OAAO;SACrB;KACF,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,+BAA+B,CAAC,SAAS,CACvC,KAAK,EAAE,OAA8C,EAAE,EAAE;YACvD,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,uBAAuB,EAAE,CAAC;YAC1D,IAAA,gBAAM,EAAC,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAEvC,IAAI,MAAM,EAAE;gBACV,MAAM,IAAA,6BAAqB,GAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC;aACX;QACH,CAAC,CACF,CAAC;QAEF,IAAA,yBAAiB,GAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AA5CW,QAAA,GAAG,OA4Cd"} \ No newline at end of file diff --git a/hardware-wallet-tests/disconnect-multiple-hardware-wallets.js b/hardware-wallet-tests/disconnect-multiple-hardware-wallets.js new file mode 100644 index 0000000000..ffe4981824 --- /dev/null +++ b/hardware-wallet-tests/disconnect-multiple-hardware-wallets.js @@ -0,0 +1,63 @@ +'use strict'; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.run = void 0; +const expect_1 = __importDefault(require('expect')); +const utils_1 = require('./utils'); +const getNextExpectedSequence = (0, utils_1.createSequentialResult)([ + { + disconnected: false, + deviceModel: 'nanoS', + }, + { + disconnected: false, + deviceModel: 'nanoSP', + }, + { + disconnected: false, + deviceModel: 'nanoX', + }, + { + disconnected: true, + deviceModel: 'nanoS', + }, + { + disconnected: true, + deviceModel: 'nanoSP', + }, + { + disconnected: true, + deviceModel: 'nanoX', + }, +]); +const run = () => { + expect_1.default.assertions(4); + (0, utils_1.createAndRegisterHardwareWalletChannels)(); + const hardwareWalletConnectionChannel = (0, + utils_1.createHardwareWalletConnectionChannel)(); + (0, utils_1.createTestInstructions)([ + 'Connect Nano S', + 'Connect Nano S Plus', + 'Connect Nano X', + 'Disconnect Nano S', + 'Disconnect Nano S Plus', + 'Disconnect Nano X', + ]); + return new Promise((resolve) => { + hardwareWalletConnectionChannel.onReceive(async (params) => { + const [expectedValue, isOver] = getNextExpectedSequence(); + (0, expect_1.default)(params).toEqual(expectedValue); + if (isOver) { + await (0, utils_1.waitForZombieMessages)(); + return resolve(); + } + }); + (0, utils_1.initLedgerChannel)(); + }); +}; +exports.run = run; +//# sourceMappingURL=disconnect-multiple-hardware-wallets.js.map diff --git a/hardware-wallet-tests/disconnect-multiple-hardware-wallets.js.map b/hardware-wallet-tests/disconnect-multiple-hardware-wallets.js.map new file mode 100644 index 0000000000..db495deb72 --- /dev/null +++ b/hardware-wallet-tests/disconnect-multiple-hardware-wallets.js.map @@ -0,0 +1 @@ +{"version":3,"file":"disconnect-multiple-hardware-wallets.js","sourceRoot":"","sources":["disconnect-multiple-hardware-wallets.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,mCAOiB;AAEjB,MAAM,uBAAuB,GAAG,IAAA,8BAAsB,EAAC;IACrD;QACE,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,OAAO;KACrB;IACD;QACE,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,QAAQ;KACtB;IACD;QACE,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,OAAO;KACrB;IACD;QACE,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,OAAO;KACrB;IACD;QACE,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,QAAQ;KACtB;IACD;QACE,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,OAAO;KACrB;CACF,CAAC,CAAC;AAEI,MAAM,GAAG,GAAG,GAAG,EAAE;IACtB,gBAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAErB,IAAA,+CAAuC,GAAE,CAAC;IAE1C,MAAM,+BAA+B,GAAG,IAAA,6CAAqC,GAAE,CAAC;IAEhF,IAAA,8BAAsB,EAAC;QACrB,gBAAgB;QAChB,qBAAqB;QACrB,gBAAgB;QAChB,mBAAmB;QACnB,wBAAwB;QACxB,mBAAmB;KACpB,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,+BAA+B,CAAC,SAAS,CACvC,KAAK,EAAE,MAA6C,EAAE,EAAE;YACtD,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,uBAAuB,EAAE,CAAC;YAE1D,IAAA,gBAAM,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAEtC,IAAI,MAAM,EAAE;gBACV,MAAM,IAAA,6BAAqB,GAAE,CAAC;gBAC9B,OAAO,OAAO,EAAE,CAAC;aAClB;QACH,CAAC,CACF,CAAC;QAEF,IAAA,yBAAiB,GAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAhCW,QAAA,GAAG,OAgCd"} \ No newline at end of file diff --git a/hardware-wallet-tests/disconnect-single-hardware-wallet.js b/hardware-wallet-tests/disconnect-single-hardware-wallet.js new file mode 100644 index 0000000000..baca1ca340 --- /dev/null +++ b/hardware-wallet-tests/disconnect-single-hardware-wallet.js @@ -0,0 +1,41 @@ +'use strict'; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.run = void 0; +const expect_1 = __importDefault(require('expect')); +const utils_1 = require('./utils'); +const run = () => { + expect_1.default.assertions(3); + (0, utils_1.createTestInstructions)([ + 'Plug Ledger Nano S to your computer', + 'Disconnect Nano S', + ]); + (0, utils_1.createAndRegisterHardwareWalletChannels)(); + const hardwareWalletConnectionChannel = (0, + utils_1.createHardwareWalletConnectionChannel)(); + const getNextExpectedSequence = (0, utils_1.createSequentialResult)([ + { + disconnected: false, + }, + { + disconnected: true, + }, + ]); + return new Promise((resolve) => { + hardwareWalletConnectionChannel.onReceive(async (params) => { + const [expectedValue, isOver] = getNextExpectedSequence(); + (0, expect_1.default)(params).toEqual(expectedValue); + if (isOver) { + await (0, utils_1.waitForZombieMessages)(); + return resolve(); + } + }); + (0, utils_1.initLedgerChannel)(); + }); +}; +exports.run = run; +//# sourceMappingURL=disconnect-single-hardware-wallet.js.map diff --git a/hardware-wallet-tests/disconnect-single-hardware-wallet.js.map b/hardware-wallet-tests/disconnect-single-hardware-wallet.js.map new file mode 100644 index 0000000000..9db9497b0c --- /dev/null +++ b/hardware-wallet-tests/disconnect-single-hardware-wallet.js.map @@ -0,0 +1 @@ +{"version":3,"file":"disconnect-single-hardware-wallet.js","sourceRoot":"","sources":["disconnect-single-hardware-wallet.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,mCAOiB;AAEV,MAAM,GAAG,GAAG,GAAG,EAAE;IACtB,gBAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAErB,IAAA,8BAAsB,EAAC;QACrB,qCAAqC;QACrC,mBAAmB;KACpB,CAAC,CAAC;IAEH,IAAA,+CAAuC,GAAE,CAAC;IAE1C,MAAM,+BAA+B,GAAG,IAAA,6CAAqC,GAAE,CAAC;IAEhF,MAAM,uBAAuB,GAAG,IAAA,8BAAsB,EAAC;QACrD;YACE,YAAY,EAAE,KAAK;SACpB;QACD;YACE,YAAY,EAAE,IAAI;SACnB;KACF,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,+BAA+B,CAAC,SAAS,CACvC,KAAK,EAAE,MAAwB,EAAE,EAAE;YACjC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,uBAAuB,EAAE,CAAC;YAC1D,IAAA,gBAAM,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAEtC,IAAI,MAAM,EAAE;gBACV,MAAM,IAAA,6BAAqB,GAAE,CAAC;gBAC9B,OAAO,OAAO,EAAE,CAAC;aAClB;QACH,CAAC,CACF,CAAC;QAEF,IAAA,yBAAiB,GAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AApCW,QAAA,GAAG,OAoCd"} \ No newline at end of file diff --git a/hardware-wallet-tests/index.js b/hardware-wallet-tests/index.js new file mode 100644 index 0000000000..7eccebeb21 --- /dev/null +++ b/hardware-wallet-tests/index.js @@ -0,0 +1,70 @@ +'use strict'; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +Object.defineProperty(exports, '__esModule', { value: true }); +const prompts_1 = __importDefault(require('prompts')); +const disconnect_multiple_hardware_wallets_1 = require('./disconnect-multiple-hardware-wallets'); +const cardano_app_already_launched_1 = require('./cardano-app-already-launched'); +const cardano_app_not_started_1 = require('./cardano-app-not-started'); +const disconnect_single_hardware_wallet_1 = require('./disconnect-single-hardware-wallet'); +const connect_multiple_hardware_wallets_1 = require('./connect-multiple-hardware-wallets'); +const CARDANO_APP_ALREADY_LAUNCHED = 'CARDANO_APP_ALREADY_LAUNCHED'; +const CARDANO_APP_NOT_STARTED = 'CARDANO_APP_NOT_STARTED'; +const SINGLE_LEDGER_DISCONNECTED = 'SINGLE_LEDGER_DISCONNECTED'; +const MULTIPLE_HARDWARE_WALLETS = 'MULTIPLE_HARDWARE_WALLETS'; +const MULTIPLE_HARDWARE_WALLETS_REMOVED = 'MULTIPLE_HARDWARE_WALLETS_REMOVED'; +(async () => { + const { testType } = await (0, prompts_1.default)({ + type: 'select', + name: 'testType', + message: 'Ledger Hardware Wallets Channel', + choices: [ + { + title: 'export public key when Cardano APP is already launched', + value: CARDANO_APP_ALREADY_LAUNCHED, + }, + { + title: + 'export public key when Cardano APP is launched after running HW test script', + value: CARDANO_APP_NOT_STARTED, + }, + { + title: 'detect when ledger is disconnected from computer', + value: SINGLE_LEDGER_DISCONNECTED, + }, + { + title: 'connect both Nano S and Nano X', + value: MULTIPLE_HARDWARE_WALLETS, + }, + { + title: 'detect when multiple hardware wallets are removed', + value: MULTIPLE_HARDWARE_WALLETS_REMOVED, + }, + { title: 'exit', value: 'exit' }, + ], + }); + switch (testType) { + case CARDANO_APP_ALREADY_LAUNCHED: + await (0, cardano_app_already_launched_1.run)(); + break; + case CARDANO_APP_NOT_STARTED: + await (0, cardano_app_not_started_1.run)(); + break; + case SINGLE_LEDGER_DISCONNECTED: + await (0, disconnect_single_hardware_wallet_1.run)(); + break; + case MULTIPLE_HARDWARE_WALLETS: + await (0, connect_multiple_hardware_wallets_1.run)(); + break; + case MULTIPLE_HARDWARE_WALLETS_REMOVED: + await (0, disconnect_multiple_hardware_wallets_1.run)(); + break; + default: + break; + } + process.exit(0); +})(); +//# sourceMappingURL=index.js.map diff --git a/hardware-wallet-tests/index.js.map b/hardware-wallet-tests/index.js.map new file mode 100644 index 0000000000..6b7935b5f8 --- /dev/null +++ b/hardware-wallet-tests/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;AAAA,sDAA8B;AAE9B,iGAAqG;AACrG,iFAAqF;AACrF,uEAA4E;AAC5E,2FAA+F;AAC/F,2FAA+F;AAE/F,MAAM,4BAA4B,GAAG,8BAA8B,CAAC;AACpE,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AAC1D,MAAM,0BAA0B,GAAG,4BAA4B,CAAC;AAChE,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AAC9D,MAAM,iCAAiC,GAAG,mCAAmC,CAAC;AAE9E,CAAC,KAAK,IAAI,EAAE;IACV,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,iBAAO,EAAC;QACjC,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,iCAAiC;QAC1C,OAAO,EAAE;YACP;gBACE,KAAK,EAAE,wDAAwD;gBAC/D,KAAK,EAAE,4BAA4B;aACpC;YACD;gBACE,KAAK,EACH,6EAA6E;gBAC/E,KAAK,EAAE,uBAAuB;aAC/B;YACD;gBACE,KAAK,EAAE,kDAAkD;gBACzD,KAAK,EAAE,0BAA0B;aAClC;YACD;gBACE,KAAK,EAAE,gCAAgC;gBACvC,KAAK,EAAE,yBAAyB;aACjC;YACD;gBACE,KAAK,EAAE,mDAAmD;gBAC1D,KAAK,EAAE,iCAAiC;aACzC;YACD,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;SACjC;KACF,CAAC,CAAC;IAEH,QAAQ,QAAQ,EAAE;QAChB,KAAK,4BAA4B;YAC/B,MAAM,IAAA,kCAA4B,GAAE,CAAC;YACrC,MAAM;QAER,KAAK,uBAAuB;YAC1B,MAAM,IAAA,6BAAwB,GAAE,CAAC;YACjC,MAAM;QAER,KAAK,0BAA0B;YAC7B,MAAM,IAAA,uCAAiC,GAAE,CAAC;YAC1C,MAAM;QAER,KAAK,yBAAyB;YAC5B,MAAM,IAAA,uCAAiC,GAAE,CAAC;YAC1C,MAAM;QAER,KAAK,iCAAiC;YACpC,MAAM,IAAA,0CAAoC,GAAE,CAAC;YAC7C,MAAM;QAER;YACE,MAAM;KACT;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,EAAE,CAAC"} \ No newline at end of file diff --git a/hardware-wallet-tests/utils.js b/hardware-wallet-tests/utils.js new file mode 100644 index 0000000000..d84a6063e7 --- /dev/null +++ b/hardware-wallet-tests/utils.js @@ -0,0 +1,107 @@ +'use strict'; +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod }; + }; +var _a; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.waitForZombieMessages = exports.createTestInstructions = exports.log = exports.createSequentialResult = exports.requestLaunchingCardanoAppOnLedger = exports.createHardwareWalletConnectionChannel = exports.createGetPublicKeyChannel = exports.createCardanoAppChannel = exports.initLedgerChannel = exports.createAndRegisterHardwareWalletChannels = exports.MockIpcChannel = exports.ipcRenderer = exports.ipcMain = void 0; +const expect_1 = __importDefault(require('expect')); +const electron_mock_ipc_1 = __importDefault(require('electron-mock-ipc')); +const chalk_1 = __importDefault(require('chalk')); +const IpcChannel_1 = require('../source/common/ipc/lib/IpcChannel'); +const api_1 = require('../source/common/ipc/api'); +const createHardwareWalletIPCChannels_1 = require('../source/main/ipc/createHardwareWalletIPCChannels'); +const getHardwareWalletChannel_1 = require('../source/main/ipc/getHardwareWalletChannel'); +(_a = (0, electron_mock_ipc_1.default)()), + (exports.ipcMain = _a.ipcMain), + (exports.ipcRenderer = _a.ipcRenderer); +class MockIpcChannel extends IpcChannel_1.IpcChannel { + async send( + message, + sender = exports.ipcRenderer, + receiver = exports.ipcRenderer + ) { + return super.send(message, sender, receiver); + } + async request(message, sender = exports.ipcMain, receiver = exports.ipcMain) { + return super.request(message, sender, receiver); + } + onReceive(handler, receiver = exports.ipcMain) { + super.onReceive(handler, receiver); + } + onRequest(handler, receiver = exports.ipcMain) { + super.onRequest(handler, receiver); + } +} +exports.MockIpcChannel = MockIpcChannel; +const createAndRegisterHardwareWalletChannels = () => + // @ts-ignore Argument of type 'ipcRenderer' is not assignable to parameter of type 'BrowserWindow'. + (0, getHardwareWalletChannel_1.handleHardwareWalletRequests)( + exports.ipcRenderer, + (0, createHardwareWalletIPCChannels_1.createChannels)(MockIpcChannel) + ); +exports.createAndRegisterHardwareWalletChannels = createAndRegisterHardwareWalletChannels; +const initLedgerChannel = () => { + const initLedgerConnectChannel = new MockIpcChannel( + api_1.GET_INIT_LEDGER_CONNECT_CHANNEL + ); + initLedgerConnectChannel.request({}, exports.ipcRenderer, exports.ipcMain); +}; +exports.initLedgerChannel = initLedgerChannel; +const createCardanoAppChannel = () => + new MockIpcChannel(api_1.GET_CARDANO_ADA_APP_CHANNEL); +exports.createCardanoAppChannel = createCardanoAppChannel; +const createGetPublicKeyChannel = () => + new MockIpcChannel(api_1.GET_EXTENDED_PUBLIC_KEY_CHANNEL); +exports.createGetPublicKeyChannel = createGetPublicKeyChannel; +const createHardwareWalletConnectionChannel = () => + new MockIpcChannel(api_1.GET_HARDWARE_WALLET_CONNECTION_CHANNEL); +exports.createHardwareWalletConnectionChannel = createHardwareWalletConnectionChannel; +const requestLaunchingCardanoAppOnLedger = (deviceId) => + new Promise((resolve, reject) => { + const cardanoAppChannel = (0, exports.createCardanoAppChannel)(); + const run = async () => { + try { + const cardanoAppChannelResponse = await cardanoAppChannel.request( + { path: deviceId }, + exports.ipcRenderer, + exports.ipcRenderer + ); + return resolve(cardanoAppChannelResponse); + } catch (err) { + if (err.code === api_1.DEVICE_NOT_CONNECTED) { + return reject(err); + } + setTimeout(run, 1000); + } + }; + run(); + }); +exports.requestLaunchingCardanoAppOnLedger = requestLaunchingCardanoAppOnLedger; +const createSequentialResult = (sequence) => { + const common = { + disconnected: expect_1.default.any(Boolean), + deviceType: expect_1.default.any(String), + deviceId: null, + deviceModel: expect_1.default.any(String), + deviceName: expect_1.default.any(String), + path: expect_1.default.any(String), + product: expect_1.default.any(String), + }; + const result = sequence.map((s) => ({ ...common, ...s })); + return () => [result.shift(), result.length === 0]; +}; +exports.createSequentialResult = createSequentialResult; +const log = (message) => + console.log(chalk_1.default.whiteBright.bgBlackBright.bold(message)); // eslint-disable-line no-console +exports.log = log; +const createTestInstructions = (messages) => { + messages.forEach((m, i) => (0, exports.log)(`${i + 1} - ${m}`)); +}; +exports.createTestInstructions = createTestInstructions; +const waitForZombieMessages = () => + new Promise((resolve) => setTimeout(resolve, 1000)); +exports.waitForZombieMessages = waitForZombieMessages; +//# sourceMappingURL=utils.js.map diff --git a/hardware-wallet-tests/utils.js.map b/hardware-wallet-tests/utils.js.map new file mode 100644 index 0000000000..e1d5ec869e --- /dev/null +++ b/hardware-wallet-tests/utils.js.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/installers/common/MacInstaller.hs b/installers/common/MacInstaller.hs index 47ec12388b..0ded55e2ec 100644 --- a/installers/common/MacInstaller.hs +++ b/installers/common/MacInstaller.hs @@ -216,6 +216,8 @@ buildElectronApp darwinConfig@DarwinConfig{dcAppName, dcAppNameApp} installerCon externalYarn :: [FilePath] externalYarn = [ "@babel" + , "@emurgo/cardano-serialization-lib-nodejs" + , "@fivebinaries/coin-selection" , "@noble" , "@protobufjs" , "@trezor" @@ -239,7 +241,6 @@ buildElectronApp darwinConfig@DarwinConfig{dcAppName, dcAppNameApp} installerCon , "bs58" , "bs58check" , "buffer" - , "bytebuffer" , "call-bind" , "cashaddrjs" , "clone" @@ -309,18 +310,27 @@ buildElectronApp darwinConfig@DarwinConfig{dcAppName, dcAppNameApp} installerCon , "tiny-secp256k1" , "tslib" , "typeforce" + , "ua-parser-js" , "unicode-properties" , "unicode-trie" , "usb-detection" , "util-deprecate" , "varuint-bitcoin" , "wif" + , "whatwg-url" + , "tr46" + , "usb" + , "node-gyp-build" + , "@sinclair" + , "ts-mixer" + , "core-js" ] mapM_ (\lib -> do cptree ("../node_modules" lib) ((fromText pathtoapp) "Contents/Resources/app/node_modules" lib) ) externalYarn mktree ((fromText pathtoapp) "Contents/Resources/app/build") - mapM_ (\(srcdir, name) -> cp ("../node_modules" srcdir name) ((fromText pathtoapp) "Contents/Resources/app/build" name)) [ ("usb/build/Release","usb_bindings.node"), ("node-hid/build/Release", "HID.node"), ("usb-detection/build/Release", "detection.node") ] + mapM_ (\(srcdir, name) -> cp ("../node_modules" srcdir name) ((fromText pathtoapp) "Contents/Resources/app/build" name)) [ ("usb/build/Release","usb_bindings.node"), ("node-hid/build/Release", "HID.node"), ("usb-detection/build/Release", "detection.node"), ( "utf-8-validate/build/Release", "validation.node") ] + rewritePackageJson (T.unpack $ pathtoapp <> "/Contents/Resources/app/package.json") (spacedName installerConfig) pure $ fromString $ T.unpack $ pathtoapp diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index 92ac5b2008..0000000000 --- a/netlify.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build] - environment = { YARN_VERSION = "1.22.4", NODE_VERSION = "14.19.1" } - command = "yarn && yarn storybook:build" diff --git a/nix/devshells.nix b/nix/devshells.nix index fd95f026a1..6927a3a1a8 100644 --- a/nix/devshells.nix +++ b/nix/devshells.nix @@ -122,8 +122,6 @@ let mkdir -p "$CARDANO_WALLET_TLS_PATH" regenerate-dev-certs >/dev/null - ${common.temporaryNodeModulesPatches} - echo echo 'Now, run ‘yarn dev’.' ''; diff --git a/nix/internal/any-darwin.nix b/nix/internal/any-darwin.nix index a9867e707c..48e0b2f6bb 100644 --- a/nix/internal/any-darwin.nix +++ b/nix/internal/any-darwin.nix @@ -13,7 +13,7 @@ let daedalus-bridge daedalus-installer launcherConfigs mock-token-metadata-server cardanoNodeVersion cardanoWalletVersion; - inherit (common) originalPackageJson electronVersion electronChromedriverVersion commonSources; + inherit (common) originalPackageJson electronVersion commonSources; archSuffix = if pkgs.system == "aarch64-darwin" then "arm64" else "x64"; packageVersion = originalPackageJson.version; @@ -33,10 +33,6 @@ in rec { mkdir -p ${cacheDir}/electron/${commonSources.electronCacheHash}/ ln -sf ${commonSources.electronShaSums} ${cacheDir}/electron/${commonSources.electronCacheHash}/SHASUMS256.txt ln -sf ${darwinSources.electron} ${cacheDir}/electron/${commonSources.electronCacheHash}/electron-v${electronVersion}-darwin-${archSuffix}.zip - - mkdir -p ${cacheDir}/electron/${commonSources.electronChromedriverCacheHash}/ - ln -sf ${commonSources.electronChromedriverShaSums} ${cacheDir}/electron/${commonSources.electronChromedriverCacheHash}/SHASUMS256.txt - ln -sf ${darwinSources.electronChromedriver} ${cacheDir}/electron/${commonSources.electronChromedriverCacheHash}/chromedriver-v${electronChromedriverVersion}-darwin-${archSuffix}.zip ''; # XXX: we don't use `autoSignDarwinBinariesHook` for ad-hoc signing, @@ -62,7 +58,7 @@ in rec { name = "daedalus-node_modules"; src = srcLockfiles; nativeBuildInputs = [ yarn nodejs ] - ++ (with pkgs; [ python3 pkgconfig jq darwin.cctools xcbuild ]); + ++ (with pkgs; [ python3 pkgconfig jq darwin.cctools xcbuild perl /* for bufferutil */ ]); buildInputs = (with pkgs.darwin; [ apple_sdk.frameworks.CoreServices apple_sdk.frameworks.AppKit @@ -80,6 +76,16 @@ in rec { patchShebangs . >/dev/null # a real lot of paths to patch, no need to litter logs + # This is building against Node.js, not Electron, but it still will fail, unless: + ourArch="${__replaceStrings ["aarch64"] ["arm64"] (__head (__split "-" targetSystem))}" + for f in \ + node_modules/usb/binding.gyp \ + node_modules/usb/libusb.gypi \ + node_modules/utf-8-validate/binding.gyp \ + ; do + sed -r 's,-arch (x86_64|arm64),-arch '"$ourArch"',g' -i "$f" + done + # And now, with correct shebangs, run the install scripts (we have to do that # semi-manually, because another `yarn install` will overwrite those shebangs…): find node_modules -type f -name 'package.json' | sort | xargs grep -F '"install":' | cut -d: -f1 | while IFS= read -r dependency ; do @@ -111,7 +117,7 @@ in rec { name = pname; src = srcWithoutNix; nativeBuildInputs = [ yarn nodejs daedalus-installer ] - ++ (with pkgs; [ python3 pkgconfig darwin.cctools xcbuild ]); + ++ (with pkgs; [ python3 pkgconfig darwin.cctools xcbuild perl /* for bufferutil */ ]); buildInputs = (with pkgs.darwin; [ apple_sdk.frameworks.CoreServices apple_sdk.frameworks.AppKit @@ -137,7 +143,8 @@ in rec { patchShebangs . sed -r 's#.*patchElectronRebuild.*#${common.patchElectronRebuild}/bin/*#' -i scripts/rebuild-native-modules.sh - ${common.temporaryNodeModulesPatches} + sed -r "s/^const usb =.*/const usb = require(require('path').join(process.env.DAEDALUS_INSTALL_DIRECTORY, 'usb_bindings.node'));/g" \ + -i node_modules/usb/dist/usb/bindings.js export DEVX_FIXME_DONT_YARN_INSTALL=1 ( @@ -295,19 +302,12 @@ in rec { darwinSources = { electron = pkgs.fetchurl { + name = "electron-v${electronVersion}-darwin-${archSuffix}.zip"; url = "https://github.com/electron/electron/releases/download/v${electronVersion}/electron-v${electronVersion}-darwin-${archSuffix}.zip"; hash = if archSuffix == "x64" - then "sha256-I/d/vecsrYMV59Nw2SnNzrVAj1UzSUJB/F3VA9itDNw=" - else "sha256-Up0HRemSeMZvYxyB7b7yKlrYhxMyNmAC7dNxtAmFCyQ="; - }; - - electronChromedriver = pkgs.fetchurl { - url = "https://github.com/electron/electron/releases/download/v${electronChromedriverVersion}/chromedriver-v${electronChromedriverVersion}-darwin-${archSuffix}.zip"; - hash = - if archSuffix == "x64" - then "sha256-avLZdXPkcZx5SirO2RVjxXN2oRfbUHs5gEymUwne3HI=" - else "sha256-jehYm1nMlHjQha7AFzMUvKZxfSbedghODhBUXk1qKB4="; + then "sha256-2iCH4N/5PbT7cTL2yIWUCwr4O7HuaSiZsC1GmBfJuPg=" + else "sha256-4UjzGbdqJE37GzpQA0TD1McRg7qwAhPLUQ9Anb4iops="; }; }; } diff --git a/nix/internal/common.nix b/nix/internal/common.nix index 7d3a840574..fdc8a36c01 100644 --- a/nix/internal/common.nix +++ b/nix/internal/common.nix @@ -138,7 +138,7 @@ rec { # }; nodejs = let - base = pkgs.nodejs-18_x; + base = pkgs.nodejs; in if !(pkgs.lib.hasInfix "-darwin" targetSystem) then base else base.overrideAttrs (drv: { # XXX: we don’t want `bypass-xcodebuild.diff` or `bypass-darwin-xcrun-node16.patch`, rather we supply # the pure `xcbuild` – without that, `blake2` doesn’t build, @@ -231,21 +231,6 @@ rec { # export DEBUG='node-gyp @electron/get:* electron-rebuild' ''; - # FIXME: this has to be done better… - temporaryNodeModulesPatches = '' - sed -r "s/'127\.0\.0\.1'/undefined/g" -i node_modules/cardano-launcher/dist/src/cardanoNode.js - - # Has to be idempotent: - if ! grep -qF "'-N'" node_modules/cardano-launcher/dist/src/cardanoWallet.js ; then - sed -r "s/'serve'/\0, '+RTS', '-N', '-RTS'/g" -i node_modules/cardano-launcher/dist/src/cardanoWallet.js - fi - - # Has to be idempotent: - if ! grep -qF "'-N'" node_modules/cardano-launcher/dist/src/cardanoNode.js ; then - sed -r "s/config.rtsOpts/(\0 || []).concat(['-N'])/g" -i node_modules/cardano-launcher/dist/src/cardanoNode.js - fi - ''; - electronVersion = originalPackageJson.dependencies.electron; versionInOfflineCache = safeName: @@ -253,14 +238,13 @@ rec { ls ${offlineCache} | grep -F ${pkgs.lib.escapeShellArg (safeName + "___" + safeName)} | grep -Po '\d+(\.\d+)*' | tr -d '\n' >$out '')); - electronChromedriverVersion = versionInOfflineCache "electron_chromedriver"; - commonSources = { - electronHeaders = pkgs.runCommandLocal "electron-headers" { + electronHeaders = pkgs.runCommandLocal "electron-headers-${electronVersion}" { # XXX: don’t use fetchzip, we need the raw .tar.gz in `patchElectronRebuild` below src = pkgs.fetchurl { + name = "node-v${electronVersion}-headers.tar.gz"; url = "https://electronjs.org/headers/v${electronVersion}/node-v${electronVersion}-headers.tar.gz"; - hash = "sha256-er08CKt3fwotSjYxqdzpm8Q0YjvD1PhfNBDZ3Jozsvk="; + hash = "sha256-/AqDBJIEk8zHqP6atq4lFGvXjzww/o9x7KA+WRW/0DE="; }; } '' tar -xf $src @@ -271,20 +255,11 @@ rec { electronShaSums = pkgs.fetchurl { name = "electronShaSums-${electronVersion}"; # cache invalidation url = "https://github.com/electron/electron/releases/download/v${electronVersion}/SHASUMS256.txt"; - hash = "sha256-75bNqt2c7u/fm0P2Ha6NvkbGThEifIHXl2x5UCdy4fM="; + hash = "sha256-Np56814pncRldaiuG5i1tHELzcrjW0J48V07czWD3vE="; }; electronCacheHash = builtins.hashString "sha256" "https://github.com/electron/electron/releases/download/v${electronVersion}"; - - electronChromedriverShaSums = pkgs.fetchurl { - name = "electronChromedriverShaSums-${electronChromedriverVersion}"; # cache invalidation - url = "https://github.com/electron/electron/releases/download/v${electronChromedriverVersion}/SHASUMS256.txt"; - hash = "sha256-nV0aT0nuzsVK5J37lEo0egXmRy/tpdF3jyrY3VBVvR8="; - }; - - electronChromedriverCacheHash = builtins.hashString "sha256" - "https://github.com/electron/electron/releases/download/v${electronChromedriverVersion}"; }; # We patch `node_modules/electron-rebuild` to force specific Node.js diff --git a/nix/internal/x86_64-linux.nix b/nix/internal/x86_64-linux.nix index 74c73c18be..e58d80eaf6 100644 --- a/nix/internal/x86_64-linux.nix +++ b/nix/internal/x86_64-linux.nix @@ -15,7 +15,7 @@ let in rec { inherit common; - inherit (common) nodejs yarn yarn2nix offlineCache srcLockfiles srcWithoutNix electronVersion electronChromedriverVersion originalPackageJson; + inherit (common) nodejs yarn yarn2nix offlineCache srcLockfiles srcWithoutNix electronVersion originalPackageJson; package = genClusters (cluster: mkDaedalus { sandboxed = false; inherit cluster; }); @@ -36,10 +36,6 @@ in rec { mkdir -p ${cacheDir}/electron/${commonSources.electronCacheHash}/ ln -sf ${commonSources.electronShaSums} ${cacheDir}/electron/${commonSources.electronCacheHash}/SHASUMS256.txt ln -sf ${linuxSources.electron} ${cacheDir}/electron/${commonSources.electronCacheHash}/electron-v${electronVersion}-linux-x64.zip - - mkdir -p ${cacheDir}/electron/${commonSources.electronChromedriverCacheHash}/ - ln -sf ${commonSources.electronChromedriverShaSums} ${cacheDir}/electron/${commonSources.electronChromedriverCacheHash}/SHASUMS256.txt - ln -sf ${linuxSources.electronChromedriver} ${cacheDir}/electron/${commonSources.electronChromedriverCacheHash}/chromedriver-v${electronChromedriverVersion}-linux-x64.zip ''; node_modules = pkgs.stdenv.mkDerivation { @@ -117,8 +113,6 @@ in rec { sed -r 's#.*patchElectronRebuild.*#${common.patchElectronRebuild}/bin/*#' -i scripts/rebuild-native-modules.sh yarn build:electron - ${common.temporaryNodeModulesPatches} - yarn run package -- --name ${lib.escapeShellArg common.launcherConfigs.${cluster}.installerConfig.spacedName} ''; installPhase = '' @@ -143,7 +137,7 @@ in rec { ln -sv $out/share/daedalus/renderer/assets $out/share/fonts/daedalus mkdir -pv $out/share/daedalus/node_modules - cp -r node_modules/{\@babel,\@noble,\@protobufjs,regenerator-runtime,node-fetch,\@trezor,randombytes,safe-buffer,bip66,pushdata-bitcoin,bitcoin-ops,typeforce,varuint-bitcoin,create-hash,blake2b,blakejs,nanoassert,blake2b-wasm,bs58check,bs58,base-x,create-hmac,wif,ms,semver-compare,long,define-properties,object-keys,has,function-bind,es-abstract,has-symbols,json-stable-stringify,cashaddrjs,big-integer,inherits,bchaddrjs,cross-fetch,js-chain-libs-node,bignumber.js,call-bind,get-intrinsic,base64-js,ieee754,util-deprecate,bech32,blake-hash,tiny-secp256k1,bn.js,elliptic,minimalistic-assert,minimalistic-crypto-utils,brorand,hash.js,hmac-drbg,int64-buffer,object.values,bytebuffer,protobufjs,usb-detection,babel-runtime,bindings,brotli,clone,deep-equal,dfa,eventemitter2,file-uri-to-path,fontkit,functions-have-names,has-property-descriptors,has-tostringtag,is-arguments,is-date-object,is-regex,linebreak,node-hid,object-is,pdfkit,png-js,regexp.prototype.flags,restructure,tiny-inflate,unicode-properties,unicode-trie,socks,socks-proxy-agent,ip,smart-buffer,ripple-lib,lodash,jsonschema,ripple-address-codec,ripple-keypairs,ripple-lib-transactionparser,ripple-binary-codec,buffer,decimal.js,debug,agent-base,tslib} $out/share/daedalus/node_modules/ + cp -r node_modules/{\@babel,\@noble,\@protobufjs,\@emurgo\/cardano-serialization-lib-nodejs,\@fivebinaries\/coin-selection,ua-parser-js,regenerator-runtime,node-fetch,\@trezor,randombytes,safe-buffer,bip66,pushdata-bitcoin,bitcoin-ops,typeforce,varuint-bitcoin,create-hash,blake2b,blakejs,nanoassert,blake2b-wasm,bs58check,bs58,base-x,create-hmac,wif,ms,semver-compare,long,define-properties,object-keys,has,function-bind,es-abstract,has-symbols,json-stable-stringify,cashaddrjs,big-integer,inherits,bchaddrjs,cross-fetch,js-chain-libs-node,bignumber.js,call-bind,get-intrinsic,base64-js,ieee754,util-deprecate,bech32,blake-hash,tiny-secp256k1,bn.js,elliptic,minimalistic-assert,minimalistic-crypto-utils,brorand,hash.js,hmac-drbg,int64-buffer,object.values,protobufjs,usb-detection,babel-runtime,bindings,brotli,clone,deep-equal,dfa,eventemitter2,file-uri-to-path,fontkit,functions-have-names,has-property-descriptors,has-tostringtag,is-arguments,is-date-object,is-regex,linebreak,node-hid,object-is,pdfkit,png-js,regexp.prototype.flags,restructure,tiny-inflate,unicode-properties,unicode-trie,socks,socks-proxy-agent,ip,smart-buffer,ripple-lib,lodash,jsonschema,ripple-address-codec,ripple-keypairs,ripple-lib-transactionparser,ripple-binary-codec,buffer,decimal.js,debug,agent-base,tslib,whatwg-url,tr46,usb,node-gyp-build,\@sinclair,ts-mixer,core-js} $out/share/daedalus/node_modules/ chmod -R +w $out @@ -170,7 +164,15 @@ in rec { # TODO: we took Release/detection.node before `rebuild-native-modules.sh` ever existed – is this still fine? cp node_modules/usb-detection/build/Debug/detection.node $out/share/daedalus/node_modules/usb-detection/build - for file in $out/share/daedalus/node_modules/usb/build/usb_bindings.node $out/share/daedalus/node_modules/node-hid/build/HID_hidraw.node $out/share/daedalus/node_modules/usb-detection/build/detection.node; do + mkdir -p $out/share/daedalus/node_modules/utf-8-validate/build + cp node_modules/utf-8-validate/build/Debug/validation.node $out/share/daedalus/node_modules/utf-8-validate/build + + for file in \ + $out/share/daedalus/node_modules/usb/build/usb_bindings.node \ + $out/share/daedalus/node_modules/node-hid/build/HID_hidraw.node \ + $out/share/daedalus/node_modules/usb-detection/build/detection.node \ + $out/share/daedalus/node_modules/utf-8-validate/build/validation.node \ + ; do $STRIP $file patchelf --shrink-rpath $file done @@ -325,13 +327,9 @@ in rec { linuxSources = { electron = pkgs.fetchurl { + name = "electron-v${electronVersion}-linux-x64.zip"; url = "https://github.com/electron/electron/releases/download/v${electronVersion}/electron-v${electronVersion}-linux-x64.zip"; - hash = "sha256-jXeA3Sr8/l6Uos9XT0+hCiosaRIndx/KSQUcUkrGdRM="; - }; - - electronChromedriver = pkgs.fetchurl { - url = "https://github.com/electron/electron/releases/download/v${electronChromedriverVersion}/chromedriver-v${electronChromedriverVersion}-linux-x64.zip"; - hash = "sha256-bkeA1l1cBppdsbLISwu8MdC/2E5sjVJx6e+KhLgQ5yA="; + hash = "sha256-bTCdzqsn2QgvuUWBq9XXofiCzgw4x4tEdo92xiBobvs="; }; }; diff --git a/nix/internal/x86_64-windows.nix b/nix/internal/x86_64-windows.nix index eff0997e97..55c8c6df89 100644 --- a/nix/internal/x86_64-windows.nix +++ b/nix/internal/x86_64-windows.nix @@ -86,7 +86,8 @@ in rec { mkdir -p installers/icons/${cluster}/${cluster} cp ${windowsIcons.${cluster}}/${cluster}/* installers/icons/${cluster}/${cluster}/ - ${common.temporaryNodeModulesPatches} + sed -r "s/^const usb =.*/const usb = require(require('path').join(process.env.DAEDALUS_INSTALL_DIRECTORY, 'usb_bindings.node'));/g" \ + -i node_modules/usb/dist/usb/bindings.js export DEBUG=electron-packager yarn --verbose --offline package --win64 --dir $(pwd) --icon installers/icons/${cluster}/${cluster} @@ -107,10 +108,10 @@ in rec { done ) - rm -rf $out/resources/app/{installers,launcher-config.yaml,gulpfile.js,home} + rm -rf $out/resources/app/{installers,launcher-config.yaml,home} mkdir -pv $out/resources/app/node_modules - cp -r node_modules/{\@babel,\@noble,\@protobufjs,regenerator-runtime,node-fetch,\@trezor,randombytes,safe-buffer,bip66,pushdata-bitcoin,bitcoin-ops,typeforce,varuint-bitcoin,create-hash,blake2b,blakejs,nanoassert,blake2b-wasm,bs58check,bs58,base-x,create-hmac,wif,ms,semver-compare,long,define-properties,object-keys,has,function-bind,es-abstract,has-symbols,json-stable-stringify,cashaddrjs,big-integer,inherits,bchaddrjs,cross-fetch,js-chain-libs-node,bignumber.js,call-bind,get-intrinsic,base64-js,ieee754,util-deprecate,bech32,blake-hash,tiny-secp256k1,bn.js,elliptic,minimalistic-assert,minimalistic-crypto-utils,brorand,hash.js,hmac-drbg,int64-buffer,object.values,bytebuffer,protobufjs,usb-detection,babel-runtime,bindings,brotli,clone,deep-equal,dfa,eventemitter2,file-uri-to-path,fontkit,functions-have-names,has-property-descriptors,has-tostringtag,is-arguments,is-date-object,is-regex,linebreak,node-hid,object-is,pdfkit,png-js,regexp.prototype.flags,restructure,tiny-inflate,unicode-properties,unicode-trie,socks,socks-proxy-agent,ip,smart-buffer,ripple-lib,lodash,jsonschema,ripple-address-codec,ripple-keypairs,ripple-lib-transactionparser,ripple-binary-codec,buffer,decimal.js,debug,agent-base,tslib} $out/resources/app/node_modules + cp -r node_modules/{\@babel,\@noble,\@protobufjs,\@emurgo\/cardano-serialization-lib-nodejs,\@fivebinaries\/coin-selection,ua-parser-js,regenerator-runtime,node-fetch,\@trezor,randombytes,safe-buffer,bip66,pushdata-bitcoin,bitcoin-ops,typeforce,varuint-bitcoin,create-hash,blake2b,blakejs,nanoassert,blake2b-wasm,bs58check,bs58,base-x,create-hmac,wif,ms,semver-compare,long,define-properties,object-keys,has,function-bind,es-abstract,has-symbols,json-stable-stringify,cashaddrjs,big-integer,inherits,bchaddrjs,cross-fetch,js-chain-libs-node,bignumber.js,call-bind,get-intrinsic,base64-js,ieee754,util-deprecate,bech32,blake-hash,tiny-secp256k1,bn.js,elliptic,minimalistic-assert,minimalistic-crypto-utils,brorand,hash.js,hmac-drbg,int64-buffer,object.values,protobufjs,usb-detection,babel-runtime,bindings,brotli,clone,deep-equal,dfa,eventemitter2,file-uri-to-path,fontkit,functions-have-names,has-property-descriptors,has-tostringtag,is-arguments,is-date-object,is-regex,linebreak,node-hid,object-is,pdfkit,png-js,regexp.prototype.flags,restructure,tiny-inflate,unicode-properties,unicode-trie,socks,socks-proxy-agent,ip,smart-buffer,ripple-lib,lodash,jsonschema,ripple-address-codec,ripple-keypairs,ripple-lib-transactionparser,ripple-binary-codec,buffer,decimal.js,debug,agent-base,tslib,whatwg-url,tr46,usb,node-gyp-build,\@sinclair,ts-mixer,core-js} $out/resources/app/node_modules chmod -R +w $out @@ -389,6 +390,7 @@ in rec { cp node_modules/usb-detection/build/Release/detection.node $out/build/Release/ cp node_modules/usb/build/Release/usb_bindings.node $out/build/Release/ cp node_modules/node-hid/build/Release/HID.node $out/build/Release/ + cp node_modules/utf-8-validate/build/Release/validation.node $out/build/Release/ # make sure they’re for Windows find $out -iname '*.node' | while IFS= read -r ext ; do @@ -420,6 +422,7 @@ in rec { native = rec { nodejs = pkgs.fetchzip { + name = "node-v${common.nodejs.version}-win-x64.zip"; url = "https://nodejs.org/dist/v${common.nodejs.version}/node-v${common.nodejs.version}-win-x64.zip"; hash = "sha256-n8ux67xrq3Rta1nE715y1m040oaLxUI2bIt12RaJdeM="; }; @@ -569,8 +572,9 @@ in rec { windowsSources = { electron = pkgs.fetchurl { + name = "electron-v${electronVersion}-win32-x64.zip"; url = "https://github.com/electron/electron/releases/download/v${electronVersion}/electron-v${electronVersion}-win32-x64.zip"; - hash = "sha256-xYQml960qP3sB/Rp3uEMU7s9aT2Ma4A5VHHzuUx8r9c="; + hash = "sha256-R8jxX41cduvqFb+/fA+UvixHQ45+alePS1gyScSZTEc="; }; # XXX: normally, node-gyp would download it only for Windows, @@ -578,7 +582,7 @@ in rec { node-lib = pkgs.fetchurl { name = "node.lib-${electronVersion}"; # cache invalidation url = "https://electronjs.org/headers/v${electronVersion}/win-x64/node.lib"; - hash = "sha256-JhIFzgm+Oig7FsHk1TP85H6PDD3drC7wXpVDfq8hIC4="; + hash = "sha256-5lKdTXZDXu/3o7mTbdEArU8+ZQHVkEQGoVO4V0XUlKQ="; }; }; diff --git a/package.json b/package.json index 4193b8d750..60c846cd6c 100644 --- a/package.json +++ b/package.json @@ -5,57 +5,34 @@ "description": "Cryptocurrency Wallet", "main": "./dist/main/index.js", "scripts": { - "dev": "concurrently --names renderer,main 'yarn dev:renderer' 'wait-on http://localhost:8080 && yarn dev:main'", - "dev:windows": "cross-env DAEDALUS_INSTALL_DIRECTORY=\"C:\\Program Files\\Daedalus Testnet\" LAUNCHER_CONFIG=\"C:\\Program Files\\Daedalus Testnet\\launcher-config.yaml\" yarn dev", + "dev": "npx concurrently --names renderer,main \"yarn dev:renderer\" \"wait-on http://localhost:8080 && yarn dev:main\"", + "dev:windows": "cross-env DAEDALUS_INSTALL_DIRECTORY=\"C:\\Program Files\\Daedalus Preview\" LAUNCHER_CONFIG=\"C:\\Program Files\\Daedalus Preview\\launcher-config.yaml\" DATA_DIR=\"C:\\Users\\daniel\\AppData\\Roaming\\Daedalus Preview\" yarn dev", "dev:main": "cross-env NODE_ENV=development yarn build:main --watch", - "dev:renderer": "concurrently 'yarn typedef:sass --watch' 'cross-env NODE_ENV=development yarn webpack serve -c source/renderer/webpack.config.js --progress'", + "dev:renderer": "npx concurrently \"yarn typedef:sass --watch\" \"cross-env NODE_ENV=development yarn webpack serve -c source/renderer/webpack.config.js --progress\"", "build": "yarn build:cleanup && yarn build:main && yarn build:renderer", - "build:main": "yarn webpack -c source/main/webpack.config.js --progress", - "build:renderer": "yarn webpack -c source/renderer/webpack.config.js --progress", + "build:main": "yarn webpack -c source/main/webpack.config.js --progress --no-cache", + "build:renderer": "yarn webpack -c source/renderer/webpack.config.js --progress --no-cache", "build:cleanup": "rimraf ./dist", "build:electron": "./scripts/rebuild-native-modules.sh", - "check:all": "yarn prettier:check && yarn lint && yarn compile && yarn stylelint && yarn i18n:manage && yarn storybook:build", + "build:electron:windows": ".\\scripts\\rebuild-native-modules.bat", + "check:all": "yarn prettier:check && yarn lint && yarn compile && yarn stylelint && yarn i18n:manage", "start": "yarn electron ./", - "start:dev": "nodemon --watch 'dist/main' --exec 'NODE_ENV=development yarn start'", - "test": "NODE_ENV=test yarn build && yarn test:unit && yarn test:e2e:fail-fast", - "test:jest": "jest", - "test:generate:report": "yarn node-swc tests/reporter.ts", - "test:unit": "yarn cucumber:run --require 'tests/**/unit/**/*.ts' --tags '@unit and not @skip and not @wip'", - "test:unit:rerun": "yarn cucumber:rerun --require 'tests/**/unit/**/*.ts' --tags '@unit and not @skip and not @wip'", - "test:unit:watch": "nodemon --watch source --watch tests --exec \"yarn test:unit --tags '@unit and @watch'\"", - "test:unit:unbound": "yarn cucumber:run --require 'tests/**/unit/**/*.ts' --tags '@unbound and not @skip and not @wip'", - "test:e2e": "yarn cucumber:run --require 'tests/setup-e2e.ts' --require 'tests/**/e2e/**/*.ts' --tags '@e2e and not @skip and not @wip'", - "test:e2e:fail-fast": "yarn cucumber:fail-fast --require 'tests/setup-e2e.ts' --require 'tests/**/e2e/**/*.ts' --tags '@e2e and not @skip and not @wip'", - "test:e2e:rerun": "yarn cucumber:rerun --require 'tests/setup-e2e.ts' --require 'tests/**/e2e/**/*.ts' --tags '@e2e and not @skip and not @wip'", - "test:e2e:rerun:fail-fast": "yarn cucumber:rerun --require 'tests/setup-e2e.ts' --require 'tests/**/e2e/**/*.ts' --tags '@e2e and not @skip and not @wip'", - "test:e2e:watch": "gulp test:e2e:watch", - "test:e2e:watch:once": "KEEP_APP_AFTER_TESTS=true yarn test:e2e --tags '@e2e and @watch'", - "test:hardware-wallets": "ts-node hardware-wallet-tests/index.ts", - "cucumber": "cross-env NODE_ENV=test cucumber-js --require-module '@swc-node/register' --require 'tests/setup-common.ts' -f json:tests-report/report-data.json -f summary:tests-report/summary.log -f node_modules/cucumber-pretty:tests-report/results.log --format-options '{\"snippetInterface\": \"async-await\"}' -f node_modules/cucumber-pretty --format-options '{\"snippetInterface\": \"async-await\"}' -f rerun:tests/@rerun.txt", - "cucumber:run": "yarn cucumber tests", - "cucumber:fail-fast": "yarn cucumber tests --fail-fast", - "cucumber:rerun": "yarn cucumber tests-report/@rerun.txt", - "cucumber:rerun:fail-fast": "yarn cucumber tests-report/@rerun.txt --fail-fast", + "start:dev": "nodemon --watch \"dist/main\" --exec \"NODE_ENV=development yarn start\"", "node-swc": "node -r @swc-node/register", - "debug": "gulp debug", "package": "cross-env NODE_ENV=production yarn build && cross-env NODE_ENV=production yarn node-swc scripts/package.js", "package:all": "yarn package --all", - "lint": "eslint --format=node_modules/eslint-formatter-pretty source storybook utils --ext .ts,.tsx", - "lint:fix": "eslint --fix --fix-type problem --ext '.ts,.tsx' source storybook utils", + "lint": "eslint --format=node_modules/eslint-formatter-pretty source utils --ext .ts,.tsx", + "lint:fix": "eslint --fix --fix-type problem --ext \".ts,.tsx\" source utils", "compile": "tsc --noEmit", "precompile": "yarn typedef:sass", - "prettier": "./node_modules/.bin/prettier \"**/*.*\"", + "prettier": "npx prettier \"**/*.*\"", "prettier:check": "yarn prettier --check", "prettier:format": "yarn prettier --write --loglevel warn", "stylelint": "./node_modules/.bin/stylelint \"**/*.scss\"", "stylelint:fix": "yarn stylelint --fix", - "i18n:extract": "formatjs extract 'source/**/*.{ts,tsx}' --ignore='**/*.d.ts' --format='translations/formatter.js' --extract-source-location --out-file='translations/messages.json'", + "i18n:extract": "formatjs extract \"source/**/*.{ts,tsx}\" --ignore=\"**/*.d.ts\" --format=\"translations/formatter.js\" --extract-source-location --out-file=\"translations/messages.json\"", "i18n:check": "yarn node-swc ./translations/translation-runner.ts", "i18n:manage": "yarn i18n:extract && yarn i18n:check", - "storybook": "start-storybook -p 6006 -c storybook --ci /", - "storybook:build": "build-storybook -c storybook -o dist/storybook", - "themes:check:createTheme": "gulp build:themes && yarn node-swc ./dist/scripts/check.js", - "themes:update": "gulp build:themes && yarn node-swc ./dist/scripts/update.js && yarn prettier --loglevel warn --write source/renderer/app/themes/daedalus/*.ts", "themes:copy": "yarn node-swc source/renderer/app/themes/utils/copyTheme.ts && yarn prettier --loglevel warn --write source/renderer/app/themes/daedalus/*.ts", "clear:cache": "rimraf ./node_modules/.cache", "clear:translations": "rimraf ./translations/messages", @@ -70,227 +47,222 @@ "itn:shelley:wallet:importer": "yarn node-swc utils/api-importer/itn-shelley-wallet-importer.ts", "yoroi:wallet:importer": "yarn node-swc utils/api-importer/yoroi-wallet-importer.ts", "create-news-verification-hashes": "yarn node-swc utils/create-news-verification-hashes/index.ts", - "typedef:sass": "typed-scss-modules source/renderer/app" + "typedef:sass": "npx typed-scss-modules source/renderer/app" }, "bin": { "electron": "./node_modules/.bin/electron" }, "devDependencies": { - "@dump247/storybook-state": "1.6.1", "@electron/rebuild": "3.2.13", - "@faker-js/faker": "6.0.0", - "@formatjs/cli": "4.8.3", - "@pmmmwh/react-refresh-webpack-plugin": "0.5.3", - "@storybook/addon-actions": "6.4.22", - "@storybook/addon-knobs": "6.4.0", - "@storybook/addon-links": "6.4.22", - "@storybook/addons": "6.4.22", - "@storybook/builder-webpack5": "6.4.22", - "@storybook/core": "6.4.22", - "@storybook/manager-webpack5": "6.4.22", - "@storybook/react": "6.4.22", - "@swc-node/register": "1.4.2", - "@swc/core": "1.2.164", - "@swc/jest": "0.2.21", - "@testing-library/jest-dom": "5.15.1", - "@testing-library/react": "12.1.2", - "@types/aes-js": "3.1.1", - "@types/jest": "27.4.0", - "@types/node": "14.18.1", - "@types/qrcode.react": "1.0.2", - "@types/react": "16.9.56", - "@types/react-router": "5.1.18", + "@faker-js/faker": "8.4.1", + "@khanacademy/flow-to-ts": "^0.5.2", + "@pmmmwh/react-refresh-webpack-plugin": "0.5.11", + "@swc-node/register": "1.9.0", + "@swc/core": "1.4.12", + "@testing-library/react": "15.0.5", + "@types/aes-js": "3.1.4", + "@types/fs-extra": "11.0.4", + "@types/lodash": "4.17.0", + "@types/node": "18.19.30", + "@types/qrcode.react": "1.0.5", + "@types/react": "18.2.74", + "@types/react-router": "5.1.20", "@types/react-router-dom": "5.3.3", - "@types/react-svg-inline": "2.1.3", + "@types/react-svg-inline": "2.1.6", "@types/react-table": "^7.7.9", - "@types/uuid": "8.3.4", - "@typescript-eslint/eslint-plugin": "5.20.0", - "@typescript-eslint/parser": "5.20.0", + "@types/tmp": "0.2.6", + "@types/uuid": "9.0.8", + "@typescript-eslint/eslint-plugin": "7.2.0", + "@typescript-eslint/parser": "7.2.0", "@xarc/run": "1.1.1", - "asar": "2.1.0", - "autodll-webpack-plugin": "0.4.2", - "axios": "0.24.0", - "bufferutil": "4.0.1", + "archiver": "^7.0.1", + "axios": "1.6.8", "cache-loader": "4.1.0", - "chai": "4.3.4", - "chalk": "4.1.0", - "concurrently": "7.1.0", - "cross-env": "7.0.2", + "chalk": "5.3.0", + "concurrently": "8.2.2", + "cross-env": "7.0.3", "crypto-browserify": "3.12.0", "css-loader": "6.2.0", - "cucumber": "6.0.5", - "cucumber-pretty": "6.0.0", - "del": "6.0.0", + "del": "7.1.0", + "depcheck": "^1.4.7", "electron-connect": "0.6.3", "electron-devtools-installer": "3.2.0", "electron-mock-ipc": "0.3.12", - "electron-packager": "17.1.1", - "electron-reloader": "1.2.1", - "eslint": "8.13.0", + "electron-packager": "17.1.2", + "eslint": "8.57.0", "eslint-config-airbnb": "19.0.4", - "eslint-config-prettier": "8.5.0", - "eslint-formatter-pretty": "4.1.0", - "eslint-import-resolver-webpack": "0.13.2", - "eslint-plugin-import": "2.26.0", - "eslint-plugin-jest": "26.1.4", - "eslint-plugin-jsx-a11y": "6.5.1", - "eslint-plugin-promise": "6.0.0", - "eslint-plugin-react": "7.29.4", - "eslint-plugin-react-hooks": "4.4.0", + "eslint-config-prettier": "9.1.0", + "eslint-formatter-pretty": "6.0.1", + "eslint-import-resolver-webpack": "0.13.8", + "eslint-plugin-import": "2.29.1", + "eslint-plugin-jest": "28.2.0", + "eslint-plugin-jsx-a11y": "6.8.0", + "eslint-plugin-promise": "6.1.1", + "eslint-plugin-react": "7.34.1", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-unused-imports": "^3.1.0", "esm": "3.2.25", - "gulp-shell": "0.8.0", "hash.js": "1.1.7", - "html-loader": "2.1.2", - "html-webpack-plugin": "5.4.0", + "html-loader": "5.0.0", + "html-webpack-plugin": "5.6.0", "https-browserify": "1.0.0", - "husky": "4.3.0", + "husky": "4.3.8", "identity-obj-proxy": "3.0.0", - "jest": "27.5.1", + "jest": "29.7.0", "jest-css-modules-transform": "4.4.2", - "jest-environment-jsdom": "27.5.1", - "markdown-loader": "7.0.0", - "mini-css-extract-plugin": "2.3.0", - "minimist": "1.2.6", + "jest-environment-jsdom": "29.7.0", + "markdown-loader": "8.0.0", + "mini-css-extract-plugin": "2.8.1", + "minimist": "1.2.8", "mobx-react-devtools": "6.1.1", - "node-forge": "1.0.0", - "nodemon": "2.0.15", - "npmlog": "4.1.2", + "node-forge": "1.3.1", + "nodemon": "3.1.0", + "npmlog": "7.0.1", "path-browserify": "1.0.1", - "postcss": "8.3.6", - "postcss-modules": "4.2.2", - "prettier": "2.1.2", - "pretty-quick": "3.0.2", + "postcss": "8.4.38", + "postcss-modules": "6.0.0", + "prettier": "3.2.5", + "pretty-quick": "4.0.0", "prettysize": "2.0.0", + "prompts": "2.4.2", "react-intl-translations-manager": "5.0.3", - "react-refresh": "0.11.0", - "react-syntax-highlighter": "13.5.3", - "regenerator-runtime": "0.13.7", - "sass": "1.44.0", - "sass-loader": "12.1.0", + "react-refresh": "0.14.0", + "react-syntax-highlighter": "15.5.0", + "regenerator-runtime": "0.14.1", + "raw-loader": "4.0.2", + "sass": "1.49.11", + "sass-loader": "12.6.0", "seedrandom": "3.0.5", "sinon": "9.2.2", "spawn-sync": "2.0.0", - "spectron": "14.0.0", - "storybook-addon-swc": "1.1.7", "stream-browserify": "3.0.0", "stream-http": "3.2.0", - "style-loader": "3.2.1", - "stylelint": "13.7.2", - "stylelint-order": "4.1.0", - "svg-inline-loader": "0.8.2", + "style-loader": "3.3.4", + "stylelint": "16.3.1", + "stylelint-order": "6.0.4", "svg-jest": "1.0.1", - "swc-loader": "0.1.15", - "thread-loader": "2.1.3", + "swc-loader": "0.2.6", + "thread-loader": "4.0.2", "timemachine": "0.3.2", "transform-loader": "0.2.4", - "typed-scss-modules": "5.0.0", - "typescript": "4.6.2", - "utf-8-validate": "5.0.2", - "wait-on": "6.0.1", - "webdriverio": "5.18.7", - "webpack": "5.72.0", - "webpack-cli": "4.9.2", - "webpack-dev-server": "4.9.0", - "ws": "7.3.1", - "yamljs": "0.3.0", - "yarn-lockfile": "1.1.1" + "typed-scss-modules": "8.0.1", + "typescript": "4.9.5", + "wait-on": "7.2.0", + "webpack": "5.91.0", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.2", + "ws": "8.16.0", + "yamljs": "0.3.0" }, "dependencies": { - "@cardano-foundation/ledgerjs-hw-app-cardano": "6.0.0", + "@cardano-foundation/ledgerjs-hw-app-cardano": "6.0.1", "@iohk-jormungandr/wallet-js": "0.5.0-pre7", - "@ledgerhq/hw-transport-node-hid": "6.27.15", - "@trezor/connect": "9.0.8", - "@trezor/transport": "1.1.12", + "@ledgerhq/devices": "^8.2.2", + "@ledgerhq/hw-transport-node-hid": "6.28.5", + "@tippyjs/react": "4.2.6", + "@trezor/connect": "9.1.12", + "@trezor/transport": "1.1.24", "aes-js": "3.1.2", "bech32": "2.0.0", - "bignumber.js": "9.0.1", - "bip39": "3.0.4", - "blake2b": "2.1.3", - "blakejs": "1.1.0", - "borc": "2.1.2", - "bs58": "4.0.1", + "bignumber.js": "9.1.2", + "bip39": "3.1.0", + "blake2b": "2.1.4", + "blakejs": "1.2.1", + "borc": "3.0.0", + "bs58": "5.0.0", "buffer": "6.0.3", - "cardano-crypto.js": "5.3.6-rc.6", + "cardano-crypto.js": "6.1.2", "cardano-js": "0.4.8", - "cardano-launcher": "0.20220119.0", - "cbor": "5.0.2", - "check-disk-space": "3.2.0", - "chroma-js": "2.1.0", + "cbor": "9.0.2", + "chalk": "5.3.0", + "check-disk-space": "3.4.0", + "chroma-js": "2.4.2", "classnames": "2.2.6", - "csv-stringify": "5.5.1", - "cucumber-html-reporter": "5.2.0", - "electron": "24.2.0", + "create-react-context": "^0.3.0", + "csv-stringify": "6.4.6", + "electron": "27.3.7", "electron-log-daedalus": "2.2.21", - "electron-store": "8.0.1", + "electron-store": "8.2.0", "es6-error": "4.1.1", + "fast-password-entropy": "1.1.1", + "filter-invalid-dom-props": "3.0.1", "find-process": "1.4.7", - "fireworks-js": "1.0.4", - "form-data": "3.0.0", + "fireworks-js": "2.10.7", + "form-data": "4.0.0", "fs-extra": "9.0.1", - "fuse.js": "6.5.3", + "fuse.js": "7.0.0", + "get-port": "7.1.0", "glob": "7.1.6", "graceful-fs": "4.2.4", - "gulp": "4.0.2", - "highlight-words": "1.2.0", - "history": "4.10.1", - "humanize-duration": "3.23.1", - "inquirer": "7.3.3", + "highlight-words": "1.2.2", + "history": "5.3.0", + "humanize-duration": "3.32.0", + "inquirer": "9.2.17", "json-bigint": "1.0.0", "lodash": "4.17.21", - "lodash-es": "4.17.15", + "lodash-es": "4.17.21", "matomo-tracker": "2.2.4", - "mime-types": "2.1.27", - "mkdirp": "1.0.4", - "mobx": "5.15.7", - "mobx-react": "6.3.1", - "mobx-react-form": "2.0.8", - "mobx-react-router": "4.1.0", - "moment": "2.29.0", - "nanoid": "3.2.0", - "node-downloader-helper": "1.0.18", - "node-hid": "2.1.2", - "omit-deep-lodash": "1.1.5", + "mime-types": "2.1.35", + "mkdirp": "3.0.1", + "mobx": "6.12.3", + "mobx-react": "7.6.0", + "mobx-react-form": "4.0.0", + "mobx-react-router": "6.0.0", + "moment": "2.30.1", + "nanoid": "5.0.7", + "node-downloader-helper": "2.1.9", + "omit-deep-lodash": "1.1.7", "pbkdf2": "3.1.2", - "pdfkit": "0.8.3", + "pdfkit": "0.15.0", + "popper.js": "^1.16.1", "process": "0.11.10", - "prop-types": "15.7.2", + "prop-types": "15.8.1", "qr-image": "3.2.0", "qrcode.react": "1.0.0", + "querystring": "^0.2.1", "rc-slider": "9.7.2", - "react": "16.14.0", - "react-animate-height": "2.0.23", - "react-copy-to-clipboard": "5.0.2", + "react": "17.0.2", + "react-animate-height": "3.2.3", + "react-copy-to-clipboard": "5.1.0", "react-custom-scrollbars": "4.2.1", - "react-datetime": "3.0.4", - "react-dom": "16.14.0", + "react-datetime": "3.2.0", + "react-dom": "17.0.2", "react-intl": "2.9.0", - "react-lottie": "1.2.3", + "react-lottie": "1.2.4", "react-markdown": "5.0.3", - "react-polymorph": "1.0.4", - "react-router": "5.2.0", - "react-router-dom": "5.2.0", + "react-modal": "^3.16.1", + "react-router": "5.3.4", + "react-router-dom": "5.3.4", + "react-scrollbars-custom": "^4.1.1", "react-svg-inline": "2.1.1", - "react-table": "7.7.0", - "react-virtualized": "9.22.3", - "recharts": "1.8.5", - "rimraf": "3.0.2", + "react-table": "7.8.0", + "react-virtualized": "9.22.5", + "recharts": "2.12.4", + "rimraf": "4.4.1", "rotating-file-stream": "1.4.6", "route-parser": "0.0.5", "rust-cardano-crypto": "0.2.0", "safe-buffer": "5.2.1", "sanitize-filename": "1.6.3", - "semver": "7.3.5", + "semver": "7.6.0", "shasum": "1.0.2", - "source-map-support": "0.5.19", - "spectron-fake-dialog": "0.0.1", - "tail": "2.2.4", - "tcp-port-used": "1.0.1", + "source-map-support": "0.5.21", + "tail": "2.2.6", + "tippy.js": "6.3.7", + "tcp-port-used": "1.0.2", + "tsee": "1.3.4", "unorm": "1.6.0", - "url": "0.11.0", - "usb-detection": "4.13.0", - "util": "0.12.4", - "uuid": "8.3.2", - "validator": "13.7.0" + "url": "0.11.3", + "usb-detection": "4.14.2", + "util": "0.12.5", + "uuid": "9.0.1", + "utility-types": "^3.11.0", + "validator": "13.7.0", + "vm-browserify": "^1.1.2", + "which": "^4.0.0" + }, + "peerDependencies": { + "classnames": "2.5.1" }, "devEngines": { "node": ">=v14.18.1", @@ -304,16 +276,23 @@ }, "resolutions": { "**/**/handlebars": "4.7.7", - "**/**/elliptic": "6.5.4", "**/**/bl": "4.1.0", + "**/**/json5": "^1.0.2", + "**/**/loader-utils": "^1.4.2", "**/**/lodash": "4.17.21", - "**/**/mobx-react-lite": "2.2.2", - "**/**/usb": "1.7.2", + "**/**/browserify-sign": "^4.2.2", + "@babel/traverse": "^7.23.2", + "get-func-name": "^2.0.1", + "http-cache-semantics": "4.1.1", + "html-minifier-terser": "^7.2.0", "**/**/prismjs": "1.27.0", "**/**/prebuild-install": "^6.1.4", - "**/**/node-abi": "^3.40.0", + "**/**/node-abi": "^3.56.0", "**/**/nan": "^2.17.0", - "prebuild-install": "^6.1.4", - "pbkdf2": "3.1.2" + "**/**/usb": "^2.11.0", + "**/**/bufferutil": "^4.0.1", + "**/**/utf-8-validate": "^5.0.2", + "node.extend": "1.1.7", + "prebuild-install": "^6.1.4" } } diff --git a/scripts/package.js b/scripts/package.js index faedbc4a78..a980603ac0 100755 --- a/scripts/package.js +++ b/scripts/package.js @@ -27,14 +27,12 @@ const DEFAULT_OPTS = { asar: shouldUseAsar, ignore: [ /^\/.buildkite($|\/)/, - /^\/.storybook($|\/)/, /^\/flow($|\/)/, /^\/installers\/.*exe/, /^\/logs($|\/)/, /^\/node_modules($|\/)/, /^\/scripts($|\/)/, /^\/source($|\/)/, - /^\/storybook($|\/)/, /^\/tests($|\/)/, /^\/tests-report($|\/)/, /^\/translations($|\/)/, diff --git a/scripts/rebuild-native-modules.bat b/scripts/rebuild-native-modules.bat new file mode 100644 index 0000000000..faf24d9c49 --- /dev/null +++ b/scripts/rebuild-native-modules.bat @@ -0,0 +1,13 @@ +@echo off +SETLOCAL EnableExtensions +set "system=x86_64-windows" + +echo Deleting all current '*.node' files before rebuilding for Electron's ABI: +for /R %%i in (*.node) do if not "%%~pi"=="*\\@swc*\\*" del /Q /F "%%i" + +call npx electron-rebuild --force +call npx electron-rebuild -w usb-detection --force -s +echo ===== all *.node files after 'electron-rebuild': ===== +powershell -Command "Get-ChildItem -Recurse -Filter *.node | Sort-Object FullName | ForEach-Object {Get-FileHash $_.FullName -Algorithm SHA1}" +echo ======================================================== +ENDLOCAL diff --git a/scripts/rebuild-native-modules.sh b/scripts/rebuild-native-modules.sh index 92635cc7da..195b2887b5 100755 --- a/scripts/rebuild-native-modules.sh +++ b/scripts/rebuild-native-modules.sh @@ -20,13 +20,25 @@ nix run -L .#internal."${system:-x86_64-darwin}".common.patchElectronRebuild # XXX: Electron 24.2 requires c++17, not 14 (or old 1y): sed -r 's,std=c\+\+(14|1y),std=c++17,g' -i node_modules/usb/binding.gyp + +if [ "$(cut -d- -f2 <<<"${system:-x86_64-darwin}")" == "darwin" ] ; then + ourArch="$(cut -d- -f1 <<<"${system:-x86_64-darwin}" | sed -r 's/aarch64/arm64/')" + for f in \ + node_modules/usb/binding.gyp \ + node_modules/usb/libusb.gypi \ + node_modules/utf-8-validate/binding.gyp \ + ; do + sed -r 's,-arch (x86_64|arm64),-arch '"$ourArch"',g' -i "$f" + done +fi + # TODO: do we really need to run `electron-rebuild` 3×? electron-rebuild --force electron-rebuild -w usb-detection --force -s # -if [ "$(uname)" == "Linux" ] ; then +if [ "$(cut -d- -f2 <<<"${system:-x86_64-darwin}")" == "linux" ] ; then # We ship debug version because the release one has issues with Ledger Nano S electron-rebuild -w usb --force -s --debug fi @@ -62,8 +74,9 @@ tryLink() { tryLink "usb" "usb_bindings.node" tryLink "usb-detection" "detection.node" +tryLink "utf-8-validate" "validation.node" tryLink "node-hid" "HID.node" -if [ "$(uname)" == "Linux" ] ; then +if [ "$(cut -d- -f2 <<<"${system:-x86_64-darwin}")" == "linux" ] ; then tryLink "node-hid" "HID_hidraw.node" fi diff --git a/source/common/config/downloadManagerConfig.ts b/source/common/config/downloadManagerConfig.ts index 4030c5e22a..98744aafb7 100644 --- a/source/common/config/downloadManagerConfig.ts +++ b/source/common/config/downloadManagerConfig.ts @@ -1,7 +1,5 @@ // https://www.npmjs.com/package/node-downloader-helper import type { - AllowedDownloadDirectoriesValues, - AllowedDownloadDirectoriesKeys, AllowedDownloadDirectoriesMap, DownloadState, DownloadEventType, diff --git a/source/main/cardano-launcher/cardanoLauncher.ts b/source/main/cardano-launcher/cardanoLauncher.ts new file mode 100644 index 0000000000..16676652dd --- /dev/null +++ b/source/main/cardano-launcher/cardanoLauncher.ts @@ -0,0 +1,461 @@ +// Copyright © 2020 IOHK +// License: Apache-2.0 + +/** + * Module for starting and managing a Cardano node and wallet backend. + * + * The main class is [[Launcher]]. + * + * @packageDocumentation + */ + +import { mkdirp } from 'mkdirp'; +import process from 'process'; +import net from 'net'; + +import _ from 'lodash'; +import { EventEmitter } from 'tsee'; + +import { Logger, prependName } from './logging'; +import { + Service, + ServiceExitStatus, + ServiceStatus, + StartService, + setupService, + serviceExitStatusMessage, + defaultTimeoutSeconds, +} from './service'; +import { + DirPath, + passthroughErrorLogger, + ignorePromiseRejection, +} from './common'; +import { + LaunchConfig, + cardanoWalletStartService, + WalletStartService, +} from './cardanoWallet'; + +import { startCardanoNode } from './cardanoNode'; + +/** ***************************************************************************** + * Api + ***************************************************************************** */ + +export interface RequestParams { + port: number; + path: string; + hostname: string; + protocol: string; +} + +/** + * Connection parameters for the `cardano-wallet` API. + * These should be used to build the HTTP requests. + */ +export interface Api { + /** + * API base URL, including trailling slash. + */ + baseUrl: string; + + /** + * URL components which can be used with the HTTP client library of + * your choice. + */ + requestParams: RequestParams; +} + +class V2Api implements Api { + /** URL of the API, including a trailing slash. */ + readonly baseUrl: string; + /** URL components which can be used with the HTTP client library of + * your choice. */ + readonly requestParams: RequestParams; + + constructor(port: number, protocol = 'http:') { + const hostname = '127.0.0.1'; + const path = '/v2/'; + this.baseUrl = `${protocol}//${hostname}:${port}${path}`; + this.requestParams = { port, path, hostname, protocol }; + } +} + +/** ***************************************************************************** + * Exit status types + ***************************************************************************** */ + +/** + * The result after the launched wallet backend has finished. + */ +export interface ExitStatus { + wallet: ServiceExitStatus; + node: ServiceExitStatus; +} + +/** + * Format an [[ExitStatus]] as a multiline human-readable string. + */ +export function exitStatusMessage(status: ExitStatus): string { + return _.map(status, serviceExitStatusMessage).join('\n'); +} + +/** + * This instance of [[Error]] will be returned when the + * `Launcher.start()` promise is rejected. + */ +export class BackendExitedError extends Error { + status: ExitStatus; + constructor(status: ExitStatus) { + super(exitStatusMessage(status)); + Object.setPrototypeOf(this, new.target.prototype); + this.status = status; + } +} + +function noop(): void { + /* empty */ +} + +/** ***************************************************************************** + * Launching + ***************************************************************************** */ + +/** + * This is the main object which controls the launched wallet backend + * and its node. + * + * Example: + * + * ```javascript + * var launcher = new cardanoLauncher.Launcher({ + * networkName: "mainnet", + * stateDir: "/tmp/state-launcher", + * nodeConfig: { + * kind: "shelley", + * configurationDir: "/home/user/cardano-node/configuration/defaults/mainnet", + * network: { + * configFile: "configuration.yaml", + * topologyFile: "topology.json" + * } + * } + * childProcessLogWriteStream: fs.createWriteStream('./logs') + * }); + * ``` + * + * Initially, the backend is not started. Use [[Launcher.start]] for that. + */ +export class Launcher { + /** + * Use this attribute to monitor and control the `cardano-wallet` process. + */ + readonly walletService: Service; + + /** + * Use this to access the `cardano-wallet` API server. + */ + readonly walletBackend: WalletBackend; + + /** + * Use this to monitor the `cardano-node` process. + */ + readonly nodeService: Service; + + /** Logging adapter */ + protected logger: Logger; + + /** Wallet API server port - set once it's known. */ + private apiPort = 0; + + /** A state flag for whether the backend services have exited yet. */ + private exited = false; + + /** Removes process signal handlers, if they were installed. */ + private cleanupSignalHandlers: () => void = noop; + + /** + * Sets up a Launcher which can start and control the wallet backend. + * + * @param config - controls how the wallet and node are started + * @param logger - logging backend that launcher will use + */ + constructor(config: LaunchConfig, logger: Logger = console) { + logger.debug('Launcher init'); + const { childProcessLogWriteStreams, installSignalHandlers = true } = + config; + this.logger = logger; + + const start = Launcher.makeServiceCommands(config, logger); + + this.walletService = setupService( + start.wallet, + prependName(logger, 'wallet'), + childProcessLogWriteStreams?.wallet + ); + this.nodeService = setupService( + start.node, + prependName(logger, 'node'), + childProcessLogWriteStreams?.node + ); + + this.walletBackend = new WalletBackend( + new V2Api( + this.apiPort, + config.tlsConfiguration !== undefined ? 'https:' : 'http:' + ) + ); + + start.wallet + .then((startService: WalletStartService) => { + this.apiPort = startService.apiPort; + this.walletBackend.api = new V2Api( + this.apiPort, + config.tlsConfiguration !== undefined ? 'https:' : 'http:' + ); + }) + .catch(passthroughErrorLogger); + + this.walletService.events.on('statusChanged', (status) => { + if (status === ServiceStatus.Stopped) { + this.logger.debug('wallet exited'); + this.stop().catch(passthroughErrorLogger); + } + }); + + this.nodeService.events.on('statusChanged', (status) => { + if (status === ServiceStatus.Stopped) { + this.logger.debug('node exited'); + this.stop().catch(passthroughErrorLogger); + } + }); + + if (installSignalHandlers) this.installSignalHandlers(); + } + + /** + * Starts the wallet and node. + * + * Example: + * + * ```javascript + * launcher.start().then(function(api) { + * console.log("*** cardano-wallet backend is ready, base URL is " + api.baseUrl); + * }); + * ``` + * + * @return a promise that will be fulfilled when the wallet API + * server is ready to accept requests. + */ + start(): Promise { + const stopWaiting = (): boolean => + this.nodeService.getStatus() > ServiceStatus.Started || + this.walletService.getStatus() > ServiceStatus.Started; + + return new Promise((resolve, reject) => { + this.nodeService.start().catch(ignorePromiseRejection); + this.walletService.start().catch(ignorePromiseRejection); + + this.waitForApi(stopWaiting, () => { + this.walletBackend.events.ready(this.walletBackend.api); + }); + + this.walletBackend.events.on('ready', resolve); + this.walletBackend.events.on('exit', (st) => + reject(new BackendExitedError(st)) + ); + }); + } + + /** + * Poll TCP port of wallet API server until it accepts connections. + * + * @param stop - a callback, which will terminate the polling loop + * if it returns a truey value. + * + * @param ready - a callback which is called once the wallet API + * server accepts connections. + */ + private waitForApi(stop: () => boolean, ready: () => void): void { + this.logger.debug('waitForApi'); + + let addr: net.SocketConnectOpts; + let client: net.Socket; + const timer = setInterval(() => { + if (stop()) { + clearInterval(timer); + } else if (this.apiPort) { + if (!addr) { + addr = { port: this.apiPort, host: '127.0.0.1' }; + this.logger.info( + `Waiting for tcp port ${addr.host}:${addr.port} to accept connections...` + ); + } + + if (client) { + client.destroy(); + } + client = new net.Socket(); + client.connect(addr, () => { + this.logger.info(`... port is ready.`); + clearInterval(timer); + ready(); + }); + client.on('error', (err) => { + this.logger.debug(`waitForApi: not ready yet: ${err}`); + }); + } + }, 250); + } + + /** + * Stops the wallet and node backends. + * + * Attempts to cleanly shut down the processes. However, if they + * have not exited before the timeout, they will be killed. The + * default timeout is [[defaultTimeoutSeconds]]. + * + * @param timeoutSeconds - how long to wait before killing the processes. + * @return a [[Promise]] that is fulfilled at the timeout, or before. + * + * @event exit - `walletBackend.events` will emit this when the + * wallet and node have both exited. + */ + stop( + timeoutSeconds = defaultTimeoutSeconds + ): Promise<{ wallet: ServiceExitStatus; node: ServiceExitStatus }> { + this.logger.debug(`Launcher.stop: stopping wallet and node`); + return Promise.all([ + this.walletService.stop(timeoutSeconds), + this.nodeService.stop(timeoutSeconds), + ]).then(([wallet, node]) => { + const status = { wallet, node }; + this.logger.debug(`Launcher.stop: both services are stopped.`, status); + if (!this.exited) { + this.walletBackend.events.exit(status); + this.exited = true; + } + this.cleanupSignalHandlers(); + return status; + }); + } + + /** + * Stop services when this process gets killed. + */ + private installSignalHandlers(): void { + const signals: NodeJS.Signals[] = [ + 'SIGINT', + 'SIGTERM', + 'SIGHUP', + 'SIGBREAK', + ]; + const handler = (signal: NodeJS.Signals): void => { + this.logger.info(`Received ${signal} - stopping services...`); + this.walletService.stop(0).catch(passthroughErrorLogger); + this.nodeService.stop(0).catch(passthroughErrorLogger); + }; + signals.forEach((signal) => process.on(signal, handler)); + this.cleanupSignalHandlers = (): void => { + signals.forEach((signal) => process.off(signal as any, handler)); + this.cleanupSignalHandlers = noop; + }; + } + + private static makeServiceCommands( + config: LaunchConfig, + logger: Logger + ): { wallet: Promise; node: Promise } { + logger.info( + `Creating state directory ${config.stateDir} (if it doesn't already exist)` + ); + const node = mkdirp(config.stateDir).then(() => + Launcher.nodeExe(config.stateDir, config) + ); + const wallet = node.then(() => Launcher.walletExe(config.stateDir, config)); + return { wallet, node }; + } + + private static async walletExe( + baseDir: DirPath, + config: LaunchConfig + ): Promise { + return cardanoWalletStartService(baseDir, config); + } + + private static nodeExe( + baseDir: DirPath, + config: LaunchConfig + ): Promise { + switch (config.nodeConfig.kind) { + case 'shelley': + return startCardanoNode(baseDir, config.nodeConfig, config.networkName); + } + } +} + +/** + * Represents the API service of `cardano-wallet`. + */ +export class WalletBackend { + api: Api; + + constructor(api: Api) { + this.api = api; + this.events = new WalletBackendEvents(); + } + + /** + * @deprecated + * @return HTTP connection parameters for the `cardano-wallet` API server. + */ + getApi(): Api { + return this.api; + } + + /** + * An [`EventEmitter`](https://nodejs.org/api/events.html#class-eventemitter) + * that can be used to register handlers when + * the process changes status. + * + * ### Example + * ```typescript + * launcher.walletBackend.events.on('ready', (api: Api) => { ... }); + * launcher.walletBackend.events.on('exit', (status: ExitStatus) => { ... }); + * ``` + */ + events: WalletBackendEvents; +} + +/** + * The type of events for [[WalletBackend]]. + */ +export class WalletBackendEvents extends EventEmitter<{ + ready: (api: Api) => void; + exit: (status: ExitStatus) => void; +}> { + /** @ignore */ + constructor() { + super(); + } + + /** + * Event emitted when the API server is ready to accept requests. + * + * @param api the wallet API info. + * @event + */ + ready(api: Api): void { + this.emit('ready', api); + } + + /** + * Event emitted when both the wallet and node have both exited. + * + * @param stats the combined exit status. + * @event + */ + exit(status: ExitStatus): void { + this.emit('exit', status); + } +} diff --git a/source/main/cardano-launcher/cardanoNode.ts b/source/main/cardano-launcher/cardanoNode.ts new file mode 100644 index 0000000000..3a19b24426 --- /dev/null +++ b/source/main/cardano-launcher/cardanoNode.ts @@ -0,0 +1,317 @@ +// Copyright © 2020 IOHK +// License: Apache-2.0 + +/** + * Configuration for `cardano-node` + * + * @packageDocumentation + */ + +import path from 'path'; +import getPort from 'get-port'; + +import { StartService, ShutdownMethod, cleanShutdownFD } from './service'; +import { FilePath, DirPath, makeRtsArgs } from './common'; + +/** + * Definition of a `cardano-node` network. + */ +export interface CardanoNetwork { + /** + * The YAML configuration file for cardano-node. + */ + configFile: FilePath; + /** + * Network topology data to pass to cardano-node. + */ + topologyFile: FilePath; + /** + * Path to the Byron genesis file in JSON format. + * This is required for testnet and staging but not mainnet. + * It is used to configure the parameters of cardano-wallet. + * It should match the ByronGenesisFile configured in the cardano-node YAML file. + * Note that the Byron-era genesis file is always required, + * even if currently in Shelley era. + */ + genesisFile?: FilePath; +} + +/** Predefined networks. */ +export const networks: { [propName: string]: CardanoNetwork } = { + mainnet: { + configFile: 'configuration.json', + topologyFile: 'topology.json', + }, + testnet: { + configFile: 'configuration.json', + topologyFile: 'topology.json', + genesisFile: 'genesis-byron.json', + }, + staging: { + configFile: 'configuration.json', + topologyFile: 'topology.json', + genesisFile: 'genesis-byron.json', + }, +}; + +/** + * The command-line arguments which can be supplied to `cardano-node`. + * These values are derived from [[CardanoNodeConfig]]. + * + * @internal + */ +export interface CardanoNodeArgs { + /** + * Filename for the socket file to use for communicating with the + * node. + */ + socketFile: FilePath; + + /** + * The path to a file describing the topology. + * Topology is ... + */ + topologyFile: FilePath; + + /** Directory where the state is stored. */ + databaseDir: DirPath; + + /** Path to the delegation certificate. */ + delegationCertificate?: string; + + /** Path to the signing key. */ + signingKey?: string; + + /** Path to the KES signing key. */ + kesKey?: string; + + /** Path to the VRF signing key. */ + vrfKey?: string; + + /** Path to the delegation certificate */ + operationalCertificate?: string; + + /** Configures the address to bind for P2P communication. */ + listen: { + /** The TCP port for node P2P. */ + port: number; + /** Optionally limit node P2P to one ipv4 address. */ + address?: string; + /** Optionally limit node P2P to one ipv6 address. */ + address6?: string; + }; + + /** Configuration file for cardano-node. */ + configFile: FilePath; + + /** Validate all on-disk database files. */ + validateDb?: boolean; + + /** GHC runtime system options. */ + rtsOpts?: string[]; + + /** + * Extra arguments to add to the `cardano-node` command line. + */ + extra?: string[]; +} + +/** + * Configuration parameters for starting cardano-node. + * + * These parameters can be set in [[LaunchConfig.nodeConfig]]. + */ +export interface CardanoNodeConfig { + kind: 'shelley'; + + /** Directory containing configurations for all networks. + * Defaults to the using the `$CARDANO_NODE_CONFIGS` + * environment variable if not supplied. + */ + configurationDir: DirPath; + + /** Path to the delegation certificate. The delegation certificate allows the delegator + * (the issuer of said certificate) to give his/her own block signing rights to somebody + * else (the delegatee). The delegatee can then sign blocks on behalf of the delegator. + * */ + delegationCertificate?: string; + + /** Network parameters. */ + network: CardanoNetwork; + + /** Path to the KES signing key. */ + kesKey?: string; + + /** Path to the VRF signing key. */ + vrfKey?: string; + + /** Path to the delegation certificate */ + operationalCertificate?: string; + + /** Path to the signing key. */ + signingKey?: string; + + /** + * Filename for the socket to use for communicating with the + * node. Optional -- will be set automatically if not provided. + */ + socketFile?: FilePath; + + /** + * GHC runtime system options for cardano-node. + * Can be used for debugging, tuning performance, etc. + * See: https://downloads.haskell.org/ghc/8.10.7/docs/html/users_guide/runtime_control.html#setting-rts-options-on-the-command-line + */ + rtsOpts?: string[]; +} + +let pipeCounter = 0; + +/** + * Allocate a name for the pipe used to communicate with the node on + * Windows. The network name and PID are used to ensure different + * applications do not cross their streams. + * + * The name also includes a per-process counter, mostly so that + * integration tests do not conflict with each other. + * + * @param networkName: which network to put in the pipe name. + * @return a [Pipe Name](https://docs.microsoft.com/en-us/windows/win32/ipc/pipe-names) + */ +function windowsPipeName(networkName: string): string { + return `\\\\.\\pipe\\cardano-node-${networkName}.${ + process.pid + }.${pipeCounter++}`; +} + +/** + * Convert a [[CardanoNodeConfig]] into command-line arguments + * ([[CardanoNodeArgs]]) for `cardano-node`. + */ +function makeArgs( + stateDir: DirPath, + config: CardanoNodeConfig, + networkName: string, + listenPort: number +): CardanoNodeArgs { + // Set default value for socketFile, and update config. + let socketFile = config.socketFile; + if (!socketFile) { + if (process.platform === 'win32') { + config.socketFile = socketFile = windowsPipeName(networkName); + } else { + socketFile = 'cardano-node.socket'; // relative to working directory + config.socketFile = path.join(stateDir, socketFile); + } + } + + // Update config with predefined network if none provided. + if (!config.network) { + if (networks[networkName]) { + config.network = networks[networkName]; + } else { + throw new Error( + `CardanoNodeConfig does not have a network defined, and ${networkName} is not a recognised network name.` + ); + } + } + + // Use $CARDANO_NODE_CONFIGS/networkName if no configuration + // directory provided. + if (!config.configurationDir) { + const def = process.env.CARDANO_NODE_CONFIGS; + if (def) { + config.configurationDir = path.join(def as string, networkName); + } + } + + return { + socketFile, + topologyFile: path.join( + config.configurationDir, + config.network.topologyFile + ), + databaseDir: 'chain', // relative to working directory + delegationCertificate: config.delegationCertificate, + listen: { + port: listenPort, + // Listen for TCP connections on the loopback interface. + // We don't want to listen on 0.0.0.0 by default. + address: undefined, + // Don't listen on IPv6 at all -- this would fail if IPv6 is disabled. + address6: undefined, + }, + configFile: path.join(config.configurationDir, config.network.configFile), + signingKey: config.signingKey, + kesKey: config.kesKey, + vrfKey: config.vrfKey, + rtsOpts: (config.rtsOpts || []).concat('-N'), + }; +} + +export interface NodeStartService extends StartService { + /** This will contain the cardano-node socket file path. */ + socketPath: string; + + /** This will contain the TCP port which the cardano-node is listening on. */ + listenPort: number; +} + +/** + * Chooses the command-line arguments for the node. + * + * @param stateDir - directory for node storage, specific to the node type and network. + * @param config - parameters for starting the node. + * @return the command-line for starting this node. + */ +export async function startCardanoNode( + stateDir: DirPath, + config: CardanoNodeConfig, + networkName: string +): Promise { + const listenPort = await getPort(); + const args = makeArgs(stateDir, config, networkName, listenPort); + return { + command: 'cardano-node', + args: [ + 'run', + '--socket-path', + args.socketFile, + '--shutdown-ipc', + '' + cleanShutdownFD, + '--topology', + args.topologyFile, + '--database-path', + args.databaseDir, + '--port', + '' + args.listen.port, + '--config', + args.configFile, + ] + .concat(args.listen.address ? ['--host-addr', args.listen.address] : []) + .concat( + args.listen.address6 ? ['--host-ipv6-addr', args.listen.address6] : [] + ) + .concat(args.validateDb || false ? ['--validate-db'] : []) + .concat(args.signingKey ? ['--signing-key', args.signingKey] : []) + .concat( + args.delegationCertificate + ? ['--delegation-certificate', args.delegationCertificate] + : [] + ) + .concat(args.kesKey ? ['--shelley-kes-key', args.kesKey] : []) + .concat(args.vrfKey ? ['--shelley-vrf-key', args.vrfKey] : []) + .concat( + args.operationalCertificate + ? ['--shelley-operational-certificate', args.operationalCertificate] + : [] + ) + .concat(makeRtsArgs(args.rtsOpts)) + .concat(args.extra || []), + shutdownMethod: ShutdownMethod.CloseFD, + // set working directory to stateDir -- config file may have relative paths for logs. + cwd: stateDir, + socketPath: args.socketFile, + listenPort: args.listen.port, + }; +} diff --git a/source/main/cardano-launcher/cardanoWallet.ts b/source/main/cardano-launcher/cardanoWallet.ts new file mode 100644 index 0000000000..9d47631d97 --- /dev/null +++ b/source/main/cardano-launcher/cardanoWallet.ts @@ -0,0 +1,240 @@ +// Copyright © 2020 IOHK +// License: Apache-2.0 + +/** + * Module for starting and managing the cardano-wallet process. + * + * The main function is [[cardanoWalletStartService]]. + * + * @packageDocumentation + */ + +import path from 'path'; +import getPort from 'get-port'; + +import _ from 'lodash'; + +import { WriteStream } from 'fs'; +import { CardanoNodeConfig } from './cardanoNode'; +import { ServerTlsConfiguration } from './tls'; +import { StartService, ShutdownMethod } from './service'; +import { DirPath } from './common'; + +/******************************************************************************* + * Configuration + ******************************************************************************/ + +/** + * How to fetch stake pool metadata. + * - `'none'` - disables stake pool metadata. + * - `'direct'` - enables fetching stake pool metadata from + * the registered pool URL. + * - otherwise, the URL of a SMASH metadata proxy server + * can be supplied. This must not contain the endpoint path. + */ +export type PoolMetadataSource = 'none' | 'direct' | { smashUrl: string }; + +/** + * Configuration parameters for starting the wallet backend and node. + */ +export interface LaunchConfig { + /** + * Directory to store wallet databases, the blockchain, socket + * files, etc. + */ + stateDir: string; + + /** + * Label for the network that will connected. This is used in the + * state directory path name. + */ + networkName: string; + + /** + * TCP port to use for the `cardano-wallet` API server. + * The default is to select any free port. + */ + apiPort?: number; + + /** + * IP address or hostname to bind the `cardano-wallet` API server + * to. Can be an IPv[46] address, hostname, or `'*'`. Defaults to + * 127.0.0.1. + */ + listenAddress?: string; + + /** + * Overrides the URL to the zip file containing stake pool metadata + * which is downloaded by cardano-wallet. + * + * This is only useful in testing scenarios, or when running a local + * development testnet. + * + * For Jörmungandr ITN, the default is + * https://github.com/cardano-foundation/incentivized-testnet-stakepool-registry/archive/master.zip. + */ + stakePoolRegistryUrl?: string; + + /** + * Stake pool metadata fetching strategy. + */ + poolMetadataSource?: PoolMetadataSource; + + /** + * Token metadata server URL. + */ + tokenMetadataServer?: string; + + /** + * Maximum time difference (in seconds) between the tip slot and the + * latest applied block within which we consider a wallet being + * synced with the network. Defaults to 300 seconds. + */ + syncToleranceSeconds?: number; + + /** + * Configuration for starting `cardano-node`. + */ + nodeConfig: CardanoNodeConfig; + + /** + * WriteStreams for the child process data events from stdout and stderr + */ + childProcessLogWriteStreams?: { + node: WriteStream; + wallet: WriteStream; + }; + + /** + * Control the termination signal handling. Set this to false if the default + * behaviour interferes with your application shutdown behaviour. + * If setting this to false, ensure stop(0) is called as part of the shutdown. + */ + installSignalHandlers?: boolean; + + /** + * Paths to server TLS credentials for establishing a HTTPS connection using TLS + * If not set, the connection will be served insecurely over HTTP. + */ + tlsConfiguration?: ServerTlsConfiguration; +} + +/******************************************************************************* + * Starting the wallet + ******************************************************************************/ + +export interface WalletStartService extends StartService { + /** This will contain the port which the API is listening on, + * after the service has started. */ + apiPort: number; +} + +/** + * Produces a [[WalletStartService]] description of how to start + * `cardano-wallet` with a given [[LaunchConfig]]. + * + * Does not actually start the wallet. + * + * @param baseDir - The state directory, under which `cardano-wallet` + * shall store its databases. + * @param config - Parameters for starting the server. + * @return The launch instructions. + */ +export async function cardanoWalletStartService( + baseDir: DirPath, + config: LaunchConfig +): Promise { + const apiPort = config.apiPort || (await getPort()); + const commandSuffix = + config.nodeConfig.kind === 'shelley' ? '' : '-' + config.nodeConfig.kind; + const base: WalletStartService = { + command: 'cardano-wallet' + commandSuffix, + args: [ + 'serve', + '+RTS', + '-N', + '-RTS', + '--shutdown-handler', + '--port', + '' + apiPort, + '--database', + path.join(baseDir, 'wallets'), + ].concat( + config.listenAddress ? ['--listen-address', config.listenAddress] : [], + config.tlsConfiguration + ? [ + '--tls-ca-cert', + config.tlsConfiguration.caCert, + '--tls-sv-cert', + config.tlsConfiguration.svCert, + '--tls-sv-key', + config.tlsConfiguration.svKey, + ] + : [], + config.poolMetadataSource + ? [ + '--pool-metadata-fetching', + typeof config.poolMetadataSource === 'string' + ? config.poolMetadataSource + : config.poolMetadataSource.smashUrl, + ] + : [], + config.tokenMetadataServer + ? ['--token-metadata-server', config.tokenMetadataServer] + : [], + config.syncToleranceSeconds + ? ['--sync-tolerance', `${config.syncToleranceSeconds}s`] + : [] + ), + extraEnv: config.stakePoolRegistryUrl + ? { + CARDANO_WALLET_STAKE_POOL_REGISTRY_URL: config.stakePoolRegistryUrl, + } + : undefined, + shutdownMethod: ShutdownMethod.CloseStdin, + apiPort, + }; + const addArgs = (args: string[]): WalletStartService => + _.assign(base, { args: base.args.concat(args) }); + + switch (config.nodeConfig.kind) { + default: + if ( + config.networkName !== 'mainnet' && + !config.nodeConfig.network.genesisFile + ) { + throw new Error('genesisFile must be configured'); + } + + const genesisArg = + config.networkName == 'mainnet' + ? '' + : path.join( + config.nodeConfig.configurationDir, + config.nodeConfig.network.genesisFile as string + ); + + let networkArg; + switch (config.networkName) { + case 'mainnet': + networkArg = ['--mainnet']; + break; + + case 'staging': + networkArg = ['--staging', genesisArg]; + break; + + default: + networkArg = ['--testnet', genesisArg]; + break; + } + + return addArgs( + networkArg.concat( + config.nodeConfig.socketFile + ? ['--node-socket', config.nodeConfig.socketFile] + : [] + ) + ); + } +} diff --git a/source/main/cardano-launcher/cli.ts b/source/main/cardano-launcher/cli.ts new file mode 100644 index 0000000000..778dc44481 --- /dev/null +++ b/source/main/cardano-launcher/cli.ts @@ -0,0 +1,125 @@ +// Copyright © 2020 IOHK +// License: Apache-2.0 + +/** + * `cardano-launcher` command-line interface. + * + * This tool can be used for testing. + * + * See also: the entrypoint script `bin/cardano-launcher`. + * + * @packageDocumentation + */ + +import _ from 'lodash'; +import process from 'process'; +import Process = NodeJS.Process; + +import { Launcher, ExitStatus } from './cardanoLauncher'; + +import { ignorePromiseRejection } from './common'; +import { ServiceExitStatus, serviceExitStatusMessage } from './service'; +import * as cardanoNode from './cardanoNode'; +import { StdioLogger } from './loggers'; + +function usage(): void { + process.stdout.write( + 'usage: cardano-launcher BACKEND NETWORK CONFIG-DIR STATE-DIR' + ); + process.stdout.write(' BACKEND - shelley'); + process.stdout.write( + ' NETWORK - depends on backend, e.g. mainnet, itn_rewards_v1' + ); + process.stdout.write( + ' CONFIG-DIR - directory which contains config files for a backend' + ); + process.stdout.write( + ' STATE-DIR - directory to put blockchains, databases, etc.' + ); + process.exit(1); +} + +function combineStatus(statuses: ServiceExitStatus[]): number { + const code = _.reduce( + statuses, + (res: number | null, status) => (res === null ? status.code : res), + null + ); + const signal = _.reduce( + statuses, + (res: string | null, status) => (res === null ? status.signal : res), + null + ); + // let err = _.reduce(statuses, (res, status) => res === null ? status.err : res, null); + + return code === null ? (signal === null ? 0 : 127) : code; +} + +function sendMaybe(message: Record): void { + if (process.send) { + process.send(message); + } +} + +/** + * Main function of the CLI. + * + * Is just a very basic interface for testing things. + */ +export function cli(argv: Process['argv']): void { + const waitForExit = setInterval(() => undefined, 3600000); + const args = argv; + + args.shift(); // /usr/bin/node + args.shift(); // cardano-launcher + + if (args.length < 4) { + usage(); + } + + const backend = args.shift() as string; + const networkName = args.shift() as string; + const configurationDir = args.shift() as string; + const stateDir = args.shift() as string; + + let nodeConfig: any; // eslint-disable-line @typescript-eslint/no-explicit-any + + if (backend === 'shelley') { + if (!(networkName in cardanoNode.networks)) { + process.stderr.write(`unknown network: ${networkName}\n`); + process.exit(2); + } + const network = cardanoNode.networks[networkName]; + nodeConfig = { + kind: backend, + configurationDir, + network, + }; + } else { + usage(); + } + + const logger = new StdioLogger(); + const launcher = new Launcher({ stateDir, nodeConfig, networkName }, logger); + + launcher.start().catch(ignorePromiseRejection); + + // inform tests of subprocess pids + launcher.nodeService + .start() + .then((pid) => sendMaybe({ node: pid })) + .catch(ignorePromiseRejection); + launcher.walletService + .start() + .then((pid) => sendMaybe({ wallet: pid })) + .catch(ignorePromiseRejection); + + launcher.walletBackend.events.on('exit', (status: ExitStatus) => { + logger.info(serviceExitStatusMessage(status.wallet)); + logger.info(serviceExitStatusMessage(status.node)); + clearInterval(waitForExit); + process.exit(combineStatus([status.wallet, status.node])); + }); +} + +cli(process.argv); diff --git a/source/main/cardano-launcher/common.ts b/source/main/cardano-launcher/common.ts new file mode 100644 index 0000000000..21c377b2ef --- /dev/null +++ b/source/main/cardano-launcher/common.ts @@ -0,0 +1,47 @@ +// Copyright © 2020 IOHK +// License: Apache-2.0 + +/** + * Common types. + * + * @packageDocumentation + */ + +/** Type alias to indicate the path of a file. */ +export type FilePath = string; +/** Type alias to indicate the path of a directory. */ +export type DirPath = string; + +/** + * Use this with `.catch()` on promises where the error condition is + * already handled elsewhere (e.g. by an event or another promise). + * + * It will debug log the `Error` and a stack trace of the "floating + * promise". + */ +export function passthroughErrorLogger(err: Error): void { + console.debug( + 'Caught an unhandled promise rejection. The promise location is:\n' + + new Error().stack + + '\n\nThe error follows:' + ); + console.debug(err); +} + +/** + * Use this with `.catch()` on promises where the error is already + * handled elsewhere. This handler does nothing except prevent an + * eslint warning from appearing. + */ +export function ignorePromiseRejection(_: Error): void {} // eslint-disable-line + +/** + * Format GHC RTS options for the command line. + * + * See: https://downloads.haskell.org/ghc/8.10.7/docs/html/users_guide/runtime_control.html#setting-rts-options-on-the-command-line + */ +export function makeRtsArgs(rtsOpts?: string[]): string[] { + return Array.isArray(rtsOpts) && rtsOpts.length + ? ['+RTS'].concat(rtsOpts as string[]).concat(['-RTS']) + : []; +} diff --git a/source/main/cardano-launcher/loggers.ts b/source/main/cardano-launcher/loggers.ts new file mode 100644 index 0000000000..ab3617efab --- /dev/null +++ b/source/main/cardano-launcher/loggers.ts @@ -0,0 +1,37 @@ +import * as util from 'util'; +import * as fs from 'fs'; + +import chalk from 'chalk'; + +import { Logger } from './logging'; +export { Logger, LogFunc } from './logging'; + +export class StdioLogger implements Logger { + constructor( + readonly options: { fd: number; prefix?: string; timestamps?: boolean } = { + fd: process.stdout.fd, + } + ) {} + + protected write(level: string, message: string, param?: unknown) { + const suffix = param ? util.format(' %o', param) : ''; + const ts = this.options.timestamps + ? chalk.dim('[' + new Date().toISOString() + ']') + ' ' + : ''; + const prefix = this.options.prefix ? this.options.prefix + ' ' : ''; + const line = `${level} ${prefix}${ts}${message}${suffix}\n`; + fs.writeSync(this.options.fd, line); + } + debug(msg: string, param?: unknown) { + this.write(chalk.dim('DEBUG'), msg, param); + } + info(msg: string, param?: unknown) { + this.write(chalk.whiteBright('INFO '), msg, param); + } + error(msg: string, param?: unknown) { + this.write(chalk.red('ERROR'), msg, param); + } + log(msg: string, param?: unknown) { + this.write(' ', msg, param); + } +} diff --git a/source/main/cardano-launcher/logging.ts b/source/main/cardano-launcher/logging.ts new file mode 100644 index 0000000000..09b1ea477e --- /dev/null +++ b/source/main/cardano-launcher/logging.ts @@ -0,0 +1,53 @@ +// Copyright © 2020 IOHK +// License: Apache-2.0 + +/** + * Cheap and cheerful logging functions. + * Same as what is already in Daedalus. + * Plug in your own logging by implementing [[Logger]]. + * + * @packageDocumentation + /* + +/** + * Logging adapter. + */ +export interface Logger { + debug: LogFunc; + info: LogFunc; + error: LogFunc; + log: LogFunc; +} + +export type Severity = 'debug' | 'log' | 'info' | 'error'; + +/** + * Function which logs a message and optional object. + */ +export interface LogFunc { + (msg: string, param?: unknown): void; +} + +/** + * Create a new logger with a context name added. + * + * @param logger - existing logger. + * @param name - context to prepend. + * @return - a new logger. + */ +export function prependName(logger: Logger, name: string): Logger { + const prefix = (severity: Severity, msg: string, param?: unknown): void => { + const prefixed = `${name}: ${msg}`; + if (param) { + logger[severity](prefixed, param); + } else { + logger[severity](prefixed); + } + }; + return { + debug: (msg: string, param?: unknown): void => prefix('debug', msg, param), + log: (msg: string, param?: unknown): void => prefix('log', msg, param), + info: (msg: string, param?: unknown): void => prefix('info', msg, param), + error: (msg: string, param?: unknown): void => prefix('error', msg, param), + }; +} diff --git a/source/main/cardano-launcher/service.ts b/source/main/cardano-launcher/service.ts new file mode 100644 index 0000000000..7178af8a8a --- /dev/null +++ b/source/main/cardano-launcher/service.ts @@ -0,0 +1,431 @@ +// Copyright © 2020 IOHK +// License: Apache-2.0 + +/** + * Functions for starting and stopping an individual backend service. + * + * The important function is [[setupService]] which creates a [[Service]]. + * + * @packageDocumentation + */ + +import { spawn, ChildProcess, StdioOptions } from 'child_process'; +import { WriteStream } from 'fs'; +import { Writable } from 'stream'; +import _ from 'lodash'; +import { EventEmitter } from 'tsee'; + +import { Logger } from './logging'; + +export interface ServiceExitStatus { + /** Program name. */ + exe: string; + /** Process exit status code, if process exited itself. */ + code: number | null; + /** Signal name, if process was killed. */ + signal: string | null; + /** Error object, if process could not be started, or could not be killed. */ + err: Error | null; +} + +/** + * Produce an exit message from an exit status. + * @param res - exit status of service. + * @return a human readable exit message. + */ +export function serviceExitStatusMessage(res: ServiceExitStatus): string { + const reason = + typeof res.code === 'number' + ? `status ${res.code}` + : res.signal + ? `signal ${res.signal}` + : `error ${res.err}`; + + return `${res.exe} exited with ${reason}`; +} + +/** + * States for a launched process. The processes are not guaranteed to + * use all of these states. For example, a process may go directly + * from `Started` to `Stopped`. + */ +export enum ServiceStatus { + /** Initial state. */ + NotStarted, + /** Waiting for [[StartService]] info. */ + Starting, + /** Subprocess has been started and has a PID. */ + Started, + /** Caller has requested to stop the process. Now waiting for it to exit, or for the timeout to elapse. */ + Stopping, + /** Subprocess has exited or been killed. */ + Stopped, +} + +/** + * A launched process. + */ +export interface Service { + /** + * @return - a promise that will be fulfilled when the process has + * started. The returned PID is not guaranteed to be running. It + * may already have exited. + */ + start(): Promise; + + /** + * Stops the process. + * + * If the process doesn't exit within `timeoutSeconds`, it will be + * killed. The default timeout is [[defaultTimeoutSeconds]]. + * + * @param timeoutSeconds - How long to wait for the service to stop + * itself before killing it. If `0`, any running timeout kill be + * cancelled and the process killed immediately. + * @return a promise that will be fulfilled when the process has stopped. + */ + stop(timeoutSeconds?: number): Promise; + + /** + * Waits for the process to finish somehow -- whether it exits by + * itself, or exits due to `stop()` being called. + * + * @return a promise that will be fulfilled when the process has exited. + */ + waitForExit(): Promise; + + /** + * @return the status of this process. + */ + getStatus(): ServiceStatus; + + /** + * @return the ChildProcess running the service, or null if + * the service has not been started yet. + */ + getProcess(): ChildProcess | null; + + /** @return the 'StartService' configuration, or null if the service + * has not been configured yet. + */ + getConfig(): StartService | null; + + /** + * An [`EventEmitter`](https://nodejs.org/api/events.html#class-eventemitter) + * that can be used to register handlers when + * the process changes status. + * + * ```typescript + * launcher.walletService.events.on('statusChanged', (status: ServiceStatus) => { ... }); + * ``` + */ + events: ServiceEvents; +} + +/** Process ID */ +export type Pid = number; + +/** + * The type of events for [[Service]]. + */ +export class ServiceEvents extends EventEmitter<{ + statusChanged: (status: ServiceStatus) => void; +}> { + /** + * [[Launcher.walletService.events]] and + * [[Launcher.nodeService.events]] will emit this when their + * processes start or stop. + * @event + */ + statusChanged(status: ServiceStatus): void { + this.emit('statusChanged', status); + } +} + +/** + * How to stop a service. + */ +export enum ShutdownMethod { + /** Terminate the process with a signal (not clean on Windows) */ + Signal, + /** Terminate the process by closing its stdin. */ + CloseStdin, + /** Terminate the process by closing an inherited file descriptor. */ + CloseFD, +} + +/** + * The file number that [[Service]] will use for child process + * notification when [[ShutdownMethod.CloseFD]] is in use. + */ +export const cleanShutdownFD = 3; + +/** + * The value for the `timeoutSeconds` argument of [[Service.stop]]. + */ +export const defaultTimeoutSeconds = 300; + +/** + * Initialise a [[Service]] which can control the lifetime of a + * backend process. + * + * This does not start the process. Use [[Service.start]] for that. + * + * @param cfgPromise - a promise which will return the command to run. + * @param logger - logging object. + * @param childProcessLogWriteStream - WriteStream for writing the child process data events from stdout and stderr. + * @return A handle on the [[Service]]. + */ +export function setupService( + cfgPromise: Promise, + logger: Logger = console, + childProcessLogWriteStream?: WriteStream +): Service { + const events = new ServiceEvents(); + // What the current state is. + let status = ServiceStatus.NotStarted; + // Fulfilled promise of service command-line. + // This will always be defined if status > Starting. + let cfg: StartService; + // NodeJS child process object, or null if not running. + let proc: ChildProcess | null = null; + // Pipe file descriptor for clean shutdown, or null if not yet running. + let shutdownFD: number | null; + // When the service started (milliseconds since epoch) + let startTimeMs = 0; + // How the child process exited, or null if it hasn't yet exited. + let exitStatus: ServiceExitStatus | null; + // For cancelling the kill timeout. + let killTimer: NodeJS.Timeout | null = null; + let startPromise: Promise; + + const setStatus = (newStatus: ServiceStatus): void => { + logger.debug( + `setStatus ${ServiceStatus[status]} -> ${ServiceStatus[newStatus]}` + ); + status = newStatus; + if (status === ServiceStatus.Started) { + startTimeMs = Date.now(); + } + events.statusChanged(status); + }; + + const onStopped = ( + code: number | null = null, + signal: string | null = null, + err: Error | null = null + ): void => { + exitStatus = { exe: cfg.command, code, signal, err }; + logger.debug(`Service onStopped`, exitStatus); + if (killTimer) { + clearTimeout(killTimer); + killTimer = null; + } + proc = null; + setStatus(ServiceStatus.Stopped); + }; + + const doStart = async (): Promise => { + const envStr = _.map( + cfg.extraEnv, + (value, name) => `${name}=${value} ` + ).join(''); + const commandStr = `${envStr}${cfg.command} ${cfg.args.join(' ')}`; + logger.info(`Service.start: trying to start ${commandStr}`, cfg); + const stdOuts = childProcessLogWriteStream ? 'pipe' : 'inherit'; + const stdio = [ + cfg.shutdownMethod === ShutdownMethod.CloseStdin ? 'pipe' : 'ignore', + stdOuts, + stdOuts, + ].concat( + cfg.shutdownMethod === ShutdownMethod.CloseFD ? ['pipe'] : [] + ) as StdioOptions; + const cwd = cfg.cwd ? { cwd: cfg.cwd } : {}; + const env = cfg.extraEnv + ? Object.assign({}, process.env, cfg.extraEnv) + : process.env; + const options = Object.assign({ stdio }, cwd, { env }); + try { + proc = spawn(cfg.command, cfg.args, options); + } catch (err) { + logger.error(`Service.start: child_process.spawn() failed: ${err}`); + logger.error( + `Service.start: child_process.spawn(${cfg.command}, ${cfg.args.join( + ' ' + )}, ...)`, + options + ); + throw err; + } + if (cfg.shutdownMethod === ShutdownMethod.CloseStdin) { + // corresponds to first element of `stdio` above + shutdownFD = 0; + } else if (cfg.shutdownMethod === ShutdownMethod.CloseFD) { + // corresponds to last element of `stdio` above + shutdownFD = cleanShutdownFD; + } + setStatus(ServiceStatus.Started); + proc.on('exit', (code, signal) => { + onStopped(code, signal); + }); + proc.on('error', (err) => { + logger.error(`Service.start: child_process failed: ${err}`); + onStopped(null, null, err); + }); + if (proc.stdout && proc.stderr && childProcessLogWriteStream) { + proc.stdout.on('data', (data) => { + childProcessLogWriteStream.write(data); + }); + proc.stderr.on('data', (data) => { + childProcessLogWriteStream.write(data); + }); + } + return proc.pid as number; + }; + + const doStop = (timeoutSeconds: number): void => { + logger.info(`Service.stop: trying to stop ${cfg.command}`, cfg); + setStatus(ServiceStatus.Stopping); + if (proc) { + if (cfg.shutdownMethod === ShutdownMethod.Signal) { + proc.kill('SIGTERM'); + } else if (shutdownFD !== null && proc.stdio[shutdownFD]) { + const stream = proc.stdio[shutdownFD] as Writable; + const closeFD = (): void => { + stream.end(); + }; + + // Allow the service one second after startup to begin reading from its + // shutdownFD, before closing the shutdown FD. + const shutdownFDGracePeriodMs = 1000; + const grace = startTimeMs - Date.now() + shutdownFDGracePeriodMs; + if (grace > 0) { + setTimeout(closeFD, grace); + } else { + closeFD(); + } + } + } + killTimer = setTimeout(() => { + if (proc) { + logger.info( + `Service.stop: timed out after ${timeoutSeconds} seconds. Killing process ${proc.pid}.` + ); + proc.kill('SIGKILL'); + } + }, timeoutSeconds * 1000); + }; + + const waitForStop = (): Promise => + new Promise((resolve) => { + logger.debug(`Service.stop: waiting for ServiceStatus.Stopped`); + events.on('statusChanged', (status) => { + if (status === ServiceStatus.Stopped && exitStatus) { + resolve(exitStatus); + } + }); + }); + + const waitForExit = (): Promise => { + const defaultExitStatus = { + exe: cfg ? cfg.command : '', + code: null, + signal: null, + err: null, + }; + switch (status) { + case ServiceStatus.NotStarted: + case ServiceStatus.Starting: + return new Promise((resolve) => { + status = ServiceStatus.Stopped; + exitStatus = defaultExitStatus; + resolve(exitStatus); + }); + case ServiceStatus.Started: + return waitForStop(); + case ServiceStatus.Stopping: + return waitForStop(); + case ServiceStatus.Stopped: + return new Promise((resolve) => + resolve(exitStatus || defaultExitStatus) + ); + } + }; + + return { + start: async (): Promise => { + switch (status) { + case ServiceStatus.NotStarted: + setStatus(ServiceStatus.Starting); + startPromise = cfgPromise.then((theCfg) => { + cfg = theCfg; + return doStart(); + }); + return startPromise; + case ServiceStatus.Starting: + logger.info(`Service.start: already starting`); + return startPromise; + case ServiceStatus.Started: + logger.info(`Service.start: already started`); + return proc?.pid || -1; + case ServiceStatus.Stopping: + logger.info(`Service.start: cannot start - already stopping`); + return -1; + case ServiceStatus.Stopped: + logger.info(`Service.start: cannot start - already stopped`); + return -1; + } + }, + stop: async ( + timeoutSeconds = defaultTimeoutSeconds + ): Promise => { + switch (status) { + case ServiceStatus.NotStarted: + case ServiceStatus.Starting: + logger.info(`Service.stop: cannot stop - never started`); + break; + case ServiceStatus.Started: + doStop(timeoutSeconds); + break; + case ServiceStatus.Stopping: + if (timeoutSeconds === 0 && proc) { + logger.info( + `Service.stop: was already stopping, but will now kill process ${proc.pid} immediately` + ); + proc.kill('SIGKILL'); + } else { + logger.info(`Service.stop: already stopping`); + } + break; + case ServiceStatus.Stopped: + logger.info(`Service.stop: already stopped`); + break; + } + return waitForExit(); + }, + waitForExit, + getStatus: (): ServiceStatus => status, + getProcess: (): ChildProcess | null => proc, + getConfig: (): StartService | null => cfg, + events, + }; +} + +/** + * Describes the command to run for the service. + */ +export interface StartService { + /** Program name. Will be searched for in `PATH`. */ + command: string; + /** Command-line arguments. */ + args: string[]; + /** Directory to start program in. Helpful if it outputs files. */ + cwd?: string; + /** Additional environment variables to set, on top of the current process environment. */ + extraEnv?: { [propName: string]: string }; + /** + * Whether this service supports the clean shutdown method documented in + * `docs/windows-clean-shutdown.md`. + */ + shutdownMethod: ShutdownMethod; +} diff --git a/source/main/cardano-launcher/tls.ts b/source/main/cardano-launcher/tls.ts new file mode 100644 index 0000000000..848cfb25d0 --- /dev/null +++ b/source/main/cardano-launcher/tls.ts @@ -0,0 +1,16 @@ +// Copyright © 2020 IOHK +// License: Apache-2.0 + +/** + * Server TLS configuration + * + * @packageDocumentation + */ + +import { FilePath } from './common'; + +export interface ServerTlsConfiguration { + caCert: FilePath; + svCert: FilePath; + svKey: FilePath; +} diff --git a/source/main/cardano/CardanoNode.ts b/source/main/cardano/CardanoNode.ts index 0f8e75ede2..bfa5eaaa8b 100644 --- a/source/main/cardano/CardanoNode.ts +++ b/source/main/cardano/CardanoNode.ts @@ -2,10 +2,10 @@ import Store from 'electron-store'; import { spawn, exec } from 'child_process'; import type { ChildProcess } from 'child_process'; import type { WriteStream } from 'fs'; -import type { Launcher } from 'cardano-launcher'; import { get, toInteger } from 'lodash'; import moment from 'moment'; import rfs from 'rotating-file-stream'; +import { Launcher } from '../cardano-launcher/cardanoLauncher'; import { environment } from '../environment'; import { deriveProcessNames, @@ -875,7 +875,6 @@ export class CardanoNode { }); this._state = state; - this._actions.broadcastStateChange(state); switch (state) { @@ -1002,9 +1001,8 @@ export class CardanoNode { }; _ensurePreviousCardanoNodeIsNotRunning = async (): Promise => { const { _log } = this; - const previousPID: number | null | undefined = await this._retrieveData( - PREVIOUS_CARDANO_PID - ); + const previousPID: number | null | undefined = + await this._retrieveData(PREVIOUS_CARDANO_PID); _log.info( 'CardanoNode: checking if previous cardano-node process is still running', diff --git a/source/main/cardano/CardanoWalletLauncher.ts b/source/main/cardano/CardanoWalletLauncher.ts index c8890f332a..ccdb713fa1 100644 --- a/source/main/cardano/CardanoWalletLauncher.ts +++ b/source/main/cardano/CardanoWalletLauncher.ts @@ -1,11 +1,8 @@ import { merge } from 'lodash'; import path from 'path'; -import * as fs from 'fs-extra'; import type { WriteStream } from 'fs'; -import * as cardanoLauncher from 'cardano-launcher'; -import type { Launcher } from 'cardano-launcher'; +import { Launcher } from '../cardano-launcher/cardanoLauncher'; import type { NodeConfig } from '../config'; -import { environment } from '../environment'; import { FALLBACK_TOKEN_METADATA_SERVER_URL, MOCK_TOKEN_METADATA_SERVER_URL, @@ -169,5 +166,5 @@ export async function CardanoWalletLauncher( launcherConfig, }); // @ts-ignore ts-migrate(2345) FIXME: Argument of type '{ networkName: string; stateDir:... Remove this comment to see the full error message - return new cardanoLauncher.Launcher(launcherConfig, logger); + return new Launcher(launcherConfig, logger); } diff --git a/source/main/cardano/utils.ts b/source/main/cardano/utils.ts index 1368590809..314614ec05 100644 --- a/source/main/cardano/utils.ts +++ b/source/main/cardano/utils.ts @@ -399,7 +399,7 @@ const showExportWalletsWarning = ( const translations = require(`../locales/${locale}`); const translation = getTranslation(translations, 'dialog'); - const exportWalletsDialogOptions = { + const exportWalletsDialogOptions: Electron.MessageBoxOptions = { buttons: [ translation('exportWalletsWarning.confirm'), translation('exportWalletsWarning.cancel'), diff --git a/source/main/index.ts b/source/main/index.ts index 20d22cca4c..e397cf724f 100644 --- a/source/main/index.ts +++ b/source/main/index.ts @@ -281,7 +281,7 @@ const onAppReady = async () => { return safeExitWithCode(0); } - const exitSelfnodeDialogOptions = { + const exitSelfnodeDialogOptions: Electron.MessageBoxOptions = { buttons: ['Yes', 'No'], type: 'warning', title: 'Daedalus is about to close', diff --git a/source/main/ipc/generateCsvChannel.ts b/source/main/ipc/generateCsvChannel.ts index b09037cd72..05a8dceb1f 100644 --- a/source/main/ipc/generateCsvChannel.ts +++ b/source/main/ipc/generateCsvChannel.ts @@ -1,5 +1,5 @@ import fs from 'fs'; -import csvStringify from 'csv-stringify'; +import { stringify } from 'csv-stringify'; import { MainIpcChannel } from './lib/MainIpcChannel'; import { GENERATE_CSV_CHANNEL } from '../../common/ipc/api'; import type { @@ -8,16 +8,14 @@ import type { } from '../../common/ipc/api'; export const generateCsvChannel: // IpcChannel -MainIpcChannel< - GenerateCsvRendererRequest, - GenerateCsvMainResponse -> = new MainIpcChannel(GENERATE_CSV_CHANNEL); +MainIpcChannel = + new MainIpcChannel(GENERATE_CSV_CHANNEL); export const handleRewardsCsvRequests = () => { generateCsvChannel.onReceive( (request: GenerateCsvRendererRequest) => new Promise((resolve, reject) => { const { fileContent, filePath } = request; - csvStringify(fileContent, (csvErr, output) => { + stringify(fileContent, (csvErr, output) => { if (csvErr) { return reject(csvErr); } diff --git a/source/main/ipc/getHardwareWalletChannel.ts b/source/main/ipc/getHardwareWalletChannel.ts index b90d6b7155..b8a69c545f 100644 --- a/source/main/ipc/getHardwareWalletChannel.ts +++ b/source/main/ipc/getHardwareWalletChannel.ts @@ -33,7 +33,7 @@ import { import { HardwareWalletChannels } from './createHardwareWalletIPCChannels'; import { Device } from './hardwareWallets/ledger/deviceDetection/types'; import { DeviceDetectionPayload } from './hardwareWallets/ledger/deviceDetection/deviceDetection'; -import { initTrezorConnect, reinitTrezorConnect } from '../trezor/connection'; +import { initTrezorConnect } from '../trezor/connection'; type ListenerType = { unsubscribe: (...args: Array) => any; @@ -181,7 +181,7 @@ export const handleHardwareWalletRequests = async ( TrezorConnect.removeAllListeners(); // Initialize new device listeners TrezorConnect.on(UI_EVENT, (event) => { - logger.info('[TREZOR-CONNECT] Received UI_EVENT: ' + event.type); + logger.info(`[TREZOR-CONNECT] Received UI_EVENT: ${event.type}`); if (event.type === UI.REQUEST_PASSPHRASE) { // ui-request_passphrase @@ -202,12 +202,11 @@ export const handleHardwareWalletRequests = async ( } }); TrezorConnect.on(TRANSPORT_EVENT, (event) => { + logger.info( + '[TREZOR-CONNECT] Received TRANSPORT_EVENT: transport-error', + event.payload + ); if (event.type === TRANSPORT.ERROR) { - logger.info( - '[TREZOR-CONNECT] Received TRANSPORT_EVENT: transport-error', - event.payload - ); - // Send Transport error to Renderer getHardwareWalletConnectionChannel.send( { @@ -222,7 +221,7 @@ export const handleHardwareWalletRequests = async ( } }); TrezorConnect.on(DEVICE_EVENT, (event) => { - logger.info('[TREZOR-CONNECT] Received DEVICE_EVENT: ' + event.type); + logger.info(`[TREZOR-CONNECT] Received DEVICE_EVENT: ${event.type}`); const connectionChanged = event.type === DEVICE.CONNECT || @@ -755,12 +754,14 @@ export const handleHardwareWalletRequests = async ( const { path, isTrezor, devicePath } = params; try { + logger.info(`[TREZOR-CONNECT] isTrezor=${isTrezor}`); + if (isTrezor) { // We re-initialize the Trezor Connect session to give the user the chance to provide // a different passphrase, in case they want to switch to a different // hidden wallet or just if they provided a wrong one. - await reinitTrezorConnect(); - resetTrezorListeners(); + // await reinitTrezorConnect(); + // resetTrezorListeners(); logger.info('[TREZOR-CONNECT] Calling TrezorConnect.getFeatures()'); const deviceFeatures = await TrezorConnect.getFeatures(); diff --git a/source/main/ipc/hardwareWallets/ledger/deviceDetection/deviceDetection.ts b/source/main/ipc/hardwareWallets/ledger/deviceDetection/deviceDetection.ts index 6485eb8233..4c5fab9813 100644 --- a/source/main/ipc/hardwareWallets/ledger/deviceDetection/deviceDetection.ts +++ b/source/main/ipc/hardwareWallets/ledger/deviceDetection/deviceDetection.ts @@ -4,7 +4,7 @@ import { logger } from '../../../../utils/logging'; import { DeviceTracker } from './deviceTracker'; import { detectDevices as useEventDrivenDetection } from './eventDrivenDetection'; import { detectDevices as usePollingDrivenDetection } from './pollingDrivenDetection'; -import { Detector, TrackedDevice, DectorUnsubscriber } from './types'; +import { TrackedDevice, DectorUnsubscriber } from './types'; export type DeviceDetectionPayload = { type: 'add' | 'remove'; diff --git a/source/main/preload.ts b/source/main/preload.ts index b33abaf456..f04d15708a 100644 --- a/source/main/preload.ts +++ b/source/main/preload.ts @@ -40,12 +40,6 @@ process.once('loaded', () => { smashUrl, }); - // Expose require for Spectron! - if (_process.env.NODE_ENV === 'test') { - // @ts-ignore - global.spectronRequire = __non_webpack_require__; // eslint-disable-line - } - // ESLint will warn about any use of eval(), even this one if (environment.isProduction) { // eslint-disable-next-line no-eval diff --git a/source/main/trezor/connection.ts b/source/main/trezor/connection.ts index 0ca6978367..661aa8ba8e 100644 --- a/source/main/trezor/connection.ts +++ b/source/main/trezor/connection.ts @@ -3,18 +3,21 @@ import { logger } from '../utils/logging'; import { manifest } from './manifest'; export const initTrezorConnect = async () => { - try { - await TrezorConnect.init({ - popup: false, // render your own UI - webusb: false, // webusb is not supported in electron - debug: process.env.DEBUG_TREZOR === 'true', - manifest, + TrezorConnect.init({ + popup: false, // render your own UI + debug: false, // see what's going on inside connect + // lazyLoad: true, // set to "false" (default) if you want to start communication with bridge on application start (and detect connected device right away) + // set it to "true", then trezor-connect will not be initialized until you call some TrezorConnect.method() + // this is useful when you don't know if you are dealing with Trezor user + manifest, + transports: ['BridgeTransport'], + }) + .then(() => { + logger.info('[HW-DEBUG] TrezorConnect is ready!'); + }) + .catch((error) => { + logger.error(`[HW-DEBUG] TrezorConnect init error:`, error); }); - - logger.info('[TREZOR-CONNECT] Called TrezorConnect.init()'); - } catch (error) { - logger.info('[TREZOR-CONNECT] Failed to call TrezorConnect.init()'); - } }; export const reinitTrezorConnect = async () => { @@ -23,7 +26,10 @@ export const reinitTrezorConnect = async () => { await TrezorConnect.dispose(); } catch (error) { // ignore any TrezorConnect instance disposal errors - logger.info('[TREZOR-CONNECT] Failed to call TrezorConnect.dispose()'); + logger.error( + '[TREZOR-CONNECT] Failed to call TrezorConnect.dispose()', + error + ); } return initTrezorConnect(); diff --git a/source/main/utils/buildAppMenus.ts b/source/main/utils/buildAppMenus.ts index 31ee34b3d7..1364c88119 100644 --- a/source/main/utils/buildAppMenus.ts +++ b/source/main/utils/buildAppMenus.ts @@ -80,7 +80,7 @@ export const buildAppMenus = async ( const toggleBlankScreenFix = async (item) => { const translation = getTranslation(translations, 'menu'); - const blankScreenFixDialogOptions = { + const blankScreenFixDialogOptions: Electron.MessageBoxOptions = { buttons: [ translation('helpSupport.blankScreenFixDialogConfirm'), translation('helpSupport.blankScreenFixDialogCancel'), diff --git a/source/main/utils/config.ts b/source/main/utils/config.ts index fbfc40891f..ab398cffc8 100644 --- a/source/main/utils/config.ts +++ b/source/main/utils/config.ts @@ -61,7 +61,7 @@ export const readLauncherConfig = ( const parsed = yamljs.parse(inputYaml); const finalYaml = recurseReplace(parsed); - if (finalYaml === null || finalYaml === []) { + if (!finalYaml || finalYaml.length === 0) { throw new Error('Daedalus requires a valid launcher config file to work'); } diff --git a/source/main/utils/ensureDirectoryExists.ts b/source/main/utils/ensureDirectoryExists.ts index 18aef2bb5e..23eef509b1 100644 --- a/source/main/utils/ensureDirectoryExists.ts +++ b/source/main/utils/ensureDirectoryExists.ts @@ -1,4 +1,4 @@ -import mkdirp from 'mkdirp'; +import { mkdirp } from 'mkdirp'; import fs from 'fs'; export default (filepath: string) => { diff --git a/source/main/utils/handleCheckBlockReplayProgress.ts b/source/main/utils/handleCheckBlockReplayProgress.ts index 445d0197c1..de5bc9772f 100644 --- a/source/main/utils/handleCheckBlockReplayProgress.ts +++ b/source/main/utils/handleCheckBlockReplayProgress.ts @@ -10,7 +10,6 @@ import { } from '../../common/types/cardano-node.types'; import { isItFreshLog } from './blockSyncProgressHelpers'; import { environment } from '../environment'; -import { logger } from './logging'; const blockKeyword = 'Replayed block'; const validatingChunkKeyword = 'Validating chunk'; diff --git a/source/main/utils/handleDiskSpace.ts b/source/main/utils/handleDiskSpace.ts index c18618ed55..5df1601b36 100644 --- a/source/main/utils/handleDiskSpace.ts +++ b/source/main/utils/handleDiskSpace.ts @@ -171,7 +171,9 @@ export const handleDiskSpace = ( '[DISK-SPACE-DEBUG] restart cardano node after freeing up disk space' ); if (cardanoNode._startupTries > 0) await cardanoNode.restart(); - else await cardanoNode.start(); + else { + await cardanoNode.start(); + } response.hadNotEnoughSpaceLeft = false; } catch (error) { logger.error( diff --git a/source/main/utils/logging.ts b/source/main/utils/logging.ts index a8d7cfe3b1..194da1ac00 100644 --- a/source/main/utils/logging.ts +++ b/source/main/utils/logging.ts @@ -23,15 +23,15 @@ const environmentData = { version, }; -const logToLevel = (level: string) => ( - message: string, - data: Record | null | undefined -) => - log[level](formatContext({ ...messageContext, level }), { - message, - data: toJS(data), - environmentData, - }); +const logToLevel = + (level: string) => + (message: string, data: Record | null | undefined) => + ['error', 'warn'].includes(level) && + log[level](formatContext({ ...messageContext, level }), { + message, + data: toJS(data), + environmentData, + }); export const logger: Logger = { debug: logToLevel('debug'), diff --git a/source/main/utils/safeExitWithCode.ts b/source/main/utils/safeExitWithCode.ts index de1f520478..fe8c4a9c2d 100644 --- a/source/main/utils/safeExitWithCode.ts +++ b/source/main/utils/safeExitWithCode.ts @@ -2,15 +2,18 @@ import { app } from 'electron'; import log from 'electron-log-daedalus'; export const safeExitWithCode = (exitCode = 0) => { - const { file } = log.transports; - // Prevent electron-log from writing to stream - file.level = false; - // Flush the stream to the log file and exit afterwards. - // https://nodejs.org/api/stream.html#stream_writable_end_chunk_encoding_callback - file.stream.end('', 'utf8', () => { - app.releaseSingleInstanceLock(); - app.exit(exitCode); - }); + if (!!log?.transports?.file?.stream) { + const { file } = log.transports; + // Prevent electron-log from writing to stream + file.level = false; + // Flush the stream to the log file and exit afterwards. + // https://nodejs.org/api/stream.html#stream_writable_end_chunk_encoding_callback + file.stream?.end('', 'utf8', () => { + app.releaseSingleInstanceLock(); + app.exit(exitCode); + }); + } + app.exit(exitCode); }; export const relaunch = () => { const { file } = log.transports; diff --git a/source/main/utils/setupLogging.ts b/source/main/utils/setupLogging.ts index de382842d5..a3d0971775 100644 --- a/source/main/utils/setupLogging.ts +++ b/source/main/utils/setupLogging.ts @@ -21,7 +21,7 @@ import type { const isTest = process.env.NODE_ENV === 'test'; const isDev = process.env.NODE_ENV === 'development'; export const setupLogging = () => { - const logFilePath = path.join(pubLogsFolderPath, 'Daedalus.json'); + const logFilePath = path?.join(pubLogsFolderPath, 'Daedalus.json'); ensureDirectoryExists(pubLogsFolderPath); rimraf.sync(path.join(pubLogsFolderPath, './Daedalus.*')); log.transports.console.level = isTest ? 'error' : 'info'; @@ -122,13 +122,8 @@ export const logStateSnapshot = ( ): MessageBody => { const { ...data } = props; const { currentTime: at, systemInfo, coreInfo } = data; - const { - platform, - platformVersion, - cpu, - ram, - availableDiskSpace, - } = systemInfo; + const { platform, platformVersion, cpu, ram, availableDiskSpace } = + systemInfo; const { daedalusVersion, daedalusProcessID, diff --git a/source/main/webpack.config.js b/source/main/webpack.config.js index 2b327de944..6bbc4cea9c 100644 --- a/source/main/webpack.config.js +++ b/source/main/webpack.config.js @@ -7,7 +7,7 @@ class ManageElectronProcessPlugin { _process = null; _shouldRestart = false; start() { - this._process = spawn('yarn', ['electron', '.'], { + this._process = spawn('yarn', ['electron', '--trace-deprecation', '.'], { stdio: 'inherit', shell: true, }); diff --git a/source/main/windows/windowBounds.ts b/source/main/windows/windowBounds.ts index 9206883f2a..33272d8e66 100644 --- a/source/main/windows/windowBounds.ts +++ b/source/main/windows/windowBounds.ts @@ -1,4 +1,4 @@ -import { debounce } from 'lodash-es/function'; +import { debounce } from 'lodash'; import { STORAGE_KEYS, STORAGE_TYPES, diff --git a/source/renderer/app/App.tsx b/source/renderer/app/App.tsx old mode 100755 new mode 100644 index 718fce2b5d..cf41c248ff --- a/source/renderer/app/App.tsx +++ b/source/renderer/app/App.tsx @@ -1,9 +1,9 @@ import React, { Component, Fragment } from 'react'; import { Provider, observer } from 'mobx-react'; import { History } from 'history'; -import { ThemeProvider } from 'react-polymorph/lib/components/ThemeProvider'; -import { SimpleSkins } from 'react-polymorph/lib/skins/simple'; -import { SimpleDefaults } from 'react-polymorph/lib/themes/simple'; +import { ThemeProvider } from '@react-polymorph/components/ThemeProvider'; +import { SimpleSkins } from '@react-polymorph/skins/simple'; +import { SimpleDefaults } from '@react-polymorph/themes/simple'; import { Router } from 'react-router-dom'; import { IntlProvider } from 'react-intl'; import { Routes } from './Routes'; @@ -22,10 +22,7 @@ import NewsFeedContainer from './containers/news/NewsFeedContainer'; import ToggleRTSFlagsDialogContainer from './containers/knownIssues/ToggleRTSFlagsDialogContainer'; import RTSFlagsRecommendationOverlayContainer from './containers/knownIssues/RTSFlagsRecommendationOverlayContainer'; import { MenuUpdater } from './containers/MenuUpdater'; -import { AnalyticsProvider } from './components/analytics'; -import { AnalyticsTracker } from './analytics'; -@observer class App extends Component<{ stores: StoresMap; actions: ActionsMap; @@ -59,7 +56,7 @@ class App extends Component<{ } return ( - + <> {/* @ts-ignore ts-migrate(2769) FIXME: No overload matches this call. */} @@ -77,7 +74,7 @@ class App extends Component<{ messages: translations[locale], }} > - + <> @@ -99,13 +96,13 @@ class App extends Component<{ , , ]} - + - + ); } } -export default App; +export default observer(App); diff --git a/source/renderer/app/Routes.tsx b/source/renderer/app/Routes.tsx index 7f6129b635..312563d143 100644 --- a/source/renderer/app/Routes.tsx +++ b/source/renderer/app/Routes.tsx @@ -1,5 +1,11 @@ import React from 'react'; -import { Redirect, Route, Switch, withRouter } from 'react-router-dom'; +import { + Redirect, + Route, + RouteComponentProps, + Switch, + withRouter, +} from 'react-router-dom'; import { ROUTES } from './routes-config'; // PAGES import Root from './containers/Root'; @@ -37,7 +43,7 @@ import { IS_STAKING_INFO_PAGE_AVAILABLE } from './config/stakingConfig'; import AnalyticsConsentPage from './containers/profile/AnalyticsConsentPage'; import TrackedRoute from './analytics/TrackedRoute'; -export const Routes = withRouter(() => ( +const RoutesComponent: React.FC = () => ( @@ -213,4 +219,7 @@ export const Routes = withRouter(() => ( -)); +); + +// Wrap your component with withRouter +export const Routes = withRouter(RoutesComponent as any); diff --git a/source/renderer/app/analytics/NoopAnalyticsTracker.ts b/source/renderer/app/analytics/NoopAnalyticsTracker.ts index f9daa5e01d..53b18b0613 100644 --- a/source/renderer/app/analytics/NoopAnalyticsTracker.ts +++ b/source/renderer/app/analytics/NoopAnalyticsTracker.ts @@ -1,3 +1,4 @@ +/* eslint-disable */ import { AnalyticsTracker } from '.'; class NoopAnalyticsTracker implements AnalyticsTracker { diff --git a/source/renderer/app/analytics/TrackedRoute.tsx b/source/renderer/app/analytics/TrackedRoute.tsx index 8696223007..ecda3b3fde 100644 --- a/source/renderer/app/analytics/TrackedRoute.tsx +++ b/source/renderer/app/analytics/TrackedRoute.tsx @@ -1,4 +1,4 @@ -import React, { FC, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { matchPath, Route, RouteProps } from 'react-router'; import { useAnalytics } from '../components/analytics'; diff --git a/source/renderer/app/api/api.ts b/source/renderer/app/api/api.ts index d88c4a64e5..2e3e287432 100644 --- a/source/renderer/app/api/api.ts +++ b/source/renderer/app/api/api.ts @@ -152,7 +152,6 @@ import { // Wallets Types import { AdaWallet, - AdaWallets, CreateHardwareWalletRequest, LegacyAdaWallet, LegacyAdaWallets, @@ -205,7 +204,6 @@ import type { import type { CreateVotingRegistrationRequest, CreateWalletSignatureRequest, - GetCatalystFundResponse, CatalystFund, } from './voting/types'; import type { StakePoolProps } from '../domains/StakePool'; @@ -664,7 +662,8 @@ export default class AdaApi { logger.debug('AdaApi::getAssets success', { assets: response, }); - const assetsLocalData = await global.daedalus.api.localStorage.getAssetsLocalData(); + const assetsLocalData = + await global.daedalus.api.localStorage.getAssetsLocalData(); logger.debug('AdaApi::getAssetsLocalData success', { assetsLocalData, }); @@ -905,13 +904,11 @@ export default class AdaApi { }) .where('code', 'transaction_is_too_big'); - const { - requiresAdaToRemainToSupportNativeTokens, - adaToProceed, - } = doesWalletRequireAdaToRemainToSupportTokens( - error, - hasAssetsRemainingAfterTransaction - ); + const { requiresAdaToRemainToSupportNativeTokens, adaToProceed } = + doesWalletRequireAdaToRemainToSupportTokens( + error, + hasAssetsRemainingAfterTransaction + ); if (requiresAdaToRemainToSupportNativeTokens) { apiError.set('cannotLeaveWalletEmpty', true, { adaAmount: adaToProceed, @@ -1563,9 +1560,8 @@ export default class AdaApi { getCurrencyList = async (): Promise => { try { const apiResponse = await getCurrencyList(); - const response: GetCurrencyListResponse = currencyConfig.responses.list( - apiResponse - ); + const response: GetCurrencyListResponse = + currencyConfig.responses.list(apiResponse); logger.debug('AdaApi::getCurrencyList success', { response, }); @@ -1582,9 +1578,8 @@ export default class AdaApi { ): Promise => { try { const apiResponse = await getCurrencyRate(currency); - const response: GetCurrencyRateResponse = currencyConfig.responses.rate( - apiResponse - ); + const response: GetCurrencyRateResponse = + currencyConfig.responses.rate(apiResponse); logger.debug('AdaApi::getCurrencyRate success', { response, }); @@ -2090,9 +2085,8 @@ export default class AdaApi { logger.debug('AdaApi::getSmashSettings called'); try { - const { - pool_metadata_source: poolMetadataSource, - } = await getSmashSettings(this.config); + const { pool_metadata_source: poolMetadataSource } = + await getSmashSettings(this.config); logger.debug('AdaApi::getSmashSettings success', { poolMetadataSource, }); @@ -2116,12 +2110,8 @@ export default class AdaApi { return true; } - const { - health, - }: CheckSmashServerHealthApiResponse = await checkSmashServerHealth( - this.config, - url - ); + const { health }: CheckSmashServerHealthApiResponse = + await checkSmashServerHealth(this.config, url); const isValid = health === SMASH_SERVER_STATUSES.AVAILABLE; logger.debug('AdaApi::checkSmashServerIsValid success', { isValid, @@ -2144,9 +2134,8 @@ export default class AdaApi { }); try { - const isSmashServerValid = await this.checkSmashServerIsValid( - poolMetadataSource - ); + const isSmashServerValid = + await this.checkSmashServerIsValid(poolMetadataSource); if (!isSmashServerValid) { const error: ErrorType = { @@ -2334,12 +2323,10 @@ export default class AdaApi { }); try { - const response: TransferFundsCalculateFeeApiResponse = await transferFundsCalculateFee( - this.config, - { + const response: TransferFundsCalculateFeeApiResponse = + await transferFundsCalculateFee(this.config, { sourceWalletId, - } - ); + }); logger.debug('AdaApi::transferFundsCalculateFee success', { response, }); @@ -2541,9 +2528,8 @@ export default class AdaApi { logger.debug('AdaApi::getNetworkParameters called'); try { - const networkParameters: GetNetworkParametersApiResponse = await getNetworkParameters( - this.config - ); + const networkParameters: GetNetworkParametersApiResponse = + await getNetworkParameters(this.config); logger.debug('AdaApi::getNetworkParameters success', { networkParameters, }); diff --git a/source/renderer/app/api/utils/patchAdaApi.ts b/source/renderer/app/api/utils/patchAdaApi.ts index b25bfeaede..31558345ad 100644 --- a/source/renderer/app/api/utils/patchAdaApi.ts +++ b/source/renderer/app/api/utils/patchAdaApi.ts @@ -1,6 +1,6 @@ import { get, map } from 'lodash'; import { action } from 'mobx'; -import BigNumber from 'bignumber.js/bignumber'; +import { BigNumber } from 'bignumber.js'; import AdaApi from '../api'; import { getNetworkInfo } from '../network/requests/getNetworkInfo'; import { logger } from '../../utils/logging'; diff --git a/source/renderer/app/assets/fonts/Montserrat-Medium.ttf b/source/renderer/app/assets/fonts/Montserrat-Medium.ttf old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/paper-wallet-certificate/template-blue.png b/source/renderer/app/assets/images/paper-wallet-certificate/template-blue.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/paper-wallet-certificate/template-brown.png b/source/renderer/app/assets/images/paper-wallet-certificate/template-brown.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/paper-wallet-certificate/template-green.png b/source/renderer/app/assets/images/paper-wallet-certificate/template-green.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/paper-wallet-certificate/template-red.png b/source/renderer/app/assets/images/paper-wallet-certificate/template-red.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/paper-wallet-certificate/template-violet.png b/source/renderer/app/assets/images/paper-wallet-certificate/template-violet.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/paper-wallet-certificate/template-yellow.png b/source/renderer/app/assets/images/paper-wallet-certificate/template-yellow.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/cardano.png b/source/renderer/app/assets/images/themes/cardano.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/dark-blue.png b/source/renderer/app/assets/images/themes/dark-blue.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/dark-cardano.png b/source/renderer/app/assets/images/themes/dark-cardano.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/flight-candidate.png b/source/renderer/app/assets/images/themes/flight-candidate.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/incentivized-testnet.png b/source/renderer/app/assets/images/themes/incentivized-testnet.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/light-blue.png b/source/renderer/app/assets/images/themes/light-blue.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/shelley-testnet.png b/source/renderer/app/assets/images/themes/shelley-testnet.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/white.png b/source/renderer/app/assets/images/themes/white.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/assets/images/themes/yellow.png b/source/renderer/app/assets/images/themes/yellow.png old mode 100755 new mode 100644 diff --git a/source/renderer/app/components/analytics/AnalyticsProvider.tsx b/source/renderer/app/components/analytics/AnalyticsProvider.tsx index 89e22e336c..e446debdc7 100644 --- a/source/renderer/app/components/analytics/AnalyticsProvider.tsx +++ b/source/renderer/app/components/analytics/AnalyticsProvider.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import React from 'react'; import { AnalyticsContext } from './AnalyticsContext'; import { AnalyticsTracker } from '../../analytics'; diff --git a/source/renderer/app/components/appUpdate/AppUpdateOverlay.tsx b/source/renderer/app/components/appUpdate/AppUpdateOverlay.tsx index e41bf0e22e..10913115d4 100644 --- a/source/renderer/app/components/appUpdate/AppUpdateOverlay.tsx +++ b/source/renderer/app/components/appUpdate/AppUpdateOverlay.tsx @@ -1,16 +1,18 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import SVGInline from 'react-svg-inline'; import classnames from 'classnames'; import { get } from 'lodash'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { ButtonSpinnerSkin } from 'react-polymorph/lib/skins/simple/ButtonSpinnerSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { ButtonSpinnerSkin } from '@react-polymorph/skins/simple/ButtonSpinnerSkin'; import ReactMarkdown from 'react-markdown'; import News from '../../domains/News'; import styles from './AppUpdateOverlay.scss'; @@ -138,7 +140,6 @@ type State = { areTermsOfUseAccepted: boolean; }; -@observer class AppUpdateOverlay extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -380,4 +381,4 @@ class AppUpdateOverlay extends Component { } } -export default AppUpdateOverlay; +export default observer(AppUpdateOverlay); diff --git a/source/renderer/app/components/assets/Asset.spec.tsx b/source/renderer/app/components/assets/Asset.spec.tsx deleted file mode 100644 index ad78d00306..0000000000 --- a/source/renderer/app/components/assets/Asset.spec.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import BigNumber from 'bignumber.js'; -import { render, screen, cleanup } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Asset from './Asset'; -import { TestDecorator } from '../../../../../tests/_utils/TestDecorator'; - -const assetWithMetadataName = { - policyId: 'policyId', - assetName: '54657374636f696e', - quantity: new BigNumber(1), - fingerprint: 'fingerprint', - metadata: { - name: 'Testcoin', - description: 'Test coin', - }, - uniqueId: 'uniqueId', - decimals: 1, - recommendedDecimals: null, -}; -const assetWitoutMetadataName = { - policyId: 'policyId', - assetName: '436f696e74657374', - quantity: new BigNumber(1), - fingerprint: 'fingerprint', - uniqueId: 'uniqueId', - decimals: 1, - recommendedDecimals: null, -}; -const assetWithoutName = { - policyId: 'policyId', - assetName: '', - quantity: new BigNumber(1), - fingerprint: 'fingerprint', - uniqueId: 'uniqueId', - decimals: 1, - recommendedDecimals: null, -}; - -describe('Asset', () => { - afterEach(cleanup); - - test('Should display asset metadata name', () => { - render( - - - - ); - expect(screen.queryByTestId('assetName')).toHaveTextContent('Testcoin'); - }); - - test('Should display asset ASCII name when metadata name is not available', () => { - render( - - - - ); - expect(screen.queryByTestId('assetName')).toHaveTextContent( - 'ASCII: Cointest' - ); - }); - - test('Should not display asset name when metadata and ASCII name are not available', () => { - render( - - - - ); - expect(screen.queryByTestId('assetName')).toBeNull(); - }); -}); diff --git a/source/renderer/app/components/assets/Asset.tsx b/source/renderer/app/components/assets/Asset.tsx index 97e7472012..37b34d341e 100644 --- a/source/renderer/app/components/assets/Asset.tsx +++ b/source/renderer/app/components/assets/Asset.tsx @@ -1,7 +1,9 @@ +// @ts-nocheck + import React, { Component } from 'react'; import SVGInline from 'react-svg-inline'; import classnames from 'classnames'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { defineMessages, intlShape } from 'react-intl'; import { observer } from 'mobx-react'; import styles from './Asset.scss'; @@ -88,7 +90,6 @@ type State = { isHoveringSettingsIcon: boolean; }; -@observer class Asset extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -171,13 +172,8 @@ class Asset extends Component { hasWarning, hasError, } = this.props; - const { - fingerprint, - metadata, - decimals, - recommendedDecimals, - assetName, - } = asset; + const { fingerprint, metadata, decimals, recommendedDecimals, assetName } = + asset; const hasMetadataName = !!metadata?.name; const name = metadata?.name || (assetName && `ASCII: ${hexToString(assetName)}`) || ''; @@ -342,4 +338,4 @@ class Asset extends Component { } } -export default Asset; +export default observer(Asset); diff --git a/source/renderer/app/components/assets/AssetAmount.tsx b/source/renderer/app/components/assets/AssetAmount.tsx index 37a4e70150..1b4c373309 100644 --- a/source/renderer/app/components/assets/AssetAmount.tsx +++ b/source/renderer/app/components/assets/AssetAmount.tsx @@ -1,7 +1,9 @@ +// @ts-nocheck + import React from 'react'; import BigNumber from 'bignumber.js'; import classnames from 'classnames'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { defineMessages, FormattedHTMLMessage } from 'react-intl'; import { observer } from 'mobx-react'; import { discreetWalletTokenAmount } from '../../features/discreet-mode/replacers/discreetWalletTokenAmount'; diff --git a/source/renderer/app/components/assets/AssetSettingsDialog.spec.tsx b/source/renderer/app/components/assets/AssetSettingsDialog.spec.tsx deleted file mode 100644 index 045e6207e4..0000000000 --- a/source/renderer/app/components/assets/AssetSettingsDialog.spec.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import '@testing-library/jest-dom'; - -import React from 'react'; -import noop from 'lodash/noop'; -import { cleanup, screen, waitFor } from '@testing-library/react'; - -import createTestBed from 'tests/_utils/TestBed'; -import { - withDecimalPlacesToken, - zeroDecimalPlacesToken, -} from 'tests/mocks/asset'; - -import AssetSettingsDialog from './AssetSettingsDialog'; - -describe('AssetSettingsDialog', () => { - afterEach(() => cleanup()); - - it('should not show a warning when an asset is set to zero recommended decimal places', async () => { - createTestBed( - - ); - await waitFor(() => screen.getByText('Number of decimal places')); - expect(screen.queryByTestId('warning-icon')).not.toBeInTheDocument(); - }); - - it('should show a warning when an asset is not set to the recommended decimal places', async () => { - createTestBed( - - ); - await waitFor(() => screen.getByText('Number of decimal places')); - expect(screen.queryByTestId('warning-icon')).toBeInTheDocument(); - }); -}); diff --git a/source/renderer/app/components/assets/AssetSettingsDialog.tsx b/source/renderer/app/components/assets/AssetSettingsDialog.tsx index eb61232d62..8dbe3d47b9 100644 --- a/source/renderer/app/components/assets/AssetSettingsDialog.tsx +++ b/source/renderer/app/components/assets/AssetSettingsDialog.tsx @@ -1,9 +1,11 @@ +// @ts-nocheck + import React, { Component } from 'react'; import SVGInline from 'react-svg-inline'; import { range } from 'lodash'; import { observer } from 'mobx-react'; -import { Select } from 'react-polymorph/lib/components/Select'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { Select } from '@react-polymorph/components/Select'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import Asset from './Asset'; import DialogCloseButton from '../widgets/DialogCloseButton'; @@ -78,7 +80,6 @@ type State = { decimals: number | null | undefined; }; -@observer class AssetSettingsDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -233,4 +234,4 @@ class AssetSettingsDialog extends Component { } } -export default AssetSettingsDialog; +export default observer(AssetSettingsDialog); diff --git a/source/renderer/app/components/assets/AssetTransactionConfirmation.tsx b/source/renderer/app/components/assets/AssetTransactionConfirmation.tsx index 84c8d30fd9..09960d351f 100644 --- a/source/renderer/app/components/assets/AssetTransactionConfirmation.tsx +++ b/source/renderer/app/components/assets/AssetTransactionConfirmation.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + import React from 'react'; import classnames from 'classnames'; import BigNumber from 'bignumber.js'; @@ -7,7 +9,7 @@ import { injectIntl, FormattedHTMLMessage, } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { observer } from 'mobx-react'; import SVGInline from 'react-svg-inline'; import questionMarkIcon from '../../assets/images/question-mark.inline.svg'; diff --git a/source/renderer/app/components/assets/AssetsTransactionConfirmation.tsx b/source/renderer/app/components/assets/AssetsTransactionConfirmation.tsx index 3e389281e0..09b0e5d180 100644 --- a/source/renderer/app/components/assets/AssetsTransactionConfirmation.tsx +++ b/source/renderer/app/components/assets/AssetsTransactionConfirmation.tsx @@ -1,7 +1,9 @@ +// @ts-nocheck + import React from 'react'; import classnames from 'classnames'; import { intlShape, injectIntl } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import BigNumber from 'bignumber.js'; import { observer } from 'mobx-react'; import styles from './AssetsTransactionConfirmation.scss'; diff --git a/source/renderer/app/components/dapp/DappTransactionRequest.tsx b/source/renderer/app/components/dapp/DappTransactionRequest.tsx index 9575bbb268..998e53fcca 100644 --- a/source/renderer/app/components/dapp/DappTransactionRequest.tsx +++ b/source/renderer/app/components/dapp/DappTransactionRequest.tsx @@ -1,7 +1,7 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import BigNumber from 'bignumber.js'; import classnames from 'classnames'; -import { Select } from 'react-polymorph/lib/components/Select'; +import { Select } from '@react-polymorph/components/Select'; import { defineMessages, FormattedHTMLMessage, diff --git a/source/renderer/app/components/hardware-wallet/HardwareWalletStatus.tsx b/source/renderer/app/components/hardware-wallet/HardwareWalletStatus.tsx index 7ccb68b798..e3489f6389 100644 --- a/source/renderer/app/components/hardware-wallet/HardwareWalletStatus.tsx +++ b/source/renderer/app/components/hardware-wallet/HardwareWalletStatus.tsx @@ -3,8 +3,8 @@ import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedMessage } from 'react-intl'; import SVGInline from 'react-svg-inline'; import classnames from 'classnames'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import checkIcon from '../../assets/images/hardware-wallet/check.inline.svg'; import clearIcon from '../../assets/images/hardware-wallet/close-cross-red.inline.svg'; import LoadingSpinner from '../widgets/LoadingSpinner'; @@ -218,7 +218,6 @@ type State = { hwDeviceStatus: HwDeviceStatus; }; -@observer class HardwareWalletStatus extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -258,9 +257,8 @@ class HardwareWalletStatus extends Component { const isLoading = hwDeviceLoadingStatuses.includes(hwDeviceStatus); const isReady = hwDeviceReadyStatuses.includes(hwDeviceStatus); const isError = hwDeviceErrorStatuses.includes(hwDeviceStatus); - const hasInstructionsLink = hwDeviceInstructionsLinkRelatedStatuses.includes( - hwDeviceStatus - ); + const hasInstructionsLink = + hwDeviceInstructionsLinkRelatedStatuses.includes(hwDeviceStatus); const passphraseLabelVisible = isTrezor && hwDevicePassphraseRelatedStatuses.includes(hwDeviceStatus); @@ -351,4 +349,4 @@ class HardwareWalletStatus extends Component { } } -export default HardwareWalletStatus; +export default observer(HardwareWalletStatus); diff --git a/source/renderer/app/components/knownIssues/RTSFlagsRecommendationOverlay/RTSFlagsRecommendationOverlay.tsx b/source/renderer/app/components/knownIssues/RTSFlagsRecommendationOverlay/RTSFlagsRecommendationOverlay.tsx index 1038ce6823..802805604f 100644 --- a/source/renderer/app/components/knownIssues/RTSFlagsRecommendationOverlay/RTSFlagsRecommendationOverlay.tsx +++ b/source/renderer/app/components/knownIssues/RTSFlagsRecommendationOverlay/RTSFlagsRecommendationOverlay.tsx @@ -1,9 +1,11 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; +import { Button } from '@react-polymorph/components/Button'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; import DialogCloseButton from '../../widgets/DialogCloseButton'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/close-c... Remove this comment to see the full error message import closeCrossThin from '../../../assets/images/close-cross-thin.inline.svg'; @@ -37,7 +39,6 @@ const messages = defineMessages({ }, }); -@observer class RTSFlagsRecommendationOverlay extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -80,4 +81,4 @@ class RTSFlagsRecommendationOverlay extends Component { } } -export default RTSFlagsRecommendationOverlay; +export default observer(RTSFlagsRecommendationOverlay); diff --git a/source/renderer/app/components/knownIssues/ToggleRTSFlagsDialog/ToggleRTSFlagsDialog.tsx b/source/renderer/app/components/knownIssues/ToggleRTSFlagsDialog/ToggleRTSFlagsDialog.tsx index 6018300afe..a3171438a7 100644 --- a/source/renderer/app/components/knownIssues/ToggleRTSFlagsDialog/ToggleRTSFlagsDialog.tsx +++ b/source/renderer/app/components/knownIssues/ToggleRTSFlagsDialog/ToggleRTSFlagsDialog.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; import globalMessages from '../../../i18n/global-messages'; @@ -41,8 +41,7 @@ const messages = defineMessages({ description: 'Disable RTS flags button label', }, manualRelaunchConfirmationCheckboxLabel: { - id: - 'knownIssues.dialog.toggleRtsFlagsMode.manualRelaunchConfirmationCheckboxLabel', + id: 'knownIssues.dialog.toggleRtsFlagsMode.manualRelaunchConfirmationCheckboxLabel', defaultMessage: '!!!I understand that I will need to launch Daedalus manually', description: 'Manual relaunch confirmation checkbox label', @@ -57,7 +56,6 @@ type State = { isConfirmationCheckboxChecked: boolean; }; -@observer class ToggleRTSFlagsDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -123,4 +121,4 @@ class ToggleRTSFlagsDialog extends Component { } } -export default ToggleRTSFlagsDialog; +export default observer(ToggleRTSFlagsDialog); diff --git a/source/renderer/app/components/layout/CenteredLayout.tsx b/source/renderer/app/components/layout/CenteredLayout.tsx index c6e730f8cc..398d01db96 100644 --- a/source/renderer/app/components/layout/CenteredLayout.tsx +++ b/source/renderer/app/components/layout/CenteredLayout.tsx @@ -8,7 +8,6 @@ type Props = { children: Node; }; -@observer class CenteredLayout extends Component { static defaultProps = { children: null, @@ -20,4 +19,4 @@ class CenteredLayout extends Component { } } -export default CenteredLayout; +export default observer(CenteredLayout); diff --git a/source/renderer/app/components/layout/SidebarLayout.tsx b/source/renderer/app/components/layout/SidebarLayout.tsx index 046595f647..455e04281f 100644 --- a/source/renderer/app/components/layout/SidebarLayout.tsx +++ b/source/renderer/app/components/layout/SidebarLayout.tsx @@ -12,20 +12,14 @@ type Props = { contentDialogs?: Array | null | undefined; }; -@observer class SidebarLayout extends Component { static defaultProps = { children: null, }; render() { - const { - children, - sidebar, - topbar, - notification, - contentDialogs, - } = this.props; + const { children, sidebar, topbar, notification, contentDialogs } = + this.props; return (
{sidebar}
@@ -42,4 +36,4 @@ class SidebarLayout extends Component { } } -export default SidebarLayout; +export default observer(SidebarLayout); diff --git a/source/renderer/app/components/layout/TopBar.tsx b/source/renderer/app/components/layout/TopBar.tsx index b50d4610bd..76cfb25c92 100644 --- a/source/renderer/app/components/layout/TopBar.tsx +++ b/source/renderer/app/components/layout/TopBar.tsx @@ -24,7 +24,6 @@ type Props = { isShelleyActivated: boolean; }; -@observer class TopBar extends Component { render() { const { @@ -108,4 +107,4 @@ class TopBar extends Component { } } -export default TopBar; +export default observer(TopBar); diff --git a/source/renderer/app/components/layout/TopBarLayout.tsx b/source/renderer/app/components/layout/TopBarLayout.tsx index 94094e7ea0..f6300fcfa1 100644 --- a/source/renderer/app/components/layout/TopBarLayout.tsx +++ b/source/renderer/app/components/layout/TopBarLayout.tsx @@ -10,7 +10,6 @@ type Props = { notification?: Node | null | undefined; }; -@observer class TopBarLayout extends Component { render() { const { children, topbar, notification } = this.props; @@ -24,4 +23,4 @@ class TopBarLayout extends Component { } } -export default TopBarLayout; +export default observer(TopBarLayout); diff --git a/source/renderer/app/components/loading/no-disk-space-error/NoDiskSpaceError.tsx b/source/renderer/app/components/loading/no-disk-space-error/NoDiskSpaceError.tsx index a51dba9495..bdb67fa6e9 100644 --- a/source/renderer/app/components/loading/no-disk-space-error/NoDiskSpaceError.tsx +++ b/source/renderer/app/components/loading/no-disk-space-error/NoDiskSpaceError.tsx @@ -25,7 +25,6 @@ type Props = { diskSpaceRecommended: string; }; -@observer class NoDiskSpaceError extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -33,11 +32,8 @@ class NoDiskSpaceError extends Component { render() { const { intl } = this.context; - const { - diskSpaceRequired, - diskSpaceMissing, - diskSpaceRecommended, - } = this.props; + const { diskSpaceRequired, diskSpaceMissing, diskSpaceRecommended } = + this.props; return (
@@ -59,4 +55,4 @@ class NoDiskSpaceError extends Component { } } -export default NoDiskSpaceError; +export default observer(NoDiskSpaceError); diff --git a/source/renderer/app/components/loading/syncing-connecting/ReportIssue.tsx b/source/renderer/app/components/loading/syncing-connecting/ReportIssue.tsx index ee231345b0..16d48844c1 100644 --- a/source/renderer/app/components/loading/syncing-connecting/ReportIssue.tsx +++ b/source/renderer/app/components/loading/syncing-connecting/ReportIssue.tsx @@ -2,10 +2,10 @@ import React, { Component } from 'react'; import SVGInline from 'react-svg-inline'; import { defineMessages, intlShape } from 'react-intl'; import classNames from 'classnames'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import styles from './ReportIssue.scss'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/link-ic... Remove this comment to see the full error message import externalLinkIcon from '../../../assets/images/link-ic.inline.svg'; diff --git a/source/renderer/app/components/loading/syncing-connecting/StatusIcons.tsx b/source/renderer/app/components/loading/syncing-connecting/StatusIcons.tsx index 2f996887d4..00862731d6 100644 --- a/source/renderer/app/components/loading/syncing-connecting/StatusIcons.tsx +++ b/source/renderer/app/components/loading/syncing-connecting/StatusIcons.tsx @@ -1,7 +1,7 @@ -import React, { Component } from 'react'; -import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; +import React from 'react'; +import { defineMessages, FormattedHTMLMessage } from 'react-intl'; import SVGInline from 'react-svg-inline'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import classNames from 'classnames'; import styles from './StatusIcons.scss'; import { CardanoNodeStates } from '../../../../../common/types/cardano-node.types'; @@ -152,15 +152,7 @@ const messages = defineMessages({ 'Message "Checking if Cardano node is syncing" on the status icon tooltip', }, }); -type Props = { - onIconClick: (...args: Array) => any; - nodeState: CardanoNodeState | null | undefined; - isNodeResponding?: boolean; - isNodeSubscribed?: boolean; - isNodeTimeCorrect?: boolean; - isNodeSyncing?: boolean; -}; -type TipParamValue = true | false | null | string; + const STATUS_CLASSNAMES: Record = { [CardanoNodeStates.STARTING]: 'unloaded', [CardanoNodeStates.RUNNING]: 'on', @@ -194,11 +186,19 @@ const VARIABLE_VALUES = { undefined: 'Loading', null: 'IsStarting', }; -export default class StatusIcons extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - getTip = (paramName: string, paramValue: TipParamValue) => { + +type Props = { + onIconClick: (...args: Array) => any; + nodeState: CardanoNodeState | null | undefined; + isNodeResponding?: boolean; + isNodeSubscribed?: boolean; + isNodeTimeCorrect?: boolean; + isNodeSyncing?: boolean; +}; +type TipParamValue = true | false | null | string; + +const StatusIcons: React.FC = (props) => { + const getTip = (paramName: string, paramValue: TipParamValue) => { let message; if (paramName === 'nodeState' && paramValue) { @@ -209,14 +209,15 @@ export default class StatusIcons extends Component { return message && ; }; - getClassName = (paramName: string) => { + + const getClassName = (paramName: string) => { // If node is not running, it displays the icons with opacity // Whether {isNodeSyncing} it displays the icons for syncing or loading screen - const { isNodeSyncing } = this.props; - const paramValue = this.props[paramName]; + const { isNodeSyncing } = props; + const paramValue = props[paramName]; let status = STATUS_CLASSNAMES[paramValue]; - if (this.isDisabled(paramName)) { + if (isDisabled(paramName)) { status = 'unknown'; } @@ -234,18 +235,19 @@ export default class StatusIcons extends Component { isNodeSyncing ? styles.syncing : styles.loading, ]); }; - getTooltipClassname = (paramName: string) => { - const paramValue = this.props[paramName]; + + const getTooltipClassname = (paramName: string) => { + const paramValue = props[paramName]; return classNames([ styles.tooltip, typeof paramValue === 'undefined' ? styles.ellipsis : null, - this.isDisabled(paramName) ? styles.disabled : null, + isDisabled(paramName) ? styles.disabled : null, ]); }; - isDisabled = (paramName: string) => - paramName !== 'nodeState' && - this.props.nodeState !== CardanoNodeStates.RUNNING; - getIconWithPopover = (icon: string, paramName: string) => ( + + const isDisabled = (paramName: string) => + paramName !== 'nodeState' && props.nodeState !== CardanoNodeStates.RUNNING; + const getIconWithPopover = (icon: string, paramName: string) => ( { '--rp-pop-over-border-radius': '5px', '--rp-bubble-padding': '6px 12px 7px', }} - contentClassName={this.getTooltipClassname(paramName)} + contentClassName={getTooltipClassname(paramName)} key={paramName} - content={this.getTip(paramName, this.props[paramName])} + content={getTip(paramName, props[paramName])} > - ); - render() { - return ( -
- {[ - this.getIconWithPopover(nodeStateIcon, 'nodeState'), - this.getIconWithPopover(isNodeRespondingIcon, 'isNodeResponding'), // this.getIconWithPopover(isNodeSubscribedIcon, 'isNodeSubscribed'), - this.getIconWithPopover(isNodeTimeCorrectIcon, 'isNodeTimeCorrect'), - this.getIconWithPopover(isNodeSyncingIcon, 'isNodeSyncing'), - ]} -
- ); - } -} + return ( +
+ {[ + getIconWithPopover(nodeStateIcon, 'nodeState'), + getIconWithPopover(isNodeRespondingIcon, 'isNodeResponding'), // getIconWithPopover(isNodeSubscribedIcon, 'isNodeSubscribed'), + getIconWithPopover(isNodeTimeCorrectIcon, 'isNodeTimeCorrect'), + getIconWithPopover(isNodeSyncingIcon, 'isNodeSyncing'), + ]} +
+ ); +}; + +export default StatusIcons; diff --git a/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.tsx b/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.tsx index c69f695891..6d777b4551 100644 --- a/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.tsx +++ b/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.tsx @@ -51,7 +51,6 @@ export type Props = { onToggleNewsFeedIconClick: (...args: Array) => any; }; -@observer class SyncingConnecting extends Component { state = { connectingTime: 0, @@ -173,6 +172,7 @@ class SyncingConnecting extends Component { isConnecting ? 'connectingScreen' : null, isSyncing || isSynced ? 'syncingScreen' : null, ]); + return (
{ onIconClick={onStatusIconClick} nodeState={cardanoNodeState} isNodeResponding={isNodeResponding} - isNodeTimeCorrect={ - isCheckingSystemTime ? undefined : isNodeTimeCorrect - } + isNodeTimeCorrect={isCheckingSystemTime && isNodeTimeCorrect} isNodeSyncing={isNodeSyncing} />
@@ -224,4 +222,4 @@ class SyncingConnecting extends Component { } } -export default SyncingConnecting; +export default observer(SyncingConnecting); diff --git a/source/renderer/app/components/loading/syncing-connecting/SyncingConnectingBackground.tsx b/source/renderer/app/components/loading/syncing-connecting/SyncingConnectingBackground.tsx index 939d1770fd..47df85a10c 100644 --- a/source/renderer/app/components/loading/syncing-connecting/SyncingConnectingBackground.tsx +++ b/source/renderer/app/components/loading/syncing-connecting/SyncingConnectingBackground.tsx @@ -9,7 +9,6 @@ type Props = { isSyncing: boolean; }; -@observer class SyncingConnectingBackground extends Component { render() { const { isConnecting, isSyncing, hasLoadedCurrentTheme } = this.props; @@ -23,4 +22,4 @@ class SyncingConnectingBackground extends Component { } } -export default SyncingConnectingBackground; +export default observer(SyncingConnectingBackground); diff --git a/source/renderer/app/components/loading/syncing-connecting/SyncingConnectingStatus.tsx b/source/renderer/app/components/loading/syncing-connecting/SyncingConnectingStatus.tsx index becba0fc55..61204cdd12 100644 --- a/source/renderer/app/components/loading/syncing-connecting/SyncingConnectingStatus.tsx +++ b/source/renderer/app/components/loading/syncing-connecting/SyncingConnectingStatus.tsx @@ -1,6 +1,11 @@ import classNames from 'classnames'; import React, { Component } from 'react'; -import { defineMessages, FormattedHTMLMessage, intlShape } from 'react-intl'; +import { + defineMessages, + FormattedHTMLMessage, + intlShape, + injectIntl, +} from 'react-intl'; import { BlockSyncType, CardanoNodeState, @@ -37,6 +42,11 @@ const messages = defineMessages({ defaultMessage: '!!!Cardano node stopped', description: 'Message "Cardano node stopped" on the loading screen.', }, + syncingMessage: { + id: 'loading.screen.syncingMessage', + defaultMessage: '!!!Backend syncing', + description: 'Message "Syncing" on the loading screen.', + }, updating: { id: 'loading.screen.updatingCardanoMessage', defaultMessage: '!!!Updating Cardano node', @@ -92,9 +102,10 @@ interface Props { isNodeStopping: boolean; isNodeStopped: boolean; isVerifyingBlockchain: boolean; + intl: intlShape.isRequired; } -export default class SyncingConnectingStatus extends Component { +class SyncingConnectingStatus extends Component { static contextTypes = { intl: intlShape.isRequired, }; @@ -160,7 +171,6 @@ export default class SyncingConnectingStatus extends Component { ? messages.reconnecting : messages.connecting; } - const isConnectingMessage = connectingMessage === messages.connecting || connectingMessage === messages.reconnecting; @@ -176,7 +186,6 @@ export default class SyncingConnectingStatus extends Component { }; render() { - const { intl } = this.context; const { isConnected, isNodeStopping, @@ -186,13 +195,13 @@ export default class SyncingConnectingStatus extends Component { hasLoadedCurrentLocale, blockSyncProgress, cardanoNodeState, + intl, } = this.props; + if (!hasLoadedCurrentLocale) return null; - const { - connectingMessage, - connectingDescription, - } = this._getConnectingMessage(); + const { connectingMessage, connectingDescription } = + this._getConnectingMessage(); if ( cardanoNodeState === CardanoNodeStates.RUNNING && @@ -223,10 +232,12 @@ export default class SyncingConnectingStatus extends Component {
{connectingDescription && ( - + )}
); } } + +export default injectIntl(SyncingConnectingStatus); diff --git a/source/renderer/app/components/loading/syncing-connecting/SyncingProgress/SyncingProgress.tsx b/source/renderer/app/components/loading/syncing-connecting/SyncingProgress/SyncingProgress.tsx index ec771fdcaa..8200f43418 100644 --- a/source/renderer/app/components/loading/syncing-connecting/SyncingProgress/SyncingProgress.tsx +++ b/source/renderer/app/components/loading/syncing-connecting/SyncingProgress/SyncingProgress.tsx @@ -2,7 +2,7 @@ import cx from 'classnames'; import React from 'react'; import BigNumber from 'bignumber.js'; import { intlShape } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import SVGInline from 'react-svg-inline'; import spinnerIcon from '../../../../assets/images/spinner-universal.inline.svg'; import checkMarkIcon from '../../../../assets/images/check-mark-universal.inline.svg'; diff --git a/source/renderer/app/components/loading/system-time-error/SystemTimeError.tsx b/source/renderer/app/components/loading/system-time-error/SystemTimeError.tsx index 40ac301d1f..4819f26c53 100644 --- a/source/renderer/app/components/loading/system-time-error/SystemTimeError.tsx +++ b/source/renderer/app/components/loading/system-time-error/SystemTimeError.tsx @@ -3,8 +3,8 @@ import SVGInline from 'react-svg-inline'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedMessage } from 'react-intl'; import classNames from 'classnames'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/attenti... Remove this comment to see the full error message import attentionIcon from '../../../assets/images/attention-big-light.inline.svg'; import { ALLOWED_TIME_DIFFERENCE } from '../../../config/timingConfig'; @@ -74,7 +74,6 @@ type Props = { isCheckingSystemTime: boolean; }; -@observer class SystemTimeError extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -187,4 +186,4 @@ class SystemTimeError extends Component { } } -export default SystemTimeError; +export default observer(SystemTimeError); diff --git a/source/renderer/app/components/navigation/NavButton.scss b/source/renderer/app/components/navigation/NavButton.scss old mode 100755 new mode 100644 diff --git a/source/renderer/app/components/navigation/NavButton.tsx b/source/renderer/app/components/navigation/NavButton.tsx old mode 100755 new mode 100644 index 5221052334..5a6c04ebca --- a/source/renderer/app/components/navigation/NavButton.tsx +++ b/source/renderer/app/components/navigation/NavButton.tsx @@ -13,7 +13,6 @@ type Props = { hasNotification?: boolean; }; -@observer class NavButton extends Component { render() { const { isActive, icon, onClick, className, hasNotification } = this.props; @@ -38,4 +37,4 @@ class NavButton extends Component { } } -export default NavButton; +export default observer(NavButton); diff --git a/source/renderer/app/components/navigation/NavDropdown.scss b/source/renderer/app/components/navigation/NavDropdown.scss old mode 100755 new mode 100644 diff --git a/source/renderer/app/components/navigation/NavDropdown.tsx b/source/renderer/app/components/navigation/NavDropdown.tsx index 8918805cce..54df9ae667 100644 --- a/source/renderer/app/components/navigation/NavDropdown.tsx +++ b/source/renderer/app/components/navigation/NavDropdown.tsx @@ -1,7 +1,9 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { Dropdown } from 'react-polymorph/lib/components/Dropdown'; +import { Dropdown } from '@react-polymorph/components/Dropdown'; import NavButton from './NavButton'; import styles from './NavDropdown.scss'; import { NavDropdownProps } from './Navigation'; @@ -19,7 +21,6 @@ type Props = { hasNotification?: boolean; }; -@observer class NavDropdown extends Component { render() { const { @@ -72,4 +73,4 @@ class NavDropdown extends Component { } } -export default NavDropdown; +export default observer(NavDropdown); diff --git a/source/renderer/app/components/navigation/Navigation.scss b/source/renderer/app/components/navigation/Navigation.scss old mode 100755 new mode 100644 diff --git a/source/renderer/app/components/navigation/Navigation.tsx b/source/renderer/app/components/navigation/Navigation.tsx old mode 100755 new mode 100644 index b63b4bc9ed..13a6c0dad1 --- a/source/renderer/app/components/navigation/Navigation.tsx +++ b/source/renderer/app/components/navigation/Navigation.tsx @@ -27,7 +27,6 @@ type Props = { items: Array; }; -@observer class Navigation extends Component { isActiveNavItem = ( id: string, @@ -82,4 +81,4 @@ class Navigation extends Component { } } -export default Navigation; +export default observer(Navigation); diff --git a/source/renderer/app/components/news/AlertsOverlay.tsx b/source/renderer/app/components/news/AlertsOverlay.tsx index 5886bfed43..dc6d9eeb03 100644 --- a/source/renderer/app/components/news/AlertsOverlay.tsx +++ b/source/renderer/app/components/news/AlertsOverlay.tsx @@ -3,7 +3,7 @@ import moment from 'moment'; import { observer } from 'mobx-react'; import { get } from 'lodash'; import ReactMarkdown from 'react-markdown'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import News from '../../domains/News'; import DialogCloseButton from '../widgets/DialogCloseButton'; import closeCrossThin from '../../assets/images/close-cross-thin.inline.svg'; @@ -25,7 +25,6 @@ type Props = { currentDateFormat: string; }; -@observer class AlertsOverlay extends Component { constructor(props: Props) { super(props); @@ -136,4 +135,4 @@ class AlertsOverlay extends Component { } } -export default AlertsOverlay; +export default observer(AlertsOverlay); diff --git a/source/renderer/app/components/news/IncidentOverlay.tsx b/source/renderer/app/components/news/IncidentOverlay.tsx index 4c4d119501..a50038f2f7 100644 --- a/source/renderer/app/components/news/IncidentOverlay.tsx +++ b/source/renderer/app/components/news/IncidentOverlay.tsx @@ -4,7 +4,7 @@ import { observer } from 'mobx-react'; import { get, camelCase } from 'lodash'; import ReactMarkdown from 'react-markdown'; import classnames from 'classnames'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import News from '../../domains/News'; import ButtonLink from '../widgets/ButtonLink'; import styles from './IncidentOverlay.scss'; @@ -17,7 +17,6 @@ type Props = { currentDateFormat: string; }; -@observer class IncidentOverlay extends Component { localizedDateFormat: 'MM/DD/YYYY'; @@ -86,4 +85,4 @@ class IncidentOverlay extends Component { } } -export default IncidentOverlay; +export default observer(IncidentOverlay); diff --git a/source/renderer/app/components/news/NewsFeed.tsx b/source/renderer/app/components/news/NewsFeed.tsx index 09e62a6677..4b4299fb65 100644 --- a/source/renderer/app/components/news/NewsFeed.tsx +++ b/source/renderer/app/components/news/NewsFeed.tsx @@ -50,7 +50,6 @@ type State = { }; const SCROLLABLE_DOM_ELEMENT_SELECTOR = '.NewsFeed_newsFeedList'; -@observer class NewsFeed extends Component { static defaultProps = { onClose: null, @@ -249,4 +248,4 @@ class NewsFeed extends Component { } } -export default NewsFeed; +export default observer(NewsFeed); diff --git a/source/renderer/app/components/news/NewsItem.tsx b/source/renderer/app/components/news/NewsItem.tsx index 716444cece..461fe7ef5a 100644 --- a/source/renderer/app/components/news/NewsItem.tsx +++ b/source/renderer/app/components/news/NewsItem.tsx @@ -5,7 +5,7 @@ import ReactMarkdown from 'react-markdown'; import moment from 'moment'; import { get } from 'lodash'; import AnimateHeight from 'react-animate-height'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import News, { NewsTypes } from '../../domains/News'; import ButtonLink from '../widgets/ButtonLink'; import styles from './NewsItem.scss'; @@ -27,7 +27,6 @@ type State = { newsItemCollapsible: boolean; }; -@observer class NewsItem extends Component { static defaultProps = { onNewsItemActionClick: null, @@ -173,4 +172,4 @@ class NewsItem extends Component { } } -export default NewsItem; +export default observer(NewsItem); diff --git a/source/renderer/app/components/news/UpdateItem.tsx b/source/renderer/app/components/news/UpdateItem.tsx index 16917f91ef..6456082404 100644 --- a/source/renderer/app/components/news/UpdateItem.tsx +++ b/source/renderer/app/components/news/UpdateItem.tsx @@ -14,7 +14,6 @@ type Props = { isUpdatePostponed: boolean; }; -@observer class UpdateItem extends Component { static defaultProps = { onupdateItemActionClick: null, @@ -77,4 +76,4 @@ class UpdateItem extends Component { } } -export default UpdateItem; +export default observer(UpdateItem); diff --git a/source/renderer/app/components/notifications/LegacyBadge.tsx b/source/renderer/app/components/notifications/LegacyBadge.tsx index d6ce49abcb..279e75853d 100644 --- a/source/renderer/app/components/notifications/LegacyBadge.tsx +++ b/source/renderer/app/components/notifications/LegacyBadge.tsx @@ -19,7 +19,6 @@ type Props = { mode: string; }; -@observer class LegacyBadge extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -37,4 +36,4 @@ class LegacyBadge extends Component { } } -export default LegacyBadge; +export default observer(LegacyBadge); diff --git a/source/renderer/app/components/notifications/LegacyNotification.tsx b/source/renderer/app/components/notifications/LegacyNotification.tsx index 81b9762d90..94c5e63075 100644 --- a/source/renderer/app/components/notifications/LegacyNotification.tsx +++ b/source/renderer/app/components/notifications/LegacyNotification.tsx @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedMessage } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import ButtonLink from '../widgets/ButtonLink'; import styles from './LegacyNotification.scss'; @@ -85,7 +85,6 @@ type Props = { onWalletAdd?: (...args: Array) => any; }; -@observer class LegacyNotification extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -108,7 +107,7 @@ class LegacyNotification extends Component { activeWalletName, ..._values, }; - return ; + return ; }; render() { @@ -173,4 +172,4 @@ class LegacyNotification extends Component { } } -export default LegacyNotification; +export default observer(LegacyNotification); diff --git a/source/renderer/app/components/notifications/NotificationActions.tsx b/source/renderer/app/components/notifications/NotificationActions.tsx index 92687d21e7..a341be46df 100644 --- a/source/renderer/app/components/notifications/NotificationActions.tsx +++ b/source/renderer/app/components/notifications/NotificationActions.tsx @@ -1,8 +1,10 @@ +// @ts-nocheck + import React from 'react'; import classnames from 'classnames'; import { map } from 'lodash'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Link } from 'react-polymorph/lib/components/Link'; +import { Button } from '@react-polymorph/components/Button'; +import { Link } from '@react-polymorph/components/Link'; import { observer } from 'mobx-react'; import styles from './NotificationActions.scss'; diff --git a/source/renderer/app/components/notifications/RestoreNotification.tsx b/source/renderer/app/components/notifications/RestoreNotification.tsx index 4959ed77da..2d4e1bec12 100644 --- a/source/renderer/app/components/notifications/RestoreNotification.tsx +++ b/source/renderer/app/components/notifications/RestoreNotification.tsx @@ -20,7 +20,6 @@ type Props = { restoreProgress: number; }; -@observer class RestoreNotification extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -46,4 +45,4 @@ class RestoreNotification extends Component { } } -export default RestoreNotification; +export default observer(RestoreNotification); diff --git a/source/renderer/app/components/profile/analytics/Analytics.stories.tsx b/source/renderer/app/components/profile/analytics/Analytics.stories.tsx deleted file mode 100644 index f51e73eb9d..0000000000 --- a/source/renderer/app/components/profile/analytics/Analytics.stories.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { storiesOf } from '@storybook/react'; -import { withKnobs } from '@storybook/addon-knobs'; -import AnalyticsConsentForm from './AnalyticsConsentForm'; -import StoryDecorator from '../../../../../../storybook/stories/_support/StoryDecorator'; - -storiesOf('Analytics', module) - .addDecorator(withKnobs) - .addDecorator((story) => {story()}) - .add('Analytics Consent Form', () => ); diff --git a/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.tsx b/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.tsx index 41bca62ca2..2ea6245c1f 100644 --- a/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.tsx +++ b/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.tsx @@ -1,8 +1,8 @@ import React, { useCallback } from 'react'; import { FormattedMessage, injectIntl } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { ButtonSpinnerSkin } from 'react-polymorph/lib/skins/simple/ButtonSpinnerSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { Link } from '@react-polymorph/components/Link'; +import { ButtonSpinnerSkin } from '@react-polymorph/skins/simple/ButtonSpinnerSkin'; import classnames from 'classnames'; import styles from './AnalyticsConsentForm.scss'; import { Intl } from '../../../types/i18nTypes'; diff --git a/source/renderer/app/components/profile/data-layer-migration/DataLayerMigrationForm.tsx b/source/renderer/app/components/profile/data-layer-migration/DataLayerMigrationForm.tsx index 51eeeafdb3..9acb4261f6 100644 --- a/source/renderer/app/components/profile/data-layer-migration/DataLayerMigrationForm.tsx +++ b/source/renderer/app/components/profile/data-layer-migration/DataLayerMigrationForm.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import LocalizableError from '../../../i18n/LocalizableError'; import styles from './DataLayerMigrationForm.scss'; @@ -41,7 +41,6 @@ type Props = { error?: LocalizableError | null | undefined; }; -@observer class DataLayerMigrationForm extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -81,4 +80,4 @@ class DataLayerMigrationForm extends Component { } } -export default DataLayerMigrationForm; +export default observer(DataLayerMigrationForm); diff --git a/source/renderer/app/components/profile/initial-settings/InitialSettings.tsx b/source/renderer/app/components/profile/initial-settings/InitialSettings.tsx index 724f255152..05021d96c9 100644 --- a/source/renderer/app/components/profile/initial-settings/InitialSettings.tsx +++ b/source/renderer/app/components/profile/initial-settings/InitialSettings.tsx @@ -4,7 +4,6 @@ import styles from './InitialSettings.scss'; import ProfileSettingsForm from '../../widgets/forms/ProfileSettingsForm'; import type { ProfileSettingsFormProps } from '../../widgets/forms/ProfileSettingsForm'; -@observer class InitialSettings extends Component { static defaultProps = { error: null, @@ -38,4 +37,4 @@ class InitialSettings extends Component { } } -export default InitialSettings; +export default observer(InitialSettings); diff --git a/source/renderer/app/components/profile/terms-of-use/TermsOfUseForm.tsx b/source/renderer/app/components/profile/terms-of-use/TermsOfUseForm.tsx index 4c154bdeda..160cb95f98 100644 --- a/source/renderer/app/components/profile/terms-of-use/TermsOfUseForm.tsx +++ b/source/renderer/app/components/profile/terms-of-use/TermsOfUseForm.tsx @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import classnames from 'classnames'; import { observer } from 'mobx-react'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import { defineMessages, intlShape } from 'react-intl'; import LocalizableError from '../../../i18n/LocalizableError'; import TermsOfUseText from './TermsOfUseText'; @@ -40,7 +40,6 @@ type State = { areTermsOfUseAccepted: boolean; }; -@observer class TermsOfUseForm extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -59,12 +58,8 @@ class TermsOfUseForm extends Component { render() { const { intl } = this.context; - const { - isSubmitting, - error, - localizedTermsOfUse, - onOpenExternalLink, - } = this.props; + const { isSubmitting, error, localizedTermsOfUse, onOpenExternalLink } = + this.props; const { areTermsOfUseAccepted } = this.state; const buttonClasses = classnames([ 'primary', @@ -102,4 +97,4 @@ class TermsOfUseForm extends Component { } } -export default TermsOfUseForm; +export default observer(TermsOfUseForm); diff --git a/source/renderer/app/components/profile/terms-of-use/TermsOfUseText.tsx b/source/renderer/app/components/profile/terms-of-use/TermsOfUseText.tsx index df265a6636..6dcba575fb 100644 --- a/source/renderer/app/components/profile/terms-of-use/TermsOfUseText.tsx +++ b/source/renderer/app/components/profile/terms-of-use/TermsOfUseText.tsx @@ -9,7 +9,6 @@ type Props = { onOpenExternalLink: (...args: Array) => any; }; -@observer class TermsOfUseText extends Component { termsOfUseClickHandler = (event: React.MouseEvent) => { const linkUrl = get(event, ['target', 'href']); @@ -36,4 +35,4 @@ class TermsOfUseText extends Component { } } -export default TermsOfUseText; +export default observer(TermsOfUseText); diff --git a/source/renderer/app/components/settings/SettingsLayout.tsx b/source/renderer/app/components/settings/SettingsLayout.tsx index 69cc130bba..93cfced108 100644 --- a/source/renderer/app/components/settings/SettingsLayout.tsx +++ b/source/renderer/app/components/settings/SettingsLayout.tsx @@ -10,7 +10,6 @@ type Props = { activePage: string; }; -@observer class SettingsLayout extends Component { scrollableDomElement: HTMLElement | null | undefined = null; @@ -44,4 +43,4 @@ class SettingsLayout extends Component { } } -export default SettingsLayout; +export default observer(SettingsLayout); diff --git a/source/renderer/app/components/settings/categories/DisplaySettings.tsx b/source/renderer/app/components/settings/categories/DisplaySettings.tsx index 4b613202dc..358f8235dc 100644 --- a/source/renderer/app/components/settings/categories/DisplaySettings.tsx +++ b/source/renderer/app/components/settings/categories/DisplaySettings.tsx @@ -85,7 +85,6 @@ type Props = { selectTheme: (...args: Array) => any; }; -@observer class DisplaySettings extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -283,4 +282,4 @@ class DisplaySettings extends Component { } } -export default DisplaySettings; +export default observer(DisplaySettings); diff --git a/source/renderer/app/components/settings/categories/GeneralSettings.tsx b/source/renderer/app/components/settings/categories/GeneralSettings.tsx index 0e22b8bedf..820b05fe87 100644 --- a/source/renderer/app/components/settings/categories/GeneralSettings.tsx +++ b/source/renderer/app/components/settings/categories/GeneralSettings.tsx @@ -3,7 +3,6 @@ import { observer } from 'mobx-react'; import ProfileSettingsForm from '../../widgets/forms/ProfileSettingsForm'; import type { ProfileSettingsFormProps } from '../../widgets/forms/ProfileSettingsForm'; -@observer class GeneralSettings extends Component { render() { const { @@ -27,4 +26,4 @@ class GeneralSettings extends Component { } } -export default GeneralSettings; +export default observer(GeneralSettings); diff --git a/source/renderer/app/components/settings/categories/StakePoolsSettings.tsx b/source/renderer/app/components/settings/categories/StakePoolsSettings.tsx index 4a34e9aba0..edfdb22933 100644 --- a/source/renderer/app/components/settings/categories/StakePoolsSettings.tsx +++ b/source/renderer/app/components/settings/categories/StakePoolsSettings.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import classnames from 'classnames'; import { map, omit } from 'lodash'; -import { Select } from 'react-polymorph/lib/components/Select'; -import { Link } from 'react-polymorph/lib/components/Link'; +import { Select } from '@react-polymorph/components/Select'; +import { Link } from '@react-polymorph/components/Link'; import SVGInline from 'react-svg-inline'; import { observer } from 'mobx-react'; import { @@ -151,7 +151,6 @@ type State = { }; const { isSelfnode } = global.environment; -@observer class StakePoolsSettings extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -402,4 +401,4 @@ class StakePoolsSettings extends Component { } } -export default StakePoolsSettings; +export default observer(StakePoolsSettings); diff --git a/source/renderer/app/components/settings/categories/SupportSettings.tsx b/source/renderer/app/components/settings/categories/SupportSettings.tsx index f4e5619337..6974a341d6 100644 --- a/source/renderer/app/components/settings/categories/SupportSettings.tsx +++ b/source/renderer/app/components/settings/categories/SupportSettings.tsx @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { inject, observer } from 'mobx-react'; +import { observer } from 'mobx-react'; import { FormattedMessage, FormattedHTMLMessage, @@ -7,8 +7,8 @@ import { Intl, } from 'react-intl'; import classNames from 'classnames'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import styles from './SupportSettings.scss'; import globalMessages from '../../../i18n/global-messages'; import { messages } from './SupportSettings.messages'; @@ -24,7 +24,6 @@ interface SupportSettingsProps { analyticsAccepted: boolean; } -@observer class SupportSettings extends Component { render() { const { @@ -145,4 +144,4 @@ class SupportSettings extends Component { } } -export default injectIntl(SupportSettings); +export default injectIntl(observer(SupportSettings)); diff --git a/source/renderer/app/components/settings/categories/TermsOfUseSettings.tsx b/source/renderer/app/components/settings/categories/TermsOfUseSettings.tsx index 16b0e8de7b..49b9f0fe47 100644 --- a/source/renderer/app/components/settings/categories/TermsOfUseSettings.tsx +++ b/source/renderer/app/components/settings/categories/TermsOfUseSettings.tsx @@ -8,7 +8,6 @@ type Props = { onOpenExternalLink: (...args: Array) => any; }; -@observer class TermsOfUseSettings extends Component { render() { const { localizedTermsOfUse, onOpenExternalLink } = this.props; @@ -23,4 +22,4 @@ class TermsOfUseSettings extends Component { } } -export default TermsOfUseSettings; +export default observer(TermsOfUseSettings); diff --git a/source/renderer/app/components/settings/categories/WalletsSettings.tsx b/source/renderer/app/components/settings/categories/WalletsSettings.tsx index 48299eab2f..c4fd4be6e9 100644 --- a/source/renderer/app/components/settings/categories/WalletsSettings.tsx +++ b/source/renderer/app/components/settings/categories/WalletsSettings.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { map } from 'lodash'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { Select } from 'react-polymorph/lib/components/Select'; -import { Link } from 'react-polymorph/lib/components/Link'; +import { Select } from '@react-polymorph/components/Select'; +import { Link } from '@react-polymorph/components/Link'; import NormalSwitch from '../../widgets/forms/NormalSwitch'; import styles from './WalletsSettings.scss'; import { currencyConfig } from '../../../config/currencyConfig'; @@ -54,7 +54,6 @@ type Props = { onOpenExternalLink: (...args: Array) => any; }; -@observer class WalletSettings extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -125,4 +124,4 @@ class WalletSettings extends Component { } } -export default WalletSettings; +export default observer(WalletSettings); diff --git a/source/renderer/app/components/settings/menu/SettingsMenuItem.tsx b/source/renderer/app/components/settings/menu/SettingsMenuItem.tsx index 04f926c328..5e0218e3e3 100644 --- a/source/renderer/app/components/settings/menu/SettingsMenuItem.tsx +++ b/source/renderer/app/components/settings/menu/SettingsMenuItem.tsx @@ -11,7 +11,6 @@ type Props = { disabled?: boolean; }; -@observer class SettingsMenuItem extends Component { render() { const { label, active, disabled, onClick, className } = this.props; @@ -32,4 +31,4 @@ class SettingsMenuItem extends Component { } } -export default SettingsMenuItem; +export default observer(SettingsMenuItem); diff --git a/source/renderer/app/components/sidebar/SidebarCategory.tsx b/source/renderer/app/components/sidebar/SidebarCategory.tsx index cd5fa92a55..d07c86c60c 100644 --- a/source/renderer/app/components/sidebar/SidebarCategory.tsx +++ b/source/renderer/app/components/sidebar/SidebarCategory.tsx @@ -1,9 +1,11 @@ +// @ts-nocheck + import React from 'react'; import SVGInline from 'react-svg-inline'; import { observer } from 'mobx-react'; import classNames from 'classnames'; import { camelCase } from 'lodash'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { injectIntl } from 'react-intl'; import type { SidebarCategoryInfo } from '../../config/sidebarConfig'; import styles from './SidebarCategory.scss'; diff --git a/source/renderer/app/components/sidebar/SidebarMenu.tsx b/source/renderer/app/components/sidebar/SidebarMenu.tsx index 4978c75f2a..0f16faf891 100644 --- a/source/renderer/app/components/sidebar/SidebarMenu.tsx +++ b/source/renderer/app/components/sidebar/SidebarMenu.tsx @@ -10,7 +10,6 @@ type Props = { visible: boolean; }; -@observer class SidebarMenu extends Component { render() { const { children, visible } = this.props; @@ -22,4 +21,4 @@ class SidebarMenu extends Component { } } -export default SidebarMenu; +export default observer(SidebarMenu); diff --git a/source/renderer/app/components/sidebar/wallets/SidebarWalletMenuItem.tsx b/source/renderer/app/components/sidebar/wallets/SidebarWalletMenuItem.tsx index 85890330fd..117db1e912 100644 --- a/source/renderer/app/components/sidebar/wallets/SidebarWalletMenuItem.tsx +++ b/source/renderer/app/components/sidebar/wallets/SidebarWalletMenuItem.tsx @@ -30,7 +30,6 @@ type Props = { searchValue: string; }; -@observer class SidebarWalletMenuItem extends Component { render() { const { @@ -112,4 +111,4 @@ class SidebarWalletMenuItem extends Component { } } -export default SidebarWalletMenuItem; +export default observer(SidebarWalletMenuItem); diff --git a/source/renderer/app/components/sidebar/wallets/SidebarWalletsMenu.spec.tsx b/source/renderer/app/components/sidebar/wallets/SidebarWalletsMenu.spec.tsx deleted file mode 100644 index a07d0c50e3..0000000000 --- a/source/renderer/app/components/sidebar/wallets/SidebarWalletsMenu.spec.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React from 'react'; -import { IntlProvider } from 'react-intl'; -import { render, screen, cleanup } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import { Provider as MobxProvider } from 'mobx-react'; -import translations from '../../../i18n/locales/en-US.json'; -import StoryDecorator from '../../../../../../storybook/stories/_support/StoryDecorator'; -import { - DiscreetModeFeatureProvider, - BrowserLocalStorageBridge, -} from '../../../features'; -import type { SidebarWalletType } from '../../../types/sidebarTypes'; -import SidebarWalletsMenu from './SidebarWalletsMenu'; - -describe('Sidebar Wallets Menu', () => { - afterEach(cleanup); - - function TestDecorator({ - wallets, - searchValue, - }: { - wallets: Array; - searchValue: string; - }) { - return ( - - - - - - {}} - isActiveWallet={() => false} - isAddWalletButtonActive={false} - isShelleyActivated - visible={false} - searchValue={searchValue} - /> - - - - - - ); - } - - function createWallet() { - let id = 0; - return (title: string): SidebarWalletType => { - id += 1; - return { - id: id.toString(), - title, - // @ts-ignore ts-migrate(2322) FIXME: Type 'number' is not assignable to type 'BigNumber... Remove this comment to see the full error message - amount: 0, - isConnected: false, - isRestoreActive: false, - restoreProgress: 0, - isNotResponding: false, - isLegacy: false, - createdAt: new Date(), - recoveryPhraseVerificationDate: new Date(), - hasNotification: false, - }; - }; - } - - const wallet = createWallet(); - const wallets = [ - wallet('Loki'), - wallet('Ledger Nano X'), - wallet('Ledger Nano S'), - wallet('Odin'), - wallet('byron'), - wallet('byron new new1'), - ]; - test.each([ - [' ', ['Ledger Nano X', 'Ledger Nano S', 'byron new new1']], - ['OKI', ['Loki', 'Odin']], - ['byron1111', ['byron', 'byron new new1']], - ['brom', ['byron', 'byron new new1']], - ['asdf', []], - ['legasdf', []], - ])( - ' should filter by %s and display %s', - (searchValue, results) => { - const assertVisibleItems = 1; - const assertSearchResult = results.length; - expect.assertions(assertSearchResult + assertVisibleItems); - render(); - results.forEach((r) => expect(screen.queryByTestId(r)).toBeVisible()); - expect(screen.queryAllByTestId('walletMenu')).toHaveLength( - assertSearchResult - ); - } - ); -}); diff --git a/source/renderer/app/components/sidebar/wallets/SidebarWalletsMenu.tsx b/source/renderer/app/components/sidebar/wallets/SidebarWalletsMenu.tsx index c70f9fb69d..ad9e3e0509 100644 --- a/source/renderer/app/components/sidebar/wallets/SidebarWalletsMenu.tsx +++ b/source/renderer/app/components/sidebar/wallets/SidebarWalletsMenu.tsx @@ -76,7 +76,6 @@ type Props = { onSearch?: (term: string) => void; }; -@observer class SidebarWalletsMenu extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -85,10 +84,8 @@ class SidebarWalletsMenu extends Component {
); walletSort = (sortBy: WalletSortByOptions) => { - const { - sortOrder = WalletSortOrder.asc, - onWalletSortBy = noop, - } = this.props; + const { sortOrder = WalletSortOrder.asc, onWalletSortBy = noop } = + this.props; return onWalletSortBy({ sortBy, sortOrder, @@ -214,4 +211,4 @@ class SidebarWalletsMenu extends Component { } } -export default SidebarWalletsMenu; +export default observer(SidebarWalletsMenu); diff --git a/source/renderer/app/components/sidebar/wallets/WalletSearch.tsx b/source/renderer/app/components/sidebar/wallets/WalletSearch.tsx index 6a486b3b64..03ee44cd50 100644 --- a/source/renderer/app/components/sidebar/wallets/WalletSearch.tsx +++ b/source/renderer/app/components/sidebar/wallets/WalletSearch.tsx @@ -1,7 +1,9 @@ +// @ts-nocheck + import React from 'react'; import { intlShape, injectIntl, defineMessages } from 'react-intl'; import { noop } from 'lodash/fp'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Input } from '@react-polymorph/components/Input'; import SVGInline from 'react-svg-inline'; import { observer } from 'mobx-react'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/search.... Remove this comment to see the full error message diff --git a/source/renderer/app/components/sidebar/wallets/WalletSortButton.tsx b/source/renderer/app/components/sidebar/wallets/WalletSortButton.tsx index 9fd5460449..0e20418e8b 100644 --- a/source/renderer/app/components/sidebar/wallets/WalletSortButton.tsx +++ b/source/renderer/app/components/sidebar/wallets/WalletSortButton.tsx @@ -1,8 +1,8 @@ import React from 'react'; import SVGInline from 'react-svg-inline'; import classNames from 'classnames'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { Button } from '@react-polymorph/components/Button'; +import { PopOver } from '@react-polymorph/components/PopOver'; import type { WalletSortOrderOptions } from '../../../types/sidebarTypes'; import { WalletSortOrder } from '../../../types/sidebarTypes'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/sort-ar... Remove this comment to see the full error message diff --git a/source/renderer/app/components/splash/SplashNetworkFlight.tsx b/source/renderer/app/components/splash/SplashNetworkFlight.tsx index e284f65609..5f709e03a8 100644 --- a/source/renderer/app/components/splash/SplashNetworkFlight.tsx +++ b/source/renderer/app/components/splash/SplashNetworkFlight.tsx @@ -1,5 +1,5 @@ -import React, { Component } from 'react'; -import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; +import React from 'react'; +import { defineMessages, injectIntl, FormattedHTMLMessage } from 'react-intl'; import Splash from '../widgets/splash/Splash'; const messages = defineMessages({ @@ -64,47 +64,51 @@ const messages = defineMessages({ description: '"Learn more" link URL on the network splash screen', }, }); + type Props = { onClose: (...args: Array) => any; openExternalLink: (...args: Array) => any; + intl: any; // Add the intl prop type }; -export default class SplashNetworkFlight extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - render() { - const { intl } = this.context; - const { onClose, openExternalLink } = this.props; - const title = intl.formatMessage(messages.title); - const subTitle1 = intl.formatMessage(messages.versionName); - const subTitle2 = intl.formatMessage(messages.networkName); - const description = ( - <> -

{intl.formatMessage(messages.flightDescription1)}

- -

{intl.formatMessage(messages.flightDescription3)}

-

{intl.formatMessage(messages.flightDescription4)}

-

{intl.formatMessage(messages.flightDescription5)}

- - ); - const buttonLabel = intl.formatMessage(messages.buttonLabel); - const linkLabel = intl.formatMessage(messages.linkLabel); +const SplashNetworkFlightBase: React.FC = ({ + onClose, + openExternalLink, + intl, +}) => { + // Use intl from props instead of useIntl hook + const title = intl.formatMessage(messages.title); + const subTitle1 = intl.formatMessage(messages.versionName); + const subTitle2 = intl.formatMessage(messages.networkName); + const description = ( + <> +

{intl.formatMessage(messages.flightDescription1)}

+ +

{intl.formatMessage(messages.flightDescription3)}

+

{intl.formatMessage(messages.flightDescription4)}

+

{intl.formatMessage(messages.flightDescription5)}

+ + ); + const buttonLabel = intl.formatMessage(messages.buttonLabel); + const linkLabel = intl.formatMessage(messages.linkLabel); + + const onLinkClick = () => + openExternalLink(intl.formatMessage(messages.linkUrl)); + + return ( + + ); +}; - const onLinkClick = () => - openExternalLink(intl.formatMessage(messages.linkUrl)); +const SplashNetworkFlight = injectIntl(SplashNetworkFlightBase); - return ( - - ); - } -} +export default SplashNetworkFlight; diff --git a/source/renderer/app/components/staking/StakingUnavailable.tsx b/source/renderer/app/components/staking/StakingUnavailable.tsx index d511ff0f9c..61fc04f789 100644 --- a/source/renderer/app/components/staking/StakingUnavailable.tsx +++ b/source/renderer/app/components/staking/StakingUnavailable.tsx @@ -10,7 +10,6 @@ type Props = { syncPercentage: number; }; -@observer class StakingUnavailable extends Component { render() { const { syncPercentage } = this.props; @@ -30,4 +29,4 @@ class StakingUnavailable extends Component { } } -export default StakingUnavailable; +export default observer(StakingUnavailable); diff --git a/source/renderer/app/components/staking/countdown/StakingCountdown.tsx b/source/renderer/app/components/staking/countdown/StakingCountdown.tsx index 0c13d7d35c..521d583c56 100644 --- a/source/renderer/app/components/staking/countdown/StakingCountdown.tsx +++ b/source/renderer/app/components/staking/countdown/StakingCountdown.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import CountdownWidget from '../../widgets/CountdownWidget'; import styles from './StakingCountdown.scss'; import ButtonLink from '../../widgets/ButtonLink'; @@ -35,18 +35,14 @@ type Props = { onLearnMoreClick: (...args: Array) => any; }; -@observer class StakingCountdown extends Component { static contextTypes = { intl: intlShape.isRequired, }; render() { - const { - redirectToStakingInfo, - startDateTime, - onLearnMoreClick, - } = this.props; + const { redirectToStakingInfo, startDateTime, onLearnMoreClick } = + this.props; const { intl } = this.context; const heading = intl.formatMessage(messages.heading); const description = intl.formatMessage(messages.description); @@ -81,4 +77,4 @@ class StakingCountdown extends Component { } } -export default StakingCountdown; +export default observer(StakingCountdown); diff --git a/source/renderer/app/components/staking/delegation-center/DelegationCenter.tsx b/source/renderer/app/components/staking/delegation-center/DelegationCenter.tsx index 5209f620ba..35a6ded4a4 100644 --- a/source/renderer/app/components/staking/delegation-center/DelegationCenter.tsx +++ b/source/renderer/app/components/staking/delegation-center/DelegationCenter.tsx @@ -31,7 +31,6 @@ type Props = { listName?: string; }; -@observer class DelegationCenter extends Component { render() { const { @@ -88,4 +87,4 @@ class DelegationCenter extends Component { } } -export default DelegationCenter; +export default observer(DelegationCenter); diff --git a/source/renderer/app/components/staking/delegation-center/DelegationCenterBody.tsx b/source/renderer/app/components/staking/delegation-center/DelegationCenterBody.tsx index 22c8c611c8..1c33e6e085 100644 --- a/source/renderer/app/components/staking/delegation-center/DelegationCenterBody.tsx +++ b/source/renderer/app/components/staking/delegation-center/DelegationCenterBody.tsx @@ -45,7 +45,6 @@ type Props = { listName?: string; }; -@observer class DelegationCenterBody extends Component { loadingSpinner: LoadingSpinner | null | undefined; static contextTypes = { @@ -138,4 +137,4 @@ class DelegationCenterBody extends Component { } } -export default DelegationCenterBody; +export default observer(DelegationCenterBody); diff --git a/source/renderer/app/components/staking/delegation-center/DelegationCenterHeader.tsx b/source/renderer/app/components/staking/delegation-center/DelegationCenterHeader.tsx index 0392df3b70..0613dafe52 100644 --- a/source/renderer/app/components/staking/delegation-center/DelegationCenterHeader.tsx +++ b/source/renderer/app/components/staking/delegation-center/DelegationCenterHeader.tsx @@ -57,7 +57,6 @@ type State = { timeUntilFutureEpoch: number; }; -@observer class DelegationCenterHeader extends Component { // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. intervalHandler: IntervalID | null | undefined = null; @@ -179,4 +178,4 @@ class DelegationCenterHeader extends Component { } } -export default DelegationCenterHeader; +export default observer(DelegationCenterHeader); diff --git a/source/renderer/app/components/staking/delegation-center/DelegationCenterNoWallets.tsx b/source/renderer/app/components/staking/delegation-center/DelegationCenterNoWallets.tsx index ad82c18e0d..16cacc4ca0 100644 --- a/source/renderer/app/components/staking/delegation-center/DelegationCenterNoWallets.tsx +++ b/source/renderer/app/components/staking/delegation-center/DelegationCenterNoWallets.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { defineMessages, intlShape } from 'react-intl'; import SVGInline from 'react-svg-inline'; import BigNumber from 'bignumber.js'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import styles from './DelegationCenterNoWallets.scss'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/attenti... Remove this comment to see the full error message import icon from '../../../assets/images/attention-big-thin.inline.svg'; diff --git a/source/renderer/app/components/staking/delegation-center/DonutRing.tsx b/source/renderer/app/components/staking/delegation-center/DonutRing.tsx index 1496e76682..3fc3e4a652 100644 --- a/source/renderer/app/components/staking/delegation-center/DonutRing.tsx +++ b/source/renderer/app/components/staking/delegation-center/DonutRing.tsx @@ -9,7 +9,6 @@ type Props = { showText?: boolean; }; -@observer class DonutRing extends Component { static defaultProps = { showText: false, @@ -62,4 +61,4 @@ class DonutRing extends Component { } } -export default DonutRing; +export default observer(DonutRing); diff --git a/source/renderer/app/components/staking/delegation-center/DropdownMenu.tsx b/source/renderer/app/components/staking/delegation-center/DropdownMenu.tsx index 93440c8942..8d42e7f361 100644 --- a/source/renderer/app/components/staking/delegation-center/DropdownMenu.tsx +++ b/source/renderer/app/components/staking/delegation-center/DropdownMenu.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; -import { Dropdown } from 'react-polymorph/lib/components/Dropdown'; +import { Dropdown } from '@react-polymorph/components/Dropdown'; import styles from './DropdownMenu.scss'; type Props = { @@ -12,7 +12,6 @@ type Props = { onMenuItemClick: (...args: Array) => any; }; -@observer class DropdownMenu extends Component { optionRenderer = (option: any) => ( {option.label} @@ -39,4 +38,4 @@ class DropdownMenu extends Component { } } -export default DropdownMenu; +export default observer(DropdownMenu); diff --git a/source/renderer/app/components/staking/delegation-center/WalletRow.tsx b/source/renderer/app/components/staking/delegation-center/WalletRow.tsx index 9f3ad32f62..fb18227f2c 100644 --- a/source/renderer/app/components/staking/delegation-center/WalletRow.tsx +++ b/source/renderer/app/components/staking/delegation-center/WalletRow.tsx @@ -4,7 +4,7 @@ import { get } from 'lodash'; import { defineMessages, intlShape } from 'react-intl'; import SVGInline from 'react-svg-inline'; import classnames from 'classnames'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import Wallet, { WalletDelegationStatuses } from '../../../domains/Wallet'; import type { WalletNextDelegation } from '../../../api/wallets/types'; import StakePool from '../../../domains/StakePool'; @@ -102,7 +102,6 @@ const initialWalletRowState = { highlightedPoolId: false, }; -@observer class WalletRow extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -236,10 +235,11 @@ class WalletRow extends Component { const nextPendingDelegatedStakePool = nextPendingDelegatedStakePoolId ? getStakePoolById(nextPendingDelegatedStakePoolId) : null; - const futurePendingDelegatedStakePoolId = this.getPendingDelegatedStakePoolId( - futureEpochNumber, - nextPendingDelegatedStakePoolId - ); + const futurePendingDelegatedStakePoolId = + this.getPendingDelegatedStakePoolId( + futureEpochNumber, + nextPendingDelegatedStakePoolId + ); const futurePendingDelegatedStakePool = futurePendingDelegatedStakePoolId ? getStakePoolById(futurePendingDelegatedStakePoolId) : null; @@ -474,4 +474,4 @@ class WalletRow extends Component { } } -export default WalletRow; +export default observer(WalletRow); diff --git a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationSetupWizardDialog.tsx b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationSetupWizardDialog.tsx index 9ba40b35e1..7c2771d082 100644 --- a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationSetupWizardDialog.tsx +++ b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationSetupWizardDialog.tsx @@ -64,7 +64,6 @@ const getOversaturationPercentage = ( return selectedPool.saturation + percentageIncrease - 100; }; -@observer class DelegationSetupWizardDialog extends Component { componentDidUpdate(prevProps: Props) { // On confirm delegation step, wait for API stake pool "join" endpoint response @@ -214,4 +213,4 @@ class DelegationSetupWizardDialog extends Component { } } -export default DelegationSetupWizardDialog; +export default observer(DelegationSetupWizardDialog); diff --git a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsChooseStakePoolDialog.tsx b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsChooseStakePoolDialog.tsx index 9200224db4..9bb90950b8 100644 --- a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsChooseStakePoolDialog.tsx +++ b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsChooseStakePoolDialog.tsx @@ -13,8 +13,8 @@ import { intlShape, } from 'react-intl'; import classNames from 'classnames'; -import { Stepper } from 'react-polymorph/lib/components/Stepper'; -import { StepperSkin } from 'react-polymorph/lib/skins/simple/StepperSkin'; +import { Stepper } from '@react-polymorph/components/Stepper'; +import { StepperSkin } from '@react-polymorph/skins/simple/StepperSkin'; import { find } from 'lodash'; import { observer } from 'mobx-react'; import DialogCloseButton from '../../widgets/DialogCloseButton'; diff --git a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsChooseWalletDialog.tsx b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsChooseWalletDialog.tsx index b046e558e2..73aa9ab74d 100644 --- a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsChooseWalletDialog.tsx +++ b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsChooseWalletDialog.tsx @@ -6,8 +6,8 @@ import { FormattedMessage, } from 'react-intl'; import classNames from 'classnames'; -import { Stepper } from 'react-polymorph/lib/components/Stepper'; -import { StepperSkin } from 'react-polymorph/lib/skins/simple/StepperSkin'; +import { Stepper } from '@react-polymorph/components/Stepper'; +import { StepperSkin } from '@react-polymorph/skins/simple/StepperSkin'; import commonStyles from './DelegationSteps.scss'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module './DelegationStepsChooseWalletD... Remove this comment to see the full error message import styles from './DelegationStepsChooseWalletDialog.scss'; diff --git a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsConfirmationDialog.tsx b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsConfirmationDialog.tsx index 14b47e723f..8e20a9dc57 100644 --- a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsConfirmationDialog.tsx +++ b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsConfirmationDialog.tsx @@ -1,13 +1,15 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { intlShape, FormattedMessage, FormattedHTMLMessage } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; import classNames from 'classnames'; import { get } from 'lodash'; import { observer } from 'mobx-react'; -import { Stepper } from 'react-polymorph/lib/components/Stepper'; -import { StepperSkin } from 'react-polymorph/lib/skins/simple/StepperSkin'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Stepper } from '@react-polymorph/components/Stepper'; +import { StepperSkin } from '@react-polymorph/skins/simple/StepperSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import commonStyles from './DelegationSteps.scss'; import styles from './DelegationStepsConfirmationDialog.scss'; import DialogCloseButton from '../../widgets/DialogCloseButton'; @@ -53,7 +55,6 @@ interface FormFields { spendingPassword: string; } -@observer class DelegationStepsConfirmationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -291,4 +292,4 @@ class DelegationStepsConfirmationDialog extends Component { } } -export default DelegationStepsConfirmationDialog; +export default observer(DelegationStepsConfirmationDialog); diff --git a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsSuccessDialog.tsx b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsSuccessDialog.tsx index 3362fa7124..557af7876e 100644 --- a/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsSuccessDialog.tsx +++ b/source/renderer/app/components/staking/delegation-setup-wizard/DelegationStepsSuccessDialog.tsx @@ -54,7 +54,6 @@ type State = { timeUntilNextEpochStart: number; }; -@observer class DelegationStepsSuccessDialog extends Component { // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. intervalHandler: IntervalID | null | undefined = null; @@ -95,12 +94,8 @@ class DelegationStepsSuccessDialog extends Component { render() { const { intl } = this.context; - const { - delegatedWallet, - delegatedStakePool, - currentLocale, - onClose, - } = this.props; + const { delegatedWallet, delegatedStakePool, currentLocale, onClose } = + this.props; const actions = [ { className: 'closeButton', @@ -156,4 +151,4 @@ class DelegationStepsSuccessDialog extends Component { } } -export default DelegationStepsSuccessDialog; +export default observer(DelegationStepsSuccessDialog); diff --git a/source/renderer/app/components/staking/epochs/StakingEpochs.tsx b/source/renderer/app/components/staking/epochs/StakingEpochs.tsx index 08d224ad12..4f61022561 100644 --- a/source/renderer/app/components/staking/epochs/StakingEpochs.tsx +++ b/source/renderer/app/components/staking/epochs/StakingEpochs.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { Select } from 'react-polymorph/lib/components/Select'; -import { SelectSkin } from 'react-polymorph/lib/skins/simple/SelectSkin'; +import { Select } from '@react-polymorph/components/Select'; +import { SelectSkin } from '@react-polymorph/skins/simple/SelectSkin'; import BorderedBox from '../../widgets/BorderedBox'; import LoadingSpinner from '../../widgets/LoadingSpinner'; import StakingEpochsCurrentEpochData from './StakingEpochsCurrentEpochData'; @@ -42,7 +42,6 @@ type State = { }; const { CURRENT_EPOCH, PREVIOUS_EPOCH } = SELECTED_EPOCH_OPTIONS; -@observer class StakingEpochs extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -164,4 +163,4 @@ class StakingEpochs extends Component { } } -export default StakingEpochs; +export default observer(StakingEpochs); diff --git a/source/renderer/app/components/staking/epochs/StakingEpochsCurrentEpochData.tsx b/source/renderer/app/components/staking/epochs/StakingEpochsCurrentEpochData.tsx index f672bb1e01..4c6db8853f 100644 --- a/source/renderer/app/components/staking/epochs/StakingEpochsCurrentEpochData.tsx +++ b/source/renderer/app/components/staking/epochs/StakingEpochsCurrentEpochData.tsx @@ -34,7 +34,6 @@ type State = { currentEpochDataSortBy: string; }; -@observer class StakingEpochsCurrentEpochData extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -128,4 +127,4 @@ class StakingEpochsCurrentEpochData extends Component { } } -export default StakingEpochsCurrentEpochData; +export default observer(StakingEpochsCurrentEpochData); diff --git a/source/renderer/app/components/staking/epochs/StakingEpochsDataTable.tsx b/source/renderer/app/components/staking/epochs/StakingEpochsDataTable.tsx index edcbd9a464..3caf737204 100644 --- a/source/renderer/app/components/staking/epochs/StakingEpochsDataTable.tsx +++ b/source/renderer/app/components/staking/epochs/StakingEpochsDataTable.tsx @@ -21,16 +21,10 @@ type Props = { handleDataSort: (...args: Array) => any; }; -@observer class StakingEpochsDataTable extends Component { render() { - const { - tableHeaders, - tableBody, - order, - sortBy, - handleDataSort, - } = this.props; + const { tableHeaders, tableBody, order, sortBy, handleDataSort } = + this.props; return ( @@ -64,4 +58,4 @@ class StakingEpochsDataTable extends Component { } } -export default StakingEpochsDataTable; +export default observer(StakingEpochsDataTable); diff --git a/source/renderer/app/components/staking/epochs/StakingEpochsNoData.tsx b/source/renderer/app/components/staking/epochs/StakingEpochsNoData.tsx index 32e993f5df..5a5a359bc9 100644 --- a/source/renderer/app/components/staking/epochs/StakingEpochsNoData.tsx +++ b/source/renderer/app/components/staking/epochs/StakingEpochsNoData.tsx @@ -11,7 +11,6 @@ const messages = defineMessages({ }, }); -@observer class StakingEpochsNoData extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -27,4 +26,4 @@ class StakingEpochsNoData extends Component { } } -export default StakingEpochsNoData; +export default observer(StakingEpochsNoData); diff --git a/source/renderer/app/components/staking/epochs/StakingEpochsPreviousEpochData.tsx b/source/renderer/app/components/staking/epochs/StakingEpochsPreviousEpochData.tsx index 084f8d5f41..096150a30a 100644 --- a/source/renderer/app/components/staking/epochs/StakingEpochsPreviousEpochData.tsx +++ b/source/renderer/app/components/staking/epochs/StakingEpochsPreviousEpochData.tsx @@ -55,7 +55,6 @@ type State = { previousEpochDataSortBy: string; }; -@observer class StakingEpochsPreviousEpochData extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -174,4 +173,4 @@ class StakingEpochsPreviousEpochData extends Component { } } -export default StakingEpochsPreviousEpochData; +export default observer(StakingEpochsPreviousEpochData); diff --git a/source/renderer/app/components/staking/info/StakingInfo.tsx b/source/renderer/app/components/staking/info/StakingInfo.tsx index 8b87f6af1b..677b6cf0c4 100644 --- a/source/renderer/app/components/staking/info/StakingInfo.tsx +++ b/source/renderer/app/components/staking/info/StakingInfo.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedMessage } from 'react-intl'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import ButtonLink from '../../widgets/ButtonLink'; import styles from './StakingInfo.scss'; @@ -44,7 +44,6 @@ type State = { progressLabelClassName: string; }; -@observer class StakingInfo extends Component { static defaultProps = { percentage: 0, @@ -140,4 +139,4 @@ class StakingInfo extends Component { } } -export default StakingInfo; +export default observer(StakingInfo); diff --git a/source/renderer/app/components/staking/info/StakingInfoCountdown.tsx b/source/renderer/app/components/staking/info/StakingInfoCountdown.tsx index 54fb614b60..752d3542b2 100644 --- a/source/renderer/app/components/staking/info/StakingInfoCountdown.tsx +++ b/source/renderer/app/components/staking/info/StakingInfoCountdown.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import ButtonLink from '../../widgets/ButtonLink'; import styles from './StakingInfoCountdown.scss'; import FullyDecentralizedEffect from '../../widgets/FullyDecentralizedEffect'; @@ -50,7 +50,6 @@ type Props = { onLearnMoreClick: (...args: Array) => any; }; -@observer class StakingInfoCountdown extends Component { static defaultProps = { percentage: 0, @@ -68,11 +67,8 @@ class StakingInfoCountdown extends Component { } checkIfShouldSetStakingInfoWasOpen = () => { - const { - onSetStakingInfoWasOpen, - isAlonzoActivated, - stakingInfoWasOpen, - } = this.props; + const { onSetStakingInfoWasOpen, isAlonzoActivated, stakingInfoWasOpen } = + this.props; if (isAlonzoActivated && !stakingInfoWasOpen) { onSetStakingInfoWasOpen(); @@ -122,4 +118,4 @@ class StakingInfoCountdown extends Component { } } -export default StakingInfoCountdown; +export default observer(StakingInfoCountdown); diff --git a/source/renderer/app/components/staking/layouts/StakingWithNavigation.tsx b/source/renderer/app/components/staking/layouts/StakingWithNavigation.tsx index 1048bc290b..1eec007f1a 100644 --- a/source/renderer/app/components/staking/layouts/StakingWithNavigation.tsx +++ b/source/renderer/app/components/staking/layouts/StakingWithNavigation.tsx @@ -18,7 +18,6 @@ export const StakingPageScrollContext = React.createContext({ scrollElementRef: null, }); -@observer class StakingWithNavigation extends Component { stakingPageScrollContext = { scrollElementRef: createRef() }; @@ -54,4 +53,4 @@ class StakingWithNavigation extends Component { } } -export default StakingWithNavigation; +export default observer(StakingWithNavigation); diff --git a/source/renderer/app/components/staking/legacy/BlockGenerationInfo.tsx b/source/renderer/app/components/staking/legacy/BlockGenerationInfo.tsx index 1450d906d1..28c1b75293 100644 --- a/source/renderer/app/components/staking/legacy/BlockGenerationInfo.tsx +++ b/source/renderer/app/components/staking/legacy/BlockGenerationInfo.tsx @@ -2,7 +2,6 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import styles from './BlockGenerationInfo.scss'; -@observer class BlockGenerationInfo extends Component { render() { return ( @@ -20,4 +19,4 @@ class BlockGenerationInfo extends Component { } } -export default BlockGenerationInfo; +export default observer(BlockGenerationInfo); diff --git a/source/renderer/app/components/staking/legacy/Staking.tsx b/source/renderer/app/components/staking/legacy/Staking.tsx index 28946c20b1..6db5a7a273 100644 --- a/source/renderer/app/components/staking/legacy/Staking.tsx +++ b/source/renderer/app/components/staking/legacy/Staking.tsx @@ -5,7 +5,6 @@ import StakingSwitch from './StakingSwitch'; import StakingSystemState from './StakingSystemState'; import styles from './Staking.scss'; -@observer class Settings extends Component { render() { return ( @@ -24,4 +23,4 @@ class Settings extends Component { } } -export default Settings; +export default observer(Settings); diff --git a/source/renderer/app/components/staking/legacy/StakingChart.tsx b/source/renderer/app/components/staking/legacy/StakingChart.tsx index d5af81d92e..4b4a5be4c5 100644 --- a/source/renderer/app/components/staking/legacy/StakingChart.tsx +++ b/source/renderer/app/components/staking/legacy/StakingChart.tsx @@ -33,7 +33,6 @@ type State = { | undefined; }; -@observer class StakingChart extends Component { state = { isHovered: false, @@ -140,4 +139,4 @@ class StakingChart extends Component { } } -export default StakingChart; +export default observer(StakingChart); diff --git a/source/renderer/app/components/staking/legacy/StakingChartTooltip.tsx b/source/renderer/app/components/staking/legacy/StakingChartTooltip.tsx index 2d1642e85f..6e9b1ba0f5 100644 --- a/source/renderer/app/components/staking/legacy/StakingChartTooltip.tsx +++ b/source/renderer/app/components/staking/legacy/StakingChartTooltip.tsx @@ -49,7 +49,6 @@ type Props = { shares: string; }; -@observer class StakingChartTooltip extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -104,4 +103,4 @@ class StakingChartTooltip extends Component { } } -export default StakingChartTooltip; +export default observer(StakingChartTooltip); diff --git a/source/renderer/app/components/staking/legacy/StakingChartTooltipItem.tsx b/source/renderer/app/components/staking/legacy/StakingChartTooltipItem.tsx index 3511d58920..3333062035 100644 --- a/source/renderer/app/components/staking/legacy/StakingChartTooltipItem.tsx +++ b/source/renderer/app/components/staking/legacy/StakingChartTooltipItem.tsx @@ -7,7 +7,6 @@ type Props = { label: string; }; -@observer class StakingChartTooltipItem extends Component { render() { const { value, label } = this.props; @@ -19,4 +18,4 @@ class StakingChartTooltipItem extends Component { } } -export default StakingChartTooltipItem; +export default observer(StakingChartTooltipItem); diff --git a/source/renderer/app/components/staking/legacy/StakingSwitch.tsx b/source/renderer/app/components/staking/legacy/StakingSwitch.tsx index 4a297c3281..8d367240c9 100644 --- a/source/renderer/app/components/staking/legacy/StakingSwitch.tsx +++ b/source/renderer/app/components/staking/legacy/StakingSwitch.tsx @@ -1,15 +1,14 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { SwitchSkin } from 'react-polymorph/lib/skins/simple/SwitchSkin'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { SwitchSkin } from '@react-polymorph/skins/simple/SwitchSkin'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; import styles from './StakingSwitch.scss'; type Props = { active: boolean; }; -@observer class StakingSwitch extends Component { handleChange = () => {}; @@ -29,4 +28,4 @@ class StakingSwitch extends Component { } } -export default StakingSwitch; +export default observer(StakingSwitch); diff --git a/source/renderer/app/components/staking/legacy/StakingSystemState.tsx b/source/renderer/app/components/staking/legacy/StakingSystemState.tsx index f25886ea2f..5cf58f6495 100644 --- a/source/renderer/app/components/staking/legacy/StakingSystemState.tsx +++ b/source/renderer/app/components/staking/legacy/StakingSystemState.tsx @@ -3,7 +3,6 @@ import { observer } from 'mobx-react'; import StakingSystemStateElement from './StakingSystemStateElement'; import styles from './StakingSystemState.scss'; -@observer class StakingSystemState extends Component { render() { return ( @@ -21,4 +20,4 @@ class StakingSystemState extends Component { } } -export default StakingSystemState; +export default observer(StakingSystemState); diff --git a/source/renderer/app/components/staking/legacy/StakingSystemStateElement.tsx b/source/renderer/app/components/staking/legacy/StakingSystemStateElement.tsx index 00ae1b7cd5..6ca85742f6 100644 --- a/source/renderer/app/components/staking/legacy/StakingSystemStateElement.tsx +++ b/source/renderer/app/components/staking/legacy/StakingSystemStateElement.tsx @@ -7,7 +7,6 @@ type Props = { label: string; }; -@observer class StakingSystemState extends Component { render() { const { value, label } = this.props; @@ -19,4 +18,4 @@ class StakingSystemState extends Component { } } -export default StakingSystemState; +export default observer(StakingSystemState); diff --git a/source/renderer/app/components/staking/navigation/StakingNavigation.tsx b/source/renderer/app/components/staking/navigation/StakingNavigation.tsx index f39f3564b9..e86ffd6989 100644 --- a/source/renderer/app/components/staking/navigation/StakingNavigation.tsx +++ b/source/renderer/app/components/staking/navigation/StakingNavigation.tsx @@ -39,19 +39,14 @@ type Props = { isActiveNavItem: (...args: Array) => any; }; -@observer class StakingNavigation extends Component { static contextTypes = { intl: intlShape.isRequired, }; render() { - const { - onNavItemClick, - activeItem, - isActiveNavItem, - showInfoTab, - } = this.props; + const { onNavItemClick, activeItem, isActiveNavItem, showInfoTab } = + this.props; const { intl } = this.context; const navigationItems = [ { @@ -89,4 +84,4 @@ class StakingNavigation extends Component { } } -export default StakingNavigation; +export default observer(StakingNavigation); diff --git a/source/renderer/app/components/staking/redeem-itn-rewards/NoWalletsDialog.tsx b/source/renderer/app/components/staking/redeem-itn-rewards/NoWalletsDialog.tsx index bc168465a2..e9d1d832d1 100644 --- a/source/renderer/app/components/staking/redeem-itn-rewards/NoWalletsDialog.tsx +++ b/source/renderer/app/components/staking/redeem-itn-rewards/NoWalletsDialog.tsx @@ -29,7 +29,6 @@ type Props = { onAddWallet: (...args: Array) => any; }; -@observer class NoWalletsDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -70,4 +69,4 @@ class NoWalletsDialog extends Component { } } -export default NoWalletsDialog; +export default observer(NoWalletsDialog); diff --git a/source/renderer/app/components/staking/redeem-itn-rewards/RedemptionUnavailableDialog.tsx b/source/renderer/app/components/staking/redeem-itn-rewards/RedemptionUnavailableDialog.tsx index 00bbc11250..26318392cc 100644 --- a/source/renderer/app/components/staking/redeem-itn-rewards/RedemptionUnavailableDialog.tsx +++ b/source/renderer/app/components/staking/redeem-itn-rewards/RedemptionUnavailableDialog.tsx @@ -29,7 +29,6 @@ type Props = { syncPercentage: number; }; -@observer class RedemptionUnavailableDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -77,4 +76,4 @@ class RedemptionUnavailableDialog extends Component { } } -export default RedemptionUnavailableDialog; +export default observer(RedemptionUnavailableDialog); diff --git a/source/renderer/app/components/staking/redeem-itn-rewards/Step1ConfigurationDialog.tsx b/source/renderer/app/components/staking/redeem-itn-rewards/Step1ConfigurationDialog.tsx index fe1a1b7a22..8d1aa8c70f 100644 --- a/source/renderer/app/components/staking/redeem-itn-rewards/Step1ConfigurationDialog.tsx +++ b/source/renderer/app/components/staking/redeem-itn-rewards/Step1ConfigurationDialog.tsx @@ -1,11 +1,13 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { get } from 'lodash'; import vjf from 'mobx-react-form/lib/validators/VJF'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import { defineMessages, FormattedHTMLMessage, @@ -147,7 +149,6 @@ interface FormFields { recoveryPhrase: string; } -@observer class Step1ConfigurationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -332,7 +333,7 @@ class Step1ConfigurationDialog extends Component { onClose={onClose} /> ); - const { reset, ...mnemonicInputProps } = recoveryPhraseField.bind(); + const { ...mnemonicInputProps } = recoveryPhraseField.bind(); return ( { } } -export default Step1ConfigurationDialog; +export default observer(Step1ConfigurationDialog); diff --git a/source/renderer/app/components/staking/redeem-itn-rewards/Step2ConfirmationDialog.tsx b/source/renderer/app/components/staking/redeem-itn-rewards/Step2ConfirmationDialog.tsx index 677c7074ca..344855453c 100644 --- a/source/renderer/app/components/staking/redeem-itn-rewards/Step2ConfirmationDialog.tsx +++ b/source/renderer/app/components/staking/redeem-itn-rewards/Step2ConfirmationDialog.tsx @@ -1,9 +1,11 @@ +// @ts-nocheck + import React, { Component } from 'react'; import BigNumber from 'bignumber.js'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import vjf from 'mobx-react-form/lib/validators/VJF'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; @@ -80,7 +82,6 @@ interface FormFields { spendingPassword: string; } -@observer class Step2ConfirmationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -144,15 +145,8 @@ class Step2ConfirmationDialog extends Component { render() { const { intl } = this.context; const { form } = this; - const { - wallet, - transactionFees, - onContinue, - onClose, - onBack, - isSubmitting, - error, - } = this.props; + const { wallet, transactionFees, onContinue, onClose, onBack, error } = + this.props; const { amount } = wallet || {}; const minRewardsReceiverBalance = new BigNumber( MIN_REWARDS_REDEMPTION_RECEIVER_BALANCE @@ -236,4 +230,4 @@ class Step2ConfirmationDialog extends Component { } } -export default Step2ConfirmationDialog; +export default observer(Step2ConfirmationDialog); diff --git a/source/renderer/app/components/staking/redeem-itn-rewards/Step3FailureDialog.tsx b/source/renderer/app/components/staking/redeem-itn-rewards/Step3FailureDialog.tsx index 241e83f834..796e71a10e 100644 --- a/source/renderer/app/components/staking/redeem-itn-rewards/Step3FailureDialog.tsx +++ b/source/renderer/app/components/staking/redeem-itn-rewards/Step3FailureDialog.tsx @@ -46,7 +46,6 @@ type Props = { onBack: (...args: Array) => any; }; -@observer class Step3FailureDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -97,4 +96,4 @@ class Step3FailureDialog extends Component { } } -export default Step3FailureDialog; +export default observer(Step3FailureDialog); diff --git a/source/renderer/app/components/staking/redeem-itn-rewards/Step3SuccessDialog.tsx b/source/renderer/app/components/staking/redeem-itn-rewards/Step3SuccessDialog.tsx index efd7656bf6..5cceb5762c 100644 --- a/source/renderer/app/components/staking/redeem-itn-rewards/Step3SuccessDialog.tsx +++ b/source/renderer/app/components/staking/redeem-itn-rewards/Step3SuccessDialog.tsx @@ -45,7 +45,6 @@ type Props = { onPDFDownload?: (...args: Array) => any; }; -@observer class Step3SuccessDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -107,4 +106,4 @@ class Step3SuccessDialog extends Component { } } -export default Step3SuccessDialog; +export default observer(Step3SuccessDialog); diff --git a/source/renderer/app/components/staking/rewards/StakingRewards.tsx b/source/renderer/app/components/staking/rewards/StakingRewards.tsx index fbce0f2f4d..1eee4cedea 100644 --- a/source/renderer/app/components/staking/rewards/StakingRewards.tsx +++ b/source/renderer/app/components/staking/rewards/StakingRewards.tsx @@ -4,12 +4,11 @@ import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import SVGInline from 'react-svg-inline'; import { get, map } from 'lodash'; import classNames from 'classnames'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { PopOver } from '@react-polymorph/components/PopOver'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import CopyToClipboard from 'react-copy-to-clipboard'; import { DECIMAL_PLACES_IN_ADA } from '../../../config/numbersConfig'; -import { formattedWalletAmount } from '../../../utils/formatters'; import { bigNumberComparator, stringComparator, @@ -121,355 +120,369 @@ type State = { contentScrollTop: number; }; -@observer -export class StakingRewards extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - static defaultProps = { - isLoading: false, - isExporting: false, - }; - - constructor(props: Props) { - super(props); - this.state = { - rewardsOrder: REWARD_ORDERS.DESCENDING, - rewardsSortBy: REWARD_FIELDS.WALLET_NAME, - contentScrollTop: 0, +export const StakingRewards = observer( + class StakingRewards extends Component { + static contextTypes = { + intl: intlShape.isRequired, + }; + static defaultProps = { + isLoading: false, + isExporting: false, }; - } - handleExportCsv = ( - availableTableHeaders: Array, - sortedRewards: Array - ) => { - const { onExportCsv } = this.props; - const { intl } = this.context; - const exportedHeader = availableTableHeaders.map((header) => header.title); - const exportedBody = sortedRewards.map((reward) => { - const rewardWallet = get(reward, REWARD_FIELDS.WALLET_NAME); - const isRestoring = get(reward, REWARD_FIELDS.IS_RESTORING); - const rewardTotal = get(reward, REWARD_FIELDS.REWARD_TOTAL)?.toFixed( - DECIMAL_PLACES_IN_ADA - ); - const rewardUnspent = get(reward, REWARD_FIELDS.REWARD_UNSPENT)?.toFixed( - DECIMAL_PLACES_IN_ADA - ); - const rewardsAddress = get(reward, REWARD_FIELDS.REWARDS_ADDRESS); - return [ - rewardWallet, - rewardsAddress, - isRestoring ? '-' : rewardTotal, - isRestoring ? '-' : rewardUnspent, - ]; - }); - const exportedContent = [exportedHeader, ...exportedBody]; - onExportCsv({ - fileContent: exportedContent, - filenamePrefix: intl.formatMessage(messages.csvFilenamePrefix), - }); - }; - getSortedRewards = (): Array => { - const { rewards } = this.props; - const { rewardsOrder, rewardsSortBy } = this.state; - return rewards.slice().sort((rewardA: Reward, rewardB: Reward) => { - const totalCompareResult = bigNumberComparator( - rewardA.total, - rewardB.total, - rewardsOrder === REWARD_ORDERS.ASCENDING - ); - const unspentCompareResult = bigNumberComparator( - rewardA.unspent, - rewardB.unspent, - rewardsOrder === REWARD_ORDERS.ASCENDING - ); - const walletNameCompareResult = stringComparator( - rewardA.wallet, - rewardB.wallet, - rewardsOrder === REWARD_ORDERS.ASCENDING - ); - const walletAddressCompareResult = stringComparator( - rewardA.rewardsAddress, - rewardB.rewardsAddress, - rewardsOrder === REWARD_ORDERS.ASCENDING + constructor(props: Props) { + super(props); + this.state = { + rewardsOrder: REWARD_ORDERS.DESCENDING, + rewardsSortBy: REWARD_FIELDS.WALLET_NAME, + contentScrollTop: 0, + }; + } + + handleExportCsv = ( + availableTableHeaders: Array, + sortedRewards: Array + ) => { + const { onExportCsv } = this.props; + const { intl } = this.context; + const exportedHeader = availableTableHeaders.map( + (header) => header.title ); + const exportedBody = sortedRewards.map((reward) => { + const rewardWallet = get(reward, REWARD_FIELDS.WALLET_NAME); + const isRestoring = get(reward, REWARD_FIELDS.IS_RESTORING); + const rewardTotal = get(reward, REWARD_FIELDS.REWARD_TOTAL)?.toFixed( + DECIMAL_PLACES_IN_ADA + ); + const rewardUnspent = get( + reward, + REWARD_FIELDS.REWARD_UNSPENT + )?.toFixed(DECIMAL_PLACES_IN_ADA); + const rewardsAddress = get(reward, REWARD_FIELDS.REWARDS_ADDRESS); + return [ + rewardWallet, + rewardsAddress, + isRestoring ? '-' : rewardTotal, + isRestoring ? '-' : rewardUnspent, + ]; + }); + const exportedContent = [exportedHeader, ...exportedBody]; + onExportCsv({ + fileContent: exportedContent, + filenamePrefix: intl.formatMessage(messages.csvFilenamePrefix), + }); + }; + getSortedRewards = (): Array => { + const { rewards } = this.props; + const { rewardsOrder, rewardsSortBy } = this.state; + return rewards.slice().sort((rewardA: Reward, rewardB: Reward) => { + const totalCompareResult = bigNumberComparator( + rewardA.total, + rewardB.total, + rewardsOrder === REWARD_ORDERS.ASCENDING + ); + const unspentCompareResult = bigNumberComparator( + rewardA.unspent, + rewardB.unspent, + rewardsOrder === REWARD_ORDERS.ASCENDING + ); + const walletNameCompareResult = stringComparator( + rewardA.wallet, + rewardB.wallet, + rewardsOrder === REWARD_ORDERS.ASCENDING + ); + const walletAddressCompareResult = stringComparator( + rewardA.rewardsAddress, + rewardB.rewardsAddress, + rewardsOrder === REWARD_ORDERS.ASCENDING + ); - if (rewardsSortBy === REWARD_FIELDS.REWARD_TOTAL) { - if (totalCompareResult !== 0) return totalCompareResult; - if (walletNameCompareResult !== 0) return walletNameCompareResult; - return walletAddressCompareResult; - } + if (rewardsSortBy === REWARD_FIELDS.REWARD_TOTAL) { + if (totalCompareResult !== 0) return totalCompareResult; + if (walletNameCompareResult !== 0) return walletNameCompareResult; + return walletAddressCompareResult; + } - if (rewardsSortBy === REWARD_FIELDS.REWARD_UNSPENT) { - if (unspentCompareResult !== 0) return unspentCompareResult; - if (walletNameCompareResult !== 0) return walletNameCompareResult; - return walletAddressCompareResult; - } + if (rewardsSortBy === REWARD_FIELDS.REWARD_UNSPENT) { + if (unspentCompareResult !== 0) return unspentCompareResult; + if (walletNameCompareResult !== 0) return walletNameCompareResult; + return walletAddressCompareResult; + } - if (rewardsSortBy === REWARD_FIELDS.WALLET_NAME) { - if (walletNameCompareResult !== 0) return walletNameCompareResult; - if (totalCompareResult !== 0) return totalCompareResult; - return walletAddressCompareResult; - } + if (rewardsSortBy === REWARD_FIELDS.WALLET_NAME) { + if (walletNameCompareResult !== 0) return walletNameCompareResult; + if (totalCompareResult !== 0) return totalCompareResult; + return walletAddressCompareResult; + } - if (rewardsSortBy === REWARD_FIELDS.REWARDS_ADDRESS) { - if (walletAddressCompareResult !== 0) return walletAddressCompareResult; - if (walletNameCompareResult !== 0) return walletNameCompareResult; - return totalCompareResult; - } + if (rewardsSortBy === REWARD_FIELDS.REWARDS_ADDRESS) { + if (walletAddressCompareResult !== 0) + return walletAddressCompareResult; + if (walletNameCompareResult !== 0) return walletNameCompareResult; + return totalCompareResult; + } - return 0; - }); - }; - handleContentScroll = (evt: React.SyntheticEvent) => { - this.setState({ - contentScrollTop: evt.currentTarget.scrollTop, - }); - }; + return 0; + }); + }; + handleContentScroll = (evt: React.SyntheticEvent) => { + this.setState({ + contentScrollTop: evt.currentTarget.scrollTop, + }); + }; - render() { - const { - rewards, - isLoading, - isExporting, - onCopyAddress, - onOpenExternalLink, - } = this.props; - const { rewardsOrder, rewardsSortBy, contentScrollTop } = this.state; - const { intl } = this.context; - const noRewards = !isLoading && ((rewards && !rewards.length) || !rewards); - const showRewards = rewards && rewards.length > 0 && !isLoading; - const sortedRewards = showRewards ? this.getSortedRewards() : []; - const availableTableHeaders = [ - { - name: REWARD_FIELDS.WALLET_NAME, - title: intl.formatMessage(messages.tableHeaderWallet), - }, - { - name: REWARD_FIELDS.REWARDS_ADDRESS, - title: intl.formatMessage(messages.tableHeaderRewardsAddress), - }, - { - name: REWARD_FIELDS.REWARD_TOTAL, - title: `${intl.formatMessage( - messages.tableHeaderRewardTotal - )} (${intl.formatMessage(globalMessages.adaUnit)})`, - }, - { - name: REWARD_FIELDS.REWARD_UNSPENT, - title: `${intl.formatMessage( - messages.tableHeaderRewardUnspent - )} (${intl.formatMessage(globalMessages.adaUnit)})`, - }, - ]; - const exportCsvButtonLabel = isExporting ? ( -
- -
- ) : ( - <> -
- {intl.formatMessage(messages.exportButtonLabel)} + render() { + const { + rewards, + isLoading, + isExporting, + onCopyAddress, + onOpenExternalLink, + } = this.props; + const { rewardsOrder, rewardsSortBy, contentScrollTop } = this.state; + const { intl } = this.context; + const noRewards = + !isLoading && ((rewards && !rewards.length) || !rewards); + const showRewards = rewards && rewards.length > 0 && !isLoading; + const sortedRewards = showRewards ? this.getSortedRewards() : []; + const availableTableHeaders = [ + { + name: REWARD_FIELDS.WALLET_NAME, + title: intl.formatMessage(messages.tableHeaderWallet), + }, + { + name: REWARD_FIELDS.REWARDS_ADDRESS, + title: intl.formatMessage(messages.tableHeaderRewardsAddress), + }, + { + name: REWARD_FIELDS.REWARD_TOTAL, + title: `${intl.formatMessage( + messages.tableHeaderRewardTotal + )} (${intl.formatMessage(globalMessages.adaUnit)})`, + }, + { + name: REWARD_FIELDS.REWARD_UNSPENT, + title: `${intl.formatMessage( + messages.tableHeaderRewardUnspent + )} (${intl.formatMessage(globalMessages.adaUnit)})`, + }, + ]; + const exportCsvButtonLabel = isExporting ? ( +
+
- - - ); - const exportCsvButtonClasses = classNames(['flat', styles.actionButton]); - const explorerButtonClasses = classNames([ - 'flat', - styles.actionExplorerLink, - ]); - const headerWrapperClasses = classNames([ - styles.headerWrapper, - contentScrollTop > 10 ? styles.headerWrapperWithShadow : null, - ]); - return ( -
-
-
- {intl.formatMessage(messages.title)} + ) : ( + <> +
+ {intl.formatMessage(messages.exportButtonLabel)}
- {!noRewards && ( -
-
- - {noRewards && ( -
- {intl.formatMessage(messages.noRewards)} -
+ + + ); + const exportCsvButtonClasses = classNames(['flat', styles.actionButton]); + const explorerButtonClasses = classNames([ + 'flat', + styles.actionExplorerLink, + ]); + const headerWrapperClasses = classNames([ + styles.headerWrapper, + contentScrollTop > 10 ? styles.headerWrapperWithShadow : null, + ]); + return ( +
+
+
+ {intl.formatMessage(messages.title)} +
+ {!noRewards && ( +
+
+ + {noRewards && ( +
+ {intl.formatMessage(messages.noRewards)} +
+ )} - {sortedRewards.length > 0 && ( -
- - - {map(availableTableHeaders, (tableHeader) => { - const isSorted = tableHeader.name === rewardsSortBy; - const sortIconClasses = classNames([ - styles.sortIcon, - isSorted ? styles.sorted : null, - isSorted && rewardsOrder === 'asc' - ? styles.ascending - : null, - ]); + {sortedRewards.length > 0 && ( +
+ + + {map(availableTableHeaders, (tableHeader) => { + const isSorted = tableHeader.name === rewardsSortBy; + const sortIconClasses = classNames([ + styles.sortIcon, + isSorted ? styles.sorted : null, + isSorted && rewardsOrder === 'asc' + ? styles.ascending + : null, + ]); + return ( + + ); + })} + + + + {map(sortedRewards, (reward, key) => { + const rewardWallet = get( + reward, + REWARD_FIELDS.WALLET_NAME + ); + const isRestoring = get( + reward, + REWARD_FIELDS.IS_RESTORING + ); + const syncingProgress = get( + reward, + REWARD_FIELDS.SYNCING_PROGRESS + ); + const rewardTotal = get( + reward, + REWARD_FIELDS.REWARD_TOTAL + ).toFormat(DECIMAL_PLACES_IN_ADA); + const rewardUnspent = get( + reward, + REWARD_FIELDS.REWARD_UNSPENT + ).toFormat(DECIMAL_PLACES_IN_ADA); + const rewardsAddress = get( + reward, + REWARD_FIELDS.REWARDS_ADDRESS + ); return ( - + + + + + + ); })} - - - - {map(sortedRewards, (reward, key) => { - const rewardWallet = get(reward, REWARD_FIELDS.WALLET_NAME); - const isRestoring = get(reward, REWARD_FIELDS.IS_RESTORING); - const syncingProgress = get( - reward, - REWARD_FIELDS.SYNCING_PROGRESS - ); - const rewardTotal = get( - reward, - REWARD_FIELDS.REWARD_TOTAL - ).toFormat(DECIMAL_PLACES_IN_ADA); - const rewardUnspent = get( - reward, - REWARD_FIELDS.REWARD_UNSPENT - ).toFormat(DECIMAL_PLACES_IN_ADA); - const rewardsAddress = get( - reward, - REWARD_FIELDS.REWARDS_ADDRESS - ); - return ( - - - - - - - ); - })} - -
+ this.handleRewardsSort(tableHeader.name) + } + > + {tableHeader.title} + +
- this.handleRewardsSort(tableHeader.name) - } - > - {tableHeader.title} - -
+ {rewardWallet} + + {rewardsAddress && ( +
+ onCopyAddress(rewardsAddress)} + > +
+ + {rewardsAddress} + + + + +
+
+ {IS_EXPLORER_LINK_BUTTON_ENABLED && ( + + onOpenExternalLink(rewardsAddress) + } + skin={ButtonSkin} + label={intl.formatMessage( + messages.actionViewInExplorer + )} + linkProps={{ + className: styles.externalLink, + hasIconBefore: false, + hasIconAfter: true, + }} + /> + )} +
+ )} +
+ {!isRestoring && ( + + )} + {isRestoring && ( +
+ + + +
+ )} +
+ {isRestoring ? ( + '-' + ) : ( + + )} +
{rewardWallet} - {rewardsAddress && ( -
- onCopyAddress(rewardsAddress)} - > -
- - {rewardsAddress} - - - - -
-
- {IS_EXPLORER_LINK_BUTTON_ENABLED && ( - - onOpenExternalLink(rewardsAddress) - } - skin={ButtonSkin} - label={intl.formatMessage( - messages.actionViewInExplorer - )} - linkProps={{ - className: styles.externalLink, - hasIconBefore: false, - hasIconAfter: true, - }} - /> - )} -
- )} -
- {!isRestoring && ( - - )} - {isRestoring && ( -
- - - -
- )} -
- {isRestoring ? ( - '-' - ) : ( - - )} -
- )} + + + )} - {isLoading && ( -
- + {isLoading && ( +
+ +
+ )} + +
+
+
- )} - -
-
-
-
- ); - } + ); + } - handleRewardsSort = (newSortBy: string) => { - const { rewardsOrder, rewardsSortBy } = this.state; - let newRewardsOrder; + handleRewardsSort = (newSortBy: string) => { + const { rewardsOrder, rewardsSortBy } = this.state; + let newRewardsOrder; - if (rewardsSortBy === newSortBy) { - // on same sort change order - newRewardsOrder = rewardsOrder === 'asc' ? 'desc' : 'asc'; - } else { - // on new sort instance, order by initial value 'descending' - newRewardsOrder = 'desc'; - } + if (rewardsSortBy === newSortBy) { + // on same sort change order + newRewardsOrder = rewardsOrder === 'asc' ? 'desc' : 'asc'; + } else { + // on new sort instance, order by initial value 'descending' + newRewardsOrder = 'desc'; + } - this.setState({ - rewardsSortBy: newSortBy, - rewardsOrder: newRewardsOrder, - }); - }; -} + this.setState({ + rewardsSortBy: newSortBy, + rewardsOrder: newRewardsOrder, + }); + }; + } +); diff --git a/source/renderer/app/components/staking/stake-pools/StakePools.tsx b/source/renderer/app/components/staking/stake-pools/StakePools.tsx index c1f1af675a..1196579599 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePools.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePools.tsx @@ -109,7 +109,6 @@ const initialState = { isTableHeaderHovered: false, }; -@observer class StakePools extends Component { loadingSpinner: LoadingSpinner | null | undefined; static contextTypes = { @@ -432,4 +431,4 @@ class StakePools extends Component { } } -export default StakePools; +export default observer(StakePools); diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsList.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsList.tsx index 6740bae46d..9485987cca 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsList.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsList.tsx @@ -1,4 +1,6 @@ -import { times } from 'lodash-es/util'; +// @ts-nocheck + +import { times } from 'lodash'; import { observer } from 'mobx-react'; import React, { useEffect, useState } from 'react'; import type { ElementRef } from 'react'; diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsRanking.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsRanking.tsx index 0e243ebee7..37ec35bb2f 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsRanking.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsRanking.tsx @@ -1,11 +1,13 @@ +// @ts-nocheck + import React, { Component } from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Config'. import type { Config } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import classnames from 'classnames'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { PopOver } from '@react-polymorph/components/PopOver'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import BigNumber from 'bignumber.js'; import { shortNumber, @@ -133,7 +135,6 @@ type State = { displayValue: string; }; -@observer class StakePoolsRanking extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -219,11 +220,8 @@ class StakePoolsRanking extends Component { rankStakePools(); }; onSliderChange = (sliderValue: number) => { - const { - updateDelegatingStake, - maxDelegationFunds, - maxDelegationFundsLog, - } = this.props; + const { updateDelegatingStake, maxDelegationFunds, maxDelegationFundsLog } = + this.props; let amountValue = null; if ( @@ -441,5 +439,5 @@ class StakePoolsRanking extends Component { } export default withDiscreetMode>( - StakePoolsRanking + observer(StakePoolsRanking) ); diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsRankingLoader.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsRankingLoader.tsx index 3c21d89019..371f9caedb 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsRankingLoader.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsRankingLoader.tsx @@ -3,7 +3,6 @@ import { observer } from 'mobx-react'; import LoadingSpinner from '../../widgets/LoadingSpinner'; import styles from './StakePoolsRankingLoader.scss'; -@observer class StakePoolsRankingLoader extends Component { render() { return ( @@ -14,4 +13,4 @@ class StakePoolsRankingLoader extends Component { } } -export default StakePoolsRankingLoader; +export default observer(StakePoolsRankingLoader); diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsSearch.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsSearch.tsx index 83d71ce9cc..fddc0baa61 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsSearch.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsSearch.tsx @@ -1,9 +1,11 @@ +// @ts-nocheck + import React, { useRef, useState } from 'react'; import SVGInline from 'react-svg-inline'; import { injectIntl } from 'react-intl'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; +import { PopOver } from '@react-polymorph/components/PopOver'; import classnames from 'classnames'; import styles from './StakePoolsSearch.scss'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/search.... Remove this comment to see the full error message diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsSearchListViewButton.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsSearchListViewButton.tsx index 3fdc75fca0..d127632f49 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsSearchListViewButton.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsSearchListViewButton.tsx @@ -1,7 +1,9 @@ +// @ts-nocheck + import React, { useState } from 'react'; import SVGInline from 'react-svg-inline'; import { injectIntl } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import styles from './StakePoolsSearch.scss'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/list-ic... Remove this comment to see the full error message import listIcon from '../../../assets/images/list-ic.inline.svg'; diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx index 22575f18ce..fd0f76adac 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx @@ -15,36 +15,37 @@ type TableHeaderProps = { children: React.ReactNode; }; -@observer -class StakePoolsTableHeaderCell extends Component { - render() { - const { - name, - stakePoolsSortBy, - stakePoolsOrder, - onHandleSort, - children, - ...headerProps - } = this.props; - const isSorted = name === stakePoolsSortBy; - const defaultOrdering = defaultTableOrdering[name]; - const sortIconClasses = classNames([ - styles.sortIcon, - isSorted ? styles.sorted : null, - isSorted && styles[`${stakePoolsOrder}CurrentOrdering`], - styles[`${defaultOrdering}DefaultOrdering`], - ]); - return ( -
onHandleSort(name)} - {...headerProps} - > - {children} - -
- ); +const StakePoolsTableHeaderCell = observer( + class StakePoolsTableHeaderCell extends Component { + render() { + const { + name, + stakePoolsSortBy, + stakePoolsOrder, + onHandleSort, + children, + ...headerProps + } = this.props; + const isSorted = name === stakePoolsSortBy; + const defaultOrdering = defaultTableOrdering[name]; + const sortIconClasses = classNames([ + styles.sortIcon, + isSorted ? styles.sorted : null, + isSorted && styles[`${stakePoolsOrder}CurrentOrdering`], + styles[`${defaultOrdering}DefaultOrdering`], + ]); + return ( +
onHandleSort(name)} + {...headerProps} + > + {children} + +
+ ); + } } -} +); export { StakePoolsTableHeaderCell }; diff --git a/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx b/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx index f4fcccbdb5..5afde93356 100644 --- a/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx +++ b/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx @@ -1,10 +1,12 @@ +// @ts-nocheck + import React, { useMemo } from 'react'; import classNames from 'classnames'; import BigNumber from 'bignumber.js'; import moment from 'moment'; import { FormattedHTMLMessage } from 'react-intl'; import { Column } from 'react-table'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { PoolPopOver } from '../../widgets/PoolPopOver'; import { Intl } from '../../../../types/i18nTypes'; import styles from '../StakePoolsTable.scss'; diff --git a/source/renderer/app/components/staking/widgets/PoolPopOver.tsx b/source/renderer/app/components/staking/widgets/PoolPopOver.tsx index 592c3ea904..4825154357 100644 --- a/source/renderer/app/components/staking/widgets/PoolPopOver.tsx +++ b/source/renderer/app/components/staking/widgets/PoolPopOver.tsx @@ -1,7 +1,9 @@ +// @ts-nocheck + import React, { useRef, useState } from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { STAKE_POOL_TOOLTIP_HOVER_WAIT } from '../../../config/timingConfig'; import StakePool from '../../../domains/StakePool'; import TooltipPool from './TooltipPool'; diff --git a/source/renderer/app/components/staking/widgets/ThumbPoolContent.tsx b/source/renderer/app/components/staking/widgets/ThumbPoolContent.tsx index 1b03e7ebf5..a06d20b7c3 100644 --- a/source/renderer/app/components/staking/widgets/ThumbPoolContent.tsx +++ b/source/renderer/app/components/staking/widgets/ThumbPoolContent.tsx @@ -24,7 +24,6 @@ type Props = { isGridRewardsView?: boolean; }; -@observer class ThumbPoolContent extends Component { formattedRewards = (potentialRewards: BigNumber) => { const potentialRewardsAsString = formattedWalletAmount(potentialRewards); @@ -46,11 +45,8 @@ class ThumbPoolContent extends Component { }; render() { - const { - stakePool, - numberOfRankedStakePools, - isGridRewardsView, - } = this.props; + const { stakePool, numberOfRankedStakePools, isGridRewardsView } = + this.props; const { ranking, nonMyopicMemberRewards, @@ -136,4 +132,4 @@ class ThumbPoolContent extends Component { } } -export default ThumbPoolContent; +export default observer(ThumbPoolContent); diff --git a/source/renderer/app/components/staking/widgets/ThumbSelectedPool.tsx b/source/renderer/app/components/staking/widgets/ThumbSelectedPool.tsx index b51837f7b1..917648f3cc 100644 --- a/source/renderer/app/components/staking/widgets/ThumbSelectedPool.tsx +++ b/source/renderer/app/components/staking/widgets/ThumbSelectedPool.tsx @@ -19,14 +19,10 @@ type Props = { numberOfRankedStakePools: number; }; -@observer class ThumbSelectedPool extends Component { render() { - const { - stakePool, - alreadyDelegated, - numberOfRankedStakePools, - } = this.props; + const { stakePool, alreadyDelegated, numberOfRankedStakePools } = + this.props; const { ticker, retiring, ranking } = stakePool || {}; const rankColor = stakePool && !retiring && IS_RANKING_DATA_AVAILABLE @@ -62,4 +58,4 @@ class ThumbSelectedPool extends Component { } } -export default ThumbSelectedPool; +export default observer(ThumbSelectedPool); diff --git a/source/renderer/app/components/staking/widgets/TooltipPool.tsx b/source/renderer/app/components/staking/widgets/TooltipPool.tsx index c41ee80735..c1f8a134ff 100644 --- a/source/renderer/app/components/staking/widgets/TooltipPool.tsx +++ b/source/renderer/app/components/staking/widgets/TooltipPool.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + import React, { createRef, Component } from 'react'; import type { ElementRef } from 'react'; import { observer } from 'mobx-react'; @@ -7,15 +9,15 @@ import { FormattedMessage, FormattedHTMLMessage, } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { Button } from '@react-polymorph/components/Button'; +import { PopOver } from '@react-polymorph/components/PopOver'; import CopyToClipboard from 'react-copy-to-clipboard'; import classnames from 'classnames'; import moment from 'moment'; import SVGInline from 'react-svg-inline'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import styles from './TooltipPool.scss'; import StakePool from '../../../domains/StakePool'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/close-c... Remove this comment to see the full error message @@ -173,7 +175,6 @@ type State = { idCopyFeedback: boolean; }; -@observer class TooltipPool extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -528,4 +529,4 @@ class TooltipPool extends Component { } } -export default TooltipPool; +export default observer(TooltipPool); diff --git a/source/renderer/app/components/static/About.tsx b/source/renderer/app/components/static/About.tsx index 9c26e2fbdf..b17056e7cc 100644 --- a/source/renderer/app/components/static/About.tsx +++ b/source/renderer/app/components/static/About.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import SVGInline from 'react-svg-inline'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import DialogCloseButton from '../widgets/DialogCloseButton'; import globalMessages from '../../i18n/global-messages'; import styles from './About.scss'; diff --git a/source/renderer/app/components/status/DaedalusDiagnostics.tsx b/source/renderer/app/components/status/DaedalusDiagnostics.tsx index d99618a9f6..0009a0550c 100644 --- a/source/renderer/app/components/status/DaedalusDiagnostics.tsx +++ b/source/renderer/app/components/status/DaedalusDiagnostics.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + import React, { Component, Fragment } from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; @@ -6,9 +8,9 @@ import { observer } from 'mobx-react'; import { get, includes, upperFirst } from 'lodash'; import { defineMessages, intlShape } from 'react-intl'; import CopyToClipboard from 'react-copy-to-clipboard'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { PopOver } from '@react-polymorph/components/PopOver'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import SVGInline from 'react-svg-inline'; import { ALLOWED_TIME_DIFFERENCE } from '../../config/timingConfig'; import globalMessages from '../../i18n/global-messages'; @@ -86,16 +88,14 @@ const messages = defineMessages({ 'Displayed on the right of the Recommended system requirements status row when hardware requirements are ok', }, hasMetHardwareRequirementsStatusLowTooltip: { - id: - 'daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatusLowTooltip', + id: 'daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatusLowTooltip', defaultMessage: '!!!Your system specifications do not meet Daedalus’ recommended hardware requirements. We suggest using a machine with at least 16 GB of RAM', description: 'Visible on hovering over Recommended system requirement status when status is Low', }, hasMetHardwareRequirementsStatusGoodTooltip: { - id: - 'daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatusGoodTooltip', + id: 'daedalus.diagnostics.dialog.hasMetHardwareRequirementsStatusGoodTooltip', defaultMessage: '!!!Your system specifications meet Daedalus’ recommended hardware requirements', description: @@ -425,7 +425,6 @@ const FINAL_CARDANO_NODE_STATES = [ CardanoNodeStates.UNRECOVERABLE, ]; -@observer class DaedalusDiagnostics extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -914,4 +913,4 @@ class DaedalusDiagnostics extends Component { }; } -export default DaedalusDiagnostics; +export default observer(DaedalusDiagnostics); diff --git a/source/renderer/app/components/voting/VotingFooterLinks.tsx b/source/renderer/app/components/voting/VotingFooterLinks.tsx index 35a993bd61..ab99e47cc8 100644 --- a/source/renderer/app/components/voting/VotingFooterLinks.tsx +++ b/source/renderer/app/components/voting/VotingFooterLinks.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Link } from 'react-polymorph/lib/components/Link'; +import { Link } from '@react-polymorph/components/Link'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, injectIntl } from 'react-intl'; import styles from './VotingFooterLinks.scss'; diff --git a/source/renderer/app/components/voting/VotingNoWallets.tsx b/source/renderer/app/components/voting/VotingNoWallets.tsx index bdf821f278..ddc6fc68cc 100644 --- a/source/renderer/app/components/voting/VotingNoWallets.tsx +++ b/source/renderer/app/components/voting/VotingNoWallets.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { defineMessages, intlShape } from 'react-intl'; import SVGInline from 'react-svg-inline'; import BigNumber from 'bignumber.js'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import styles from './VotingNoWallets.scss'; import icon from '../../assets/images/attention-big-thin.inline.svg'; diff --git a/source/renderer/app/components/voting/VotingRegistrationDialogWizard.tsx b/source/renderer/app/components/voting/VotingRegistrationDialogWizard.tsx index 66b11a6838..2be4714563 100644 --- a/source/renderer/app/components/voting/VotingRegistrationDialogWizard.tsx +++ b/source/renderer/app/components/voting/VotingRegistrationDialogWizard.tsx @@ -44,7 +44,6 @@ type Props = { hwDeviceStatus: HwDeviceStatus; }; -@observer class VotingRegistrationDialogWizard extends Component { render() { const { @@ -165,4 +164,4 @@ class VotingRegistrationDialogWizard extends Component { } } -export default VotingRegistrationDialogWizard; +export default observer(VotingRegistrationDialogWizard); diff --git a/source/renderer/app/components/voting/voting-info/RegisterToVote.tsx b/source/renderer/app/components/voting/voting-info/RegisterToVote.tsx index 743cf439a7..4bfcfe961e 100644 --- a/source/renderer/app/components/voting/voting-info/RegisterToVote.tsx +++ b/source/renderer/app/components/voting/voting-info/RegisterToVote.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { injectIntl } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; +import { Button } from '@react-polymorph/components/Button'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; import type { Intl } from '../../../types/i18nTypes'; import { messages } from './RegisterToVote.messages'; import { messages as votingMessages } from './VotingInfo.messages'; diff --git a/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsConfirm.tsx b/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsConfirm.tsx index 4bf86ac35b..59b64e1396 100644 --- a/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsConfirm.tsx +++ b/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsConfirm.tsx @@ -82,7 +82,6 @@ type Props = { onRestart: (...args: Array) => any; }; -@observer class VotingRegistrationStepsConfirm extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -185,4 +184,4 @@ class VotingRegistrationStepsConfirm extends Component { } } -export default VotingRegistrationStepsConfirm; +export default observer(VotingRegistrationStepsConfirm); diff --git a/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsEnterPinCode.tsx b/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsEnterPinCode.tsx index 12e5d57ff3..a180e62f27 100644 --- a/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsEnterPinCode.tsx +++ b/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsEnterPinCode.tsx @@ -45,8 +45,7 @@ const messages = defineMessages({ description: 'Error message shown when repeat pin code is invalid.', }, invalidRepeatPinCode: { - id: - 'voting.votingRegistration.enterPinCode.step.errors.invalidRepeatPinCode', + id: 'voting.votingRegistration.enterPinCode.step.errors.invalidRepeatPinCode', defaultMessage: '!!!PIN doesn’t match', description: 'Error message shown when repeat pin code is invalid.', }, @@ -69,7 +68,6 @@ interface FormFields { repeatPinCode: string[]; } -@observer class VotingRegistrationStepsEnterPinCode extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -191,4 +189,4 @@ class VotingRegistrationStepsEnterPinCode extends Component { } } -export default VotingRegistrationStepsEnterPinCode; +export default observer(VotingRegistrationStepsEnterPinCode); diff --git a/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsQrCode.tsx b/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsQrCode.tsx index 7781da820f..90b418e74c 100644 --- a/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsQrCode.tsx +++ b/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsQrCode.tsx @@ -3,7 +3,7 @@ import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import QRCode from 'qrcode.react'; import { set } from 'lodash'; import { observer } from 'mobx-react'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; import VotingRegistrationDialog from './widgets/VotingRegistrationDialog'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module './VotingRegistrationStepsQrCod... Remove this comment to see the full error message import styles from './VotingRegistrationStepsQrCode.scss'; @@ -73,7 +73,6 @@ type State = { isCheckbox2Accepted: boolean; }; -@observer class VotingRegistrationStepsQrCode extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -176,4 +175,4 @@ class VotingRegistrationStepsQrCode extends Component { } } -export default VotingRegistrationStepsQrCode; +export default observer(VotingRegistrationStepsQrCode); diff --git a/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsRegister.tsx b/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsRegister.tsx index d4f31dfd37..14b43a8d69 100644 --- a/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsRegister.tsx +++ b/source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsRegister.tsx @@ -1,15 +1,17 @@ +// @ts-nocheck + import React, { Component } from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; import { get } from 'lodash'; import { defineMessages, intlShape } from 'react-intl'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import vjf from 'mobx-react-form/lib/validators/VJF'; import BigNumber from 'bignumber.js'; import { observer } from 'mobx-react'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import { submitOnEnter } from '../../../utils/form'; import globalMessages from '../../../i18n/global-messages'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; @@ -89,7 +91,6 @@ interface FormFields { spendingPassword: string; } -@observer class VotingRegistrationStepsRegister extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -253,4 +254,4 @@ class VotingRegistrationStepsRegister extends Component { } } -export default VotingRegistrationStepsRegister; +export default observer(VotingRegistrationStepsRegister); diff --git a/source/renderer/app/components/voting/voting-registration-wizard-steps/widgets/ConfirmationDialog.tsx b/source/renderer/app/components/voting/voting-registration-wizard-steps/widgets/ConfirmationDialog.tsx index ef0caac78c..478a6a736f 100644 --- a/source/renderer/app/components/voting/voting-registration-wizard-steps/widgets/ConfirmationDialog.tsx +++ b/source/renderer/app/components/voting/voting-registration-wizard-steps/widgets/ConfirmationDialog.tsx @@ -21,15 +21,13 @@ const messages = defineMessages({ 'Content for the voting registration cancellation confirmation dialog.', }, cancelButtonLabel: { - id: - 'voting.votingRegistration.dialog.confirmation.button.cancelButtonLabel', + id: 'voting.votingRegistration.dialog.confirmation.button.cancelButtonLabel', defaultMessage: '!!!Cancel registration', description: '"Cancel registration" button label for the voting registration cancellation confirmation dialog.', }, confirmButtonLabel: { - id: - 'voting.votingRegistration.dialog.confirmation.button.confirmButtonLabel', + id: 'voting.votingRegistration.dialog.confirmation.button.confirmButtonLabel', defaultMessage: '!!!Continue registration', description: '"Continue registration" button label for the voting registration cancellation confirmation dialog.', @@ -40,7 +38,6 @@ type Props = { onCancel: (...args: Array) => any; }; -@observer class ConfirmationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -77,4 +74,4 @@ class ConfirmationDialog extends Component { } } -export default ConfirmationDialog; +export default observer(ConfirmationDialog); diff --git a/source/renderer/app/components/voting/voting-registration-wizard-steps/widgets/VotingRegistrationDialog.tsx b/source/renderer/app/components/voting/voting-registration-wizard-steps/widgets/VotingRegistrationDialog.tsx index 2a4cf64bc9..00d352a765 100644 --- a/source/renderer/app/components/voting/voting-registration-wizard-steps/widgets/VotingRegistrationDialog.tsx +++ b/source/renderer/app/components/voting/voting-registration-wizard-steps/widgets/VotingRegistrationDialog.tsx @@ -3,8 +3,8 @@ import { observer } from 'mobx-react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; import classnames from 'classnames'; -import { Stepper } from 'react-polymorph/lib/components/Stepper'; -import { StepperSkin } from 'react-polymorph/lib/skins/simple/StepperSkin'; +import { Stepper } from '@react-polymorph/components/Stepper'; +import { StepperSkin } from '@react-polymorph/skins/simple/StepperSkin'; import { defineMessages, FormattedMessage, intlShape } from 'react-intl'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module './VotingRegistrationDialog.scs... Remove this comment to see the full error message import styles from './VotingRegistrationDialog.scss'; @@ -38,7 +38,6 @@ type Props = { hideSteps?: boolean; }; -@observer class VotingRegistrationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -101,4 +100,4 @@ class VotingRegistrationDialog extends Component { } } -export default VotingRegistrationDialog; +export default observer(VotingRegistrationDialog); diff --git a/source/renderer/app/components/wallet/WalletAdd.tsx b/source/renderer/app/components/wallet/WalletAdd.tsx index 7b4c1368ae..f339ec93be 100644 --- a/source/renderer/app/components/wallet/WalletAdd.tsx +++ b/source/renderer/app/components/wallet/WalletAdd.tsx @@ -105,7 +105,6 @@ type Props = { isProduction: boolean; }; -@observer class WalletAdd extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -196,4 +195,4 @@ class WalletAdd extends Component { } } -export default WalletAdd; +export default observer(WalletAdd); diff --git a/source/renderer/app/components/wallet/WalletBackupDialog.tsx b/source/renderer/app/components/wallet/WalletBackupDialog.tsx index 1ae24514d3..ba9cf66b89 100644 --- a/source/renderer/app/components/wallet/WalletBackupDialog.tsx +++ b/source/renderer/app/components/wallet/WalletBackupDialog.tsx @@ -29,7 +29,6 @@ type Props = { onRestartBackup: (...args: Array) => any; }; -@observer class WalletBackupDialog extends Component { render() { const { @@ -103,4 +102,4 @@ class WalletBackupDialog extends Component { } } -export default WalletBackupDialog; +export default observer(WalletBackupDialog); diff --git a/source/renderer/app/components/wallet/WalletConnectDialog.tsx b/source/renderer/app/components/wallet/WalletConnectDialog.tsx index aab551c4aa..f17b67ab43 100644 --- a/source/renderer/app/components/wallet/WalletConnectDialog.tsx +++ b/source/renderer/app/components/wallet/WalletConnectDialog.tsx @@ -9,8 +9,8 @@ import { FormattedMessage, } from 'react-intl'; import SVGInline from 'react-svg-inline'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import { get } from 'lodash'; import ledgerIcon from '../../assets/images/hardware-wallet/ledger-cropped.inline.svg'; import ledgerSpIcon from '../../assets/images/hardware-wallet/ledgerSP-cropped.inline.svg'; @@ -84,7 +84,6 @@ type Props = { onExternalLinkClick: (...args: Array) => any; }; -@observer class WalletConnectDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -227,4 +226,4 @@ class WalletConnectDialog extends Component { } } -export default WalletConnectDialog; +export default observer(WalletConnectDialog); diff --git a/source/renderer/app/components/wallet/WalletCreateDialog.tsx b/source/renderer/app/components/wallet/WalletCreateDialog.tsx index 5610b50cb9..d720a09567 100644 --- a/source/renderer/app/components/wallet/WalletCreateDialog.tsx +++ b/source/renderer/app/components/wallet/WalletCreateDialog.tsx @@ -1,13 +1,15 @@ +// @ts-nocheck + // TODO: Remove once the new wallet creation process is ready import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; import SVGInline from 'react-svg-inline'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import ReactToolboxMobxForm, { handleFormErrors, } from '../../utils/ReactToolboxMobxForm'; @@ -100,7 +102,6 @@ interface FormFields { walletName: string; } -@observer class WalletCreateDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -304,4 +305,4 @@ class WalletCreateDialog extends Component { } } -export default WalletCreateDialog; +export default observer(WalletCreateDialog); diff --git a/source/renderer/app/components/wallet/WalletRestoreDialog.tsx b/source/renderer/app/components/wallet/WalletRestoreDialog.tsx index 8843f99555..6dc9667c81 100644 --- a/source/renderer/app/components/wallet/WalletRestoreDialog.tsx +++ b/source/renderer/app/components/wallet/WalletRestoreDialog.tsx @@ -1,13 +1,15 @@ +// @ts-nocheck + import React, { Component, Fragment } from 'react'; import { join } from 'lodash'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { Autocomplete } from 'react-polymorph/lib/components/Autocomplete'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Autocomplete } from '@react-polymorph/components/Autocomplete'; +import { Input } from '@react-polymorph/components/Input'; import { defineMessages, FormattedHTMLMessage, intlShape } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; import SVGInline from 'react-svg-inline'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { PasswordInput } from '../widgets/forms/PasswordInput'; import RadioSet from '../widgets/RadioSet'; import ReactToolboxMobxForm, { @@ -189,7 +191,6 @@ interface FormFields { walletName: string; } -@observer class WalletRestoreDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -380,7 +381,7 @@ class WalletRestoreDialog extends Component { 'yoroiTab', this.isYoroi() ? styles.activeButton : '', ]); - const { reset, ...mnemonicInputProps } = recoveryPhraseField.bind(); + const { ...mnemonicInputProps } = recoveryPhraseField.bind(); return ( { }; } -export default WalletRestoreDialog; +export default observer(WalletRestoreDialog); diff --git a/source/renderer/app/components/wallet/WalletSendForm.scss b/source/renderer/app/components/wallet/WalletSendForm.scss old mode 100755 new mode 100644 diff --git a/source/renderer/app/components/wallet/WalletSendForm.spec.tsx b/source/renderer/app/components/wallet/WalletSendForm.spec.tsx deleted file mode 100644 index e9b9a23fc5..0000000000 --- a/source/renderer/app/components/wallet/WalletSendForm.spec.tsx +++ /dev/null @@ -1,624 +0,0 @@ -import React, { useState } from 'react'; -import { addLocaleData } from 'react-intl'; -import BigNumber from 'bignumber.js'; -import { Provider as MobxProvider } from 'mobx-react'; -import faker from '@faker-js/faker'; -import { - render, - fireEvent, - screen, - cleanup, - within, - waitForElementToBeRemoved, -} from '@testing-library/react'; -import '@testing-library/jest-dom'; -import en from 'react-intl/locale-data/en'; -// Assets and helpers -import { TestDecorator } from '../../../../../tests/_utils/TestDecorator'; -import { NUMBER_OPTIONS } from '../../config/profileConfig'; -import { DiscreetModeFeatureProvider } from '../../features/discreet-mode'; -import { BrowserLocalStorageBridge } from '../../features/local-storage'; -import { HwDeviceStatuses } from '../../domains/Wallet'; -import WalletTokenPicker from './tokens/wallet-token-picker/WalletTokenPicker'; -import WalletSendForm, { FormData } from './WalletSendForm'; -import { noopAnalyticsTracker } from '../../analytics'; - -jest.mock( - '../../containers/wallet/dialogs/send-confirmation/SendConfirmation.container', - () => { - function Dialog({ - amount, - formattedTotalAmount, - }: { - amount: number; - formattedTotalAmount: number; - }) { - return ( -
- {amount} - - {formattedTotalAmount} - -
- ); - } - - return { - __esModule: true, - WalletSendConfirmationDialogContainer: Dialog, - }; - } -); - -describe('wallet/Wallet Send Form', () => { - beforeEach(() => addLocaleData([...en])); - afterEach(cleanup); - const currencyMaxFractionalDigits = 6; - - function createAssets(index: number) { - const id = `${faker.datatype.uuid()}:${index}`; - return { - policyId: id, - assetName: faker.internet.domainWord(), - uniqueId: id, - fingerprint: faker.datatype.uuid(), - quantity: new BigNumber(faker.finance.amount()), - decimals: 0, - recommendedDecimals: null, - metadata: { - name: id, - ticker: faker.finance.currencyCode(), - description: '', - }, - }; - } - - const assets = [createAssets(0), createAssets(1)]; - - function SetupWallet({ - calculateTransactionFee, - currentNumberFormat = NUMBER_OPTIONS[0].value, - validationDebounceWait, - validateAmount = jest.fn().mockResolvedValue(true), - onTransactionFeeChange = jest.fn(), - }: { - calculateTransactionFee: (...args: Array) => any; - validateAmount?: (amount: string) => Promise; - currentNumberFormat?: string; - validationDebounceWait?: number; - onTransactionFeeChange?: () => Promise; - }) { - const [tokenPickerOpen, setTokenPickerOpen] = useState(false); - const [state, setState] = useState<{ - isDialogOpen: boolean; - formData: FormData; - }>({ isDialogOpen: false, formData: null }); - - return ( - - - - - true} - onSubmit={(formData) => - setState({ isDialogOpen: true, formData }) - } - isDialogOpen={(dialog) => - (dialog === WalletTokenPicker && tokenPickerOpen) || - state.isDialogOpen - } - isRestoreActive={false} - hwDeviceStatus={HwDeviceStatuses.READY} - isHardwareWallet={false} - isLoadingAssets={false} - onExternalLinkClick={jest.fn()} - hasAssets - selectedAsset={null} - onUnsetActiveAsset={() => {}} - isAddressFromSameWallet={false} - tokenFavorites={{}} - walletName={faker.name.firstName()} - onTokenPickerDialogClose={() => setTokenPickerOpen(false)} - onTokenPickerDialogOpen={() => setTokenPickerOpen(true)} - analyticsTracker={noopAnalyticsTracker} - confirmationDialogData={state.formData} - validationDebounceWait={validationDebounceWait} - onTransactionFeeChange={onTransactionFeeChange} - /> - - - - - ); - } - - function enterReceiverAddress() { - const address = - 'addr_test1qrjzmxr4x7vhlusn05fd4lt7cs6dy8wtcv6vaf9lff7m9yqkw6whlsg36t3laez562llhkvfy5tny4p9y8zrspe48vgsea3q6m'; - const receiverAddress = screen.getByPlaceholderText('Paste an address'); - fireEvent.change(receiverAddress, { - target: { - value: address, - }, - }); - } - - function getInput(label: string) { - return screen.getByLabelText(label); - } - - async function findInput(label: string) { - return screen.findByLabelText(label); - } - - async function addToken(value = 1, tokenIndex = 0) { - const addTokenButton = await screen.findByText('+ Add a token'); - fireEvent.click(addTokenButton); - const tokenPicker = await screen.findByTestId('WalletTokenPicker'); - const tokenCheckbox = tokenPicker.querySelectorAll( - 'input[type="checkbox"]' - )[tokenIndex]; - fireEvent.click(tokenCheckbox); - const addTokenPickerButton = await screen.findByText('Add'); - fireEvent.click(addTokenPickerButton); - - const { uniqueId } = assets[tokenIndex]; - - const token = await screen.findByTestId(`assetInput:${uniqueId}`); - fireEvent.change(token, { - target: { - value, - }, - }); - return async () => { - fireEvent.mouseEnter(token); - const removeTokenButton = await screen.findByTestId( - `removeAsset:${uniqueId}` - ); - fireEvent.click(removeTokenButton); - }; - } - - async function waitForMinimumAdaRequiredMsg(minimumAda = 2) { - const minimumAdaRequiredMsg = screen.getByTestId('minimumAdaRequiredMsg'); - await within(minimumAdaRequiredMsg).findByText( - `a minimum of ${minimumAda} ADA required` - ); - } - - function assertAdaInput(amount: number) { - const adaInput = getInput('Ada'); - expect(adaInput).toHaveValue( - new BigNumber(amount).toFormat(currencyMaxFractionalDigits) - ); - } - - function createTransactionFeeMock(times: number, minimumAda: number) { - const mock = jest.fn().mockResolvedValue({ - fee: new BigNumber(1), - minimumAda: new BigNumber(1), - }); - Array.from({ - length: times, - }).forEach(() => { - // @ts-ignore - mock.mockResolvedValueOnce({ - fee: new BigNumber(1), - minimumAda: new BigNumber(minimumAda), - }); - }); - return mock; - } - - const MINIMUM_AMOUNT_UPDATED_MESSAGE_TEST_ID = - 'WalletSendForm::minimumAmountNotice::updated'; - - function assertMinimumAmountNoticeMessage(minimumAda: number) { - expect( - screen.getByTestId(MINIMUM_AMOUNT_UPDATED_MESSAGE_TEST_ID) - ).toHaveTextContent( - `Note: the ada field was automatically updated because this transaction requires a minimum of ${minimumAda} ADA.` - ); - } - - function sleep(ms) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); - } - - async function waitForTransactionFee() { - const transactionFeeSpinner = screen.getByTestId('transaction-fee-spinner'); - - return waitForElementToBeRemoved(transactionFeeSpinner); - } - - test('should update Ada input field to minimum required and restore to original value when tokens are removed', async () => { - expect.assertions(4); - - const minimumAda = 2; - const calculateTransactionFeeMock = createTransactionFeeMock(1, minimumAda); - - render( - - ); - - enterReceiverAddress(); - - const removeToken1 = await addToken(); - - await waitForMinimumAdaRequiredMsg(); - assertAdaInput(minimumAda); - - const minimumAmountNoticeTestId = - 'WalletSendForm::minimumAmountNotice::updated'; - - expect(screen.getByTestId(minimumAmountNoticeTestId)).toHaveTextContent( - `Note: the ada field was automatically updated because this transaction requires a minimum of ${minimumAda} ADA.` - ); - - await removeToken1(); - - await waitForMinimumAdaRequiredMsg(1); - - assertAdaInput(1); - - expect(screen.queryByTestId(minimumAmountNoticeTestId)).toBeInTheDocument(); - }); - - test('should display an update button when Ada input field is less than minimum required', async () => { - expect.assertions(3); - const minimumAda = 2; - const calculateTransactionFeeMock = createTransactionFeeMock(2, minimumAda); - render( - - ); - enterReceiverAddress(); - await addToken(); - await waitForMinimumAdaRequiredMsg(); - assertAdaInput(minimumAda); - const lowerAdaValue = 1.5; - const adaInput = getInput('Ada'); - fireEvent.change(adaInput, { - target: { - value: lowerAdaValue, - }, - }); - const minimumAdaRequiredMsg = screen.getByTestId('minimumAdaRequiredMsg'); - const updateButton = await within(minimumAdaRequiredMsg).findByText( - 'UPDATE' - ); - assertAdaInput(lowerAdaValue); - fireEvent.click(updateButton); - await waitForElementToBeRemoved(updateButton); - assertAdaInput(minimumAda); - }); - - test('should favour user Ada input instead of minimum required when the value is greater than the minimum one', async () => { - expect.assertions(2); - const minimumAda = 2.5; - const calculateTransactionFeeMock = createTransactionFeeMock(2, minimumAda); - render( - - ); - enterReceiverAddress(); - const userAdaAmount = 1.5; - const adaInput = await findInput('Ada'); - fireEvent.change(adaInput, { - target: { - value: userAdaAmount, - }, - }); - const removeToken1 = await addToken(); - await waitForMinimumAdaRequiredMsg(minimumAda); - assertAdaInput(minimumAda); - await removeToken1(); - const minimumAmountNotice = await screen.findByTestId( - 'WalletSendForm::minimumAmountNotice::restored' - ); - expect(minimumAmountNotice).toHaveTextContent( - `Note: the ada field was automatically updated to ${userAdaAmount} ADA because now it fulfills the minimum amount of 1 ADA for the transaction.` - ); - }); - - test('should remove message when user entry is higher than minimum amount', async () => { - expect.assertions(3); - const minimumAda = 2.5; - const calculateTransactionFeeMock = createTransactionFeeMock(2, minimumAda); - render( - - ); - enterReceiverAddress(); - const userAdaAmount = 1.5; - const adaInput = await findInput('Ada'); - fireEvent.change(adaInput, { - target: { - value: userAdaAmount, - }, - }); - const removeToken1 = await addToken(); - await waitForMinimumAdaRequiredMsg(minimumAda); - assertAdaInput(minimumAda); - await removeToken1(); - const minimumAmountNoticeTestId = - 'WalletSendForm::minimumAmountNotice::restored'; - const minimumAmountNotice = await screen.findByTestId( - minimumAmountNoticeTestId - ); - expect(minimumAmountNotice).toBeInTheDocument(); - fireEvent.change(adaInput, { - target: { - value: userAdaAmount + 1, - }, - }); - expect( - screen.queryByTestId(minimumAmountNoticeTestId) - ).not.toBeInTheDocument(); - }); - - test('should not display any minimum amount notice message when ada input is greater than minimum amount', async () => { - expect.assertions(2); - const calculateTransactionFeeMock = jest.fn().mockResolvedValue({ - fee: new BigNumber(1), - minimumAda: new BigNumber(2), - }); - render( - - ); - enterReceiverAddress(); - const userAdaAmount = 3.5; - const adaInput = await findInput('Ada'); - fireEvent.change(adaInput, { - target: { - value: userAdaAmount, - }, - }); - await addToken(); - await waitForMinimumAdaRequiredMsg(); - assertAdaInput(userAdaAmount); - await expect( - screen.findByTestId('WalletSendForm::minimumAmountNotice::restored') - ).rejects.toBeTruthy(); - }); - - test('should apply minimum fee to ada field when user has removed the previous update', async () => { - expect.assertions(3); - const minimumAda = 2; - const calculateTransactionFeeMock = createTransactionFeeMock(4, minimumAda); - render( - - ); - enterReceiverAddress(); - const removeToken1 = await addToken(); - await waitForMinimumAdaRequiredMsg(); - assertAdaInput(minimumAda); - const adaInput = await findInput('Ada'); - fireEvent.change(adaInput, { - target: { - value: '', - }, - }); - await removeToken1(); - expect(adaInput).toHaveValue(''); - await addToken(); - await waitForMinimumAdaRequiredMsg(); - assertAdaInput(minimumAda); - }); - - test('should format ada input field using numeric format profile', async () => { - expect.assertions(1); - const minimumAda = 2; - const calculateTransactionFeeMock = createTransactionFeeMock(4, minimumAda); - render( - - ); - enterReceiverAddress(); - await addToken(); - await waitForMinimumAdaRequiredMsg(); - const adaInput = getInput('Ada'); - expect(adaInput).toHaveValue(`${minimumAda},000000`); - }); - - test('should calculate transaction fee even when one of the assets are empty', async () => { - expect.assertions(2); - const minimumAda = 2; - const calculateTransactionFeeMock = createTransactionFeeMock(4, minimumAda); - render( - - ); - enterReceiverAddress(); - await addToken(0); - await waitForMinimumAdaRequiredMsg(1); - expect(getInput('Ada')).toHaveValue(''); - await addToken(minimumAda, 1); - await waitForMinimumAdaRequiredMsg(); - assertAdaInput(minimumAda); - }); - - test('should keep transaction fee when assets are removed and ada field is untouched', async () => { - expect.assertions(3); - - const fee = 2; - const minimumAda = 1; - const calculateTransactionFeeMock = createTransactionFeeMock(1, fee); - - render( - - ); - - enterReceiverAddress(); - - const removeToken = await addToken(); - await waitForMinimumAdaRequiredMsg(); - - assertAdaInput(fee); - - await removeToken(); - - await waitForMinimumAdaRequiredMsg(minimumAda); - - assertAdaInput(minimumAda); - assertMinimumAmountNoticeMessage(minimumAda); - }); - - type Cases = [ - Array<[number, number]>, - [number, number], - Array<[number, number]>, - [string, string, number] - ]; - - const cases: Cases[] = [ - [ - // ada inputs [delay, value] - [ - [0, 2.5], - [0, 1.5], - ], - // validate amount [delay] - [50, 5], - // fee calculation [delay, value] - [ - [50, 2], - [0, 1], - ], - ['1.500000', '3.500000', 1], - ], - [ - [ - [0, 2.5], - [0, 1.5], - ], - [0, 0], - [ - [0, 1], - [0, 2], - ], - ['1.500000', '3.500000', 1], - ], - [ - [ - [50, 2.5], - [0, 1.5], - ], - [0, 0], - [ - [0, 1], - [0, 2], - ], - ['1.500000', '3.500000', 2], - ], - ]; - - cases.forEach( - ( - [ - inputs, - validateAmountParams, - feeCalculationParams, - [expectedAdaAmount, expectedTotalAmount, expectedTimesFeeCalled], - ], - idx - ) => { - test(`case ${idx}: should not allow to submit before fees are calculated`, async () => { - expect.assertions(5); - - const validationDebounceWait = 0; - const onTransactionFeeChangeSpy = jest.fn(); - - const calculateTransactionFeeMock = jest.fn(); - - feeCalculationParams.forEach(([delay, value]) => { - calculateTransactionFeeMock.mockImplementationOnce( - () => - new Promise(async (resolve) => { - await sleep(delay); - resolve({ - fee: new BigNumber(value), - minimumAda: new BigNumber(1), - }); - }) - ); - }); - - const validateAmountMock = jest.fn(); - - validateAmountParams.forEach((delay) => { - validateAmountMock.mockImplementationOnce( - () => - new Promise(async (resolve) => { - await sleep(delay); - return resolve(true); - }) - ); - }); - - render( - - ); - - enterReceiverAddress(); - - const adaField = await screen.findByLabelText('Ada'); - - for (const [delay, value] of inputs) { - fireEvent.change(adaField, { - target: { - value, - }, - }); - - await sleep(delay); - } - - const sendButton: HTMLButtonElement = screen.getByText('Send'); - expect(sendButton).not.toBeEnabled(); - - await waitForTransactionFee(); - - expect(sendButton).toBeEnabled(); - - fireEvent.keyPress(adaField, { - key: 'Enter', - code: 13, - charCode: 13, - target: adaField, - }); - - const adaAmountConfirmation = await screen.findByTestId( - 'confirmation-dialog-ada-amount', - {} - ); - - expect(adaAmountConfirmation).toHaveTextContent(expectedAdaAmount); - - const totalAmountConfirmation = screen.getByTestId( - 'confirmation-dialog-total-amount', - {} - ); - - expect(totalAmountConfirmation).toHaveTextContent(expectedTotalAmount); - expect(onTransactionFeeChangeSpy).toHaveBeenCalledTimes( - expectedTimesFeeCalled - ); - }); - } - ); -}); diff --git a/source/renderer/app/components/wallet/WalletSendForm.tsx b/source/renderer/app/components/wallet/WalletSendForm.tsx old mode 100755 new mode 100644 index 060cbd35e9..391e59dde5 --- a/source/renderer/app/components/wallet/WalletSendForm.tsx +++ b/source/renderer/app/components/wallet/WalletSendForm.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + import React, { Component, Fragment } from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; @@ -9,10 +11,10 @@ import BigNumber from 'bignumber.js'; import classNames from 'classnames'; import SVGInline from 'react-svg-inline'; import vjf from 'mobx-react-form/lib/validators/VJF'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { NumericInput } from 'react-polymorph/lib/components/NumericInput'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { Button } from '@react-polymorph/components/Button'; +import { Input } from '@react-polymorph/components/Input'; +import { NumericInput } from '@react-polymorph/components/NumericInput'; +import { PopOver } from '@react-polymorph/components/PopOver'; import BorderedBox from '../widgets/BorderedBox'; import LoadingSpinner from '../widgets/LoadingSpinner'; import ReadOnlyInput from '../widgets/forms/ReadOnlyInput'; @@ -153,7 +155,6 @@ interface RequestToken { release: () => void; } -@observer class WalletSendForm extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -651,11 +652,8 @@ class WalletSendForm extends Component { } try { - const { - fee, - minimumAda, - coinSelection, - } = await this.props.calculateTransactionFee(receiver, adaAmount, assets); + const { fee, minimumAda, coinSelection } = + await this.props.calculateTransactionFee(receiver, adaAmount, assets); if (this._isMounted && !requestToken.aborted) { const minimumAdaValue = minimumAda || new BigNumber(0); @@ -703,9 +701,8 @@ class WalletSendForm extends Component { if (shouldUpdateMinimumAdaAmount) { const minimumAdaValue = new BigNumber(minimumAda); - const adaInputState = await this.checkAdaInputState( - minimumAdaValue - ); + const adaInputState = + await this.checkAdaInputState(minimumAdaValue); this.trySetMinimumAdaAmount(adaInputState, minimumAdaValue); this.setState({ ...nextState, @@ -740,11 +737,7 @@ class WalletSendForm extends Component { checkAdaInputState = async ( minimumAda: BigNumber ): Promise => { - const { - adaAmountInputTrack, - selectedAssetUniqueIds, - adaInputState, - } = this.state; + const { adaAmountInputTrack, adaInputState } = this.state; if ( adaAmountInputTrack.gte(minimumAda) && @@ -1392,4 +1385,4 @@ class WalletSendForm extends Component { } } -export default WalletSendForm; +export default observer(WalletSendForm); diff --git a/source/renderer/app/components/wallet/backup-recovery/WalletBackupPrivacyWarningDialog.tsx b/source/renderer/app/components/wallet/backup-recovery/WalletBackupPrivacyWarningDialog.tsx index 2ee5b83a93..b884459334 100644 --- a/source/renderer/app/components/wallet/backup-recovery/WalletBackupPrivacyWarningDialog.tsx +++ b/source/renderer/app/components/wallet/backup-recovery/WalletBackupPrivacyWarningDialog.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import Dialog from '../../widgets/Dialog'; import DialogCloseButton from '../../widgets/DialogCloseButton'; @@ -55,7 +55,6 @@ type Props = { onCancelBackup: (...args: Array) => any; }; -@observer class WalletBackupPrivacyWarningDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -126,4 +125,4 @@ class WalletBackupPrivacyWarningDialog extends Component { } } -export default WalletBackupPrivacyWarningDialog; +export default observer(WalletBackupPrivacyWarningDialog); diff --git a/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryInstructions.tsx b/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryInstructions.tsx index 9e0195804b..113ab173cc 100644 --- a/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryInstructions.tsx +++ b/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryInstructions.tsx @@ -9,7 +9,6 @@ type Props = { instructionsText: string | Element; }; -@observer class WalletRecoveryInstructions extends Component { render() { const { instructionsText } = this.props; @@ -17,4 +16,4 @@ class WalletRecoveryInstructions extends Component { } } -export default WalletRecoveryInstructions; +export default observer(WalletRecoveryInstructions); diff --git a/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseDisplayDialog.tsx b/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseDisplayDialog.tsx index 7ac6272e48..ec128dd090 100644 --- a/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseDisplayDialog.tsx +++ b/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseDisplayDialog.tsx @@ -20,8 +20,7 @@ const messages = defineMessages({ 'Instructions for backing up wallet recovery phrase on dialog that displays wallet recovery phrase.', }, buttonLabelIHaveWrittenItDown: { - id: - 'wallet.backup.recovery.phrase.display.dialog.button.label.iHaveWrittenItDown', + id: 'wallet.backup.recovery.phrase.display.dialog.button.label.iHaveWrittenItDown', defaultMessage: '!!!Yes, I have written down my wallet recovery phrase.', description: 'Label for button "Yes, I have written down my wallet recovery phrase." on wallet backup dialog', @@ -34,7 +33,6 @@ type Props = { isSubmitting: boolean; }; -@observer class WalletRecoveryPhraseDisplayDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -79,7 +77,8 @@ class WalletRecoveryPhraseDisplayDialog extends Component { } @@ -94,4 +93,4 @@ class WalletRecoveryPhraseDisplayDialog extends Component { } } -export default WalletRecoveryPhraseDisplayDialog; +export default observer(WalletRecoveryPhraseDisplayDialog); diff --git a/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseEntryDialog.tsx b/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseEntryDialog.tsx index 3467a11549..a3bd727d17 100644 --- a/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseEntryDialog.tsx +++ b/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseEntryDialog.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; import vjf from 'mobx-react-form/lib/validators/VJF'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import { defineMessages, FormattedHTMLMessage, intlShape } from 'react-intl'; import { WALLET_RECOVERY_PHRASE_WORD_COUNT } from '../../../config/cryptoConfig'; import suggestedMnemonics from '../../../../../common/config/crypto/valid-words.en'; @@ -39,8 +39,7 @@ const messages = defineMessages({ description: 'Placeholder hint for the mnemonics autocomplete.', }, recoveryPhraseInvalidMnemonics: { - id: - 'wallet.backup.recovery.phrase.entry.dialog.recoveryPhraseInvalidMnemonics', + id: 'wallet.backup.recovery.phrase.entry.dialog.recoveryPhraseInvalidMnemonics', defaultMessage: '!!!Invalid recovery phrase', description: 'Error message shown when invalid recovery phrase was entered.', @@ -51,15 +50,13 @@ const messages = defineMessages({ description: 'Label for button "Confirm" on wallet backup dialog', }, termOffline: { - id: - 'wallet.backup.recovery.phrase.entry.dialog.terms.and.condition.offline', + id: 'wallet.backup.recovery.phrase.entry.dialog.terms.and.condition.offline', defaultMessage: '!!!I understand that the simplest way to keep my wallet recovery phrase secure is to never store it digitally or online. If I decide to use an online service, such as a password manager with an encrypted database, it is my responsibility to make sure that I use it correctly.', description: 'Term on wallet creation to store recovery phrase offline', }, termRecovery: { - id: - 'wallet.backup.recovery.phrase.entry.dialog.terms.and.condition.recovery', + id: 'wallet.backup.recovery.phrase.entry.dialog.terms.and.condition.recovery', defaultMessage: '!!!I understand that the only way to recover my wallet if my computer is lost, broken, stolen, or stops working is to use my wallet recovery phrase.', description: @@ -86,7 +83,6 @@ interface FormFields { recoveryPhrase: string; } -@observer class WalletRecoveryPhraseEntryDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -182,7 +178,7 @@ class WalletRecoveryPhraseEntryDialog extends Component { primary: true, }, ]; - const { reset, ...mnemonicInputProps } = recoveryPhraseField.bind(); + const { ...mnemonicInputProps } = recoveryPhraseField.bind(); return ( { } } -export default WalletRecoveryPhraseEntryDialog; +export default observer(WalletRecoveryPhraseEntryDialog); diff --git a/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseMnemonic.tsx b/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseMnemonic.tsx index 6113699c5b..65b83f9224 100644 --- a/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseMnemonic.tsx +++ b/source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseMnemonic.tsx @@ -6,7 +6,6 @@ type Props = { phrase: string; }; -@observer class WalletRecoveryPhraseMnemonic extends Component { render() { const { phrase } = this.props; @@ -14,4 +13,4 @@ class WalletRecoveryPhraseMnemonic extends Component { } } -export default WalletRecoveryPhraseMnemonic; +export default observer(WalletRecoveryPhraseMnemonic); diff --git a/source/renderer/app/components/wallet/file-import/WalletFileImportDialog.tsx b/source/renderer/app/components/wallet/file-import/WalletFileImportDialog.tsx index 57e1ee9131..fcca8a88e0 100644 --- a/source/renderer/app/components/wallet/file-import/WalletFileImportDialog.tsx +++ b/source/renderer/app/components/wallet/file-import/WalletFileImportDialog.tsx @@ -3,8 +3,8 @@ import { observer } from 'mobx-react'; import classnames from 'classnames'; import { defineMessages, intlShape } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; -// import { Input } from 'react-polymorph/lib/components/Input'; -// import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +// import { Input } from '@react-polymorph/components/Input'; +// import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; @@ -86,7 +86,6 @@ interface FormFields { repeatPassword: string; } -@observer class WalletFileImportDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -268,4 +267,4 @@ class WalletFileImportDialog extends Component { } } -export default WalletFileImportDialog; +export default observer(WalletFileImportDialog); diff --git a/source/renderer/app/components/wallet/layouts/WalletWithNavigation.tsx b/source/renderer/app/components/wallet/layouts/WalletWithNavigation.tsx index 47045c8016..2acefc6336 100644 --- a/source/renderer/app/components/wallet/layouts/WalletWithNavigation.tsx +++ b/source/renderer/app/components/wallet/layouts/WalletWithNavigation.tsx @@ -24,7 +24,6 @@ type Props = { onWalletNavItemClick: (...args: Array) => any; }; -@observer class WalletWithNavigation extends Component { render() { const { @@ -76,4 +75,4 @@ class WalletWithNavigation extends Component { } } -export default WalletWithNavigation; +export default observer(WalletWithNavigation); diff --git a/source/renderer/app/components/wallet/mnemonic-input/MnemonicAutocompleteContainer.tsx b/source/renderer/app/components/wallet/mnemonic-input/MnemonicAutocompleteContainer.tsx index fc8e122370..fd873e5522 100644 --- a/source/renderer/app/components/wallet/mnemonic-input/MnemonicAutocompleteContainer.tsx +++ b/source/renderer/app/components/wallet/mnemonic-input/MnemonicAutocompleteContainer.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + import React, { ChangeEventHandler, ClipboardEventHandler, @@ -10,7 +12,7 @@ import React, { useState, } from 'react'; import { startsWith } from 'lodash/fp'; -import { GlobalListeners } from 'react-polymorph/lib/components/HOC/GlobalListeners'; +import { GlobalListeners } from '@react-polymorph/components/HOC/GlobalListeners'; import { MnemonicAutocompleteLayout } from './MnemonicAutocompleteLayout'; interface MnemonicAutocompleteContainerProps { diff --git a/source/renderer/app/components/wallet/mnemonic-input/MnemonicAutocompleteLayout.tsx b/source/renderer/app/components/wallet/mnemonic-input/MnemonicAutocompleteLayout.tsx index dab08e04be..fbc10eb4be 100644 --- a/source/renderer/app/components/wallet/mnemonic-input/MnemonicAutocompleteLayout.tsx +++ b/source/renderer/app/components/wallet/mnemonic-input/MnemonicAutocompleteLayout.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + import React, { ChangeEventHandler, ClipboardEventHandler, @@ -5,9 +7,9 @@ import React, { RefObject, } from 'react'; import cx from 'classnames'; -import { FormField } from 'react-polymorph/lib/components/FormField'; -import { Options } from 'react-polymorph/lib/components/Options'; -import { OptionsSkin } from 'react-polymorph/lib/skins/simple/OptionsSkin'; +import { FormField } from '@react-polymorph/components/FormField'; +import { Options } from '@react-polymorph/components/Options'; +import { OptionsSkin } from '@react-polymorph/skins/simple/OptionsSkin'; import styles from './MnemonicAutocompleteLayout.scss'; interface MnemonicInputSkinProps { diff --git a/source/renderer/app/components/wallet/navigation/WalletNavigation.tsx b/source/renderer/app/components/wallet/navigation/WalletNavigation.tsx old mode 100755 new mode 100644 index 60933597fe..163d672a26 --- a/source/renderer/app/components/wallet/navigation/WalletNavigation.tsx +++ b/source/renderer/app/components/wallet/navigation/WalletNavigation.tsx @@ -65,7 +65,6 @@ type Props = { hasNotification?: boolean; }; -@observer class WalletNavigation extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -134,4 +133,4 @@ class WalletNavigation extends Component { } } -export default WalletNavigation; +export default observer(WalletNavigation); diff --git a/source/renderer/app/components/wallet/not-responding/NotResponding.tsx b/source/renderer/app/components/wallet/not-responding/NotResponding.tsx index 94422e95fe..f36cecdd2e 100644 --- a/source/renderer/app/components/wallet/not-responding/NotResponding.tsx +++ b/source/renderer/app/components/wallet/not-responding/NotResponding.tsx @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import SVGInline from 'react-svg-inline'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/not-res... Remove this comment to see the full error message import icon from '../../../assets/images/not-responding.inline.svg'; import styles from './NotResponding.scss'; diff --git a/source/renderer/app/components/wallet/paper-wallet-certificate/CompletionDialog.tsx b/source/renderer/app/components/wallet/paper-wallet-certificate/CompletionDialog.tsx index a04e146502..5ff36eed53 100644 --- a/source/renderer/app/components/wallet/paper-wallet-certificate/CompletionDialog.tsx +++ b/source/renderer/app/components/wallet/paper-wallet-certificate/CompletionDialog.tsx @@ -4,8 +4,8 @@ import QRCode from 'qrcode.react'; import { defineMessages, intlShape } from 'react-intl'; import CopyToClipboard from 'react-copy-to-clipboard'; import SVGInline from 'react-svg-inline'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import Dialog from '../../widgets/Dialog'; import { getNetworkExplorerUrl } from '../../../utils/network'; import styles from './CompletionDialog.scss'; @@ -78,7 +78,6 @@ type State = { showCopyNotification: boolean; }; -@observer class CompletionDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -109,12 +108,8 @@ class CompletionDialog extends Component { render() { const { intl } = this.context; - const { - onClose, - walletCertificateAddress, - onOpenExternalLink, - network, - } = this.props; + const { onClose, walletCertificateAddress, onOpenExternalLink, network } = + this.props; const { showCopyNotification } = this.state; const actions = [ { @@ -200,4 +195,4 @@ class CompletionDialog extends Component { } } -export default CompletionDialog; +export default observer(CompletionDialog); diff --git a/source/renderer/app/components/wallet/paper-wallet-certificate/ConfirmationDialog.tsx b/source/renderer/app/components/wallet/paper-wallet-certificate/ConfirmationDialog.tsx index 4f45a54a75..d708c570af 100644 --- a/source/renderer/app/components/wallet/paper-wallet-certificate/ConfirmationDialog.tsx +++ b/source/renderer/app/components/wallet/paper-wallet-certificate/ConfirmationDialog.tsx @@ -44,7 +44,6 @@ type Props = { onCancel: (...args: Array) => any; }; -@observer class ConfirmationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -85,4 +84,4 @@ class ConfirmationDialog extends Component { } } -export default ConfirmationDialog; +export default observer(ConfirmationDialog); diff --git a/source/renderer/app/components/wallet/paper-wallet-certificate/InstructionsDialog.tsx b/source/renderer/app/components/wallet/paper-wallet-certificate/InstructionsDialog.tsx index a7da583e8f..9b162e3000 100644 --- a/source/renderer/app/components/wallet/paper-wallet-certificate/InstructionsDialog.tsx +++ b/source/renderer/app/components/wallet/paper-wallet-certificate/InstructionsDialog.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; import { defineMessages, intlShape, FormattedMessage } from 'react-intl'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import Dialog from '../../widgets/Dialog'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import { getNetworkExplorerUrl } from '../../../utils/network'; @@ -39,15 +39,13 @@ const messages = defineMessages({ 'subtitle2 for the "Paper wallet create certificate instructions dialog".', }, instructionsListLabel: { - id: - 'paper.wallet.create.certificate.instructions.dialog.instructionsList.label', + id: 'paper.wallet.create.certificate.instructions.dialog.instructionsList.label', defaultMessage: '!!!Instructions', description: 'Instructions list label for the "Paper wallet create certificate instructions dialog".', }, instructionsListDefinition1: { - id: - 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition1', + id: 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition1', defaultMessage: `!!!Your printed certificate will include your paper wallet recovery phrase of {paperWalletRecoveryPhraseWordCount} words. Note that your paper wallet recovery phrase is different to the {walletRecoveryPhraseWordCount}-word recovery phrases used to restore your @@ -55,37 +53,32 @@ const messages = defineMessages({ description: 'Wallet certificate create instructions dialog definition 1.', }, instructionsListDefinition2: { - id: - 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition2', + id: 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition2', defaultMessage: `!!!For security reasons, the last {paperWalletWrittenWordsCount} words of your paper wallet recovery phrase will not be printed on the paper wallet certificate itself. You will need to write them on your certificate by hand in a moment.`, description: 'Wallet certificate create instructions dialog definition 2.', }, instructionsListDefinition3: { - id: - 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition3', + id: 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition3', defaultMessage: '!!!Use the address on your certificate to send funds to your paper wallet.', description: 'Wallet certificate create instructions dialog definition 3.', }, instructionsListDefinition4: { - id: - 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition4', + id: 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition4', defaultMessage: `!!!Your paper wallet will be offline so will not be held in Daedalus. To check the balance of the wallet, input the address on the certificate into`, description: 'Wallet certificate create instructions dialog definition 4.', }, instructionsListDefinition5: { - id: - 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition5', + id: 'paper.wallet.create.certificate.instructions.dialog.instructionsList.definition5', defaultMessage: '!!!Store your certificate containing your paper wallet recovery phrase in a safe place.', description: 'Wallet certificate create instructions dialog definition 5.', }, printingInstructions: { - id: - 'paper.wallet.create.certificate.instructions.dialog.printingInstructions', + id: 'paper.wallet.create.certificate.instructions.dialog.printingInstructions', defaultMessage: `!!!When you click “Save PDF file for printing” you will be prompted to choose a location on your computer where the PDF file will be saved. After that open the saved PDF file and print it.`, @@ -114,7 +107,6 @@ type Props = { error?: LocalizableError | null | undefined; }; -@observer class InstructionsDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -133,14 +125,8 @@ class InstructionsDialog extends Component { render() { const { intl } = this.context; - const { - onClose, - onPrint, - inProgress, - onOpenExternalLink, - network, - error, - } = this.props; + const { onClose, onPrint, inProgress, onOpenExternalLink, network, error } = + this.props; const printButtonClasses = classnames([ 'printButton', inProgress ? styles.submitButtonSpinning : null, @@ -189,13 +175,16 @@ class InstructionsDialog extends Component {
  • {intl.formatMessage(messages.instructionsListDefinition1, { - paperWalletRecoveryPhraseWordCount: PAPER_WALLET_RECOVERY_PHRASE_WORD_COUNT, - walletRecoveryPhraseWordCount: WALLET_RECOVERY_PHRASE_WORD_COUNT, + paperWalletRecoveryPhraseWordCount: + PAPER_WALLET_RECOVERY_PHRASE_WORD_COUNT, + walletRecoveryPhraseWordCount: + WALLET_RECOVERY_PHRASE_WORD_COUNT, })}
  • {intl.formatMessage(messages.instructionsListDefinition2, { - paperWalletWrittenWordsCount: PAPER_WALLET_WRITTEN_WORDS_COUNT, + paperWalletWrittenWordsCount: + PAPER_WALLET_WRITTEN_WORDS_COUNT, })}
  • @@ -226,4 +215,4 @@ class InstructionsDialog extends Component { } } -export default InstructionsDialog; +export default observer(InstructionsDialog); diff --git a/source/renderer/app/components/wallet/paper-wallet-certificate/PrintDialog.tsx b/source/renderer/app/components/wallet/paper-wallet-certificate/PrintDialog.tsx index 582e9da88a..17bfd50787 100644 --- a/source/renderer/app/components/wallet/paper-wallet-certificate/PrintDialog.tsx +++ b/source/renderer/app/components/wallet/paper-wallet-certificate/PrintDialog.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; import { defineMessages, intlShape } from 'react-intl'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import Dialog from '../../widgets/Dialog'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import globalMessages from '../../../i18n/global-messages'; @@ -35,16 +35,14 @@ const messages = defineMessages({ description: '"Paper wallet create certificate print dialog" info.', }, certificatePrintedConfirmationLabel: { - id: - 'paper.wallet.create.certificate.print.dialog.certificatePrintedConfirmation', + id: 'paper.wallet.create.certificate.print.dialog.certificatePrintedConfirmation', defaultMessage: '!!!Yes, the paper wallet certificate printed successfully.', description: '"Paper wallet create certificate print dialog" certificate printed confirmation.', }, certificateReadableConfirmationLabel: { - id: - 'paper.wallet.create.certificate.print.dialog.certificateReadableConfirmation', + id: 'paper.wallet.create.certificate.print.dialog.certificateReadableConfirmation', defaultMessage: '!!!Yes, first {paperWalletPrintedWordsCount} words of the paper wallet recovery phrase are readable.', description: @@ -67,7 +65,6 @@ type Props = { onClose: (...args: Array) => any; }; -@observer class PrintDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -152,7 +149,8 @@ class PrintDialog extends Component { label={intl.formatMessage( messages.certificateReadableConfirmationLabel, { - paperWalletPrintedWordsCount: PAPER_WALLET_PRINTED_WORDS_COUNT, + paperWalletPrintedWordsCount: + PAPER_WALLET_PRINTED_WORDS_COUNT, } )} onChange={this.onConfirmReadable} @@ -174,4 +172,4 @@ class PrintDialog extends Component { } } -export default PrintDialog; +export default observer(PrintDialog); diff --git a/source/renderer/app/components/wallet/paper-wallet-certificate/SecuringPasswordDialog.tsx b/source/renderer/app/components/wallet/paper-wallet-certificate/SecuringPasswordDialog.tsx index 6139414bd5..2b175eb8ac 100644 --- a/source/renderer/app/components/wallet/paper-wallet-certificate/SecuringPasswordDialog.tsx +++ b/source/renderer/app/components/wallet/paper-wallet-certificate/SecuringPasswordDialog.tsx @@ -1,9 +1,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; -import classnames from 'classnames'; import { defineMessages, intlShape } from 'react-intl'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import Dialog from '../../widgets/Dialog'; import DialogCloseButton from '../../widgets/DialogCloseButton'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/paper-w... Remove this comment to see the full error message @@ -34,8 +33,7 @@ const messages = defineMessages({ description: 'You may write the remaining words here:', }, securingPasswordConfirmation: { - id: - 'paper.wallet.create.certificate.securingPassword.dialog.securingPasswordConfirmation', + id: 'paper.wallet.create.certificate.securingPassword.dialog.securingPasswordConfirmation', defaultMessage: '!!!I have written the remaining {paperWalletWrittenWordsCount} words on the certificate.', description: @@ -51,7 +49,6 @@ type Props = { onClose: (...args: Array) => any; }; -@observer class SecuringPasswordDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -120,4 +117,4 @@ class SecuringPasswordDialog extends Component { } } -export default SecuringPasswordDialog; +export default observer(SecuringPasswordDialog); diff --git a/source/renderer/app/components/wallet/paper-wallet-certificate/VerificationDialog.tsx b/source/renderer/app/components/wallet/paper-wallet-certificate/VerificationDialog.tsx index f4f8703b1b..169eb7996f 100644 --- a/source/renderer/app/components/wallet/paper-wallet-certificate/VerificationDialog.tsx +++ b/source/renderer/app/components/wallet/paper-wallet-certificate/VerificationDialog.tsx @@ -3,8 +3,8 @@ import { observer } from 'mobx-react'; import classnames from 'classnames'; import { defineMessages, intlShape } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import Dialog from '../../widgets/Dialog'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; @@ -41,8 +41,7 @@ const messages = defineMessages({ '"Paper wallet create certificate verification dialog" subtitle.', }, recoveryPhraseLabel: { - id: - 'paper.wallet.create.certificate.verification.dialog.recoveryPhrase.label', + id: 'paper.wallet.create.certificate.verification.dialog.recoveryPhrase.label', defaultMessage: '!!!Paper wallet recovery phrase', description: '"Paper wallet create certificate verification dialog" recovery phrase label.', @@ -54,16 +53,14 @@ const messages = defineMessages({ '"Paper wallet create certificate verification dialog" button clear label.', }, storingUnderstandanceLabel: { - id: - 'paper.wallet.create.certificate.verification.dialog.storingUnderstandanceConfirmationLabel', + id: 'paper.wallet.create.certificate.verification.dialog.storingUnderstandanceConfirmationLabel', defaultMessage: '!!!I understand that the paper wallet I create will not be stored in Daedalus.', description: '"Paper wallet create certificate verification dialog" storing understandance confirmation.', }, recoveringUnderstandanceLabel: { - id: - 'paper.wallet.create.certificate.verification.dialog.recoveringUnderstandanceConfirmationLabel', + id: 'paper.wallet.create.certificate.verification.dialog.recoveringUnderstandanceConfirmationLabel', defaultMessage: '!!!I understand that my paper wallet can be recovered only by using my paper wallet certificate.', description: @@ -88,7 +85,6 @@ interface FormFields { recoveryPhrase: string; } -@observer class VerificationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -212,11 +208,8 @@ class VerificationDialog extends Component { const { intl } = this.context; const { form, resetForm } = this; const { suggestedMnemonics, onClose } = this.props; - const { - storingConfirmed, - recoveringConfirmed, - isRecoveryPhraseValid, - } = this.state; + const { storingConfirmed, recoveringConfirmed, isRecoveryPhraseValid } = + this.state; const recoveryPhraseField = form.$('recoveryPhrase'); const dialogClasses = classnames([styles.dialog, 'verificationDialog']); const storingUnderstandanceCheckboxClasses = classnames([ @@ -241,7 +234,7 @@ class VerificationDialog extends Component { onClick: this.submit.bind(this), }, ]; - const { reset, ...mnemonicInputProps } = recoveryPhraseField.bind(); + const { ...mnemonicInputProps } = recoveryPhraseField.bind(); return ( { } } -export default VerificationDialog; +export default observer(VerificationDialog); diff --git a/source/renderer/app/components/wallet/receive/AddressActions.tsx b/source/renderer/app/components/wallet/receive/AddressActions.tsx index a402d9d1f2..3268a3b8e3 100644 --- a/source/renderer/app/components/wallet/receive/AddressActions.tsx +++ b/source/renderer/app/components/wallet/receive/AddressActions.tsx @@ -52,7 +52,6 @@ type Props = { type?: 'share' | 'copy'; }; -@observer class AddressActions extends Component { static defaultProps = { type: 'copy', @@ -105,4 +104,4 @@ class AddressActions extends Component { } } -export default AddressActions; +export default observer(AddressActions); diff --git a/source/renderer/app/components/wallet/receive/AddressRandom.tsx b/source/renderer/app/components/wallet/receive/AddressRandom.tsx index 7715bd070e..5592de81e1 100644 --- a/source/renderer/app/components/wallet/receive/AddressRandom.tsx +++ b/source/renderer/app/components/wallet/receive/AddressRandom.tsx @@ -12,7 +12,6 @@ type Props = { onShareAddress: (...args: Array) => any; }; -@observer class AddressRandom extends Component { render() { const { address, onCopyAddress, onShareAddress, index } = this.props; @@ -37,4 +36,4 @@ class AddressRandom extends Component { } } -export default AddressRandom; +export default observer(AddressRandom); diff --git a/source/renderer/app/components/wallet/receive/AddressSequential.tsx b/source/renderer/app/components/wallet/receive/AddressSequential.tsx index 68db531500..fad036de15 100644 --- a/source/renderer/app/components/wallet/receive/AddressSequential.tsx +++ b/source/renderer/app/components/wallet/receive/AddressSequential.tsx @@ -14,7 +14,6 @@ type Props = { addressSlice: number; }; -@observer class AddressSequential extends Component { addressElement: HTMLElement | null | undefined; addressContainerElement: HTMLElement | null | undefined; @@ -92,4 +91,4 @@ class AddressSequential extends Component { } } -export default AddressSequential; +export default observer(AddressSequential); diff --git a/source/renderer/app/components/wallet/receive/VirtualAddressesList.tsx b/source/renderer/app/components/wallet/receive/VirtualAddressesList.tsx index b7ccd7cff0..3f5368cc4f 100644 --- a/source/renderer/app/components/wallet/receive/VirtualAddressesList.tsx +++ b/source/renderer/app/components/wallet/receive/VirtualAddressesList.tsx @@ -24,105 +24,106 @@ const ADDRESS_LINE_HEIGHT = 22; const ADDRESS_LINE_PADDING = 21; const ADDRESS_SELECTOR = '.Address'; -@observer -class VirtualAddressesList extends Component { - list: List; - listWidth = 0; - addressHeight = 0; +const VirtualAddressesList = observer( + class VirtualAddressesList extends Component { + list: List; + listWidth = 0; + addressHeight = 0; - /** - * Estimate the address height based on number of lines - */ - estimateAddressHeight = (lines: number): number => - ADDRESS_LINE_HEIGHT * lines + ADDRESS_LINE_PADDING; + /** + * Estimate the address height based on number of lines + */ + estimateAddressHeight = (lines: number): number => + ADDRESS_LINE_HEIGHT * lines + ADDRESS_LINE_PADDING; - /** - * Gets the number of lines based on the container width - */ - getLinesFromWidth = (width: number): number => { - if (width >= BREAKPOINT_1_LINE) return 1; - if (width >= BREAKPOINT_2_LINES) return 2; - return 3; - }; + /** + * Gets the number of lines based on the container width + */ + getLinesFromWidth = (width: number): number => { + if (width >= BREAKPOINT_1_LINE) return 1; + if (width >= BREAKPOINT_2_LINES) return 2; + return 3; + }; - /** - * Virtual row heights only once per tick (debounced) - */ - updateRowHeights = () => { - const { list, addressHeight } = this; - if (!list) return; - const firstAddress = document.querySelector(ADDRESS_SELECTOR); + /** + * Virtual row heights only once per tick (debounced) + */ + updateRowHeights = () => { + const { list, addressHeight } = this; + if (!list) return; + const firstAddress = document.querySelector(ADDRESS_SELECTOR); - if (firstAddress instanceof HTMLElement) { - this.addressHeight = firstAddress.offsetHeight; - } else { - this.addressHeight = this.estimateAddressHeight( - this.getLinesFromWidth(this.listWidth) - ); - // Since we could only estimate the address heights, re-try - // the update and hope that DOM is rendered then (for exact measurements) - setTimeout(this.updateRowHeights, 100); - } + if (firstAddress instanceof HTMLElement) { + this.addressHeight = firstAddress.offsetHeight; + } else { + this.addressHeight = this.estimateAddressHeight( + this.getLinesFromWidth(this.listWidth) + ); + // Since we could only estimate the address heights, re-try + // the update and hope that DOM is rendered then (for exact measurements) + setTimeout(this.updateRowHeights, 100); + } - if (addressHeight !== this.addressHeight) { - list.recomputeRowHeights(0); - } - }; + if (addressHeight !== this.addressHeight) { + list.recomputeRowHeights(0); + } + }; - /** - * Update row height and recompute virtual rows. - */ - onResize = ({ width }: { width: number }): void => { - this.listWidth = width; - this.updateRowHeights(); - }; - rowRenderer = ({ - index, - // Index of row - key, - // Unique key within array of rendered rows - style, // Style object to be applied to row (to position it); - }: { - index: number; - key: string; - style: string; - }) => { - const { rows, renderRow } = this.props; - const address = rows[index]; - return ( - // @ts-ignore ts-migrate(2559) FIXME: Type 'string' has no properties in common with typ... Remove this comment to see the full error message -
    - {renderRow(address, index)} -
    - ); - }; + /** + * Update row height and recompute virtual rows. + */ + onResize = ({ width }: { width: number }): void => { + this.listWidth = width; + this.updateRowHeights(); + }; + rowRenderer = ({ + index, + // Index of row + key, + // Unique key within array of rendered rows + style, // Style object to be applied to row (to position it); + }: { + index: number; + key: string; + style: string; + }) => { + const { rows, renderRow } = this.props; + const address = rows[index]; + return ( + // @ts-ignore ts-migrate(2559) FIXME: Type 'string' has no properties in common with typ... Remove this comment to see the full error message +
    + {renderRow(address, index)} +
    + ); + }; - render() { - const { rows } = this.props; - if (!rows.length) return null; - return ( -
    - - {({ width, height }) => ( - { - this.list = list; - }} - width={width} - height={height} - rowCount={rows.length} - rowHeight={() => this.addressHeight} - rowRenderer={this.rowRenderer} - style={{ - overflowY: 'scroll', - }} - /> - )} - -
    - ); + render() { + const { rows } = this.props; + if (!rows.length) return null; + return ( +
    + + {({ width, height }) => ( + { + this.list = list; + }} + width={width} + height={height} + rowCount={rows.length} + rowHeight={() => this.addressHeight} + rowRenderer={this.rowRenderer} + style={{ + overflowY: 'scroll', + }} + /> + )} + +
    + ); + } } -} +); export { VirtualAddressesList }; diff --git a/source/renderer/app/components/wallet/receive/WalletReceiveDialog.tsx b/source/renderer/app/components/wallet/receive/WalletReceiveDialog.tsx index 19e4e027ce..a70afeb666 100644 --- a/source/renderer/app/components/wallet/receive/WalletReceiveDialog.tsx +++ b/source/renderer/app/components/wallet/receive/WalletReceiveDialog.tsx @@ -2,15 +2,14 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { map, filter } from 'lodash'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import classnames from 'classnames'; import CopyToClipboard from 'react-copy-to-clipboard'; import SVGInline from 'react-svg-inline'; -import { TextArea } from 'react-polymorph/lib/components/TextArea'; -import { TextAreaSkin } from 'react-polymorph/lib/skins/simple/TextAreaSkin'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { TextArea } from '@react-polymorph/components/TextArea'; +import { TextAreaSkin } from '@react-polymorph/skins/simple/TextAreaSkin'; +import { PopOver } from '@react-polymorph/components/PopOver'; import QRCode from 'qrcode.react'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import { str_to_path } from '@cardano-foundation/ledgerjs-hw-app-cardano/dist/utils/address'; import RadioSet from '../../widgets/RadioSet'; import Dialog from '../../widgets/Dialog'; @@ -160,7 +159,6 @@ interface FormFields { noteInput: string; } -@observer class WalletReceiveDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -197,9 +195,8 @@ class WalletReceiveDialog extends Component { }; constructPaths = (address: WalletAddress) => { const hardenedSpendingPath = str_to_path(address.spendingPath); - const derivationSpendingPath = hardenedPathToDerivationPath( - hardenedSpendingPath - ); + const derivationSpendingPath = + hardenedPathToDerivationPath(hardenedSpendingPath); const spendingPath = map( derivationSpendingPath.constructed, (constructeSpendingPathChunk, index) => { @@ -525,4 +522,4 @@ class WalletReceiveDialog extends Component { } } -export default WalletReceiveDialog; +export default observer(WalletReceiveDialog); diff --git a/source/renderer/app/components/wallet/receive/WalletReceiveRandom.tsx b/source/renderer/app/components/wallet/receive/WalletReceiveRandom.tsx index 0c1fc39a20..bb3356d6c9 100644 --- a/source/renderer/app/components/wallet/receive/WalletReceiveRandom.tsx +++ b/source/renderer/app/components/wallet/receive/WalletReceiveRandom.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; @@ -6,10 +8,10 @@ import SVGInline from 'react-svg-inline'; import classnames from 'classnames'; import CopyToClipboard from 'react-copy-to-clipboard'; import QRCode from 'qrcode.react'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { Input } from '@react-polymorph/components/Input'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; import { submitOnEnter } from '../../../utils/form'; import BorderedBox from '../../widgets/BorderedBox'; @@ -85,7 +87,6 @@ interface FormFields { spendingPassword: string; } -@observer class WalletReceiveRandom extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -302,4 +303,4 @@ class WalletReceiveRandom extends Component { } } -export default WalletReceiveRandom; +export default observer(WalletReceiveRandom); diff --git a/source/renderer/app/components/wallet/receive/WalletReceiveSequential.tsx b/source/renderer/app/components/wallet/receive/WalletReceiveSequential.tsx index a0e52e846b..877ef415cb 100644 --- a/source/renderer/app/components/wallet/receive/WalletReceiveSequential.tsx +++ b/source/renderer/app/components/wallet/receive/WalletReceiveSequential.tsx @@ -55,7 +55,6 @@ type State = { charWidth: number; }; -@observer class WalletReceiveSequential extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -194,4 +193,4 @@ class WalletReceiveSequential extends Component { } } -export default WalletReceiveSequential; +export default observer(WalletReceiveSequential); diff --git a/source/renderer/app/components/wallet/send-form/AssetInput.tsx b/source/renderer/app/components/wallet/send-form/AssetInput.tsx index ae2541d470..f515d31f18 100644 --- a/source/renderer/app/components/wallet/send-form/AssetInput.tsx +++ b/source/renderer/app/components/wallet/send-form/AssetInput.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import type { Field } from 'mobx-react-form'; @@ -5,7 +7,7 @@ import { intlShape } from 'react-intl'; import { get } from 'lodash'; import classNames from 'classnames'; import SVGInline from 'react-svg-inline'; -import { NumericInput } from 'react-polymorph/lib/components/NumericInput'; +import { NumericInput } from '@react-polymorph/components/NumericInput'; import AmountInputSkin from '../skins/AmountInputSkin'; import removeIcon from '../../../assets/images/remove.inline.svg'; import type { NumberFormat } from '../../../../../common/types/number.types'; @@ -29,7 +31,6 @@ type Props = { }; const INPUT_FIELD_PADDING_DELTA = 10; -@observer class AssetInput extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -172,4 +173,4 @@ class AssetInput extends Component { } } -export default AssetInput; +export default observer(AssetInput); diff --git a/source/renderer/app/components/wallet/settings/ChangeSpendingPasswordDialog.tsx b/source/renderer/app/components/wallet/settings/ChangeSpendingPasswordDialog.tsx index bf7a6e54be..2d6876f64a 100644 --- a/source/renderer/app/components/wallet/settings/ChangeSpendingPasswordDialog.tsx +++ b/source/renderer/app/components/wallet/settings/ChangeSpendingPasswordDialog.tsx @@ -1,11 +1,13 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Input } from '@react-polymorph/components/Input'; import { defineMessages, FormattedHTMLMessage, intlShape } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; import SVGInline from 'react-svg-inline'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; @@ -104,7 +106,6 @@ interface FormFields { repeatPassword: string; } -@observer class ChangeSpendingPasswordDialog extends Component { static defaultProps = { currentPasswordValue: '', @@ -339,4 +340,4 @@ class ChangeSpendingPasswordDialog extends Component { } } -export default ChangeSpendingPasswordDialog; +export default observer(ChangeSpendingPasswordDialog); diff --git a/source/renderer/app/components/wallet/settings/DelegateWalletButton.tsx b/source/renderer/app/components/wallet/settings/DelegateWalletButton.tsx index eff16106fa..a286da9682 100644 --- a/source/renderer/app/components/wallet/settings/DelegateWalletButton.tsx +++ b/source/renderer/app/components/wallet/settings/DelegateWalletButton.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { defineMessages, intlShape } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; +import { Button } from '@react-polymorph/components/Button'; import styles from './DelegateWalletButton.scss'; const messages = defineMessages({ diff --git a/source/renderer/app/components/wallet/settings/DeleteWalletConfirmation.tsx b/source/renderer/app/components/wallet/settings/DeleteWalletConfirmation.tsx index c12186b26e..4a096505f4 100644 --- a/source/renderer/app/components/wallet/settings/DeleteWalletConfirmation.tsx +++ b/source/renderer/app/components/wallet/settings/DeleteWalletConfirmation.tsx @@ -1,8 +1,10 @@ +// @ts-nocheck + import React from 'react'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import { submitOnEnter } from '../../../utils/form'; import styles from './DeleteWalletConfirmationDialog.scss'; diff --git a/source/renderer/app/components/wallet/settings/ExportWalletToFileDialog.tsx b/source/renderer/app/components/wallet/settings/ExportWalletToFileDialog.tsx index 87ecbca361..c1a4e41ce2 100644 --- a/source/renderer/app/components/wallet/settings/ExportWalletToFileDialog.tsx +++ b/source/renderer/app/components/wallet/settings/ExportWalletToFileDialog.tsx @@ -5,8 +5,8 @@ import { observer } from 'mobx-react'; import classnames from 'classnames'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; -// import { Input } from 'react-polymorph/lib/components/Input'; -// import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +// import { Input } from '@react-polymorph/components/Input'; +// import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; import globalMessages from '../../../i18n/global-messages'; @@ -15,7 +15,6 @@ import LocalizableError from '../../../i18n/LocalizableError'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module './ExportWalletToFileDialog.scs... Remove this comment to see the full error message import styles from './ExportWalletToFileDialog.scss'; import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../../config/timingConfig'; -import { submitOnEnter } from '../../../utils/form'; const messages = defineMessages({ headline: { @@ -69,7 +68,6 @@ interface FormFields { spendingPassword: string; } -@observer class ExportWalletToFileDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -205,4 +203,4 @@ class ExportWalletToFileDialog extends Component { } } -export default ExportWalletToFileDialog; +export default observer(ExportWalletToFileDialog); diff --git a/source/renderer/app/components/wallet/settings/ICOPublicKeyDialog.tsx b/source/renderer/app/components/wallet/settings/ICOPublicKeyDialog.tsx index 215f99c510..a88f6e6eff 100644 --- a/source/renderer/app/components/wallet/settings/ICOPublicKeyDialog.tsx +++ b/source/renderer/app/components/wallet/settings/ICOPublicKeyDialog.tsx @@ -1,7 +1,9 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Input } from '@react-polymorph/components/Input'; import vjf from 'mobx-react-form/lib/validators/VJF'; import styles from './PublicKeyDialog.scss'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; @@ -44,7 +46,6 @@ interface FormFields { spendingPassword: string; } -@observer class ICOPublicKeyDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -147,4 +148,4 @@ class ICOPublicKeyDialog extends Component { } } -export default ICOPublicKeyDialog; +export default observer(ICOPublicKeyDialog); diff --git a/source/renderer/app/components/wallet/settings/ICOPublicKeyQRCodeDialog.tsx b/source/renderer/app/components/wallet/settings/ICOPublicKeyQRCodeDialog.tsx index 6fa6b8db22..4ec049a5fd 100644 --- a/source/renderer/app/components/wallet/settings/ICOPublicKeyQRCodeDialog.tsx +++ b/source/renderer/app/components/wallet/settings/ICOPublicKeyQRCodeDialog.tsx @@ -4,7 +4,7 @@ import { injectIntl, intlShape } from 'react-intl'; import CopyToClipboard from 'react-copy-to-clipboard'; import SVGInline from 'react-svg-inline'; import QRCode from 'qrcode.react'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/clipboa... Remove this comment to see the full error message diff --git a/source/renderer/app/components/wallet/settings/PublicKeyField.tsx b/source/renderer/app/components/wallet/settings/PublicKeyField.tsx index 92ee21e946..f7a1e5e32a 100644 --- a/source/renderer/app/components/wallet/settings/PublicKeyField.tsx +++ b/source/renderer/app/components/wallet/settings/PublicKeyField.tsx @@ -1,9 +1,11 @@ +// @ts-nocheck + import React, { useCallback, useState, useEffect } from 'react'; import SVGInline from 'react-svg-inline'; import classnames from 'classnames'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { Button } from '@react-polymorph/components/Button'; +import { Input } from '@react-polymorph/components/Input'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { injectIntl, intlShape } from 'react-intl'; import PublicKeyFieldSkin from './PublicKeyFieldSkin'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/qr-code... Remove this comment to see the full error message diff --git a/source/renderer/app/components/wallet/settings/PublicKeyFieldSkin.tsx b/source/renderer/app/components/wallet/settings/PublicKeyFieldSkin.tsx index c001670369..70cb78282e 100644 --- a/source/renderer/app/components/wallet/settings/PublicKeyFieldSkin.tsx +++ b/source/renderer/app/components/wallet/settings/PublicKeyFieldSkin.tsx @@ -1,13 +1,15 @@ +// @ts-nocheck + import React from 'react'; import type { ElementRef } from 'react'; import classnames from 'classnames'; import CopyToClipboard from 'react-copy-to-clipboard'; import SVGInline from 'react-svg-inline'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; -import { FormField } from 'react-polymorph/lib/components/FormField'; -import type { InputProps } from 'react-polymorph/lib/components/Input'; -import { pickDOMProps } from 'react-polymorph/lib/utils/props'; +import { Button } from '@react-polymorph/components/Button'; +import { PopOver } from '@react-polymorph/components/PopOver'; +import { FormField } from '@react-polymorph/components/FormField'; +import type { InputProps } from '@react-polymorph/components/Input'; +import { pickDOMProps } from '@react-polymorph/utils/props'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/copy.in... Remove this comment to see the full error message import copyImage from '../../../assets/images/copy.inline.svg'; import styles from './PublicKeyField.scss'; diff --git a/source/renderer/app/components/wallet/settings/SetWalletPassword.tsx b/source/renderer/app/components/wallet/settings/SetWalletPassword.tsx index ef53319565..a4d7e53477 100644 --- a/source/renderer/app/components/wallet/settings/SetWalletPassword.tsx +++ b/source/renderer/app/components/wallet/settings/SetWalletPassword.tsx @@ -33,7 +33,6 @@ type Props = { onSetWalletPassword: (...args: Array) => any; }; -@observer class SetWalletPassword extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -75,4 +74,4 @@ class SetWalletPassword extends Component { } } -export default SetWalletPassword; +export default observer(SetWalletPassword); diff --git a/source/renderer/app/components/wallet/settings/UndelegateWalletButton.tsx b/source/renderer/app/components/wallet/settings/UndelegateWalletButton.tsx index a5c53df866..b2b65aba36 100644 --- a/source/renderer/app/components/wallet/settings/UndelegateWalletButton.tsx +++ b/source/renderer/app/components/wallet/settings/UndelegateWalletButton.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { defineMessages, intlShape } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; +import { Button } from '@react-polymorph/components/Button'; import styles from './UndelegateWalletButton.scss'; const messages = defineMessages({ diff --git a/source/renderer/app/components/wallet/settings/UndelegateWalletConfirmationDialog.tsx b/source/renderer/app/components/wallet/settings/UndelegateWalletConfirmationDialog.tsx index dd4710ff00..fba239d0e1 100644 --- a/source/renderer/app/components/wallet/settings/UndelegateWalletConfirmationDialog.tsx +++ b/source/renderer/app/components/wallet/settings/UndelegateWalletConfirmationDialog.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck /* eslint-disable jsx-a11y/label-has-associated-control, jsx-a11y/label-has-for */ import React, { Component } from 'react'; import { observer } from 'mobx-react'; @@ -5,8 +6,8 @@ import { get } from 'lodash'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; import classnames from 'classnames'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { Input } from '@react-polymorph/components/Input'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../../config/timingConfig'; import { formattedWalletAmount } from '../../../utils/formatters'; @@ -121,7 +122,6 @@ interface FormFields { passphrase: string; } -@observer class UndelegateWalletConfirmationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -412,4 +412,4 @@ class UndelegateWalletConfirmationDialog extends Component { } } -export default UndelegateWalletConfirmationDialog; +export default observer(UndelegateWalletConfirmationDialog); diff --git a/source/renderer/app/components/wallet/settings/UndelegateWalletSuccessDialog.tsx b/source/renderer/app/components/wallet/settings/UndelegateWalletSuccessDialog.tsx index cb1005bec7..4053ab2317 100644 --- a/source/renderer/app/components/wallet/settings/UndelegateWalletSuccessDialog.tsx +++ b/source/renderer/app/components/wallet/settings/UndelegateWalletSuccessDialog.tsx @@ -40,7 +40,6 @@ type State = { timeUntilNextEpochStart: number; }; -@observer class UndelegateWalletSuccessDialog extends Component { // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. intervalHandler: IntervalID | null | undefined = null; @@ -126,4 +125,4 @@ class UndelegateWalletSuccessDialog extends Component { } } -export default UndelegateWalletSuccessDialog; +export default observer(UndelegateWalletSuccessDialog); diff --git a/source/renderer/app/components/wallet/settings/WalletPublicKeyDialog.tsx b/source/renderer/app/components/wallet/settings/WalletPublicKeyDialog.tsx index 8a72c6f025..2782962ac3 100644 --- a/source/renderer/app/components/wallet/settings/WalletPublicKeyDialog.tsx +++ b/source/renderer/app/components/wallet/settings/WalletPublicKeyDialog.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Input } from '@react-polymorph/components/Input'; import vjf from 'mobx-react-form/lib/validators/VJF'; import styles from './PublicKeyDialog.scss'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; @@ -43,7 +44,6 @@ interface FormFields { spendingPassword: string; } -@observer class WalletPublicKeyDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -146,4 +146,4 @@ class WalletPublicKeyDialog extends Component { } } -export default WalletPublicKeyDialog; +export default observer(WalletPublicKeyDialog); diff --git a/source/renderer/app/components/wallet/settings/WalletPublicKeyQRCodeDialog.tsx b/source/renderer/app/components/wallet/settings/WalletPublicKeyQRCodeDialog.tsx index f3a2984cbf..98eafff229 100644 --- a/source/renderer/app/components/wallet/settings/WalletPublicKeyQRCodeDialog.tsx +++ b/source/renderer/app/components/wallet/settings/WalletPublicKeyQRCodeDialog.tsx @@ -4,7 +4,7 @@ import { injectIntl, intlShape } from 'react-intl'; import CopyToClipboard from 'react-copy-to-clipboard'; import SVGInline from 'react-svg-inline'; import QRCode from 'qrcode.react'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/clipboa... Remove this comment to see the full error message diff --git a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep1Dialog.tsx b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep1Dialog.tsx index a975e13ab3..a3ef1a5b9b 100644 --- a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep1Dialog.tsx +++ b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep1Dialog.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; import styles from './WalletRecoveryPhraseStepDialogs.scss'; @@ -42,7 +42,6 @@ type State = { safetyAgreement: boolean; }; -@observer class WalletRecoveryPhraseStep1Dialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -93,4 +92,4 @@ class WalletRecoveryPhraseStep1Dialog extends Component { } } -export default WalletRecoveryPhraseStep1Dialog; +export default observer(WalletRecoveryPhraseStep1Dialog); diff --git a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep2Dialog.tsx b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep2Dialog.tsx index 792d73682e..8d3591e78a 100644 --- a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep2Dialog.tsx +++ b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep2Dialog.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React, { Component } from 'react'; import { observer } from 'mobx-react'; -import { Autocomplete } from 'react-polymorph/lib/components/Autocomplete'; -import { AutocompleteSkin } from 'react-polymorph/lib/skins/simple/AutocompleteSkin'; +import { Autocomplete } from '@react-polymorph/components/Autocomplete'; +import { AutocompleteSkin } from '@react-polymorph/skins/simple/AutocompleteSkin'; import { defineMessages, intlShape } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; import suggestedMnemonics from '../../../../../common/config/crypto/valid-words.en'; @@ -73,7 +74,6 @@ interface FormFields { recoveryPhrase: string; } -@observer class WalletRecoveryPhraseStep2Dialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -204,4 +204,4 @@ class WalletRecoveryPhraseStep2Dialog extends Component { } } -export default WalletRecoveryPhraseStep2Dialog; +export default observer(WalletRecoveryPhraseStep2Dialog); diff --git a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep3Dialog.tsx b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep3Dialog.tsx index e26a61e79d..3d72c20850 100644 --- a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep3Dialog.tsx +++ b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep3Dialog.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; import { defineMessages, intlShape } from 'react-intl'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module './WalletRecoveryPhraseStepDial... Remove this comment to see the full error message @@ -43,7 +43,6 @@ type State = { safetyAgreement: boolean; }; -@observer class WalletRecoveryPhraseStep3Dialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -95,4 +94,4 @@ class WalletRecoveryPhraseStep3Dialog extends Component { } } -export default WalletRecoveryPhraseStep3Dialog; +export default observer(WalletRecoveryPhraseStep3Dialog); diff --git a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep4Dialog.tsx b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep4Dialog.tsx index 9f1dade81f..0688e0d622 100644 --- a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep4Dialog.tsx +++ b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseStep4Dialog.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; import { defineMessages, intlShape } from 'react-intl'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import Dialog from '../../widgets/Dialog'; import styles from './WalletRecoveryPhraseStepDialogs.scss'; @@ -54,7 +54,6 @@ type Props = { walletName: string; }; -@observer class WalletRecoveryPhraseStep4Dialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -104,4 +103,4 @@ class WalletRecoveryPhraseStep4Dialog extends Component { } } -export default WalletRecoveryPhraseStep4Dialog; +export default observer(WalletRecoveryPhraseStep4Dialog); diff --git a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseVerificationWidget.tsx b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseVerificationWidget.tsx index 7062631950..2a8b6dc123 100644 --- a/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseVerificationWidget.tsx +++ b/source/renderer/app/components/wallet/settings/WalletRecoveryPhraseVerificationWidget.tsx @@ -1,11 +1,12 @@ +// @ts-nocheck import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { camelCase } from 'lodash'; import classnames from 'classnames'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { PopOver } from '@react-polymorph/components/PopOver'; import moment from 'moment'; import SVGInline from 'react-svg-inline'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/recover... Remove this comment to see the full error message @@ -118,8 +119,7 @@ export const messages = defineMessages({ 'Label for the recoveryPhraseVerificationButton on wallet settings.', }, timeUntilWarningReplacement: { - id: - 'wallet.settings.recoveryPhraseVerification.timeUntilWarningReplacement', + id: 'wallet.settings.recoveryPhraseVerification.timeUntilWarningReplacement', defaultMessage: '!!!ヶ月,か月', description: 'Label for the recoveryPhraseVerificationButton on wallet settings.', @@ -134,7 +134,6 @@ export type Props = { isLegacy: boolean; }; -@observer class WalletRecoveryPhraseVerificationWidget extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -226,12 +225,8 @@ class WalletRecoveryPhraseVerificationWidget extends Component { recoveryPhraseVerificationDate, isLegacy, } = this.props; - const { - icon, - message, - timeAgo, - timeUntilWarning, - } = this.recoveryPhraseStatus; + const { icon, message, timeAgo, timeUntilWarning } = + this.recoveryPhraseStatus; const { recoveryPhraseVerificationStatus } = getStatusFromWalletData({ creationDate, recoveryPhraseVerificationDate, @@ -294,4 +289,4 @@ class WalletRecoveryPhraseVerificationWidget extends Component { } } -export default WalletRecoveryPhraseVerificationWidget; +export default observer(WalletRecoveryPhraseVerificationWidget); diff --git a/source/renderer/app/components/wallet/settings/WalletSettings.tsx b/source/renderer/app/components/wallet/settings/WalletSettings.tsx index 7f8ba2d9c3..1b43fd224f 100644 --- a/source/renderer/app/components/wallet/settings/WalletSettings.tsx +++ b/source/renderer/app/components/wallet/settings/WalletSettings.tsx @@ -142,7 +142,6 @@ type State = { isFormBlocked: boolean; }; -@observer class WalletSettings extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -181,11 +180,8 @@ class WalletSettings extends Component { }); }; onUndelegateWalletClick = async () => { - const { - walletId, - openDialogAction, - updateDataForActiveDialogAction, - } = this.props; + const { walletId, openDialogAction, updateDataForActiveDialogAction } = + this.props; this.onBlockForm(); openDialogAction({ dialog: UndelegateWalletConfirmationDialog, @@ -374,19 +370,21 @@ class WalletSettings extends Component { )} - {IS_ICO_PUBLIC_KEY_SHARING_ENABLED && !isLegacy && !isHardwareWallet && ( - <> - - {isDialogOpen(ICOPublicKeyDialog) && icoPublicKeyDialogContainer} - {isDialogOpen(ICOPublicKeyQRCodeDialog) && - icoPublicKeyQRCodeDialogContainer} - - )} + {IS_ICO_PUBLIC_KEY_SHARING_ENABLED && + !isLegacy && + !isHardwareWallet && ( + <> + + {isDialogOpen(ICOPublicKeyDialog) && icoPublicKeyDialogContainer} + {isDialogOpen(ICOPublicKeyQRCodeDialog) && + icoPublicKeyQRCodeDialogContainer} + + )} {this.renderUndelegateWalletBox()} {isHardwareWallet ? ( @@ -409,4 +407,4 @@ class WalletSettings extends Component { } } -export default WalletSettings; +export default observer(WalletSettings); diff --git a/source/renderer/app/components/wallet/settings/WalletSettingsRemoveButton.tsx b/source/renderer/app/components/wallet/settings/WalletSettingsRemoveButton.tsx index 3b6f8ba463..e5c3dee434 100644 --- a/source/renderer/app/components/wallet/settings/WalletSettingsRemoveButton.tsx +++ b/source/renderer/app/components/wallet/settings/WalletSettingsRemoveButton.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import styles from './RemoveWalletButton.scss'; type Props = { diff --git a/source/renderer/app/components/wallet/settings/WalletSettingsRemoveConfirmationDialog.tsx b/source/renderer/app/components/wallet/settings/WalletSettingsRemoveConfirmationDialog.tsx index ace2331c1f..40a2ef3291 100644 --- a/source/renderer/app/components/wallet/settings/WalletSettingsRemoveConfirmationDialog.tsx +++ b/source/renderer/app/components/wallet/settings/WalletSettingsRemoveConfirmationDialog.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import classnames from 'classnames'; import { FormattedHTMLMessage, injectIntl, intlShape } from 'react-intl'; import { observer } from 'mobx-react'; import DialogCloseButton from '../../widgets/DialogCloseButton'; diff --git a/source/renderer/app/components/wallet/skins/AmountInputSkin.tsx b/source/renderer/app/components/wallet/skins/AmountInputSkin.tsx index 1e8bdcfb16..d4e6c3218b 100644 --- a/source/renderer/app/components/wallet/skins/AmountInputSkin.tsx +++ b/source/renderer/app/components/wallet/skins/AmountInputSkin.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React, { Component } from 'react'; import { defineMessages, intlShape } from 'react-intl'; import BigNumber from 'bignumber.js'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import styles from './AmountInputSkin.scss'; export const messages = defineMessages({ diff --git a/source/renderer/app/components/wallet/summary/WalletSummary.tsx b/source/renderer/app/components/wallet/summary/WalletSummary.tsx index 2f5fb3e439..32305f962b 100644 --- a/source/renderer/app/components/wallet/summary/WalletSummary.tsx +++ b/source/renderer/app/components/wallet/summary/WalletSummary.tsx @@ -43,7 +43,6 @@ type Props = { tokenFavorites: Record; }; -@observer class WalletSummary extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -122,4 +121,4 @@ class WalletSummary extends Component { } } -export default WalletSummary; +export default observer(WalletSummary); diff --git a/source/renderer/app/components/wallet/summary/WalletSummaryCurrency.tsx b/source/renderer/app/components/wallet/summary/WalletSummaryCurrency.tsx index 63d9a61a6b..d311207e76 100644 --- a/source/renderer/app/components/wallet/summary/WalletSummaryCurrency.tsx +++ b/source/renderer/app/components/wallet/summary/WalletSummaryCurrency.tsx @@ -41,7 +41,6 @@ type Props = { onCurrencySettingClick: (...args: Array) => any; }; -@observer class WalletSummaryCurrency extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -115,4 +114,4 @@ class WalletSummaryCurrency extends Component { } } -export default WalletSummaryCurrency; +export default observer(WalletSummaryCurrency); diff --git a/source/renderer/app/components/wallet/summary/WalletSummaryHeader.tsx b/source/renderer/app/components/wallet/summary/WalletSummaryHeader.tsx index b40ba89679..fc71144d6e 100644 --- a/source/renderer/app/components/wallet/summary/WalletSummaryHeader.tsx +++ b/source/renderer/app/components/wallet/summary/WalletSummaryHeader.tsx @@ -30,7 +30,6 @@ type Props = { currency?: Node; }; -@observer class WalletSummaryHeader extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -111,4 +110,4 @@ class WalletSummaryHeader extends Component { } } -export default WalletSummaryHeader; +export default observer(WalletSummaryHeader); diff --git a/source/renderer/app/components/wallet/summary/WalletSummaryHeaderRewards.tsx b/source/renderer/app/components/wallet/summary/WalletSummaryHeaderRewards.tsx index 214d2ffed9..20380a6504 100644 --- a/source/renderer/app/components/wallet/summary/WalletSummaryHeaderRewards.tsx +++ b/source/renderer/app/components/wallet/summary/WalletSummaryHeaderRewards.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck import BigNumber from 'bignumber.js'; import React from 'react'; import { observer } from 'mobx-react'; @@ -6,7 +7,7 @@ import { FormattedHTMLMessage, FormattedMessage, } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import SVGInline from 'react-svg-inline'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/question... Remove this comment to see the full error message import questionMarkIcon from '../../../assets/images/question-mark.inline.svg'; diff --git a/source/renderer/app/components/wallet/tokens/wallet-no-tokens/WalletNoTokens.tsx b/source/renderer/app/components/wallet/tokens/wallet-no-tokens/WalletNoTokens.tsx index e1802c69ea..1e4fae4197 100644 --- a/source/renderer/app/components/wallet/tokens/wallet-no-tokens/WalletNoTokens.tsx +++ b/source/renderer/app/components/wallet/tokens/wallet-no-tokens/WalletNoTokens.tsx @@ -41,7 +41,6 @@ type Props = { numberOfAssets: number; }; -@observer class WalletSummaryNoTokens extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -80,4 +79,4 @@ class WalletSummaryNoTokens extends Component { } } -export default WalletSummaryNoTokens; +export default observer(WalletSummaryNoTokens); diff --git a/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPicker.stories.tsx b/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPicker.stories.tsx deleted file mode 100644 index fa14c48f69..0000000000 --- a/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPicker.stories.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { storiesOf } from '@storybook/react'; -import { withKnobs } from '@storybook/addon-knobs'; -import BigNumber from 'bignumber.js'; -import StoryDecorator from '../../../../../../../storybook/stories/_support/StoryDecorator'; -import StoryProvider from '../../../../../../../storybook/stories/_support/StoryProvider'; -import { generateHash } from '../../../../../../../storybook/stories/_support/utils'; -import WalletTokenPicker from './WalletTokenPicker'; - -const assets = [ - { - policyId: generateHash(), - assetName: '546f6b656e31', - assetNameASCII: 'Token1', - quantity: new BigNumber(10), - fingerprint: generateHash(), - recommendedDecimals: null, - uniqueId: generateHash(), - metadata: { - name: 'MakerDAO', - ticker: 'DAI', - description: 'Test description', - unit: { - name: 'DAI', - decimals: 6, - }, - url: 'http://example.com', - logo: '', - }, - }, - { - policyId: generateHash(), - assetName: '546f6b656e32', - assetNameASCII: 'Token2', - quantity: new BigNumber(20), - fingerprint: generateHash(), - recommendedDecimals: null, - uniqueId: generateHash(), - }, - { - policyId: generateHash(), - assetName: '546f6b656e33', - assetNameASCII: 'Token3', - quantity: new BigNumber(30), - fingerprint: generateHash(), - recommendedDecimals: null, - uniqueId: generateHash(), - }, - { - policyId: generateHash(), - assetName: '546f6b656e34', - assetNameASCII: 'Token4', - quantity: new BigNumber(30), - fingerprint: generateHash(), - recommendedDecimals: null, - uniqueId: generateHash(), - }, -]; -storiesOf('Wallets / Tokens', module) - .addDecorator((story) => ( - - {story()} - - )) - .addDecorator(withKnobs) - .add('WalletTokenPicker', () => ( - {}} - onCancel={() => {}} - /> - )); diff --git a/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPicker.tsx b/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPicker.tsx index 54d845e7e5..4033efc8bf 100644 --- a/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPicker.tsx +++ b/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPicker.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { observer } from 'mobx-react'; import { injectIntl } from 'react-intl'; import classNames from 'classnames'; -import { Select } from 'react-polymorph/lib/components/Select'; +import { Select } from '@react-polymorph/components/Select'; import Dialog from '../../../widgets/Dialog'; import WalletToken from '../wallet-token/WalletToken'; import WalletTokensSearch from '../wallet-tokens-search/WalletTokensSearch'; diff --git a/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPickerCheckbox.tsx b/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPickerCheckbox.tsx index 50bf2226a6..792b48e7d1 100644 --- a/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPickerCheckbox.tsx +++ b/source/renderer/app/components/wallet/tokens/wallet-token-picker/WalletTokenPickerCheckbox.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React, { useCallback } from 'react'; import { injectIntl } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; +import { PopOver } from '@react-polymorph/components/PopOver'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; import { messages } from './WalletTokenPicker.messages'; import { MAX_TOKENS } from './const'; import type { ItemProps as Props } from './types'; diff --git a/source/renderer/app/components/wallet/tokens/wallet-token/WalletToken.spec.tsx b/source/renderer/app/components/wallet/tokens/wallet-token/WalletToken.spec.tsx deleted file mode 100644 index e432d10600..0000000000 --- a/source/renderer/app/components/wallet/tokens/wallet-token/WalletToken.spec.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import '@testing-library/jest-dom'; - -import React from 'react'; -import noop from 'lodash/noop'; -import { cleanup, screen, waitFor } from '@testing-library/react'; - -import createTestBed from 'tests/_utils/TestBed'; -import { - withDecimalPlacesToken, - zeroDecimalPlacesToken, -} from 'tests/mocks/asset'; - -import WalletToken from './WalletToken'; - -const defaultWalletProps = { - isFavorite: false, - isInsertingAsset: false, - isLoading: false, - isRemovingAsset: false, - anyAssetWasHovered: false, - assetSettingsDialogWasOpened: false, - onAssetSettings: noop, - onCopyAssetParam: noop, - onOpenAssetSend: noop, - onToggleFavorite: noop, -}; - -describe('WalletToken', () => { - afterEach(() => cleanup()); - - it('should not show a warning when an asset is set to zero recommended decimal places', async () => { - createTestBed( - - ); - await waitFor(() => screen.getByText(zeroDecimalPlacesToken.policyId)); - expect(screen.queryByTestId('warning-icon')).not.toBeInTheDocument(); - }); - - it('should show a warning when an asset is not set to the recommended decimal places', async () => { - createTestBed( - - ); - await waitFor(() => screen.getByText(withDecimalPlacesToken.policyId)); - expect(screen.queryAllByTestId('warning-icon').length).toEqual(2); - }); -}); diff --git a/source/renderer/app/components/wallet/tokens/wallet-token/WalletTokenFooter.tsx b/source/renderer/app/components/wallet/tokens/wallet-token/WalletTokenFooter.tsx index 47fbe8598e..b82b0f39cd 100644 --- a/source/renderer/app/components/wallet/tokens/wallet-token/WalletTokenFooter.tsx +++ b/source/renderer/app/components/wallet/tokens/wallet-token/WalletTokenFooter.tsx @@ -2,8 +2,8 @@ import React from 'react'; import classNames from 'classnames'; import { observer } from 'mobx-react'; import { intlShape, injectIntl } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { Button } from '@react-polymorph/components/Button'; +import { PopOver } from '@react-polymorph/components/PopOver'; import SVGInline from 'react-svg-inline'; import styles from './WalletTokenFooter.scss'; import AssetAmount from '../../../assets/AssetAmount'; diff --git a/source/renderer/app/components/wallet/tokens/wallet-tokens-list/WalletTokensList.tsx b/source/renderer/app/components/wallet/tokens/wallet-tokens-list/WalletTokensList.tsx index 3f5fe22ec3..158a9b70cc 100644 --- a/source/renderer/app/components/wallet/tokens/wallet-tokens-list/WalletTokensList.tsx +++ b/source/renderer/app/components/wallet/tokens/wallet-tokens-list/WalletTokensList.tsx @@ -1,6 +1,6 @@ import React, { useState, useMemo, useCallback } from 'react'; import { intlShape, injectIntl } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; +import { Button } from '@react-polymorph/components/Button'; import classnames from 'classnames'; import { observer } from 'mobx-react'; import SVGInline from 'react-svg-inline'; diff --git a/source/renderer/app/components/wallet/tokens/wallet-tokens-search/WalletTokensSearch.tsx b/source/renderer/app/components/wallet/tokens/wallet-tokens-search/WalletTokensSearch.tsx index 709b01c5cc..2f6b5ea64e 100644 --- a/source/renderer/app/components/wallet/tokens/wallet-tokens-search/WalletTokensSearch.tsx +++ b/source/renderer/app/components/wallet/tokens/wallet-tokens-search/WalletTokensSearch.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React, { useState } from 'react'; import { intlShape, injectIntl, defineMessages } from 'react-intl'; import classNames from 'classnames'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Input } from '@react-polymorph/components/Input'; import SVGInline from 'react-svg-inline'; import { observer } from 'mobx-react'; import styles from './WalletTokensSearch.scss'; diff --git a/source/renderer/app/components/wallet/transactions/CancelTransactionButton.tsx b/source/renderer/app/components/wallet/transactions/CancelTransactionButton.tsx index 0ffc389120..5187ee3aab 100644 --- a/source/renderer/app/components/wallet/transactions/CancelTransactionButton.tsx +++ b/source/renderer/app/components/wallet/transactions/CancelTransactionButton.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { defineMessages, intlShape } from 'react-intl'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import styles from './CancelTransactionButton.scss'; const messages = defineMessages({ diff --git a/source/renderer/app/components/wallet/transactions/CancelTransactionConfirmationDialog.tsx b/source/renderer/app/components/wallet/transactions/CancelTransactionConfirmationDialog.tsx index bb729b04f1..19a0bad0a4 100644 --- a/source/renderer/app/components/wallet/transactions/CancelTransactionConfirmationDialog.tsx +++ b/source/renderer/app/components/wallet/transactions/CancelTransactionConfirmationDialog.tsx @@ -45,7 +45,6 @@ type Props = { onCancel: (...args: Array) => any; }; -@observer class CancelTransactionConfirmationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -92,4 +91,4 @@ class CancelTransactionConfirmationDialog extends Component { } } -export default CancelTransactionConfirmationDialog; +export default observer(CancelTransactionConfirmationDialog); diff --git a/source/renderer/app/components/wallet/transactions/FilterButton.tsx b/source/renderer/app/components/wallet/transactions/FilterButton.tsx index 4dc66489fc..0da07511a1 100644 --- a/source/renderer/app/components/wallet/transactions/FilterButton.tsx +++ b/source/renderer/app/components/wallet/transactions/FilterButton.tsx @@ -15,7 +15,6 @@ type Props = { onClick?: (...args: Array) => any; }; -@observer class FilterButton extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -52,4 +51,4 @@ class FilterButton extends Component { } } -export default FilterButton; +export default observer(FilterButton); diff --git a/source/renderer/app/components/wallet/transactions/FilterDialog.tsx b/source/renderer/app/components/wallet/transactions/FilterDialog.tsx index 6b133e9f93..15207bfee3 100644 --- a/source/renderer/app/components/wallet/transactions/FilterDialog.tsx +++ b/source/renderer/app/components/wallet/transactions/FilterDialog.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck /* eslint-disable jsx-a11y/label-has-associated-control, jsx-a11y/label-has-for */ import React, { Component, createRef } from 'react'; // @ts-ignore ts-migrate(2724) FIXME: '"react"' has no exported member named 'Element'. ... Remove this comment to see the full error message @@ -6,7 +7,7 @@ import { observer } from 'mobx-react'; import moment from 'moment'; import { isEqual, pick } from 'lodash'; import { defineMessages, intlShape } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import classNames from 'classnames'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; import { @@ -126,7 +127,6 @@ interface FormFields { toAmount: string; } -@observer class FilterDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -585,4 +585,6 @@ class FilterDialog extends Component { } } -export default withDiscreetMode>(FilterDialog); +export default withDiscreetMode>( + observer(FilterDialog) +); diff --git a/source/renderer/app/components/wallet/transactions/FilterResultInfo.tsx b/source/renderer/app/components/wallet/transactions/FilterResultInfo.tsx index 8b2e154ef6..191238b962 100644 --- a/source/renderer/app/components/wallet/transactions/FilterResultInfo.tsx +++ b/source/renderer/app/components/wallet/transactions/FilterResultInfo.tsx @@ -16,7 +16,6 @@ type Props = { total: number; }; -@observer class FilterResultInfo extends Component { render() { const { filtered, total } = this.props; @@ -35,4 +34,4 @@ class FilterResultInfo extends Component { } } -export default FilterResultInfo; +export default observer(FilterResultInfo); diff --git a/source/renderer/app/components/wallet/transactions/Transaction.tsx b/source/renderer/app/components/wallet/transactions/Transaction.tsx index c7f1d86fd8..d02edaf9a7 100644 --- a/source/renderer/app/components/wallet/transactions/Transaction.tsx +++ b/source/renderer/app/components/wallet/transactions/Transaction.tsx @@ -1,11 +1,12 @@ +// @ts-nocheck import React, { Component, Fragment } from 'react'; import { defineMessages, intlShape } from 'react-intl'; import moment from 'moment'; import { includes, get } from 'lodash'; import SVGInline from 'react-svg-inline'; import classNames from 'classnames'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import CancelTransactionButton from './CancelTransactionButton'; import { TransactionMetadataView } from './metadata/TransactionMetadataView'; import styles from './Transaction.scss'; diff --git a/source/renderer/app/components/wallet/transactions/TransactionTypeIcon.tsx b/source/renderer/app/components/wallet/transactions/TransactionTypeIcon.tsx index ed9543ffe7..ace863f6b4 100644 --- a/source/renderer/app/components/wallet/transactions/TransactionTypeIcon.tsx +++ b/source/renderer/app/components/wallet/transactions/TransactionTypeIcon.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import classNames from 'classnames'; import SVGInline from 'react-svg-inline'; -import { LoadingSpinner } from 'react-polymorph/lib/components/LoadingSpinner'; -import { LoadingSpinnerSkin } from 'react-polymorph/lib/skins/simple/LoadingSpinnerSkin'; +import { LoadingSpinner } from '@react-polymorph/components/LoadingSpinner'; +import { LoadingSpinnerSkin } from '@react-polymorph/skins/simple/LoadingSpinnerSkin'; import styles from './TransactionTypeIcon.scss'; import spinnerOverrides from './SpinnerOverrides.scss'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/wallet-... Remove this comment to see the full error message diff --git a/source/renderer/app/components/wallet/transactions/WalletNoTransactions.tsx b/source/renderer/app/components/wallet/transactions/WalletNoTransactions.tsx index a50f6ed8af..b737865e03 100644 --- a/source/renderer/app/components/wallet/transactions/WalletNoTransactions.tsx +++ b/source/renderer/app/components/wallet/transactions/WalletNoTransactions.tsx @@ -6,7 +6,6 @@ type Props = { label: string; }; -@observer class WalletNoTransactions extends Component { render() { const { label } = this.props; @@ -18,4 +17,4 @@ class WalletNoTransactions extends Component { } } -export default WalletNoTransactions; +export default observer(WalletNoTransactions); diff --git a/source/renderer/app/components/wallet/transactions/WalletTransactions.tsx b/source/renderer/app/components/wallet/transactions/WalletTransactions.tsx index bf7ea07ac7..24408738f9 100644 --- a/source/renderer/app/components/wallet/transactions/WalletTransactions.tsx +++ b/source/renderer/app/components/wallet/transactions/WalletTransactions.tsx @@ -50,7 +50,6 @@ type State = { isScrolling: boolean; }; -@observer class WalletTransactions extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -100,9 +99,8 @@ class WalletTransactions extends Component { if (!filterOptions || !activeWallet) return null; let walletTransactions = null; // const { searchLimit } = filterOptions; - const numberOfFilterDimensionsApplied = getNumberOfFilterDimensionsApplied( - filterOptions - ); + const numberOfFilterDimensionsApplied = + getNumberOfFilterDimensionsApplied(filterOptions); const noTransactionsLabel = intl.formatMessage(messages.noTransactions); const isRestoreActive = activeWallet && activeWallet.isRestoring; const isFilterDisabled = @@ -168,4 +166,4 @@ class WalletTransactions extends Component { } } -export default WalletTransactions; +export default observer(WalletTransactions); diff --git a/source/renderer/app/components/wallet/transactions/WalletTransactionsHeader.tsx b/source/renderer/app/components/wallet/transactions/WalletTransactionsHeader.tsx index 0cfdbb4935..59f6f74f6f 100644 --- a/source/renderer/app/components/wallet/transactions/WalletTransactionsHeader.tsx +++ b/source/renderer/app/components/wallet/transactions/WalletTransactionsHeader.tsx @@ -32,7 +32,6 @@ type Props = { onRequestCSVFile: (...args: Array) => any; }; -@observer class WalletTransactionsHeader extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -100,4 +99,4 @@ class WalletTransactionsHeader extends Component { } } -export default WalletTransactionsHeader; +export default observer(WalletTransactionsHeader); diff --git a/source/renderer/app/components/wallet/transactions/WalletTransactionsList.tsx b/source/renderer/app/components/wallet/transactions/WalletTransactionsList.tsx index c9c2862988..174d01df6d 100644 --- a/source/renderer/app/components/wallet/transactions/WalletTransactionsList.tsx +++ b/source/renderer/app/components/wallet/transactions/WalletTransactionsList.tsx @@ -3,8 +3,8 @@ import React, { Component } from 'react'; import type { Node } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; import { defineMessages, intlShape } from 'react-intl'; import moment from 'moment'; import styles from './WalletTransactionsList.scss'; @@ -45,11 +45,10 @@ const messages = defineMessages({ export type ScrollContextType = { setIsScrolling: (...args: Array) => any; }; -export const WalletTransactionsListScrollContext = React.createContext< - ScrollContextType ->({ - setIsScrolling: () => null, -}); +export const WalletTransactionsListScrollContext = + React.createContext({ + setIsScrolling: () => null, + }); type Props = { deletePendingTransaction: (...args: Array) => any; formattedWalletAmount: (...args: Array) => any; @@ -77,7 +76,6 @@ type State = { }; const DATE_FORMAT = 'YYYY-MM-DD'; -@observer class WalletTransactionsList extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -378,4 +376,4 @@ class WalletTransactionsList extends Component { } } -export default WalletTransactionsList; +export default observer(WalletTransactionsList); diff --git a/source/renderer/app/components/wallet/transactions/WalletTransactionsSearch.tsx b/source/renderer/app/components/wallet/transactions/WalletTransactionsSearch.tsx index 6be7f1b23f..eab5f49488 100644 --- a/source/renderer/app/components/wallet/transactions/WalletTransactionsSearch.tsx +++ b/source/renderer/app/components/wallet/transactions/WalletTransactionsSearch.tsx @@ -1,8 +1,9 @@ +// @ts-nocheck import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import styles from './WalletTransactionsSearch.scss'; const messages = defineMessages({ @@ -17,7 +18,6 @@ type Props = { onChange: (...args: Array) => any; }; -@observer class WalletTransactionsSearch extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -40,4 +40,4 @@ class WalletTransactionsSearch extends Component { } } -export default WalletTransactionsSearch; +export default observer(WalletTransactionsSearch); diff --git a/source/renderer/app/components/wallet/transactions/render-strategies/SimpleTransactionList.tsx b/source/renderer/app/components/wallet/transactions/render-strategies/SimpleTransactionList.tsx index eba55b8972..084e306a15 100644 --- a/source/renderer/app/components/wallet/transactions/render-strategies/SimpleTransactionList.tsx +++ b/source/renderer/app/components/wallet/transactions/render-strategies/SimpleTransactionList.tsx @@ -12,46 +12,49 @@ type Props = { rows: Row[]; }; -@observer -class SimpleTransactionList extends Component { - static defaultProps = { - onOpenExternalLink: () => {}, - }; - onListScroll = ( - context: ScrollContextType, - evt: React.SyntheticEvent - ) => { - const { scrollTop } = evt.currentTarget; +const SimpleTransactionList = observer( + class SimpleTransactionList extends Component { + static defaultProps = { + onOpenExternalLink: () => {}, + }; + onListScroll = ( + context: ScrollContextType, + evt: React.SyntheticEvent + ) => { + const { scrollTop } = evt.currentTarget; - if (scrollTop > 10) { - context.setIsScrolling(true); - } else { - context.setIsScrolling(false); - } - }; + if (scrollTop > 10) { + context.setIsScrolling(true); + } else { + context.setIsScrolling(false); + } + }; - render() { - const { rows, renderRow } = this.props; - return ( - - {(context) => ( -
    this.onListScroll(context, evt)} - > - {rows.map(( - row, - index // eslint-disable-next-line react/no-array-index-key - ) => ( -
    - {renderRow(row)} -
    - ))} -
    - )} -
    - ); + render() { + const { rows, renderRow } = this.props; + return ( + + {(context) => ( +
    this.onListScroll(context, evt)} + > + {rows.map( + ( + row, + index // eslint-disable-next-line react/no-array-index-key + ) => ( +
    + {renderRow(row)} +
    + ) + )} +
    + )} +
    + ); + } } -} +); export { SimpleTransactionList }; diff --git a/source/renderer/app/components/wallet/transactions/render-strategies/VirtualTransactionList.tsx b/source/renderer/app/components/wallet/transactions/render-strategies/VirtualTransactionList.tsx index 711aecf04e..6f05181a6c 100644 --- a/source/renderer/app/components/wallet/transactions/render-strategies/VirtualTransactionList.tsx +++ b/source/renderer/app/components/wallet/transactions/render-strategies/VirtualTransactionList.tsx @@ -28,337 +28,341 @@ const TX_BOTTOM_BORDER_MARGIN = 1; const TX_ADDRESS_SELECTOR = '.Transaction_address'; const TX_ID_SELECTOR = '.Transaction_transactionId'; -@observer -class VirtualTransactionList extends Component { - list: List; - rowHeights: RowHeight[] = []; - txAddressHeight = 0; - txIdHeight = 0; - overscanStartIndex: number; - overscanStopIndex: number; - static defaultProps = { - isLoadingSpinnerShown: false, - isSyncingSpinnerShown: false, - }; - - componentDidMount() { - window.addEventListener('resize', this.onResize); - } - - componentWillUnmount() { - window.removeEventListener('resize', this.onResize); - } - - /** - * Returns the row index of a given tx. - */ - findIndexForTx = (tx: WalletTransaction): number => - this.props.rows.findIndex( - (r) => r instanceof TransactionInfo && r.tx.id === tx.id - ); +const VirtualTransactionList = observer( + class VirtualTransactionList extends Component { + list: List; + rowHeights: RowHeight[] = []; + txAddressHeight = 0; + txIdHeight = 0; + overscanStartIndex: number; + overscanStopIndex: number; + static defaultProps = { + isLoadingSpinnerShown: false, + isSyncingSpinnerShown: false, + }; - /** - * Recomputes virtual row heights only once per tick (debounced) - */ - recomputeVirtualRowHeights = debounce((startIndex = 0): void => { - const { list } = this; - if (!list) return; - list.recomputeRowHeights(startIndex); - }); - - /** - * Calculates the number of lines of the addresses and id from the first expanded tx - */ - updateAddressesAndIdHeights = (): void => { - const firstTxAddress = document.querySelector(TX_ADDRESS_SELECTOR); - const firstTxId = document.querySelector(TX_ID_SELECTOR); - - if ( - firstTxAddress instanceof HTMLElement && - firstTxId instanceof HTMLElement - ) { - this.txAddressHeight = firstTxAddress.offsetHeight; - this.txIdHeight = firstTxId.offsetHeight; + componentDidMount() { + window.addEventListener('resize', this.onResize); } - }; - - /** - * Gets an Info expanded row height - */ - estimateHeightOfTxExpandedRow = (row: Row, tx: WalletTransaction): number => { - if (!this.txAddressHeight) this.updateAddressesAndIdHeights(); - const txSingleAddressHeight = this.txAddressHeight; - const txIdHeightValue = this.txIdHeight; - const { addresses } = tx; - const txAddressesCount = addresses.from.length + addresses.to.length; - const txAddressesHeight = txAddressesCount * txSingleAddressHeight; - // @ts-ignore ts-migrate(2339) FIXME: Property 'isLastInGroup' does not exist on type 'R... Remove this comment to see the full error message - const txBottomMargin = row.isLastInGroup ? TX_LAST_IN_GROUP_MARGIN : 1; - return ( - TX_EXPANDED_ROW_BASE_HEIGHT + - txAddressesHeight + - txIdHeightValue + - txBottomMargin - ); - }; - - /** - * Gets an Info contracted row height - */ - estimateHeightOfTxContractedRow = (row: Row): number => { - // @ts-ignore ts-migrate(2339) FIXME: Property 'isLastInGroup' does not exist on type 'R... Remove this comment to see the full error message - const txBottomMargin = row.isLastInGroup ? TX_LAST_IN_GROUP_MARGIN : 0; - return TX_CONTRACTED_ROW_HEIGHT + txBottomMargin; - }; - - /** - * Updates and recomputes row height - */ - updateTxRowHeight = ( - tx: WalletTransaction, - isExpanded: boolean, - wasToggled: boolean // Is "true" when transaction is manually expanded/collapsed - ): void => { - const { rowHeights } = this; - const txIndex = this.findIndexForTx(tx); - const row = this.props.rows[txIndex]; - if (row instanceof TransactionsGroup) return; - rowHeights[txIndex] = isExpanded - ? this.estimateHeightOfTxExpandedRow(row, tx) - : this.estimateHeightOfTxContractedRow(row); - this.recomputeVirtualRowHeights(); - - // In case transaction has just been manually expanded we need to schedule - // another row height calculation if the transaction still isn't fully - // expanded in the moment of the initial execution of this method - if (isExpanded && wasToggled) { - const isFullyExpanded = this.checkIfTxContentIsFullyExpanded(tx); - - if (isFullyExpanded) { - const estimatedHeight = rowHeights[txIndex]; - this.correctExpandedTxHeightEstimationErrors(tx, estimatedHeight); - } else { - setTimeout(this.updateTxRowHeight, 1, tx, true, true); - } + + componentWillUnmount() { + window.removeEventListener('resize', this.onResize); } - }; - /** - * Gets a row height based on its type - */ - estimateRowHeight = (row: Row): number => { - if (row instanceof TransactionInfo) { - const expandedTxMap = this.props.getExpandedTransactions(); + /** + * Returns the row index of a given tx. + */ + findIndexForTx = (tx: WalletTransaction): number => + this.props.rows.findIndex( + (r) => r instanceof TransactionInfo && r.tx.id === tx.id + ); + + /** + * Recomputes virtual row heights only once per tick (debounced) + */ + recomputeVirtualRowHeights = debounce((startIndex = 0): void => { + const { list } = this; + if (!list) return; + list.recomputeRowHeights(startIndex); + }); - if (expandedTxMap.has(row.tx.id)) { - return this.estimateHeightOfTxExpandedRow(row, row.tx); + /** + * Calculates the number of lines of the addresses and id from the first expanded tx + */ + updateAddressesAndIdHeights = (): void => { + const firstTxAddress = document.querySelector(TX_ADDRESS_SELECTOR); + const firstTxId = document.querySelector(TX_ID_SELECTOR); + + if ( + firstTxAddress instanceof HTMLElement && + firstTxId instanceof HTMLElement + ) { + this.txAddressHeight = firstTxAddress.offsetHeight; + this.txIdHeight = firstTxId.offsetHeight; + } + }; + + /** + * Gets an Info expanded row height + */ + estimateHeightOfTxExpandedRow = ( + row: Row, + tx: WalletTransaction + ): number => { + if (!this.txAddressHeight) this.updateAddressesAndIdHeights(); + const txSingleAddressHeight = this.txAddressHeight; + const txIdHeightValue = this.txIdHeight; + const { addresses } = tx; + const txAddressesCount = addresses.from.length + addresses.to.length; + const txAddressesHeight = txAddressesCount * txSingleAddressHeight; + // @ts-ignore ts-migrate(2339) FIXME: Property 'isLastInGroup' does not exist on type 'R... Remove this comment to see the full error message + const txBottomMargin = row.isLastInGroup ? TX_LAST_IN_GROUP_MARGIN : 1; + return ( + TX_EXPANDED_ROW_BASE_HEIGHT + + txAddressesHeight + + txIdHeightValue + + txBottomMargin + ); + }; + + /** + * Gets an Info contracted row height + */ + estimateHeightOfTxContractedRow = (row: Row): number => { + // @ts-ignore ts-migrate(2339) FIXME: Property 'isLastInGroup' does not exist on type 'R... Remove this comment to see the full error message + const txBottomMargin = row.isLastInGroup ? TX_LAST_IN_GROUP_MARGIN : 0; + return TX_CONTRACTED_ROW_HEIGHT + txBottomMargin; + }; + + /** + * Updates and recomputes row height + */ + updateTxRowHeight = ( + tx: WalletTransaction, + isExpanded: boolean, + wasToggled: boolean // Is "true" when transaction is manually expanded/collapsed + ): void => { + const { rowHeights } = this; + const txIndex = this.findIndexForTx(tx); + const row = this.props.rows[txIndex]; + if (row instanceof TransactionsGroup) return; + rowHeights[txIndex] = isExpanded + ? this.estimateHeightOfTxExpandedRow(row, tx) + : this.estimateHeightOfTxContractedRow(row); + this.recomputeVirtualRowHeights(); + + // In case transaction has just been manually expanded we need to schedule + // another row height calculation if the transaction still isn't fully + // expanded in the moment of the initial execution of this method + if (isExpanded && wasToggled) { + const isFullyExpanded = this.checkIfTxContentIsFullyExpanded(tx); + + if (isFullyExpanded) { + const estimatedHeight = rowHeights[txIndex]; + this.correctExpandedTxHeightEstimationErrors(tx, estimatedHeight); + } else { + setTimeout(this.updateTxRowHeight, 1, tx, true, true); + } } + }; - return this.estimateHeightOfTxContractedRow(row); - } + /** + * Gets a row height based on its type + */ + estimateRowHeight = (row: Row): number => { + if (row instanceof TransactionInfo) { + const expandedTxMap = this.props.getExpandedTransactions(); - return GROUP_DATE_HEIGHT; - }; - - /** - * Maps over all rows and returns array of calculated heights. - */ - estimateRowHeights = (rows: Row[]): RowHeight[] => - rows.map(this.estimateRowHeight); - - /** - * Returns the DOM element for given transaction id - */ - getTxRowElementById = (id: string) => document.getElementById(`tx-${id}`); - - /** - * Measures the exact height of a rendered tx content DOM element. - */ - measureTxContentHeight = ( - tx: WalletTransaction - ): number | null | undefined => { - const txRow = this.getTxRowElementById(tx.id); - - if (txRow) { - const txElement = txRow.firstChild; - // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'ChildNode' is not assignable to ... Remove this comment to see the full error message - const style = window.getComputedStyle(txElement, null); - return parseInt(style.getPropertyValue('height'), 10); - } + if (expandedTxMap.has(row.tx.id)) { + return this.estimateHeightOfTxExpandedRow(row, row.tx); + } - return null; - }; - - /** - * Checks if rendered tx content DOM element has been fully expanded. - */ - checkIfTxContentIsFullyExpanded = (tx: WalletTransaction): boolean => { - const txRow = this.getTxRowElementById(tx.id); - const txElement = txRow && txRow.firstChild; - return ( - txElement instanceof HTMLElement && - txElement.classList.contains('Transaction_expanded') - ); - }; - - /** - * Corrects potential estimation errors that can happen due to various reasons. - */ - correctExpandedTxHeightEstimationErrors = ( - tx: WalletTransaction, - estimatedHeight: number - ) => { - const txContentHeight = this.measureTxContentHeight(tx); - if (!txContentHeight) return; - const { rows } = this.props; - const txIndex = this.findIndexForTx(tx); - const txInfo = rows[txIndex]; - - if (txInfo instanceof TransactionInfo) { - const margin = txInfo.isLastInGroup - ? TX_LAST_IN_GROUP_MARGIN - : TX_BOTTOM_BORDER_MARGIN; - const requiredHeight = txContentHeight + margin; - const estimationError = Math.abs(estimatedHeight - requiredHeight); - - if (estimationError > 1) { - this.rowHeights[txIndex] = requiredHeight; - this.recomputeVirtualRowHeights(); + return this.estimateHeightOfTxContractedRow(row); } - } - }; - updateVisibleExpandedTxRowHeights = () => { - const expandedTxMap = this.props.getExpandedTransactions(); - // This is needed because a spread Map results in an array of [key, value] - const expandedTxArray = [...expandedTxMap].map((mapValue) => mapValue[1]); - const visibleExpandedTx = expandedTxArray.filter((tx) => { - const index = this.findIndexForTx(tx); + + return GROUP_DATE_HEIGHT; + }; + + /** + * Maps over all rows and returns array of calculated heights. + */ + estimateRowHeights = (rows: Row[]): RowHeight[] => + rows.map(this.estimateRowHeight); + + /** + * Returns the DOM element for given transaction id + */ + getTxRowElementById = (id: string) => document.getElementById(`tx-${id}`); + + /** + * Measures the exact height of a rendered tx content DOM element. + */ + measureTxContentHeight = ( + tx: WalletTransaction + ): number | null | undefined => { + const txRow = this.getTxRowElementById(tx.id); + + if (txRow) { + const txElement = txRow.firstChild; + // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'ChildNode' is not assignable to ... Remove this comment to see the full error message + const style = window.getComputedStyle(txElement, null); + return parseInt(style.getPropertyValue('height'), 10); + } + + return null; + }; + + /** + * Checks if rendered tx content DOM element has been fully expanded. + */ + checkIfTxContentIsFullyExpanded = (tx: WalletTransaction): boolean => { + const txRow = this.getTxRowElementById(tx.id); + const txElement = txRow && txRow.firstChild; return ( - index >= this.overscanStartIndex && index <= this.overscanStopIndex + txElement instanceof HTMLElement && + txElement.classList.contains('Transaction_expanded') ); - }); - visibleExpandedTx.forEach((tx) => { - this.updateTxRowHeight(tx, true, false); - const estimatedHeight = this.rowHeights[this.findIndexForTx(tx)]; - this.correctExpandedTxHeightEstimationErrors(tx, estimatedHeight); - }); - }; - - /** - * Since the transaction addresses are pretty long, they break into the next line on smaller - * window sizes and the height of expanded tx rows in the list must be adjusted accordingly. - */ - onResize = (): void => { - // First load, calculates all the rows heights - if (!this.rowHeights.length) { - this.rowHeights = this.estimateRowHeights(this.props.rows); - return; - } + }; + + /** + * Corrects potential estimation errors that can happen due to various reasons. + */ + correctExpandedTxHeightEstimationErrors = ( + tx: WalletTransaction, + estimatedHeight: number + ) => { + const txContentHeight = this.measureTxContentHeight(tx); + if (!txContentHeight) return; + const { rows } = this.props; + const txIndex = this.findIndexForTx(tx); + const txInfo = rows[txIndex]; + + if (txInfo instanceof TransactionInfo) { + const margin = txInfo.isLastInGroup + ? TX_LAST_IN_GROUP_MARGIN + : TX_BOTTOM_BORDER_MARGIN; + const requiredHeight = txContentHeight + margin; + const estimationError = Math.abs(estimatedHeight - requiredHeight); + + if (estimationError > 1) { + this.rowHeights[txIndex] = requiredHeight; + this.recomputeVirtualRowHeights(); + } + } + }; + updateVisibleExpandedTxRowHeights = () => { + const expandedTxMap = this.props.getExpandedTransactions(); + // This is needed because a spread Map results in an array of [key, value] + const expandedTxArray = [...expandedTxMap].map((mapValue) => mapValue[1]); + const visibleExpandedTx = expandedTxArray.filter((tx) => { + const index = this.findIndexForTx(tx); + return ( + index >= this.overscanStartIndex && index <= this.overscanStopIndex + ); + }); + visibleExpandedTx.forEach((tx) => { + this.updateTxRowHeight(tx, true, false); + const estimatedHeight = this.rowHeights[this.findIndexForTx(tx)]; + this.correctExpandedTxHeightEstimationErrors(tx, estimatedHeight); + }); + }; + + /** + * Since the transaction addresses are pretty long, they break into the next line on smaller + * window sizes and the height of expanded tx rows in the list must be adjusted accordingly. + */ + onResize = (): void => { + // First load, calculates all the rows heights + if (!this.rowHeights.length) { + this.rowHeights = this.estimateRowHeights(this.props.rows); + return; + } - // Subsequently resizes, updates the expanded rows heights if there is any expanded one - const expandedTransactions = this.props.getExpandedTransactions(); - if (!expandedTransactions.size) return; - this.updateAddressesAndIdHeights(); - this.updateVisibleExpandedTxRowHeights(); - }; - - /** - * Callback that gets invoked when virtual rows are rendered. - * Used to update the array of visible expanded transactions and remeasure their height. - */ - onRowsRendered = ({ - overscanStartIndex, - overscanStopIndex, - }: { - overscanStartIndex: number; - overscanStopIndex: number; - }) => { - this.overscanStartIndex = overscanStartIndex; - this.overscanStopIndex = overscanStopIndex; - this.updateVisibleExpandedTxRowHeights(); - }; - rowRenderer = ({ - key, - // Unique key within array of rows - index, - // Index of row within collection - style, // Style object to be applied to row (to position it) - }: { - key: string; - index: number; - style: string; - }) => ( - // @ts-ignore ts-migrate(2559) FIXME: Type 'string' has no properties in common with typ... Remove this comment to see the full error message -
    - {this.props.renderRow(this.props.rows[index])} -
    - ); - onListScroll = ( - context: ScrollContextType, - { - scrollTop, + // Subsequently resizes, updates the expanded rows heights if there is any expanded one + const expandedTransactions = this.props.getExpandedTransactions(); + if (!expandedTransactions.size) return; + this.updateAddressesAndIdHeights(); + this.updateVisibleExpandedTxRowHeights(); + }; + + /** + * Callback that gets invoked when virtual rows are rendered. + * Used to update the array of visible expanded transactions and remeasure their height. + */ + onRowsRendered = ({ + overscanStartIndex, + overscanStopIndex, }: { - scrollTop: number; - } - ) => { - context.setIsScrolling(scrollTop > 10); - }; - - // =============== REACT LIFECYCLE ================= // - render() { - const { rows, isLoadingSpinnerShown, isSyncingSpinnerShown } = this.props; - // Prevent List rendering if we have no rows to render - if (!rows.length) return false; - - if (rows.length !== this.rowHeights.length) { - this.rowHeights = this.estimateRowHeights(rows); + overscanStartIndex: number; + overscanStopIndex: number; + }) => { + this.overscanStartIndex = overscanStartIndex; + this.overscanStopIndex = overscanStopIndex; this.updateVisibleExpandedTxRowHeights(); - } - - const componentStyles = classNames([ - styles.component, - isLoadingSpinnerShown ? styles.withLoadingSpinner : null, - isSyncingSpinnerShown ? styles.withSyncingSpinner : null, - ]); - return ( - - {(context) => ( -
    - - {({ width, height }) => ( - { - this.list = list; - }} - width={width} - height={height} - onRowsRendered={throttle(this.onRowsRendered, 100, { - leading: true, - trailing: true, - })} - rowCount={rows.length} - rowHeight={({ index }) => - this.rowHeights[index] || TX_CONTRACTED_ROW_HEIGHT - } - rowRenderer={this.rowRenderer} - style={{ - overflowY: 'scroll', - }} - onScroll={(param) => this.onListScroll(context, param)} - /> - )} - -
    - )} -
    + }; + rowRenderer = ({ + key, + // Unique key within array of rows + index, + // Index of row within collection + style, // Style object to be applied to row (to position it) + }: { + key: string; + index: number; + style: string; + }) => ( + // @ts-ignore ts-migrate(2559) FIXME: Type 'string' has no properties in common with typ... Remove this comment to see the full error message +
    + {this.props.renderRow(this.props.rows[index])} +
    ); + onListScroll = ( + context: ScrollContextType, + { + scrollTop, + }: { + scrollTop: number; + } + ) => { + context.setIsScrolling(scrollTop > 10); + }; + + // =============== REACT LIFECYCLE ================= // + render() { + const { rows, isLoadingSpinnerShown, isSyncingSpinnerShown } = this.props; + // Prevent List rendering if we have no rows to render + if (!rows.length) return false; + + if (rows.length !== this.rowHeights.length) { + this.rowHeights = this.estimateRowHeights(rows); + this.updateVisibleExpandedTxRowHeights(); + } + + const componentStyles = classNames([ + styles.component, + isLoadingSpinnerShown ? styles.withLoadingSpinner : null, + isSyncingSpinnerShown ? styles.withSyncingSpinner : null, + ]); + return ( + + {(context) => ( +
    + + {({ width, height }) => ( + { + this.list = list; + }} + width={width} + height={height} + onRowsRendered={throttle(this.onRowsRendered, 100, { + leading: true, + trailing: true, + })} + rowCount={rows.length} + rowHeight={({ index }) => + this.rowHeights[index] || TX_CONTRACTED_ROW_HEIGHT + } + rowRenderer={this.rowRenderer} + style={{ + overflowY: 'scroll', + }} + onScroll={(param) => this.onListScroll(context, param)} + /> + )} + +
    + )} +
    + ); + } } -} +); export { VirtualTransactionList }; diff --git a/source/renderer/app/components/wallet/transfer-funds/TransferFundsStep2Dialog.tsx b/source/renderer/app/components/wallet/transfer-funds/TransferFundsStep2Dialog.tsx index cf3428f7d1..7ab1128b31 100644 --- a/source/renderer/app/components/wallet/transfer-funds/TransferFundsStep2Dialog.tsx +++ b/source/renderer/app/components/wallet/transfer-funds/TransferFundsStep2Dialog.tsx @@ -1,13 +1,15 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; import BigNumber from 'bignumber.js'; import { defineMessages, intlShape, FormattedMessage } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import DialogBackButton from '../../widgets/DialogBackButton'; import Dialog from '../../widgets/Dialog'; @@ -97,7 +99,6 @@ interface FormFields { spendingPassword: string; } -@observer class TransferFundsStep2Dialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -269,4 +270,4 @@ class TransferFundsStep2Dialog extends Component { } } -export default TransferFundsStep2Dialog; +export default observer(TransferFundsStep2Dialog); diff --git a/source/renderer/app/components/wallet/utxo/WalletUtxo.tsx b/source/renderer/app/components/wallet/utxo/WalletUtxo.tsx index f4da83bf68..6dfec1ca51 100644 --- a/source/renderer/app/components/wallet/utxo/WalletUtxo.tsx +++ b/source/renderer/app/components/wallet/utxo/WalletUtxo.tsx @@ -12,8 +12,8 @@ import { Bar, ResponsiveContainer, } from 'recharts'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import BorderedBox from '../../widgets/BorderedBox'; import LoadingSpinner from '../../widgets/LoadingSpinner'; import Tick from './WalletUtxoTick'; diff --git a/source/renderer/app/components/wallet/utxo/WalletUtxoCursor.tsx b/source/renderer/app/components/wallet/utxo/WalletUtxoCursor.tsx index 12ee7e6556..51a51c559a 100644 --- a/source/renderer/app/components/wallet/utxo/WalletUtxoCursor.tsx +++ b/source/renderer/app/components/wallet/utxo/WalletUtxoCursor.tsx @@ -11,7 +11,6 @@ type CursorProps = { const OFFSET_TOP = 20; const OFFSET_BOTTOM = 60; -@observer class WalletUtxoCursor extends Component { render() { let { x, width, height } = this.props; @@ -37,4 +36,4 @@ class WalletUtxoCursor extends Component { } } -export default WalletUtxoCursor; +export default observer(WalletUtxoCursor); diff --git a/source/renderer/app/components/wallet/utxo/WalletUtxoTick.tsx b/source/renderer/app/components/wallet/utxo/WalletUtxoTick.tsx index bdb7345626..766f7e73ec 100644 --- a/source/renderer/app/components/wallet/utxo/WalletUtxoTick.tsx +++ b/source/renderer/app/components/wallet/utxo/WalletUtxoTick.tsx @@ -13,7 +13,6 @@ export type TickProps = { vertical?: boolean; }; -@observer class WalletUtxoTick extends Component { render() { const { @@ -35,4 +34,4 @@ class WalletUtxoTick extends Component { } } -export default WalletUtxoTick; +export default observer(WalletUtxoTick); diff --git a/source/renderer/app/components/wallet/utxo/WalletUtxoTooltip.tsx b/source/renderer/app/components/wallet/utxo/WalletUtxoTooltip.tsx index a254d8aeb8..fe8ebf89fa 100644 --- a/source/renderer/app/components/wallet/utxo/WalletUtxoTooltip.tsx +++ b/source/renderer/app/components/wallet/utxo/WalletUtxoTooltip.tsx @@ -1,7 +1,6 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { get } from 'lodash'; import styles from './WalletUtxoTooltip.scss'; import { PRETTY_WALLET_AMOUNTS } from '../../../config/utxoConfig'; @@ -34,7 +33,6 @@ type Props = { }>; }; -@observer class WalletUtxoTooltip extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -48,7 +46,7 @@ class WalletUtxoTooltip extends Component { render() { const { label: walletAmount = '', payload } = this.props; - const { walletUtxosAmount } = get(payload, '[0].payload', {}); + const { walletUtxosAmount } = payload?.[0]?.payload || {}; const previousWalletAmount = this.getPreviousAmount(walletAmount); let message = messages.tooltip; if (!previousWalletAmount) message = messages.tooltipFirst; @@ -70,4 +68,4 @@ class WalletUtxoTooltip extends Component { } } -export default WalletUtxoTooltip; +export default observer(WalletUtxoTooltip); diff --git a/source/renderer/app/components/wallet/wallet-create/WalletCreateSteps.tsx b/source/renderer/app/components/wallet/wallet-create/WalletCreateSteps.tsx index 63e025e992..1291ff014c 100644 --- a/source/renderer/app/components/wallet/wallet-create/WalletCreateSteps.tsx +++ b/source/renderer/app/components/wallet/wallet-create/WalletCreateSteps.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { Stepper } from 'react-polymorph/lib/components/Stepper'; -import { StepperSkin } from 'react-polymorph/lib/skins/simple/StepperSkin'; +import { Stepper } from '@react-polymorph/components/Stepper'; +import { StepperSkin } from '@react-polymorph/skins/simple/StepperSkin'; import styles from './WalletCreateSteps.scss'; import { CREATE_WALLET_STEPS } from '../../../config/walletsConfig'; import type { RestoreWalletStep } from '../../../types/walletRestoreTypes'; diff --git a/source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.tsx b/source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.tsx index 2e3ca46a73..1d9c5438e9 100644 --- a/source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.tsx +++ b/source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.tsx @@ -1,25 +1,25 @@ +// @ts-nocheck import React, { Component } from 'react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import { observer } from 'mobx-react'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import SVGInline from 'react-svg-inline'; import classNames from 'classnames'; import { get } from 'lodash'; import styles from './WalletImportFileDialog.scss'; import RadioSet from '../../widgets/RadioSet'; + import DialogCloseButton from '../../widgets/DialogCloseButton'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/pen.inl... Remove this comment to see the full error message import penIcon from '../../../assets/images/pen.inline.svg'; import LoadingSpinner from '../../widgets/LoadingSpinner'; import { ImportFromOptions } from '../../../types/walletExportTypes'; import type { ImportFromOption } from '../../../types/walletExportTypes'; import Dialog from '../../widgets/Dialog'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/close-c... Remove this comment to see the full error message import closeCrossThin from '../../../assets/images/close-cross-thin.inline.svg'; const messages = defineMessages({ @@ -107,7 +107,6 @@ type State = { importFrom: ImportFromOption; }; -@observer class WalletImportFileDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -291,4 +290,4 @@ class WalletImportFileDialog extends Component { } } -export default WalletImportFileDialog; +export default observer(WalletImportFileDialog); diff --git a/source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.tsx b/source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.tsx index 2af9bdfa88..f9cb9f4693 100644 --- a/source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.tsx +++ b/source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck import React, { Component } from 'react'; import { defineMessages, @@ -6,24 +7,23 @@ import { FormattedMessage, } from 'react-intl'; import { observer } from 'mobx-react'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import classNames from 'classnames'; import SVGInline from 'react-svg-inline'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import styles from './WalletSelectImportDialog.scss'; + import DialogCloseButton from '../../widgets/DialogCloseButton'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/close-c... Remove this comment to see the full error message import closeCrossThin from '../../../assets/images/close-cross-thin.inline.svg'; import globalMessages from '../../../i18n/global-messages'; import { WalletImportStatuses } from '../../../types/walletExportTypes'; import LoadingSpinner from '../../widgets/LoadingSpinner'; import InlineEditingSmallInput from '../../widgets/forms/InlineEditingSmallInput'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/check-w... Remove this comment to see the full error message import checkmarkImage from '../../../assets/images/check-w.inline.svg'; import { MAX_ADA_WALLETS_COUNT } from '../../../config/numbersConfig'; import type { ExportedByronWallet } from '../../../types/walletExportTypes'; @@ -128,7 +128,6 @@ type Props = { isMaxNumberOfWalletsReached: boolean; }; -@observer class WalletSelectImportDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -186,8 +185,8 @@ class WalletSelectImportDialog extends Component { ); if (checkboxes[index] && topWrapper.length) { - const checkboxTopOffset = checkboxes[index].getBoundingClientRect() - .top; + const checkboxTopOffset = + checkboxes[index].getBoundingClientRect().top; const topWrapperTopOffset = topWrapper[0].getBoundingClientRect().top; const topPart = topWrapperTopOffset + 121; const spaceForTooltip = checkboxTopOffset - topPart; @@ -494,4 +493,4 @@ class WalletSelectImportDialog extends Component { } } -export default WalletSelectImportDialog; +export default observer(WalletSelectImportDialog); diff --git a/source/renderer/app/components/wallet/wallet-restore/ConfigurationDialog.tsx b/source/renderer/app/components/wallet/wallet-restore/ConfigurationDialog.tsx index 949e6d1dcf..1b8966a670 100644 --- a/source/renderer/app/components/wallet/wallet-restore/ConfigurationDialog.tsx +++ b/source/renderer/app/components/wallet/wallet-restore/ConfigurationDialog.tsx @@ -1,11 +1,13 @@ +// @ts-nocheck + import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Input } from '@react-polymorph/components/Input'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; import SVGInline from 'react-svg-inline'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { PasswordInput } from '../../widgets/forms/PasswordInput'; import WalletRestoreDialog from './widgets/WalletRestoreDialog'; import styles from './ConfigurationDialog.scss'; @@ -20,8 +22,8 @@ import { import { submitOnEnter } from '../../../utils/form'; import globalMessages from '../../../i18n/global-messages'; import LocalizableError from '../../../i18n/LocalizableError'; + import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../../config/timingConfig'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/info-ic... Remove this comment to see the full error message import infoIconInline from '../../../assets/images/info-icon.inline.svg'; import LoadingSpinner from '../../widgets/LoadingSpinner'; @@ -61,8 +63,7 @@ const messages = defineMessages({ 'Label for the "Repeat password" input in the wallet restore dialog.', }, passwordFieldsPlaceholder: { - id: - 'wallet.restore.dialog.step.configuration.input.passwordFields.placeholder', + id: 'wallet.restore.dialog.step.configuration.input.passwordFields.placeholder', defaultMessage: '!!!Password', description: 'Placeholder for the "Password" inputs in the wallet restore dialog.', @@ -98,7 +99,6 @@ interface FormFields { repeatPassword: string; } -@observer class ConfigurationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -307,4 +307,4 @@ class ConfigurationDialog extends Component { } } -export default ConfigurationDialog; +export default observer(ConfigurationDialog); diff --git a/source/renderer/app/components/wallet/wallet-restore/MnemonicsDialog.tsx b/source/renderer/app/components/wallet/wallet-restore/MnemonicsDialog.tsx index 90edcacfc8..d661246eac 100644 --- a/source/renderer/app/components/wallet/wallet-restore/MnemonicsDialog.tsx +++ b/source/renderer/app/components/wallet/wallet-restore/MnemonicsDialog.tsx @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import _ from 'lodash'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; import vjf from 'mobx-react-form/lib/validators/VJF'; @@ -23,8 +22,7 @@ const messages = defineMessages({ description: 'Placeholder for the mnemonics autocomplete.', }, autocompleteMultiLengthPhrase: { - id: - 'wallet.restore.dialog.step.mnemonics.autocomplete.multiLengthPhrase.placeholder', + id: 'wallet.restore.dialog.step.mnemonics.autocomplete.multiLengthPhrase.placeholder', defaultMessage: '!!!Enter your 12, 18 or 24-word recovery phrase', description: 'Placeholder for the multi-length mnemonics autocomplete.', }, @@ -40,8 +38,7 @@ const messages = defineMessages({ description: 'Label for the mnemonics Continue button.', }, invalidRecoveryPhrase: { - id: - 'wallet.restore.dialog.step.mnemonics.autocomplete.invalidRecoveryPhrase', + id: 'wallet.restore.dialog.step.mnemonics.autocomplete.invalidRecoveryPhrase', defaultMessage: '!!!Invalid recovery phrase', description: 'Label for invalid recovery phrase', }, @@ -65,7 +62,6 @@ interface FormFields { recoveryPhrase: string; } -@observer class MnemonicsDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -112,7 +108,7 @@ class MnemonicsDialog extends Component { !recoveryPhraseField.error && recoveryPhraseField.value.length === maxWordCount && recoveryPhraseField.value.every((word) => word); - const { reset, ...mnemonicInputProps } = recoveryPhraseField.bind(); + const { ...mnemonicInputProps } = recoveryPhraseField.bind(); return ( { } } -export default MnemonicsDialog; +export default observer(MnemonicsDialog); diff --git a/source/renderer/app/components/wallet/wallet-restore/WalletTypeDialog.tsx b/source/renderer/app/components/wallet/wallet-restore/WalletTypeDialog.tsx index 7e3d3eafa5..84f3d53939 100644 --- a/source/renderer/app/components/wallet/wallet-restore/WalletTypeDialog.tsx +++ b/source/renderer/app/components/wallet/wallet-restore/WalletTypeDialog.tsx @@ -1,8 +1,8 @@ import React, { Component, Fragment } from 'react'; import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; import { set } from 'lodash'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import RadioSet from '../../widgets/RadioSet'; import WalletRestoreDialog from './widgets/WalletRestoreDialog'; import globalMessages from '../../../i18n/global-messages'; diff --git a/source/renderer/app/components/wallet/wallet-restore/widgets/ConfirmationDialog.tsx b/source/renderer/app/components/wallet/wallet-restore/widgets/ConfirmationDialog.tsx index 4aa50a7413..44a670c4f0 100644 --- a/source/renderer/app/components/wallet/wallet-restore/widgets/ConfirmationDialog.tsx +++ b/source/renderer/app/components/wallet/wallet-restore/widgets/ConfirmationDialog.tsx @@ -37,7 +37,6 @@ type Props = { onCancel: (...args: Array) => any; }; -@observer class ConfirmationDialog extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -74,4 +73,4 @@ class ConfirmationDialog extends Component { } } -export default ConfirmationDialog; +export default observer(ConfirmationDialog); diff --git a/source/renderer/app/components/wallet/wallet-restore/widgets/WalletRestoreSteps.tsx b/source/renderer/app/components/wallet/wallet-restore/widgets/WalletRestoreSteps.tsx index df33b6af40..e2f51a18e1 100644 --- a/source/renderer/app/components/wallet/wallet-restore/widgets/WalletRestoreSteps.tsx +++ b/source/renderer/app/components/wallet/wallet-restore/widgets/WalletRestoreSteps.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { defineMessages, intlShape } from 'react-intl'; -import { Stepper } from 'react-polymorph/lib/components/Stepper'; -import { StepperSkin } from 'react-polymorph/lib/skins/simple/StepperSkin'; +import { Stepper } from '@react-polymorph/components/Stepper'; +import { StepperSkin } from '@react-polymorph/skins/simple/StepperSkin'; import styles from './WalletRestoreSteps.scss'; import { RESTORE_WALLET_STEPS } from '../../../../config/walletRestoreConfig'; import type { RestoreWalletStep } from '../../../../types/walletRestoreTypes'; diff --git a/source/renderer/app/components/wallet/widgets/ClearButton.tsx b/source/renderer/app/components/wallet/widgets/ClearButton.tsx index 5a9f0d706b..e9e9d8e228 100644 --- a/source/renderer/app/components/wallet/widgets/ClearButton.tsx +++ b/source/renderer/app/components/wallet/widgets/ClearButton.tsx @@ -1,5 +1,6 @@ +// @ts-nocheck import React from 'react'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import SVGInline from 'react-svg-inline'; import closeIcon from '../../../assets/images/close-cross.inline.svg'; import styles from './ClearButton.scss'; diff --git a/source/renderer/app/components/widgets/BorderedBox.tsx b/source/renderer/app/components/widgets/BorderedBox.tsx index c86440c507..1b1c398274 100644 --- a/source/renderer/app/components/widgets/BorderedBox.tsx +++ b/source/renderer/app/components/widgets/BorderedBox.tsx @@ -13,16 +13,10 @@ type Props = { onMouseLeave?: (...args: Array) => any; }; -@observer class BorderedBox extends Component { render() { - const { - children, - className, - fullHeight, - onMouseEnter, - onMouseLeave, - } = this.props; + const { children, className, fullHeight, onMouseEnter, onMouseLeave } = + this.props; const componentClasses = classnames([ styles.component, fullHeight ? styles.fullHeight : null, @@ -40,4 +34,4 @@ class BorderedBox extends Component { } } -export default BorderedBox; +export default observer(BorderedBox); diff --git a/source/renderer/app/components/widgets/ButtonLink.tsx b/source/renderer/app/components/widgets/ButtonLink.tsx index 856080fc54..aa12013291 100644 --- a/source/renderer/app/components/widgets/ButtonLink.tsx +++ b/source/renderer/app/components/widgets/ButtonLink.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import { get } from 'lodash'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; import styles from './ButtonLink.scss'; type LinkPropsTypes = { diff --git a/source/renderer/app/components/widgets/CountdownWidget.tsx b/source/renderer/app/components/widgets/CountdownWidget.tsx index ec1d914b16..1b6d0ec725 100644 --- a/source/renderer/app/components/widgets/CountdownWidget.tsx +++ b/source/renderer/app/components/widgets/CountdownWidget.tsx @@ -39,7 +39,6 @@ type State = { timeLeft: number | null | undefined; }; -@observer class CountdownWidget extends Component { // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. intervalHandler: IntervalID | null | undefined = null; @@ -184,4 +183,4 @@ class CountdownWidget extends Component { } } -export default CountdownWidget; +export default observer(CountdownWidget); diff --git a/source/renderer/app/components/widgets/Dialog.tsx b/source/renderer/app/components/widgets/Dialog.tsx index 41aef19ae4..75ce521949 100644 --- a/source/renderer/app/components/widgets/Dialog.tsx +++ b/source/renderer/app/components/widgets/Dialog.tsx @@ -1,14 +1,15 @@ +// @ts-nocheck import React, { Component } from 'react'; import { map } from 'lodash'; + import classnames from 'classnames'; -// @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node, Element, ElementRef } from 'react'; -import { Modal } from 'react-polymorph/lib/components/Modal'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; -import { ModalSkin } from 'react-polymorph/lib/skins/simple/ModalSkin'; +import { Modal } from '@react-polymorph/components/Modal'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; +import { ModalSkin } from '@react-polymorph/skins/simple/ModalSkin'; import styles from './Dialog.scss'; import dialogOverrides from './DialogOverride.scss'; import dialogFullSizeOverride from './DialogFullSizeOverride.scss'; diff --git a/source/renderer/app/components/widgets/ExternalLinkButton.tsx b/source/renderer/app/components/widgets/ExternalLinkButton.tsx index 297288409e..2616e9be72 100644 --- a/source/renderer/app/components/widgets/ExternalLinkButton.tsx +++ b/source/renderer/app/components/widgets/ExternalLinkButton.tsx @@ -1,6 +1,6 @@ import React from 'react'; import classnames from 'classnames'; -import { Button } from 'react-polymorph/lib/components/Button'; +import { Button } from '@react-polymorph/components/Button'; import SVGInline from 'react-svg-inline'; import externalLinkIcon from '../../assets/images/external-link-ic.inline.svg'; import styles from './ExternalLinkButton.scss'; diff --git a/source/renderer/app/components/widgets/FullyDecentralizedEffect.tsx b/source/renderer/app/components/widgets/FullyDecentralizedEffect.tsx index 118fae6e1c..0c2f62fa8f 100644 --- a/source/renderer/app/components/widgets/FullyDecentralizedEffect.tsx +++ b/source/renderer/app/components/widgets/FullyDecentralizedEffect.tsx @@ -1,5 +1,4 @@ import React, { Component, createRef } from 'react'; -import { get } from 'lodash'; import classnames from 'classnames'; import { observer } from 'mobx-react'; import { Fireworks } from 'fireworks-js'; @@ -10,7 +9,6 @@ type Props = { className?: string; }; -@observer class FullyDecentralizedEffect extends Component { constructor(props: Props) { super(props); @@ -22,7 +20,7 @@ class FullyDecentralizedEffect extends Component { componentDidMount() { const { isActive } = this.props; - const container = get(this, 'container.current'); + const container = this.container?.current; if (container instanceof HTMLElement) { const fireworks = new Fireworks({ @@ -71,4 +69,4 @@ class FullyDecentralizedEffect extends Component { } } -export default FullyDecentralizedEffect; +export default observer(FullyDecentralizedEffect); diff --git a/source/renderer/app/components/widgets/NewsFeedIcon.tsx b/source/renderer/app/components/widgets/NewsFeedIcon.tsx index 83794383c6..9c36a38358 100644 --- a/source/renderer/app/components/widgets/NewsFeedIcon.tsx +++ b/source/renderer/app/components/widgets/NewsFeedIcon.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React from 'react'; import SVGInline from 'react-svg-inline'; import classNames from 'classnames'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { defineMessages, injectIntl } from 'react-intl'; import newsFeedIcon from '../../assets/images/top-bar/news-feed-icon.inline.svg'; import styles from './NewsFeedIcon.scss'; diff --git a/source/renderer/app/components/widgets/NodeSyncStatusIcon.tsx b/source/renderer/app/components/widgets/NodeSyncStatusIcon.tsx index edadca28dd..a575fc7a77 100644 --- a/source/renderer/app/components/widgets/NodeSyncStatusIcon.tsx +++ b/source/renderer/app/components/widgets/NodeSyncStatusIcon.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React, { Component } from 'react'; import SVGInline from 'react-svg-inline'; import { defineMessages, intlShape } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import classNames from 'classnames'; import { formattedNumber } from '../../utils/formatters'; import spinnerIcon from '../../assets/images/top-bar/node-sync-spinner.inline.svg'; diff --git a/source/renderer/app/components/widgets/ProgressBar.tsx b/source/renderer/app/components/widgets/ProgressBar.tsx index a9c1d7649a..f9c258440d 100644 --- a/source/renderer/app/components/widgets/ProgressBar.tsx +++ b/source/renderer/app/components/widgets/ProgressBar.tsx @@ -6,7 +6,6 @@ type Props = { progress: number; }; -@observer class ProgressBar extends Component { static defaultProps = { progress: 0, @@ -27,4 +26,4 @@ class ProgressBar extends Component { } } -export default ProgressBar; +export default observer(ProgressBar); diff --git a/source/renderer/app/components/widgets/ProgressBarLarge.tsx b/source/renderer/app/components/widgets/ProgressBarLarge.tsx index 1e5bcc9e42..0f2f2d6296 100644 --- a/source/renderer/app/components/widgets/ProgressBarLarge.tsx +++ b/source/renderer/app/components/widgets/ProgressBarLarge.tsx @@ -13,7 +13,6 @@ type Props = { loading?: boolean; }; -@observer class ProgressBarLarge extends Component { static defaultProps = { progress: 0, @@ -67,4 +66,4 @@ class ProgressBarLarge extends Component { } } -export default ProgressBarLarge; +export default observer(ProgressBarLarge); diff --git a/source/renderer/app/components/widgets/RadioSet.tsx b/source/renderer/app/components/widgets/RadioSet.tsx index fe97ead72d..edd0bd18c9 100644 --- a/source/renderer/app/components/widgets/RadioSet.tsx +++ b/source/renderer/app/components/widgets/RadioSet.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import classnames from 'classnames'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; -import { Radio } from 'react-polymorph/lib/components/Radio'; -import { RadioSkin } from 'react-polymorph/lib/skins/simple/RadioSkin'; +import { Radio } from '@react-polymorph/components/Radio'; +import { RadioSkin } from '@react-polymorph/skins/simple/RadioSkin'; import { observer } from 'mobx-react'; import styles from './RadioSet.scss'; import stylesOverride from './RadioOverride.scss'; @@ -26,7 +26,6 @@ type RadioProps = { themeOverrides: Record; }; -@observer class RadioSet extends Component { render() { const { label, items, verticallyAligned } = this.props; @@ -52,4 +51,4 @@ class RadioSet extends Component { } } -export default RadioSet; +export default observer(RadioSet); diff --git a/source/renderer/app/components/widgets/Slider.tsx b/source/renderer/app/components/widgets/Slider.tsx index 02c9ccb49e..98c4f865ae 100644 --- a/source/renderer/app/components/widgets/Slider.tsx +++ b/source/renderer/app/components/widgets/Slider.tsx @@ -1,8 +1,9 @@ +// @ts-nocheck import React, { useState } from 'react'; import { observer } from 'mobx-react'; import BigNumber from 'bignumber.js'; import RcSlider from 'rc-slider'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { shortNumber } from '../../utils/formatters'; import styles from './Slider.scss'; diff --git a/source/renderer/app/components/widgets/collapsible-section/CollapsibleSection.tsx b/source/renderer/app/components/widgets/collapsible-section/CollapsibleSection.tsx index a8f66212cc..3a7219a7d4 100644 --- a/source/renderer/app/components/widgets/collapsible-section/CollapsibleSection.tsx +++ b/source/renderer/app/components/widgets/collapsible-section/CollapsibleSection.tsx @@ -1,7 +1,7 @@ import React, { ReactNode, useCallback, useState } from 'react'; import { injectIntl } from 'react-intl'; import classnames from 'classnames'; -import { Link } from 'react-polymorph/lib/components/Link'; +import { Link } from '@react-polymorph/components/Link'; import styles from './CollapsibleSection.scss'; import { Intl, ReactIntlMessage } from '../../../types/i18nTypes'; import globalMessages from '../../../i18n/global-messages'; diff --git a/source/renderer/app/components/widgets/forms/FileUploadWidget.tsx b/source/renderer/app/components/widgets/forms/FileUploadWidget.tsx index 7190591d72..2d5b7aa461 100644 --- a/source/renderer/app/components/widgets/forms/FileUploadWidget.tsx +++ b/source/renderer/app/components/widgets/forms/FileUploadWidget.tsx @@ -16,7 +16,6 @@ type Props = { acceptedFileTypes: Array; }; -@observer class FileUploadWidget extends Component { onOpen = async () => { const params: FileDialogRequestParams = { @@ -78,4 +77,4 @@ class FileUploadWidget extends Component { } } -export default FileUploadWidget; +export default observer(FileUploadWidget); diff --git a/source/renderer/app/components/widgets/forms/FormFieldSkinTooltip.tsx b/source/renderer/app/components/widgets/forms/FormFieldSkinTooltip.tsx index ea28a3433f..a886494b37 100644 --- a/source/renderer/app/components/widgets/forms/FormFieldSkinTooltip.tsx +++ b/source/renderer/app/components/widgets/forms/FormFieldSkinTooltip.tsx @@ -1,14 +1,13 @@ +// @ts-nocheck /* eslint-disable */ import React from 'react'; -// @ts-ignore ts-migrate(2724) FIXME: '"react"' has no exported member named 'Element'. ... Remove this comment to see the full error message import type { Element } from 'react'; import { omit } from 'lodash'; import classnames from 'classnames'; import SVGInline from 'react-svg-inline'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/exclama... Remove this comment to see the full error message import exclamationPointIcon from '../../../assets/images/exclamation-point.inline.svg'; import styles from './FormFieldSkinTooltip.scss'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; type Props = { className: string; diff --git a/source/renderer/app/components/widgets/forms/ImageUploadWidget.tsx b/source/renderer/app/components/widgets/forms/ImageUploadWidget.tsx index be04d116b5..3c8900825a 100644 --- a/source/renderer/app/components/widgets/forms/ImageUploadWidget.tsx +++ b/source/renderer/app/components/widgets/forms/ImageUploadWidget.tsx @@ -19,7 +19,6 @@ type Props = { label: string; }; -@observer class ImageUploadWidget extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -46,4 +45,4 @@ class ImageUploadWidget extends Component { } } -export default ImageUploadWidget; +export default observer(ImageUploadWidget); diff --git a/source/renderer/app/components/widgets/forms/InlineEditingDropdown.tsx b/source/renderer/app/components/widgets/forms/InlineEditingDropdown.tsx index 4de9b114f9..eda270b156 100644 --- a/source/renderer/app/components/widgets/forms/InlineEditingDropdown.tsx +++ b/source/renderer/app/components/widgets/forms/InlineEditingDropdown.tsx @@ -1,13 +1,14 @@ +// @ts-nocheck import React, { Component } from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import SVGInline from 'react-svg-inline'; import classnames from 'classnames'; -import { Select } from 'react-polymorph/lib/components/Select'; -import { SelectSkin } from 'react-polymorph/lib/skins/simple/SelectSkin'; +import { Select } from '@react-polymorph/components/Select'; +import { SelectSkin } from '@react-polymorph/skins/simple/SelectSkin'; import styles from './InlineEditingDropdown.scss'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/questio... Remove this comment to see the full error message import questionMarkIcon from '../../../assets/images/question-mark.inline.svg'; @@ -36,7 +37,6 @@ type Props = { successfullyUpdated: boolean; }; -@observer class InlineEditingDropdown extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -95,4 +95,4 @@ class InlineEditingDropdown extends Component { } } -export default InlineEditingDropdown; +export default observer(InlineEditingDropdown); diff --git a/source/renderer/app/components/widgets/forms/InlineEditingInput.tsx b/source/renderer/app/components/widgets/forms/InlineEditingInput.tsx index dc66590368..e10040ffe8 100644 --- a/source/renderer/app/components/widgets/forms/InlineEditingInput.tsx +++ b/source/renderer/app/components/widgets/forms/InlineEditingInput.tsx @@ -1,23 +1,20 @@ /* eslint-disable react/no-did-update-set-state */ +// @ts-nocheck import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { defineMessages, intlShape } from 'react-intl'; import { get } from 'lodash'; -import { Button } from 'react-polymorph/lib/components/Button'; +import { Button } from '@react-polymorph/components/Button'; import vjf from 'mobx-react-form/lib/validators/VJF'; import SVGInline from 'react-svg-inline'; import classnames from 'classnames'; -import { Input } from 'react-polymorph/lib/components/Input'; +import { Input } from '@react-polymorph/components/Input'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; import styles from './InlineEditingInput.scss'; import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../../config/timingConfig'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/pen.inl... Remove this comment to see the full error message import penIcon from '../../../assets/images/pen.inline.svg'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/close-c... Remove this comment to see the full error message import crossIcon from '../../../assets/images/close-cross.inline.svg'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/arrow-r... Remove this comment to see the full error message import arrowIcon from '../../../assets/images/arrow-right.inline.svg'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/spinner... Remove this comment to see the full error message import spinningIcon from '../../../assets/images/spinner-ic.inline.svg'; import { ENTER_KEY_CODE, ESCAPE_KEY_CODE } from '../../../config/numbersConfig'; @@ -68,7 +65,6 @@ interface FormFields { inputField: string; } -@observer class InlineEditingInput extends Component { static defaultProps = { validateOnChange: true, @@ -367,4 +363,4 @@ class InlineEditingInput extends Component { } } -export default InlineEditingInput; +export default observer(InlineEditingInput); diff --git a/source/renderer/app/components/widgets/forms/InlineEditingSmallInput.tsx b/source/renderer/app/components/widgets/forms/InlineEditingSmallInput.tsx index eb5f2a3441..6b72ffa088 100644 --- a/source/renderer/app/components/widgets/forms/InlineEditingSmallInput.tsx +++ b/source/renderer/app/components/widgets/forms/InlineEditingSmallInput.tsx @@ -1,21 +1,19 @@ +// @ts-nocheck import React, { Component } from 'react'; import { observer } from 'mobx-react'; -import { get } from 'lodash'; import SVGInline from 'react-svg-inline'; import vjf from 'mobx-react-form/lib/validators/VJF'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Button } from 'react-polymorph/lib/components/Button'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Button } from '@react-polymorph/components/Button'; import classnames from 'classnames'; import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm'; import styles from './InlineEditingSmallInput.scss'; + import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../../config/timingConfig'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/pen.inl... Remove this comment to see the full error message import penIcon from '../../../assets/images/pen.inline.svg'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/close-c... Remove this comment to see the full error message import crossIcon from '../../../assets/images/close-cross.inline.svg'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/arrow-r... Remove this comment to see the full error message import arrowIcon from '../../../assets/images/arrow-right.inline.svg'; type Props = { @@ -43,7 +41,6 @@ interface FormFields { inputField: string; } -@observer class InlineEditingSmallInput extends Component { state = { isActive: false, @@ -154,7 +151,7 @@ class InlineEditingSmallInput extends Component { blur: () => {}, focus: () => {}, }; - return get(this, 'inputField.inputElement.current', fallbackInput); + return this?.inputField?.inputElement?.current || fallbackInput; } inputField: Input; @@ -303,4 +300,4 @@ class InlineEditingSmallInput extends Component { } } -export default InlineEditingSmallInput; +export default observer(InlineEditingSmallInput); diff --git a/source/renderer/app/components/widgets/forms/ItemDropdownOption.tsx b/source/renderer/app/components/widgets/forms/ItemDropdownOption.tsx index 5f4d19ee94..f91f02238a 100644 --- a/source/renderer/app/components/widgets/forms/ItemDropdownOption.tsx +++ b/source/renderer/app/components/widgets/forms/ItemDropdownOption.tsx @@ -2,7 +2,7 @@ import React, { Component } from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; import SVGInline from 'react-svg-inline'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { intlShape, defineMessages } from 'react-intl'; import classnames from 'classnames'; import styles from './ItemDropdownOption.scss'; diff --git a/source/renderer/app/components/widgets/forms/ItemsDropdown.tsx b/source/renderer/app/components/widgets/forms/ItemsDropdown.tsx index 7599f0d4a0..552d241a8e 100644 --- a/source/renderer/app/components/widgets/forms/ItemsDropdown.tsx +++ b/source/renderer/app/components/widgets/forms/ItemsDropdown.tsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import classnames from 'classnames'; import { filter, escapeRegExp } from 'lodash'; import { intlShape } from 'react-intl'; -import { Select } from 'react-polymorph/lib/components/Select'; -import { SelectSkin } from 'react-polymorph/lib/skins/simple/SelectSkin'; +import { Select } from '@react-polymorph/components/Select'; +import { SelectSkin } from '@react-polymorph/skins/simple/SelectSkin'; import ItemDropdownOption from './ItemDropdownOption'; import type { ItemDropdown } from './ItemDropdownOption'; import styles from './ItemsDropdown.scss'; diff --git a/source/renderer/app/components/widgets/forms/NormalSwitch.tsx b/source/renderer/app/components/widgets/forms/NormalSwitch.tsx index 4f356bb2d9..2b0baa9683 100644 --- a/source/renderer/app/components/widgets/forms/NormalSwitch.tsx +++ b/source/renderer/app/components/widgets/forms/NormalSwitch.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { SwitchSkin } from 'react-polymorph/lib/skins/simple/SwitchSkin'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { SwitchSkin } from '@react-polymorph/skins/simple/SwitchSkin'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; import classnames from 'classnames'; import styles from './NormalSwitch.scss'; diff --git a/source/renderer/app/components/widgets/forms/PasswordInput.tsx b/source/renderer/app/components/widgets/forms/PasswordInput.tsx index 4fcdae73c2..b308173e2f 100644 --- a/source/renderer/app/components/widgets/forms/PasswordInput.tsx +++ b/source/renderer/app/components/widgets/forms/PasswordInput.tsx @@ -1,8 +1,8 @@ import classnames from 'classnames'; import React, { Component } from 'react'; import { intlShape } from 'react-intl'; -import { PasswordInput as RPPasswordInput } from 'react-polymorph/lib/components/PasswordInput'; -import type { PasswordInputProps } from 'react-polymorph/lib/components/PasswordInput'; +import { PasswordInput as RPPasswordInput } from '@react-polymorph/components/PasswordInput'; +import type { PasswordInputProps } from '@react-polymorph/components/PasswordInput'; import globalMessages from '../../../i18n/global-messages'; import styles from './PasswordInput.scss'; diff --git a/source/renderer/app/components/widgets/forms/PinCode.tsx b/source/renderer/app/components/widgets/forms/PinCode.tsx index f06eab285e..8460942af6 100644 --- a/source/renderer/app/components/widgets/forms/PinCode.tsx +++ b/source/renderer/app/components/widgets/forms/PinCode.tsx @@ -1,9 +1,10 @@ +// @ts-nocheck import React, { Component } from 'react'; import { map } from 'lodash'; -import { NumericInput } from 'react-polymorph/lib/components/NumericInput'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { NumericInput } from '@react-polymorph/components/NumericInput'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; +import { PopOver } from '@react-polymorph/components/PopOver'; import classNames from 'classnames'; import styles from './PinCode.scss'; diff --git a/source/renderer/app/components/widgets/forms/ProfileSettingsForm.tsx b/source/renderer/app/components/widgets/forms/ProfileSettingsForm.tsx index 1648c632c1..fd996e837d 100644 --- a/source/renderer/app/components/widgets/forms/ProfileSettingsForm.tsx +++ b/source/renderer/app/components/widgets/forms/ProfileSettingsForm.tsx @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classNames from 'classnames'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { Select } from 'react-polymorph/lib/components/Select'; -import { ButtonSpinnerSkin } from 'react-polymorph/lib/skins/simple/ButtonSpinnerSkin'; -import { SelectSkin } from 'react-polymorph/lib/skins/simple/SelectSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { Select } from '@react-polymorph/components/Select'; +import { ButtonSpinnerSkin } from '@react-polymorph/skins/simple/ButtonSpinnerSkin'; +import { SelectSkin } from '@react-polymorph/skins/simple/SelectSkin'; import { defineMessages, intlShape } from 'react-intl'; import LocalizableError from '../../../i18n/LocalizableError'; import styles from './ProfileSettingsForm.scss'; @@ -55,7 +55,6 @@ export type ProfileSettingsFormProps = { error?: LocalizableError | null | undefined; }; -@observer class ProfileSettingsForm extends Component { static defaultProps = { onChangeItem: () => {}, @@ -142,4 +141,4 @@ class ProfileSettingsForm extends Component { } } -export default ProfileSettingsForm; +export default observer(ProfileSettingsForm); diff --git a/source/renderer/app/components/widgets/forms/ReadOnlyInput.tsx b/source/renderer/app/components/widgets/forms/ReadOnlyInput.tsx index c82ac06551..00105b05e5 100644 --- a/source/renderer/app/components/widgets/forms/ReadOnlyInput.tsx +++ b/source/renderer/app/components/widgets/forms/ReadOnlyInput.tsx @@ -1,9 +1,10 @@ +// @ts-nocheck import React, { Component } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; import { intlShape } from 'react-intl'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import globalMessages from '../../../i18n/global-messages'; import styles from './ReadOnlyInput.scss'; @@ -15,7 +16,6 @@ type Props = { withButton?: boolean; }; -@observer class ReadOnlyInput extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -52,4 +52,4 @@ class ReadOnlyInput extends Component { } } -export default ReadOnlyInput; +export default observer(ReadOnlyInput); diff --git a/source/renderer/app/components/widgets/forms/TinyButton.tsx b/source/renderer/app/components/widgets/forms/TinyButton.tsx index ceb807b91f..181114fb3d 100644 --- a/source/renderer/app/components/widgets/forms/TinyButton.tsx +++ b/source/renderer/app/components/widgets/forms/TinyButton.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import classnames from 'classnames'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; // @ts-ignore ts-migrate(2724) FIXME: '"react"' has no exported member named 'Element'. ... Remove this comment to see the full error message import type { Element } from 'react'; import styles from './TinyButton.scss'; diff --git a/source/renderer/app/components/widgets/forms/TinyCheckbox.tsx b/source/renderer/app/components/widgets/forms/TinyCheckbox.tsx index 12431e1d97..448e19bcfb 100644 --- a/source/renderer/app/components/widgets/forms/TinyCheckbox.tsx +++ b/source/renderer/app/components/widgets/forms/TinyCheckbox.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; import styles from './TinyCheckbox.scss'; type Props = { diff --git a/source/renderer/app/components/widgets/forms/TinyDatePicker.tsx b/source/renderer/app/components/widgets/forms/TinyDatePicker.tsx index 8890db52cf..d113d4481b 100644 --- a/source/renderer/app/components/widgets/forms/TinyDatePicker.tsx +++ b/source/renderer/app/components/widgets/forms/TinyDatePicker.tsx @@ -1,10 +1,10 @@ +// @ts-nocheck import React, { Component } from 'react'; -// @ts-ignore ts-migrate(2724) FIXME: '"react"' has no exported member named 'Element'. ... Remove this comment to see the full error message import type { Element } from 'react'; import Datetime from 'react-datetime'; import { intlShape } from 'react-intl'; import moment from 'moment'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import globalMessages from '../../../i18n/global-messages'; import TinyButton from './TinyButton'; import TinyInput from './TinyInput'; @@ -48,13 +48,11 @@ export default class TinyDatePicker extends Component { onChange, isValidDate, dateFormat, - disablePaste, value, label, placeholder, innerLabelPrefix, innerValue, - pickerPanelPosition, useReadMode, error, ...rest diff --git a/source/renderer/app/components/widgets/forms/TinyInput.tsx b/source/renderer/app/components/widgets/forms/TinyInput.tsx index 94d3dfe564..e0e54b9966 100644 --- a/source/renderer/app/components/widgets/forms/TinyInput.tsx +++ b/source/renderer/app/components/widgets/forms/TinyInput.tsx @@ -1,10 +1,10 @@ +// @ts-nocheck import React, { Component } from 'react'; -// @ts-ignore ts-migrate(2724) FIXME: '"react"' has no exported member named 'Element'. ... Remove this comment to see the full error message import type { ComponentType, Element, Node, Ref } from 'react'; -import { NumericInput } from 'react-polymorph/lib/components/NumericInput'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import { NumericInput } from '@react-polymorph/components/NumericInput'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; import styles from './TinyInput.scss'; import { NumberFormat } from '../../../../../common/types/number.types'; // TODO: Extend react-polymorph Input component props when they are available diff --git a/source/renderer/app/components/widgets/forms/TinySelect.tsx b/source/renderer/app/components/widgets/forms/TinySelect.tsx index 2527484666..196522285e 100644 --- a/source/renderer/app/components/widgets/forms/TinySelect.tsx +++ b/source/renderer/app/components/widgets/forms/TinySelect.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; -import { Select } from 'react-polymorph/lib/components/Select'; -import { SelectSkin } from 'react-polymorph/lib/skins/simple/SelectSkin'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import { Select } from '@react-polymorph/components/Select'; +import { SelectSkin } from '@react-polymorph/skins/simple/SelectSkin'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; import styles from './TinySelect.scss'; type Props = { diff --git a/source/renderer/app/components/widgets/forms/TinySwitch.tsx b/source/renderer/app/components/widgets/forms/TinySwitch.tsx index 5390947316..5620ecaca3 100644 --- a/source/renderer/app/components/widgets/forms/TinySwitch.tsx +++ b/source/renderer/app/components/widgets/forms/TinySwitch.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { SwitchSkin } from 'react-polymorph/lib/skins/simple/SwitchSkin'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { SwitchSkin } from '@react-polymorph/skins/simple/SwitchSkin'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; import styles from './TinySwitch.scss'; type Props = { diff --git a/source/renderer/app/components/widgets/monospace-text-block/MonospaceTextBlock.tsx b/source/renderer/app/components/widgets/monospace-text-block/MonospaceTextBlock.tsx index 163be56c7a..414edc3fd9 100644 --- a/source/renderer/app/components/widgets/monospace-text-block/MonospaceTextBlock.tsx +++ b/source/renderer/app/components/widgets/monospace-text-block/MonospaceTextBlock.tsx @@ -1,4 +1,4 @@ -import React, { FC, ReactNode } from 'react'; +import React, { ReactNode } from 'react'; import classNames from 'classnames'; import styles from './MonospaceTextBlock.scss'; diff --git a/source/renderer/app/components/widgets/splash/Splash.tsx b/source/renderer/app/components/widgets/splash/Splash.tsx index f8c692c933..72fc5b5323 100644 --- a/source/renderer/app/components/widgets/splash/Splash.tsx +++ b/source/renderer/app/components/widgets/splash/Splash.tsx @@ -1,11 +1,12 @@ -import React, { Component } from 'react'; +// @ts-nocheck +import React from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; import SVGInline from 'react-svg-inline'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import { Link } from 'react-polymorph/lib/components/Link'; -import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { Button } from '@react-polymorph/components/Button'; +import { ButtonSkin } from '@react-polymorph/skins/simple/ButtonSkin'; +import { Link } from '@react-polymorph/components/Link'; +import { LinkSkin } from '@react-polymorph/skins/simple/LinkSkin'; // @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/daedalu... Remove this comment to see the full error message import daedalusIcon from '../../../assets/images/daedalus-logo-loading-grey.inline.svg'; import styles from './Splash.scss'; @@ -21,56 +22,56 @@ type Props = { linkLabel: boolean | string; backgroundImage?: string; }; -export default class SplashNetwork extends Component { - render() { - const { - onButtonClick, - onLinkClick, - title, - subTitle1, - subTitle2, - description, - buttonLabel, - linkLabel, - backgroundImage, - } = this.props; - return ( -
    -
    - {backgroundImage && ( - <> -
    - - - )} -
    -
    - -
    {title}
    -
    {subTitle1}
    - {subTitle2 &&
    {subTitle2}
    } -
    {description}
    -
    -
    - {linkLabel && ( - = ({ + onButtonClick, + onLinkClick, + title, + subTitle1, + subTitle2, + description, + buttonLabel, + linkLabel, + backgroundImage, +}) => { + return ( +
    +
    + {backgroundImage && ( + <> +
    + - )} + + )} +
    +
    + +
    {title}
    +
    {subTitle1}
    + {subTitle2 &&
    {subTitle2}
    } +
    {description}
    +
    +
    + {linkLabel && ( + + )}
    - ); - } -} +
    + ); +}; + +export default SplashNetwork; diff --git a/source/renderer/app/config/currencyConfig.nomics.ts b/source/renderer/app/config/currencyConfig.nomics.ts index 837efdcbb9..6104e88268 100644 --- a/source/renderer/app/config/currencyConfig.nomics.ts +++ b/source/renderer/app/config/currencyConfig.nomics.ts @@ -7,7 +7,7 @@ * check `currencyConfig.js` for more info * */ -import { get, values } from 'lodash'; +import { values } from 'lodash'; import { logger } from '../utils/logging'; import type { Currency, CurrencyApiConfig } from '../types/currencyTypes'; import type { @@ -51,7 +51,7 @@ const responses = { }, rate: (apiResponse: CurrencyRateNomicsResponse): GetCurrencyRateResponse => { try { - const rate = parseFloat(get(apiResponse, '[0].price', 0)); + const rate = parseFloat(apiResponse?.[0]?.price || '0'); logger.debug('Currency::Nomics::Rate success', { rate, }); diff --git a/source/renderer/app/config/hardwareWalletsConfig.ts b/source/renderer/app/config/hardwareWalletsConfig.ts index 56ff5fda01..1658b79189 100644 --- a/source/renderer/app/config/hardwareWalletsConfig.ts +++ b/source/renderer/app/config/hardwareWalletsConfig.ts @@ -9,13 +9,7 @@ export const SHELLEY_PURPOSE_INDEX = 1852; export const BYRON_PURPOSE_INDEX = 44; export const ADA_COIN_TYPE = 1815; export const DEFAULT_ADDRESS_INDEX = 0; -const { - isMainnet, - isStaging, - isSelfnode, - isPreprod, - isPreview, -} = global.environment; +const { isMainnet, isStaging, isSelfnode } = global.environment; const hardwareWalletNetworksConfig = {}; map(NetworkMagics, (networkMagic: NetworkMagicType, network: Network) => { const isMainnetLikeNetwork = isMainnet || isSelfnode || isStaging; diff --git a/source/renderer/app/config/stakingConfig.ts b/source/renderer/app/config/stakingConfig.ts index 53bbcddbb7..365d810cbe 100644 --- a/source/renderer/app/config/stakingConfig.ts +++ b/source/renderer/app/config/stakingConfig.ts @@ -18,7 +18,7 @@ export const SMASH_SERVERS_LIST: Record< > = { iohk: { name: 'IOHK', - url: smashUrl, + url: !!smashUrl ? smashUrl : 'https://smash.cardano-mainnet.iohk.io', }, // Metadata is fetched directly in URLs registered on chain, direct: { diff --git a/source/renderer/app/containers/MainLayout.tsx b/source/renderer/app/containers/MainLayout.tsx index 42cc0222fe..469ce56e31 100644 --- a/source/renderer/app/containers/MainLayout.tsx +++ b/source/renderer/app/containers/MainLayout.tsx @@ -11,8 +11,6 @@ import type { InjectedContainerProps } from '../types/injectedPropsType'; import { ROUTES } from '../routes-config'; import type { WalletSortConfig } from '../types/sidebarTypes'; -@inject('stores', 'actions') -@observer class MainLayout extends Component { static defaultProps = { actions: null, @@ -123,4 +121,4 @@ class MainLayout extends Component { } } -export default MainLayout; +export default inject('stores', 'actions')(observer(MainLayout)); diff --git a/source/renderer/app/containers/Root.tsx b/source/renderer/app/containers/Root.tsx index 67c902a03e..c860abc21f 100644 --- a/source/renderer/app/containers/Root.tsx +++ b/source/renderer/app/containers/Root.tsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { FC, useEffect, useState } from 'react'; import { inject, observer } from 'mobx-react'; import WalletAddPage from './wallet/WalletAddPage'; import LoadingPage from './loading/LoadingPage'; @@ -8,18 +8,19 @@ import AppUpdateContainer from './appUpdate/AppUpdateContainer'; import WalletImportFileDialog from '../components/wallet/wallet-import/WalletImportFileDialog'; import type { InjectedContainerProps } from '../types/injectedPropsType'; -type Props = InjectedContainerProps; +interface RootProps extends InjectedContainerProps { + children: React.ReactElement; +} -@inject('stores', 'actions') -@observer -class Root extends Component { - static defaultProps = { - actions: null, - stores: null, - }; +const Root: FC = inject( + 'stores', + 'actions' +)( + observer(({ stores, actions, children }) => { + // States for conditional rendering + const [currentPage, setCurrentPage] = useState(null); - render() { - const { stores, actions, children } = this.props; + // Destructure stores for convenience const { app, appUpdate, @@ -30,11 +31,13 @@ class Root extends Component { uiDialogs, wallets, } = stores; + const { isVotingPage } = voting; const { isStakingPage, redeemStep } = staking; const { isProfilePage, isSettingsPage } = profile; const { displayAppUpdateOverlay } = appUpdate; const { hasLoadedWallets } = wallets; + const { isConnected, isNodeStopping, @@ -43,58 +46,66 @@ class Root extends Component { isSplashShown, isSystemTimeCorrect, } = networkStatus; + const { isCurrentLocaleSet, areTermsOfUseAccepted } = profile; const isWalletImportDialogOpen = uiDialogs.isOpen(WalletImportFileDialog); + const isPageThatDoesntNeedWallets = (isStakingPage || isSettingsPage || isVotingPage) && hasLoadedWallets && isConnected; - // In case node is in stopping sequence we must show the "Connecting" screen - // with the "Stopping Cardano node..." and "Cardano node stopped" messages - // for all the screens except of the "Network status" screen. + const isNodeInStoppingSequence = isNodeStopping || isNodeStopped; - if ( + const shouldDisplayNetworkPage = isCurrentLocaleSet && areTermsOfUseAccepted && !app.environment.isTest && - isSplashShown - ) { - return ; - } - - if (!isNodeInStoppingSequence && redeemStep !== null) { - return ; - } - - if (!isNodeInStoppingSequence && displayAppUpdateOverlay) { - return ; - } - - // Just render any page that doesn't require wallets to be loaded or node to be connected - if ( - (isPageThatDoesntNeedWallets && !isNodeInStoppingSequence) || - (isProfilePage && (isNotEnoughDiskSpace || !isNodeInStoppingSequence)) - ) { - return children; - } - - if ( + isSplashShown; + const shouldDisplayRedeemItnRewards = + !isNodeInStoppingSequence && redeemStep !== null; + const shouldDisplayAppUpdate = + !isNodeInStoppingSequence && displayAppUpdateOverlay; + const shouldDisplayLoading = !isConnected || !hasLoadedWallets || isNotEnoughDiskSpace || !isSystemTimeCorrect || - displayAppUpdateOverlay - ) { - return ; - } - - if (!wallets.hasAnyWallets || isWalletImportDialogOpen) { - return ; - } + displayAppUpdateOverlay; + const shouldDisplayAddNewWallet = + !wallets.hasAnyWallets || isWalletImportDialogOpen; + const shouldDisplayNoWalletNoNodeConnected = + (isPageThatDoesntNeedWallets && !isNodeInStoppingSequence) || + (isProfilePage && (isNotEnoughDiskSpace || !isNodeInStoppingSequence)); + // Logic to determine which page to display, replicated using a useEffect hook + useEffect(() => { + if (shouldDisplayNetworkPage) { + setCurrentPage(); + } else if (shouldDisplayRedeemItnRewards) { + setCurrentPage(); + } else if (shouldDisplayAppUpdate) { + setCurrentPage(); + } else if (shouldDisplayNoWalletNoNodeConnected) { + setCurrentPage(children); + } else if (shouldDisplayLoading) { + setCurrentPage(); + } else if (shouldDisplayAddNewWallet) { + setCurrentPage(); + } else { + setCurrentPage(children); + } + }, [ + shouldDisplayNetworkPage, + shouldDisplayRedeemItnRewards, + shouldDisplayAppUpdate, + shouldDisplayLoading, + shouldDisplayAddNewWallet, + shouldDisplayNoWalletNoNodeConnected, + children, + ]); - return children; - } -} + return <>{currentPage}; + }) +); export default Root; diff --git a/source/renderer/app/containers/appUpdate/AppUpdateContainer.tsx b/source/renderer/app/containers/appUpdate/AppUpdateContainer.tsx index c315214077..95e41ebb7a 100644 --- a/source/renderer/app/containers/appUpdate/AppUpdateContainer.tsx +++ b/source/renderer/app/containers/appUpdate/AppUpdateContainer.tsx @@ -3,8 +3,6 @@ import { inject, observer } from 'mobx-react'; import AppUpdateOverlay from '../../components/appUpdate/AppUpdateOverlay'; import type { InjectedProps } from '../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class AppUpdateContainer extends Component { static defaultProps = { actions: null, @@ -29,11 +27,8 @@ class AppUpdateContainer extends Component { isWaitingToQuitDaedalus, installationProgress, } = appUpdate; - const { - installUpdate, - closeAppUpdateOverlay, - postponeUpdate, - } = actions.appUpdate; + const { installUpdate, closeAppUpdateOverlay, postponeUpdate } = + actions.appUpdate; if (!availableUpdate) return null; return ( { } } -export default AppUpdateContainer; +export default inject('stores', 'actions')(observer(AppUpdateContainer)); diff --git a/source/renderer/app/containers/assets/AssetSettingsDialogContainer.tsx b/source/renderer/app/containers/assets/AssetSettingsDialogContainer.tsx index 46b7f86eb2..7a9a7e2f10 100644 --- a/source/renderer/app/containers/assets/AssetSettingsDialogContainer.tsx +++ b/source/renderer/app/containers/assets/AssetSettingsDialogContainer.tsx @@ -6,8 +6,6 @@ import type { AssetToken } from '../../api/assets/types'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer class AssetSettingsDialogContainer extends Component { static defaultProps = { actions: null, @@ -42,4 +40,7 @@ class AssetSettingsDialogContainer extends Component { } } -export default AssetSettingsDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(AssetSettingsDialogContainer)); diff --git a/source/renderer/app/containers/knownIssues/RTSFlagsRecommendationOverlayContainer.tsx b/source/renderer/app/containers/knownIssues/RTSFlagsRecommendationOverlayContainer.tsx index c487deceba..7901ab1594 100644 --- a/source/renderer/app/containers/knownIssues/RTSFlagsRecommendationOverlayContainer.tsx +++ b/source/renderer/app/containers/knownIssues/RTSFlagsRecommendationOverlayContainer.tsx @@ -5,8 +5,6 @@ import RTSFlagsRecommendationOverlay from '../../components/knownIssues/RTSFlags type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class RTSFlagsRecommendationOverlayContainer extends Component { static defaultProps = { actions: null, @@ -53,4 +51,7 @@ class RTSFlagsRecommendationOverlayContainer extends Component { } } -export default RTSFlagsRecommendationOverlayContainer; +export default inject( + 'stores', + 'actions' +)(observer(RTSFlagsRecommendationOverlayContainer)); diff --git a/source/renderer/app/containers/knownIssues/ToggleRTSFlagsDialogContainer.tsx b/source/renderer/app/containers/knownIssues/ToggleRTSFlagsDialogContainer.tsx index 0b0114efde..b69d90963e 100644 --- a/source/renderer/app/containers/knownIssues/ToggleRTSFlagsDialogContainer.tsx +++ b/source/renderer/app/containers/knownIssues/ToggleRTSFlagsDialogContainer.tsx @@ -5,8 +5,6 @@ import ToggleRTSFlagsDialog from '../../components/knownIssues/ToggleRTSFlagsDia type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class ToggleRTSFlagsDialogContainer extends Component { static defaultProps = { actions: null, @@ -34,4 +32,7 @@ class ToggleRTSFlagsDialogContainer extends Component { } } -export default ToggleRTSFlagsDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(ToggleRTSFlagsDialogContainer)); diff --git a/source/renderer/app/containers/loading/LoadingPage.tsx b/source/renderer/app/containers/loading/LoadingPage.tsx index 90159fb923..c33363ba2b 100644 --- a/source/renderer/app/containers/loading/LoadingPage.tsx +++ b/source/renderer/app/containers/loading/LoadingPage.tsx @@ -1,50 +1,43 @@ -import React, { Component } from 'react'; -import { inject, observer } from 'mobx-react'; +import React, { FC, useState, useEffect } from 'react'; +import { observer } from 'mobx-react'; import CenteredLayout from '../../components/layout/CenteredLayout'; import NoDiskSpaceErrorPage from './NoDiskSpaceErrorPage'; import SystemTimeErrorPage from './SystemTimeErrorPage'; import SyncingConnectingPage from './SyncingConnectingPage'; import type { InjectedProps } from '../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer -class LoadingPage extends Component { - static defaultProps = { - stores: null, - actions: null, - }; +interface LoadingPageProps extends InjectedProps {} - get activeOverlay() { - if (this.isNotEnoughDiskSpace) return ; - if (this.isSystemTimeError) return ; - return null; - } +const LoadingPage: FC = ({ stores, actions }) => { + const { networkStatus } = stores; - get isNotEnoughDiskSpace() { - return this.networkStatus.isNotEnoughDiskSpace; - } + const [activeOverlay, setActiveOverlay] = useState(null); - get isSystemTimeError() { - const { - isSystemTimeCorrect, - isNodeStopping, - isNodeStopped, - } = this.networkStatus; - return !isSystemTimeCorrect && !isNodeStopping && !isNodeStopped; - } + useEffect(() => { + if (networkStatus.isNotEnoughDiskSpace) { + setActiveOverlay(); + } else if ( + !networkStatus.isSystemTimeCorrect && + !networkStatus.isNodeStopping && + !networkStatus.isNodeStopped + ) { + setActiveOverlay(); + } else { + setActiveOverlay(null); + } + }, [ + networkStatus.isNotEnoughDiskSpace, + networkStatus.isSystemTimeCorrect, + networkStatus.isNodeStopping, + networkStatus.isNodeStopped, + ]); - get networkStatus() { - return this.props.stores.networkStatus; - } + return ( + + + {activeOverlay} + + ); +}; - render() { - return ( - - - {this.activeOverlay} - - ); - } -} - -export default LoadingPage; +export default observer(LoadingPage); diff --git a/source/renderer/app/containers/loading/NoDiskSpaceErrorPage.tsx b/source/renderer/app/containers/loading/NoDiskSpaceErrorPage.tsx index 626b4923ee..713635fc3f 100644 --- a/source/renderer/app/containers/loading/NoDiskSpaceErrorPage.tsx +++ b/source/renderer/app/containers/loading/NoDiskSpaceErrorPage.tsx @@ -5,8 +5,6 @@ import NoDiskSpaceError from '../../components/loading/no-disk-space-error/NoDis type Props = InjectedStoresProps; -@inject('stores') -@observer class NoDiskSpaceErrorPage extends Component { static defaultProps = { stores: null, @@ -14,11 +12,8 @@ class NoDiskSpaceErrorPage extends Component { render() { const { stores } = this.props; - const { - diskSpaceRequired, - diskSpaceMissing, - diskSpaceRecommended, - } = stores.networkStatus; + const { diskSpaceRequired, diskSpaceMissing, diskSpaceRecommended } = + stores.networkStatus; return ( { } } -export default NoDiskSpaceErrorPage; +export default inject('stores')(observer(NoDiskSpaceErrorPage)); diff --git a/source/renderer/app/containers/loading/SyncingConnectingPage.tsx b/source/renderer/app/containers/loading/SyncingConnectingPage.tsx index 61e82aa4af..6058bea109 100644 --- a/source/renderer/app/containers/loading/SyncingConnectingPage.tsx +++ b/source/renderer/app/containers/loading/SyncingConnectingPage.tsx @@ -1,50 +1,63 @@ -import React, { Component } from 'react'; -import { observer, inject } from 'mobx-react'; +import React from 'react'; +import { observer } from 'mobx-react'; import type { InjectedProps } from '../../types/injectedPropsType'; import SyncingConnecting from '../../components/loading/syncing-connecting/SyncingConnecting'; import { generateSupportRequestLink } from '../../../../common/utils/reporting'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer -class LoadingSyncingConnectingPage extends Component { - static defaultProps = { - stores: null, - actions: null, +const SyncingConnectingPage: React.FC = ({ stores, actions }) => { + const { newsFeed, appUpdate, networkStatus, profile, app } = stores; + const { + cardanoNodeState, + isNodeResponding, + isNodeSyncing, + isNodeTimeCorrect, + isConnected, + isSynced, + isSyncProgressStalling, + hasBeenConnected, + getNetworkClockRequest, + isNodeStopping, + isNodeStopped, + isNotEnoughDiskSpace, + isTlsCertInvalid, + isVerifyingBlockchain, + blockSyncProgress, + } = networkStatus; + + const handleIssueClick = async (issueButtonUrl: string) => { + const locale = stores.profile.currentLocale; + const { environment } = stores.app; + const supportUrl = generateSupportRequestLink( + issueButtonUrl, + environment, + locale + ); + stores.app.openExternalLink(supportUrl); + }; + + const handleOpenExternalLink = (articleUrl: string) => { + stores.app.openExternalLink(articleUrl); + }; + + const handleDownloadLogs = () => { + actions.downloadLogs.trigger(); + actions.setIsDownloadingLogs.trigger(true); + }; + + const openDaedalusDiagnosticsDialog = () => { + actions.app.openDaedalusDiagnosticsDialog.trigger(); }; - render() { - const { - newsFeed, - appUpdate, - networkStatus, - profile, - app, - } = this.props.stores; - const { - cardanoNodeState, - isNodeResponding, - isNodeSyncing, - isNodeTimeCorrect, - isConnected, - isSynced, - isSyncProgressStalling, - hasBeenConnected, - getNetworkClockRequest, - isNodeStopping, - isNodeStopped, - isNotEnoughDiskSpace, - isTlsCertInvalid, - isVerifyingBlockchain, - blockSyncProgress, - } = networkStatus; - const { displayAppUpdateNewsItem } = appUpdate; - const { hasLoadedCurrentLocale, hasLoadedCurrentTheme } = profile; - const { toggleNewsFeed } = this.props.actions.app; - const { unread } = newsFeed.newsFeedData; - const hasNotification = unread.length > 0; - return ( + const { displayAppUpdateNewsItem } = appUpdate; + const { hasLoadedCurrentLocale, hasLoadedCurrentTheme } = profile; + const { toggleNewsFeed } = actions.app; + const { unread } = newsFeed.newsFeedData; + const hasNotification = unread.length > 0; + + return ( + stores.networkStatus.cardanoNodeState !== 'unknown' && ( { isNodeResponding={isNodeResponding} isNodeSyncing={isNodeSyncing} isNodeTimeCorrect={isNodeTimeCorrect} - onIssueClick={this.handleIssueClick} - onOpenExternalLink={this.handleOpenExternalLink} - onStatusIconClick={this.openDaedalusDiagnosticsDialog} - onDownloadLogs={this.handleDownloadLogs} + onIssueClick={handleIssueClick} + onOpenExternalLink={handleOpenExternalLink} + onStatusIconClick={openDaedalusDiagnosticsDialog} + onDownloadLogs={handleDownloadLogs} onToggleNewsFeedIconClick={toggleNewsFeed.trigger} disableDownloadLogs={app.isDownloadNotificationVisible} showNewsFeedIcon={!isNodeStopping && !isNodeStopped} isVerifyingBlockchain={isVerifyingBlockchain} blockSyncProgress={blockSyncProgress} /> - ); - } - - handleIssueClick = async (issueButtonUrl: string) => { - const locale = this.props.stores.profile.currentLocale; - const { environment } = this.props.stores.app; - const supportUrl = generateSupportRequestLink( - issueButtonUrl, - environment, - locale - ); - this.props.stores.app.openExternalLink(supportUrl); - }; - handleOpenExternalLink = (articleUrl: string) => { - this.props.stores.app.openExternalLink(articleUrl); - }; - handleDownloadLogs = () => { - const { app } = this.props.actions; - app.downloadLogs.trigger(); - app.setIsDownloadingLogs.trigger(true); - }; - openDaedalusDiagnosticsDialog = () => { - const { - actions: { app }, - } = this.props; - app.openDaedalusDiagnosticsDialog.trigger(); - }; -} + ) + ); +}; -export default LoadingSyncingConnectingPage; +export default observer(SyncingConnectingPage); diff --git a/source/renderer/app/containers/loading/SystemTimeErrorPage.tsx b/source/renderer/app/containers/loading/SystemTimeErrorPage.tsx index 320aab74fd..46702b130c 100644 --- a/source/renderer/app/containers/loading/SystemTimeErrorPage.tsx +++ b/source/renderer/app/containers/loading/SystemTimeErrorPage.tsx @@ -5,8 +5,6 @@ import type { InjectedProps } from '../../types/injectedPropsType'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer class SystemTimeErrorPage extends Component { static defaultProps = { stores: null, @@ -15,10 +13,8 @@ class SystemTimeErrorPage extends Component { render() { const { actions, stores } = this.props; - const { - localTimeDifference, - ignoreSystemTimeChecks, - } = stores.networkStatus; + const { localTimeDifference, ignoreSystemTimeChecks } = + stores.networkStatus; const { forceCheckNetworkClock } = actions.networkStatus; const { app, networkStatus, profile } = stores; const { openExternalLink } = app; @@ -39,4 +35,4 @@ class SystemTimeErrorPage extends Component { } } -export default SystemTimeErrorPage; +export default inject('stores', 'actions')(observer(SystemTimeErrorPage)); diff --git a/source/renderer/app/containers/news/NewsFeedContainer.tsx b/source/renderer/app/containers/news/NewsFeedContainer.tsx index 50a6339eb2..3c721b0b10 100644 --- a/source/renderer/app/containers/news/NewsFeedContainer.tsx +++ b/source/renderer/app/containers/news/NewsFeedContainer.tsx @@ -3,8 +3,6 @@ import { inject, observer } from 'mobx-react'; import NewsFeed from '../../components/news/NewsFeed'; import type { InjectedProps } from '../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class NewsFeedContainer extends Component { static defaultProps = { actions: null, @@ -21,11 +19,8 @@ class NewsFeedContainer extends Component { const { app, profile, appUpdate, newsFeed } = stores; const { newsFeedData, isLoadingNews, proceedNewsAction } = newsFeed; const { openAppUpdateOverlay } = actions.appUpdate; - const { - downloadProgress, - displayAppUpdateNewsItem, - isUpdatePostponed, - } = appUpdate; + const { downloadProgress, displayAppUpdateNewsItem, isUpdatePostponed } = + appUpdate; const { toggleNewsFeed } = actions.app; const { openExternalLink, newsFeedIsOpen } = app; const { currentDateFormat } = profile; @@ -50,4 +45,4 @@ class NewsFeedContainer extends Component { } } -export default NewsFeedContainer; +export default inject('stores', 'actions')(observer(NewsFeedContainer)); diff --git a/source/renderer/app/containers/news/NewsOverlayContainer.tsx b/source/renderer/app/containers/news/NewsOverlayContainer.tsx index 5f82d5c205..51242cad15 100644 --- a/source/renderer/app/containers/news/NewsOverlayContainer.tsx +++ b/source/renderer/app/containers/news/NewsOverlayContainer.tsx @@ -4,8 +4,6 @@ import AlertsOverlay from '../../components/news/AlertsOverlay'; import IncidentOverlay from '../../components/news/IncidentOverlay'; import type { InjectedProps } from '../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class NewsOverlayContainer extends Component { static defaultProps = { actions: null, @@ -74,4 +72,4 @@ class NewsOverlayContainer extends Component { } } -export default NewsOverlayContainer; +export default inject('stores', 'actions')(observer(NewsOverlayContainer)); diff --git a/source/renderer/app/containers/notifications/NotificationsContainer.tsx b/source/renderer/app/containers/notifications/NotificationsContainer.tsx index 522c4aa976..ec66efc69e 100644 --- a/source/renderer/app/containers/notifications/NotificationsContainer.tsx +++ b/source/renderer/app/containers/notifications/NotificationsContainer.tsx @@ -94,8 +94,6 @@ const messages = defineMessages({ }, }); -@inject('stores', 'actions') -@observer class NotificationsContainer extends Component { static defaultProps = { actions: null, @@ -125,8 +123,8 @@ class NotificationsContainer extends Component { }, { id: 'downloadTransactionsCSVSuccess', - actionToListenAndOpen: this.props.actions.transactions - .requestCSVFileSuccess, + actionToListenAndOpen: + this.props.actions.transactions.requestCSVFileSuccess, actionToListenAndClose: this.props.actions.transactions.requestCSVFile, }, { @@ -143,8 +141,8 @@ class NotificationsContainer extends Component { }, { id: 'downloadAddressPDFSuccess', - actionToListenAndOpen: this.props.actions.wallets - .generateAddressPDFSuccess, + actionToListenAndOpen: + this.props.actions.wallets.generateAddressPDFSuccess, actionToListenAndClose: this.props.actions.wallets.generateAddressPDF, }, { @@ -159,13 +157,13 @@ class NotificationsContainer extends Component { }, { id: 'copyStateDirectoryPath', - actionToListenAndOpen: this.props.actions.networkStatus - .copyStateDirectoryPath, + actionToListenAndOpen: + this.props.actions.networkStatus.copyStateDirectoryPath, }, { id: 'copyAssetParam', - actionToListenAndOpen: this.props.actions.assets - .copyAssetParamNotification, + actionToListenAndOpen: + this.props.actions.assets.copyAssetParamNotification, }, ]; // @ts-ignore ts-migrate(2740) FIXME: Type '{ downloadLogsProgress: { icon: string; hasE... Remove this comment to see the full error message @@ -229,4 +227,4 @@ class NotificationsContainer extends Component { } } -export default NotificationsContainer; +export default inject('stores', 'actions')(observer(NotificationsContainer)); diff --git a/source/renderer/app/containers/profile/AnalyticsConsentPage.tsx b/source/renderer/app/containers/profile/AnalyticsConsentPage.tsx index 3dbfa94a85..f08757ff17 100644 --- a/source/renderer/app/containers/profile/AnalyticsConsentPage.tsx +++ b/source/renderer/app/containers/profile/AnalyticsConsentPage.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback } from 'react'; +import React, { useCallback } from 'react'; import TopBar from '../../components/layout/TopBar'; import TopBarLayout from '../../components/layout/TopBarLayout'; import AnalyticsConsentForm from '../../components/profile/analytics/AnalyticsConsentForm'; diff --git a/source/renderer/app/containers/profile/DataLayerMigrationPage.tsx b/source/renderer/app/containers/profile/DataLayerMigrationPage.tsx index c1ecd45076..75abf3f8ef 100644 --- a/source/renderer/app/containers/profile/DataLayerMigrationPage.tsx +++ b/source/renderer/app/containers/profile/DataLayerMigrationPage.tsx @@ -4,8 +4,6 @@ import CenteredLayout from '../../components/layout/CenteredLayout'; import DataLayerMigrationForm from '../../components/profile/data-layer-migration/DataLayerMigrationForm'; import type { InjectedProps } from '../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class DataLayerMigrationPage extends Component { static defaultProps = { actions: null, @@ -16,9 +14,8 @@ class DataLayerMigrationPage extends Component { }; render() { - const { - setDataLayerMigrationAcceptanceRequest, - } = this.props.stores.profile; + const { setDataLayerMigrationAcceptanceRequest } = + this.props.stores.profile; return ( { } } -export default DataLayerMigrationPage; +export default inject('stores', 'actions')(observer(DataLayerMigrationPage)); diff --git a/source/renderer/app/containers/profile/InitialSettingsPage.tsx b/source/renderer/app/containers/profile/InitialSettingsPage.tsx index 9f8e962ff2..5ed68d8634 100644 --- a/source/renderer/app/containers/profile/InitialSettingsPage.tsx +++ b/source/renderer/app/containers/profile/InitialSettingsPage.tsx @@ -5,8 +5,6 @@ import TopBarLayout from '../../components/layout/TopBarLayout'; import InitialSettings from '../../components/profile/initial-settings/InitialSettings'; import type { InjectedProps } from '../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class InitialSettingsPage extends Component { static defaultProps = { actions: null, @@ -63,4 +61,4 @@ class InitialSettingsPage extends Component { } } -export default InitialSettingsPage; +export default inject('stores', 'actions')(observer(InitialSettingsPage)); diff --git a/source/renderer/app/containers/profile/TermsOfUsePage.tsx b/source/renderer/app/containers/profile/TermsOfUsePage.tsx index c13e37a4ae..9519712600 100644 --- a/source/renderer/app/containers/profile/TermsOfUsePage.tsx +++ b/source/renderer/app/containers/profile/TermsOfUsePage.tsx @@ -5,8 +5,6 @@ import TopBarLayout from '../../components/layout/TopBarLayout'; import TermsOfUseForm from '../../components/profile/terms-of-use/TermsOfUseForm'; import type { InjectedProps } from '../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class TermsOfUsePage extends Component { static defaultProps = { actions: null, @@ -44,4 +42,4 @@ class TermsOfUsePage extends Component { } } -export default TermsOfUsePage; +export default inject('stores', 'actions')(observer(TermsOfUsePage)); diff --git a/source/renderer/app/containers/settings/Settings.tsx b/source/renderer/app/containers/settings/Settings.tsx index 6b75455be4..5a14f2d0cc 100644 --- a/source/renderer/app/containers/settings/Settings.tsx +++ b/source/renderer/app/containers/settings/Settings.tsx @@ -7,8 +7,6 @@ import Layout from '../MainLayout'; import { buildRoute } from '../../utils/routing'; import type { InjectedContainerProps } from '../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class Settings extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -58,4 +56,4 @@ class Settings extends Component { } } -export default Settings; +export default inject('stores', 'actions')(observer(Settings)); diff --git a/source/renderer/app/containers/settings/categories/DisplaySettingsPage.tsx b/source/renderer/app/containers/settings/categories/DisplaySettingsPage.tsx index 557bab8bb6..385073bb20 100644 --- a/source/renderer/app/containers/settings/categories/DisplaySettingsPage.tsx +++ b/source/renderer/app/containers/settings/categories/DisplaySettingsPage.tsx @@ -3,8 +3,6 @@ import { inject, observer } from 'mobx-react'; import DisplaySettings from '../../../components/settings/categories/DisplaySettings'; import type { InjectedProps } from '../../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class DisplaySettingsPage extends Component { static defaultProps = { actions: null, @@ -22,4 +20,4 @@ class DisplaySettingsPage extends Component { } } -export default DisplaySettingsPage; +export default inject('stores', 'actions')(observer(DisplaySettingsPage)); diff --git a/source/renderer/app/containers/settings/categories/GeneralSettingsPage.tsx b/source/renderer/app/containers/settings/categories/GeneralSettingsPage.tsx index b601513620..7891604d81 100644 --- a/source/renderer/app/containers/settings/categories/GeneralSettingsPage.tsx +++ b/source/renderer/app/containers/settings/categories/GeneralSettingsPage.tsx @@ -3,8 +3,6 @@ import { observer, inject } from 'mobx-react'; import GeneralSettings from '../../../components/settings/categories/GeneralSettings'; import type { InjectedProps } from '../../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class GeneralSettingsPage extends Component { static defaultProps = { actions: null, @@ -40,4 +38,4 @@ class GeneralSettingsPage extends Component { } } -export default GeneralSettingsPage; +export default inject('stores', 'actions')(observer(GeneralSettingsPage)); diff --git a/source/renderer/app/containers/settings/categories/StakePoolsSettingsPage.tsx b/source/renderer/app/containers/settings/categories/StakePoolsSettingsPage.tsx index 7572ce5562..4dbb7a04e6 100644 --- a/source/renderer/app/containers/settings/categories/StakePoolsSettingsPage.tsx +++ b/source/renderer/app/containers/settings/categories/StakePoolsSettingsPage.tsx @@ -3,8 +3,6 @@ import { inject, observer } from 'mobx-react'; import StakePoolsSettings from '../../../components/settings/categories/StakePoolsSettings'; import type { InjectedProps } from '../../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class StakePoolsSettingsPage extends Component { static defaultProps = { actions: null, @@ -18,11 +16,8 @@ class StakePoolsSettingsPage extends Component { render() { const { stores, actions } = this.props; - const { - smashServerUrl, - smashServerUrlError, - smashServerLoading, - } = stores.staking; + const { smashServerUrl, smashServerUrlError, smashServerLoading } = + stores.staking; const { isSynced, syncPercentage } = stores.networkStatus; const { openExternalLink } = stores.app; const { resetSmashServerError } = actions.staking; @@ -41,4 +36,4 @@ class StakePoolsSettingsPage extends Component { } } -export default StakePoolsSettingsPage; +export default inject('stores', 'actions')(observer(StakePoolsSettingsPage)); diff --git a/source/renderer/app/containers/settings/categories/SupportSettingsPage.tsx b/source/renderer/app/containers/settings/categories/SupportSettingsPage.tsx index 1742b3842d..55337bc6b2 100644 --- a/source/renderer/app/containers/settings/categories/SupportSettingsPage.tsx +++ b/source/renderer/app/containers/settings/categories/SupportSettingsPage.tsx @@ -16,8 +16,6 @@ const messages = defineMessages({ }, }); -@inject('stores', 'actions') -@observer class SupportSettingsPage extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -79,4 +77,4 @@ class SupportSettingsPage extends Component { } } -export default SupportSettingsPage; +export default inject('stores', 'actions')(observer(SupportSettingsPage)); diff --git a/source/renderer/app/containers/settings/categories/TermsOfUseSettingsPage.tsx b/source/renderer/app/containers/settings/categories/TermsOfUseSettingsPage.tsx index a60c01b2f9..70f35f8ef5 100644 --- a/source/renderer/app/containers/settings/categories/TermsOfUseSettingsPage.tsx +++ b/source/renderer/app/containers/settings/categories/TermsOfUseSettingsPage.tsx @@ -3,8 +3,6 @@ import { observer, inject } from 'mobx-react'; import TermsOfUseSettings from '../../../components/settings/categories/TermsOfUseSettings'; import type { InjectedProps } from '../../../types/injectedPropsType'; -@inject('stores') -@observer class TermsOfUseSettingsPage extends Component { static defaultProps = { actions: null, @@ -23,4 +21,4 @@ class TermsOfUseSettingsPage extends Component { } } -export default TermsOfUseSettingsPage; +export default inject('stores')(observer(TermsOfUseSettingsPage)); diff --git a/source/renderer/app/containers/settings/categories/WalletsSettingsPage.tsx b/source/renderer/app/containers/settings/categories/WalletsSettingsPage.tsx index 021a9ea528..d0dcc85f49 100644 --- a/source/renderer/app/containers/settings/categories/WalletsSettingsPage.tsx +++ b/source/renderer/app/containers/settings/categories/WalletsSettingsPage.tsx @@ -3,8 +3,6 @@ import { inject, observer } from 'mobx-react'; import WalletsSettings from '../../../components/settings/categories/WalletsSettings'; import type { InjectedProps } from '../../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class WalletsSettingsPage extends Component { static defaultProps = { actions: null, @@ -44,4 +42,4 @@ class WalletsSettingsPage extends Component { } } -export default WalletsSettingsPage; +export default inject('stores', 'actions')(observer(WalletsSettingsPage)); diff --git a/source/renderer/app/containers/splash/SplashNetworkPage.tsx b/source/renderer/app/containers/splash/SplashNetworkPage.tsx index 276ae1a892..e332be7822 100644 --- a/source/renderer/app/containers/splash/SplashNetworkPage.tsx +++ b/source/renderer/app/containers/splash/SplashNetworkPage.tsx @@ -5,15 +5,13 @@ import SplashNetworkFlight from '../../components/splash/SplashNetworkFlight'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer class SplashNetworkPage extends Component { static defaultProps = { actions: null, stores: {}, }; - render() { + render(): React.ReactNode { const { networkStatus: networkStatusActions } = this.props.actions; const { openExternalLink } = this.props.stores.app; @@ -30,4 +28,4 @@ class SplashNetworkPage extends Component { } } -export default SplashNetworkPage; +export default inject('stores', 'actions')(observer(SplashNetworkPage)); diff --git a/source/renderer/app/containers/staking/DelegationCenterPage.tsx b/source/renderer/app/containers/staking/DelegationCenterPage.tsx index 9b93ccaa6c..479801d2cb 100644 --- a/source/renderer/app/containers/staking/DelegationCenterPage.tsx +++ b/source/renderer/app/containers/staking/DelegationCenterPage.tsx @@ -22,8 +22,6 @@ const initialState = { selectedList: null, }; -@inject('actions', 'stores') -@observer class DelegationCenterPage extends Component { static defaultProps = { stores: null, @@ -128,4 +126,4 @@ class DelegationCenterPage extends Component { } } -export default DelegationCenterPage; +export default inject('actions', 'stores')(observer(DelegationCenterPage)); diff --git a/source/renderer/app/containers/staking/RedeemItnRewardsContainer.tsx b/source/renderer/app/containers/staking/RedeemItnRewardsContainer.tsx index 6c16fd19a6..31231c6aa2 100644 --- a/source/renderer/app/containers/staking/RedeemItnRewardsContainer.tsx +++ b/source/renderer/app/containers/staking/RedeemItnRewardsContainer.tsx @@ -11,8 +11,6 @@ import type { InjectedProps } from '../../types/injectedPropsType'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer class RedeemItnRewardsContainer extends Component { static defaultProps = { actions: null, @@ -39,11 +37,8 @@ class RedeemItnRewardsContainer extends Component { render() { const { stores, actions } = this.props; const { allWallets } = stores.wallets; - const { - redeemStep, - isSubmittingReedem, - isCalculatingReedemFees, - } = stores.staking; + const { redeemStep, isSubmittingReedem, isCalculatingReedemFees } = + stores.staking; const { isSynced } = stores.networkStatus; const { onRedeemStart, closeRedeemDialog } = actions.staking; if (!redeemStep) return null; @@ -66,4 +61,4 @@ class RedeemItnRewardsContainer extends Component { } } -export default RedeemItnRewardsContainer; +export default inject('stores', 'actions')(observer(RedeemItnRewardsContainer)); diff --git a/source/renderer/app/containers/staking/StakePoolsListPage.tsx b/source/renderer/app/containers/staking/StakePoolsListPage.tsx index 2ac5d09dc5..ebfbafbd03 100644 --- a/source/renderer/app/containers/staking/StakePoolsListPage.tsx +++ b/source/renderer/app/containers/staking/StakePoolsListPage.tsx @@ -13,8 +13,6 @@ import { type Props = InjectedProps & WithAnalyticsTrackerProps; -@inject('stores', 'actions') -@observer class StakePoolsListPage extends Component { static defaultProps = { actions: null, @@ -52,15 +50,8 @@ class StakePoolsListPage extends Component { }; render() { - const { - uiDialogs, - staking, - app, - networkStatus, - profile, - wallets, - analytics, - } = this.props.stores; + const { uiDialogs, staking, app, networkStatus, profile, wallets } = + this.props.stores; const { currentTheme, currentLocale } = profile; const { isSynced } = networkStatus; const { @@ -113,4 +104,6 @@ class StakePoolsListPage extends Component { } } -export default withAnalytics(StakePoolsListPage); +export default withAnalytics( + inject('stores', 'actions')(observer(StakePoolsListPage)) +); diff --git a/source/renderer/app/containers/staking/Staking.tsx b/source/renderer/app/containers/staking/Staking.tsx index 3b8b538eb3..6c2b32bf72 100644 --- a/source/renderer/app/containers/staking/Staking.tsx +++ b/source/renderer/app/containers/staking/Staking.tsx @@ -14,8 +14,6 @@ import { IS_STAKING_INFO_PAGE_AVAILABLE } from '../../config/stakingConfig'; type Props = InjectedContainerProps; -@inject('stores', 'actions') -@observer class Staking extends Component { static defaultProps = { actions: null, @@ -82,12 +80,8 @@ class Staking extends Component { stores: { app, staking, networkStatus, uiDialogs }, children, } = this.props; - const { - isSynced, - syncPercentage, - isAlonzoPending, - isAlonzoActivated, - } = networkStatus; + const { isSynced, syncPercentage, isAlonzoPending, isAlonzoActivated } = + networkStatus; const { isStakingDelegationCountdown } = staking; const shouldShowInfoTab = isAlonzoPending || isAlonzoActivated; const isDelegationWizardOpen = uiDialogs.isOpen( @@ -126,4 +120,4 @@ class Staking extends Component { } } -export default Staking; +export default inject('stores', 'actions')(observer(Staking)); diff --git a/source/renderer/app/containers/staking/StakingCountdownPage.tsx b/source/renderer/app/containers/staking/StakingCountdownPage.tsx index 747416d04d..327b2a48e4 100644 --- a/source/renderer/app/containers/staking/StakingCountdownPage.tsx +++ b/source/renderer/app/containers/staking/StakingCountdownPage.tsx @@ -13,8 +13,6 @@ const messages = defineMessages({ }); type Props = InjectedProps; -@inject('stores', 'actions') -@observer class StakingCountdownPage extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -47,4 +45,4 @@ class StakingCountdownPage extends Component { } } -export default StakingCountdownPage; +export default inject('stores', 'actions')(observer(StakingCountdownPage)); diff --git a/source/renderer/app/containers/staking/StakingEpochsPage.tsx b/source/renderer/app/containers/staking/StakingEpochsPage.tsx index f8e55b21c9..f789d8f2ca 100644 --- a/source/renderer/app/containers/staking/StakingEpochsPage.tsx +++ b/source/renderer/app/containers/staking/StakingEpochsPage.tsx @@ -7,8 +7,6 @@ import CURRENT_EPOCHS from '../../config/stakingCurrentEpoch.dummy.json'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer class StakingEpochsPage extends Component { static defaultProps = { actions: null, @@ -32,4 +30,4 @@ class StakingEpochsPage extends Component { } } -export default StakingEpochsPage; +export default inject('stores', 'actions')(observer(StakingEpochsPage)); diff --git a/source/renderer/app/containers/staking/StakingInfoPage.tsx b/source/renderer/app/containers/staking/StakingInfoPage.tsx index a831a2122d..1c96ea3889 100644 --- a/source/renderer/app/containers/staking/StakingInfoPage.tsx +++ b/source/renderer/app/containers/staking/StakingInfoPage.tsx @@ -5,8 +5,6 @@ import type { InjectedProps } from '../../types/injectedPropsType'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer class StakingInfoPage extends Component { static defaultProps = { actions: null, @@ -31,4 +29,4 @@ class StakingInfoPage extends Component { } } -export default StakingInfoPage; +export default inject('stores', 'actions')(observer(StakingInfoPage)); diff --git a/source/renderer/app/containers/staking/StakingRewardsPage.tsx b/source/renderer/app/containers/staking/StakingRewardsPage.tsx index 0ea2ce5511..a65ba233ef 100644 --- a/source/renderer/app/containers/staking/StakingRewardsPage.tsx +++ b/source/renderer/app/containers/staking/StakingRewardsPage.tsx @@ -15,8 +15,6 @@ const messages = defineMessages({ }); type Props = InjectedProps; -@inject('stores', 'actions') -@observer class StakingRewardsPage extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -69,4 +67,4 @@ class StakingRewardsPage extends Component { } } -export default StakingRewardsPage; +export default inject('stores', 'actions')(observer(StakingRewardsPage)); diff --git a/source/renderer/app/containers/staking/dialogs/DelegationSetupWizardDialogContainer.tsx b/source/renderer/app/containers/staking/dialogs/DelegationSetupWizardDialogContainer.tsx index 41e4819678..106b692a0e 100644 --- a/source/renderer/app/containers/staking/dialogs/DelegationSetupWizardDialogContainer.tsx +++ b/source/renderer/app/containers/staking/dialogs/DelegationSetupWizardDialogContainer.tsx @@ -44,8 +44,6 @@ type State = { }; type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class DelegationSetupWizardDialogContainer extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -165,20 +163,10 @@ class DelegationSetupWizardDialogContainer extends Component { }; render() { - const { - activeStep, - selectedWalletId, - selectedPoolId, - stakePoolJoinFee, - } = this.state; - const { - app, - staking, - wallets, - profile, - networkStatus, - hardwareWallets, - } = this.props.stores; + const { activeStep, selectedWalletId, selectedPoolId, stakePoolJoinFee } = + this.state; + const { app, staking, wallets, profile, networkStatus, hardwareWallets } = + this.props.stores; const { futureEpoch } = networkStatus; const { currentTheme, currentLocale } = profile; const { @@ -301,4 +289,7 @@ class DelegationSetupWizardDialogContainer extends Component { } } -export default DelegationSetupWizardDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(DelegationSetupWizardDialogContainer)); diff --git a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/NoWalletsContainer.tsx b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/NoWalletsContainer.tsx index 00476d0132..c1528c8d1a 100644 --- a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/NoWalletsContainer.tsx +++ b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/NoWalletsContainer.tsx @@ -8,8 +8,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class NoWalletsContainer extends Component { static defaultProps = DefaultProps; @@ -27,4 +25,4 @@ class NoWalletsContainer extends Component { } } -export default NoWalletsContainer; +export default inject('stores', 'actions')(observer(NoWalletsContainer)); diff --git a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/RedemptionUnavailableContainer.tsx b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/RedemptionUnavailableContainer.tsx index aa71a9c4b6..f6adc0f01c 100644 --- a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/RedemptionUnavailableContainer.tsx +++ b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/RedemptionUnavailableContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class RedemptionUnavailableContainer extends Component { static defaultProps = DefaultProps; @@ -24,4 +22,7 @@ class RedemptionUnavailableContainer extends Component { } } -export default RedemptionUnavailableContainer; +export default inject( + 'stores', + 'actions' +)(observer(RedemptionUnavailableContainer)); diff --git a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step1ConfigurationContainer.tsx b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step1ConfigurationContainer.tsx index 9dd4faa992..a268d95251 100644 --- a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step1ConfigurationContainer.tsx +++ b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step1ConfigurationContainer.tsx @@ -31,8 +31,6 @@ const messages = defineMessages({ }, }); -@inject('stores', 'actions') -@observer class Step1ConfigurationContainer extends Component { static defaultProps = DefaultProps; hasEnoughAdaToCoverFees = (walletAmount?: BigNumber) => { @@ -63,16 +61,11 @@ class Step1ConfigurationContainer extends Component { const { actions, stores, onClose } = this.props; const { app, staking, wallets } = stores; const { allWallets } = wallets; - const { - redeemWallet, - isCalculatingReedemFees, - redeemRecoveryPhrase, - } = staking; + const { redeemWallet, isCalculatingReedemFees, redeemRecoveryPhrase } = + staking; const { openExternalLink } = app; - const { - onConfigurationContinue, - onCalculateRedeemWalletFees, - } = actions.staking; + const { onConfigurationContinue, onCalculateRedeemWalletFees } = + actions.staking; const selectedWalletId = get(redeemWallet, 'id', null); const selectedWallet: Wallet | null | undefined = allWallets.find( (current: Wallet) => current && current.id === selectedWalletId @@ -102,4 +95,7 @@ class Step1ConfigurationContainer extends Component { } } -export default Step1ConfigurationContainer; +export default inject( + 'stores', + 'actions' +)(observer(Step1ConfigurationContainer)); diff --git a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step2ConfirmationContainer.tsx b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step2ConfirmationContainer.tsx index 07284576c2..2377d18264 100644 --- a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step2ConfirmationContainer.tsx +++ b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step2ConfirmationContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class Step2ConfirmationContainer extends Component { static defaultProps = DefaultProps; @@ -36,4 +34,7 @@ class Step2ConfirmationContainer extends Component { } } -export default Step2ConfirmationContainer; +export default inject( + 'stores', + 'actions' +)(observer(Step2ConfirmationContainer)); diff --git a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step3ResultContainer.tsx b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step3ResultContainer.tsx index 6747fd4325..93cd8cea1a 100644 --- a/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step3ResultContainer.tsx +++ b/source/renderer/app/containers/staking/dialogs/redeem-itn-rewards/Step3ResultContainer.tsx @@ -8,19 +8,13 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class Step3ResultContainer extends Component { static defaultProps = DefaultProps; render() { const { onBack, onClose, stores, actions } = this.props; - const { - redeemWallet, - transactionFees, - redeemedRewards, - redeemSuccess, - } = stores.staking; + const { redeemWallet, transactionFees, redeemedRewards, redeemSuccess } = + stores.staking; const { onResultContinue } = actions.staking; if (!redeemWallet) throw new Error('Redeem wallet required'); @@ -42,4 +36,4 @@ class Step3ResultContainer extends Component { } } -export default Step3ResultContainer; +export default inject('stores', 'actions')(observer(Step3ResultContainer)); diff --git a/source/renderer/app/containers/static/AboutDialog.tsx b/source/renderer/app/containers/static/AboutDialog.tsx index 3b7eb36ab2..1db1c2ba04 100644 --- a/source/renderer/app/containers/static/AboutDialog.tsx +++ b/source/renderer/app/containers/static/AboutDialog.tsx @@ -7,8 +7,6 @@ import type { InjectedDialogContainerProps } from '../../types/injectedPropsType type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class AboutDialog extends Component { static defaultProps = { actions: null, @@ -46,4 +44,4 @@ class AboutDialog extends Component { } } -export default AboutDialog; +export default inject('stores', 'actions')(observer(AboutDialog)); diff --git a/source/renderer/app/containers/status/DaedalusDiagnosticsDialog.tsx b/source/renderer/app/containers/status/DaedalusDiagnosticsDialog.tsx index acd57096cc..0ce010d345 100644 --- a/source/renderer/app/containers/status/DaedalusDiagnosticsDialog.tsx +++ b/source/renderer/app/containers/status/DaedalusDiagnosticsDialog.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck import React, { Component } from 'react'; import { inject, observer } from 'mobx-react'; import ReactModal from 'react-modal'; @@ -8,8 +9,6 @@ import { buildSystemInfo } from '../../utils/buildSystemInfo'; type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class DaedalusDiagnosticsDialog extends Component { static defaultProps = { actions: null, @@ -131,4 +130,4 @@ class DaedalusDiagnosticsDialog extends Component { } } -export default DaedalusDiagnosticsDialog; +export default inject('stores', 'actions')(observer(DaedalusDiagnosticsDialog)); diff --git a/source/renderer/app/containers/voting/VotingRegistrationPage.tsx b/source/renderer/app/containers/voting/VotingRegistrationPage.tsx index 365dc7b390..c545647ec7 100644 --- a/source/renderer/app/containers/voting/VotingRegistrationPage.tsx +++ b/source/renderer/app/containers/voting/VotingRegistrationPage.tsx @@ -14,8 +14,6 @@ import { VotingFooterLinks } from '../../components/voting/VotingFooterLinks'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer class VotingRegistrationPage extends Component { static defaultProps = { actions: null, @@ -91,4 +89,4 @@ class VotingRegistrationPage extends Component { } } -export default VotingRegistrationPage; +export default inject('stores', 'actions')(observer(VotingRegistrationPage)); diff --git a/source/renderer/app/containers/voting/dialogs/VotingRegistrationDialogContainer.tsx b/source/renderer/app/containers/voting/dialogs/VotingRegistrationDialogContainer.tsx index 9c1e9797b9..a604f0f5c1 100644 --- a/source/renderer/app/containers/voting/dialogs/VotingRegistrationDialogContainer.tsx +++ b/source/renderer/app/containers/voting/dialogs/VotingRegistrationDialogContainer.tsx @@ -49,8 +49,6 @@ type State = { transactionFeeError: string | Node | null; }; -@inject('stores', 'actions') -@observer class VotingRegistrationDialogContainer extends Component { static contextTypes = { intl: intlShape.isRequired, @@ -145,18 +143,10 @@ class VotingRegistrationDialogContainer extends Component { }; render() { - const { - selectedWalletId, - transactionFee, - transactionFeeError, - } = this.state; - const { - wallets, - staking, - voting, - app, - hardwareWallets, - } = this.props.stores; + const { selectedWalletId, transactionFee, transactionFeeError } = + this.state; + const { wallets, staking, voting, app, hardwareWallets } = + this.props.stores; const { closeConfirmationDialog, saveAsPDF } = this.props.actions.voting; const { all } = wallets; const { stakePools, getStakePoolById } = staking; @@ -241,22 +231,13 @@ class VotingRegistrationDialogContainer extends Component { } async _handleCalculateTransactionFee() { - const { - transactions, - addresses, - app, - wallets, - hardwareWallets, - voting, - } = this.props.stores; + const { transactions, addresses, app, wallets, hardwareWallets, voting } = + this.props.stores; const { calculateTransactionFee } = transactions; const { getAddressesByWalletId } = addresses; const { getWalletById } = wallets; - const { - selectCoins, - initiateTransaction, - updateTxSignRequest, - } = hardwareWallets; + const { selectCoins, initiateTransaction, updateTxSignRequest } = + hardwareWallets; const { prepareVotingData } = voting; const amount = formattedAmountToLovelace( `${VOTING_REGISTRATION_FEE_CALCULATION_AMOUNT}` @@ -328,4 +309,7 @@ class VotingRegistrationDialogContainer extends Component { } } -export default VotingRegistrationDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(VotingRegistrationDialogContainer)); diff --git a/source/renderer/app/containers/wallet/PaperWalletCreateCertificatePage.tsx b/source/renderer/app/containers/wallet/PaperWalletCreateCertificatePage.tsx index 78715d84b6..fa4d54025f 100644 --- a/source/renderer/app/containers/wallet/PaperWalletCreateCertificatePage.tsx +++ b/source/renderer/app/containers/wallet/PaperWalletCreateCertificatePage.tsx @@ -19,8 +19,6 @@ type State = { showConfirmationDialog: boolean; }; -@inject('actions', 'stores') -@observer class PaperWalletCreateCertificatePage extends Component { static defaultProps = { actions: null, @@ -175,4 +173,7 @@ class PaperWalletCreateCertificatePage extends Component { }; } -export default PaperWalletCreateCertificatePage; +export default inject( + 'actions', + 'stores' +)(observer(PaperWalletCreateCertificatePage)); diff --git a/source/renderer/app/containers/wallet/TransferFundsPage.tsx b/source/renderer/app/containers/wallet/TransferFundsPage.tsx index c82fd1ef9f..1bd3380f40 100644 --- a/source/renderer/app/containers/wallet/TransferFundsPage.tsx +++ b/source/renderer/app/containers/wallet/TransferFundsPage.tsx @@ -6,8 +6,6 @@ import type { InjectedProps } from '../../types/injectedPropsType'; type Props = InjectedProps; -@inject('actions', 'stores') -@observer class TransferFundsPage extends Component { static defaultProps = { actions: null, @@ -18,11 +16,8 @@ class TransferFundsPage extends Component { const { actions, stores } = this.props; const { wallets: walletsActions } = actions; const { wallets: walletsStore } = stores; - const { - transferFundsNextStep, - transferFundsPrevStep, - transferFundsClose, - } = walletsActions; + const { transferFundsNextStep, transferFundsPrevStep, transferFundsClose } = + walletsActions; const { transferFundsStep } = walletsStore; if (!transferFundsStep) return null; let Container; @@ -43,4 +38,4 @@ class TransferFundsPage extends Component { } } -export default TransferFundsPage; +export default inject('actions', 'stores')(observer(TransferFundsPage)); diff --git a/source/renderer/app/containers/wallet/Wallet.tsx b/source/renderer/app/containers/wallet/Wallet.tsx index 767df04818..218e3ff205 100644 --- a/source/renderer/app/containers/wallet/Wallet.tsx +++ b/source/renderer/app/containers/wallet/Wallet.tsx @@ -12,8 +12,6 @@ import type { NavDropdownProps } from '../../components/navigation/Navigation'; type Props = InjectedContainerProps; -@inject('stores', 'actions') -@observer class Wallet extends Component { static defaultProps = { actions: null, @@ -70,11 +68,8 @@ class Wallet extends Component { ); } - const { - hasNotification, - } = walletSettings.getWalletsRecoveryPhraseVerificationData( - activeWallet.id - ); + const { hasNotification } = + walletSettings.getWalletsRecoveryPhraseVerificationData(activeWallet.id); const { isRestoring, isLegacy, @@ -117,4 +112,4 @@ class Wallet extends Component { } } -export default Wallet; +export default inject('stores', 'actions')(observer(Wallet)); diff --git a/source/renderer/app/containers/wallet/WalletAddPage.tsx b/source/renderer/app/containers/wallet/WalletAddPage.tsx index 5b1c30a83b..18b507ed94 100644 --- a/source/renderer/app/containers/wallet/WalletAddPage.tsx +++ b/source/renderer/app/containers/wallet/WalletAddPage.tsx @@ -19,8 +19,6 @@ import WalletImportDialogContainer from './dialogs/WalletImportDialogContainer'; type Props = InjectedProps; -@inject('actions', 'stores') -@observer class WalletAddPage extends Component { static defaultProps = { actions: null, @@ -111,4 +109,4 @@ class WalletAddPage extends Component { } } -export default WalletAddPage; +export default inject('actions', 'stores')(observer(WalletAddPage)); diff --git a/source/renderer/app/containers/wallet/WalletReceivePage.tsx b/source/renderer/app/containers/wallet/WalletReceivePage.tsx old mode 100755 new mode 100644 index 9128b75213..03d3995add --- a/source/renderer/app/containers/wallet/WalletReceivePage.tsx +++ b/source/renderer/app/containers/wallet/WalletReceivePage.tsx @@ -27,8 +27,6 @@ type State = { addressToShare?: WalletAddress | null | undefined; }; -@inject('stores', 'actions') -@observer class WalletReceivePage extends Component { static defaultProps = { actions: null, @@ -180,13 +178,8 @@ class WalletReceivePage extends Component { render() { const { actions, stores } = this.props; - const { - uiDialogs, - addresses, - sidebar, - hardwareWallets, - walletSettings, - } = stores; + const { uiDialogs, addresses, sidebar, hardwareWallets, walletSettings } = + stores; const { activeWallet } = this; const { addressToShare } = this.state; const { toggleSubMenus } = actions.sidebar; @@ -199,10 +192,8 @@ class WalletReceivePage extends Component { checkIsTrezorByWalletId, } = hardwareWallets; const { getLocalWalletDataById } = walletSettings; - const localWalletData: - | WalletLocalData - | null - | undefined = getLocalWalletDataById(activeWallet ? activeWallet.id : ''); + const localWalletData: WalletLocalData | null | undefined = + getLocalWalletDataById(activeWallet ? activeWallet.id : ''); const { showUsedAddresses } = localWalletData || {}; // Guard against potential null values if (!activeWallet) @@ -272,4 +263,4 @@ class WalletReceivePage extends Component { } } -export default WalletReceivePage; +export default inject('stores', 'actions')(observer(WalletReceivePage)); diff --git a/source/renderer/app/containers/wallet/WalletSendPage.tsx b/source/renderer/app/containers/wallet/WalletSendPage.tsx old mode 100755 new mode 100644 index f7509247ae..a98075a508 --- a/source/renderer/app/containers/wallet/WalletSendPage.tsx +++ b/source/renderer/app/containers/wallet/WalletSendPage.tsx @@ -26,8 +26,6 @@ type State = { confirmationDialogData: ConfirmationDialogData; }; -@inject('stores', 'actions') -@observer class WalletSendPage extends Component { static defaultProps = { actions: null, @@ -45,38 +43,30 @@ class WalletSendPage extends Component { isHardwareWallet: boolean; selectedAssets?: ApiTokens; }) => { - const { - walletId, - address, - amount, - isHardwareWallet, - selectedAssets, - } = params; + const { walletId, address, amount, isHardwareWallet, selectedAssets } = + params; if (isHardwareWallet) { - const coinSelection: CoinSelectionsResponse = await this.props.stores.hardwareWallets.selectCoins( - { + const coinSelection: CoinSelectionsResponse = + await this.props.stores.hardwareWallets.selectCoins({ walletId, address, amount, assets: selectedAssets, - } - ); + }); return { fee: coinSelection.fee, coinSelection, }; } - const { - fee, - minimumAda, - } = await this.props.stores.transactions.calculateTransactionFee({ - walletId, - address, - amount, - assets: selectedAssets, - }); + const { fee, minimumAda } = + await this.props.stores.transactions.calculateTransactionFee({ + walletId, + address, + amount, + assets: selectedAssets, + }); return { fee, @@ -197,4 +187,6 @@ class WalletSendPage extends Component { } } -export default withAnalytics(WalletSendPage); +export default withAnalytics( + inject('stores', 'actions')(observer(WalletSendPage)) +); diff --git a/source/renderer/app/containers/wallet/WalletSettingsPage.tsx b/source/renderer/app/containers/wallet/WalletSettingsPage.tsx index ea80837ae2..2eaf3c721a 100644 --- a/source/renderer/app/containers/wallet/WalletSettingsPage.tsx +++ b/source/renderer/app/containers/wallet/WalletSettingsPage.tsx @@ -22,8 +22,6 @@ import VerticalFlexContainer from '../../components/layout/VerticalFlexContainer type Props = InjectedProps; -@inject('stores', 'actions') -@observer class WalletSettingsPage extends Component { static defaultProps = { actions: null, @@ -194,4 +192,4 @@ class WalletSettingsPage extends Component { } } -export default WalletSettingsPage; +export default inject('stores', 'actions')(observer(WalletSettingsPage)); diff --git a/source/renderer/app/containers/wallet/WalletSummaryPage.tsx b/source/renderer/app/containers/wallet/WalletSummaryPage.tsx old mode 100755 new mode 100644 index 5dd71ccf91..d3ad949e20 --- a/source/renderer/app/containers/wallet/WalletSummaryPage.tsx +++ b/source/renderer/app/containers/wallet/WalletSummaryPage.tsx @@ -33,8 +33,6 @@ type OpenAssetSettingsDialogArgs = { asset: AssetToken; }; -@inject('stores', 'actions') -@observer class WalletSummaryPage extends Component { static defaultProps = { actions: null, @@ -95,11 +93,8 @@ class WalletSummaryPage extends Component { } = stores; const { all, getAsset, favorites } = assets; const { isInternalAddress } = addresses; - const { - onOpenAssetSend, - onCopyAssetParam, - onToggleFavorite, - } = actions.assets; + const { onOpenAssetSend, onCopyAssetParam, onToggleFavorite } = + actions.assets; const { openExternalLink, environment: { network }, @@ -208,4 +203,6 @@ class WalletSummaryPage extends Component { } } -export default withAnalytics(WalletSummaryPage); +export default withAnalytics( + inject('stores', 'actions')(observer(WalletSummaryPage)) +); diff --git a/source/renderer/app/containers/wallet/WalletTransactionsPage.tsx b/source/renderer/app/containers/wallet/WalletTransactionsPage.tsx old mode 100755 new mode 100644 index a398c45b14..95914b3c66 --- a/source/renderer/app/containers/wallet/WalletTransactionsPage.tsx +++ b/source/renderer/app/containers/wallet/WalletTransactionsPage.tsx @@ -7,8 +7,6 @@ import { WALLET_ASSETS_ENABLED } from '../../config/walletsConfig'; type Props = InjectedProps; -@inject('stores', 'actions') -@observer class WalletTransactionsPage extends Component { render() { const { actions, stores } = this.props; @@ -80,4 +78,4 @@ class WalletTransactionsPage extends Component { } } -export default WalletTransactionsPage; +export default inject('stores', 'actions')(observer(WalletTransactionsPage)); diff --git a/source/renderer/app/containers/wallet/WalletUtxoPage.tsx b/source/renderer/app/containers/wallet/WalletUtxoPage.tsx index 591b59cd9e..2c15362538 100644 --- a/source/renderer/app/containers/wallet/WalletUtxoPage.tsx +++ b/source/renderer/app/containers/wallet/WalletUtxoPage.tsx @@ -10,8 +10,6 @@ import { type Props = InjectedProps; -@inject('stores', 'actions') -@observer class WalletUtxoPage extends Component { static defaultProps = { actions: null, @@ -53,4 +51,4 @@ class WalletUtxoPage extends Component { } } -export default WalletUtxoPage; +export default inject('stores', 'actions')(observer(WalletUtxoPage)); diff --git a/source/renderer/app/containers/wallet/dialogs/WalletBackupDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/WalletBackupDialogContainer.tsx index cf4331f8a2..534569d7bf 100644 --- a/source/renderer/app/containers/wallet/dialogs/WalletBackupDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/WalletBackupDialogContainer.tsx @@ -5,8 +5,6 @@ import type { InjectedDialogContainerProps } from '../../../types/injectedPropsT type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class WalletBackupDialogContainer extends Component { static defaultProps = { actions: null, @@ -75,4 +73,7 @@ class WalletBackupDialogContainer extends Component { } } -export default WalletBackupDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(WalletBackupDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/WalletConnectDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/WalletConnectDialogContainer.tsx index b212627510..aa852e3a69 100644 --- a/source/renderer/app/containers/wallet/dialogs/WalletConnectDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/WalletConnectDialogContainer.tsx @@ -7,8 +7,6 @@ type Props = InjectedStoresProps & { onClose: (...args: Array) => any; }; -@inject('stores') -@observer class WalletConnectDialogContainer extends Component { static defaultProps = { stores: null, @@ -40,4 +38,4 @@ class WalletConnectDialogContainer extends Component { } } -export default WalletConnectDialogContainer; +export default inject('stores')(observer(WalletConnectDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/WalletCreateDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/WalletCreateDialogContainer.tsx index 8d34c7bb3e..6536bb2eba 100644 --- a/source/renderer/app/containers/wallet/dialogs/WalletCreateDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/WalletCreateDialogContainer.tsx @@ -15,8 +15,6 @@ function CreateWalletAbortConfirmation() { return
    Are you sure
    ; } -@inject('stores', 'actions') -@observer class WalletCreateDialogContainer extends Component { static defaultProps = { actions: null, @@ -46,10 +44,8 @@ class WalletCreateDialogContainer extends Component { } onContinue = () => { - const { - createWalletChangeStep, - createWalletClose, - } = this.props.actions.wallets; + const { createWalletChangeStep, createWalletClose } = + this.props.actions.wallets; if (this.currentStep !== null) { if (this.currentStep < CREATE_WALLET_STEPS.length - 1) { @@ -74,10 +70,8 @@ class WalletCreateDialogContainer extends Component { onAbort = () => this.props.actions.wallets.createWalletAbort.trigger(); render() { - const { - createWalletStep, - createWalletShowAbortConfirmation, - } = this.props.stores.wallets; + const { createWalletStep, createWalletShowAbortConfirmation } = + this.props.stores.wallets; if (createWalletStep === null) { return null; @@ -101,4 +95,7 @@ class WalletCreateDialogContainer extends Component { } } -export default WalletCreateDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(WalletCreateDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/WalletCreateDialogContainerOld.tsx b/source/renderer/app/containers/wallet/dialogs/WalletCreateDialogContainerOld.tsx index 1c9ebbae24..c8b9477a9a 100644 --- a/source/renderer/app/containers/wallet/dialogs/WalletCreateDialogContainerOld.tsx +++ b/source/renderer/app/containers/wallet/dialogs/WalletCreateDialogContainerOld.tsx @@ -6,8 +6,6 @@ import type { InjectedDialogContainerProps } from '../../../types/injectedPropsT type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class WalletCreateDialogContainer extends Component { static defaultProps = { actions: null, @@ -34,4 +32,7 @@ class WalletCreateDialogContainer extends Component { } } -export default WalletCreateDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(WalletCreateDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.tsx index 89f40db715..d8872b3ba3 100644 --- a/source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.tsx @@ -6,8 +6,6 @@ import WalletSelectImportStepContainer from './wallet-import/WalletSelectImportS type Props = InjectedProps; -@inject('stores', 'actions') -@observer class WalletImportDialogContainer extends Component { static defaultProps = { actions: null, @@ -49,4 +47,7 @@ class WalletImportDialogContainer extends Component { } } -export default WalletImportDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(WalletImportDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/WalletRestoreDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/WalletRestoreDialogContainer.tsx index c26e4aca0a..410ce0da24 100644 --- a/source/renderer/app/containers/wallet/dialogs/WalletRestoreDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/WalletRestoreDialogContainer.tsx @@ -10,8 +10,6 @@ import ConfirmationDialog from '../../../components/wallet/wallet-restore/widget type Props = InjectedProps; -@inject('stores', 'actions') -@observer class WalletRestoreContainer extends Component { static defaultProps = { actions: null, @@ -29,10 +27,8 @@ class WalletRestoreContainer extends Component { render() { const { stores, actions } = this.props; - const { - restoreWalletStep, - restoreWalletShowAbortConfirmation, - } = stores.wallets; + const { restoreWalletStep, restoreWalletShowAbortConfirmation } = + stores.wallets; const { restoreWalletClose, restoreWalletCancelClose, @@ -63,4 +59,4 @@ class WalletRestoreContainer extends Component { } } -export default WalletRestoreContainer; +export default inject('stores', 'actions')(observer(WalletRestoreContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/WalletRestoreDialogContainerOld.tsx b/source/renderer/app/containers/wallet/dialogs/WalletRestoreDialogContainerOld.tsx index 32f50f1012..e457d303fc 100644 --- a/source/renderer/app/containers/wallet/dialogs/WalletRestoreDialogContainerOld.tsx +++ b/source/renderer/app/containers/wallet/dialogs/WalletRestoreDialogContainerOld.tsx @@ -7,8 +7,6 @@ import { isValidMnemonic } from '../../../../../common/config/crypto/decrypt'; type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class WalletRestoreDialogContainer extends Component { static defaultProps = { actions: null, @@ -73,4 +71,7 @@ class WalletRestoreDialogContainer extends Component { } } -export default WalletRestoreDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(WalletRestoreDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/CompletionDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/CompletionDialogContainer.tsx index b0c16b1ffd..2e259ee863 100644 --- a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/CompletionDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/CompletionDialogContainer.tsx @@ -6,8 +6,6 @@ import { ADDRESS_COPY_NOTIFICATION_SMALL_DURATION } from '../../../../config/tim type Props = InjectedDialogContainerProps; -@inject('stores') -@observer class CompletionDialogContainer extends Component { static defaultProps = { actions: null, @@ -43,4 +41,4 @@ class CompletionDialogContainer extends Component { } } -export default CompletionDialogContainer; +export default inject('stores')(observer(CompletionDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/InstructionsDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/InstructionsDialogContainer.tsx index 14badd20dc..d6b27fa702 100644 --- a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/InstructionsDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/InstructionsDialogContainer.tsx @@ -10,8 +10,6 @@ import type { FileDialogRequestParams } from '../../../../../../common/types/fil type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class InstructionsDialogContainer extends Component { static defaultProps = { actions: null, @@ -20,10 +18,8 @@ class InstructionsDialogContainer extends Component { onClose: () => {}, }; onPrint = async () => { - const { - currentDateFormat, - currentTimeFormatShort, - } = this.props.stores.profile; + const { currentDateFormat, currentTimeFormatShort } = + this.props.stores.profile; const date = moment(); const formattedDate = date.format(currentDateFormat); const formattedTime = date.format(currentTimeFormatShort); @@ -76,4 +72,7 @@ class InstructionsDialogContainer extends Component { } } -export default InstructionsDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(InstructionsDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/PrintDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/PrintDialogContainer.tsx index 09ead7c112..fa9bbd495e 100644 --- a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/PrintDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/PrintDialogContainer.tsx @@ -5,8 +5,6 @@ import type { InjectedDialogContainerProps } from '../../../../types/injectedPro type Props = InjectedDialogContainerProps; -@inject('actions') -@observer class PrintDialogContainer extends Component { static defaultProps = { actions: null, @@ -25,4 +23,4 @@ class PrintDialogContainer extends Component { } } -export default PrintDialogContainer; +export default inject('actions')(observer(PrintDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/SecuringPasswordDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/SecuringPasswordDialogContainer.tsx index 379364df87..ddd14f6339 100644 --- a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/SecuringPasswordDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/SecuringPasswordDialogContainer.tsx @@ -5,8 +5,6 @@ import type { InjectedDialogContainerProps } from '../../../../types/injectedPro type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class SecuringPasswordDialogContainer extends Component { static defaultProps = { actions: null, @@ -36,4 +34,7 @@ class SecuringPasswordDialogContainer extends Component { } } -export default SecuringPasswordDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(SecuringPasswordDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/VerificationDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/VerificationDialogContainer.tsx index 2df67f190c..37eac3a4af 100644 --- a/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/VerificationDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/paper-wallet-certificate/VerificationDialogContainer.tsx @@ -6,8 +6,6 @@ import type { InjectedDialogContainerProps } from '../../../../types/injectedPro type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class VerificationDialogContainer extends Component { static defaultProps = { actions: null, @@ -21,10 +19,8 @@ class VerificationDialogContainer extends Component { render() { const { wallets } = this.props.stores; - const { - walletCertificateRecoveryPhrase, - additionalMnemonicWords, - } = wallets; + const { walletCertificateRecoveryPhrase, additionalMnemonicWords } = + wallets; if (!walletCertificateRecoveryPhrase || !additionalMnemonicWords) { throw new Error( @@ -44,4 +40,7 @@ class VerificationDialogContainer extends Component { } } -export default VerificationDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(VerificationDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/send-confirmation/DialogContentWithAssets.tsx b/source/renderer/app/containers/wallet/dialogs/send-confirmation/DialogContentWithAssets.tsx index ae404379d9..8d95beb263 100644 --- a/source/renderer/app/containers/wallet/dialogs/send-confirmation/DialogContentWithAssets.tsx +++ b/source/renderer/app/containers/wallet/dialogs/send-confirmation/DialogContentWithAssets.tsx @@ -2,7 +2,7 @@ import React, { Fragment } from 'react'; import compose from 'lodash/fp/compose'; import SVGInline from 'react-svg-inline'; import { observer } from 'mobx-react'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { injectIntl, FormattedHTMLMessage } from 'react-intl'; import questionMarkIcon from '../../../../assets/images/question-mark.inline.svg'; import Asset from '../../../../components/assets/Asset'; diff --git a/source/renderer/app/containers/wallet/dialogs/send-confirmation/PasswordInput.tsx b/source/renderer/app/containers/wallet/dialogs/send-confirmation/PasswordInput.tsx index 252f5cbb74..13c0a5e711 100644 --- a/source/renderer/app/containers/wallet/dialogs/send-confirmation/PasswordInput.tsx +++ b/source/renderer/app/containers/wallet/dialogs/send-confirmation/PasswordInput.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React from 'react'; import { observer } from 'mobx-react'; -import { Input } from 'react-polymorph/lib/components/Input'; -import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; +import { Input } from '@react-polymorph/components/Input'; +import { InputSkin } from '@react-polymorph/skins/simple/InputSkin'; import { doTermsNeedAcceptance } from './helpers'; import { PasswordInputProps as Props } from './types'; import HardwareWalletStatus from '../../../../components/hardware-wallet/HardwareWalletStatus'; diff --git a/source/renderer/app/containers/wallet/dialogs/send-confirmation/SendConfirmation.view.tsx b/source/renderer/app/containers/wallet/dialogs/send-confirmation/SendConfirmation.view.tsx index 37fddf1d35..ebcee4c74e 100644 --- a/source/renderer/app/containers/wallet/dialogs/send-confirmation/SendConfirmation.view.tsx +++ b/source/renderer/app/containers/wallet/dialogs/send-confirmation/SendConfirmation.view.tsx @@ -1,9 +1,10 @@ +// @ts-nocheck import React from 'react'; import compose from 'lodash/fp/compose'; import { observer } from 'mobx-react'; import { FormattedHTMLMessage, injectIntl } from 'react-intl'; -import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; -import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Checkbox } from '@react-polymorph/components/Checkbox'; +import { CheckboxSkin } from '@react-polymorph/skins/simple/CheckboxSkin'; import { DialogContentWithAssets } from './DialogContentWithAssets'; import { DialogContentWithoutAssets } from './DialogContentWithoutAssets'; import { ConfirmationError } from './Error'; diff --git a/source/renderer/app/containers/wallet/dialogs/settings/ChangeSpendingPasswordDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/settings/ChangeSpendingPasswordDialogContainer.tsx index e099b2728f..1d94283f3a 100644 --- a/source/renderer/app/containers/wallet/dialogs/settings/ChangeSpendingPasswordDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/settings/ChangeSpendingPasswordDialogContainer.tsx @@ -9,8 +9,6 @@ type Props = { actions: any | ActionsMap; }; -@inject('actions', 'stores') -@observer class ChangeSpendingPasswordDialogContainer extends Component { static defaultProps = { actions: null, @@ -63,4 +61,7 @@ class ChangeSpendingPasswordDialogContainer extends Component { } } -export default ChangeSpendingPasswordDialogContainer; +export default inject( + 'actions', + 'stores' +)(observer(ChangeSpendingPasswordDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/settings/DeleteWalletDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/settings/DeleteWalletDialogContainer.tsx index 0f43bffa7d..e0c3e873de 100644 --- a/source/renderer/app/containers/wallet/dialogs/settings/DeleteWalletDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/settings/DeleteWalletDialogContainer.tsx @@ -38,8 +38,6 @@ const messages: WalletSettingRemoveMessages = defineMessages({ }, }); -@inject('actions', 'stores') -@observer class DeleteWalletDialogContainer extends Component { static defaultProps = { actions: null, @@ -100,4 +98,7 @@ class DeleteWalletDialogContainer extends Component { } } -export default DeleteWalletDialogContainer; +export default inject( + 'actions', + 'stores' +)(observer(DeleteWalletDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/settings/ExportWalletToFileDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/settings/ExportWalletToFileDialogContainer.tsx index c97ce987a6..1b1b510265 100644 --- a/source/renderer/app/containers/wallet/dialogs/settings/ExportWalletToFileDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/settings/ExportWalletToFileDialogContainer.tsx @@ -8,8 +8,6 @@ import type { InjectedDialogContainerProps } from '../../../../types/injectedPro type Props = InjectedDialogContainerProps; -@inject('stores', 'actions') -@observer class ExportWalletToFileDialogContainer extends Component { static defaultProps = { actions: null, @@ -63,4 +61,7 @@ class ExportWalletToFileDialogContainer extends Component { } } -export default ExportWalletToFileDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(ExportWalletToFileDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/settings/PublicKeyDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/settings/PublicKeyDialogContainer.tsx index 62ca05728e..47d76af170 100644 --- a/source/renderer/app/containers/wallet/dialogs/settings/PublicKeyDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/settings/PublicKeyDialogContainer.tsx @@ -6,8 +6,6 @@ import ICOPublicKeyDialog from '../../../../components/wallet/settings/ICOPublic type Props = InjectedProps; -@inject('actions', 'stores') -@observer class PublicKeyDialogContainer extends Component { static defaultProps = { actions: null, @@ -52,4 +50,4 @@ class PublicKeyDialogContainer extends Component { } } -export default PublicKeyDialogContainer; +export default inject('actions', 'stores')(observer(PublicKeyDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/settings/PublicKeyQRCodeDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/settings/PublicKeyQRCodeDialogContainer.tsx index 92eed7c666..e9006c2fcd 100644 --- a/source/renderer/app/containers/wallet/dialogs/settings/PublicKeyQRCodeDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/settings/PublicKeyQRCodeDialogContainer.tsx @@ -31,8 +31,6 @@ const walletMessages: Record = defineMessages({ }); type Props = InjectedProps; -@inject('actions', 'stores') -@observer class PublicKeyQRCodeDialogContainer extends Component { static defaultProps = { actions: null, @@ -111,4 +109,7 @@ class PublicKeyQRCodeDialogContainer extends Component { } } -export default PublicKeyQRCodeDialogContainer; +export default inject( + 'actions', + 'stores' +)(observer(PublicKeyQRCodeDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/settings/UndelegateWalletDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/settings/UndelegateWalletDialogContainer.tsx index 17a496dccc..a73aafd25e 100644 --- a/source/renderer/app/containers/wallet/dialogs/settings/UndelegateWalletDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/settings/UndelegateWalletDialogContainer.tsx @@ -18,8 +18,6 @@ type State = { stakePoolQuitFee: DelegationCalculateFeeResponse | null | undefined; }; -@inject('actions', 'stores') -@observer class UndelegateWalletDialogContainer extends Component { static defaultProps = { actions: null, @@ -94,13 +92,8 @@ class UndelegateWalletDialogContainer extends Component { render() { const { actions, stores, onExternalLinkClick } = this.props; - const { - wallets, - staking, - networkStatus, - profile, - hardwareWallets, - } = stores; + const { wallets, staking, networkStatus, profile, hardwareWallets } = + stores; const { futureEpoch } = networkStatus; const { currentLocale } = profile; const { @@ -121,10 +114,8 @@ class UndelegateWalletDialogContainer extends Component { if (!walletToBeUndelegated) return null; const isTrezor = checkIsTrezorByWalletId(walletToBeUndelegated.id); const { name: walletName } = walletToBeUndelegated; - const { - lastDelegatedStakePoolId, - delegatedStakePoolId, - } = walletToBeUndelegated; + const { lastDelegatedStakePoolId, delegatedStakePoolId } = + walletToBeUndelegated; const stakePoolId = lastDelegatedStakePoolId || delegatedStakePoolId || ''; if ( @@ -189,4 +180,7 @@ class UndelegateWalletDialogContainer extends Component { } } -export default UndelegateWalletDialogContainer; +export default inject( + 'actions', + 'stores' +)(observer(UndelegateWalletDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/settings/UnpairWalletDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/settings/UnpairWalletDialogContainer.tsx index 9b82e1d4ad..012c854923 100644 --- a/source/renderer/app/containers/wallet/dialogs/settings/UnpairWalletDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/settings/UnpairWalletDialogContainer.tsx @@ -26,8 +26,6 @@ const messages: WalletSettingRemoveMessages = defineMessages({ }, }); -@inject('actions', 'stores') -@observer class UnpairWalletDialogContainer extends Component { static defaultProps = { actions: null, @@ -89,4 +87,7 @@ class UnpairWalletDialogContainer extends Component { } } -export default UnpairWalletDialogContainer; +export default inject( + 'actions', + 'stores' +)(observer(UnpairWalletDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/settings/WalletRecoveryPhraseContainer.tsx b/source/renderer/app/containers/wallet/dialogs/settings/WalletRecoveryPhraseContainer.tsx index 98df76c250..631296a2cb 100644 --- a/source/renderer/app/containers/wallet/dialogs/settings/WalletRecoveryPhraseContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/settings/WalletRecoveryPhraseContainer.tsx @@ -10,8 +10,6 @@ import { } from '../../../../config/cryptoConfig'; import type { InjectedProps as Props } from '../../../../types/injectedPropsType'; -@inject('stores', 'actions') -@observer class WalletRecoveryPhraseContainer extends Component { static defaultProps = { actions: null, @@ -65,4 +63,7 @@ class WalletRecoveryPhraseContainer extends Component { } } -export default WalletRecoveryPhraseContainer; +export default inject( + 'stores', + 'actions' +)(observer(WalletRecoveryPhraseContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/transfer-funds/TransferFundsStep1Container.tsx b/source/renderer/app/containers/wallet/dialogs/transfer-funds/TransferFundsStep1Container.tsx index ff391f88da..ad550fdc58 100644 --- a/source/renderer/app/containers/wallet/dialogs/transfer-funds/TransferFundsStep1Container.tsx +++ b/source/renderer/app/containers/wallet/dialogs/transfer-funds/TransferFundsStep1Container.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class TransferFundsStep1Container extends Component { static defaultProps = DefaultProps; @@ -48,4 +46,7 @@ class TransferFundsStep1Container extends Component { } } -export default TransferFundsStep1Container; +export default inject( + 'stores', + 'actions' +)(observer(TransferFundsStep1Container)); diff --git a/source/renderer/app/containers/wallet/dialogs/transfer-funds/TransferFundsStep2Container.tsx b/source/renderer/app/containers/wallet/dialogs/transfer-funds/TransferFundsStep2Container.tsx index 3ffcf35d53..953ded189d 100644 --- a/source/renderer/app/containers/wallet/dialogs/transfer-funds/TransferFundsStep2Container.tsx +++ b/source/renderer/app/containers/wallet/dialogs/transfer-funds/TransferFundsStep2Container.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class TransferFundsStep2Container extends Component { static defaultProps = DefaultProps; onClose = () => { @@ -78,4 +76,7 @@ class TransferFundsStep2Container extends Component { } } -export default TransferFundsStep2Container; +export default inject( + 'stores', + 'actions' +)(observer(TransferFundsStep2Container)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-create/ConfigDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-create/ConfigDialogContainer.tsx index a794099a6e..8c89940561 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-create/ConfigDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-create/ConfigDialogContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class ConfigDialogContainer extends Component { static defaultProps = DefaultProps; @@ -18,4 +16,4 @@ class ConfigDialogContainer extends Component { } } -export default ConfigDialogContainer; +export default inject('stores', 'actions')(observer(ConfigDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-create/HashDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-create/HashDialogContainer.tsx index 8594abcd97..79b7a7a978 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-create/HashDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-create/HashDialogContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class HashDialogContainer extends Component { static defaultProps = DefaultProps; @@ -18,4 +16,4 @@ class HashDialogContainer extends Component { } } -export default HashDialogContainer; +export default inject('stores', 'actions')(observer(HashDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-create/InstructionsDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-create/InstructionsDialogContainer.tsx index 27ebd4496b..8f6fa758fa 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-create/InstructionsDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-create/InstructionsDialogContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class InstructionsDialogContainer extends Component { static defaultProps = DefaultProps; @@ -24,4 +22,7 @@ class InstructionsDialogContainer extends Component { } } -export default InstructionsDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(InstructionsDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-create/MnemonicsDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-create/MnemonicsDialogContainer.tsx index 2ed716479b..52ee648f70 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-create/MnemonicsDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-create/MnemonicsDialogContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class MnemonicsDialogContainer extends Component { static defaultProps = DefaultProps; @@ -25,4 +23,4 @@ class MnemonicsDialogContainer extends Component { } } -export default MnemonicsDialogContainer; +export default inject('stores', 'actions')(observer(MnemonicsDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-create/TemplateDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-create/TemplateDialogContainer.tsx index a06229884c..6d55eb3b4a 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-create/TemplateDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-create/TemplateDialogContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class InstructionsDialogContainer extends Component { static defaultProps = DefaultProps; @@ -25,4 +23,7 @@ class InstructionsDialogContainer extends Component { } } -export default InstructionsDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(InstructionsDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-create/ValidateDialogContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-create/ValidateDialogContainer.tsx index 347e522a97..0192514a4c 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-create/ValidateDialogContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-create/ValidateDialogContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class ValidateDialogContainer extends Component { static defaultProps = DefaultProps; @@ -25,4 +23,4 @@ class ValidateDialogContainer extends Component { } } -export default ValidateDialogContainer; +export default inject('stores', 'actions')(observer(ValidateDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-import/WalletFileImportStepContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-import/WalletFileImportStepContainer.tsx index 4e5ae5ce31..076abd8358 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-import/WalletFileImportStepContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-import/WalletFileImportStepContainer.tsx @@ -8,8 +8,6 @@ import type { ImportFromOption } from '../../../../types/walletExportTypes'; type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class WalletFileImportStepContainer extends Component { static defaultProps = DefaultProps; onSelectExportSourcePath = (params: { importFrom: ImportFromOption }) => { @@ -53,4 +51,7 @@ class WalletFileImportStepContainer extends Component { } } -export default WalletFileImportStepContainer; +export default inject( + 'stores', + 'actions' +)(observer(WalletFileImportStepContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-import/WalletSelectImportStepContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-import/WalletSelectImportStepContainer.tsx index b870981099..70b3ddd98e 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-import/WalletSelectImportStepContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-import/WalletSelectImportStepContainer.tsx @@ -14,8 +14,6 @@ type State = { existingWalletsCount: number; }; -@inject('stores', 'actions') -@observer class WalletSelectImportStepContainer extends Component { static defaultProps = DefaultProps; state = { @@ -33,11 +31,8 @@ class WalletSelectImportStepContainer extends Component { render() { const { onClose, onContinue, stores } = this.props; const { walletMigration, app } = stores; - const { - exportedWallets, - pendingImportWalletsCount, - isRestorationRunning, - } = walletMigration; + const { exportedWallets, pendingImportWalletsCount, isRestorationRunning } = + walletMigration; const { openExternalLink } = app; let walletsCount = this.state.existingWalletsCount + pendingImportWalletsCount; @@ -67,4 +62,7 @@ class WalletSelectImportStepContainer extends Component { } } -export default WalletSelectImportStepContainer; +export default inject( + 'stores', + 'actions' +)(observer(WalletSelectImportStepContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepConfigurationContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepConfigurationContainer.tsx index c74e0699ee..5240433f74 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepConfigurationContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepConfigurationContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class ConfigurationDialogContainer extends Component { static defaultProps = DefaultProps; handleContinue = () => { @@ -25,15 +23,10 @@ class ConfigurationDialogContainer extends Component { const { wallets, profile } = stores; const { currentLocale } = profile; const { error } = wallets.restoreRequest; - const { - walletName, - spendingPassword, - repeatPassword, - isRestoring, - } = wallets; - const { - error: certificateError, - } = stores.wallets.getWalletRecoveryPhraseFromCertificateRequest; + const { walletName, spendingPassword, repeatPassword, isRestoring } = + wallets; + const { error: certificateError } = + stores.wallets.getWalletRecoveryPhraseFromCertificateRequest; return ( { } } -export default ConfigurationDialogContainer; +export default inject( + 'stores', + 'actions' +)(observer(ConfigurationDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepMnemonicsContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepMnemonicsContainer.tsx index 6bd6065a78..f30e80c653 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepMnemonicsContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepMnemonicsContainer.tsx @@ -28,8 +28,6 @@ import type { type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class MnemonicsDialogContainer extends Component { static defaultProps = DefaultProps; handleSetWalletMnemonics = (mnemonics: Array) => @@ -127,4 +125,4 @@ class MnemonicsDialogContainer extends Component { } } -export default MnemonicsDialogContainer; +export default inject('stores', 'actions')(observer(MnemonicsDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepSuccessContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepSuccessContainer.tsx index 15a79f0e39..a53e9c94d5 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepSuccessContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepSuccessContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class SuccessDialogContainer extends Component { static defaultProps = DefaultProps; @@ -26,4 +24,4 @@ class SuccessDialogContainer extends Component { } } -export default SuccessDialogContainer; +export default inject('stores', 'actions')(observer(SuccessDialogContainer)); diff --git a/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepWalletTypeContainer.tsx b/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepWalletTypeContainer.tsx index fe1c979181..9085286c8a 100644 --- a/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepWalletTypeContainer.tsx +++ b/source/renderer/app/containers/wallet/dialogs/wallet-restore/StepWalletTypeContainer.tsx @@ -7,8 +7,6 @@ import { InjectedDialogContainerStepDefaultProps } from '../../../../types/injec type Props = InjectedDialogContainerStepProps; const DefaultProps = InjectedDialogContainerStepDefaultProps; -@inject('stores', 'actions') -@observer class WalletTypeDialogContainer extends Component { static defaultProps = DefaultProps; handleSetWalletKind = (kind: string, param?: string) => @@ -39,4 +37,4 @@ class WalletTypeDialogContainer extends Component { } } -export default WalletTypeDialogContainer; +export default inject('stores', 'actions')(observer(WalletTypeDialogContainer)); diff --git a/source/renderer/app/domains/ApiError.ts b/source/renderer/app/domains/ApiError.ts index 8f31f289a2..0bae839ba9 100644 --- a/source/renderer/app/domains/ApiError.ts +++ b/source/renderer/app/domains/ApiError.ts @@ -1,4 +1,4 @@ -import { action, observable } from 'mobx'; +import { action, observable, makeObservable } from 'mobx'; import { camelCase, get, includes, keys, map, omit, snakeCase } from 'lodash'; import { GenericApiError } from '../api/common/errors'; import { messages } from '../api/errors'; @@ -58,13 +58,9 @@ export type ErrorType = { message?: string; }; export default class ApiError { - @observable tempError = ''; - @observable clause: boolean; - @observable forceSet = false; - @observable additionalValues: Record = {}; isFinalError = false; id: string; @@ -73,6 +69,17 @@ export default class ApiError { code: string; constructor(error: ErrorType = {}, logging?: LoggingType) { + makeObservable(this, { + tempError: observable, + clause: observable, + forceSet: observable, + additionalValues: observable, + set: action, + where: action, + inc: action, + result: action, + }); + // Construct Localizable Error const errorCode = error.code ? camelCase(error.code) : null; const localizableError = get(messages, errorCode); @@ -103,7 +110,6 @@ export default class ApiError { this._logError(logging); } - @action set( predefinedError: string, // @ts-ignore ts-migrate(1015) FIXME: Parameter cannot have question mark and initialize... Remove this comment to see the full error message @@ -142,7 +148,6 @@ export default class ApiError { return this; } - @action where(type: string, declaration: string) { if ( this.clause && @@ -168,7 +173,6 @@ export default class ApiError { return this; } - @action inc(type: string, declaration: string) { const fullMessage = get(this.values, type, ''); @@ -183,7 +187,6 @@ export default class ApiError { return this; } - @action result(fallbackError?: string) { if (this.isFinalError && !this.forceSet) return this; diff --git a/source/renderer/app/domains/Asset.ts b/source/renderer/app/domains/Asset.ts index d65bb55046..22579eaa41 100644 --- a/source/renderer/app/domains/Asset.ts +++ b/source/renderer/app/domains/Asset.ts @@ -1,37 +1,40 @@ import { pick } from 'lodash'; -import { observable, action, computed } from 'mobx'; +import { observable, action, computed, makeObservable } from 'mobx'; import type { Asset as AssetProps, AssetMetadata } from '../api/assets/types'; import { hexToString } from '../utils/strings'; export default class Asset { - @observable policyId = ''; - @observable assetName = ''; - @observable uniqueId = ''; - @observable fingerprint = ''; - @observable metadata: AssetMetadata | null | undefined; - @observable decimals: number | null | undefined; - @observable recommendedDecimals: number | null | undefined; - @computed get assetNameASCII() { return hexToString(this.assetName || ''); } constructor(props: AssetProps) { + makeObservable(this, { + policyId: observable, + assetName: observable, + uniqueId: observable, + fingerprint: observable, + metadata: observable, + decimals: observable, + recommendedDecimals: observable, + assetNameASCII: computed, + update: action, + }); + const { uniqueId } = props; Object.assign(this, props, { uniqueId, }); } - @action update(props: Partial) { const { uniqueId } = props; Object.assign( diff --git a/source/renderer/app/domains/News.ts b/source/renderer/app/domains/News.ts index a1c1315960..446fb8518c 100644 --- a/source/renderer/app/domains/News.ts +++ b/source/renderer/app/domains/News.ts @@ -1,4 +1,4 @@ -import { observable, computed, runInAction } from 'mobx'; +import { observable, computed, runInAction, makeObservable } from 'mobx'; import { get, filter, orderBy, includes } from 'lodash'; import semver from 'semver'; import type { NewsTarget, NewsType } from '../api/news/types'; @@ -39,25 +39,15 @@ export type NewsTypesStateType = { }; class News { - @observable id: number; - @observable title: string; - @observable content: string; - @observable target: NewsTarget; - @observable action: NewsAction; - @observable date: number; - @observable type: NewsType; - @observable read: boolean; - @observable color: IncidentColor | null | undefined; - @observable repeatOnStartup: boolean | null | undefined; constructor(data: { @@ -72,17 +62,40 @@ class News { color?: IncidentColor | null | undefined; repeatOnStartup?: boolean | null | undefined; }) { + makeObservable(this, { + id: observable, + title: observable, + content: observable, + target: observable, + action: observable, + date: observable, + type: observable, + read: observable, + color: observable, + repeatOnStartup: observable, + }); + Object.assign(this, data); } } class NewsCollection { - @observable all: Array = []; - @observable allWithAppUpdates: Array = []; constructor(data: Array) { + makeObservable(this, { + all: observable, + allWithAppUpdates: observable, + incident: computed, + alerts: computed, + announcements: computed, + infos: computed, + unread: computed, + read: computed, + update: computed, + }); + const { version, platform } = global.environment; // Filter news by platform and versions const filteredNewsWithAppUpdates = filter(data, (newsItem) => { @@ -132,7 +145,6 @@ class NewsCollection { }); } - @computed get incident(): News | null | undefined { const incidents = filter( this.all, @@ -148,7 +160,6 @@ class NewsCollection { return null; } - @computed get alerts(): NewsTypesStateType { const alerts = filter(this.all, (item) => item.type === NewsTypes.ALERT); // Order alerts from newest to oldest @@ -161,7 +172,6 @@ class NewsCollection { }; } - @computed get announcements(): NewsTypesStateType { const announcements = filter( this.all, @@ -175,7 +185,6 @@ class NewsCollection { }; } - @computed get infos(): NewsTypesStateType { const infos = filter( this.all, @@ -189,21 +198,18 @@ class NewsCollection { }; } - @computed get unread(): Array { const unread = filter(this.all, (item) => !item.read); // Order unread from newest to oldest return orderBy(unread, 'date', 'asc'); } - @computed get read(): Array { const read = filter(this.all, (item) => item.read); // Order read from newest to oldest return orderBy(read, 'date', 'asc'); } - @computed get update(): News | null { return this.allWithAppUpdates.filter( (item) => item.type === NewsTypes.UPDATE diff --git a/source/renderer/app/domains/Profile.ts b/source/renderer/app/domains/Profile.ts index 0534ff22df..d2fed47020 100644 --- a/source/renderer/app/domains/Profile.ts +++ b/source/renderer/app/domains/Profile.ts @@ -1,17 +1,11 @@ -import { observable } from 'mobx'; +import { observable, makeObservable } from 'mobx'; export default class Profile { - @observable name: string; - @observable email: string; - @observable phoneNumber: string; - @observable passwordHash: string; - @observable passwordUpdateDate: Date; - @observable languageLocale: string; constructor(data: { @@ -22,6 +16,15 @@ export default class Profile { passwordUpdateDate: Date; languageLocale: string; }) { + makeObservable(this, { + name: observable, + email: observable, + phoneNumber: observable, + passwordHash: observable, + passwordUpdateDate: observable, + languageLocale: observable, + }); + Object.assign(this, data); } } diff --git a/source/renderer/app/domains/StakePool.ts b/source/renderer/app/domains/StakePool.ts index 9040f21e50..4ed91a08bf 100644 --- a/source/renderer/app/domains/StakePool.ts +++ b/source/renderer/app/domains/StakePool.ts @@ -1,4 +1,4 @@ -import { observable } from 'mobx'; +import { observable, makeObservable } from 'mobx'; import BigNumber from 'bignumber.js'; import type { DelegationAction } from '../api/staking/types'; @@ -31,38 +31,41 @@ export type StakePoolProps = { }; export default class StakePool { id: string; - @observable ticker: string; - @observable homepage: string; - @observable producedBlocks: number; - @observable potentialRewards: BigNumber; - @observable nonMyopicMemberRewards: number; - @observable relativeStake: BigNumber; - @observable pledge: BigNumber; - @observable cost: BigNumber; - @observable description = ''; - @observable isCharity: boolean; - @observable name = ''; - @observable profitMargin: number; - @observable ranking: number; - @observable retiring: Date | null | undefined; - @observable saturation: number; constructor(data: StakePoolProps) { + makeObservable(this, { + ticker: observable, + homepage: observable, + producedBlocks: observable, + potentialRewards: observable, + nonMyopicMemberRewards: observable, + relativeStake: observable, + pledge: observable, + cost: observable, + description: observable, + isCharity: observable, + name: observable, + profitMargin: observable, + ranking: observable, + retiring: observable, + saturation: observable, + }); + Object.assign(this, data); } } diff --git a/source/renderer/app/domains/User.ts b/source/renderer/app/domains/User.ts index 340ea9f6bc..1f7ebbffbe 100644 --- a/source/renderer/app/domains/User.ts +++ b/source/renderer/app/domains/User.ts @@ -1,13 +1,16 @@ -import { observable } from 'mobx'; +import { observable, makeObservable } from 'mobx'; import Profile from './Profile'; export default class User { - @observable id: string; - @observable profile: Profile; constructor(id: string, profile: Profile) { + makeObservable(this, { + id: observable, + profile: observable, + }); + this.id = id; this.profile = profile; } diff --git a/source/renderer/app/domains/Wallet.ts b/source/renderer/app/domains/Wallet.ts index b22020ac29..99960e593b 100644 --- a/source/renderer/app/domains/Wallet.ts +++ b/source/renderer/app/domains/Wallet.ts @@ -1,11 +1,10 @@ import { get, pick } from 'lodash'; -import { observable, computed, action } from 'mobx'; +import { observable, computed, action, makeObservable } from 'mobx'; import BigNumber from 'bignumber.js'; import type { WalletSyncState, SyncStateStatus, DelegationStatus, - WalletUnit, WalletPendingDelegations, Discovery, } from '../api/wallets/types'; @@ -128,48 +127,60 @@ export type WalletProps = { }; export default class Wallet { id = ''; - @observable addressPoolGap: number; - @observable name = ''; - @observable amount: BigNumber; - @observable availableAmount: BigNumber; - @observable reward: BigNumber; - @observable assets: WalletTokens; - @observable passwordUpdateDate: Date | null | undefined; - @observable syncState: WalletSyncState; - @observable isLegacy: boolean; - @observable delegatedStakePoolId: string | null | undefined; - @observable delegationStakePoolStatus: string | null | undefined; - @observable lastDelegatedStakePoolId: string | null | undefined; - @observable lastDelegationStakePoolStatus: string | null | undefined; - @observable pendingDelegations: WalletPendingDelegations; - @observable discovery: Discovery; - @observable hasPassword: boolean; - @observable walletNotConnected: boolean; - @observable isHardwareWallet: boolean; constructor(data: WalletProps) { + makeObservable(this, { + addressPoolGap: observable, + name: observable, + amount: observable, + availableAmount: observable, + reward: observable, + assets: observable, + passwordUpdateDate: observable, + syncState: observable, + isLegacy: observable, + delegatedStakePoolId: observable, + delegationStakePoolStatus: observable, + lastDelegatedStakePoolId: observable, + lastDelegationStakePoolStatus: observable, + pendingDelegations: observable, + discovery: observable, + hasPassword: observable, + walletNotConnected: observable, + isHardwareWallet: observable, + update: action, + hasFunds: computed, + hasAssets: computed, + isRestoring: computed, + isSyncing: computed, + isNotResponding: computed, + isRandom: computed, + isDelegating: computed, + isSequential: computed, + restorationProgress: computed, + }); + Object.assign(this, data); } - @action update(other: Wallet) { Object.assign( this, @@ -197,17 +208,14 @@ export default class Wallet { ); } - @computed get hasFunds(): boolean { return this.amount.gt(0); } - @computed get hasAssets(): boolean { return get(this, 'assets.total', []).length > 0; } - @computed get isRestoring(): boolean { return ( get(this, 'syncState.status') === WalletSyncStateStatuses.RESTORING && @@ -215,24 +223,20 @@ export default class Wallet { ); } - @computed get isSyncing(): boolean { return get(this, 'syncState.status') === WalletSyncStateStatuses.SYNCING; } - @computed get isNotResponding(): boolean { return ( get(this, 'syncState.status') === WalletSyncStateStatuses.NOT_RESPONDING ); } - @computed get isRandom(): boolean { return this.discovery === WalletDiscovery.RANDOM; } - @computed get isDelegating(): boolean { return this.lastDelegationStakePoolStatus ? this.lastDelegationStakePoolStatus === @@ -240,12 +244,10 @@ export default class Wallet { : this.delegationStakePoolStatus === WalletDelegationStatuses.DELEGATING; } - @computed get isSequential(): boolean { return this.discovery !== WalletDiscovery.RANDOM; } - @computed get restorationProgress(): number { return get(this, 'syncState.progress.quantity', 0); } diff --git a/source/renderer/app/domains/WalletAddress.ts b/source/renderer/app/domains/WalletAddress.ts index df0b64d925..0491e6bc52 100644 --- a/source/renderer/app/domains/WalletAddress.ts +++ b/source/renderer/app/domains/WalletAddress.ts @@ -1,4 +1,4 @@ -import { observable } from 'mobx'; +import { observable, makeObservable } from 'mobx'; import type { AddressStyle } from '../api/addresses/types'; type WalletAddressProps = { @@ -16,14 +16,17 @@ export const AddressStyles: { ADDRESS_ICARUS: 'Icarus', }; export default class WalletAddress { - @observable id = ''; - @observable used = false; - @observable spendingPath = "1852'/1815'/0'"; constructor(data: WalletAddressProps) { + makeObservable(this, { + id: observable, + used: observable, + spendingPath: observable, + }); + Object.assign(this, data); } } diff --git a/source/renderer/app/domains/WalletTransaction.ts b/source/renderer/app/domains/WalletTransaction.ts index 219bd37d5a..c0642d0f61 100644 --- a/source/renderer/app/domains/WalletTransaction.ts +++ b/source/renderer/app/domains/WalletTransaction.ts @@ -1,4 +1,4 @@ -import { observable } from 'mobx'; +import { observable, makeObservable } from 'mobx'; import BigNumber from 'bignumber.js'; import type { TransactionAddresses, @@ -24,39 +24,24 @@ export const TransactionTypes: EnumMap = { }; export const TransactionWithdrawal: TransactionWithdrawalType = 'self'; export class WalletTransaction { - @observable id = ''; - @observable type: TransactionType; - @observable title = ''; - @observable amount: BigNumber; - @observable fee: BigNumber; - @observable deposit: BigNumber; - @observable assets: Tokens; - @observable date: Date | null | undefined; - @observable description = ''; - @observable addresses: TransactionAddresses = { from: [], to: [], withdrawals: [], }; - @observable state: TransactionState; - @observable confirmations: number; - @observable slotNumber: number | null | undefined; - @observable epochNumber: number | null | undefined; - @observable metadata: TransactionMetadata | null | undefined; constructor(data: { @@ -76,6 +61,24 @@ export class WalletTransaction { epochNumber: number | null | undefined; metadata: TransactionMetadata | null | undefined; }) { + makeObservable(this, { + id: observable, + type: observable, + title: observable, + amount: observable, + fee: observable, + deposit: observable, + assets: observable, + date: observable, + description: observable, + addresses: observable, + state: observable, + confirmations: observable, + slotNumber: observable, + epochNumber: observable, + metadata: observable, + }); + Object.assign(this, data); } } diff --git a/source/renderer/app/features/discreet-mode/context.tsx b/source/renderer/app/features/discreet-mode/context.tsx index 752686fe4f..3bf608efaf 100644 --- a/source/renderer/app/features/discreet-mode/context.tsx +++ b/source/renderer/app/features/discreet-mode/context.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; // @ts-ignore ts-migrate(2305) FIXME: Module '"react"' has no exported member 'Node'. import type { Node } from 'react'; import { merge } from 'lodash/fp'; diff --git a/source/renderer/app/features/discreet-mode/feature.ts b/source/renderer/app/features/discreet-mode/feature.ts index 9d59a72e99..64294267fc 100644 --- a/source/renderer/app/features/discreet-mode/feature.ts +++ b/source/renderer/app/features/discreet-mode/feature.ts @@ -1,4 +1,4 @@ -import { observable, action, runInAction } from 'mobx'; +import { observable, action, runInAction, makeObservable } from 'mobx'; import { Feature } from '../../utils/mobx-features/feature'; import Request from '../../stores/lib/LocalizedRequest'; import { DiscreetModeApi } from './api'; @@ -13,6 +13,16 @@ export class DiscreetMode extends Feature { private analyticsTracker: AnalyticsTracker ) { super(); + + makeObservable(this, { + isDiscreetMode: observable, + openInDiscreetMode: observable, + getDiscreetModeSettingsRequest: observable, + setDiscreetModeSettingsRequest: observable, + toggleDiscreetMode: action, + toggleOpenInDiscreetMode: action, + }); + runInAction(() => { this.getDiscreetModeSettingsRequest = new Request( this.api.getDiscreetModeSettings @@ -23,13 +33,9 @@ export class DiscreetMode extends Feature { }); } - @observable isDiscreetMode = false; - @observable openInDiscreetMode = false; - @observable getDiscreetModeSettingsRequest: Request>; - @observable setDiscreetModeSettingsRequest: Request>; async start() { @@ -41,14 +47,13 @@ export class DiscreetMode extends Feature { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.getDiscreetModeSettingsRequest.execute(); const isDiscreetModeEnabled = this.getDiscreetModeSettingsRequest.result; - runInAction('Initialize discreet mode variables', () => { + runInAction(() => { // @ts-ignore ts-migrate(2322) FIXME: Type 'Promise' is not assignable to type ... Remove this comment to see the full error message this.openInDiscreetMode = isDiscreetModeEnabled; // @ts-ignore ts-migrate(2322) FIXME: Type 'Promise' is not assignable to type ... Remove this comment to see the full error message this.isDiscreetMode = isDiscreetModeEnabled; }); }; - @action toggleDiscreetMode = () => { this.isDiscreetMode = !this.isDiscreetMode; this.analyticsTracker.sendEvent( @@ -56,12 +61,11 @@ export class DiscreetMode extends Feature { `Turned ${this.isDiscreetMode ? 'on' : 'off'} discreet mode` ); }; - @action toggleOpenInDiscreetMode = async () => { const nextSetting = !this.openInDiscreetMode; // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.setDiscreetModeSettingsRequest.execute(nextSetting); - runInAction('Update open in discreet mode settings', () => { + runInAction(() => { this.openInDiscreetMode = nextSetting; }); this.analyticsTracker.sendEvent( diff --git a/source/renderer/app/features/discreet-mode/ui/DiscreetValue.story.tsx b/source/renderer/app/features/discreet-mode/ui/DiscreetValue.story.tsx deleted file mode 100644 index 13dca22132..0000000000 --- a/source/renderer/app/features/discreet-mode/ui/DiscreetValue.story.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { useLayoutEffect } from 'react'; -import { storiesOf } from '@storybook/react'; -import { observer } from 'mobx-react'; -import { withKnobs, boolean } from '@storybook/addon-knobs'; -import StoryDecorator from '../../../../../../storybook/stories/_support/StoryDecorator'; -import StoryProvider from '../../../../../../storybook/stories/_support/StoryProvider'; -import { - DiscreetModeFeatureProvider, - useDiscreetModeFeature, -} from '../context'; -import DiscreetValue from './DiscreetValue'; - -const Toggle = observer(({ knob }: { knob: boolean }) => { - const feature = useDiscreetModeFeature(); - useLayoutEffect(() => { - if (knob !== feature.isDiscreetMode) { - feature.toggleDiscreetMode(); - } - }, [knob, feature.isDiscreetMode]); - return null; -}); -storiesOf('Discreet Mode / Discreet Asset Amount', module) - .addDecorator(withKnobs) - .addDecorator((story) => ( - - - {story()} - - - )) - .add('Discreet mode disabled', () => ( - <> - {/* @ts-ignore ts-migrate(2741) FIXME: Property 'replacer' is missing in type '{ children... Remove this comment to see the full error message */} - 123 - - - )) - .add('Discreet mode enabled', () => ( - <> - {/* @ts-ignore ts-migrate(2741) FIXME: Property 'replacer' is missing in type '{ children... Remove this comment to see the full error message */} - 123 - - - )); diff --git a/source/renderer/app/features/discreet-mode/ui/discreet-toggle-top-bar/DiscreetToggleTopBar.tsx b/source/renderer/app/features/discreet-mode/ui/discreet-toggle-top-bar/DiscreetToggleTopBar.tsx index 928eb61516..903a700bab 100644 --- a/source/renderer/app/features/discreet-mode/ui/discreet-toggle-top-bar/DiscreetToggleTopBar.tsx +++ b/source/renderer/app/features/discreet-mode/ui/discreet-toggle-top-bar/DiscreetToggleTopBar.tsx @@ -1,7 +1,8 @@ +// @ts-nocheck import React from 'react'; import classnames from 'classnames'; import { observer } from 'mobx-react'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PopOver } from '@react-polymorph/components/PopOver'; import { injectIntl, FormattedHTMLMessage } from 'react-intl'; import styles from './DiscreetToggleTopBar.scss'; import { messages } from './DiscreetToggleTopBar.messages'; diff --git a/source/renderer/app/features/discreet-mode/ui/discreet-toggle/DiscreetModeToggle.story.tsx b/source/renderer/app/features/discreet-mode/ui/discreet-toggle/DiscreetModeToggle.story.tsx deleted file mode 100644 index ed17ac5ad1..0000000000 --- a/source/renderer/app/features/discreet-mode/ui/discreet-toggle/DiscreetModeToggle.story.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { storiesOf } from '@storybook/react'; -import { withKnobs } from '@storybook/addon-knobs'; -import { action } from '@storybook/addon-actions'; -import { DiscreetModeToggleComponent } from './DiscreetModeToggle'; - -storiesOf('Discreet Mode / Discreet Mode Toggle', module) - .addDecorator(withKnobs) - .add('Main', () => ( -
    -
    - -
    -
    - -
    -
    - )); diff --git a/source/renderer/app/i18n/locales/babeledit-project.babel b/source/renderer/app/i18n/locales/babeledit-project.babel old mode 100755 new mode 100644 diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json old mode 100755 new mode 100644 index 9fa22718af..fb62a7d528 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -262,6 +262,7 @@ "loading.screen.reportIssue.connecting.text": "Having trouble connecting to network?", "loading.screen.reportIssue.downloadLogsLinkLabel": "Download logs", "loading.screen.reportIssue.reportIssueButtonUrl": "https://iohk.zendesk.com/hc/en-us/requests/new/", + "loading.screen.syncingMessage": "Syncing blockchain data.", "loading.screen.startingCardanoDescription": "This process validates the integrity of local blockchain data.", "loading.screen.startingCardanoMessage": "Starting Cardano node", "loading.screen.stoppedCardanoMessage": "Cardano node stopped", @@ -1346,4 +1347,4 @@ "wallet.transferFunds.dialog2.total.label": "Total", "widgets.itemsDropdown.syncingLabel": "Syncing", "widgets.itemsDropdown.syncingLabelProgress": "Syncing {syncingProgress}%" -} \ No newline at end of file +} diff --git a/source/renderer/app/i18n/locales/ja-JP.json b/source/renderer/app/i18n/locales/ja-JP.json old mode 100755 new mode 100644 index a0f505a221..8480da92fe --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -263,6 +263,7 @@ "loading.screen.reportIssue.downloadLogsLinkLabel": "ログをダウンロードする", "loading.screen.reportIssue.reportIssueButtonUrl": "https://iohk.zendesk.com/hc/ja/requests/new/", "loading.screen.startingCardanoDescription": "このプロセスは、ローカルブロックチェーンデータの整合性を検証します。", + "loading.screen.syncingMessage": "ブロックチェーンデータの同期中", "loading.screen.startingCardanoMessage": "Cardanoノードを起動しています", "loading.screen.stoppedCardanoMessage": "Cardanoノードは停止しました", "loading.screen.stoppingCardanoDescription": "このプロセスはデータベースを更新するもので、数分間かかる場合があります。
    データの完全性を保つために、このプロセスが完了するまでお待ちください。", @@ -1346,4 +1347,4 @@ "wallet.transferFunds.dialog2.total.label": "合計", "widgets.itemsDropdown.syncingLabel": "同期", "widgets.itemsDropdown.syncingLabelProgress": "同期中 {syncingProgress}%" -} \ No newline at end of file +} diff --git a/source/renderer/app/i18n/translations.ts b/source/renderer/app/i18n/translations.ts index b629c7c530..4038714aff 100644 --- a/source/renderer/app/i18n/translations.ts +++ b/source/renderer/app/i18n/translations.ts @@ -1,9 +1,15 @@ -// This is essentially bulk require -const req = require.context('./locales', true, /\.json.*$/); +import defaultMessages from './locales/defaultMessages.json'; +import enUS from './locales/en-US.json'; +import jaJP from './locales/ja-JP.json'; +import whitelist_enUS from './locales/whitelist_en-US.json'; +import whitelist_jaJP from './locales/whitelist_ja-JP.json'; + +const translations = { + defaultMessages, + 'en-US': enUS, + 'ja-JP': jaJP, + 'whitelist_en-US': whitelist_enUS, + 'whitelist_ja-JP': whitelist_jaJP, +}; -const translations = {}; -req.keys().forEach((file) => { - const locale = file.replace('./', '').replace('.json', ''); - translations[locale] = req(file); -}); export default translations; diff --git a/source/renderer/app/index.tsx b/source/renderer/app/index.tsx old mode 100755 new mode 100644 diff --git a/source/renderer/app/stores/AddressesStore.ts b/source/renderer/app/stores/AddressesStore.ts index e0ad276dd5..766dcf27a4 100644 --- a/source/renderer/app/stores/AddressesStore.ts +++ b/source/renderer/app/stores/AddressesStore.ts @@ -1,5 +1,11 @@ import { has, find, last, filter, findIndex } from 'lodash'; -import { observable, computed, action, runInAction } from 'mobx'; +import { + observable, + computed, + action, + runInAction, + makeObservable, +} from 'mobx'; import Store from './lib/Store'; import CachedRequest from './lib/LocalizedCachedRequest'; import WalletAddress from '../domains/WalletAddress'; @@ -7,30 +13,48 @@ import Request from './lib/LocalizedRequest'; import LocalizableError from '../i18n/LocalizableError'; import { getStakeAddressFromStakeKey } from '../utils/crypto'; import type { Address, InspectAddressResponse } from '../api/addresses/types'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; +import { AnalyticsTracker } from '../analytics'; export default class AddressesStore extends Store { - @observable lastGeneratedAddress: WalletAddress | null | undefined = null; - @observable addressesRequests: Array<{ walletId: string; isLegacy: boolean; allRequest: CachedRequest>; }> = []; - @observable stakeAddresses: Record = {}; - @observable error: LocalizableError | null | undefined = null; // REQUESTS - @observable createByronWalletAddressRequest: Request
    = new Request( this.api.ada.createAddress ); - @observable inspectAddressRequest: Request = new Request( this.api.ada.inspectAddress ); + constructor(api: Api, actions: ActionsMap, analytics: AnalyticsTracker) { + super(api, actions, analytics); + + makeObservable(this, { + lastGeneratedAddress: observable, + addressesRequests: observable, + stakeAddresses: observable, + error: observable, + createByronWalletAddressRequest: observable, + inspectAddressRequest: observable, + all: computed, + hasAny: computed, + active: computed, + totalAvailable: computed, + stakeAddress: computed, + _getStakeAddress: action, + _refreshAddresses: action, + _resetErrors: action, + }); + } + setup() { const actions = this.actions.addresses; actions.createByronWalletAddress.listen(this._createByronWalletAddress); @@ -45,24 +69,23 @@ export default class AddressesStore extends Store { const { walletId, passphrase } = params; const accountIndex = await this.getAccountIndexByWalletId(walletId); // @ts-ignore ts-migrate(2739) FIXME: Type 'Address' is missing the following properties... Remove this comment to see the full error message - const address: WalletAddress = await this.createByronWalletAddressRequest.execute( - { + const address: WalletAddress = + await this.createByronWalletAddressRequest.execute({ addressIndex: accountIndex, passphrase, walletId, - } - ).promise; + }).promise; if (address != null) { this._refreshAddresses(); - runInAction('set last generated address and reset error', () => { + runInAction(() => { this.lastGeneratedAddress = address; this.error = null; }); } } catch (error) { - runInAction('set error', () => { + runInAction(() => { this.error = error; }); } @@ -76,7 +99,6 @@ export default class AddressesStore extends Store { return addressDetails; }; - @computed get all(): Array { const wallet = this.stores.wallets.active; if (!wallet) return []; @@ -86,7 +108,6 @@ export default class AddressesStore extends Store { return addresses || []; } - @computed get hasAny(): boolean { const wallet = this.stores.wallets.active; if (!wallet) return false; @@ -96,7 +117,6 @@ export default class AddressesStore extends Store { return addresses ? addresses.length > 0 : false; } - @computed get active(): WalletAddress | null | undefined { const wallet = this.stores.wallets.active; if (!wallet) return null; @@ -115,7 +135,6 @@ export default class AddressesStore extends Store { return last(addresses); } - @computed get totalAvailable(): number { const wallet = this.stores.wallets.active; if (!wallet) return 0; @@ -125,14 +144,12 @@ export default class AddressesStore extends Store { return addresses ? addresses.length : 0; } - @computed get stakeAddress(): string { const wallet = this.stores.wallets.active; if (!wallet) return ''; return this.stakeAddresses[wallet.id] || ''; } - @action _getStakeAddress = async (walletId: string, isLegacy: boolean) => { const hasStakeAddress = has(this.stakeAddresses, walletId); @@ -150,13 +167,12 @@ export default class AddressesStore extends Store { index: '0', }); const stakeAddress = getStakeAddressFromStakeKey(stakeKeyBech32); - runInAction('set stake address', () => { + runInAction(() => { this.stakeAddresses[walletId] = stakeAddress; }); } } }; - @action _refreshAddresses = () => { if (this.stores.networkStatus.isConnected) { const { all } = this.stores.wallets; @@ -178,7 +194,6 @@ export default class AddressesStore extends Store { } } }; - @action _resetErrors = () => { this.error = null; }; diff --git a/source/renderer/app/stores/AppStore.ts b/source/renderer/app/stores/AppStore.ts index 04d90bd00a..1c19236d2d 100644 --- a/source/renderer/app/stores/AppStore.ts +++ b/source/renderer/app/stores/AppStore.ts @@ -1,4 +1,10 @@ -import { observable, computed, action, runInAction } from 'mobx'; +import { + observable, + computed, + action, + runInAction, + makeObservable, +} from 'mobx'; import path from 'path'; import Store from './lib/Store'; import LocalizableError from '../i18n/LocalizableError'; @@ -15,20 +21,39 @@ import { getGPUStatusChannel } from '../ipc/get-gpu-status.ipc'; import { generateFileNameWithTimestamp } from '../../../common/utils/files'; import type { GpuStatus } from '../types/gpuStatus'; import type { ApplicationDialog } from '../types/applicationDialogTypes'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export default class AppStore extends Store { - @observable error: LocalizableError | null | undefined = null; - @observable isDownloadNotificationVisible = false; - @observable gpuStatus: GpuStatus | null | undefined = null; - @observable activeDialog: ApplicationDialog = null; - @observable newsFeedIsOpen = false; + constructor(api: Api, actions: ActionsMap, analytics: AnalyticsTracker) { + super(api, actions, analytics); + + makeObservable(this, { + error: observable, + isDownloadNotificationVisible: observable, + gpuStatus: observable, + activeDialog: observable, + newsFeedIsOpen: observable, + currentRoute: computed, + currentPage: computed, + _toggleNewsFeed: action, + _closeNewsFeed: action, + isSetupPage: computed, + _updateRouteLocation: action, + _updateActiveDialog: action, + _closeActiveDialog: action, + _downloadLogs: action, + _setIsDownloadingLogs: action, + }); + } + setup() { this.actions.router.goToRoute.listen(this._updateRouteLocation); this.actions.app.getGpuStatus.listen(this._getGpuStatus); @@ -66,13 +91,11 @@ export default class AppStore extends Store { showUiPartChannel.onReceive(this.showUiPart); } - @computed get currentRoute(): string { const { location } = this.stores.router; return location ? location.pathname : ''; } - @computed get currentPage(): string { return this.currentRoute.split('/').pop(); } @@ -85,7 +108,6 @@ export default class AppStore extends Store { isActiveDialog = (dialog: ApplicationDialog): boolean => { return this.activeDialog === dialog; }; - @action _toggleNewsFeed = () => { this.newsFeedIsOpen = !this.newsFeedIsOpen; @@ -93,7 +115,6 @@ export default class AppStore extends Store { this.analytics.sendEvent(EventCategories.LAYOUT, 'Opened newsfeed'); } }; - @action _closeNewsFeed = () => { this.newsFeedIsOpen = false; }; @@ -190,7 +211,6 @@ export default class AppStore extends Store { return Promise.resolve(); }; - @computed get isSetupPage(): boolean { return ( this.currentRoute === ROUTES.PROFILE.INITIAL_SETTINGS || @@ -202,11 +222,10 @@ export default class AppStore extends Store { // ===================== PRIVATE ======================= // _getGpuStatus = async () => { const gpuStatus = await getGPUStatusChannel.request(); - runInAction('get gpu status', () => { + runInAction(() => { this.gpuStatus = gpuStatus; }); }; - @action _updateRouteLocation = (options: { route: string; params?: Record | null | undefined; @@ -217,18 +236,15 @@ export default class AppStore extends Store { this.stores.router.push(newRoutePath); } }; - @action _updateActiveDialog = (currentDialog: ApplicationDialog) => { if (this.newsFeedIsOpen) { this.newsFeedIsOpen = false; } if (this.activeDialog !== currentDialog) this.activeDialog = currentDialog; }; - @action _closeActiveDialog = () => { if (this.activeDialog !== null) this.activeDialog = null; }; - @action _downloadLogs = async () => { if (this.isDownloadNotificationVisible) { return; @@ -253,7 +269,6 @@ export default class AppStore extends Store { this.actions.app.setIsDownloadingLogs.trigger(false); } }; - @action _setIsDownloadingLogs = (isDownloadNotificationVisible: boolean) => { this.isDownloadNotificationVisible = isDownloadNotificationVisible; }; diff --git a/source/renderer/app/stores/AppUpdateStore.ts b/source/renderer/app/stores/AppUpdateStore.ts index 07ee1de339..f17d1ddad2 100644 --- a/source/renderer/app/stores/AppUpdateStore.ts +++ b/source/renderer/app/stores/AppUpdateStore.ts @@ -1,9 +1,14 @@ -import { action, computed, observable, runInAction } from 'mobx'; +import { + action, + computed, + observable, + runInAction, + makeObservable, +} from 'mobx'; import { get } from 'lodash'; import semver from 'semver'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; -import NewsDomains from '../domains/News'; import { logger } from '../utils/logging'; import { requestDownloadChannel, @@ -29,66 +34,86 @@ import { DOWNLOAD_EVENT_TYPES, DOWNLOAD_STATES, } from '../../../common/config/downloadManagerConfig'; -import type { SoftwareUpdateInfo } from '../api/news/types'; +import { NewsItem, SoftwareUpdateInfo } from '../api/news/types'; import type { DownloadInfo, DownloadData, } from '../../../common/types/downloadManager.types'; import type { FormattedDownloadData } from '../utils/formatters'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; +import { AnalyticsTracker } from '../analytics'; const { version: currentVersion, platform } = global.environment; -const { News } = NewsDomains; export default class AppUpdateStore extends Store { - @observable // @ts-ignore ts-migrate(2749) FIXME: 'News' refers to a value, but is being used as a t... Remove this comment to see the full error message - availableUpdate: News | null | undefined = null; - @observable + availableUpdate: NewsItem | null | undefined = null; availableUpdateVersion = ''; - @observable isUpdateDownloading = false; - @observable isUpdateDownloaded = false; - @observable isUpdateProgressOpen = false; - @observable isAutomaticUpdateFailed = false; - @observable isUpdatePostponed = false; - @observable isWaitingToQuitDaedalus = false; - @observable installationProgress = 0; - @observable downloadInfo: DownloadInfo | null | undefined = null; - @observable downloadData: DownloadData | null | undefined = null; - @observable availableAppVersion: string | null | undefined = null; - @observable getAppAutomaticUpdateFailedRequest: Request> = new Request( this.api.localStorage.getAppAutomaticUpdateFailed ); - @observable setAppAutomaticUpdateFailedRequest: Request> = new Request( this.api.localStorage.setAppAutomaticUpdateFailed ); - @observable unsetAppAutomaticUpdateFailedRequest: Request> = new Request( this.api.localStorage.unsetAppAutomaticUpdateFailed ); - @observable getAppUpdateCompletedRequest: Request> = new Request( this.api.localStorage.getAppUpdateCompleted ); - @observable setAppUpdateCompletedRequest: Request> = new Request( this.api.localStorage.setAppUpdateCompleted ); - @observable unsetAppUpdateCompletedRequest: Request> = new Request( this.api.localStorage.unsetAppUpdateCompleted ); + constructor(api: Api, actions: ActionsMap, analytics: AnalyticsTracker) { + super(api, actions, analytics); + + makeObservable(this, { + availableUpdate: observable, + availableUpdateVersion: observable, + isUpdateDownloading: observable, + isUpdateDownloaded: observable, + isUpdateProgressOpen: observable, + isAutomaticUpdateFailed: observable, + isUpdatePostponed: observable, + isWaitingToQuitDaedalus: observable, + installationProgress: observable, + downloadInfo: observable, + downloadData: observable, + availableAppVersion: observable, + getAppAutomaticUpdateFailedRequest: observable, + setAppAutomaticUpdateFailedRequest: observable, + unsetAppAutomaticUpdateFailedRequest: observable, + getAppUpdateCompletedRequest: observable, + setAppUpdateCompletedRequest: observable, + unsetAppUpdateCompletedRequest: observable, + displayAppUpdateOverlay: computed, + displayAppUpdateNewsItem: computed, + formattedDownloadData: computed, + downloadTimeLeft: computed, + totalDownloaded: computed, + totalDownloadSize: computed, + downloadProgress: computed, + showManualUpdate: computed, + _openAppUpdateOverlay: action, + _closeAppUpdateOverlay: action, + _postponeUpdate: action, + }); + } + setup() { const actions = this.actions.appUpdate; actions.installUpdate.listen(this._installUpdate); @@ -120,7 +145,6 @@ export default class AppUpdateStore extends Store { }; // ==================== PUBLIC ================== - @computed get displayAppUpdateOverlay(): boolean { return ( !!this.availableUpdate && @@ -131,12 +155,10 @@ export default class AppUpdateStore extends Store { ); } - @computed get displayAppUpdateNewsItem(): boolean { return this.isUpdateDownloading || this.isUpdatePostponed; } - @computed get formattedDownloadData(): FormattedDownloadData { return formattedDownloadData( this.downloadData, @@ -144,27 +166,22 @@ export default class AppUpdateStore extends Store { ); } - @computed get downloadTimeLeft(): string { return this.formattedDownloadData.timeLeft; } - @computed get totalDownloaded(): string { return this.formattedDownloadData.downloaded; } - @computed get totalDownloadSize(): string { return this.formattedDownloadData.total; } - @computed get downloadProgress(): number { return this.formattedDownloadData.progress; } - @computed get showManualUpdate(): boolean { return this.isAutomaticUpdateFailed; } @@ -290,12 +307,11 @@ export default class AppUpdateStore extends Store { }); }; - _getUpdateDownloadLocalData = async (): Promise< - DownloadLocalDataMainResponse - > => - getDownloadLocalDataChannel.request({ - id: APP_UPDATE_DOWNLOAD_ID, - }); + _getUpdateDownloadLocalData = + async (): Promise => + getDownloadLocalDataChannel.request({ + id: APP_UPDATE_DOWNLOAD_ID, + }); _checkFileExists = async (): Promise => checkFileExistsChannel.request({ @@ -308,7 +324,7 @@ export default class AppUpdateStore extends Store { data, error, }: DownloadMainResponse) => { - runInAction('updates the download information', () => { + runInAction(() => { if (eventType === DOWNLOAD_EVENT_TYPES.PAUSE) { this.availableUpdate = null; this.isUpdateDownloaded = true; @@ -446,18 +462,15 @@ export default class AppUpdateStore extends Store { }); }; - @action _openAppUpdateOverlay = () => { this.isUpdateProgressOpen = true; this.isUpdatePostponed = false; }; - @action _closeAppUpdateOverlay = () => { this.isUpdateProgressOpen = false; }; - @action _postponeUpdate = () => { this.isUpdatePostponed = true; }; diff --git a/source/renderer/app/stores/AssetsStore.ts b/source/renderer/app/stores/AssetsStore.ts index bc23ef3d10..397ace2275 100644 --- a/source/renderer/app/stores/AssetsStore.ts +++ b/source/renderer/app/stores/AssetsStore.ts @@ -1,4 +1,4 @@ -import { observable, action, computed } from 'mobx'; +import { observable, action, computed, makeObservable } from 'mobx'; import { get } from 'lodash'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; @@ -6,29 +6,51 @@ import Asset from '../domains/Asset'; import { ROUTES } from '../routes-config'; import { ellipsis } from '../utils/strings'; import type { GetAssetsResponse, AssetToken } from '../api/assets/types'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; type WalletId = string; export default class AssetsStore extends Store { ASSETS_REFRESH_INTERVAL: number = 1 * 60 * 1000; // 1 minute | unit: milliseconds // REQUESTS - @observable favoritesRequest: Request> = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'AssetsStore... Remove this comment to see the full error message this.api.localStorage.getWalletTokenFavorites ); - @observable activeAsset: string | null | undefined = null; - @observable editedAsset: AssetToken | null | undefined = null; - @observable assetsRequests: Record> = {}; - @observable insertingAssetUniqueId: string | null | undefined = null; - @observable removingAssetUniqueId: string | null | undefined = null; + constructor(api: Api, actions: ActionsMap, analytics: AnalyticsTracker) { + super(api, actions, analytics); + + makeObservable(this, { + favoritesRequest: observable, + activeAsset: observable, + editedAsset: observable, + assetsRequests: observable, + insertingAssetUniqueId: observable, + removingAssetUniqueId: observable, + all: computed, + details: computed, + favorites: computed, + _onEditedAssetSet: action, + _onAssetSettingsSubmit: action, + _onEditedAssetUnset: action, + _onOpenAssetSend: action, + _onCopyAssetParam: action, + _refreshAssetsData: action, + _setActiveAsset: action, + _unsetActiveAsset: action, + _createWalletTokensRequest: action, + _onToggleFavorite: action, + }); + } + setup() { setInterval(this._refreshAssetsData, this.ASSETS_REFRESH_INTERVAL); // @ts-ignore ts-migrate(2339) FIXME: Property 'actions' does not exist on type 'AssetsS... Remove this comment to see the full error message @@ -47,7 +69,6 @@ export default class AssetsStore extends Store { } // ==================== PUBLIC ================== - @computed get all(): Array { const wallet = this.stores.wallets.active; @@ -60,7 +81,6 @@ export default class AssetsStore extends Store { return get(request, 'result.assets', []); } - @computed get details(): Record { return this.all.reduce((details, asset) => { const { policyId, assetName } = asset; @@ -72,7 +92,6 @@ export default class AssetsStore extends Store { getAsset = (policyId: string, assetName: string): Asset | null | undefined => this.details[`${policyId}${assetName}`]; - @computed get favorites(): Record { return this.favoritesRequest.result || {}; } @@ -81,11 +100,9 @@ export default class AssetsStore extends Store { _setUpFavorites = async () => { this.favoritesRequest.execute(); }; - @action _onEditedAssetSet = ({ asset }: { asset: AssetToken }) => { this.editedAsset = asset; }; - @action _onAssetSettingsSubmit = async ({ asset, decimals, @@ -114,11 +131,9 @@ export default class AssetsStore extends Store { 'Changed native token settings' ); }; - @action _onEditedAssetUnset = () => { this.editedAsset = null; }; - @action _onOpenAssetSend = ({ uniqueId }: { uniqueId: string }) => { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'AssetsSt... Remove this comment to see the full error message const { stores, actions } = this; @@ -138,7 +153,6 @@ export default class AssetsStore extends Store { }); } }; - @action _onCopyAssetParam = ({ param, fullValue, @@ -153,7 +167,6 @@ export default class AssetsStore extends Store { shortValue, }); }; - @action _refreshAssetsData = () => { if (this.stores.networkStatus.isConnected) { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'AssetsSt... Remove this comment to see the full error message @@ -168,15 +181,12 @@ export default class AssetsStore extends Store { } } }; - @action _setActiveAsset = (uniqueId: string) => { this.activeAsset = uniqueId; }; - @action _unsetActiveAsset = () => { this.activeAsset = null; }; - @action _createWalletTokensRequest = ( walletId: string ): Request => { @@ -184,7 +194,6 @@ export default class AssetsStore extends Store { this.assetsRequests[walletId] = new Request(this.api.ada.getAssets); return this.assetsRequests[walletId]; }; - @action _onToggleFavorite = async ({ uniqueId, isFavorite, diff --git a/source/renderer/app/stores/CurrencyStore.ts b/source/renderer/app/stores/CurrencyStore.ts index cbdaddb31a..8f20a30fb4 100644 --- a/source/renderer/app/stores/CurrencyStore.ts +++ b/source/renderer/app/stores/CurrencyStore.ts @@ -1,4 +1,10 @@ -import { action, observable, computed, runInAction } from 'mobx'; +import { + action, + observable, + computed, + runInAction, + makeObservable, +} from 'mobx'; import Store from './lib/Store'; import { CURRENCY_REQUEST_RATE_INTERVAL, @@ -7,26 +13,42 @@ import { getCurrencyFromCode, } from '../config/currencyConfig'; import type { Currency, LocalizedCurrency } from '../types/currencyTypes'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export default class CurrencyStore extends Store { - @observable isFetchingList = false; - @observable isFetchingRate = false; - @observable isActive = false; - @observable list: Array = []; - @observable selected: Currency | null | undefined = null; - @observable rate: number | null | undefined = null; - @observable lastFetched: Date | null | undefined = null; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. _getCurrencyRateInterval: IntervalID | null | undefined = null; + constructor(api: Api, actions: ActionsMap, analytics: AnalyticsTracker) { + super(api, actions, analytics); + + makeObservable(this, { + isFetchingList: observable, + isFetchingRate: observable, + isActive: observable, + list: observable, + selected: observable, + rate: observable, + lastFetched: observable, + localizedCurrencyList: computed, + localizedCurrency: computed, + getCurrencyList: action, + getCurrencyRate: action, + _setupCurrency: action, + _setCurrencySelected: action, + _toggleCurrencyIsActive: action, + }); + } + setup() { const { currency: currencyActions } = this.actions; currencyActions.setCurrencySelected.listen(this._setCurrencySelected); @@ -36,14 +58,12 @@ export default class CurrencyStore extends Store { } // PUBLIC - @computed get localizedCurrencyList(): Array { const { list, stores } = this; const { currentLocale } = stores.profile; return getLocalizedCurrenciesList(list, currentLocale); } - @computed get localizedCurrency(): LocalizedCurrency | null | undefined { const { selected, stores } = this; const { currentLocale } = stores.profile; @@ -51,7 +71,6 @@ export default class CurrencyStore extends Store { return getLocalizedCurrency(selected, currentLocale); } - @action getCurrencyList = async () => { this.isFetchingList = true; const list = await this.api.ada.getCurrencyList(); @@ -60,7 +79,6 @@ export default class CurrencyStore extends Store { this.isFetchingList = false; }); }; - @action getCurrencyRate = async () => { const { localizedCurrency, list } = this; @@ -96,7 +114,6 @@ export default class CurrencyStore extends Store { } }; // PRIVATE - @action _setupCurrency = async () => { // Check if the user has enabled currencies // Otherwise applies the default config @@ -118,7 +135,6 @@ export default class CurrencyStore extends Store { this.getCurrencyList(); this.getCurrencyRate(); }; - @action _setCurrencySelected = async ({ code }: { code: string }) => { const { list } = this; const selected = list.find((item) => item.code === code); @@ -135,7 +151,6 @@ export default class CurrencyStore extends Store { code ); }; - @action _toggleCurrencyIsActive = () => { this.isActive = !this.isActive; this.api.localStorage.setCurrencyIsActive(this.isActive); diff --git a/source/renderer/app/stores/HardwareWalletsStore.ts b/source/renderer/app/stores/HardwareWalletsStore.ts index a04be6b82c..2abf58d8f7 100644 --- a/source/renderer/app/stores/HardwareWalletsStore.ts +++ b/source/renderer/app/stores/HardwareWalletsStore.ts @@ -1,4 +1,10 @@ -import { observable, action, runInAction, computed } from 'mobx'; +import { + observable, + action, + runInAction, + computed, + makeObservable, +} from 'mobx'; import { get, map, find, includes, last, sortBy, filter } from 'lodash'; import semver from 'semver'; import { @@ -97,13 +103,16 @@ import type { TrezorWitness, } from '../../../common/types/hardware-wallets.types'; import { logger } from '../utils/logging'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; import { HardwareWalletDevicesType, HardwareWalletLocalData, HardwareWalletsLocalData, UnpairedHardwareWalletData, } from '../types/localDataTypes'; +import { Environment } from '../../../common/types/environment.types'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export type TxSignRequestTypes = { coinSelection: CoinSelectionsResponse; @@ -188,7 +197,7 @@ interface IdentifyAndHandleAssociatedWalletArgs { expectedWalletId?: string; } -const { network, isDev } = global.environment; +const { network, isDev }: Environment = global.environment; const hardwareWalletsNetworkConfig = getHardwareWalletsNetworkConfig(network); interface CardanoAdaAppPoller { @@ -197,11 +206,85 @@ interface CardanoAdaAppPoller { } export default class HardwareWalletsStore extends Store { - @observable + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + selectCoinsRequest: observable, + sendMoneyRequest: observable, + hardwareWalletsLocalDataRequest: observable, + setHardwareWalletLocalDataRequest: observable, + unsetHardwareWalletLocalDataRequest: observable, + hardwareWalletDevicesRequest: observable, + setHardwareWalletDeviceRequest: observable, + overrideHardwareWalletDevicesRequest: observable, + unsetHardwareWalletDeviceRequest: observable, + unsetHardwareWalletDevicesAllRequest: observable, + unsetHardwareWalletLocalDataAllRequest: observable, + hwDeviceStatus: observable, + extendedPublicKey: observable, + txSignRequest: observable, + transportDevice: observable, + txBody: observable, + isTransactionPending: observable, + isTrezorBridgeInstalled: observable, + isTransactionInitiated: observable, + activeDevicePath: observable, + unfinishedWalletTxSigning: observable, + isListeningForDevice: observable, + isConnectInitiated: observable, + isAddressVerificationInitiated: observable, + isWalletPairingInitiated: observable, + unfinishedWalletAddressVerification: observable, + isAddressDerived: observable, + isAddressChecked: observable, + isAddressCorrect: observable, + tempAddressToVerify: observable, + isExportKeyAborted: observable, + activeDelegationWalletId: observable, + activeVotingWalletId: observable, + votingData: observable, + checkTransaction: action, + resetStakePoolTransactionChecker: action, + setTransactionPendingState: action, + establishHardwareWalletConnection: action, + getCardanoAdaApp: action, + verifyAddress: action, + initiateWalletPairing: action, + resetWalletPairing: action, + showAddress: action, + setAddressVerificationCheckStatus: action, + _resetInitiatedConnection: action, + _requestExtendedPublicKey: action, + _findAssociatedWalletByExtendedPublicKey: action, + _deletePendingDeviceWithGivenPath: action, + _discardConnectedDeviceAndReInitiateTransaction: action, + _proceedWithTransactionAfterConnectingDevice: action, + _discardConnectedDeviceAndReInitiateAddressVerification: action, + _proceedWithAddressVerificationAfterConnectingDevice: action, + _storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting: + action, + _createNewWalletForRecognizedPendingDevice: action, + _identifyAndHandleAssociatedWallet: action, + _signTransactionTrezor: action, + _signTransactionLedger: action, + _changeHardwareWalletConnectionStatus: action, + resetInitializedConnection: action, + resetInitializedAddressVerification: action, + _refreshHardwareWalletsLocalData: action, + _refreshHardwareWalletDevices: action, + hardwareWalletsConnectionData: computed, + hardwareWalletDevices: computed, + }); + } + selectCoinsRequest: Request = new Request( this.api.ada.selectCoins ); - @observable sendMoneyRequest: Request = new Request( this.api.ada.createExternalTransaction ); @@ -211,92 +294,54 @@ export default class HardwareWalletsStore extends Store { constructAddressRequest: Request = new Request( this.api.ada.constructAddress ); - @observable - hardwareWalletsLocalDataRequest: Request< - HardwareWalletsLocalData - > = new Request(this.api.localStorage.getHardwareWalletsLocalData); - @observable - setHardwareWalletLocalDataRequest: Request< - HardwareWalletLocalData - > = new Request(this.api.localStorage.setHardwareWalletLocalData); - @observable + hardwareWalletsLocalDataRequest: Request = + new Request(this.api.localStorage.getHardwareWalletsLocalData); + setHardwareWalletLocalDataRequest: Request = + new Request(this.api.localStorage.setHardwareWalletLocalData); unsetHardwareWalletLocalDataRequest: Request = new Request( this.api.localStorage.unsetHardwareWalletLocalData ); - @observable hardwareWalletDevicesRequest: Request = new Request( this.api.localStorage.getHardwareWalletDevices ); - @observable - setHardwareWalletDeviceRequest: Request< - HardwareWalletLocalData - > = new Request(this.api.localStorage.setHardwareWalletDevice); - @observable - overrideHardwareWalletDevicesRequest: Request< - HardwareWalletDevicesType - > = new Request(this.api.localStorage.overrideHardwareWalletDevices); - @observable - unsetHardwareWalletDeviceRequest: Request< - HardwareWalletLocalData - > = new Request(this.api.localStorage.unsetHardwareWalletDevice); - @observable + setHardwareWalletDeviceRequest: Request = + new Request(this.api.localStorage.setHardwareWalletDevice); + overrideHardwareWalletDevicesRequest: Request = + new Request(this.api.localStorage.overrideHardwareWalletDevices); + unsetHardwareWalletDeviceRequest: Request = + new Request(this.api.localStorage.unsetHardwareWalletDevice); unsetHardwareWalletDevicesAllRequest: Request = new Request( this.api.localStorage.unsetHardwareWalletDevicesAll ); - @observable - unsetHardwareWalletLocalDataAllRequest: Request< - HardwareWalletLocalData - > = new Request(this.api.localStorage.unsetHardwareWalletLocalDataAll); - @observable + unsetHardwareWalletLocalDataAllRequest: Request = + new Request(this.api.localStorage.unsetHardwareWalletLocalDataAll); hwDeviceStatus: HwDeviceStatus = HwDeviceStatuses.CONNECTING; - @observable extendedPublicKey: | HardwareWalletExtendedPublicKeyResponse | null | undefined = null; - @observable // @ts-ignore ts-migrate(2741) FIXME: Property 'coinSelection' is missing in type '{}' b... Remove this comment to see the full error message txSignRequest: TxSignRequestTypes = {}; - @observable transportDevice: TransportDevice | null | undefined = null; - @observable txBody: string | null | undefined = null; - @observable isTransactionPending = false; - @observable isTrezorBridgeInstalled = false; - @observable isTransactionInitiated = false; - @observable activeDevicePath: string | null | undefined = null; - @observable unfinishedWalletTxSigning: string | null | undefined = null; - @observable isListeningForDevice = false; - @observable isConnectInitiated = false; - @observable isAddressVerificationInitiated = false; - @observable isWalletPairingInitiated = false; - @observable unfinishedWalletAddressVerification: WalletAddress | null | undefined = null; - @observable isAddressDerived = false; - @observable isAddressChecked = false; - @observable isAddressCorrect: boolean | null | undefined = null; - @observable // @ts-ignore ts-migrate(2739) FIXME: Type '{}' is missing the following properties from... Remove this comment to see the full error message tempAddressToVerify: TempAddressToVerify = {}; - @observable isExportKeyAborted = false; - @observable activeDelegationWalletId: string | null | undefined = null; - @observable activeVotingWalletId: string | null | undefined = null; - @observable votingData: VotingDataType | null | undefined = null; cardanoAdaAppPoller: CardanoAdaAppPoller | null; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. @@ -308,7 +353,7 @@ export default class HardwareWalletsStore extends Store { setup() { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - setup'); + logger.info('[HW-DEBUG] HWStore - setup'); const { hardwareWallets: hardwareWalletsActions } = this.actions; hardwareWalletsActions.sendMoney.listen(this._sendMoney); hardwareWalletsActions.refreshHardwareWalletsLocalData.listen( @@ -327,7 +372,7 @@ export default class HardwareWalletsStore extends Store { initTrezor = async () => { if (isHardwareWalletSupportEnabled && isTrezorEnabled) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - start trezor'); + logger.info('[HW-DEBUG] HWStore - start trezor'); await handleInitTrezorConnectChannel.request(); await this.getAvailableDevices({ isTrezor: true, @@ -336,18 +381,18 @@ export default class HardwareWalletsStore extends Store { }; initLedger = async () => { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( `[HW-DEBUG] HWStore - initLedger() | isHardwareWalletSupportEnabled=${isHardwareWalletSupportEnabled.toString()} isLedgerEnabled=${isLedgerEnabled.toString()}` ); if (isHardwareWalletSupportEnabled && isLedgerEnabled) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - start ledger'); + logger.info('[HW-DEBUG] HWStore - start ledger'); // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.hardwareWalletDevicesRequest.execute(); const storedDevices = this.hardwareWalletDevicesRequest.result; // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - storedDevices fetched'); + logger.info('[HW-DEBUG] HWStore - storedDevices fetched'); const devicesWithoutLedgers = {}; map(storedDevices, async (device) => { if (device.deviceType === DeviceTypes.TREZOR) { @@ -355,17 +400,17 @@ export default class HardwareWalletsStore extends Store { } }); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Remove all LEDGERS from LC'); + logger.info('[HW-DEBUG] HWStore - Remove all LEDGERS from LC'); // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.overrideHardwareWalletDevicesRequest.execute( devicesWithoutLedgers ); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Refresh LC'); + logger.info('[HW-DEBUG] HWStore - Refresh LC'); await this._refreshHardwareWalletsLocalData(); await this._refreshHardwareWalletDevices(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - INIT Ledger listeners'); + logger.info('[HW-DEBUG] HWStore - INIT Ledger listeners'); await handleInitLedgerConnectChannel.request(); await this.getAvailableDevices({ isTrezor: false, @@ -390,9 +435,8 @@ export default class HardwareWalletsStore extends Store { let canRun = true; let isRunning = false; - const connectedDevice = this.connectedHardwareWalletsDevices.get( - devicePath - ); + const connectedDevice = + this.connectedHardwareWalletsDevices.get(devicePath); const product = connectedDevice?.deviceType === 'ledger' @@ -445,10 +489,10 @@ export default class HardwareWalletsStore extends Store { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.hardwareWalletDevicesRequest.execute(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - getAvailableDevices'); + logger.info('[HW-DEBUG] HWStore - getAvailableDevices'); // Set all logical HW into disconnected state // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Set Hardware Wallets local data'); + logger.info('[HW-DEBUG] HWStore - Set Hardware Wallets local data'); map(this.hardwareWalletsConnectionData, async (connectedWallet) => { await this._setHardwareWalletLocalData({ walletId: connectedWallet.id, @@ -472,7 +516,7 @@ export default class HardwareWalletsStore extends Store { try { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - CHECK device'); + logger.info('[HW-DEBUG] HWStore - CHECK device'); if (device.deviceType === DeviceTypes.TREZOR) { await getHardwareWalletTransportChannel.request({ @@ -484,7 +528,7 @@ export default class HardwareWalletsStore extends Store { } catch (e) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. // eslint-disable-next-line - logger.debug(' HWStore - CHECK device Error'); + logger.info(' HWStore - CHECK device Error'); } }); await this._refreshHardwareWalletsLocalData(); @@ -542,7 +586,7 @@ export default class HardwareWalletsStore extends Store { return transaction; } catch (e) { this.setTransactionPendingState(false); - runInAction('HardwareWalletsStore:: reset Transaction verifying', () => { + runInAction(() => { this.txBody = null; this.activeDevicePath = null; this.unfinishedWalletTxSigning = null; @@ -552,21 +596,16 @@ export default class HardwareWalletsStore extends Store { } }; // Check stake pool transaction state and reset pending state when transaction is "in_ledger" - @action checkTransaction = (request: { transactionId: string; walletId: string; isVotingRegistrationTransaction: boolean; }) => { - const { - transactionId, - walletId, - isVotingRegistrationTransaction, - } = request; + const { transactionId, walletId, isVotingRegistrationTransaction } = + request; - const recentTransactionsResponse = this.stores.transactions._getTransactionsRecentRequest( - walletId - ).result; + const recentTransactionsResponse = + this.stores.transactions._getTransactionsRecentRequest(walletId).result; const recentTransactions = recentTransactionsResponse ? recentTransactionsResponse.transactions @@ -609,7 +648,6 @@ export default class HardwareWalletsStore extends Store { } } }; - @action resetStakePoolTransactionChecker = (walletId: string) => { if (this.checkTransactionTimeInterval) { clearInterval(this.checkTransactionTimeInterval); @@ -626,9 +664,8 @@ export default class HardwareWalletsStore extends Store { this.stores.wallets.goToWalletRoute(walletId); }; - @action setTransactionPendingState = (isTransactionPending: boolean) => { - runInAction('HardwareWalletsStore:: set transaction state', () => { + runInAction(() => { this.isTransactionPending = isTransactionPending; }); }; @@ -642,9 +679,9 @@ export default class HardwareWalletsStore extends Store { try { this.selectCoinsRequest.reset(); - // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message - const coinSelection: CoinSelectionsResponse = await this.selectCoinsRequest.execute( - { + const coinSelection: CoinSelectionsResponse = + // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message + await this.selectCoinsRequest.execute({ walletId, walletBalance: totalAmount, availableBalance: availableAmount.plus(reward), @@ -655,23 +692,19 @@ export default class HardwareWalletsStore extends Store { assets, }, metadata, - } - ); + }); return coinSelection; } catch (e) { - runInAction( - 'HardwareWalletsStore:: set Transaction verifying failed', - () => { - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; + }); throw e; } }; updateTxSignRequest = (coinSelection: CoinSelectionsResponse) => { - runInAction('HardwareWalletsStore:: set coin selections', () => { + runInAction(() => { this.txSignRequest = { coinSelection, }; @@ -699,29 +732,25 @@ export default class HardwareWalletsStore extends Store { delegationAction, }, }); - runInAction('HardwareWalletsStore:: set coin selections', () => { + runInAction(() => { this.txSignRequest = { coinSelection, }; }); return coinSelection; } catch (e) { - runInAction( - 'HardwareWalletsStore:: set Transaction verifying failed', - () => { - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; + }); throw e; } }; - @action establishHardwareWalletConnection = async () => { - runInAction('HardwareWalletsStore:: set HW device CONNECTING', () => { + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.CONNECTING; }); const { hardwareWalletDevices, hardwareWalletsConnectionData } = this; - logger.debug('[HW-DEBUG] HWStore - establishHardwareWalletConnection', { + logger.info('[HW-DEBUG] HWStore - establishHardwareWalletConnection', { hardwareWalletDevices: toJS(hardwareWalletDevices), hardwareWalletsConnectionData: toJS(hardwareWalletsConnectionData), activeDelegationWalletId: toJS(this.activeDelegationWalletId), @@ -754,7 +783,7 @@ export default class HardwareWalletsStore extends Store { if (activeWalletId) { // Check if device connected to wallet // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - active wallet exists'); + logger.info('[HW-DEBUG] HWStore - active wallet exists'); recognizedPairedHardwareWallet = find( hardwareWalletDevices, // @ts-ignore ts-migrate(2339) FIXME: Property 'paired' does not exist on type 'Hardware... Remove this comment to see the full error message @@ -791,15 +820,15 @@ export default class HardwareWalletsStore extends Store { !recognizedPairedHardwareWallet.disconnected ) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Establish connection:: New Transaction / Address verification initiated - Recognized device found' ); - logger.debug('[HW-DEBUG] HWStore - Set transport device 1', { + logger.info('[HW-DEBUG] HWStore - Set transport device 1', { recognizedPairedHardwareWallet: toJS( recognizedPairedHardwareWallet ), }); - runInAction('HardwareWalletsStore:: Set transport device', () => { + runInAction(() => { this.transportDevice = recognizedPairedHardwareWallet; }); // Special case when Pub key export rejected by the user and then device reconnected @@ -837,7 +866,7 @@ export default class HardwareWalletsStore extends Store { if (relatedConnectionDataDeviceType) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Connect - New Transaction / Address verification initiated - return last device' ); // @ts-ignore @@ -848,10 +877,10 @@ export default class HardwareWalletsStore extends Store { isTrezor, } ); - logger.debug('[HW-DEBUG] HWStore - Set transport device 2', { + logger.info('[HW-DEBUG] HWStore - Set transport device 2', { lastDeviceTransport: toJS(lastDeviceTransport), }); - runInAction('HardwareWalletsStore:: Set transport device', () => { + runInAction(() => { this.transportDevice = lastDeviceTransport; }); @@ -874,7 +903,7 @@ export default class HardwareWalletsStore extends Store { } // End of special case } - logger.debug('[HW-DEBUG] HWStore - return lastDeviceTransport', { + logger.info('[HW-DEBUG] HWStore - return lastDeviceTransport', { lastDeviceTransport: toJS(lastDeviceTransport), }); @@ -886,7 +915,7 @@ export default class HardwareWalletsStore extends Store { // it is triggered after flag activation "isListeningForDevice" if (lastUnpairedDevice) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - establishHardwareWalletConnection:: Start process with last UNPAIRED device' ); // Start listeners for specific (last plugged) device @@ -900,7 +929,7 @@ export default class HardwareWalletsStore extends Store { } // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - establishHardwareWalletConnection:: Listening for device' ); @@ -910,7 +939,7 @@ export default class HardwareWalletsStore extends Store { isTrezor, }); } else { - logger.debug( + logger.info( '[HW-DEBUG] HWStore - establishHardwareWalletConnection:: wait for new ledger devices' ); @@ -923,13 +952,13 @@ export default class HardwareWalletsStore extends Store { } // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Transport retrieved'); + logger.info('[HW-DEBUG] HWStore - Transport retrieved'); } else { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - establishHardwareWalletConnection:: Set device listener' ); - runInAction('HardwareWalletsStore:: set device listener', () => { + runInAction(() => { this.isListeningForDevice = true; }); return null; @@ -937,7 +966,7 @@ export default class HardwareWalletsStore extends Store { // End of Cases for wallet create / restore // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - establishHardwareWalletConnection:: start process with known transport' ); @@ -952,12 +981,9 @@ export default class HardwareWalletsStore extends Store { !DeviceModels.LEDGER_NANO_S_PLUS && !DeviceModels.LEDGER_NANO_X) ) { - runInAction( - 'HardwareWalletsStore:: set HW device CONNECTING FAILED - device not supported', - () => { - this.hwDeviceStatus = HwDeviceStatuses.UNSUPPORTED_DEVICE; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.UNSUPPORTED_DEVICE; + }); throw new Error('Device not Supported!'); } @@ -974,12 +1000,9 @@ export default class HardwareWalletsStore extends Store { ); if (!isFirmwareVersionValid) { - runInAction( - 'HardwareWalletsStore:: set HW device CONNECTING FAILED - wrong firmware', - () => { - this.hwDeviceStatus = HwDeviceStatuses.WRONG_FIRMWARE; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.WRONG_FIRMWARE; + }); throw new Error( `Firmware must be ${minFirmwareVersion} or greater!` ); @@ -987,24 +1010,25 @@ export default class HardwareWalletsStore extends Store { } // All Checks pass - mark device as connected (set transport device for this session) - logger.debug('[HW-DEBUG] HWStore - Set transport device 3', { + logger.info('[HW-DEBUG] HWStore - Set transport device 3', { transportDevice: toJS(transportDevice), }); - runInAction('HardwareWalletsStore:: set HW device CONNECTED', () => { + runInAction(() => { this.transportDevice = transportDevice; }); + logger.info('[HW-DEBUG] deviceType', deviceType); if (deviceType === DeviceTypes.TREZOR) { await this._identifyAndHandleAssociatedWallet({ path: transportDevice.path, }); } else { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - START cardano app poller'); + logger.info('[HW-DEBUG] HWStore - START cardano app poller'); // Start poller to recognize if Cardano App is launched on device const devicePath = transportDevice.path; // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - getCardanoAdaApp - from establishHardwareWalletConnection', { devicePath, @@ -1018,12 +1042,9 @@ export default class HardwareWalletsStore extends Store { this.useCardanoAppInterval(devicePath); } } else { - runInAction( - 'HardwareWalletsStore:: set HW device CONNECTING FAILED', - () => { - this.hwDeviceStatus = HwDeviceStatuses.CONNECTING_FAILED; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.CONNECTING_FAILED; + }); throw new Error('Device not found'); } @@ -1038,12 +1059,9 @@ export default class HardwareWalletsStore extends Store { } if (e.code === 'Transport_Missing' && !this.isTrezorBridgeInstalled) { - runInAction( - 'HardwareWalletsStore:: set HW device CONNECTING FAILED', - () => { - this.hwDeviceStatus = HwDeviceStatuses.TREZOR_BRIDGE_FAILURE; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.TREZOR_BRIDGE_FAILURE; + }); throw new Error('Trezor Bridge not installed!'); } @@ -1051,7 +1069,6 @@ export default class HardwareWalletsStore extends Store { } }; // Ledger method only - @action getCardanoAdaApp = async (params: { path: string | null | undefined; walletId?: string; @@ -1059,7 +1076,7 @@ export default class HardwareWalletsStore extends Store { product?: string; }) => { const { path, walletId, address, product } = params; - logger.debug( + logger.info( '[HW-DEBUG] HWStore - START FUNCTION getCardanoAdaApp PARAMS: ', { walletId, @@ -1074,7 +1091,7 @@ export default class HardwareWalletsStore extends Store { path, product, }); - logger.debug( + logger.info( '[HW-DEBUG] HWStore - cardanoAdaApp RESPONSE: ', toJS(cardanoAdaApp) ); @@ -1083,7 +1100,7 @@ export default class HardwareWalletsStore extends Store { if (cardanoAdaApp) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - cardanoAdaApp - Set device'); + logger.info('[HW-DEBUG] HWStore - cardanoAdaApp - Set device'); // Check is Cardano App version supported const cardanoAppVersion = `${cardanoAdaApp.major}.${cardanoAdaApp.minor}.${cardanoAdaApp.patch}`; const isValidAppVersion = semver.gte( @@ -1092,12 +1109,9 @@ export default class HardwareWalletsStore extends Store { ); if (!isValidAppVersion) { - runInAction( - 'HardwareWalletsStore:: set HW device CONNECTING FAILED - wrong firmware', - () => { - this.hwDeviceStatus = HwDeviceStatuses.WRONG_CARDANO_APP_VERSION; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.WRONG_CARDANO_APP_VERSION; + }); throw new Error( `Cardano app must be ${MINIMAL_CARDANO_APP_VERSION} or greater!` ); @@ -1110,7 +1124,7 @@ export default class HardwareWalletsStore extends Store { }); } } catch (error) { - logger.debug('[HW-DEBUG] HWStore - Cardano app fetching error', { + logger.info('[HW-DEBUG] HWStore - Cardano app fetching error', { error: toJS(error), }); const isDeviceBusy = includes(error.message, 'Ledger Device is busy'); @@ -1119,27 +1133,24 @@ export default class HardwareWalletsStore extends Store { // Keep isTransactionInitiated active & Set new device listener by initiating transaction // Show message to reconnect proper software wallet device pair this.stopCardanoAdaAppFetchPoller(); - logger.debug('[HW-DEBUG] Device is busy: ', { + logger.info('[HW-DEBUG] Device is busy: ', { walletId, error, address, isTransactionInitiated: this.isTransactionInitiated, isAddressVerificationInitiated: this.isAddressVerificationInitiated, }); - runInAction( - 'HardwareWalletsStore:: set HW device CONNECTING FAILED', - () => { - this.hwDeviceStatus = HwDeviceStatuses.CONNECTING_FAILED; - this.activeDevicePath = null; - this.unfinishedWalletTxSigning = this.isTransactionInitiated - ? walletId - : null; - this.unfinishedWalletAddressVerification = this - .isAddressVerificationInitiated - ? address - : null; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.CONNECTING_FAILED; + this.activeDevicePath = null; + this.unfinishedWalletTxSigning = this.isTransactionInitiated + ? walletId + : null; + this.unfinishedWalletAddressVerification = this + .isAddressVerificationInitiated + ? address + : null; + }); } if ( @@ -1154,13 +1165,10 @@ export default class HardwareWalletsStore extends Store { '[HW-DEBUG] HW Store::getCardanoAdaApp::DEVICE_NOT_CONNECTED' ); this.stopCardanoAdaAppFetchPoller(); - runInAction( - 'HardwareWalletsStore:: Re-run initiated connection', - () => { - this.hwDeviceStatus = HwDeviceStatuses.CONNECTING; - this.isListeningForDevice = true; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.CONNECTING; + this.isListeningForDevice = true; + }); } else if (error.code === 'DEVICE_PATH_CHANGED' && error.path) { // Special case on Windows where device path changes after opening Cardano app // Stop poller and re-initiate connecting state / don't kill devices listener @@ -1209,7 +1217,7 @@ export default class HardwareWalletsStore extends Store { // Update connected wallet data with new path - LC if (walletId) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] Update connected wallet data with new path'); + logger.info('[HW-DEBUG] Update connected wallet data with new path'); const hardwareWalletConnectionData = get( this.hardwareWalletsConnectionData, walletId @@ -1217,7 +1225,7 @@ export default class HardwareWalletsStore extends Store { if (hardwareWalletConnectionData) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] Update connected wallet data with new path - Set to LC' ); await this._setHardwareWalletLocalData({ @@ -1240,15 +1248,12 @@ export default class HardwareWalletsStore extends Store { this.isAddressVerificationInitiated ) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] Update connected wallet data with new path - Set to LC' ); - runInAction( - 'HardwareWalletsStore:: Change active device path for Transaction send', - () => { - this.activeDevicePath = error.path; - } - ); + runInAction(() => { + this.activeDevicePath = error.path; + }); } this.useCardanoAppInterval(error.path, walletId, address); @@ -1274,11 +1279,11 @@ export default class HardwareWalletsStore extends Store { path: string | null | undefined ) => { if (this.isAddressVerificationInitiated) return; - logger.debug('[HW-DEBUG] HWStore - Initiate Address Verification: ', { + logger.info('[HW-DEBUG] HWStore - Initiate Address Verification: ', { address: toJS(address), path, }); - runInAction('HardwareWalletsStore:: Initiate Address Verification', () => { + runInAction(() => { this.isAddressVerificationInitiated = true; this.unfinishedWalletAddressVerification = address; this.hwDeviceStatus = HwDeviceStatuses.CONNECTING; @@ -1289,7 +1294,7 @@ export default class HardwareWalletsStore extends Store { this.hardwareWalletsConnectionData, walletId ); - logger.debug('[HW-DEBUG] HWStore - Verify address with wallet: ', { + logger.info('[HW-DEBUG] HWStore - Verify address with wallet: ', { walletId, }); // Guard against potential null value @@ -1302,7 +1307,7 @@ export default class HardwareWalletsStore extends Store { // @ts-ignore ts-migrate(2339) FIXME: Property 'path' does not exist on type 'HardwareWa... Remove this comment to see the full error message hardwareWalletConnectionData.path || hardwareWalletConnectionData.device.path; - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Verify address - check is device connected: ', { disconnected, @@ -1314,7 +1319,7 @@ export default class HardwareWalletsStore extends Store { if (disconnected) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] CHECK FOR NEXT device'); + logger.info('[HW-DEBUG] CHECK FOR NEXT device'); try { if (deviceType === DeviceTypes.LEDGER) { @@ -1329,19 +1334,16 @@ export default class HardwareWalletsStore extends Store { if (transportDevice) { devicePath = transportDevice.path; - logger.debug('[HW-DEBUG] HWStore - Set transport device 4', { + logger.info('[HW-DEBUG] HWStore - Set transport device 4', { transportDevice: toJS(transportDevice), }); - runInAction( - 'HardwareWalletsStore:: Set transport device for tx init', - () => { - this.transportDevice = transportDevice; - } - ); + runInAction(() => { + this.transportDevice = transportDevice; + }); } } catch (e) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Establishing connection failed'); + logger.info('[HW-DEBUG] HWStore - Establishing connection failed'); } } else if (deviceType === DeviceTypes.LEDGER) { const connectedDevice = await this.waitForLedgerDevicesToConnect(); @@ -1349,7 +1351,7 @@ export default class HardwareWalletsStore extends Store { if (!transportDevice) { transportDevice = await this.establishHardwareWalletConnection(); - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Set transport device for ledger device', { transportDevice: toJS(transportDevice), @@ -1357,32 +1359,26 @@ export default class HardwareWalletsStore extends Store { ); } - runInAction( - 'HardwareWalletsStore:: Set transport device from tx init', - () => { - this.transportDevice = transportDevice; - } - ); + runInAction(() => { + this.transportDevice = transportDevice; + }); } if (deviceType === DeviceTypes.TREZOR) { - logger.debug('[HW-DEBUG] Verify Address with Trezor: ', { + logger.info('[HW-DEBUG] Verify Address with Trezor: ', { address: toJS(address), }); if (!transportDevice) { transportDevice = await this.establishHardwareWalletConnection(); - logger.debug('[HW-DEBUG] HWStore - Set transport device 4', { + logger.info('[HW-DEBUG] HWStore - Set transport device 4', { transportDevice: toJS(transportDevice), }); } - runInAction( - 'HardwareWalletsStore:: Set transport device from tx init', - () => { - this.transportDevice = transportDevice; - } - ); + runInAction(() => { + this.transportDevice = transportDevice; + }); const newConnectionData = get( this.hardwareWalletsConnectionData, walletId @@ -1401,9 +1397,10 @@ export default class HardwareWalletsStore extends Store { walletId, address ); - const associatedWallet = await this._findAssociatedWalletByExtendedPublicKey( - { extendedPublicKey } - ); + const associatedWallet = + await this._findAssociatedWalletByExtendedPublicKey({ + extendedPublicKey, + }); if (associatedWallet) { await this._storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting( @@ -1416,7 +1413,7 @@ export default class HardwareWalletsStore extends Store { } ); } else { - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Software wallet not recognized - Setting error states' ); this._discardConnectedDeviceAndReInitiateAddressVerification({ @@ -1425,7 +1422,7 @@ export default class HardwareWalletsStore extends Store { }); } } else { - logger.debug('[HW-DEBUG] Verify Address with Ledger: ', { + logger.info('[HW-DEBUG] Verify Address with Ledger: ', { address: toJS(address), devicePath, }); @@ -1433,14 +1430,13 @@ export default class HardwareWalletsStore extends Store { this.useCardanoAppInterval(devicePath, walletId, address); } }; - @action verifyAddress = async (params: { address: WalletAddress; path: string | null | undefined; isTrezor: boolean; }) => { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] - VERIFY Address'); + logger.info('[HW-DEBUG] - VERIFY Address'); const { address, path, isTrezor } = params; this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS; this.tempAddressToVerify = params; @@ -1457,28 +1453,22 @@ export default class HardwareWalletsStore extends Store { }); if (derivedAddress === address.id) { - logger.debug('[HW-DEBUG] HWStore - Address successfully verified', { + logger.info('[HW-DEBUG] HWStore - Address successfully verified', { address: derivedAddress, }); if (isTrezor) { - runInAction( - 'HardwareWalletsStore:: Address Verified and is correct - Trezor', - () => { - this.isAddressDerived = true; - this.isAddressChecked = true; - this.isListeningForDevice = false; - this.hwDeviceStatus = - HwDeviceStatuses.VERIFYING_ADDRESS_CONFIRMATION; - } - ); + runInAction(() => { + this.isAddressDerived = true; + this.isAddressChecked = true; + this.isListeningForDevice = false; + this.hwDeviceStatus = + HwDeviceStatuses.VERIFYING_ADDRESS_CONFIRMATION; + }); } else { - runInAction( - 'HardwareWalletsStore:: Address Verified and is correct - Ledger', - () => { - this.isAddressDerived = true; - } - ); + runInAction(() => { + this.isAddressDerived = true; + }); this.showAddress(params); } @@ -1487,19 +1477,16 @@ export default class HardwareWalletsStore extends Store { 'Verified wallet address with hardware wallet' ); } else { - runInAction( - 'HardwareWalletsStore:: Address Verified but not correct', - () => { - this.isAddressDerived = false; - this.isAddressChecked = false; - this.isAddressCorrect = false; - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_FAILED; - } - ); + runInAction(() => { + this.isAddressDerived = false; + this.isAddressChecked = false; + this.isAddressCorrect = false; + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_FAILED; + }); } } catch (error) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Verifying address error'); + logger.info('[HW-DEBUG] HWStore - Verifying address error'); /** * ============ Verifying aborted ============= @@ -1512,31 +1499,28 @@ export default class HardwareWalletsStore extends Store { const isAborted = error.name === 'DisconnectedDevice' || error.error === 'device disconnected during action'; - logger.debug('[HW-DEBUG] HWStore - Verifying error case: ', { + logger.info('[HW-DEBUG] HWStore - Verifying error case: ', { isCancelled, isAborted, }); if (isCancelled || isAborted) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - verifyAddress:: WAIT FOR ANOTHER DEVICE' ); // Special case. E.g. device unplugged before cardano app is opened // Stop poller and re-initiate connecting state / don't kill devices listener this.stopCardanoAdaAppFetchPoller(); - runInAction( - 'HardwareWalletsStore:: Re-run initiated connection', - () => { - this.isAddressDerived = false; - this.isAddressChecked = false; - this.isAddressCorrect = false; - this.isListeningForDevice = true; - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_ABORTED; - } - ); + runInAction(() => { + this.isAddressDerived = false; + this.isAddressChecked = false; + this.isAddressCorrect = false; + this.isListeningForDevice = true; + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_ABORTED; + }); } else { - runInAction('HardwareWalletsStore:: Cannot Verify Address', () => { + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_FAILED; this.isAddressDerived = false; this.isAddressChecked = false; @@ -1548,16 +1532,14 @@ export default class HardwareWalletsStore extends Store { } }; - @action initiateWalletPairing = () => { - logger.debug('[HW-DEBUG] HWStore::initiateWalletPairing'); + logger.info('[HW-DEBUG] HWStore::initiateWalletPairing'); this.isWalletPairingInitiated = true; this.establishHardwareWalletConnection(); }; - @action resetWalletPairing = async () => { this.stopCardanoAdaAppFetchPoller(); @@ -1566,14 +1548,13 @@ export default class HardwareWalletsStore extends Store { return this.cleanUpPendingDevices(); }; - @action showAddress = async (params: { address: WalletAddress; path: string | null | undefined; isTrezor: boolean; }) => { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] - SHOW Address'); + logger.info('[HW-DEBUG] - SHOW Address'); const { address, path, isTrezor } = params; try { @@ -1586,18 +1567,15 @@ export default class HardwareWalletsStore extends Store { networkId: hardwareWalletsNetworkConfig.networkId, protocolMagic: hardwareWalletsNetworkConfig.protocolMagic, }); - runInAction( - 'HardwareWalletsStore:: Address show process finished', - () => { - this.isAddressChecked = true; - this.isListeningForDevice = true; - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_CONFIRMATION; - } - ); + runInAction(() => { + this.isAddressChecked = true; + this.isListeningForDevice = true; + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_CONFIRMATION; + }); } catch (error) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Show address error'); - runInAction('HardwareWalletsStore:: Showing address failed', () => { + logger.info('[HW-DEBUG] HWStore - Show address error'); + runInAction(() => { this.isAddressChecked = false; this.isAddressCorrect = false; this.isListeningForDevice = true; @@ -1606,43 +1584,33 @@ export default class HardwareWalletsStore extends Store { throw error; } }; - @action setAddressVerificationCheckStatus = ( checkStatus: AddressVerificationCheckStatus ) => { // Yes / No - Reverify / No - Invalid if (checkStatus === AddressVerificationCheckStatuses.VALID) { - runInAction( - 'HardwareWalletsStore:: Set address verification status CORRECT', - () => { - this.isAddressCorrect = true; - this.isListeningForDevice = true; - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_SUCCEEDED; - } - ); + runInAction(() => { + this.isAddressCorrect = true; + this.isListeningForDevice = true; + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_SUCCEEDED; + }); } if (checkStatus === AddressVerificationCheckStatuses.INVALID) { - runInAction( - 'HardwareWalletsStore:: Set address verification status CORRECT', - () => { - this.isAddressCorrect = false; - this.isListeningForDevice = true; - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_ABORTED; - } - ); + runInAction(() => { + this.isAddressCorrect = false; + this.isListeningForDevice = true; + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_ADDRESS_ABORTED; + }); } if (checkStatus === AddressVerificationCheckStatuses.REVERIFY) { - runInAction( - 'HardwareWalletsStore:: Set address verification status CORRECT', - () => { - this.isAddressDerived = false; - this.isAddressChecked = false; - this.isAddressCorrect = null; - this.isListeningForDevice = true; - } - ); + runInAction(() => { + this.isAddressDerived = false; + this.isAddressChecked = false; + this.isAddressCorrect = null; + this.isListeningForDevice = true; + }); this.verifyAddress(this.tempAddressToVerify); } }; @@ -1650,23 +1618,22 @@ export default class HardwareWalletsStore extends Store { waitForLedgerTransportDevice = async () => { const ledgerDevice = await this.waitForLedgerDevicesToConnect(); - logger.debug( + logger.info( '[HW-DEBUG] HWStore::getLedgerTransportDevice::Use ledger device as transport', { transportDevice: toJS(ledgerDevice), } ); - runInAction('HardwareWalletsStore:: set HW transportDevice', () => { + runInAction(() => { this.transportDevice = ledgerDevice; }); return ledgerDevice; }; - @action _resetInitiatedConnection = ({ isAborted }: ResetInitiatedConnectionArgs) => { - runInAction('HardwareWalletsStore:: Re-run initiated connection', () => { + runInAction(() => { this.hwDeviceStatus = isAborted ? HwDeviceStatuses.CONNECTING : HwDeviceStatuses.EXPORTING_PUBLIC_KEY_FAILED; @@ -1675,13 +1642,12 @@ export default class HardwareWalletsStore extends Store { }); }; - @action _requestExtendedPublicKey = async ( forcedPath?: string | null, walletId?: string, address?: WalletAddress | null ): Promise => { - logger.debug('[HW-DEBUG] HWStore - extendedPublicKey', { + logger.info('[HW-DEBUG] HWStore - extendedPublicKey', { forcedPath, walletId, address: toJS(address), @@ -1690,7 +1656,7 @@ export default class HardwareWalletsStore extends Store { const { transportDevice } = this; if (!transportDevice) { - logger.debug( + logger.info( '[HW-DEBUG] HWStore::_requestExtendedPublicKey:: Device not recognized ' ); @@ -1712,7 +1678,7 @@ export default class HardwareWalletsStore extends Store { }); } catch (error) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Export key error'); + logger.info('[HW-DEBUG] HWStore - Export key error'); /** * ============ Exporting aborted ============= @@ -1727,14 +1693,14 @@ export default class HardwareWalletsStore extends Store { const isAborted = error.name === 'DisconnectedDevice' || error.error === 'device disconnected during action'; - logger.debug('[HW-DEBUG] HWStore - Export error case: ', { + logger.info('[HW-DEBUG] HWStore - Export error case: ', { isCancelled, isAborted, }); if (isCancelled || isAborted) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Export:: WAIT FOR ANOTHER DEVICE'); + logger.info('[HW-DEBUG] HWStore - Export:: WAIT FOR ANOTHER DEVICE'); // Special case. E.g. device unplugged before cardano app is opened // Stop poller and re-initiate connecting state / don't kill devices listener @@ -1742,7 +1708,7 @@ export default class HardwareWalletsStore extends Store { // Skip Trezor device-change events when rejected setTimeout(() => { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] NOW RESET'); + logger.info('[HW-DEBUG] NOW RESET'); this._resetInitiatedConnection({ isAborted }); }, 2000); } else { @@ -1750,12 +1716,9 @@ export default class HardwareWalletsStore extends Store { this._resetInitiatedConnection({ isAborted }); } } else { - runInAction( - 'HardwareWalletsStore:: Cannot export extended public key', - () => { - this.hwDeviceStatus = HwDeviceStatuses.EXPORTING_PUBLIC_KEY_FAILED; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.EXPORTING_PUBLIC_KEY_FAILED; + }); } // Pass other errors to caller (establishHardwareWalletConnection() in this case) and handle additional actions if needed @@ -1763,13 +1726,12 @@ export default class HardwareWalletsStore extends Store { } }; - @action _findAssociatedWalletByExtendedPublicKey = async ({ extendedPublicKey, }: FindAssociatedWalletByExtendedPublicKeyArgs): Promise => { const deviceId = extendedPublicKey?.deviceId || this.transportDevice.deviceId; - logger.debug('[HW-DEBUG] HWStore - EXPORT - deviceID: ', { + logger.info('[HW-DEBUG] HWStore - EXPORT - deviceID: ', { deviceId, }); @@ -1788,7 +1750,6 @@ export default class HardwareWalletsStore extends Store { }; // Delete initiated (pending) device with this path since now is paired to wallet - @action _deletePendingDeviceWithGivenPath = async ({ path, }: DeletePendingDeviceWithGivenPathArgs) => { @@ -1800,7 +1761,7 @@ export default class HardwareWalletsStore extends Store { if (!device) return; - logger.debug( + logger.info( '[HW-DEBUG] HWStore - __deletePendingDeviceWithGivenPath - UNSET Device with path: ', { deviceId: device.id, @@ -1811,40 +1772,35 @@ export default class HardwareWalletsStore extends Store { }); }; - @action _discardConnectedDeviceAndReInitiateTransaction = ({ walletId, }: DiscardConnectedDeviceAndReInitiateTransactionArgs = {}) => { // Keep isTransactionInitiated active & Set new device listener by initiating transaction // Show message to reconnect proper software wallet device pair - logger.debug( + logger.info( '[HW-DEBUG] unfinishedWalletTxSigning SET: ', // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message walletId ); - runInAction( - 'HardwareWalletsStore:: set HW device UNRECOGNIZED_WALLET', - () => { - this.hwDeviceStatus = HwDeviceStatuses.UNRECOGNIZED_WALLET; - this.activeDevicePath = null; - this.unfinishedWalletTxSigning = walletId; - this.isExportKeyAborted = false; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.UNRECOGNIZED_WALLET; + this.activeDevicePath = null; + this.unfinishedWalletTxSigning = walletId; + this.isExportKeyAborted = false; + }); }; - @action _proceedWithTransactionAfterConnectingDevice = ({ isTrezor, walletId, deviceId, devicePath, }: ProceedWithTransactionAfterConnectingDeviceArgs) => { - logger.debug('[HW-DEBUG] HWStore - Transaction Initiated - Close: ', { + logger.info('[HW-DEBUG] HWStore - Transaction Initiated - Close: ', { walletId, }); - logger.debug('[HW-DEBUG] unfinishedWalletTxSigning UNSET'); - runInAction('HardwareWalletsStore:: Initiate transaction', () => { + logger.info('[HW-DEBUG] unfinishedWalletTxSigning UNSET'); + runInAction(() => { this.isTransactionInitiated = false; this.unfinishedWalletTxSigning = null; this.isExportKeyAborted = false; @@ -1857,40 +1813,35 @@ export default class HardwareWalletsStore extends Store { } }; - @action _discardConnectedDeviceAndReInitiateAddressVerification = ({ address, walletId, }: DiscardConnectedDeviceAndReInitiateAddressVerificationArgs) => { - logger.debug('[HW-DEBUG] HWStore - Device not belongs to this wallet'); + logger.info('[HW-DEBUG] HWStore - Device not belongs to this wallet'); // Show message to reconnect proper software wallet device pair - logger.debug('[HW-DEBUG] unfinishedWalletAddressVerification SET: ', { + logger.info('[HW-DEBUG] unfinishedWalletAddressVerification SET: ', { walletId, }); - runInAction( - 'HardwareWalletsStore:: set HW device UNRECOGNIZED_WALLET', - () => { - this.isAddressVerificationInitiated = false; - this.hwDeviceStatus = HwDeviceStatuses.UNRECOGNIZED_WALLET; - this.activeDevicePath = null; - this.unfinishedWalletAddressVerification = address; - this.isExportKeyAborted = false; - } - ); + runInAction(() => { + this.isAddressVerificationInitiated = false; + this.hwDeviceStatus = HwDeviceStatuses.UNRECOGNIZED_WALLET; + this.activeDevicePath = null; + this.unfinishedWalletAddressVerification = address; + this.isExportKeyAborted = false; + }); }; - @action _proceedWithAddressVerificationAfterConnectingDevice = ({ address, devicePath, isTrezor, walletId, }: ProceedWithAddressVerificationAfterConnectingDeviceArgs) => { - logger.debug('[HW-DEBUG] HWStore - Address Verification - Close: ', { + logger.info('[HW-DEBUG] HWStore - Address Verification - Close: ', { walletId, }); - logger.debug('[HW-DEBUG] unfinishedWalletTxSigning UNSET'); - runInAction('HardwareWalletsStore:: Initiate transaction', () => { + logger.info('[HW-DEBUG] unfinishedWalletTxSigning UNSET'); + runInAction(() => { this.isAddressVerificationInitiated = false; this.unfinishedWalletAddressVerification = null; this.isExportKeyAborted = false; @@ -1903,135 +1854,134 @@ export default class HardwareWalletsStore extends Store { }); }; - @action - _storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting = async ({ - address, - associatedWallet, - expectedWalletId, - extendedPublicKey, - path, - }: HandleAssociatedWalletArgs) => { - const { deviceType, deviceName, deviceModel } = this.transportDevice; - const isTrezor = deviceType === DeviceTypes.TREZOR; - const devicePath = path || this.transportDevice.path; - const deviceId = - extendedPublicKey.deviceId || this.transportDevice.deviceId; - - // Check if public key matches already restored hardware wallet public key - // Update LC data and redirect to paired wallet - logger.debug('[HW-DEBUG] HWStore - I have recognized wallet: ', { - recognizedWallet: associatedWallet.id, - }); - - this._setHardwareWalletLocalData({ - walletId: associatedWallet.id, - data: { - disconnected: false, - // @ts-ignore ts-migrate(2322) FIXME: Type '{ disconnected: false; data: { deviceType: D... Remove this comment to see the full error message - data: { - deviceType, - deviceModel, - deviceName, - path: devicePath, - paired: associatedWallet.id, - // device paired with software wallet - disconnected: false, // device physically disconnected - }, - }, - }); - - await this._deletePendingDeviceWithGivenPath({ path }); - - if (deviceId) { - logger.debug('[HW-DEBUG] HWStore - SET device from key export: ', { - deviceId, + _storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting = + async ({ + address, + associatedWallet, + expectedWalletId, + extendedPublicKey, + path, + }: HandleAssociatedWalletArgs) => { + const { deviceType, deviceName, deviceModel } = this.transportDevice; + const isTrezor = deviceType === DeviceTypes.TREZOR; + const devicePath = path || this.transportDevice.path; + const deviceId = + extendedPublicKey.deviceId || this.transportDevice.deviceId; + + // Check if public key matches already restored hardware wallet public key + // Update LC data and redirect to paired wallet + logger.info('[HW-DEBUG] HWStore - I have recognized wallet: ', { + recognizedWallet: associatedWallet.id, }); - this._setHardwareWalletDevice({ - deviceId, + this._setHardwareWalletLocalData({ + walletId: associatedWallet.id, data: { - // @ts-ignore ts-migrate(2322) FIXME: Type '{ deviceId: string; deviceType: DeviceType; ... Remove this comment to see the full error message - deviceId, - deviceType, - deviceModel, - deviceName, - path: devicePath, - paired: associatedWallet.id, - // device paired with software wallet disconnected: false, - // device physically disconnected - isPending: false, + // @ts-ignore ts-migrate(2322) FIXME: Type '{ disconnected: false; data: { deviceType: D... Remove this comment to see the full error message + data: { + deviceType, + deviceModel, + deviceName, + path: devicePath, + paired: associatedWallet.id, + // device paired with software wallet + disconnected: false, // device physically disconnected + }, }, }); - } - // Prevent redirect / check if device is valid / proceed with tx - if (this.isTransactionInitiated) { - logger.debug( - '[HW-DEBUG] HWStore - Re-initiate tx from _storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting: ', - { - expectedWalletId, - recognizedWalletId: associatedWallet.id, - deviceId, - devicePath, - } - ); + await this._deletePendingDeviceWithGivenPath({ path }); - // Check if sender wallet match transaction initialization - if (!expectedWalletId || associatedWallet.id !== expectedWalletId) { - logger.debug('[HW-DEBUG] HWStore - Device not belongs to this wallet'); - this._discardConnectedDeviceAndReInitiateTransaction({ - walletId: expectedWalletId, + if (deviceId) { + logger.info('[HW-DEBUG] HWStore - SET device from key export: ', { + deviceId, }); - } else { - this._proceedWithTransactionAfterConnectingDevice({ - isTrezor, + + this._setHardwareWalletDevice({ deviceId, - devicePath, - walletId: expectedWalletId, + data: { + // @ts-ignore ts-migrate(2322) FIXME: Type '{ deviceId: string; deviceType: DeviceType; ... Remove this comment to see the full error message + deviceId, + deviceType, + deviceModel, + deviceName, + path: devicePath, + paired: associatedWallet.id, + // device paired with software wallet + disconnected: false, + // device physically disconnected + isPending: false, + }, }); } - return; - } + // Prevent redirect / check if device is valid / proceed with tx + if (this.isTransactionInitiated) { + logger.info( + '[HW-DEBUG] HWStore - Re-initiate tx from _storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting: ', + { + expectedWalletId, + recognizedWalletId: associatedWallet.id, + deviceId, + devicePath, + } + ); - // Prevent redirect / check if device is valid / proceed with address verification - if (this.isAddressVerificationInitiated && address) { - logger.debug( - '[HW-DEBUG] HWStore - Re-initiate Address verification from _storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting: ', - { - address: toJS(address), - devicePath, - expectedWalletId, - recognizedWalletId: associatedWallet.id, - deviceId, + // Check if sender wallet match transaction initialization + if (!expectedWalletId || associatedWallet.id !== expectedWalletId) { + logger.info('[HW-DEBUG] HWStore - Device not belongs to this wallet'); + this._discardConnectedDeviceAndReInitiateTransaction({ + walletId: expectedWalletId, + }); + } else { + this._proceedWithTransactionAfterConnectingDevice({ + isTrezor, + deviceId, + devicePath, + walletId: expectedWalletId, + }); } - ); - if (!expectedWalletId || associatedWallet.id !== expectedWalletId) { - logger.debug('[HW-DEBUG] HWStore - Device not belongs to this wallet'); - this._discardConnectedDeviceAndReInitiateAddressVerification({ - address, - walletId: expectedWalletId, - }); - } else { - this._proceedWithAddressVerificationAfterConnectingDevice({ - address, - devicePath, - isTrezor, - walletId: expectedWalletId, - }); + return; } - return; - } + // Prevent redirect / check if device is valid / proceed with address verification + if (this.isAddressVerificationInitiated && address) { + logger.info( + '[HW-DEBUG] HWStore - Re-initiate Address verification from _storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting: ', + { + address: toJS(address), + devicePath, + expectedWalletId, + recognizedWalletId: associatedWallet.id, + deviceId, + } + ); - // --> Else - this.stores.wallets.goToWalletRoute(associatedWallet.id); - this.actions.dialogs.closeActiveDialog.trigger(); - }; + if (!expectedWalletId || associatedWallet.id !== expectedWalletId) { + logger.info('[HW-DEBUG] HWStore - Device not belongs to this wallet'); + this._discardConnectedDeviceAndReInitiateAddressVerification({ + address, + walletId: expectedWalletId, + }); + } else { + this._proceedWithAddressVerificationAfterConnectingDevice({ + address, + devicePath, + isTrezor, + walletId: expectedWalletId, + }); + } + + return; + } + + // --> Else + this.stores.wallets.goToWalletRoute(associatedWallet.id); + this.actions.dialogs.closeActiveDialog.trigger(); + }; - @action _createNewWalletForRecognizedPendingDevice = async ({ extendedPublicKey, path, @@ -2042,7 +1992,7 @@ export default class HardwareWalletsStore extends Store { extendedPublicKey.deviceId || this.transportDevice.deviceId; // Software Wallet not recognized, create new one with default name - logger.debug('[HW-DEBUG] HWStore - Initiate HW create / restore', { + logger.info('[HW-DEBUG] HWStore - Initiate HW create / restore', { transportDevice: toJS(this.transportDevice), device: { deviceId, @@ -2065,7 +2015,7 @@ export default class HardwareWalletsStore extends Store { firmwareVersion: null, }, }); - logger.debug('[HW-DEBUG] HWStore - HW created / restored'); + logger.info('[HW-DEBUG] HWStore - HW created / restored'); // Get all Pending devices with this path and delete const recognizedPendingDevice = find( this.hardwareWalletDevices, @@ -2075,7 +2025,7 @@ export default class HardwareWalletsStore extends Store { // @ts-ignore ts-migrate(2339) FIXME: Property 'isPending' does not exist on type 'Hardw... Remove this comment to see the full error message if (recognizedPendingDevice && recognizedPendingDevice.isPending) { - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Export key - UNSET Device with path: ', { path, @@ -2094,21 +2044,22 @@ export default class HardwareWalletsStore extends Store { await this.resetWalletPairing(); }; - @action _identifyAndHandleAssociatedWallet = async ({ address, path, expectedWalletId, }: IdentifyAndHandleAssociatedWalletArgs) => { + logger.info('[HW-DEBUG] Identifying ...: '); try { const extendedPublicKey = await this._requestExtendedPublicKey( path, expectedWalletId, address ); - const associatedWallet = await this._findAssociatedWalletByExtendedPublicKey( - { extendedPublicKey } - ); + const associatedWallet = + await this._findAssociatedWalletByExtendedPublicKey({ + extendedPublicKey, + }); if (associatedWallet) { await this._storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting( @@ -2123,7 +2074,7 @@ export default class HardwareWalletsStore extends Store { } else { const deviceId = extendedPublicKey.deviceId || this.transportDevice.deviceId; - logger.debug( + logger.info( '[HW-DEBUG] HWStore - I don not have recognized wallet - create new one or reject TX: ', { deviceId, @@ -2149,27 +2100,23 @@ export default class HardwareWalletsStore extends Store { }; // Trezor - Shelley only - @action _signTransactionTrezor = async ( walletId: string, deviceId?: string | null | undefined ) => { const { coinSelection } = this.txSignRequest; - runInAction('HardwareWalletsStore:: set Transaction verifying', () => { + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION; }); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] _signTransactionTrezor:: Execute'); + logger.info('[HW-DEBUG] _signTransactionTrezor:: Execute'); // @TODO - remove once signing delegation transaction will call coins selection // This case is covered in coins selection action if (!coinSelection) { - runInAction( - 'HardwareWalletsStore:: set Transaction verifying failed', - () => { - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; + }); throw new Error(`Missing Coins Selection for wallet: ${walletId}`); } @@ -2180,7 +2127,7 @@ export default class HardwareWalletsStore extends Store { certificates, withdrawals, } = coinSelection; - logger.debug('[HW-DEBUG] HWStore - sign transaction Trezor: ', { + logger.info('[HW-DEBUG] HWStore - sign transaction Trezor: ', { walletId, }); const hardwareWalletConnectionData = get( @@ -2209,11 +2156,10 @@ export default class HardwareWalletsStore extends Store { const outputsData = []; for (const output of outputs) { - const { - address_style: addressStyle, - } = await this.stores.addresses._inspectAddress({ - addressId: output.address, - }); + const { address_style: addressStyle } = + await this.stores.addresses._inspectAddress({ + addressId: output.address, + }); const shelleyTxOutput = ShelleyTxOutput(output, addressStyle); unsignedTxOutputs.push(shelleyTxOutput); const ledgerOutput = prepareTrezorOutput(output); @@ -2275,8 +2221,7 @@ export default class HardwareWalletsStore extends Store { // @ts-ignore ts-migrate(2339) FIXME: Property 'paired' does not exist on type 'Hardware... Remove this comment to see the full error message (hardwareWalletDevice) => hardwareWalletDevice.paired === walletId ); - const recognizedDevicePath = get(recognizedDevice, 'path', null); - logger.debug('[HW-DEBUG] sign Trezor:: recognizedDevicePath and wallet: ', { + logger.info('[HW-DEBUG] sign Trezor:: recognizedDevicePath and wallet: ', { walletId, deviceId, isTransactionInitiated: this.isTransactionInitiated, @@ -2289,28 +2234,25 @@ export default class HardwareWalletsStore extends Store { (recognizedDevice && deviceId && recognizedDevice.id !== deviceId) ) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - Device not belongs to this wallet'); + logger.info('[HW-DEBUG] HWStore - Device not belongs to this wallet'); // Keep isTransactionInitiated active & Set new device listener by initiating transaction // Show message to reconnect proper software wallet device pair // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message - logger.debug('[HW-DEBUG] unfinishedWalletTxSigning SET: ', walletId); - runInAction( - 'HardwareWalletsStore:: set HW device CONNECTING FAILED', - () => { - this.hwDeviceStatus = HwDeviceStatuses.CONNECTING_FAILED; - this.activeDevicePath = null; - this.unfinishedWalletTxSigning = walletId; - } - ); + logger.info('[HW-DEBUG] unfinishedWalletTxSigning SET: ', walletId); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.CONNECTING_FAILED; + this.activeDevicePath = null; + this.unfinishedWalletTxSigning = walletId; + }); return; } - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Transaction Initiated - RESET: ', // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message walletId ); - runInAction('HardwareWalletsStore:: Initiate transaction', () => { + runInAction(() => { this.isTransactionInitiated = false; this.unfinishedWalletTxSigning = null; }); @@ -2341,14 +2283,11 @@ export default class HardwareWalletsStore extends Store { const serializedTx = get(signedTransaction, ['payload', 'serializedTx']); if (serializedTx) { - runInAction( - 'HardwareWalletsStore:: transaction successfully signed', - () => { - this.txBody = serializedTx; - this.hwDeviceStatus = - HwDeviceStatuses.VERIFYING_TRANSACTION_SUCCEEDED; - } - ); + runInAction(() => { + this.txBody = serializedTx; + this.hwDeviceStatus = + HwDeviceStatuses.VERIFYING_TRANSACTION_SUCCEEDED; + }); return; } @@ -2397,29 +2336,23 @@ export default class HardwareWalletsStore extends Store { txWitnesses, txAuxiliaryData ); - runInAction('HardwareWalletsStore:: set Transaction verified', () => { + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_SUCCEEDED; this.txBody = txBody; this.activeDevicePath = null; }); } catch (error) { - runInAction( - 'HardwareWalletsStore:: set Transaction verifying failed', - () => { - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; - this.isTransactionInitiated = false; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; + this.isTransactionInitiated = false; + }); if (error.code === 'Device_CallInProgress') { throw new Error('Device is busy - reconnect device and try again'); } else if (error.code === 'Device_InvalidState') { - runInAction( - 'HardwareWalletsStore:: Unrecognized wallet (wrong passphrase)', - () => { - this.hwDeviceStatus = HwDeviceStatuses.UNRECOGNIZED_WALLET; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.UNRECOGNIZED_WALLET; + }); } throw error; @@ -2487,12 +2420,11 @@ export default class HardwareWalletsStore extends Store { }); return constructedAddress.address; }; - @action _signTransactionLedger = async ( walletId: string, devicePath: string | null | undefined ) => { - runInAction('HardwareWalletsStore:: set Transaction verifying', () => { + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION; }); const { coinSelection } = this.txSignRequest; @@ -2503,7 +2435,7 @@ export default class HardwareWalletsStore extends Store { fee: flatFee, withdrawals, } = coinSelection; - logger.debug('[HW-DEBUG] HWStore - sign transaction Ledger: ', { + logger.info('[HW-DEBUG] HWStore - sign transaction Ledger: ', { walletId, }); const hardwareWalletConnectionData = get( @@ -2532,11 +2464,10 @@ export default class HardwareWalletsStore extends Store { const outputsData = []; for (const output of outputs) { - const { - address_style: addressStyle, - } = await this.stores.addresses._inspectAddress({ - addressId: output.address, - }); + const { address_style: addressStyle } = + await this.stores.addresses._inspectAddress({ + addressId: output.address, + }); const shelleyTxOutput = ShelleyTxOutput(output, addressStyle); unsignedTxOutputs.push(shelleyTxOutput); const ledgerOutput = prepareLedgerOutput(output, addressStyle); @@ -2659,19 +2590,16 @@ export default class HardwareWalletsStore extends Store { txWitnesses, txAuxiliaryData ); - runInAction('HardwareWalletsStore:: set Transaction verified', () => { + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_SUCCEEDED; this.txBody = txBody; this.activeDevicePath = null; }); } catch (error) { logger.info('[HW-DEBUG] HWStore:: sign Transaction Ledger', { error }); - runInAction( - 'HardwareWalletsStore:: set Transaction verifying failed', - () => { - this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; - } - ); + runInAction(() => { + this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; + }); throw error; } }; @@ -2680,7 +2608,7 @@ export default class HardwareWalletsStore extends Store { votingData?: VotingDataType; }) => { const { walletId, votingData } = params; - runInAction('HardwareWalletsStore:: Initiate Transaction', () => { + runInAction(() => { this.isTransactionInitiated = true; this.hwDeviceStatus = HwDeviceStatuses.CONNECTING; this.activeDelegationWalletId = walletId; @@ -2691,7 +2619,7 @@ export default class HardwareWalletsStore extends Store { this.hardwareWalletsConnectionData, walletId ); - logger.debug('[HW-DEBUG] HWStore - initiateTransaction: ', { + logger.info('[HW-DEBUG] HWStore - initiateTransaction: ', { walletId, }); // Guard against potential null value @@ -2703,7 +2631,7 @@ export default class HardwareWalletsStore extends Store { if (disconnected) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - initiateTransaction - DISCONNECTED'); + logger.info('[HW-DEBUG] HWStore - initiateTransaction - DISCONNECTED'); // Wait for connection to be established and continue to signing process try { @@ -2717,11 +2645,11 @@ export default class HardwareWalletsStore extends Store { if (lastUnpairedDevice) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] I HAVE UNPAIRED'); + logger.info('[HW-DEBUG] I HAVE UNPAIRED'); transportDevice = lastUnpairedDevice; } else { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] CHECK FOR NEXT device'); + logger.info('[HW-DEBUG] CHECK FOR NEXT device'); transportDevice = await getHardwareWalletTransportChannel.request({ devicePath: null, isTrezor: true, @@ -2729,7 +2657,7 @@ export default class HardwareWalletsStore extends Store { } // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] INITIATE tx - I have transport'); + logger.info('[HW-DEBUG] INITIATE tx - I have transport'); } else { logger.info('[HW-DEBUG] HW STORE WAIT FOR LEDGER DEVICE'); @@ -2742,17 +2670,17 @@ export default class HardwareWalletsStore extends Store { if (!transportDevice) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] No new devices recognized for tx signing'); + logger.info('[HW-DEBUG] No new devices recognized for tx signing'); throw new Error('Signing device not recognized!'); } devicePath = transportDevice.path; } catch (e) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + logger.info( '[HW-DEBUG] HWStore - initiateTransaction - DISCONNECTED - ERROR' ); - runInAction('HardwareWalletsStore:: Initiate transaction', () => { + runInAction(() => { this.isTransactionInitiated = false; this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_FAILED; }); @@ -2766,38 +2694,33 @@ export default class HardwareWalletsStore extends Store { devicePath = ledgerDevice.path; } - runInAction( - 'HardwareWalletsStore:: Set active device path for Transaction send', - () => { - this.activeDevicePath = devicePath; - } - ); + runInAction(() => { + this.activeDevicePath = devicePath; + }); // Add more cases / edge cases if needed if (deviceType === DeviceTypes.TREZOR && walletId) { - logger.debug('[HW-DEBUG] Sign Trezor: ', { + logger.info('[HW-DEBUG] Sign Trezor: ', { id, }); const transportDevice = await this.establishHardwareWalletConnection(); if (transportDevice) { - logger.debug('[HW-DEBUG] HWStore - Set transport device 4', { + logger.info('[HW-DEBUG] HWStore - Set transport device 4', { transportDevice: toJS(transportDevice), }); - runInAction( - 'HardwareWalletsStore:: Set transport device for tx init', - () => { - this.transportDevice = transportDevice; - } - ); + runInAction(() => { + this.transportDevice = transportDevice; + }); const extendedPublicKey = await this._requestExtendedPublicKey( transportDevice.path, walletId ); - const associatedWallet = await this._findAssociatedWalletByExtendedPublicKey( - { extendedPublicKey } - ); + const associatedWallet = + await this._findAssociatedWalletByExtendedPublicKey({ + extendedPublicKey, + }); if (associatedWallet) { await this._storeWalletDataInLocalStorageAndHandleTransactionOrAddressVerificationOrRouting( @@ -2811,7 +2734,7 @@ export default class HardwareWalletsStore extends Store { } else { const deviceId = extendedPublicKey.deviceId || this.transportDevice.deviceId; - logger.debug( + logger.info( '[HW-DEBUG] HWStore - I don not have recognized wallet - reject TX: ', { deviceId, @@ -2823,7 +2746,7 @@ export default class HardwareWalletsStore extends Store { } } } else { - logger.debug( + logger.info( '[HW-DEBUG] HWStore - getCardanoAdaApp - from initiateTransaction', { devicePath, @@ -2846,8 +2769,8 @@ export default class HardwareWalletsStore extends Store { ) => { if (isHardwareWalletSupportEnabled) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] RESET TX'); - runInAction('HardwareWalletsStore:: Reset initiated transaction', () => { + logger.info('[HW-DEBUG] RESET TX'); + runInAction(() => { this.isTransactionInitiated = false; }); this.stopCardanoAdaAppFetchPoller(); @@ -2860,8 +2783,8 @@ export default class HardwareWalletsStore extends Store { this.sendMoneyRequest.reset(); this.selectCoinsRequest.reset(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] unfinishedWalletTxSigning UNSET'); - runInAction('HardwareWalletsStore:: reset Transaction verifying', () => { + logger.info('[HW-DEBUG] unfinishedWalletTxSigning UNSET'); + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.READY; this.txBody = null; this.activeDevicePath = null; @@ -2872,7 +2795,6 @@ export default class HardwareWalletsStore extends Store { }); } }; - @action _changeHardwareWalletConnectionStatus = async ( params: HardwareWalletConnectionRequest ) => { @@ -2887,12 +2809,12 @@ export default class HardwareWalletsStore extends Store { eventType, product, } = params; - logger.debug('[HW-DEBUG] HWStore - CHANGE status: ', { + logger.info('[HW-DEBUG] HWStore - CHANGE status: ', { params, }); if (disconnected) { - logger.debug( + logger.info( '[HW-DEBUG] HWStore - CHANGE status::in-memory-path::removing path from memory ', { path, @@ -2900,7 +2822,7 @@ export default class HardwareWalletsStore extends Store { ); this.connectedHardwareWalletsDevices.delete(path); } else { - logger.debug( + logger.info( '[HW-DEBUG] HWStore - CHANGE status::in-memory-path::adding path to memory ', { path, @@ -2924,12 +2846,9 @@ export default class HardwareWalletsStore extends Store { error.payload && error.payload.code === 'ECONNREFUSED' ) { - runInAction( - 'HardwareWalletsStore:: Mark Trezor Bridge as not installed', - () => { - this.isTrezorBridgeInstalled = false; - } - ); + runInAction(() => { + this.isTrezorBridgeInstalled = false; + }); } return; @@ -2937,12 +2856,9 @@ export default class HardwareWalletsStore extends Store { // Unset Trezor Bridge instance checker if (deviceType === DeviceTypes.TREZOR && !this.isTrezorBridgeInstalled) { - runInAction( - 'HardwareWalletsStore:: Mark Trezor Bridge as installed', - () => { - this.isTrezorBridgeInstalled = true; - } - ); + runInAction(() => { + this.isTrezorBridgeInstalled = true; + }); } const { hardwareWalletsConnectionData, hardwareWalletDevices } = this; @@ -2961,7 +2877,7 @@ export default class HardwareWalletsStore extends Store { if (disconnected && deviceType === DeviceTypes.LEDGER) { // Remove all stored Ledger instances from LC - both pending and paired (with software Wallets) // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - device disconnected', { + logger.info('[HW-DEBUG] HWStore - device disconnected', { hardwareWalletDevices: toJS(hardwareWalletDevices), path, }); @@ -2972,7 +2888,7 @@ export default class HardwareWalletsStore extends Store { ); if (recognizedLedgerDevice) { - logger.debug('[HW-DEBUG] HWStore - Remove Device from LC', { + logger.info('[HW-DEBUG] HWStore - Remove Device from LC', { recognizedLedgerDevice: toJS(recognizedLedgerDevice), }); await this._unsetHardwareWalletDevice({ @@ -2981,7 +2897,7 @@ export default class HardwareWalletsStore extends Store { } // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - GET Paired and set to disconnected'); + logger.info('[HW-DEBUG] HWStore - GET Paired and set to disconnected'); const recognizedLedgerWallet = find( hardwareWalletsConnectionData, ( @@ -2994,7 +2910,7 @@ export default class HardwareWalletsStore extends Store { if (recognizedLedgerWallet) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - I have stored Ledger wallet'); + logger.info('[HW-DEBUG] HWStore - I have stored Ledger wallet'); await this._setHardwareWalletLocalData({ walletId: recognizedLedgerWallet.id, data: { @@ -3015,7 +2931,7 @@ export default class HardwareWalletsStore extends Store { recognizedPairedHardwareWallet.device.deviceType === DeviceTypes.TREZOR ) { // Change software wallet status - paired with device - logger.debug( + logger.info( '[HW-DEBUG] HWStore - set Hardware Wallet local data: ', // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message recognizedPairedHardwareWallet.id @@ -3036,7 +2952,7 @@ export default class HardwareWalletsStore extends Store { // Set Pending Ledger or Trezor device with ID let pendingId; - + logger.info('[HW-DEBUG] HWStore deviceId', { deviceId }); if ( deviceId || (deviceType === DeviceTypes.LEDGER && @@ -3046,7 +2962,7 @@ export default class HardwareWalletsStore extends Store { deviceType === DeviceTypes.LEDGER && recognizedPairedHardwareWallet ? recognizedPairedHardwareWallet.device.deviceId : new Date().valueOf(); - logger.debug('[HW-DEBUG] HWStore - SET DEVICE DATA: ', { + logger.info('[HW-DEBUG] HWStore - SET DEVICE DATA: ', { deviceId, pendingId, disconnected, @@ -3079,7 +2995,7 @@ export default class HardwareWalletsStore extends Store { await this._refreshHardwareWalletDevices(); // Start connection establishing process if devices listener flag is UP - logger.debug('[HW-DEBUG] HWStore - establish connection guard: ', { + logger.info('[HW-DEBUG] HWStore - establish connection guard: ', { isListeningForDevice: this.isListeningForDevice, }); @@ -3088,13 +3004,13 @@ export default class HardwareWalletsStore extends Store { !disconnected && (!eventType || eventType === DeviceEvents.CONNECT) ) { - runInAction('HardwareWalletsStore:: remove device listener', () => { + runInAction(() => { this.isListeningForDevice = false; }); if (deviceType === DeviceTypes.LEDGER) { // To Force Ledger with manual parameters because ID is not available and device not stored to LC - logger.debug( + logger.info( '[HW-DEBUG] HWStore - CALL establish connection with pendingID: ', pendingId ); @@ -3110,15 +3026,15 @@ export default class HardwareWalletsStore extends Store { !disconnected && eventType === DeviceEvents.CONNECT ) { - logger.debug( + logger.info( '[HW-DEBUG] CHANGE STATUS to: ', // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message HwDeviceStatuses.CONNECTING ); - runInAction('HardwareWalletsStore:: Change status to Connecting', () => { + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.CONNECTING; }); - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Reinitialize TX signing: ', // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message this.unfinishedWalletTxSigning @@ -3134,15 +3050,15 @@ export default class HardwareWalletsStore extends Store { !disconnected && (!eventType || eventType === DeviceEvents.CONNECT) ) { - logger.debug( + logger.info( '[HW-DEBUG] CHANGE STATUS to: ', // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message HwDeviceStatuses.CONNECTING ); - runInAction('HardwareWalletsStore:: Change status to Connecting', () => { + runInAction(() => { this.hwDeviceStatus = HwDeviceStatuses.CONNECTING; }); - logger.debug( + logger.info( '[HW-DEBUG] HWStore - Reinitialize Address Verification process: ', toJS(this.unfinishedWalletAddressVerification) ); @@ -3187,7 +3103,7 @@ export default class HardwareWalletsStore extends Store { .filter(({ isPending }) => isPending) .map(({ id }) => id); - logger.debug('[HW-DEBUG] HWStore - cleanUpPendingDevices - cleanup ids: ', { + logger.info('[HW-DEBUG] HWStore - cleanUpPendingDevices - cleanup ids: ', { pendingHardwareWalletsIds, }); @@ -3201,7 +3117,6 @@ export default class HardwareWalletsStore extends Store { return Promise.all(unsetHardwareWalletDeviceRequests); }; - @action resetInitializedConnection = async ( params: | { @@ -3225,7 +3140,6 @@ export default class HardwareWalletsStore extends Store { this.isListeningForDevice = false; this.isExportKeyAborted = false; }; - @action resetInitializedAddressVerification = async ( params: | { @@ -3255,23 +3169,19 @@ export default class HardwareWalletsStore extends Store { this.isExportKeyAborted = false; this.activeDevicePath = null; }; - @action _refreshHardwareWalletsLocalData = async () => { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.hardwareWalletsLocalDataRequest.execute(); }; - @action _refreshHardwareWalletDevices = async () => { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.hardwareWalletDevicesRequest.execute(); }; - @computed get hardwareWalletsConnectionData(): HardwareWalletsLocalData { return this.hardwareWalletsLocalDataRequest.result; } - @computed get hardwareWalletDevices(): HardwareWalletsLocalData { return this.hardwareWalletDevicesRequest.result; } @@ -3342,7 +3252,7 @@ export default class HardwareWalletsStore extends Store { walletId, data, }: SetHardwareWalletLocalDataRequestType) => { - logger.debug( + logger.info( '[HW-DEBUG] HWStore - CALL SET - _setHardwareWalletLocalData METHOD: ', // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message walletId @@ -3363,7 +3273,7 @@ export default class HardwareWalletsStore extends Store { walletId: string; }) => { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('[HW-DEBUG] HWStore - _unsetHardwareWalletLocalData'); + logger.info('[HW-DEBUG] HWStore - _unsetHardwareWalletLocalData'); // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.unsetHardwareWalletLocalDataRequest.execute(walletId); const pairedDevice = find( diff --git a/source/renderer/app/stores/NetworkStatusStore.ts b/source/renderer/app/stores/NetworkStatusStore.ts index 3c14077ec0..d159a99393 100644 --- a/source/renderer/app/stores/NetworkStatusStore.ts +++ b/source/renderer/app/stores/NetworkStatusStore.ts @@ -1,4 +1,10 @@ -import { action, computed, observable, runInAction } from 'mobx'; +import { + action, + computed, + makeObservable, + observable, + runInAction, +} from 'mobx'; import moment from 'moment'; import { get, includes, isEqual } from 'lodash'; import Store from './lib/Store'; @@ -11,7 +17,6 @@ import { NETWORK_STATUS_POLL_INTERVAL, } from '../config/timingConfig'; import { INITIAL_DESIRED_POOLS_NUMBER } from '../config/stakingConfig'; -import { logger } from '../utils/logging'; import { cardanoStateChangeChannel, cardanoTlsConfigChannel, @@ -42,7 +47,9 @@ import type { CheckDiskSpaceResponse } from '../../../common/types/no-disk-space import { TlsCertificateNotValidError } from '../api/nodes/errors'; import { openLocalDirectoryChannel } from '../ipc/open-local-directory'; import { toggleRTSFlagsModeChannel } from '../ipc/toggleRTSFlagsModeChannel'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; // DEFINE CONSTANTS ------------------------- const NETWORK_STATUS = { @@ -76,115 +83,150 @@ export default class NetworkStatusStore extends Store { _networkParametersPollingInterval: IntervalID | null | undefined = null; // Initialize store observables // Internal Node states - @observable tlsConfig: TlsConfig | null | undefined = null; - @observable cardanoNodeState: CardanoNodeState = 'unknown'; - @observable cardanoNodePID = 0; - @observable cardanoWalletPID = 0; - @observable isRTSFlagsModeEnabled = false; - @observable + isRTSFlagsModeEnabled = false; isNodeResponding = false; // Is 'true' as long we are receiving node Api responses - @observable isNodeSyncing = false; // Is 'true' in case we are receiving blocks and not stalling - @observable isNodeInSync = false; // Is 'true' if syncing & local/network blocks diff within limit - @observable isNodeStopping = false; // Is 'true' if node is in `NODE_STOPPING_STATES` states - @observable isNodeStopped = false; // Is 'true' if node is in `NODE_STOPPED_STATES` states - @observable isNodeTimeCorrect = true; // Is 'true' in case local and global time are in sync - @observable isSystemTimeIgnored = false; // Tracks if NTP time checks are ignored - @observable isSplashShown = isFlight; // Visibility of splash screen - @observable isSyncProgressStalling = false; // Is 'true' in case sync progress doesn't change within limit - @observable hasBeenConnected = false; - @observable syncProgress = null; - @observable localTip: TipInfo | null | undefined = null; - @observable networkTip: TipInfo | null | undefined = null; - @observable nextEpoch: NextEpoch | null | undefined = null; - @observable futureEpoch: FutureEpoch | null | undefined = null; - @observable lastSyncProgressChangeTimestamp = 0; // milliseconds - @observable localTimeDifference: number | null | undefined = 0; // microseconds - @observable decentralizationProgress = 0; // percentage - @observable desiredPoolNumber: number = INITIAL_DESIRED_POOLS_NUMBER; - @observable getNetworkInfoRequest: Request = new Request( this.api.ada.getNetworkInfo ); - @observable getNetworkClockRequest: Request = new Request( this.api.ada.getNetworkClock ); - @observable - getNetworkParametersRequest: Request< - GetNetworkParametersResponse - > = new Request(this.api.ada.getNetworkParameters); - @observable + getNetworkParametersRequest: Request = + new Request(this.api.ada.getNetworkParameters); isNotEnoughDiskSpace = false; - @observable diskSpaceRequired = ''; - @observable diskSpaceMissing = ''; - @observable diskSpaceRecommended = ''; - @observable diskSpaceAvailable = ''; - @observable isTlsCertInvalid = false; - @observable stateDirectoryPath = ''; - @observable isShelleyActivated = false; - @observable isShelleyPending = false; - @observable isAlonzoActivated = false; - @observable shelleyActivationTime = ''; - @observable isAlonzoPending = false; - @observable alonzoActivationTime = ''; - @observable blockSyncProgress: Record = { [BlockSyncType.validatingChunk]: 0, [BlockSyncType.replayedBlock]: 0, [BlockSyncType.pushingLedger]: 0, }; - @observable epochLength: number | null | undefined = null; // unit: 1 slot - @observable slotLength: number | null | undefined = null; // unit: 1 second + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + tlsConfig: observable, + cardanoNodeState: observable, + cardanoNodePID: observable, + cardanoWalletPID: observable, + isRTSFlagsModeEnabled: observable, + isNodeResponding: observable, + isNodeSyncing: observable, + isNodeInSync: observable, + isNodeStopping: observable, + isNodeStopped: observable, + isNodeTimeCorrect: observable, + isSystemTimeIgnored: observable, + isSplashShown: observable, + isSyncProgressStalling: observable, + hasBeenConnected: observable, + syncProgress: observable, + localTip: observable, + networkTip: observable, + nextEpoch: observable, + futureEpoch: observable, + lastSyncProgressChangeTimestamp: observable, + localTimeDifference: observable, + decentralizationProgress: observable, + desiredPoolNumber: observable, + getNetworkInfoRequest: observable, + getNetworkClockRequest: observable, + getNetworkParametersRequest: observable, + isNotEnoughDiskSpace: observable, + diskSpaceRequired: observable, + diskSpaceMissing: observable, + diskSpaceRecommended: observable, + diskSpaceAvailable: observable, + isTlsCertInvalid: observable, + stateDirectoryPath: observable, + isShelleyActivated: observable, + isShelleyPending: observable, + isAlonzoActivated: observable, + shelleyActivationTime: observable, + isAlonzoPending: observable, + alonzoActivationTime: observable, + blockSyncProgress: observable, + epochLength: observable, + slotLength: observable, + _toggleRTSFlagsMode: action, + _setNetworkStatusPollingInterval: action, + _setNetworkClockPollingInterval: action, + _setNetworkParametersPollingInterval: action, + _clearNetworkStatusPollingInterval: action, + _clearNetworkClockPollingInterval: action, + _clearNetworkParametersPollingInterval: action, + ignoreSystemTimeChecks: action, + _forceCheckNetworkClock: action, + _updateNetworkClock: action, + _updateNetworkStatus: action, + _setDisconnected: action, + _getNetworkParameters: action, + _onCheckDiskSpace: action, + _onBlockSyncProgressUpdate: action, + _onReceiveStateDirectoryPath: action, + _toggleSplash: action, + isConnected: computed, + isSystemTimeCorrect: computed, + isSynced: computed, + syncPercentage: computed, + absoluteSlotNumber: computed, + isEpochsInfoAvailable: computed, + isVerifyingBlockchain: computed, + }); + } + // DEFINE STORE METHODS setup() { // ========== IPC CHANNELS =========== // @@ -236,11 +278,11 @@ export default class NetworkStatusStore extends Store { try { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: Requesting a restart of cardano-node'); + console.info('NetworkStatusStore: Requesting a restart of cardano-node'); await restartCardanoNodeChannel.send(); } catch (error) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.error('NetworkStatusStore: Restart of cardano-node failed', { + console.error('NetworkStatusStore: Restart of cardano-node failed', { error, }); } @@ -270,7 +312,7 @@ export default class NetworkStatusStore extends Store { _updateNetworkStatusWhenConnected = () => { if (this.isConnected) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: Connected'); + console.info('NetworkStatusStore: Connected'); this._updateNetworkStatus(); @@ -283,11 +325,11 @@ export default class NetworkStatusStore extends Store { try { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: Updating node status'); + console.info('NetworkStatusStore: Updating node status'); await setCachedCardanoStatusChannel.send(this._extractNodeStatus(this)); } catch (error) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.error('NetworkStatusStore: Error while updating node status', { + console.error('NetworkStatusStore: Error while updating node status', { error, }); } @@ -310,29 +352,25 @@ export default class NetworkStatusStore extends Store { }; _requestCardanoState = async () => { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: requesting node state'); + console.info('NetworkStatusStore: requesting node state'); const state = await cardanoStateChangeChannel.request(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info(`NetworkStatusStore: handling node state <${state}>`, { - state, - }); await this._handleCardanoNodeStateChange(state); }; _requestCardanoStatus = async () => { try { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: requesting node status'); + console.info('NetworkStatusStore: requesting node status'); // @ts-ignore ts-migrate(2554) FIXME: Expected 1-3 arguments, but got 0. const status = await getCachedCardanoStatusChannel.request(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: received cached node status', { + console.info('NetworkStatusStore: received cached node status', { status, }); - if (status) - runInAction('assigning node status', () => Object.assign(this, status)); + if (status) runInAction(() => Object.assign(this, status)); } catch (error) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.error('NetworkStatusStore: error while requesting node state', { + console.error('NetworkStatusStore: error while requesting node state', { error, }); } @@ -340,14 +378,14 @@ export default class NetworkStatusStore extends Store { _requestTlsConfig = async () => { try { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info( + console.info( 'NetworkStatusStore: requesting tls config from main process' ); const tlsConfig = await cardanoTlsConfigChannel.request(); await this._updateTlsConfig(tlsConfig); } catch (error) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.error('NetworkStatusStore: error while requesting tls config', { + console.error('NetworkStatusStore: error while requesting tls config', { error, }); } @@ -357,9 +395,9 @@ export default class NetworkStatusStore extends Store { if (config == null || isEqual(config, this.tlsConfig)) return Promise.resolve(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: received tls config from main process'); + console.info('NetworkStatusStore: received tls config from main process'); this.api.ada.setRequestConfig(config); - runInAction('updating tlsConfig', () => { + runInAction(() => { this.tlsConfig = config; }); // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. @@ -368,10 +406,8 @@ export default class NetworkStatusStore extends Store { }; _handleCardanoNodeStateChange = async (state: CardanoNodeState) => { if (state === this.cardanoNodeState) return Promise.resolve(); + // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info(`NetworkStatusStore: handling cardano-node state <${state}>`, { - state, - }); const wasConnected = this.isConnected; switch (state) { @@ -386,7 +422,7 @@ export default class NetworkStatusStore extends Store { case CardanoNodeStates.STOPPING: case CardanoNodeStates.EXITING: case CardanoNodeStates.UPDATING: - runInAction('updating tlsConfig', () => { + runInAction(() => { this.tlsConfig = null; }); @@ -400,7 +436,7 @@ export default class NetworkStatusStore extends Store { this._setDisconnected(wasConnected); } - runInAction('setting cardanoNodeState', () => { + runInAction(() => { this.cardanoNodeState = state; this.isNodeStopping = includes(NODE_STOPPING_STATES, state); this.isNodeStopped = includes(NODE_STOPPED_STATES, state); @@ -431,7 +467,7 @@ export default class NetworkStatusStore extends Store { }; // DEFINE ACTIONS - @action _toggleRTSFlagsMode = async () => { + _toggleRTSFlagsMode = async () => { this.analytics.sendEvent( EventCategories.SETTINGS, `RTS flags ${this.isRTSFlagsModeEnabled ? 'disabled' : 'enabled'}` @@ -439,60 +475,51 @@ export default class NetworkStatusStore extends Store { await toggleRTSFlagsModeChannel.send(); }; - @action _setNetworkStatusPollingInterval = () => { this._networkStatusPollingInterval = setInterval( this._updateNetworkStatus, NETWORK_STATUS_POLL_INTERVAL ); }; - @action _setNetworkClockPollingInterval = () => { this._networkClockPollingInterval = setInterval( this._updateNetworkClock, NETWORK_CLOCK_POLL_INTERVAL ); }; - @action _setNetworkParametersPollingInterval = () => { this._networkParametersPollingInterval = setInterval( this._getNetworkParameters, DECENTRALIZATION_LEVEL_POLLING_INTERVAL ); }; - @action _clearNetworkStatusPollingInterval = () => { if (this._networkStatusPollingInterval) { clearInterval(this._networkStatusPollingInterval); this._networkStatusPollingInterval = null; } }; - @action _clearNetworkClockPollingInterval = () => { if (this._networkClockPollingInterval) { clearInterval(this._networkClockPollingInterval); this._networkClockPollingInterval = null; } }; - @action _clearNetworkParametersPollingInterval = () => { if (this._networkParametersPollingInterval) { clearInterval(this._networkParametersPollingInterval); this._networkParametersPollingInterval = null; } }; - @action ignoreSystemTimeChecks = (flag = true) => { this.isSystemTimeIgnored = flag; }; - @action _forceCheckNetworkClock = () => { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: Force checking network clock...'); + console.info('NetworkStatusStore: Force checking network clock...'); this._updateNetworkClock(true); }; - @action _updateNetworkClock = async (isForceCheck = false) => { // Skip checking network clock if we are not connected or system time is ignored if (!this.isNodeResponding || (this.isSystemTimeIgnored && !isForceCheck)) @@ -510,18 +537,17 @@ export default class NetworkStatusStore extends Store { } // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: Checking network clock...', { + console.info('NetworkStatusStore: Checking network clock...', { isForceCheck, }); try { - const networkClock: GetNetworkClockResponse = await this.getNetworkClockRequest.execute( - { + const networkClock: GetNetworkClockResponse = + await this.getNetworkClockRequest.execute({ isForceCheck, - } - ).promise; + }).promise; // System time is correct if local time difference is below allowed threshold - runInAction('update localTimeDifference and isNodeTimeCorrect', () => { + runInAction(() => { // Update localTimeDifference only in case NTP check status is not still pending if (networkClock.status !== 'pending') { this.localTimeDifference = networkClock.offset; @@ -533,7 +559,7 @@ export default class NetworkStatusStore extends Store { } }); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info('NetworkStatusStore: Network clock response received', { + console.info('NetworkStatusStore: Network clock response received', { localTimeDifference: this.localTimeDifference, isNodeTimeCorrect: this.isNodeTimeCorrect, allowedDifference: ALLOWED_TIME_DIFFERENCE, @@ -541,7 +567,6 @@ export default class NetworkStatusStore extends Store { }); } catch (error) {} // eslint-disable-line }; - @action _updateNetworkStatus = async () => { // In case we haven't received TLS config we shouldn't trigger any API calls if (!this.tlsConfig) return; @@ -549,14 +574,14 @@ export default class NetworkStatusStore extends Store { const wasConnected = this.isConnected; try { - const networkStatus: GetNetworkInfoResponse = await this.getNetworkInfoRequest.execute() - .promise; + const networkStatus: GetNetworkInfoResponse = + await this.getNetworkInfoRequest.execute().promise; // In case we no longer have TLS config we ignore all API call responses // as this means we are in the Cardano shutdown (stopping|exiting|updating) sequence if (!this.tlsConfig) { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug( + console.debug( 'NetworkStatusStore: Ignoring NetworkStatusRequest result during Cardano shutdown sequence...' ); return; @@ -578,18 +603,15 @@ export default class NetworkStatusStore extends Store { } // We got response which means node is responding - runInAction('update isNodeResponding', () => { + runInAction(() => { this.isNodeResponding = true; }); - runInAction( - 'update localTip, networkTip, nextEpoch and futureEpoch', - () => { - this.localTip = localTip; - this.networkTip = networkTip; - this.nextEpoch = nextEpoch; - this.futureEpoch = futureEpoch; - } - ); + runInAction(() => { + this.localTip = localTip; + this.networkTip = networkTip; + this.nextEpoch = nextEpoch; + this.futureEpoch = futureEpoch; + }); if (this._networkStatus === NETWORK_STATUS.CONNECTING) { // We are connected for the first time, move on to syncing stage @@ -598,20 +620,20 @@ export default class NetworkStatusStore extends Store { const connectingTimeDelta = this._getStartupTimeDelta(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info(`Connected after ${connectingTimeDelta} milliseconds`, { + console.info(`Connected after ${connectingTimeDelta} milliseconds`, { connectingTimeDelta, }); } // Update sync progress const lastSyncProgress = this.syncProgress; - runInAction('update syncProgress', () => { + runInAction(() => { this.syncProgress = syncProgress; }); - runInAction('update isNodeInSync', () => { + runInAction(() => { this.isNodeInSync = this.syncProgress === 100; }); - runInAction('update isSyncProgressStalling', () => { + runInAction(() => { // Check if sync progress is stalling const hasSyncProgressChanged = this.syncProgress !== lastSyncProgress; @@ -631,7 +653,7 @@ export default class NetworkStatusStore extends Store { this.isSyncProgressStalling = lastSyncProgressChangeStall > MAX_ALLOWED_STALL_DURATION; }); - runInAction('update isNodeSyncing', () => { + runInAction(() => { this.isNodeSyncing = !this.isSyncProgressStalling; }); @@ -644,30 +666,31 @@ export default class NetworkStatusStore extends Store { const syncingTimeDelta = this._getStartupTimeDelta(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.info(`Synced after ${syncingTimeDelta} milliseconds`, { + console.info(`Synced after ${syncingTimeDelta} milliseconds`, { syncingTimeDelta, }); } + // this._requestCardanoState(); if (wasConnected !== this.isConnected) { if (!this.isConnected) { if (!this.hasBeenConnected) { - runInAction('update hasBeenConnected', () => { + runInAction(() => { this.hasBeenConnected = true; }); } // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('NetworkStatusStore: Connection Lost. Reconnecting...'); + console.debug('NetworkStatusStore: Connection Lost. Reconnecting...'); } else if (this.hasBeenConnected) { // Make sure all wallets data is fully reloaded after the connection is re-established this.stores.wallets.resetWalletsData(); // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('NetworkStatusStore: Connection Restored'); + console.debug('NetworkStatusStore: Connection Restored'); } if (this.isTlsCertInvalid) { - runInAction('set isTlsCertInvalid = false', () => { + runInAction(() => { this.isTlsCertInvalid = false; }); } @@ -682,13 +705,12 @@ export default class NetworkStatusStore extends Store { this._setDisconnected(wasConnected); if (error instanceof TlsCertificateNotValidError) { - runInAction('set isTlsCertInvalid = true', () => { + runInAction(() => { this.isTlsCertInvalid = true; }); } } }; - @action _setDisconnected = (wasConnected: boolean) => { this.isNodeResponding = false; this.isNodeSyncing = false; @@ -698,23 +720,22 @@ export default class NetworkStatusStore extends Store { if (wasConnected) { if (!this.hasBeenConnected) { - runInAction('update hasBeenConnected', () => { + runInAction(() => { this.hasBeenConnected = true; }); } // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - logger.debug('NetworkStatusStore: Connection Lost. Reconnecting...'); + console.debug('NetworkStatusStore: Connection Lost. Reconnecting...'); } }; - @action _getNetworkParameters = async () => { // Skip checking network parameters if we are not connected if (!this.isNodeResponding) return; try { - const networkParameters: GetNetworkParametersResponse = await this.getNetworkParametersRequest.execute() - .promise; + const networkParameters: GetNetworkParametersResponse = + await this.getNetworkParametersRequest.execute().promise; let { isShelleyActivated, isShelleyPending, @@ -754,7 +775,7 @@ export default class NetworkStatusStore extends Store { } } - runInAction('Update Decentralization Progress', () => { + runInAction(() => { this.decentralizationProgress = decentralizationLevel.quantity; this.isShelleyActivated = isShelleyActivated; this.isShelleyPending = isShelleyPending; @@ -763,16 +784,16 @@ export default class NetworkStatusStore extends Store { this.isAlonzoPending = isAlonzoPending; this.alonzoActivationTime = alonzoActivationTime; }); - runInAction('Update Desired Pool Number', () => { + runInAction(() => { this.desiredPoolNumber = desiredPoolNumber || INITIAL_DESIRED_POOLS_NUMBER; }); - runInAction('Update Epoch Config', () => { + runInAction(() => { this.slotLength = slotLength.quantity; this.epochLength = epochLength.quantity; }); } catch (e) { - runInAction('Clear Decentralization Progress', () => { + runInAction(() => { this.decentralizationProgress = 0; }); } @@ -783,7 +804,6 @@ export default class NetworkStatusStore extends Store { openLocalDirectoryChannel.send(path); } - @action _onCheckDiskSpace = ({ isNotEnoughDiskSpace, diskSpaceRequired, @@ -809,24 +829,22 @@ export default class NetworkStatusStore extends Store { return Promise.resolve(); }; - @action _onBlockSyncProgressUpdate = async ( + _onBlockSyncProgressUpdate = async ( blockSyncProgress: GetBlockSyncProgressMainResponse ) => { this.blockSyncProgress = blockSyncProgress; }; - @action _onReceiveStateDirectoryPath = (stateDirectoryPath: string) => { this.stateDirectoryPath = stateDirectoryPath; }; - @action _toggleSplash = () => { - runInAction('Toggle splash visibility', () => { + runInAction(() => { this.isSplashShown = !this.isSplashShown; }); }; _resetSystemTime = () => { - runInAction('Reset system time', () => { + runInAction(() => { this.getNetworkClockRequest.reset(); this.localTimeDifference = null; this.isNodeTimeCorrect = true; @@ -834,33 +852,27 @@ export default class NetworkStatusStore extends Store { }); }; - @computed get isConnected(): boolean { return this.isNodeResponding && this.isNodeSyncing; } - @computed get isSystemTimeCorrect(): boolean { return this.isNodeTimeCorrect || this.isSystemTimeIgnored; } - @computed get isSynced(): boolean { return this.isConnected && this.isNodeInSync && this.isSystemTimeCorrect; } - @computed get syncPercentage(): number { return this.syncProgress || 0; } - @computed get absoluteSlotNumber(): number { const { networkTip } = this; return get(networkTip, 'absoluteSlotNumber', 0); } - @computed get isEpochsInfoAvailable(): boolean { const { networkTip, nextEpoch } = this; return ( @@ -871,11 +883,10 @@ export default class NetworkStatusStore extends Store { ); } - @computed get isVerifyingBlockchain(): boolean { - return ( - !this.isConnected && - Object.values(this.blockSyncProgress).some((value) => value < 100) + const isNotReady = Object.values(this.blockSyncProgress).some( + (value) => value < 100 ); + return !this.isConnected && isNotReady; } } diff --git a/source/renderer/app/stores/NewsFeedStore.ts b/source/renderer/app/stores/NewsFeedStore.ts index 88dbb52f44..3a5efa860c 100644 --- a/source/renderer/app/stores/NewsFeedStore.ts +++ b/source/renderer/app/stores/NewsFeedStore.ts @@ -1,4 +1,10 @@ -import { observable, action, runInAction, computed } from 'mobx'; +import { + observable, + action, + runInAction, + computed, + makeObservable, +} from 'mobx'; import { map, get, find } from 'lodash'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; @@ -14,6 +20,9 @@ import type { NewsItem, MarkNewsAsReadResponse, } from '../api/news/types'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; +import { AnalyticsTracker } from '../analytics'; const { isTest, version, isDev } = global.environment; const AVAILABLE_NEWSFEED_EVENT_ACTIONS = [ @@ -21,32 +30,22 @@ const AVAILABLE_NEWSFEED_EVENT_ACTIONS = [ 'OPEN_DIAGNOSTIC_DIALOG', ]; export default class NewsFeedStore extends Store { - @observable rawNews: Array | null | undefined = null; - @observable newsUpdatedAt: Date | null | undefined = null; - @observable fetchingNewsFailed = false; - @observable getNewsRequest: Request = new Request(this.api.ada.getNews); - @observable getReadNewsRequest: Request = new Request( this.api.localStorage.getReadNews ); - @observable markNewsAsReadRequest: Request = new Request( this.api.localStorage.markNewsAsRead ); - @observable markNewsAsUnreadRequest: Request = new Request( this.api.localStorage.markNewsAsUnread ); - @observable // @ts-ignore ts-migrate(2503) FIXME: Cannot find namespace 'News'. openedAlert: News.News | null | undefined = null; - @observable fetchLocalNews = false; - @observable rawNewsJsonQA: GetNewsResponse | null | undefined = null; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. pollingNewsIntervalId: IntervalID | null | undefined = null; @@ -55,6 +54,36 @@ export default class NewsFeedStore extends Store { // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. pollingNewsOnIncidentIntervalId: IntervalID | null | undefined = null; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + rawNews: observable, + newsUpdatedAt: observable, + fetchingNewsFailed: observable, + getNewsRequest: observable, + getReadNewsRequest: observable, + markNewsAsReadRequest: observable, + markNewsAsUnreadRequest: observable, + openedAlert: observable, + fetchLocalNews: observable, + rawNewsJsonQA: observable, + getNews: action, + markNewsAsRead: action, + openAlert: action, + closeOpenedAlert: action, + _setFetchingNewsFailed: action, + proceedNewsAction: action, + setFakedNewsfeed: action, + newsFeedData: computed, + isLoadingNews: computed, + }); + } + setup() { // Fetch news on app start this.getNews({ @@ -70,7 +99,6 @@ export default class NewsFeedStore extends Store { } } - @action getNews = async (params?: { isInit: boolean }) => { let rawNews; @@ -180,13 +208,12 @@ export default class NewsFeedStore extends Store { await this.getReadNewsRequest.execute(); if (rawNews) { - runInAction('set news data', () => { + runInAction(() => { this.rawNews = get(rawNews, 'items', []); this.newsUpdatedAt = get(rawNews, 'updatedAt', null); }); } }; - @action markNewsAsRead = async (newsId: number[]) => { // Set news timestamp to LC // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message @@ -195,7 +222,6 @@ export default class NewsFeedStore extends Store { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.getReadNewsRequest.execute(); }; - @action openAlert = (newsId: number) => { if (this.getNewsRequest.wasExecuted) { const alertToOpen = this.newsFeedData.alerts.all.find( @@ -207,15 +233,12 @@ export default class NewsFeedStore extends Store { } } }; - @action closeOpenedAlert = () => { this.openedAlert = null; }; - @action _setFetchingNewsFailed = (fetchingNewsFailed: boolean) => { this.fetchingNewsFailed = fetchingNewsFailed; }; - @action // @ts-ignore ts-migrate(2503) FIXME: Cannot find namespace 'News'. proceedNewsAction = (newsItem: News.News, e: MouseEvent) => { const { url, route, event } = newsItem.action; @@ -249,7 +272,6 @@ export default class NewsFeedStore extends Store { } } }; - @action setFakedNewsfeed = (params: { isAutomaticUpdateTest: boolean | null | undefined; appVersion?: string; @@ -279,8 +301,6 @@ export default class NewsFeedStore extends Store { } }; - @computed - // @ts-ignore ts-migrate(2503) FIXME: Cannot find namespace 'News'. get newsFeedData(): News.NewsCollection { const { currentLocale } = this.stores.profile; const readNews = this.getReadNewsRequest.result; @@ -332,7 +352,6 @@ export default class NewsFeedStore extends Store { return new News.NewsCollection(news); } - @computed get isLoadingNews() { return this.fetchingNewsFailed || !this.rawNews; } diff --git a/source/renderer/app/stores/ProfileStore.ts b/source/renderer/app/stores/ProfileStore.ts index 57c0b5e549..1130b95a44 100644 --- a/source/renderer/app/stores/ProfileStore.ts +++ b/source/renderer/app/stores/ProfileStore.ts @@ -1,4 +1,10 @@ -import { action, computed, observable, runInAction } from 'mobx'; +import { + action, + computed, + makeObservable, + observable, + runInAction, +} from 'mobx'; import BigNumber from 'bignumber.js'; import { camelCase, includes } from 'lodash'; import { toJS } from '../../../common/utils/helper'; @@ -41,110 +47,150 @@ import { TIME_OPTIONS, } from '../config/profileConfig'; import { buildSystemInfo } from '../utils/buildSystemInfo'; -import { AnalyticsAcceptanceStatus, EventCategories } from '../analytics/types'; +import { + AnalyticsAcceptanceStatus, + AnalyticsTracker, + EventCategories, +} from '../analytics/types'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export default class ProfileStore extends Store { - @observable systemLocale: Locale = LOCALES.english; - @observable systemNumberFormat: string = NUMBER_OPTIONS[0].value; - @observable systemDateFormatEnglish: string = DATE_ENGLISH_OPTIONS[0].value; - @observable systemDateFormatJapanese: string = DATE_JAPANESE_OPTIONS[0].value; - @observable systemTimeFormat: string = TIME_OPTIONS[0].value; - @observable getProfileLocaleRequest: Request = new Request( this.api.localStorage.getUserLocale ); - @observable setProfileLocaleRequest: Request = new Request( this.api.localStorage.setUserLocale ); - @observable getProfileNumberFormatRequest: Request = new Request( this.api.localStorage.getUserNumberFormat ); - @observable setProfileNumberFormatRequest: Request = new Request( this.api.localStorage.setUserNumberFormat ); - @observable getProfileDateFormatEnglishRequest: Request = new Request( this.api.localStorage.getUserDateFormatEnglish ); - @observable setProfileDateFormatEnglishRequest: Request = new Request( this.api.localStorage.setUserDateFormatEnglish ); - @observable getProfileDateFormatJapaneseRequest: Request = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'ProfileStor... Remove this comment to see the full error message this.api.localStorage.getUserDateFormatJapanese ); - @observable setProfileDateFormatJapaneseRequest: Request = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'ProfileStor... Remove this comment to see the full error message this.api.localStorage.setUserDateFormatJapanese ); - @observable getProfileTimeFormatRequest: Request = new Request( this.api.localStorage.getUserTimeFormat ); - @observable setProfileTimeFormatRequest: Request = new Request( this.api.localStorage.setUserTimeFormat ); - @observable getTermsOfUseAcceptanceRequest: Request = new Request( this.api.localStorage.getTermsOfUseAcceptance ); - @observable setTermsOfUseAcceptanceRequest: Request = new Request( this.api.localStorage.setTermsOfUseAcceptance ); - @observable - getAnalyticsAcceptanceRequest: Request< - AnalyticsAcceptanceStatus - > = new Request(this.api.localStorage.getAnalyticsAcceptance); - @observable + getAnalyticsAcceptanceRequest: Request = + new Request(this.api.localStorage.getAnalyticsAcceptance); setAnalyticsAcceptanceRequest: Request = new Request( this.api.localStorage.setAnalyticsAcceptance ); - @observable getDataLayerMigrationAcceptanceRequest: Request = new Request( this.api.localStorage.getDataLayerMigrationAcceptance ); - @observable setDataLayerMigrationAcceptanceRequest: Request = new Request( this.api.localStorage.setDataLayerMigrationAcceptance ); - @observable getThemeRequest: Request = new Request( this.api.localStorage.getUserTheme ); - @observable setThemeRequest: Request = new Request( this.api.localStorage.setUserTheme ); - @observable error: LocalizableError | null | undefined = null; - @observable logFiles: LogFiles = null; - @observable compressedLogsFilePath: string | null | undefined = null; - @observable compressedLogsStatus: CompressedLogStatus = {}; - @observable desktopDirectoryPath = ''; - @observable isSubmittingBugReport = false; - @observable isInitialScreen = false; - @observable isRTSModeRecommendationAcknowledged = false; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + systemLocale: observable, + systemNumberFormat: observable, + systemDateFormatEnglish: observable, + systemDateFormatJapanese: observable, + systemTimeFormat: observable, + getProfileLocaleRequest: observable, + setProfileLocaleRequest: observable, + getProfileNumberFormatRequest: observable, + setProfileNumberFormatRequest: observable, + getProfileDateFormatEnglishRequest: observable, + setProfileDateFormatEnglishRequest: observable, + getProfileDateFormatJapaneseRequest: observable, + setProfileDateFormatJapaneseRequest: observable, + getProfileTimeFormatRequest: observable, + setProfileTimeFormatRequest: observable, + getTermsOfUseAcceptanceRequest: observable, + setTermsOfUseAcceptanceRequest: observable, + getAnalyticsAcceptanceRequest: observable, + setAnalyticsAcceptanceRequest: observable, + getDataLayerMigrationAcceptanceRequest: observable, + setDataLayerMigrationAcceptanceRequest: observable, + getThemeRequest: observable, + setThemeRequest: observable, + error: observable, + logFiles: observable, + compressedLogsFilePath: observable, + compressedLogsStatus: observable, + desktopDirectoryPath: observable, + isSubmittingBugReport: observable, + isInitialScreen: observable, + isRTSModeRecommendationAcknowledged: observable, + currentLocale: computed, + hasLoadedCurrentLocale: computed, + isCurrentLocaleSet: computed, + currentTheme: computed, + isCurrentThemeSet: computed, + hasLoadedCurrentTheme: computed, + currentNumberFormat: computed, + currentDateFormat: computed, + currentDateEnglishFormat: computed, + currentDateJapaneseFormat: computed, + currentTimeFormat: computed, + currentTimeFormatShort: computed, + termsOfUse: computed, + hasLoadedTermsOfUseAcceptance: computed, + areTermsOfUseAccepted: computed, + analyticsAcceptanceStatus: computed, + hasLoadedDataLayerMigrationAcceptance: computed, + isDataLayerMigrationAccepted: computed, + isProfilePage: computed, + isSettingsPage: computed, + _acknowledgeRTSFlagsModeRecommendation: action, + _onReceiveSystemLocale: action, + _onReceiveDesktopDirectoryPath: action, + _reset: action, + }); + } + /* eslint-enable max-len */ setup() { // @ts-ignore ts-migrate(2339) FIXME: Property 'actions' does not exist on type 'Profile... Remove this comment to see the full error message @@ -197,22 +243,18 @@ export default class ProfileStore extends Store { }); }; - @computed get currentLocale(): Locale { return requestGetterLocale(this.getProfileLocaleRequest, this.systemLocale); } - @computed get hasLoadedCurrentLocale(): boolean { return hasLoadedRequest(this.getProfileLocaleRequest); } - @computed get isCurrentLocaleSet(): boolean { return isRequestSet(this.getProfileLocaleRequest); } - @computed get currentTheme(): string { // Default theme handling let systemValue; @@ -229,17 +271,14 @@ export default class ProfileStore extends Store { return requestGetter(this.getThemeRequest, systemValue); } - @computed get isCurrentThemeSet(): boolean { return isRequestSet(this.getThemeRequest); } - @computed get hasLoadedCurrentTheme(): boolean { return hasLoadedRequest(this.getThemeRequest); } - @computed get currentNumberFormat(): string { return requestGetter( this.getProfileNumberFormatRequest, @@ -247,14 +286,12 @@ export default class ProfileStore extends Store { ); } - @computed get currentDateFormat(): string { return this.currentLocale === 'en-US' ? this.currentDateEnglishFormat : this.currentDateJapaneseFormat; } - @computed get currentDateEnglishFormat(): string { return requestGetter( this.getProfileDateFormatEnglishRequest, @@ -262,7 +299,6 @@ export default class ProfileStore extends Store { ); } - @computed get currentDateJapaneseFormat(): string { return requestGetter( this.getProfileDateFormatJapaneseRequest, @@ -270,7 +306,6 @@ export default class ProfileStore extends Store { ); } - @computed get currentTimeFormat(): string { return requestGetter( this.getProfileTimeFormatRequest, @@ -278,18 +313,15 @@ export default class ProfileStore extends Store { ); } - @computed get currentTimeFormatShort(): string { return this.currentTimeFormat.replace(':ss', ''); } - @computed get termsOfUse(): string { return require(`../i18n/locales/terms-of-use/${this.currentLocale}.md`) .default; } - @computed get hasLoadedTermsOfUseAcceptance(): boolean { return ( this.getTermsOfUseAcceptanceRequest.wasExecuted && @@ -297,18 +329,15 @@ export default class ProfileStore extends Store { ); } - @computed get areTermsOfUseAccepted(): boolean { // @ts-ignore ts-migrate(2367) FIXME: This condition will always return 'false' since th... Remove this comment to see the full error message return this.getTermsOfUseAcceptanceRequest.result === true; } - @computed get analyticsAcceptanceStatus(): AnalyticsAcceptanceStatus { return this.getAnalyticsAcceptanceRequest.result; } - @computed get hasLoadedDataLayerMigrationAcceptance(): boolean { return ( this.getDataLayerMigrationAcceptanceRequest.wasExecuted && @@ -316,18 +345,15 @@ export default class ProfileStore extends Store { ); } - @computed get isDataLayerMigrationAccepted(): boolean { return this.getDataLayerMigrationAcceptanceRequest.result === true; } - @computed get isProfilePage(): boolean { const { currentRoute } = this.stores.app; return includes(ROUTES.PROFILE, currentRoute); } - @computed get isSettingsPage(): boolean { const { currentRoute } = this.stores.app; return includes(ROUTES.SETTINGS, currentRoute); @@ -418,7 +444,6 @@ export default class ProfileStore extends Store { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.getDataLayerMigrationAcceptanceRequest.execute(); }; - @action _acknowledgeRTSFlagsModeRecommendation = () => { this.isRTSModeRecommendationAcknowledged = true; }; @@ -438,7 +463,7 @@ export default class ProfileStore extends Store { (this.hasLoadedCurrentLocale && !this.isCurrentLocaleSet) || this.isInitialScreen ) { - runInAction('Set `isInitialScreen` true', () => { + runInAction(() => { this.isInitialScreen = true; }); // @ts-ignore ts-migrate(2339) FIXME: Property 'actions' does not exist on type 'Profile... Remove this comment to see the full error message @@ -546,16 +571,15 @@ export default class ProfileStore extends Store { } }; _compressLogs = action(async ({ logs }) => { - const { - fileName = generateFileNameWithTimestamp(), - } = this.compressedLogsStatus; + const { fileName = generateFileNameWithTimestamp() } = + this.compressedLogsStatus; try { const outputPath = await compressLogsChannel.request({ logs: toJS(logs), compressedFileName: fileName, }); - runInAction('ProfileStore::_compressLogs:success', () => { + runInAction(() => { this.compressedLogsFilePath = outputPath; const { isDownloading, destination } = this.compressedLogsStatus; @@ -568,7 +592,7 @@ export default class ProfileStore extends Store { } }); } catch (error) { - runInAction('ProfileStore::_compressLogs:error', () => { + runInAction(() => { this.isSubmittingBugReport = false; this.error = new WalletSupportRequestLogsCompressError(); }); @@ -702,15 +726,12 @@ export default class ProfileStore extends Store { ); } ); - @action _onReceiveSystemLocale = (systemLocale: Locale) => { this.systemLocale = systemLocale; }; - @action _onReceiveDesktopDirectoryPath = (desktopDirectoryPath: string) => { this.desktopDirectoryPath = desktopDirectoryPath; }; - @action _reset = () => { this.error = null; this.compressedLogsFilePath = null; diff --git a/source/renderer/app/stores/SidebarStore.ts b/source/renderer/app/stores/SidebarStore.ts index c641e58690..6abd9991fd 100644 --- a/source/renderer/app/stores/SidebarStore.ts +++ b/source/renderer/app/stores/SidebarStore.ts @@ -1,4 +1,4 @@ -import { action, computed, observable } from 'mobx'; +import { action, computed, makeObservable, observable } from 'mobx'; import { debounce, get } from 'lodash'; import { sidebarConfig } from '../config/sidebarConfig'; import type { SidebarCategoryInfo } from '../config/sidebarConfig'; @@ -10,23 +10,47 @@ import type { import { WalletSortBy, WalletSortOrder } from '../types/sidebarTypes'; import { changeWalletSorting, sortWallets } from '../utils/walletSorting'; import Store from './lib/Store'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export default class SidebarStore extends Store { - @observable CATEGORIES: Array = sidebarConfig.CATEGORIES_LIST; - @observable activeSidebarCategory: string = this.CATEGORIES[0].route; - @observable isShowingSubMenus = true; - @observable walletSortConfig: WalletSortConfig = { sortBy: WalletSortBy.Date, sortOrder: WalletSortOrder.Asc, }; - @observable searchValue = ''; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + CATEGORIES: observable, + activeSidebarCategory: observable, + isShowingSubMenus: observable, + walletSortConfig: observable, + searchValue: observable, + wallets: computed.struct, + onChangeWalletSortType: action, + onSearchValueUpdated: action, + _configureCategories: action, + _onActivateSidebarCategory: action, + _onWalletSelected: action, + _setActivateSidebarCategory: action, + _resetActivateSidebarCategory: action, + _showSubMenus: action, + _hideSubMenus: action, + _toggleSubMenus: action, + }); + } + setup() { const { sidebar: sidebarActions } = this.actions; sidebarActions.showSubMenus.listen(this._showSubMenus); @@ -46,14 +70,9 @@ export default class SidebarStore extends Store { // We need to use computed.struct for computed objects (so they are structurally compared // for equality instead of idendity (which would always invalidate) // https://alexhisen.gitbooks.io/mobx-recipes/content/use-computedstruct-for-computed-objects.html - @computed.struct get wallets(): Array { - const { - networkStatus, - wallets, - walletSettings, - hardwareWallets, - } = this.stores; + const { networkStatus, wallets, walletSettings, hardwareWallets } = + this.stores; const { hardwareWalletsConnectionData } = hardwareWallets; const shelleyWallets = sortWallets({ wallets: wallets.all.filter((w) => !w.isLegacy), @@ -72,9 +91,8 @@ export default class SidebarStore extends Store { [wallet.id, 'disconnected'], true ); - const { - hasNotification, - } = walletSettings.getWalletsRecoveryPhraseVerificationData(wallet.id); + const { hasNotification } = + walletSettings.getWalletsRecoveryPhraseVerificationData(wallet.id); return { id: wallet.id, title: wallet.name, @@ -91,7 +109,6 @@ export default class SidebarStore extends Store { }); } - @action onChangeWalletSortType = (sortBy: WalletSortByOptions) => { this.walletSortConfig = changeWalletSorting({ sortBy, @@ -104,12 +121,10 @@ export default class SidebarStore extends Store { 'Changed wallet sorting settings' ); }; - @action onSearchValueUpdated = (searchValue: string) => { this.searchValue = searchValue; this._sendSearchAnalyticsEvent(); }; - @action _configureCategories = () => { const { // @ts-ignore ts-migrate(2339) FIXME: Property 'isFlight' does not exist on type 'typeof... Remove this comment to see the full error message @@ -117,10 +132,8 @@ export default class SidebarStore extends Store { // @ts-ignore ts-migrate(2339) FIXME: Property 'environment' does not exist on type 'typ... Remove this comment to see the full error message environment: { isDev, isMainnet }, } = global; - const { - CATEGORIES_BY_NAME: categories, - CATEGORIES_LIST: list, - } = sidebarConfig; + const { CATEGORIES_BY_NAME: categories, CATEGORIES_LIST: list } = + sidebarConfig; const categoryValidation: Record< string, boolean | ((...args: Array) => any) @@ -148,7 +161,6 @@ export default class SidebarStore extends Store { ); this.CATEGORIES = categoriesFilteredList; }; - @action _onActivateSidebarCategory = (params: { category: string; showSubMenu?: boolean; @@ -168,29 +180,23 @@ export default class SidebarStore extends Store { this.isShowingSubMenus = showSubMenu; } }; - @action _onWalletSelected = ({ walletId }: { walletId: string }) => { this.stores.wallets.goToWalletRoute(walletId); }; - @action _setActivateSidebarCategory = (category: string) => { this.activeSidebarCategory = category; }; - @action _resetActivateSidebarCategory = () => { this.activeSidebarCategory = ''; }; - @action _showSubMenus = () => { this.isShowingSubMenus = true; this.analytics.sendEvent(EventCategories.LAYOUT, 'Toggled submenu'); }; - @action _hideSubMenus = () => { this.isShowingSubMenus = false; this.analytics.sendEvent(EventCategories.LAYOUT, 'Toggled submenu'); }; - @action _toggleSubMenus = () => { this.isShowingSubMenus = !this.isShowingSubMenus; this.analytics.sendEvent(EventCategories.LAYOUT, 'Toggled submenu'); diff --git a/source/renderer/app/stores/StakingStore.ts b/source/renderer/app/stores/StakingStore.ts index fb33de3e34..47fd1d639e 100644 --- a/source/renderer/app/stores/StakingStore.ts +++ b/source/renderer/app/stores/StakingStore.ts @@ -1,7 +1,13 @@ -import { computed, action, observable, runInAction } from 'mobx'; +import { + computed, + action, + observable, + runInAction, + makeObservable, +} from 'mobx'; import BigNumber from 'bignumber.js'; import path from 'path'; -import { orderBy, find, map, get, debounce } from 'lodash'; +import { orderBy, find, map, debounce } from 'lodash'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; import { ROUTES } from '../routes-config'; @@ -36,60 +42,38 @@ import { showSaveDialogChannel } from '../ipc/show-file-dialog-channels'; import { generateFileNameWithTimestamp } from '../../../common/utils/files'; import type { RedeemItnRewardsStep } from '../types/stakingTypes'; import type { CsvFileContent } from '../../../common/types/csv-request.types'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export default class StakingStore extends Store { - @observable isDelegationTransactionPending = false; - @observable fetchingStakePoolsFailed = false; - @observable selectedDelegationWalletId = null; - @observable stake = INITIAL_DELEGATION_FUNDS; - @observable isRanking = false; - @observable smashServerUrl: string | null | undefined = null; - @observable smashServerUrlError: LocalizableError | null | undefined = null; - @observable smashServerLoading = false; - @observable stakePoolsListViewTooltipVisible = true; /* ---------- Redeem ITN Rewards ---------- */ - @observable redeemStep: RedeemItnRewardsStep | null | undefined = null; - @observable redeemRecoveryPhrase: Array | null | undefined = null; - @observable redeemWallet: Wallet | null | undefined = null; - @observable walletName: string | null | undefined = null; - @observable transactionFees: BigNumber | null | undefined = null; - @observable redeemedRewards: BigNumber | null | undefined = null; - @observable isSubmittingReedem = false; - @observable isCalculatingReedemFees = false; - @observable redeemSuccess: boolean | null | undefined = null; - @observable configurationStepError: LocalizableError | null | undefined = null; - @observable confirmationStepError: LocalizableError | null | undefined = null; /* ---------- Stake Pools Fetching Tracker ---------- */ - @observable isFetchingStakePools = false; - @observable numberOfStakePoolsFetched = 0; - @observable cyclesWithoutIncreasingStakePools = 0; - @observable stakingInfoWasOpen = false; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. pollingStakePoolsInterval: IntervalID | null | undefined = null; @@ -103,6 +87,90 @@ export default class StakingStore extends Store { stakePoolsFetchTrackerInterval: IntervalID | null | undefined = null; _delegationFeeCalculationWalletId: string | null | undefined = null; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + isDelegationTransactionPending: observable, + fetchingStakePoolsFailed: observable, + selectedDelegationWalletId: observable, + stake: observable, + isRanking: observable, + smashServerUrl: observable, + smashServerUrlError: observable, + smashServerLoading: observable, + stakePoolsListViewTooltipVisible: observable, + redeemStep: observable, + redeemRecoveryPhrase: observable, + redeemWallet: observable, + walletName: observable, + transactionFees: observable, + redeemedRewards: observable, + isSubmittingReedem: observable, + isCalculatingReedemFees: observable, + redeemSuccess: observable, + configurationStepError: observable, + confirmationStepError: observable, + isFetchingStakePools: observable, + numberOfStakePoolsFetched: observable, + cyclesWithoutIncreasingStakePools: observable, + stakingInfoWasOpen: observable, + joinStakePoolRequest: observable, + quitStakePoolRequest: observable, + stakePoolsRequest: observable, + calculateDelegationFeeRequest: observable, + getRedeemItnRewardsFeeRequest: observable, + requestRedeemItnRewardsRequest: observable, + getSmashSettingsRequest: observable, + updateSmashSettingsRequest: observable, + _getSmashSettingsRequest: action, + _setSelectedDelegationWalletId: action, + _setStake: action, + _rankStakePools: action, + _selectSmashServerUrl: action, + _startStakePoolsFetchTracker: action, + _getStakingInfoWasOpen: action, + _setStakingInfoWasOpen: action, + _getStakePoolsListViewTooltip: action, + hideStakePoolsListViewTooltip: action, + _stakePoolsFetchTracker: action, + _stopStakePoolsFetchTracker: action, + _resetSmashServerError: action, + _joinStakePool: action, + _quitStakePool: action, + checkDelegationTransaction: action, + resetStakePoolTransactionChecker: action, + _requestCSVFile: action, + currentRoute: computed, + isStakingPage: computed, + maxDelegationFunds: computed, + stakePools: computed, + recentStakePools: computed, + isStakingDelegationCountdown: computed, + rewards: computed, + showCountdown: action, + getStakePoolsData: action, + _resetPolling: action, + _resetIsRanking: action, + _setFakePoller: action, + _setFakedStakePools: action, + _goToConfigurationStep: action, + _goToConfirmationStep: action, + _goToResultStep: action, + _onCalculateRedeemWalletFees: action, + _onRedeemStart: action, + _onConfigurationContinue: action, + _onConfirmationContinue: action, + _onResultContinue: action, + _resetRedeemItnRewards: action, + _closeRedeemDialog: action, + }); + } + setup() { const { staking: stakingActions, @@ -152,51 +220,41 @@ export default class StakingStore extends Store { } // REQUESTS - @observable joinStakePoolRequest: Request = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message this.api.ada.joinStakePool ); - @observable quitStakePoolRequest: Request = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message this.api.ada.quitStakePool ); - @observable stakePoolsRequest: Request> = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message this.api.ada.getStakePools ); - @observable - calculateDelegationFeeRequest: Request< - DelegationCalculateFeeResponse - > = new Request( - // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message - this.api.ada.calculateDelegationFee - ); + calculateDelegationFeeRequest: Request = + new Request( + // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message + this.api.ada.calculateDelegationFee + ); // @REDEEM TODO: Proper type it when the API endpoint is implemented. - @observable getRedeemItnRewardsFeeRequest: Request = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message this.api.ada.getRedeemItnRewardsFee ); - @observable requestRedeemItnRewardsRequest: Request = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message this.api.ada.requestRedeemItnRewards ); - @observable getSmashSettingsRequest: Request = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message this.api.ada.getSmashSettings ); - @observable updateSmashSettingsRequest: Request = new Request( // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message this.api.ada.updateSmashSettings ); // =================== PUBLIC API ==================== // - @action _getSmashSettingsRequest = async () => { this.smashServerLoading = true; // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message @@ -224,7 +282,6 @@ export default class StakingStore extends Store { this.smashServerLoading = false; }); }; - @action _setSelectedDelegationWalletId = (walletId: string) => { this.selectedDelegationWalletId = walletId; }; @@ -236,18 +293,15 @@ export default class StakingStore extends Store { ); }, 5000); - @action _setStake = (stake: number) => { this.stake = stake; this._sendStakePoolsSliderUsedAnalyticsEvent(); }; - @action _rankStakePools = () => { this.isRanking = true; this.getStakePoolsData(); }; - @action _selectSmashServerUrl = async ({ smashServerUrl, }: { @@ -290,7 +344,6 @@ export default class StakingStore extends Store { } } }; - @action _startStakePoolsFetchTracker = () => { this._stopStakePoolsFetchTracker(); @@ -301,35 +354,32 @@ export default class StakingStore extends Store { ); this.getStakePoolsData(true); }; - @action _getStakingInfoWasOpen = async () => { // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message - const stakingInfoWasOpen = await this.api.localStorage.getStakingInfoWasOpen(); + const stakingInfoWasOpen = + await this.api.localStorage.getStakingInfoWasOpen(); runInAction(() => { this.stakingInfoWasOpen = stakingInfoWasOpen; }); }; - @action _setStakingInfoWasOpen = () => { this.stakingInfoWasOpen = true; // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'StakingStor... Remove this comment to see the full error message this.api.localStorage.setStakingInfoWasOpen(); }; - @action _getStakePoolsListViewTooltip = async () => { - const tooltipShown = await this.api.localStorage.getStakePoolsListViewTooltip(); + const tooltipShown = + await this.api.localStorage.getStakePoolsListViewTooltip(); runInAction(() => { this.stakePoolsListViewTooltipVisible = tooltipShown; }); }; - @action hideStakePoolsListViewTooltip = () => { this.stakePoolsListViewTooltipVisible = false; this.api.localStorage.setStakePoolsListViewTooltip( this.stakePoolsListViewTooltipVisible ); }; - @action _stakePoolsFetchTracker = () => { const lastNumberOfStakePoolsFetched = this.numberOfStakePoolsFetched; this.numberOfStakePoolsFetched = this.stakePools.length; @@ -349,7 +399,6 @@ export default class StakingStore extends Store { this._stopStakePoolsFetchTracker(); } }; - @action _stopStakePoolsFetchTracker = () => { clearInterval(this.stakePoolsFetchTrackerInterval); this.numberOfStakePoolsFetched = 0; @@ -357,12 +406,10 @@ export default class StakingStore extends Store { this.isFetchingStakePools = false; this.getStakePoolsData(); }; - @action _resetSmashServerError = () => { this.smashServerUrlError = null; this.smashServerLoading = false; }; - @action _joinStakePool = async (request: JoinStakePoolRequest) => { const { walletId, stakePoolId, passphrase, isHardwareWallet } = request; // Set join transaction in "PENDING" state @@ -411,7 +458,6 @@ export default class StakingStore extends Store { throw error; } }; - @action _quitStakePool = async (request: QuitStakePoolRequest) => { const { walletId, passphrase, isHardwareWallet } = request; // Set quit transaction in "PENDING" state @@ -453,7 +499,6 @@ export default class StakingStore extends Store { } }; // Check stake pool transaction state and reset pending state when transction is "in_ledger" - @action checkDelegationTransaction = (request: { transactionId: string; walletId: string; @@ -461,9 +506,8 @@ export default class StakingStore extends Store { const { transactionId, walletId } = request; // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message - const recentTransactionsResponse = this.stores.transactions._getTransactionsRecentRequest( - walletId - ).result; + const recentTransactionsResponse = + this.stores.transactions._getTransactionsRecentRequest(walletId).result; const recentTransactions = recentTransactionsResponse ? recentTransactionsResponse.transactions @@ -481,7 +525,6 @@ export default class StakingStore extends Store { } }; // Reset "PENDING" state, transaction state check poller and refresh wallets data - @action resetStakePoolTransactionChecker = () => { if (this.delegationCheckTimeInterval) { clearInterval(this.delegationCheckTimeInterval); @@ -493,7 +536,6 @@ export default class StakingStore extends Store { this.stores.wallets.refreshWalletsData(); this.isDelegationTransactionPending = false; }; - @action _requestCSVFile = async ({ fileContent, filenamePrefix: prefix, @@ -555,11 +597,10 @@ export default class StakingStore extends Store { } try { - const delegationFee: DelegationCalculateFeeResponse = await this.calculateDelegationFeeRequest.execute( - { + const delegationFee: DelegationCalculateFeeResponse = + await this.calculateDelegationFeeRequest.execute({ ...delegationFeeRequest, - } - ).promise; + }).promise; if (this._delegationFeeCalculationWalletId !== walletId) { return null; @@ -572,30 +613,25 @@ export default class StakingStore extends Store { }; // GETTERS - @computed get currentRoute(): string { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message return this.stores.router.location.pathname; } - @computed get isStakingPage(): boolean { return this.currentRoute.indexOf(ROUTES.STAKING.ROOT) > -1; } - @computed get maxDelegationFunds(): number { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message const { desiredPoolNumber } = this.stores.networkStatus; return Math.round(CIRCULATING_SUPPLY / desiredPoolNumber); } - @computed get stakePools(): Array { return this.stakePoolsRequest.result ? this.stakePoolsRequest.result : []; } - @computed get recentStakePools(): Array { const delegatedStakePools = []; // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message @@ -629,12 +665,10 @@ export default class StakingStore extends Store { return orderedStakePools; } - @computed get isStakingDelegationCountdown(): boolean { return this.currentRoute === ROUTES.STAKING.COUNTDOWN; } - @computed get rewards(): Array { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message const { wallets } = this.stores; @@ -645,7 +679,7 @@ export default class StakingStore extends Store { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message const { transactions, addresses } = this.stores; const rewardsAddress = addresses.stakeAddresses[wallet.id]; - const syncingProgress = get(wallet.syncState, 'progress.quantity', ''); + const syncingProgress = wallet.syncState?.progress?.quantity; return { wallet: wallet.name, total: wallet.reward.plus(transactions.withdrawals[wallet.id]), @@ -656,14 +690,12 @@ export default class StakingStore extends Store { }; } - @action showCountdown(): boolean { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message const { isShelleyPending } = this.stores.networkStatus; return isShelleyPending; } - @action getStakePoolsData = async (isSmash?: boolean) => { const { isConnected, @@ -694,7 +726,6 @@ export default class StakingStore extends Store { this._resetIsRanking(); }; - @action _resetPolling = (type?: 'regular' | 'failed' | 'kill' | 'smash') => { if (type === 'kill') { this.fetchingStakePoolsFailed = true; @@ -741,12 +772,10 @@ export default class StakingStore extends Store { ); } }; - @action _resetIsRanking = () => { this.isRanking = false; }; // For testing only - @action _setFakePoller = (forceLoading: boolean) => { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message const { stores, environment } = this; @@ -789,7 +818,6 @@ export default class StakingStore extends Store { } }; // For testing only - @action _setFakedStakePools = () => { // @ts-ignore ts-migrate(2339) FIXME: Property 'environment' does not exist on type 'Sta... Remove this comment to see the full error message if (this.environment.isDev) { @@ -839,19 +867,15 @@ export default class StakingStore extends Store { }; } - @action _goToConfigurationStep = () => { this.redeemStep = steps.CONFIGURATION; }; - @action _goToConfirmationStep = () => { this.redeemStep = steps.CONFIRMATION; }; - @action _goToResultStep = () => { this.redeemStep = steps.RESULT; }; - @action _onCalculateRedeemWalletFees = async ({ walletId, recoveryPhrase, @@ -866,9 +890,8 @@ export default class StakingStore extends Store { try { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message - const [address] = await this.stores.addresses.getAddressesByWalletId( - walletId - ); + const [address] = + await this.stores.addresses.getAddressesByWalletId(walletId); // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message const transactionFees = await this.getRedeemItnRewardsFeeRequest.execute({ wallet: this.redeemWallet, @@ -888,13 +911,11 @@ export default class StakingStore extends Store { }); } }; - @action _onRedeemStart = () => { this.configurationStepError = null; this.confirmationStepError = null; this.redeemStep = steps.CONFIGURATION; }; - @action _onConfigurationContinue = () => { if (this.transactionFees && this.redeemRecoveryPhrase) { this.redeemStep = steps.CONFIRMATION; @@ -905,7 +926,6 @@ export default class StakingStore extends Store { this.redeemStep = steps.RESULT; } }; - @action _onConfirmationContinue = async ({ spendingPassword, }: { @@ -919,9 +939,8 @@ export default class StakingStore extends Store { try { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'StakingS... Remove this comment to see the full error message - const [address] = await this.stores.addresses.getAddressesByWalletId( - walletId - ); + const [address] = + await this.stores.addresses.getAddressesByWalletId(walletId); // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message const redeemedRewards = await this.requestRedeemItnRewardsRequest.execute( { @@ -950,7 +969,6 @@ export default class StakingStore extends Store { }); } }; - @action _onResultContinue = () => { if (!this.redeemWallet) throw new Error('Redeem wallet require'); const { id } = this.redeemWallet; @@ -960,7 +978,6 @@ export default class StakingStore extends Store { this._resetRedeemItnRewards(); }; - @action _resetRedeemItnRewards = () => { this.isSubmittingReedem = false; this.isCalculatingReedemFees = false; @@ -972,7 +989,6 @@ export default class StakingStore extends Store { this.configurationStepError = null; this.confirmationStepError = null; }; - @action _closeRedeemDialog = () => { this._resetRedeemItnRewards(); diff --git a/source/renderer/app/stores/TransactionsStore.ts b/source/renderer/app/stores/TransactionsStore.ts index ab480dd3b0..65c4b1a928 100644 --- a/source/renderer/app/stores/TransactionsStore.ts +++ b/source/renderer/app/stores/TransactionsStore.ts @@ -4,6 +4,7 @@ import { action, extendObservable, runInAction, + makeObservable, } from 'mobx'; import { find, get } from 'lodash'; import BigNumber from 'bignumber.js'; @@ -28,7 +29,9 @@ import { isTransactionInFilterRange, } from '../utils/transaction'; import type { ApiTokens } from '../api/assets/types'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; const INITIAL_SEARCH_LIMIT = null; // 'null' value stands for 'load all' @@ -82,7 +85,6 @@ type TransactionFeeRequest = { assets?: ApiTokens; }; export default class TransactionsStore extends Store { - @observable transactionsRequests: Array<{ walletId: string; isLegacy: boolean; @@ -90,20 +92,50 @@ export default class TransactionsStore extends Store { allRequest: Request; withdrawalsRequest: Request; }> = []; - @observable deleteTransactionRequest: Request = new Request( this.api.ada.deleteTransaction ); - @observable - createExternalTransactionRequest: Request< - CreateExternalTransactionRequest - > = new Request(this.api.ada.createExternalTransaction); - @observable + createExternalTransactionRequest: Request = + new Request(this.api.ada.createExternalTransaction); _filterOptionsForWallets = {}; - @observable - calculateTransactionFeeRequest: Request< - GetTransactionFeeRequest - > = new Request(this.api.ada.calculateTransactionFee); + calculateTransactionFeeRequest: Request = + new Request(this.api.ada.calculateTransactionFee); + + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + transactionsRequests: observable, + deleteTransactionRequest: observable, + createExternalTransactionRequest: observable, + _filterOptionsForWallets: observable, + calculateTransactionFeeRequest: observable, + recentTransactionsRequest: computed, + searchRequest: computed, + filterOptions: computed, + withdrawals: computed, + all: computed, + allFiltered: computed, + defaultFilterOptions: computed, + populatedFilterOptions: computed, + recent: computed, + recentFiltered: computed, + hasAnyFiltered: computed, + hasAny: computed, + totalAvailable: computed, + totalFilteredAvailable: computed, + pendingTransactionsCount: computed, + _refreshTransactionData: action, + _updateFilterOptions: action, + _clearFilterOptions: action, + _requestCSVFile: action, + _createExternalTransaction: action, + }); + } setup() { const { @@ -117,7 +149,6 @@ export default class TransactionsStore extends Store { this.registerReactions([this._ensureFilterOptionsForActiveWallet]); } - @computed get recentTransactionsRequest(): Request { const wallet = this.stores.wallets.active; // TODO: Do not return new request here @@ -125,7 +156,6 @@ export default class TransactionsStore extends Store { return this._getTransactionsRecentRequest(wallet.id); } - @computed get searchRequest(): Request { const wallet = this.stores.wallets.active; // TODO: Do not return new request here @@ -133,14 +163,12 @@ export default class TransactionsStore extends Store { return this._getTransactionsAllRequest(wallet.id); } - @computed get filterOptions(): TransactionFilterOptionsType | null | undefined { const wallet = this.stores.wallets.active; if (!wallet) return null; return this._filterOptionsForWallets[wallet.id]; } - @computed get withdrawals(): Record { const withdrawals = {}; const { allWallets: wallets } = this.stores.wallets; @@ -157,7 +185,6 @@ export default class TransactionsStore extends Store { return withdrawals; } - @computed get all(): Array { const wallet = this.stores.wallets.active; if (!wallet) return []; @@ -171,7 +198,6 @@ export default class TransactionsStore extends Store { return request.result.transactions || []; } - @computed get allFiltered(): Array { const { recentFiltered } = this; const allFiltered = this.all.filter((transaction) => @@ -183,19 +209,16 @@ export default class TransactionsStore extends Store { : allFiltered; } - @computed get defaultFilterOptions(): TransactionFilterOptionsType { // @ts-ignore ts-migrate(2322) FIXME: Type '{ dateRange: string; fromDate: string; toDat... Remove this comment to see the full error message return generateFilterOptions(this.all); } - @computed get populatedFilterOptions(): TransactionFilterOptionsType { // @ts-ignore ts-migrate(2322) FIXME: Type 'TransactionFilterOptionsType | { searchTerm:... Remove this comment to see the full error message return this.filterOptions || emptyTransactionFilterOptions; } - @computed get recent(): Array { const wallet = this.stores.wallets.active; if (!wallet) return []; @@ -205,14 +228,12 @@ export default class TransactionsStore extends Store { return results ? results.transactions : []; } - @computed get recentFiltered(): Array { return this.recent.filter((transaction) => isTransactionInFilterRange(this.filterOptions, transaction) ); } - @computed get hasAnyFiltered(): boolean { const wallet = this.stores.wallets.active; if (!wallet) return false; @@ -222,7 +243,6 @@ export default class TransactionsStore extends Store { return results ? results.transactions.length > 0 : false; } - @computed get hasAny(): boolean { const wallet = this.stores.wallets.active; if (!wallet) return false; @@ -232,7 +252,6 @@ export default class TransactionsStore extends Store { return results ? results.total > 0 : false; } - @computed get totalAvailable(): number { const wallet = this.stores.wallets.active; if (!wallet) return 0; @@ -242,7 +261,6 @@ export default class TransactionsStore extends Store { return results ? results.total : 0; } - @computed get totalFilteredAvailable(): number { const wallet = this.stores.wallets.active; if (!wallet) return 0; @@ -252,12 +270,10 @@ export default class TransactionsStore extends Store { return results ? results.transactions.length : 0; } - @computed get pendingTransactionsCount(): number { return this.recent.filter(({ state }) => state === 'pending').length; } - @action _refreshTransactionData = async () => { if (this.stores.networkStatus.isConnected) { const { all: wallets } = this.stores.wallets; @@ -359,7 +375,6 @@ export default class TransactionsStore extends Store { validateAssetAmount = (amountInNaturalUnits: string): Promise => Promise.resolve(isValidAssetAmountInNaturalUnits(amountInNaturalUnits)); // ======================= PRIVATE ========================== // - @action _updateFilterOptions = (filterOptions: TransactionFilterOptionsType) => { const wallet = this.stores.wallets.active; if (!wallet) return false; @@ -375,7 +390,6 @@ export default class TransactionsStore extends Store { ); return true; }; - @action _clearFilterOptions = () => { const wallet = this.stores.wallets.active; if (!wallet) return false; @@ -385,7 +399,6 @@ export default class TransactionsStore extends Store { return true; }; - @action _requestCSVFile = async () => { const { stores: { profile }, @@ -417,7 +430,6 @@ export default class TransactionsStore extends Store { ); } }; - @action _createExternalTransaction = async (signedTransactionBlob: Buffer) => { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.createExternalTransactionRequest.execute({ @@ -468,7 +480,7 @@ export default class TransactionsStore extends Store { if (!options) { // Setup options for active wallet - runInAction('setFilterOptionsForActiveWallet', () => { + runInAction(() => { extendObservable(this._filterOptionsForWallets, { [wallet.id]: emptyTransactionFilterOptions, }); diff --git a/source/renderer/app/stores/UiDialogsStore.ts b/source/renderer/app/stores/UiDialogsStore.ts index 064cb8dc87..dd6f8b9cb1 100644 --- a/source/renderer/app/stores/UiDialogsStore.ts +++ b/source/renderer/app/stores/UiDialogsStore.ts @@ -1,21 +1,39 @@ -import { Component, ReactNode } from 'react'; -import { observable, action } from 'mobx'; +import { ReactNode } from 'react'; +import { observable, action, makeObservable } from 'mobx'; import Store from './lib/Store'; import WalletReceiveDialog from '../components/wallet/receive/WalletReceiveDialog'; import AssetSettingsDialog from '../components/assets/AssetSettingsDialog'; import DelegationSetupWizardDialog from '../components/staking/delegation-setup-wizard/DelegationSetupWizardDialog'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export default class UiDialogsStore extends Store { - @observable - activeDialog: ReactNode | null = null; - @observable + activeDialog: object = null; secondsSinceActiveDialogIsOpen = 0; - @observable dataForActiveDialog: Record = {}; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. _secondsTimerInterval: IntervalID | null | undefined = null; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + activeDialog: observable, + secondsSinceActiveDialogIsOpen: observable, + dataForActiveDialog: observable, + _onOpen: action, + _onClose: action, + _updateSeconds: action, + _onUpdateDataForActiveDialog: action, + _reset: action, + }); + } + setup() { this.actions.dialogs.open.listen(this._onOpen); this.actions.dialogs.closeActiveDialog.listen(this._onClose); @@ -28,11 +46,10 @@ export default class UiDialogsStore extends Store { isOpen = (dialog: ReactNode): boolean => this.activeDialog === dialog; countdownSinceDialogOpened = (countDownTo: number) => Math.max(countDownTo - this.secondsSinceActiveDialogIsOpen, 0); - @action _onOpen = ({ dialog }: { dialog: object }) => { this._reset(); - this.activeDialog = dialog; + this.activeDialog = dialog as object; // @ts-ignore ts-migrate(2339) FIXME: Property 'defaultProps' does not exist on type '(.... Remove this comment to see the full error message this.dataForActiveDialog = observable(dialog.defaultProps || {}); this.secondsSinceActiveDialogIsOpen = 0; @@ -41,19 +58,15 @@ export default class UiDialogsStore extends Store { this._handleAnalytics(dialog); }; - @action _onClose = () => { this._reset(); }; - @action _updateSeconds = () => { this.secondsSinceActiveDialogIsOpen += 1; }; - @action _onUpdateDataForActiveDialog = ({ data }: { data: Record }) => { Object.assign(this.dataForActiveDialog, data); }; - @action _reset = () => { this.activeDialog = null; this.secondsSinceActiveDialogIsOpen = 0; diff --git a/source/renderer/app/stores/UiNotificationsStore.ts b/source/renderer/app/stores/UiNotificationsStore.ts index 4457b76221..96ff2dfd65 100644 --- a/source/renderer/app/stores/UiNotificationsStore.ts +++ b/source/renderer/app/stores/UiNotificationsStore.ts @@ -1,4 +1,4 @@ -import { observable, action } from 'mobx'; +import { observable, action, makeObservable } from 'mobx'; import { omit } from 'lodash'; import Store from './lib/Store'; import { NOTIFICATION_DEFAULT_DURATION } from '../config/timingConfig'; @@ -6,9 +6,11 @@ import type { NotificationConfig, NotificationId, } from '../types/notificationTypes'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; +import { AnalyticsTracker } from '../analytics'; export default class UiNotificationsStore extends Store { - @observable // @ts-ignore ts-migrate(2740) FIXME: Type '{}' is missing the following properties from... Remove this comment to see the full error message activeNotifications: Record< NotificationId, @@ -20,6 +22,21 @@ export default class UiNotificationsStore extends Store { // @ts-ignore ts-migrate(2740) FIXME: Type '{}' is missing the following properties from... Remove this comment to see the full error message activeNotificationsTimeouts: Record = {}; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + activeNotifications: observable, + _registerNotification: action, + _openNotification: action, + _onClose: action, + }); + } + setup() { this.actions.notifications.registerNotification.listen( this._registerNotification @@ -28,13 +45,9 @@ export default class UiNotificationsStore extends Store { } isOpen = (id: NotificationId): boolean => !!this.activeNotifications[id]; - @action _registerNotification = (notificationConfig: NotificationConfig) => { - const { - id, - actionToListenAndOpen, - actionToListenAndClose, - } = notificationConfig; + const { id, actionToListenAndOpen, actionToListenAndClose } = + notificationConfig; actionToListenAndOpen.listen((labelValues?: Record) => this._openNotification(notificationConfig, labelValues) ); @@ -47,7 +60,6 @@ export default class UiNotificationsStore extends Store { ); } }; - @action _openNotification = ( notificationConfig: NotificationConfig, labelValues?: Record @@ -67,7 +79,6 @@ export default class UiNotificationsStore extends Store { duration ); }; - @action _onClose = ({ id }: { id: NotificationId }) => { if (id in this.activeNotifications) { // @ts-ignore ts-migrate(2740) FIXME: Type 'Pick) => any; @@ -48,33 +56,67 @@ export enum FundPhase { RESULTS = 'results', } export default class VotingStore extends Store { - @observable registrationStep = 1; - @observable selectedWalletId: string | null | undefined = null; - @observable transactionId: string | null | undefined = null; - @observable transactionConfirmations = 0; - @observable isTransactionPending = false; - @observable isTransactionConfirmed = false; - @observable votingRegistrationKey: VotingRegistrationKeyType | null | undefined = null; - @observable qrCode: string | null | undefined = null; - @observable isConfirmationDialogOpen = false; - @observable fundPhase?: FundPhase; - @observable catalystFund: CatalystFund; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. transactionPollingInterval: IntervalID | null | undefined = null; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. fundPhaseInterval: IntervalID | null | undefined = null; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + registrationStep: observable, + selectedWalletId: observable, + transactionId: observable, + transactionConfirmations: observable, + isTransactionPending: observable, + isTransactionConfirmed: observable, + votingRegistrationKey: observable, + qrCode: observable, + isConfirmationDialogOpen: observable, + fundPhase: observable, + catalystFund: observable, + _setupFund: action, + getWalletPublicKeyRequest: observable, + createVotingRegistrationTransactionRequest: observable, + signMetadataRequest: observable, + getTransactionRequest: observable, + getCatalystFundRequest: observable, + _showConfirmationDialog: action, + _closeConfirmationDialog: action, + _setSelectedWalletId: action, + _nextRegistrationStep: action, + _previousRegistrationStep: action, + _resetRegistration: action, + _startTransactionPolling: action, + _initializeFundPhaseInterval: action, + _setVotingRegistrationKey: action, + _setTransactionId: action, + _setTransactionConfirmations: action, + _setIsTransactionPending: action, + _setIsTransactionConfirmed: action, + _setQrCode: action, + _checkFundPhase: action, + currentRoute: computed, + isVotingPage: computed, + }); + } + setup() { const { voting: votingActions } = this.actions; votingActions.selectWallet.listen(this._setSelectedWalletId); @@ -92,61 +134,48 @@ export default class VotingStore extends Store { this._setupFund(); } - @action _setupFund = async () => { await this.getCatalystFundRequest.execute().promise; this._initializeFundPhaseInterval(); - runInAction('Initialize fund', () => { + runInAction(() => { this.catalystFund = this.getCatalystFundRequest.result; this._checkFundPhase(new Date()); }); }; // REQUESTS - @observable getWalletPublicKeyRequest: Request = new Request( this.api.ada.getWalletPublicKey ); - @observable - createVotingRegistrationTransactionRequest: Request< - WalletTransaction - > = new Request(this.api.ada.createVotingRegistrationTransaction); - @observable + createVotingRegistrationTransactionRequest: Request = + new Request(this.api.ada.createVotingRegistrationTransaction); signMetadataRequest: Request = new Request( this.api.ada.createWalletSignature ); - @observable getTransactionRequest: Request = new Request( this.api.ada.getTransaction ); - @observable getCatalystFundRequest: Request = new Request( this.api.ada.getCatalystFund ); // ACTIONS - @action _showConfirmationDialog = () => { this.isConfirmationDialogOpen = true; }; - @action _closeConfirmationDialog = () => { this.isConfirmationDialogOpen = false; }; - @action _setSelectedWalletId = (walletId: string) => { this.selectedWalletId = walletId; }; - @action _nextRegistrationStep = () => { this.registrationStep++; }; - @action _previousRegistrationStep = () => { this.registrationStep--; }; - @action _resetRegistration = () => { this.isConfirmationDialogOpen = false; this.registrationStep = 1; @@ -169,7 +198,6 @@ export default class VotingStore extends Store { clearInterval(this.fundPhaseInterval); } }; - @action _startTransactionPolling = () => { if (this.transactionPollingInterval) clearInterval(this.transactionPollingInterval); @@ -177,7 +205,6 @@ export default class VotingStore extends Store { this._checkVotingRegistrationTransaction(); }, VOTING_REGISTRATION_TRANSACTION_POLLING_INTERVAL); }; - @action _initializeFundPhaseInterval = () => { if (this.fundPhaseInterval) { clearInterval(this.fundPhaseInterval); @@ -187,35 +214,28 @@ export default class VotingStore extends Store { this._checkFundPhase(new Date()); }, VOTING_PHASE_CHECK_INTERVAL); }; - @action _setVotingRegistrationKey = (value: VotingRegistrationKeyType) => { this.votingRegistrationKey = value; }; - @action _setTransactionId = (transactionId: string) => { this.transactionId = transactionId; }; - @action _setTransactionConfirmations = (confirmations: number) => { this.transactionConfirmations = confirmations; }; - @action _setIsTransactionPending = (value: boolean) => { this.isTransactionPending = value; }; - @action _setIsTransactionConfirmed = (value: boolean) => { this.isTransactionConfirmed = value; }; - @action _setQrCode = (value: string | null | undefined) => { this.qrCode = value; }; prepareVotingData = async ({ walletId }: { walletId: string }) => { try { - const [address] = await this.stores.addresses.getAddressesByWalletId( - walletId - ); + const [address] = + await this.stores.addresses.getAddressesByWalletId(walletId); const addressHex = await this._getHexFromBech32(address.id); await this._generateVotingRegistrationKey(); if (!this.votingRegistrationKey) @@ -309,9 +329,8 @@ export default class VotingStore extends Store { throw new Error( 'Selected wallet required before send voting registration.' ); - const [address] = await this.stores.addresses.getAddressesByWalletId( - walletId - ); + const [address] = + await this.stores.addresses.getAddressesByWalletId(walletId); const selectedWallet = this.stores.wallets.getWalletById(walletId); const isHardwareWallet = get(selectedWallet, 'isHardwareWallet', false); const { absoluteSlotNumber } = this.stores.networkStatus; @@ -413,11 +432,8 @@ export default class VotingStore extends Store { if (!selectedWallet) return; const { name: walletName } = selectedWallet; const { desktopDirectoryPath } = this.stores.profile; - const { - currentLocale, - currentDateFormat, - currentTimeFormat, - } = this.stores.profile; + const { currentLocale, currentDateFormat, currentTimeFormat } = + this.stores.profile; const { network, isMainnet } = this.environment; const intl = i18nContext(currentLocale); @@ -468,7 +484,6 @@ export default class VotingStore extends Store { clearInterval(this.transactionPollingInterval); } }; - @action _checkFundPhase = (now: Date) => { const phaseValidation = { [FundPhase.SNAPSHOT]: (date: Date) => @@ -497,12 +512,10 @@ export default class VotingStore extends Store { }; // GETTERS - @computed get currentRoute(): string { return this.stores.router.location.pathname; } - @computed get isVotingPage(): boolean { return this.currentRoute.indexOf(ROUTES.VOTING.REGISTRATION) > -1; } diff --git a/source/renderer/app/stores/WalletBackupStore.ts b/source/renderer/app/stores/WalletBackupStore.ts index 3665ea1324..c09abdd992 100644 --- a/source/renderer/app/stores/WalletBackupStore.ts +++ b/source/renderer/app/stores/WalletBackupStore.ts @@ -1,33 +1,59 @@ -import { observable, action, computed } from 'mobx'; +import { observable, action, computed, makeObservable } from 'mobx'; import Store from './lib/Store'; import WalletBackupDialog from '../components/wallet/WalletBackupDialog'; import { WALLET_BACKUP_STEPS } from '../types/walletBackupTypes'; import type { walletBackupStep } from '../types/walletBackupTypes'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; +import { AnalyticsTracker } from '../analytics'; export default class WalletBackupStore extends Store { - @observable inProgress = false; - @observable currentStep: walletBackupStep = WALLET_BACKUP_STEPS.NOT_INITIATED; - @observable recoveryPhrase = []; - @observable completed = false; - @observable enteredPhrase = []; - @observable isPrivacyNoticeAccepted = false; - @observable isEntering = false; - @observable isTermOfflineAccepted = false; - @observable isTermRecoveryAccepted = false; - @observable countdownRemaining = 0; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. countdownTimerInterval: IntervalID | null | undefined = null; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + inProgress: observable, + currentStep: observable, + recoveryPhrase: observable, + completed: observable, + enteredPhrase: observable, + isPrivacyNoticeAccepted: observable, + isEntering: observable, + isTermOfflineAccepted: observable, + isTermRecoveryAccepted: observable, + countdownRemaining: observable, + _initiateWalletBackup: action, + _acceptPrivacyNoticeForWalletBackup: action, + _continueToRecoveryPhraseForWalletBackup: action, + _startWalletBackup: action, + _updateWalletBackupVerificationPhrase: action, + _clearEnteredRecoveryPhrase: action, + isRecoveryPhraseValid: computed, + _acceptWalletBackupTermOffline: action, + _acceptWalletBackupTermRecovery: action, + _restartWalletBackup: action, + _cancelWalletBackup: action, + _finishWalletBackup: action, + }); + } + setup() { const a = this.actions.walletBackup; a.initiateWalletBackup.listen(this._initiateWalletBackup); @@ -51,7 +77,6 @@ export default class WalletBackupStore extends Store { this.actions.app.initAppEnvironment.listen(() => {}); } - @action _initiateWalletBackup = (params: { recoveryPhrase: Array }) => { this.recoveryPhrase = params.recoveryPhrase; this.inProgress = true; @@ -77,59 +102,48 @@ export default class WalletBackupStore extends Store { dialog: WalletBackupDialog, }); }; - @action _acceptPrivacyNoticeForWalletBackup = () => { this.isPrivacyNoticeAccepted = true; }; - @action _continueToRecoveryPhraseForWalletBackup = () => { // @ts-ignore ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'walletBac... Remove this comment to see the full error message this.currentStep = WALLET_BACKUP_STEPS.RECOVERY_PHRASE_DISPLAY; }; - @action _startWalletBackup = () => { // @ts-ignore ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'walletBac... Remove this comment to see the full error message this.currentStep = WALLET_BACKUP_STEPS.RECOVERY_PHRASE_ENTRY; }; - @action _updateWalletBackupVerificationPhrase = (params: { verificationPhrase: Array; }) => { const { verificationPhrase } = params; this.enteredPhrase = verificationPhrase; }; - @action _clearEnteredRecoveryPhrase = () => { this.enteredPhrase = []; }; - @computed get isRecoveryPhraseValid(): boolean { return this.recoveryPhrase.join(' ') === this.enteredPhrase.join(' '); } - @action _acceptWalletBackupTermOffline = () => { this.isTermOfflineAccepted = true; }; - @action _acceptWalletBackupTermRecovery = () => { this.isTermRecoveryAccepted = true; }; - @action _restartWalletBackup = () => { this._clearEnteredRecoveryPhrase(); // @ts-ignore ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'walletBac... Remove this comment to see the full error message this.currentStep = WALLET_BACKUP_STEPS.RECOVERY_PHRASE_DISPLAY; }; - @action _cancelWalletBackup = () => { this.inProgress = false; this._clearEnteredRecoveryPhrase(); }; - @action _finishWalletBackup = () => { this.inProgress = false; }; diff --git a/source/renderer/app/stores/WalletMigrationStore.ts b/source/renderer/app/stores/WalletMigrationStore.ts index ee07ac5f5a..8ca09a5150 100644 --- a/source/renderer/app/stores/WalletMigrationStore.ts +++ b/source/renderer/app/stores/WalletMigrationStore.ts @@ -1,4 +1,10 @@ -import { action, computed, observable, runInAction } from 'mobx'; +import { + action, + computed, + makeObservable, + observable, + runInAction, +} from 'mobx'; import path from 'path'; import { orderBy } from 'lodash'; import Store from './lib/Store'; @@ -29,7 +35,9 @@ import { import { IMPORT_WALLET_STEPS } from '../config/walletRestoreConfig'; import { IS_AUTOMATIC_WALLET_MIGRATION_ENABLED } from '../config/walletsConfig'; import type { ImportWalletStep } from '../types/walletRestoreTypes'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export type WalletMigrationStatus = | 'unstarted' @@ -51,42 +59,77 @@ export const WalletMigrationStatuses: { ERRORED: 'errored', }; export default class WalletMigrationStore extends Store { - @observable walletMigrationStep: ImportWalletStep | null | undefined = null; - @observable isExportRunning = false; - @observable exportedWallets: Array = []; - @observable exportErrors = ''; - @observable exportSourcePath = ''; - @observable defaultExportSourcePath: string = global.legacyStateDir; - @observable isTestMigrationEnabled = false; - @observable isRestorationRunning = false; - @observable restoredWallets: Array = []; - @observable restorationErrors: Array<{ error: string; wallet: ExportedWalletData; }> = []; - @observable getWalletMigrationStatusRequest: Request = new Request( this.api.localStorage.getWalletMigrationStatus ); - @observable setWalletMigrationStatusRequest: Request = new Request( this.api.localStorage.setWalletMigrationStatus ); - @observable restoreExportedWalletRequest: Request = new Request( this.api.ada.restoreExportedByronWallet ); + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + walletMigrationStep: observable, + isExportRunning: observable, + exportedWallets: observable, + exportErrors: observable, + exportSourcePath: observable, + defaultExportSourcePath: observable, + isTestMigrationEnabled: observable, + isRestorationRunning: observable, + restoredWallets: observable, + restorationErrors: observable, + getWalletMigrationStatusRequest: observable, + setWalletMigrationStatusRequest: observable, + restoreExportedWalletRequest: observable, + _initiateMigration: action, + _selectExportSourcePath: action, + _resetExportSourcePath: action, + _nextStep: action, + _toggleWalletImportSelection: action, + _updateWalletImportStatus: action, + _updateWalletName: action, + _exportWallets: action, + _restoreWallets: action, + _restoreWallet: action, + _generateMigrationReport: action, + _startMigration: action, + _resetExportData: action, + _resetRestorationData: action, + _resetMigration: action, + _setFakedImportPath: action, + _enableTestWalletMigration: action, + _finishMigration: action, + pendingImportWallets: computed, + pendingImportWalletsCount: computed, + exportedWalletsData: computed, + exportedWalletsCount: computed, + restoredWalletsData: computed, + restoredWalletsCount: computed, + }); + } + setup() { const { walletMigration } = this.actions; walletMigration.initiateMigration.listen(this._initiateMigration); @@ -115,11 +158,9 @@ export default class WalletMigrationStore extends Store { index: number ): ExportedByronWallet | null | undefined => this.exportedWallets.find((w) => w.index === index); - @action _initiateMigration = () => { this.walletMigrationStep = IMPORT_WALLET_STEPS.WALLET_IMPORT_FILE; }; - @action _selectExportSourcePath = async ({ importFrom, }: { @@ -151,23 +192,21 @@ export default class WalletMigrationStore extends Store { } const filePath = filePaths[0]; - runInAction('update exportSourcePath', () => { + runInAction(() => { this.exportSourcePath = filePath; this.exportErrors = ''; }); }; - @action _resetExportSourcePath = () => { this.exportSourcePath = ''; this.exportErrors = ''; }; - @action _nextStep = async () => { if (this.walletMigrationStep === IMPORT_WALLET_STEPS.WALLET_IMPORT_FILE) { await this._exportWallets(); if (this.exportedWalletsCount) { - runInAction('update walletMigrationStep', () => { + runInAction(() => { this.walletMigrationStep = IMPORT_WALLET_STEPS.WALLET_SELECT_IMPORT; }); } @@ -175,7 +214,6 @@ export default class WalletMigrationStore extends Store { this._restoreWallets(); } }; - @action _toggleWalletImportSelection = ({ index }: { index: number }) => { const wallet = this.getExportedWalletByIndex(index); @@ -204,7 +242,6 @@ export default class WalletMigrationStore extends Store { } } }; - @action _updateWalletImportStatus = ( index: number, status: WalletImportStatus, @@ -217,7 +254,6 @@ export default class WalletMigrationStore extends Store { if (error) wallet.import.error = error; } }; - @action _updateWalletName = ({ index, name }: { index: number; name: string }) => { const wallet = this.getExportedWalletByIndex(index); @@ -225,7 +261,6 @@ export default class WalletMigrationStore extends Store { wallet.name = name; } }; - @action _exportWallets = async () => { // Reset export data this._resetExportData(); @@ -233,14 +268,12 @@ export default class WalletMigrationStore extends Store { // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.debug('WalletMigrationStore: Starting wallet export...'); this.isExportRunning = true; - const { - wallets, - errors, - }: ExportWalletsMainResponse = await exportWalletsChannel.request({ - exportSourcePath: this.exportSourcePath || this.defaultExportSourcePath, - locale: this.stores.profile.currentLocale, - }); - runInAction('update exportedWallets and exportErrors', () => { + const { wallets, errors }: ExportWalletsMainResponse = + await exportWalletsChannel.request({ + exportSourcePath: this.exportSourcePath || this.defaultExportSourcePath, + locale: this.stores.profile.currentLocale, + }); + runInAction(() => { this.exportedWallets = orderBy( wallets.map((wallet) => { const hasName = wallet.name !== null; @@ -278,11 +311,10 @@ export default class WalletMigrationStore extends Store { exportErrors: this.exportErrors, } ); - runInAction('update isExportRunning', () => { + runInAction(() => { this.isExportRunning = false; }); }; - @action _restoreWallets = async () => { // Reset restoration data this._resetRestorationData(); @@ -309,7 +341,7 @@ export default class WalletMigrationStore extends Store { logger.debug( `WalletMigrationStore: Restored ${this.restoredWalletsCount} of ${this.pendingImportWalletsCount} selected wallets` ); - runInAction('update isRestorationRunning', () => { + runInAction(() => { this.isRestorationRunning = false; }); @@ -318,7 +350,6 @@ export default class WalletMigrationStore extends Store { 'Restored legacy wallet(s)' ); }; - @action _restoreWallet = async (exportedWallet: ExportedByronWallet) => { // Reset restore requests to clear previous errors this.restoreExportedWalletRequest.reset(); @@ -327,12 +358,11 @@ export default class WalletMigrationStore extends Store { this._updateWalletImportStatus(index, WalletImportStatuses.RUNNING); try { - const restoredWallet = await this.restoreExportedWalletRequest.execute( - exportedWallet - ).promise; + const restoredWallet = + await this.restoreExportedWalletRequest.execute(exportedWallet).promise; if (!restoredWallet) throw new Error('Restored wallet was not received correctly'); - runInAction('update restoredWallets', () => { + runInAction(() => { this._updateWalletImportStatus(index, WalletImportStatuses.COMPLETED); const walletDuplicates = this.getExportedWalletDuplicatesById( @@ -353,7 +383,7 @@ export default class WalletMigrationStore extends Store { } catch (error) { const errorStr = error.defaultMessage || error.message || error.toString(); - runInAction('update restorationErrors', () => { + runInAction(() => { const { name, isEmptyPassphrase } = exportedWallet; this._updateWalletImportStatus( @@ -373,10 +403,9 @@ export default class WalletMigrationStore extends Store { }); } }; - @action _generateMigrationReport = async () => { - const finalMigrationStatus = await this.getWalletMigrationStatusRequest.execute() - .promise; + const finalMigrationStatus = + await this.getWalletMigrationStatusRequest.execute().promise; const walletMigrationReportData: WalletMigrationReportData = { exportedWalletsData: this.exportedWalletsData, exportedWalletsCount: this.exportedWalletsCount, @@ -408,7 +437,6 @@ export default class WalletMigrationStore extends Store { ); } }; - @action _startMigration = async () => { if (!IS_AUTOMATIC_WALLET_MIGRATION_ENABLED) return; const { isMainnet, isTestnet, isTest } = this.environment; @@ -417,8 +445,8 @@ export default class WalletMigrationStore extends Store { // Reset migration data this._resetMigration(); - const walletMigrationStatus = await this.getWalletMigrationStatusRequest.execute() - .promise; + const walletMigrationStatus = + await this.getWalletMigrationStatusRequest.execute().promise; if (walletMigrationStatus === WalletMigrationStatuses.UNSTARTED) { // Wait for wallets to load as we need to match existing and exported wallets @@ -435,7 +463,7 @@ export default class WalletMigrationStore extends Store { if (this.exportedWalletsCount) { // Wallets successfully exported - ask the user to select the ones to import - runInAction('update walletMigrationStep', () => { + runInAction(() => { this.walletMigrationStep = IMPORT_WALLET_STEPS.WALLET_SELECT_IMPORT; }); this.actions.dialogs.open.trigger({ @@ -459,19 +487,16 @@ export default class WalletMigrationStore extends Store { ); } }; - @action _resetExportData = () => { this.isExportRunning = false; this.exportedWallets = []; this.exportErrors = ''; }; - @action _resetRestorationData = () => { this.isRestorationRunning = false; this.restoredWallets = []; this.restorationErrors = []; }; - @action _resetMigration = () => { this._resetExportData(); @@ -481,7 +506,6 @@ export default class WalletMigrationStore extends Store { this.walletMigrationStep = null; }; // For E2E test purpose - @action _setFakedImportPath = (sourcePath: string) => { if (this.environment.isTest) { this.exportSourcePath = sourcePath; @@ -489,7 +513,6 @@ export default class WalletMigrationStore extends Store { } }; // For E2E test purpose - @action _enableTestWalletMigration = async () => { if (this.environment.isTest) { this.isTestMigrationEnabled = true; @@ -501,7 +524,6 @@ export default class WalletMigrationStore extends Store { this._startMigration(); } }; - @action _finishMigration = async () => { // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'typeof WalletImportFileDialog' i... Remove this comment to see the full error message if (this.stores.uiDialogs.isOpen(WalletImportFileDialog)) { @@ -509,8 +531,8 @@ export default class WalletMigrationStore extends Store { this.actions.dialogs.closeActiveDialog.trigger(); } - const walletMigrationStatus = await this.getWalletMigrationStatusRequest.execute() - .promise; + const walletMigrationStatus = + await this.getWalletMigrationStatusRequest.execute().promise; if (walletMigrationStatus === WalletMigrationStatuses.RUNNING) { // Update migration status @@ -541,7 +563,6 @@ export default class WalletMigrationStore extends Store { this.stores.wallets.refreshWalletsData(); }; - @computed get pendingImportWallets(): Array { return this.exportedWallets.filter( ({ import: { status } }: ExportedByronWallet) => @@ -549,12 +570,10 @@ export default class WalletMigrationStore extends Store { ); } - @computed get pendingImportWalletsCount(): number { return this.pendingImportWallets.length; } - @computed get exportedWalletsData(): Array { return this.exportedWallets.map((wallet) => ({ id: wallet.id, @@ -564,12 +583,10 @@ export default class WalletMigrationStore extends Store { })); } - @computed get exportedWalletsCount(): number { return this.exportedWallets.length; } - @computed get restoredWalletsData(): Array { return this.restoredWallets.map((wallet) => ({ id: getRawWalletId(wallet.id), @@ -578,7 +595,6 @@ export default class WalletMigrationStore extends Store { })); } - @computed get restoredWalletsCount(): number { return this.restoredWallets.length; } diff --git a/source/renderer/app/stores/WalletSettingsStore.ts b/source/renderer/app/stores/WalletSettingsStore.ts index 46ae6116c5..04b95555f0 100644 --- a/source/renderer/app/stores/WalletSettingsStore.ts +++ b/source/renderer/app/stores/WalletSettingsStore.ts @@ -1,4 +1,10 @@ -import { observable, action, runInAction, computed } from 'mobx'; +import { + observable, + action, + runInAction, + computed, + makeObservable, +} from 'mobx'; import { findIndex } from 'lodash'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; @@ -10,40 +16,68 @@ import { getRawWalletId } from '../api/utils'; import type { WalletExportToFileParams } from '../actions/wallet-settings-actions'; import type { WalletUtxos } from '../api/wallets/types'; import { RECOVERY_PHRASE_VERIFICATION_STATUSES } from '../config/walletRecoveryPhraseVerificationConfig'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; import { WalletLocalData } from '../types/localDataTypes'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; export default class WalletSettingsStore extends Store { - @observable updateWalletRequest: Request = new Request(this.api.ada.updateWallet); - @observable updateSpendingPasswordRequest: Request = new Request( this.api.ada.updateSpendingPassword ); - @observable exportWalletToFileRequest: Request> = new Request( this.api.ada.exportWalletToFile ); - @observable getWalletUtxosRequest: Request = new Request( this.api.ada.getWalletUtxos ); - @observable walletFieldBeingEdited = null; - @observable lastUpdatedWalletField = null; - @observable walletUtxos: WalletUtxos | null | undefined = null; - @observable recoveryPhraseStep = 0; // @ts-ignore ts-migrate(2304) FIXME: Cannot find name 'IntervalID'. pollingApiInterval: IntervalID | null | undefined = null; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + updateWalletRequest: observable, + updateSpendingPasswordRequest: observable, + exportWalletToFileRequest: observable, + getWalletUtxosRequest: observable, + walletFieldBeingEdited: observable, + lastUpdatedWalletField: observable, + walletUtxos: observable, + recoveryPhraseStep: observable, + walletsRecoveryPhraseVerificationData: computed, + _startEditingWalletField: action, + _stopEditingWalletField: action, + _cancelEditingWalletField: action, + _updateSpendingPassword: action, + _updateWalletField: action, + _exportToFile: action, + _startWalletUtxoPolling: action, + _stopWalletUtxoPolling: action, + _clearWalletUtxoPollingInterval: action, + _getWalletUtxoApiData: action, + _updateWalletUtxos: action, + _onWalletSelected: action, + _recoveryPhraseVerificationContinue: action, + _recoveryPhraseVerificationClose: action, + _recoveryPhraseVerificationCheck: action, + _toggleShowUsedAddressesStatuses: action, + }); + } + setup() { - const { - walletSettings: walletSettingsActions, - sidebar: sidebarActions, - } = this.actions; + const { walletSettings: walletSettingsActions, sidebar: sidebarActions } = + this.actions; walletSettingsActions.startEditingWalletField.listen( this._startEditingWalletField ); @@ -88,7 +122,6 @@ export default class WalletSettingsStore extends Store { return walletsLocalData[id]; }; - @computed get walletsRecoveryPhraseVerificationData() { const { all: walletsLocalData } = this.stores.walletsLocal; return Object.keys(walletsLocalData) @@ -117,11 +150,9 @@ export default class WalletSettingsStore extends Store { } // =================== PRIVATE API ==================== // - @action _startEditingWalletField = ({ field }: { field: string }) => { this.walletFieldBeingEdited = field; }; - @action _stopEditingWalletField = () => { if (this.walletFieldBeingEdited) { this.lastUpdatedWalletField = this.walletFieldBeingEdited; @@ -129,12 +160,10 @@ export default class WalletSettingsStore extends Store { this.walletFieldBeingEdited = null; }; - @action _cancelEditingWalletField = () => { this.lastUpdatedWalletField = null; this.walletFieldBeingEdited = null; }; - @action _updateSpendingPassword = async ({ walletId, oldPassword, @@ -163,7 +192,6 @@ export default class WalletSettingsStore extends Store { 'password' ); }; - @action _updateWalletField = async ({ field, value, @@ -201,7 +229,6 @@ export default class WalletSettingsStore extends Store { field ); }; - @action _exportToFile = async (params: WalletExportToFileParams) => { const { walletId, filePath, password } = params; // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message @@ -213,7 +240,6 @@ export default class WalletSettingsStore extends Store { // @ts-ignore ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. this.actions.dialogs.closeActiveDialog.trigger(); }; - @action _startWalletUtxoPolling = () => { this._clearWalletUtxoPollingInterval(); @@ -224,20 +250,17 @@ export default class WalletSettingsStore extends Store { WALLET_UTXO_API_REQUEST_INTERVAL ); }; - @action _stopWalletUtxoPolling = () => { this._clearWalletUtxoPollingInterval(); this.getWalletUtxosRequest.reset(); }; - @action _clearWalletUtxoPollingInterval = () => { if (this.pollingApiInterval) { clearInterval(this.pollingApiInterval); this.pollingApiInterval = null; } }; - @action _getWalletUtxoApiData = async () => { const activeWallet = this.stores.wallets.active; if (!activeWallet) return; @@ -250,11 +273,9 @@ export default class WalletSettingsStore extends Store { this._updateWalletUtxos(walletUtxos); }; - @action _updateWalletUtxos = (walletUtxos: WalletUtxos | null | undefined) => { this.walletUtxos = walletUtxos; }; - @action _onWalletSelected = () => { this._updateWalletUtxos(null); }; @@ -262,17 +283,14 @@ export default class WalletSettingsStore extends Store { /* ========================================================== = Wallet Recovery Phrase Verification = ========================================================== */ - @action _recoveryPhraseVerificationContinue = async () => { const step = this.recoveryPhraseStep; if (step === 4) this.recoveryPhraseStep = 2; else this.recoveryPhraseStep = step + 1; }; - @action _recoveryPhraseVerificationClose = async () => { this.recoveryPhraseStep = 0; }; - @action _recoveryPhraseVerificationCheck = async ({ recoveryPhrase, }: { @@ -305,28 +323,20 @@ export default class WalletSettingsStore extends Store { }); } - runInAction( - 'AdaWalletBackupStore::_recoveryPhraseVerificationCheck', - () => { - this.recoveryPhraseStep = nextStep; - } - ); + runInAction(() => { + this.recoveryPhraseStep = nextStep; + }); }; /* ==== End of Wallet Recovery Phrase Verification ===== */ - @action _toggleShowUsedAddressesStatuses = async () => { const activeWallet = this.stores.wallets.active; if (!activeWallet) throw new Error( 'Active wallet required before checking show used addresses statuses.' ); - const localWalletData: - | WalletLocalData - | null - | undefined = this.getLocalWalletDataById( - activeWallet ? activeWallet.id : '' - ); + const localWalletData: WalletLocalData | null | undefined = + this.getLocalWalletDataById(activeWallet ? activeWallet.id : ''); const { showUsedAddresses } = localWalletData || {}; await this.actions.walletsLocal.setWalletLocalData.trigger({ walletId: activeWallet.id, diff --git a/source/renderer/app/stores/WalletsLocalStore.ts b/source/renderer/app/stores/WalletsLocalStore.ts index dc6ea6df2f..1a334c6878 100644 --- a/source/renderer/app/stores/WalletsLocalStore.ts +++ b/source/renderer/app/stores/WalletsLocalStore.ts @@ -1,23 +1,38 @@ -import { observable, computed } from 'mobx'; +import { observable, computed, makeObservable } from 'mobx'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; import { asyncForEach } from '../utils/asyncForEach'; import { WalletsLocalData } from '../types/localDataTypes'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; +import { AnalyticsTracker } from '../analytics'; export default class WalletsLocalStore extends Store { - @observable localWalletsRequest: Request = new Request( this.api.localStorage.getWalletsLocalData ); - @observable setWalletLocalDataRequest: Request = new Request( this.api.localStorage.setWalletLocalData ); - @observable unsetWalletLocalDataRequest: Request = new Request( this.api.localStorage.unsetWalletLocalData ); + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + localWalletsRequest: observable, + setWalletLocalDataRequest: observable, + unsetWalletLocalDataRequest: observable, + all: computed, + }); + } + setup() { const { walletsLocal: actions } = this.actions; actions.refreshWalletsLocalData.listen(this._refreshWalletsLocalData); @@ -28,7 +43,6 @@ export default class WalletsLocalStore extends Store { // =================== PUBLIC API ==================== // // GETTERS - @computed get all(): WalletsLocalData { // @ts-ignore ts-migrate(2322) FIXME: Type 'WalletsLocalData | {}' is not assignable to ... Remove this comment to see the full error message return this.localWalletsRequest.result diff --git a/source/renderer/app/stores/WalletsStore.ts b/source/renderer/app/stores/WalletsStore.ts index 2c29904715..3efae4a847 100644 --- a/source/renderer/app/stores/WalletsStore.ts +++ b/source/renderer/app/stores/WalletsStore.ts @@ -1,4 +1,11 @@ -import { observable, action, computed, runInAction, flow } from 'mobx'; +import { + observable, + action, + computed, + runInAction, + flow, + makeObservable, +} from 'mobx'; import { get, find, findIndex, isEqual, includes } from 'lodash'; import { BigNumber } from 'bignumber.js'; import Store from './lib/Store'; @@ -57,8 +64,10 @@ import type { HardwareWalletExtendedPublicKeyResponse, } from '../../../common/types/hardware-wallets.types'; import { NetworkMagics } from '../../../common/types/cardano-node.types'; -import { EventCategories } from '../analytics'; +import { AnalyticsTracker, EventCategories } from '../analytics'; import { getEventNameFromWallet } from '../analytics/utils/getEventNameFromWallet'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; /* eslint-disable consistent-return */ /** @@ -67,189 +76,123 @@ import { getEventNameFromWallet } from '../analytics/utils/getEventNameFromWalle export default class WalletsStore extends Store { WALLET_REFRESH_INTERVAL = 5000; - @observable undelegateWalletSubmissionSuccess: boolean | null | undefined = null; - @observable isAddressFromSameWallet = false; // REQUESTS - @observable walletsRequest: Request> = new Request(this.api.ada.getWallets); - @observable accountPublicKeyRequest: Request = new Request( this.api.ada.getAccountPublicKey ); - @observable icoPublicKeyRequest: Request = new Request( this.api.ada.getICOPublicKey ); - @observable importFromFileRequest: Request = new Request( this.api.ada.importWalletFromFile ); - @observable createWalletRequest: Request = new Request(this.api.ada.createWallet); - @observable getWalletAddressesRequest: Request> = new Request( this.api.ada.getAddresses ); - @observable deleteWalletRequest: Request = new Request( this.api.ada.deleteWallet ); - @observable sendMoneyRequest: Request = new Request( this.api.ada.createTransaction ); - @observable getWalletRecoveryPhraseRequest: Request> = new Request( this.api.ada.getWalletRecoveryPhrase ); - @observable - getWalletCertificateAdditionalMnemonicsRequest: Request< - Array - > = new Request(this.api.ada.getWalletCertificateAdditionalMnemonics); - @observable - getWalletCertificateRecoveryPhraseRequest: Request< - Array - > = new Request(this.api.ada.getWalletCertificateRecoveryPhrase); - @observable - getWalletRecoveryPhraseFromCertificateRequest: Request< - Array - > = new Request(this.api.ada.getWalletRecoveryPhraseFromCertificate); - @observable + getWalletCertificateAdditionalMnemonicsRequest: Request> = + new Request(this.api.ada.getWalletCertificateAdditionalMnemonics); + getWalletCertificateRecoveryPhraseRequest: Request> = + new Request(this.api.ada.getWalletCertificateRecoveryPhrase); + getWalletRecoveryPhraseFromCertificateRequest: Request> = + new Request(this.api.ada.getWalletRecoveryPhraseFromCertificate); restoreDaedalusRequest: Request = new Request( this.api.ada.restoreWallet ); - @observable restoreLegacyRequest: Request = new Request( this.api.ada.restoreLegacyWallet ); - @observable restoreByronRandomWalletRequest: Request = new Request( this.api.ada.restoreByronRandomWallet ); - @observable restoreByronIcarusWalletRequest: Request = new Request( this.api.ada.restoreByronIcarusWallet ); - @observable restoreByronTrezorWalletRequest: Request = new Request( this.api.ada.restoreByronTrezorWallet ); - @observable restoreByronLedgerWalletRequest: Request = new Request( this.api.ada.restoreByronLedgerWallet ); - @observable - transferFundsCalculateFeeRequest: Request< - TransferFundsCalculateFeeRequest - > = new Request(this.api.ada.transferFundsCalculateFee); - @observable + transferFundsCalculateFeeRequest: Request = + new Request(this.api.ada.transferFundsCalculateFee); transferFundsRequest: Request = new Request( this.api.ada.transferFunds ); - @observable createHardwareWalletRequest: Request = new Request( this.api.ada.createHardwareWallet ); /* ---------- Active Wallet ---------- */ - @observable active: Wallet | null | undefined = null; - @observable activeValue: BigNumber | null | undefined = null; - @observable activePublicKey: string | null | undefined = null; - @observable icoPublicKey: string | null | undefined = null; /* ---------- Create Wallet ---------- */ - @observable createWalletStep = null; - @observable createWalletShowAbortConfirmation = false; // TODO: Remove once the new wallet creation process is ready - @observable createWalletUseNewProcess = false; /* ---------- Restore Wallet ---------- */ - @observable restoreWalletStep = null; - @observable restoreWalletShowAbortConfirmation = false; // STEP: WALLET TYPE - @observable walletKind: WalletKind | null | undefined = null; - @observable walletKindDaedalus: WalletDaedalusKind | null | undefined = null; - @observable walletKindYoroi: WalletYoroiKind | null | undefined = null; - @observable walletKindHardware: WalletHardwareKind | null | undefined = null; // STEP: RECOVERY PHRASE - @observable mnemonics: Array = []; // STEP: CONFIGURATION - @observable walletName = ''; - @observable spendingPassword = ''; - @observable repeatPassword = ''; // TODO: Remove once the new restore creation process is ready - @observable restoreWalletUseNewProcess = true; - @observable restoredWallet: Wallet | null | undefined = null; /* ---------- Export Wallet ---------- */ - @observable walletExportType: WalletExportTypeChoices = 'paperWallet'; - @observable walletExportMnemonic = 'marine joke dry silk ticket thing sugar stereo aim'; /* ---------- Delete Wallet ---------- */ - @observable isDeleting = false; /* ---------- Restore Wallet ---------- */ - @observable isRestoring = false; /* ---------- Paper Wallet ---------- */ - @observable createPaperWalletCertificateStep = 0; - @observable walletCertificatePassword = null; - @observable walletCertificateAddress = null; - @observable walletCertificateRecoveryPhrase = null; - @observable generatingCertificateInProgress = false; - @observable generatingCertificateError: LocalizableError | null | undefined = null; - @observable generatingRewardsCsvInProgress = false; - @observable generatingRewardsCsvError: LocalizableError | null | undefined = null; - @observable certificateStep = null; - @observable certificateTemplate = null; - @observable additionalMnemonicWords = null; /* ---------- Transfer Funds ---------- */ - @observable transferFundsSourceWalletId = ''; - @observable transferFundsTargetWalletId = ''; - @observable transferFundsStep = 0; - @observable transferFundsFee: BigNumber | null | undefined = null; - @observable transferFundsLeftovers: BigNumber | null | undefined = null; /* ---------- Other ---------- */ @@ -264,6 +207,136 @@ export default class WalletsStore extends Store { }; _pollingBlocked = false; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + + makeObservable(this, { + undelegateWalletSubmissionSuccess: observable, + isAddressFromSameWallet: observable, + walletsRequest: observable, + accountPublicKeyRequest: observable, + icoPublicKeyRequest: observable, + importFromFileRequest: observable, + createWalletRequest: observable, + getWalletAddressesRequest: observable, + deleteWalletRequest: observable, + sendMoneyRequest: observable, + getWalletRecoveryPhraseRequest: observable, + getWalletCertificateAdditionalMnemonicsRequest: observable, + getWalletCertificateRecoveryPhraseRequest: observable, + getWalletRecoveryPhraseFromCertificateRequest: observable, + restoreDaedalusRequest: observable, + restoreLegacyRequest: observable, + restoreByronRandomWalletRequest: observable, + restoreByronIcarusWalletRequest: observable, + restoreByronTrezorWalletRequest: observable, + restoreByronLedgerWalletRequest: observable, + transferFundsCalculateFeeRequest: observable, + transferFundsRequest: observable, + createHardwareWalletRequest: observable, + active: observable, + activeValue: observable, + activePublicKey: observable, + icoPublicKey: observable, + createWalletStep: observable, + createWalletShowAbortConfirmation: observable, + createWalletUseNewProcess: observable, + restoreWalletStep: observable, + restoreWalletShowAbortConfirmation: observable, + walletKind: observable, + walletKindDaedalus: observable, + walletKindYoroi: observable, + walletKindHardware: observable, + mnemonics: observable, + walletName: observable, + spendingPassword: observable, + repeatPassword: observable, + restoreWalletUseNewProcess: observable, + restoredWallet: observable, + walletExportType: observable, + walletExportMnemonic: observable, + isDeleting: observable, + isRestoring: observable, + createPaperWalletCertificateStep: observable, + walletCertificatePassword: observable, + walletCertificateAddress: observable, + walletCertificateRecoveryPhrase: observable, + generatingCertificateInProgress: observable, + generatingCertificateError: observable, + generatingRewardsCsvInProgress: observable, + generatingRewardsCsvError: observable, + certificateStep: observable, + certificateTemplate: observable, + additionalMnemonicWords: observable, + transferFundsSourceWalletId: observable, + transferFundsTargetWalletId: observable, + transferFundsStep: observable, + transferFundsFee: observable, + transferFundsLeftovers: observable, + _getAccountPublicKey: action, + _getICOPublicKey: action, + _togglecreateWalletUseNewProcess: action, + _createWalletBegin: action, + _createWalletChangeStep: action, + _createWalletClose: action, + _createWalletAbort: action, + _restoreWalletBegin: action, + _restoreWalletEnd: action, + _restoreWalletChangeStep: action, + _restoreWalletClose: action, + _restoreWalletCancelClose: action, + _restoreWalletResetData: action, + _restoreWalletSetKind: action, + _restoreWalletSetMnemonics: action, + _restoreWalletSetConfig: action, + _createHardwareWallet: action, + _restore: action, + _transferFundsNextStep: action, + _transferFundsPrevStep: action, + _transferFunds: action, + _transferFundsSetSourceWalletId: action, + _transferFundsSetTargetWalletId: action, + _transferFundsRedeem: action, + _transferFundsClose: action, + _transferFundsCalculateFee: action, + hasActiveWallet: computed, + hasLoadedWallets: computed, + hasAnyWallets: computed, + hasRewardsWallets: computed, + hasMaxWallets: computed, + all: computed, + allWallets: computed, + allLegacyWallets: computed, + first: computed, + hasAnyLoaded: computed, + activeWalletRoute: computed, + isWalletRoute: computed, + restoreRequest: computed, + _canRedirectToWallet: computed, + refreshWalletsData: action, + resetWalletsData: action, + _importWalletFromFile: action, + _setActiveWallet: action, + _unsetActiveWallet: action, + _onRouteChange: action, + _chooseWalletExportType: action, + _pausePolling: action, + _resumePolling: action, + _setCertificateTemplate: action, + _finishCertificate: action, + _finishRewardsCsv: action, + _updateCertificateStep: action, + _closeCertificateGeneration: action, + _closeRewardsCsvGeneration: action, + _resetCertificateData: action, + _resetRewardsCsvData: action, + }); + } + setup() { setInterval(this._pollRefresh, this.WALLET_REFRESH_INTERVAL); this.registerReactions([this._updateActiveWalletOnRouteChanges]); @@ -343,7 +416,6 @@ export default class WalletsStore extends Store { ); } - @action _getAccountPublicKey = async ({ spendingPassword: passphrase, }: { @@ -364,7 +436,7 @@ export default class WalletsStore extends Store { passphrase, extended, }).promise; - runInAction('update account public key', () => { + runInAction(() => { this.activePublicKey = accountPublicKey; }); this.analytics.sendEvent( @@ -375,7 +447,6 @@ export default class WalletsStore extends Store { throw error; } }; - @action _getICOPublicKey = async ({ spendingPassword: passphrase, }: { @@ -400,7 +471,7 @@ export default class WalletsStore extends Store { purpose, }, }).promise; - runInAction('update ICO public key', () => { + runInAction(() => { this.icoPublicKey = icoPublicKey; }); this.analytics.sendEvent( @@ -415,11 +486,8 @@ export default class WalletsStore extends Store { Object.assign(this._newWalletDetails, params); try { - const recoveryPhrase: - | Array - | null - | undefined = await this.getWalletRecoveryPhraseRequest.execute() - .promise; + const recoveryPhrase: Array | null | undefined = + await this.getWalletRecoveryPhraseRequest.execute().promise; if (recoveryPhrase != null) { this.actions.walletBackup.initiateWalletBackup.trigger({ @@ -431,16 +499,13 @@ export default class WalletsStore extends Store { } }; // TODO: Remove once the new wallet creation process is ready - @action _togglecreateWalletUseNewProcess = () => { this.createWalletUseNewProcess = !this.createWalletUseNewProcess; }; - @action _createWalletBegin = () => { this.createWalletStep = 0; this.createWalletShowAbortConfirmation = false; }; - @action _createWalletChangeStep = (isBack = false) => { const currentCreateWalletStep = this.createWalletStep || 0; this.createWalletStep = @@ -449,21 +514,17 @@ export default class WalletsStore extends Store { : currentCreateWalletStep + 1; this.createWalletShowAbortConfirmation = false; }; - @action _createWalletClose = () => { this.createWalletStep = null; this.createWalletShowAbortConfirmation = false; }; - @action _createWalletAbort = () => { this.createWalletShowAbortConfirmation = true; }; - @action _restoreWalletBegin = () => { this.restoreWalletStep = 0; this.restoreWalletShowAbortConfirmation = false; }; - @action _restoreWalletEnd = async () => { this._resumePolling(); @@ -484,7 +545,6 @@ export default class WalletsStore extends Store { this._restoreWalletResetData(); } }; - @action _restoreWalletChangeStep = (isBack = false) => { // Reset restore requests to clear previous errors const currentRestoreWalletStep = this.restoreWalletStep || 0; @@ -501,7 +561,6 @@ export default class WalletsStore extends Store { : currentRestoreWalletStep + 1; this.restoreWalletShowAbortConfirmation = false; }; - @action _restoreWalletClose = () => { this._resumePolling(); @@ -521,7 +580,6 @@ export default class WalletsStore extends Store { this.actions.dialogs.closeActiveDialog.trigger(); } }; - @action _restoreWalletCancelClose = () => { this.restoreWalletShowAbortConfirmation = false; }; @@ -533,7 +591,6 @@ export default class WalletsStore extends Store { this.restoreByronTrezorWalletRequest.reset(); this.getWalletRecoveryPhraseFromCertificateRequest.reset(); }; - @action _restoreWalletResetData = () => { this.restoreWalletStep = null; this.restoreWalletShowAbortConfirmation = false; @@ -547,7 +604,6 @@ export default class WalletsStore extends Store { this.spendingPassword = ''; this.repeatPassword = ''; }; - @action _restoreWalletSetKind = ({ param, kind, @@ -558,7 +614,6 @@ export default class WalletsStore extends Store { (this as any)[`walletKind${param || ''}`] = kind; this.mnemonics = []; }; - @action _restoreWalletSetMnemonics = ({ mnemonics, }: { @@ -566,7 +621,6 @@ export default class WalletsStore extends Store { }) => { this.mnemonics = mnemonics; }; - @action _restoreWalletSetConfig = ({ param, value, @@ -582,7 +636,6 @@ export default class WalletsStore extends Store { this.repeatPassword = value; } }; - @action _createHardwareWallet = async (params: { walletName: string; extendedPublicKey: HardwareWalletExtendedPublicKeyResponse; @@ -646,9 +699,8 @@ export default class WalletsStore extends Store { } }; _finishWalletBackup = async () => { - this._newWalletDetails.mnemonic = this.stores.walletBackup.recoveryPhrase.join( - ' ' - ); + this._newWalletDetails.mnemonic = + this.stores.walletBackup.recoveryPhrase.join(' '); const wallet = await this.createWalletRequest.execute( this._newWalletDetails ).promise; @@ -666,14 +718,14 @@ export default class WalletsStore extends Store { }; _deleteWallet = async (params: { walletId: string; isLegacy: boolean }) => { // Pause polling in order to avoid fetching data for wallet we are about to delete - runInAction('AdaWalletsStore::isDeleting set', () => { + runInAction(() => { this.isDeleting = true; }); await this._pausePolling(); const walletToDelete = this.getWalletById(params.walletId); if (!walletToDelete) { - runInAction('AdaWalletsStore::isDeleting reset', () => { + runInAction(() => { this.isDeleting = false; }); return; @@ -689,7 +741,7 @@ export default class WalletsStore extends Store { await this.walletsRequest.patch((result) => { result.splice(indexOfWalletToDelete, 1); }); - runInAction('AdaWalletsStore::_deleteWallet', () => { + runInAction(() => { this.isDeleting = false; if (this.hasAnyWallets) { @@ -743,12 +795,9 @@ export default class WalletsStore extends Store { this.refreshWalletsData(); }; _setUndelegateWalletSubmissionSuccess = ({ result }: { result: boolean }) => { - runInAction( - 'AdaWalletsStore::_setUndelegateWalletSubmissionSuccess', - () => { - this.undelegateWalletSubmissionSuccess = result; - } - ); + runInAction(() => { + this.undelegateWalletSubmissionSuccess = result; + }); }; _getUnscrambledMnemonics = async ( mnemonics: Array @@ -757,16 +806,14 @@ export default class WalletsStore extends Store { // Split recovery phrase to 18 (scrambled mnemonics) + 9 (mnemonics seed) mnemonics const { passphrase, scrambledInput } = getScrambledInput(mnemonics); // Unscramble 18-word wallet certificate mnemonic to 12-word mnemonic - const unscrambledRecoveryPhrase: Array = await this.getWalletRecoveryPhraseFromCertificateRequest.execute( - { + const unscrambledRecoveryPhrase: Array = + await this.getWalletRecoveryPhraseFromCertificateRequest.execute({ passphrase, scrambledInput, - } - ).promise; + }).promise; this.getWalletRecoveryPhraseFromCertificateRequest.reset(); return unscrambledRecoveryPhrase; }; - @action _restore = async () => { this.isRestoring = true; // Pause polling in order to avoid fetching data for wallet we are about to restore @@ -796,12 +843,12 @@ export default class WalletsStore extends Store { const restoredWallet = await request.execute(data).promise; if (!restoredWallet) throw new Error('Restored wallet was not received correctly'); - runInAction('set restoredWallet', () => { + runInAction(() => { this.restoredWallet = restoredWallet; this.restoreWalletStep = 3; }); } finally { - runInAction('end wallet restore', () => { + runInAction(() => { this.isRestoring = false; }); } @@ -857,7 +904,6 @@ export default class WalletsStore extends Store { this.sendMoneyRequest.reset(); this.goToWalletRoute(wallet.id); }; - @action _transferFundsNextStep = async () => { const { transferFundsStep, @@ -881,17 +927,15 @@ export default class WalletsStore extends Store { nextStep = 2; } - runInAction('update transfer funds step', () => { + runInAction(() => { this.transferFundsStep = nextStep; }); }; - @action _transferFundsPrevStep = () => { const { transferFundsStep } = this; const prevStep = transferFundsStep > 0 ? transferFundsStep - 1 : 0; this.transferFundsStep = prevStep; }; - @action _transferFunds = async ({ spendingPassword, }: { @@ -919,7 +963,6 @@ export default class WalletsStore extends Store { this.transferFundsRequest.reset(); this.goToWalletRoute(transferFundsSourceWalletId); }; - @action _transferFundsSetSourceWalletId = ({ sourceWalletId, }: { @@ -932,7 +975,6 @@ export default class WalletsStore extends Store { // Sets to first step this.transferFundsStep = 1; }; - @action _transferFundsSetTargetWalletId = ({ targetWalletId, }: { @@ -940,17 +982,14 @@ export default class WalletsStore extends Store { }) => { this.transferFundsTargetWalletId = targetWalletId; }; - @action _transferFundsRedeem = () => { this.transferFundsStep = 0; // TODO: Call API method }; - @action _transferFundsClose = () => { this.transferFundsStep = 0; this.transferFundsFee = null; this.transferFundsCalculateFeeRequest.reset(); }; - @action _transferFundsCalculateFee = async ({ sourceWalletId, }: { @@ -964,7 +1003,7 @@ export default class WalletsStore extends Store { } = await this.transferFundsCalculateFeeRequest.execute({ sourceWalletId, }).promise; - runInAction('set migration fee and leftovers', () => { + runInAction(() => { this.transferFundsFee = fee; this.transferFundsLeftovers = leftovers; }); @@ -972,17 +1011,14 @@ export default class WalletsStore extends Store { // =================== PUBLIC API ==================== // // GETTERS - @computed get hasActiveWallet(): boolean { return !!this.active; } - @computed get hasLoadedWallets(): boolean { return this.walletsRequest.wasExecuted; } - @computed get hasAnyWallets(): boolean { if (this.walletsRequest.result == null) return false; return ( @@ -990,59 +1026,48 @@ export default class WalletsStore extends Store { ); } - @computed get hasRewardsWallets(): boolean { return this.allWallets.length > 0; } - @computed get hasMaxWallets(): boolean { return this.all.length >= MAX_ADA_WALLETS_COUNT; } - @computed get all(): Array { return [...this.allWallets, ...this.allLegacyWallets]; } - @computed get allWallets(): Array { return this.walletsRequest.result ? this.walletsRequest.result.filter(({ isLegacy }: Wallet) => !isLegacy) : []; } - @computed get allLegacyWallets(): Array { return this.walletsRequest.result ? this.walletsRequest.result.filter(({ isLegacy }: Wallet) => isLegacy) : []; } - @computed get first(): Wallet | null | undefined { return this.all.length > 0 ? this.all[0] : null; } - @computed get hasAnyLoaded(): boolean { return this.all.length > 0; } - @computed get activeWalletRoute(): string | null | undefined { if (!this.active) return null; return this.getWalletRoute(this.active.id); } - @computed get isWalletRoute(): boolean { const { currentRoute } = this.stores.app; return matchRoute(`${ROUTES.WALLETS.ROOT}(/*rest)`, currentRoute); } - @computed - // @ts-ignore ts-migrate(2314) FIXME: Generic type 'LocalizedRequest' requires 1... Remove this comment to see the full error message get restoreRequest(): Request { switch (this.walletKind) { case WALLET_KINDS.DAEDALUS: @@ -1093,7 +1118,6 @@ export default class WalletsStore extends Store { } // =================== PRIVATE API ==================== // - @computed get _canRedirectToWallet(): boolean { const { currentRoute } = this.stores.app; const isRootRoute = matchRoute(ROUTES.WALLETS.ROOT, currentRoute); @@ -1133,7 +1157,7 @@ export default class WalletsStore extends Store { const { currentRoute } = this.stores.app; const hasAnyWalletLoaded = this.hasAnyLoaded; const isWalletAddPage = matchRoute(ROUTES.WALLETS.ADD, currentRoute); - runInAction('WalletsStore::_updateActiveWalletOnRouteChanges', () => { + runInAction(() => { // There are not wallets loaded (yet) -> unset active and return if (isWalletAddPage || !hasAnyWalletLoaded) return this._unsetActiveWallet(); @@ -1195,7 +1219,7 @@ export default class WalletsStore extends Store { return false; } - runInAction('check if address is from the same wallet', () => { + runInAction(() => { const walletAddresses = this.stores.addresses.all .slice() .map((addr) => addr.id); @@ -1216,7 +1240,6 @@ export default class WalletsStore extends Store { }; isValidCertificateMnemonic = (mnemonic: string) => this.api.ada.isValidCertificateMnemonic(mnemonic); - @action refreshWalletsData = async () => { // Prevent wallets data refresh if polling is blocked if (this._pollingBlocked) return; @@ -1231,14 +1254,14 @@ export default class WalletsStore extends Store { ) .map((wallet: Wallet) => wallet.id); await this.actions.walletsLocal.refreshWalletsLocalData.trigger(); - runInAction('refresh active wallet', () => { + runInAction(() => { if (this.active) { this._setActiveWallet({ walletId: this.active.id, }); } }); - runInAction('refresh address data', () => { + runInAction(() => { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'WalletsS... Remove this comment to see the full error message this.stores.addresses.addressesRequests = walletIds.map((walletId) => ({ walletId, @@ -1247,20 +1270,17 @@ export default class WalletsStore extends Store { this.stores.addresses._refreshAddresses(); }); - runInAction('refresh transaction data', () => { + runInAction(() => { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'WalletsS... Remove this comment to see the full error message this.stores.transactions.transactionsRequests = walletIds.map( (walletId) => ({ walletId, - recentRequest: this.stores.transactions._getTransactionsRecentRequest( - walletId - ), - allRequest: this.stores.transactions._getTransactionsAllRequest( - walletId - ), - withdrawalsRequest: this.stores.transactions._getWithdrawalsRequest( - walletId - ), + recentRequest: + this.stores.transactions._getTransactionsRecentRequest(walletId), + allRequest: + this.stores.transactions._getTransactionsAllRequest(walletId), + withdrawalsRequest: + this.stores.transactions._getWithdrawalsRequest(walletId), }) ); @@ -1269,14 +1289,12 @@ export default class WalletsStore extends Store { this.actions.wallets.refreshWalletsDataSuccess.trigger(); } }; - @action resetWalletsData = () => { this.walletsRequest.reset(); this.stores.addresses.addressesRequests = []; this.stores.transactions.transactionsRequests = []; this.isAddressFromSameWallet = false; }; - @action _importWalletFromFile = async (params: WalletImportFromFileParams) => { const { filePath, walletName, spendingPassword } = params; const importedWallet = await this.importFromFileRequest.execute({ @@ -1292,7 +1310,6 @@ export default class WalletsStore extends Store { this.goToWalletRoute(importedWallet.id); this.refreshWalletsData(); }; - @action _setActiveWallet = ({ walletId }: { walletId: string }) => { if (this.hasAnyWallets) { const activeWalletId = this.active ? this.active.id : null; @@ -1325,9 +1342,8 @@ export default class WalletsStore extends Store { this.activeValue = formattedWalletAmount(this.active.amount); if (this.active && this.active.isHardwareWallet) { - const { - hardwareWalletsConnectionData, - } = this.stores.hardwareWallets; + const { hardwareWalletsConnectionData } = + this.stores.hardwareWallets; const hardwareWalletConnectionData = get( hardwareWalletsConnectionData, this.active.id @@ -1351,7 +1367,6 @@ export default class WalletsStore extends Store { } } }; - @action _unsetActiveWallet = () => { this.active = null; this.activeValue = null; @@ -1359,7 +1374,6 @@ export default class WalletsStore extends Store { this.icoPublicKey = null; this.stores.addresses.lastGeneratedAddress = null; }; - @action _onRouteChange = (options: { route: string; params: Record | null | undefined; @@ -1372,7 +1386,6 @@ export default class WalletsStore extends Store { this.isAddressFromSameWallet = false; } }; - @action _chooseWalletExportType = (params: { walletExportType: WalletExportTypeChoices; }) => { @@ -1380,15 +1393,13 @@ export default class WalletsStore extends Store { this.walletExportType = params.walletExportType; } }; - @action _pausePolling = async () => { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message if (this.walletsRequest.isExecuting) await this.walletsRequest; - runInAction('AdaWalletsStore::_pausePolling', () => { + runInAction(() => { this._pollingBlocked = true; }); }; - @action _resumePolling = () => { this._pollingBlocked = false; }; @@ -1414,12 +1425,13 @@ export default class WalletsStore extends Store { // Generate wallet recovery phrase - const recoveryPhrase: Array = yield this.getWalletRecoveryPhraseRequest.execute() - .promise; + const recoveryPhrase: Array = + yield this.getWalletRecoveryPhraseRequest.execute().promise; // Generate 9-words (additional) mnemonic - const additionalMnemonicWords: Array = yield this.getWalletCertificateAdditionalMnemonicsRequest.execute() - .promise; + const additionalMnemonicWords: Array = + yield this.getWalletCertificateAdditionalMnemonicsRequest.execute() + .promise; this.additionalMnemonicWords = additionalMnemonicWords.join(' '); // Generate spending password from 9-word mnemonic and save to store // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. @@ -1427,15 +1439,13 @@ export default class WalletsStore extends Store { this.walletCertificatePassword = spendingPassword; // Generate paper wallet scrambled mnemonic - const walletCertificateRecoveryPhrase: Array = yield this.getWalletCertificateRecoveryPhraseRequest.execute( - { + const walletCertificateRecoveryPhrase: Array = + yield this.getWalletCertificateRecoveryPhraseRequest.execute({ passphrase: spendingPassword, input: recoveryPhrase.join(' '), - } - ).promise; - this.walletCertificateRecoveryPhrase = walletCertificateRecoveryPhrase.join( - ' ' - ); + }).promise; + this.walletCertificateRecoveryPhrase = + walletCertificateRecoveryPhrase.join(' '); // Create temporary wallet const walletData = { name: 'Paper Wallet', @@ -1496,7 +1506,7 @@ export default class WalletsStore extends Store { buildLabel, timestamp, }); - runInAction('handle successful certificate download', () => { + runInAction(() => { // Reset progress this._updateCertificateCreationState(false); @@ -1504,7 +1514,7 @@ export default class WalletsStore extends Store { this._updateCertificateStep(); }); } catch (error) { - runInAction('handle failed certificate download', () => { + runInAction(() => { // Reset progress this._updateCertificateCreationState(false, error); }); @@ -1521,11 +1531,8 @@ export default class WalletsStore extends Store { filePath: string; wallet: Wallet; }) => { - const { - currentLocale, - currentDateFormat, - currentTimeFormat, - } = this.stores.profile; + const { currentLocale, currentDateFormat, currentTimeFormat } = + this.stores.profile; const { network, isMainnet } = this.environment; const intl = i18nContext(currentLocale); @@ -1635,11 +1642,11 @@ export default class WalletsStore extends Store { fileContent, filePath, }); - runInAction('handle successful rewards csv download', () => { + runInAction(() => { this._updateRewardsCsvCreationState(false); }); } catch (error) { - runInAction('handle failed rewards csv download', () => { + runInAction(() => { this._updateRewardsCsvCreationState(false, error); }); } @@ -1661,25 +1668,21 @@ export default class WalletsStore extends Store { } } ); - @action _setCertificateTemplate = (params: { selectedTemplate: string }) => { this.certificateTemplate = params.selectedTemplate; this._updateCertificateStep(); }; - @action _finishCertificate = () => { this._updateGeneratingCertificateError(); this._closeCertificateGeneration(); }; - @action _finishRewardsCsv = () => { this._updateGeneratingRewardsCsvError(); this._closeRewardsCsvGeneration(); }; - @action _updateCertificateStep = (isBack = false) => { this._updateGeneratingCertificateError(); @@ -1688,19 +1691,16 @@ export default class WalletsStore extends Store { ? currentCertificateStep - 1 : currentCertificateStep + 1; }; - @action _closeCertificateGeneration = () => { this.actions.dialogs.closeActiveDialog.trigger(); this._resetCertificateData(); }; - @action _closeRewardsCsvGeneration = () => { this.actions.dialogs.closeActiveDialog.trigger(); this._resetRewardsCsvData(); }; - @action _resetCertificateData = () => { this.walletCertificatePassword = null; this.walletCertificateAddress = null; @@ -1711,7 +1711,6 @@ export default class WalletsStore extends Store { this._updateGeneratingCertificateError(); }; - @action _resetRewardsCsvData = () => { this.generatingRewardsCsvInProgress = false; diff --git a/source/renderer/app/stores/WindowStore.ts b/source/renderer/app/stores/WindowStore.ts index 3a18df072c..bd33b95343 100644 --- a/source/renderer/app/stores/WindowStore.ts +++ b/source/renderer/app/stores/WindowStore.ts @@ -1,11 +1,23 @@ -import { action } from 'mobx'; +import { action, makeObservable } from 'mobx'; import Store from './lib/Store'; +import { Api } from '../api'; +import { ActionsMap } from '../actions'; +import { AnalyticsTracker } from '../analytics'; // TODO: refactor all parts that rely on this to ipc channels! // @ts-ignore ts-migrate(2339) FIXME: Property 'ipcRenderer' does not exist on type 'typ... Remove this comment to see the full error message const { ipcRenderer } = global; export default class WindowStore extends Store { _isTest = false; + constructor( + protected api: Api, + protected actions: ActionsMap, + protected analytics: AnalyticsTracker + ) { + super(api, actions, analytics); + makeObservable(this); + } + setup() { this.actions.window.resizeWindow.listen(this._resizeWindow); this.actions.window.closeWindow.listen(this.closeWindow); diff --git a/source/renderer/app/stores/index.ts b/source/renderer/app/stores/index.ts index 56a5e6e958..1c4808ca49 100644 --- a/source/renderer/app/stores/index.ts +++ b/source/renderer/app/stores/index.ts @@ -128,8 +128,9 @@ export const setUpStores = action( }); // Configure and initialize all stores executeOnEveryStore((store) => { - if (stores) store.configure(stores); + !!stores && store?.configure(stores); }); + executeOnEveryStore((store) => store.initialize()); return stores; } diff --git a/source/renderer/app/stores/lib/Request.ts b/source/renderer/app/stores/lib/Request.ts index 162b93760d..76a916ea09 100644 --- a/source/renderer/app/stores/lib/Request.ts +++ b/source/renderer/app/stores/lib/Request.ts @@ -1,4 +1,10 @@ -import { observable, action, computed, isObservableArray } from 'mobx'; +import { + observable, + action, + computed, + isObservableArray, + makeObservable, +} from 'mobx'; import { isEqual } from 'lodash/fp'; import ExtendableError from 'es6-error'; @@ -12,15 +18,10 @@ export type ApiCallType = { result: any; }; export default class Request { - @observable result: Result | null | undefined = null; - @observable error: Error | null | undefined = null; - @observable isExecuting = false; - @observable isError = false; - @observable wasExecuted = false; promise: Promise | null | undefined = null; _method: (...args: Array) => any; @@ -28,6 +29,16 @@ export default class Request { _currentApiCall: ApiCallType | null | undefined = null; constructor(method: (...args: Array) => any) { + makeObservable(this, { + result: observable, + error: observable, + isExecuting: observable, + isError: observable, + wasExecuted: observable, + isExecutingFirstTime: computed, + reset: action, + }); + this._method = method; } @@ -104,7 +115,6 @@ export default class Request { ); } - @computed get isExecutingFirstTime(): boolean { return !this.wasExecuted && this.isExecuting; } @@ -144,7 +154,6 @@ export default class Request { }); } - @action reset(): Request { this.result = null; this.error = null; diff --git a/source/renderer/app/stores/lib/Store.ts b/source/renderer/app/stores/lib/Store.ts index d53f15e9bc..521a6632bd 100644 --- a/source/renderer/app/stores/lib/Store.ts +++ b/source/renderer/app/stores/lib/Store.ts @@ -1,8 +1,8 @@ import Reaction from './Reaction'; -import type { ActionsMap } from '../../actions/index'; import type { StoresMap } from '../index'; -import type { Api } from '../../api/index'; import type { Environment } from '../../../../common/types/environment.types'; +import { Api } from '../../api'; +import { ActionsMap } from '../../actions'; import { AnalyticsTracker } from '../../analytics'; export default class Store { diff --git a/source/renderer/app/themes/daedalus.ts b/source/renderer/app/themes/daedalus.ts index 0caee5b6a5..54fb136386 100644 --- a/source/renderer/app/themes/daedalus.ts +++ b/source/renderer/app/themes/daedalus.ts @@ -1,24 +1,24 @@ -import SimpleAutocomplete from 'react-polymorph/lib/themes/simple/SimpleAutocomplete.scss'; -import SimpleBubble from 'react-polymorph/lib/themes/simple/SimpleBubble.scss'; -import SimpleButton from 'react-polymorph/lib/themes/simple/SimpleButton.scss'; -import SimpleCheckbox from 'react-polymorph/lib/themes/simple/SimpleCheckbox.scss'; -import SimpleDropdown from 'react-polymorph/lib/themes/simple/SimpleDropdown.scss'; -import SimpleFormField from 'react-polymorph/lib/themes/simple/SimpleFormField.scss'; -import SimpleInput from 'react-polymorph/lib/themes/simple/SimpleInput.scss'; -import SimpleLoadingSpinner from 'react-polymorph/lib/themes/simple/SimpleLoadingSpinner.scss'; -import SimpleModal from 'react-polymorph/lib/themes/simple/SimpleModal.scss'; -import SimpleOptions from 'react-polymorph/lib/themes/simple/SimpleOptions.scss'; -import SimplePasswordInput from 'react-polymorph/lib/themes/simple/SimplePasswordInput.scss'; -import SimplePopOver from 'react-polymorph/lib/themes/simple/SimplePopOver.scss'; -import SimpleRadio from 'react-polymorph/lib/themes/simple/SimpleRadio.scss'; -import SimpleScrollBar from 'react-polymorph/lib/themes/simple/SimpleScrollBar.scss'; -import SimpleSelect from 'react-polymorph/lib/themes/simple/SimpleSelect.scss'; -import SimpleStepper from 'react-polymorph/lib/themes/simple/SimpleStepper.scss'; -import SimpleSwitch from 'react-polymorph/lib/themes/simple/SimpleSwitch.scss'; -import SimpleTextArea from 'react-polymorph/lib/themes/simple/SimpleTextArea.scss'; -import SimpleTooltip from 'react-polymorph/lib/themes/simple/SimpleTooltip.scss'; -import SimpleLink from 'react-polymorph/lib/themes/simple/SimpleLink.scss'; -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import SimpleAutocomplete from '@react-polymorph/themes/simple/SimpleAutocomplete.scss'; +import SimpleBubble from '@react-polymorph/themes/simple/SimpleBubble.scss'; +import SimpleButton from '@react-polymorph/themes/simple/SimpleButton.scss'; +import SimpleCheckbox from '@react-polymorph/themes/simple/SimpleCheckbox.scss'; +import SimpleDropdown from '@react-polymorph/themes/simple/SimpleDropdown.scss'; +import SimpleFormField from '@react-polymorph/themes/simple/SimpleFormField.scss'; +import SimpleInput from '@react-polymorph/themes/simple/SimpleInput.scss'; +import SimpleLoadingSpinner from '@react-polymorph/themes/simple/SimpleLoadingSpinner.scss'; +import SimpleModal from '@react-polymorph/themes/simple/SimpleModal.scss'; +import SimpleOptions from '@react-polymorph/themes/simple/SimpleOptions.scss'; +import SimplePasswordInput from '@react-polymorph/themes/simple/SimplePasswordInput.scss'; +import SimplePopOver from '@react-polymorph/themes/simple/SimplePopOver.scss'; +import SimpleRadio from '@react-polymorph/themes/simple/SimpleRadio.scss'; +import SimpleScrollBar from '@react-polymorph/themes/simple/SimpleScrollBar.scss'; +import SimpleSelect from '@react-polymorph/themes/simple/SimpleSelect.scss'; +import SimpleStepper from '@react-polymorph/themes/simple/SimpleStepper.scss'; +import SimpleSwitch from '@react-polymorph/themes/simple/SimpleSwitch.scss'; +import SimpleTextArea from '@react-polymorph/themes/simple/SimpleTextArea.scss'; +import SimpleTooltip from '@react-polymorph/themes/simple/SimpleTooltip.scss'; +import SimpleLink from '@react-polymorph/themes/simple/SimpleLink.scss'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; const { AUTOCOMPLETE, diff --git a/source/renderer/app/themes/overrides/index.ts b/source/renderer/app/themes/overrides/index.ts index a2f989f3c6..38581cd997 100644 --- a/source/renderer/app/themes/overrides/index.ts +++ b/source/renderer/app/themes/overrides/index.ts @@ -1,4 +1,4 @@ -import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import { IDENTIFIERS } from '@react-polymorph/themes/API'; import AutocompleteOverrides from './AutocompleteOverrides.scss'; import ButtonOverrides from './ButtonOverrides.scss'; import CheckboxOverrides from './CheckboxOverrides.scss'; diff --git a/source/renderer/app/utils/logging.ts b/source/renderer/app/utils/logging.ts index 0587a45412..0143f60de6 100644 --- a/source/renderer/app/utils/logging.ts +++ b/source/renderer/app/utils/logging.ts @@ -23,20 +23,19 @@ const environmentData = { version, }; -const logToLevel = (level: LoggingLevel) => ( - message: string, - data: Record | null | undefined -) => { - const args = [ - formatContext({ ...messageContext, level }), - { - message, - data, - environmentData, - }, - ]; - electronLog[level](...args); -}; +const logToLevel = + (level: LoggingLevel) => + (message: string, data: Record | null | undefined) => { + const args = [ + formatContext({ ...messageContext, level }), + { + message, + data, + environmentData, + }, + ]; + ['error', 'warn'].includes(level) && electronLog[level](...args); + }; export const logger: Logger = { debug: logToLevel('debug'), diff --git a/source/renderer/declaration.d.ts b/source/renderer/declaration.d.ts index dd70ec5b51..42ae828b5e 100644 --- a/source/renderer/declaration.d.ts +++ b/source/renderer/declaration.d.ts @@ -1,6 +1,13 @@ +import { Environment } from '../common/types/environment.types'; + declare module '*.scss'; -declare module '*.inline.svg'; +declare module '*.inline.svg' { + const content: React.FC>; + export default content; +} declare namespace globalThis { /* eslint-disable-next-line */ var isFlight: boolean; + /* eslint-disable-next-line */ + var environment: Environment; } diff --git a/source/renderer/index.js b/source/renderer/index.js new file mode 100644 index 0000000000..3fd3922f39 --- /dev/null +++ b/source/renderer/index.js @@ -0,0 +1,4 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +require('./app/index'); +//# sourceMappingURL=index.js.map diff --git a/source/renderer/index.js.map b/source/renderer/index.js.map new file mode 100644 index 0000000000..6df410dc9b --- /dev/null +++ b/source/renderer/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;AAAA,uBAAqB"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Autocomplete.js.map b/source/renderer/react-polymorph/components/Autocomplete.js.map new file mode 100644 index 0000000000..4d208792b4 --- /dev/null +++ b/source/renderer/react-polymorph/components/Autocomplete.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Autocomplete.js","sourceRoot":"","sources":["Autocomplete.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Autocomplete.tsx b/source/renderer/react-polymorph/components/Autocomplete.tsx new file mode 100644 index 0000000000..58f44b5357 --- /dev/null +++ b/source/renderer/react-polymorph/components/Autocomplete.tsx @@ -0,0 +1,421 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element } from 'react'; +// external libraries +import _ from 'lodash'; +// interal components +import { GlobalListeners } from './HOC/GlobalListeners'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +import { composeFunctions } from '../utils/props'; +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +export type AutocompleteProps = { + className?: string; + context: ThemeContextProp; + error: string | null | undefined; + invalidCharsRegex: RegExp; + isOpeningUpward: boolean; + label?: string | Element; + maxSelections?: number; + requiredSelections: [number]; + requiredSelectionsInfo?: (required: number, actual: number) => string; + maxVisibleOptions: number; + multipleSameSelections: boolean; + onChange?: (...args: Array) => any; + options: Array; + preselectedOptions?: Array; + placeholder?: string; + renderSelections?: (...args: Array) => any; + renderOptions?: (...args: Array) => any; + skin?: ComponentType; + sortAlphabetically: boolean; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; + error: string; + filteredOptions: Array; + isOpen: boolean; + inputValue: string; + mouseIsOverOptions: boolean; + selectedOptions: Array; +}; + +class AutocompleteBase extends Component { + // declare ref types + rootElement: Element | null | undefined; + inputElement: Element<'input'> | null | undefined; + suggestionsElement: Element | null | undefined; + optionsElement: Element | null | undefined; + // define static properties + static displayName = 'Autocomplete'; + static defaultProps = { + context: createEmptyContext(), + error: null, + invalidCharsRegex: /[^a-zA-Z0-9\s]/g, + // only allow letters and numbers by default + isOpeningUpward: false, + maxVisibleOptions: 10, + // max number of visible options + multipleSameSelections: true, + // if true then same word can be selected multiple times + options: [], + requiredSelections: [], + sortAlphabetically: true, + // options are sorted alphabetically by default + theme: null, + themeId: IDENTIFIERS.AUTOCOMPLETE, + themeOverrides: {}, + }; + + constructor(props: AutocompleteProps) { + super(props); + // define refs + this.rootElement = React.createRef(); + this.inputElement = React.createRef(); + this.suggestionsElement = React.createRef(); + this.optionsElement = React.createRef(); + const { + context, + themeId, + theme, + themeOverrides, + sortAlphabetically, + options, + preselectedOptions, + } = props; + this.state = { + inputValue: '', + error: '', + selectedOptions: preselectedOptions || [], + filteredOptions: + sortAlphabetically && options ? options.sort() : options || [], + isOpen: false, + mouseIsOverOptions: false, + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: AutocompleteProps) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + clear = () => this._removeOptions(); + focus = () => this.handleAutocompleteClick(); + open = () => + this.setState({ + isOpen: true, + }); + close = () => + this.setState({ + isOpen: false, + }); + toggleOpen = () => { + if ( + this.state.isOpen && + this.optionsElement && + this.optionsElement.current + ) { + // set Options scroll position to top on close + this.optionsElement.current.scrollTop = 0; + } + + this.setState((prevState) => ({ + isOpen: !prevState.isOpen, + })); + }; + toggleMouseLocation = () => + this.setState((prevState) => ({ + mouseIsOverOptions: !prevState.mouseIsOverOptions, + })); + handleAutocompleteClick = () => { + const { inputElement } = this; + + if (inputElement && inputElement.current) { + inputElement.current.focus(); + } + + // toggle options open/closed + this.toggleOpen(); + }; + onKeyDown = (event: React.KeyboardEvent) => { + if ( + // Check for backspace in order to delete the last selected option + event.keyCode === 8 && + !event.target.value && + this.state.selectedOptions.length + ) { + // Remove last selected option + this.removeOption(this.state.selectedOptions.length - 1, event); + } else if (event.keyCode === 27) { + // ESCAPE key: Stops propagation & modal closing + event.stopPropagation(); + } else if (event.keyCode === 13) { + // ENTER key: Opens suggestions + this.open(); + } + }; + // onChange handler for input element in AutocompleteSkin + handleInputChange = (event: React.SyntheticEvent) => { + const { value } = event.target; + const multipleValues = value.split(' '); + const hasMultipleValues = multipleValues.length > 1; + + this._setInputValue(value); + + if (hasMultipleValues) { + this.open(); + setTimeout(() => { + this.updateSelectedOptions(event, multipleValues); + }, 0); + } + }; + // passed to Options onChange handler in AutocompleteSkin + handleChange = (option: any, event: React.SyntheticEvent) => { + this.updateSelectedOptions(event, option); + }; + updateSelectedOptions = ( + event: React.SyntheticEvent, + selectedOption: any = null + ) => { + const { maxSelections, multipleSameSelections, options } = this.props; + const { selectedOptions, isOpen } = this.state; + let { filteredOptions } = this.state; + const canMoreOptionsBeSelected = + maxSelections != null ? selectedOptions.length < maxSelections : true; + const areFilteredOptionsAvailable = + filteredOptions && filteredOptions.length > 0; + let skipValueSelection = false; + + if ( + !maxSelections || + (canMoreOptionsBeSelected && areFilteredOptionsAvailable) + ) { + if (!selectedOption || !selectedOption.length) return; + const option = _.isString(selectedOption) + ? selectedOption.trim() + : selectedOption.filter((item) => item); + const newSelectedOptions: Array = [...selectedOptions]; + + if (option && Array.isArray(option)) { + filteredOptions = options; + option.forEach((item) => { + const optionCanBeSelected = + (multipleSameSelections && filteredOptions.includes(item)) || + (filteredOptions.includes(item) && + !selectedOptions.includes(item) && + !newSelectedOptions.includes(item)); + + if (!optionCanBeSelected && !skipValueSelection) { + this._setInputValue(item, true); + + skipValueSelection = true; + return; + } + + if ( + item && + optionCanBeSelected && + isOpen && + !skipValueSelection && + newSelectedOptions.length < maxSelections + ) { + newSelectedOptions.push(item); + } + }); + } else { + const optionCanBeSelected = + multipleSameSelections || !selectedOptions.includes(option); + + if (option && optionCanBeSelected && isOpen) { + newSelectedOptions.push(option); + } + } + + this.selectionChanged(newSelectedOptions, event); + this.setState({ + selectedOptions: newSelectedOptions, + isOpen: false, + }); + } + + if (!skipValueSelection) { + this._setInputValue(''); + } + }; + removeOption = (index: number, event: React.SyntheticEvent) => { + const { selectedOptions } = this.state; + + _.pullAt(selectedOptions, index); + + this.selectionChanged(selectedOptions, event); + this.setState({ + selectedOptions, + }); + }; + selectionChanged = ( + selectedOptions: Array, + event: React.SyntheticEvent + ) => { + if (this.props.onChange) this.props.onChange(selectedOptions, event); + }; + // returns an object containing props, theme, and method handlers + // associated with rendering this.state.selectedOptions, the user can call + // this in the body of the renderSelections function + getSelectionProps = ({ + removeSelection, + }: { + removeSelection: (...args: Array) => any; + } = {}) => { + const { themeId } = this.props; + const { inputValue, isOpen, selectedOptions, composedTheme } = this.state; + return { + inputValue, + isOpen, + selectedOptions, + theme: composedTheme[themeId], + removeSelection: ( + index: number, + event: React.SyntheticEvent // the user's custom removeSelection event handler is composed with + ) => + // the internal functionality of Autocomplete (this.removeOption) + composeFunctions(removeSelection, this.removeOption)(index, event), + }; + }; + + render() { + // destructuring props ensures only the "...rest" get passed down + const { + context, + invalidCharsRegex, + multipleSameSelections, + sortAlphabetically, + skin, + theme, + themeOverrides, + onChange, + error, + ...rest + } = this.props; + const AutocompleteSkin = skin || context.skins[IDENTIFIERS.AUTOCOMPLETE]; + return ( + + {({ optionsMaxHeight, optionHeight }) => ( + + )} + + ); + } + + // ======== PRIVATE METHOD ========== + _removeOptions = () => { + const { onChange } = this.props; + onChange ? onChange([]) : null; + this.setState({ + selectedOptions: [], + inputValue: '', + }); + }; + _filterOptions = (value: string) => { + let filteredOptions = []; + + if (value !== '') { + _.some(this.props.options, (option) => { + if (_.startsWith(option, value)) { + filteredOptions.push(option); + } + }); + } else { + filteredOptions = this.props.options; + } + + return filteredOptions; + }; + _filterInvalidChars = (value: string) => { + let filteredValue = ''; + + if (this.props.invalidCharsRegex.test(value)) { + filteredValue = value.replace(this.props.invalidCharsRegex, ''); + } else { + filteredValue = value; + } + + return filteredValue; + }; + _setInputValue = (value: string, shouldFocus?: boolean) => { + const multipleValues = value.split(' '); + + if (multipleValues && multipleValues.length > 1) { + let selectedOptions = []; + multipleValues.forEach((itemValue) => { + const filteredValue = this._filterInvalidChars(itemValue); + + selectedOptions = [ + ...selectedOptions, + ...this._filterOptions(filteredValue), + ]; + }); + this.setState({ + isOpen: true, + inputValue: '', + filteredOptions: Array.from(new Set(selectedOptions)), + }); + } else { + const filteredValue = this._filterInvalidChars(value); + + const filteredOptions = this._filterOptions(filteredValue); + + this.setState({ + isOpen: !!value, + inputValue: filteredValue, + filteredOptions, + }); + setTimeout(() => { + if (shouldFocus) this.focus(); + }, 0); + } + }; +} + +export const Autocomplete = withTheme(AutocompleteBase); diff --git a/source/renderer/react-polymorph/components/Bubble.js.map b/source/renderer/react-polymorph/components/Bubble.js.map new file mode 100644 index 0000000000..770c4bfb2a --- /dev/null +++ b/source/renderer/react-polymorph/components/Bubble.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Bubble.js","sourceRoot":"","sources":["Bubble.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Bubble.tsx b/source/renderer/react-polymorph/components/Bubble.tsx new file mode 100644 index 0000000000..7b83c28f7d --- /dev/null +++ b/source/renderer/react-polymorph/components/Bubble.tsx @@ -0,0 +1,210 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element, ElementRef } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +import { addDocumentListeners, removeDocumentListeners } from '../utils/events'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +export type BubblePosition = { + width: number; + positionX: number; + positionY: number; +}; +export type BubbleProps = { + className?: string; + context: ThemeContextProp; + isCentered: boolean; + isHidden: boolean; + isFloating: boolean; + isOpeningUpward: boolean; + isTransparent: boolean; + arrowRelativeToTip: boolean; + noArrow?: boolean; + skin?: ComponentType; + theme: Record | null | undefined; + // takes precedence over them in context if passed + themeId: string; + themeOverrides: Record; + // custom css/scss from user adhering to component's theme API + targetRef?: ElementRef; // ref to the target DOM element used for positioning the bubble +}; +type State = { + composedTheme: Record; + position: BubblePosition | null | undefined; +}; + +class BubbleBase extends Component { + // declare ref types + rootElement: Element | null | undefined; + // define static properties + static displayName = 'Bubble'; + static defaultProps = { + context: createEmptyContext(), + isCentered: false, + isHidden: false, + isFloating: false, + isOpeningUpward: false, + isTransparent: true, + arrowRelativeToTip: false, + noArrow: false, + theme: null, + themeId: IDENTIFIERS.BUBBLE, + themeOverrides: {}, + }; + + constructor(props: BubbleProps) { + super(props); + // define ref + this.rootElement = React.createRef(); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + position: null, + }; + } + + _hasEventListeners = false; + + componentDidMount() { + setTimeout(() => { + if (this.props.isFloating) this._updatePosition(); + }, 0); + } + + componentDidUpdate(prevProps: BubbleProps) { + const { isHidden } = this.props; + const didVisibilityChange = isHidden !== prevProps.isHidden; + const wasBubbleHidden = !prevProps.isHidden && isHidden; + + if (prevProps.isFloating && !isHidden && !this._hasEventListeners) { + this._handleScrollEventListener('add'); + + addDocumentListeners(this._getDocumentEvents()); + window.addEventListener('resize', this._updatePosition); + this._hasEventListeners = true; + } + + if (wasBubbleHidden) this._removeAllEventListeners(); + if (didVisibilityChange) this._updatePosition(); + + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + componentWillUnmount() { + if (this._hasEventListeners) this._removeAllEventListeners(); + } + + // =========== PRIVATE HELPERS ============== + _handleScrollEventListener = (action: string) => { + // const rootNode = this.rootElement; + const { rootElement } = this; + + if (rootElement) { + const scrollableNode = this._getFirstScrollableParent(rootElement); + + if (scrollableNode) { + if (action === 'add') { + scrollableNode.addEventListener('scroll', this._updatePosition); + } else if (action === 'remove') { + scrollableNode.removeEventListener('scroll', this._updatePosition); + } + } + } + }; + + _removeAllEventListeners() { + if (this._hasEventListeners) { + removeDocumentListeners(this._getDocumentEvents()); + + this._handleScrollEventListener('remove'); + + window.removeEventListener('resize', this._updatePosition); + this._hasEventListeners = false; + } + } + + _getFirstScrollableParent = (element: ElementRef) => { + if (element == null) return null; + const { rootElement } = this; + const node = {}.hasOwnProperty.call(element, 'current') + ? element.current + : element; + + if (rootElement) { + if ( + node === rootElement.current || + node.scrollHeight <= node.clientHeight + ) { + return this._getFirstScrollableParent(node.parentElement); + } + } + + return node; + }; + _updatePosition = () => { + const { isOpeningUpward, targetRef } = this.props; + const { rootElement } = this; + let target = + targetRef && typeof targetRef !== 'string' ? targetRef.current : null; + + // Without a target, try to fallback to the parent node + if (!target) { + // Only proceed if the root element is defined + if (!rootElement || !rootElement.current) return; + target = rootElement.current.parentElement; + } + + const targetRect = target.getBoundingClientRect(); + let positionY; + + if (isOpeningUpward) { + // Since we don't know the height of the bubble before rendering it we positioning + // it with { bottom: XYpx } (within the viewport) and need this calculation: + positionY = window.innerHeight - targetRect.top; + } else { + positionY = targetRect.bottom; + } + + const position = { + width: targetRect.width, + positionX: targetRect.left, + positionY, + }; + this.setState({ + position, + }); + }; + + _getDocumentEvents() { + return { + resize: this._updatePosition, + scroll: this._updatePosition, + }; + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const BubbleSkin = skin || context.skins[IDENTIFIERS.BUBBLE]; + return ( + + ); + } +} + +export const Bubble = withTheme(BubbleBase); diff --git a/source/renderer/react-polymorph/components/Button.js.map b/source/renderer/react-polymorph/components/Button.js.map new file mode 100644 index 0000000000..0f4c6267d6 --- /dev/null +++ b/source/renderer/react-polymorph/components/Button.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Button.js","sourceRoot":"","sources":["Button.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAoBhC,MAAM,UAAW,SAAQ,iBAAuB;IAC9C,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC;IAC9B,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,MAAM;QAC3B,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,UAAU,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,MAAM,CAAC,CAAC;QAC7D,OAAO,8BAAC,UAAU,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IACnE,CAAC;;AAGU,QAAA,MAAM,GAAG,IAAA,qBAAS,EAAC,UAAU,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Button.tsx b/source/renderer/react-polymorph/components/Button.tsx new file mode 100644 index 0000000000..4a522d4be0 --- /dev/null +++ b/source/renderer/react-polymorph/components/Button.tsx @@ -0,0 +1,65 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + className?: string; + context: ThemeContextProp; + disabled?: boolean; + label?: string | Element; + loading: boolean; + onClick?: (...args: Array) => any; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; // custom css/scss from user that adheres to component's theme API +}; +type State = { + composedTheme: Record; +}; + +class ButtonBase extends Component { + // define static properties + static displayName = 'Button'; + static defaultProps = { + context: createEmptyContext(), + loading: false, + theme: null, + themeId: IDENTIFIERS.BUTTON, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const ButtonSkin = skin || context.skins[IDENTIFIERS.BUTTON]; + return ; + } +} + +export const Button = withTheme(ButtonBase); diff --git a/source/renderer/react-polymorph/components/Checkbox.js.map b/source/renderer/react-polymorph/components/Checkbox.js.map new file mode 100644 index 0000000000..f84362b93a --- /dev/null +++ b/source/renderer/react-polymorph/components/Checkbox.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["Checkbox.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAwBhC,MAAM,YAAa,SAAQ,iBAAuB;IAChD,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,QAAQ;QAC7B,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,YAAY,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/D,OAAO,8BAAC,YAAY,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IACrE,CAAC;;AAGU,QAAA,QAAQ,GAAG,IAAA,qBAAS,EAAC,YAAY,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Checkbox.tsx b/source/renderer/react-polymorph/components/Checkbox.tsx new file mode 100644 index 0000000000..1758e73af3 --- /dev/null +++ b/source/renderer/react-polymorph/components/Checkbox.tsx @@ -0,0 +1,69 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + checked?: boolean; + className?: string; + context?: ThemeContextProp; + disabled?: boolean; + label?: string | Element; + labelLeft?: string | Element; + labelRight?: string | Element; + onChange?: (...args: Array) => any; + onBlur?: (...args: Array) => any; + onFocus?: (...args: Array) => any; + skin?: ComponentType; + theme?: Record | null | undefined; + // will take precedence over theme in context if passed + themeId?: string; + themeOverrides?: Record; +}; +type State = { + composedTheme: Record; +}; + +class CheckboxBase extends Component { + // define static properties + static displayName = 'Checkbox'; + static defaultProps = { + checked: false, + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.CHECKBOX, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const CheckboxSkin = skin || context.skins[this.props.themeId]; + return ; + } +} + +export const Checkbox = withTheme(CheckboxBase); diff --git a/source/renderer/react-polymorph/components/Dropdown.js.map b/source/renderer/react-polymorph/components/Dropdown.js.map new file mode 100644 index 0000000000..795eaf5eef --- /dev/null +++ b/source/renderer/react-polymorph/components/Dropdown.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Dropdown.js","sourceRoot":"","sources":["Dropdown.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Dropdown.tsx b/source/renderer/react-polymorph/components/Dropdown.tsx new file mode 100644 index 0000000000..2c93e67541 --- /dev/null +++ b/source/renderer/react-polymorph/components/Dropdown.tsx @@ -0,0 +1,177 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; +import { GlobalListeners } from './HOC/GlobalListeners'; + +type Props = { + activeItem: any; + className?: string; + clickToOpen?: boolean; + context: ThemeContextProp; + isOpen?: boolean; + isOpeningUpward: boolean; + items: Array; + label: string | Element; + noArrow?: boolean; + onItemSelected?: (...args: Array) => any; + optionRenderer?: (...args: Array) => any; + skin?: ComponentType; + theme: Record | null | undefined; + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; + isMouseOverItems: boolean; + isMouseOverRoot: boolean; + isOpen: boolean; +}; + +class DropdownBase extends Component { + // declare ref types + rootElement: Element | null | undefined; + optionsElement: Element | null | undefined; + // define static properties + static displayName = 'Dropdown'; + static defaultProps = { + context: createEmptyContext(), + clickToOpen: false, + isOpeningUpward: false, + noArrow: false, + theme: null, + themeOverrides: {}, + themeId: IDENTIFIERS.DROPDOWN, + }; + + constructor(props: Props) { + super(props); + // define ref + this.rootElement = React.createRef(); + this.optionsElement = React.createRef(); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + isMouseOverItems: false, + isMouseOverRoot: false, + isOpen: false, + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + componentWillUnmount() { + this.close(); + } + + // ========= PUBLIC SKIN API ========= + isOpen = () => { + const { clickToOpen, isOpen } = this.props; + const { isMouseOverItems, isMouseOverRoot } = this.state; + const isOpenBecauseOfHover = clickToOpen + ? false + : isMouseOverItems || isMouseOverRoot; + return isOpen || this.state.isOpen || isOpenBecauseOfHover; + }; + toggleOpen = () => { + if (this.isOpen()) { + this.close(); + } else { + this.setState({ + isOpen: true, + }); + } + }; + close = () => { + this._setMouseOverRoot(false); + + this._setMouseOverItems(false); + + this.setState({ + isOpen: false, + }); + }; + // ========= PRIVATE SKIN API ========= + _setMouseOverItems = (isMouseOverItems: boolean) => { + this.setState({ + isMouseOverItems, + }); + }; + _setMouseOverRoot = (isMouseOverRoot: boolean) => { + this.setState({ + isMouseOverRoot, + }); + }; + _onItemSelected = (item) => { + const { onItemSelected } = this.props; + this.close(); + + if (onItemSelected) { + onItemSelected(item); + } + }; + _onLabelClick = () => { + if (this.props.clickToOpen) { + this.toggleOpen(); + } + }; + + render() { + const { + clickToOpen, + context, + isOpen, + isOpeningUpward, + onItemSelected, + skin, + theme, + themeOverrides, + ...rest + } = this.props; + const DropdownSkin = skin || context.skins[IDENTIFIERS.DROPDOWN]; + const { isMouseOverItems, isMouseOverRoot } = this.state; + return ( + + {({ optionsMaxHeight, optionHeight }) => ( + + )} + + ); + } +} + +export const Dropdown = withTheme(DropdownBase); diff --git a/source/renderer/react-polymorph/components/FormField.js.map b/source/renderer/react-polymorph/components/FormField.js.map new file mode 100644 index 0000000000..a6617f4f6a --- /dev/null +++ b/source/renderer/react-polymorph/components/FormField.js.map @@ -0,0 +1 @@ +{"version":3,"file":"FormField.js","sourceRoot":"","sources":["FormField.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AA6BhC,MAAM,aAAc,SAAQ,iBAAgC;IAC1D,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,qBAAqB,EAAE,IAAI;QAC3B,qBAAqB,EAAE,IAAI;QAC3B,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,UAAU;QAC/B,cAAc,EAAE,EAAE;KACnB,CAAC;IACF,YAAY,CAAkB;IAE9B,YAAY,KAAqB;QAC/B,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,IAAI,eAAK,CAAC,SAAS,EAAE,CAAC;QAC5D,IAAI,CAAC,KAAK,GAAG;YACX,KAAK,EAAE,EAAE;YACT,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAyB;QAC1C,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,QAAQ,GAAG,CAAC,KAAa,EAAE,EAAE,CAC3B,IAAI,CAAC,QAAQ,CAAC;QACZ,KAAK;KACN,CAAC,CAAC;IACL,UAAU,GAAG,GAAG,EAAE;QAChB,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;QAE9B,IAAI,YAAY,IAAI,YAAY,CAAC,OAAO,EAAE;YACxC,IAAI,OAAO,YAAY,CAAC,OAAO,CAAC,KAAK,KAAK,UAAU,EAAE;gBACpD,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;aAC9B;SACF;IACH,CAAC,CAAC;IAEF,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5E,MAAM,aAAa,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,UAAU,CAAC,CAAC;QACpE,OAAO,CACL,8BAAC,aAAa,IACZ,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,EAC/B,YAAY,EAAE,IAAI,CAAC,YAAY,EAC/B,UAAU,EAAE,IAAI,CAAC,UAAU,KACvB,IAAI,GACR,CACH,CAAC;IACJ,CAAC;;AAGU,QAAA,SAAS,GAAG,IAAA,qBAAS,EAAC,aAAa,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/FormField.tsx b/source/renderer/react-polymorph/components/FormField.tsx new file mode 100644 index 0000000000..aa6c38b4fc --- /dev/null +++ b/source/renderer/react-polymorph/components/FormField.tsx @@ -0,0 +1,101 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ElementRef, ComponentType, Element } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +export type FormFieldProps = { + className?: string | null | undefined; + context: ThemeContextProp; + disabled?: boolean; + error?: string | Element; + formFieldRef: ElementRef; + id?: string; + isErrorHidden?: boolean; + isErrorShown?: boolean; + isShowingErrorOnFocus: boolean; + isShowingErrorOnHover: boolean; + label?: string | Element; + onChange: (...args: Array) => any; + render: (setFormFieldRef: (arg0: ElementRef) => void) => React.ReactNode; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; + themeVariables?: Record; +}; +type State = { + error: string; + composedTheme: Record; +}; + +class FormFieldBase extends Component { + // define static properties + static displayName = 'FormField'; + static defaultProps = { + context: createEmptyContext(), + isShowingErrorOnFocus: true, + isShowingErrorOnHover: true, + theme: null, + themeId: IDENTIFIERS.FORM_FIELD, + themeOverrides: {}, + }; + formFieldRef: ElementRef; + + constructor(props: FormFieldProps) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.formFieldRef = props.formFieldRef ?? React.createRef(); + this.state = { + error: '', + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: FormFieldProps) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + setError = (error: string) => + this.setState({ + error, + }); + focusChild = () => { + const { formFieldRef } = this; + + if (formFieldRef && formFieldRef.current) { + if (typeof formFieldRef.current.focus === 'function') { + formFieldRef.current.focus(); + } + } + }; + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, error, context, ...rest } = this.props; + const FormFieldSkin = skin || context.skins[IDENTIFIERS.FORM_FIELD]; + return ( + + ); + } +} + +export const FormField = withTheme(FormFieldBase); diff --git a/source/renderer/react-polymorph/components/HOC/GlobalListeners.js.map b/source/renderer/react-polymorph/components/HOC/GlobalListeners.js.map new file mode 100644 index 0000000000..7f02cf5838 --- /dev/null +++ b/source/renderer/react-polymorph/components/HOC/GlobalListeners.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GlobalListeners.js","sourceRoot":"","sources":["GlobalListeners.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/HOC/GlobalListeners.tsx b/source/renderer/react-polymorph/components/HOC/GlobalListeners.tsx new file mode 100644 index 0000000000..9781c1b637 --- /dev/null +++ b/source/renderer/react-polymorph/components/HOC/GlobalListeners.tsx @@ -0,0 +1,210 @@ +// @ts-nocheck +import React, { Component, Fragment } from 'react'; +// @ts-expect-error +import type { ElementRef } from 'react'; +import { debounce } from 'lodash'; +import { + addDocumentListeners, + addWindowListeners, + removeDocumentListeners, + removeWindowListeners, + targetIsDescendant, +} from '../../utils/events'; + +type Props = { + children: (...args: Array) => any; + mouseIsOverOptions: boolean; + mouseIsOverRoot?: boolean; + optionsIsOpen: boolean; + optionsIsOpeningUpward: boolean; + optionsRef?: ElementRef; + optionRenderer?: (...args: Array) => any; + optionsLength?: number; + rootRef?: ElementRef; + toggleOpen: (...args: Array) => any; + hasSearch?: boolean; +}; +type State = { + optionsMaxHeight: number; +}; +export class GlobalListeners extends Component { + // define static properties + static displayName = 'GlobalListeners'; + static defaultProps = { + optionsIsOpen: false, + }; + + constructor(props: Props) { + super(props); + this.state = { + optionsMaxHeight: 300, + }; + } + + componentDidMount() { + if (this.props.optionsIsOpen) { + return; + } + + // adds scroll and resize event listeners for calculating Options max-height + this._addCalculateMaxHeightListeners(); + + // runs initial Options max-height calculation + this._calculateOptionsMaxHeight(); + } + + componentDidUpdate(prevProps: Props) { + const { optionsIsOpen, hasSearch, optionsLength } = this.props; + + // if Options is transferring from closed to open, add listeners + // if Options is transferring from open to closed, remove listeners + if (!prevProps.optionsIsOpen && optionsIsOpen) { + // first remove max-height calc handler on scroll and resize + // then add toggle handler on scroll and resize + this._removeGlobalListeners(); + + addWindowListeners(this._getWindowListeners()); + addDocumentListeners(this._getDocumentListeners()); + } else if (prevProps.optionsIsOpen && !optionsIsOpen) { + // remove toggle handler on scroll and resize + // then add calc max-height calc handler on scroll and resize + this._removeGlobalListeners(); + + this._addCalculateMaxHeightListeners(); + } + + // if hasSearch prop or options.length changed, re-calculates max-height + if ( + prevProps.hasSearch !== hasSearch || + prevProps.optionsLength !== optionsLength + ) { + this._calculateOptionsMaxHeight(); + } + } + + // before unmounting, remove all global listeners + componentWillUnmount() { + this._removeGlobalListeners(); + } + + // removes all event listeners from document and window + _removeGlobalListeners() { + removeDocumentListeners(this._getDocumentListeners()); + removeWindowListeners(this._getWindowListeners()); + } + + // removes all global listeners, then closes Options + _removeListenersAndToggle = () => { + const { optionsIsOpen, optionsRef } = this.props; + + this._removeGlobalListeners(); + + // before toggle, ensure options is open and optionsRef exists on DOM + if (!optionsIsOpen || !optionsRef || !optionsRef.current) { + return; + } + + this.props.toggleOpen(); + }; + _getDocumentListeners = () => ({ + click: this._handleDocumentClick, + scroll: this._handleDocumentScroll, + }); + _getWindowListeners = () => ({ + resize: this._handleWindowResize, + }); + _handleDocumentClick = (event: React.MouseEvent) => { + const { optionsIsOpen, rootRef } = this.props; + + // ensure Options is open + if (!optionsIsOpen || !rootRef || !rootRef.current) { + return; + } + + // return early if the user clicked an element within the parent component + // for example, the parent component could be Autocomplete or Select + if (targetIsDescendant(event, rootRef.current)) { + return; + } + + // otherwise, remove all listeners and close Options + this._removeListenersAndToggle(); + }; + _handleWindowResize = () => this._removeListenersAndToggle(); + _handleDocumentScroll = () => this._removeListenersAndToggle(); + _addCalculateMaxHeightListeners = () => { + const scrollListener = [ + 'scroll', + debounce(this._calculateOptionsMaxHeight, 300, { + leading: true, + }), + true, + ]; + const resizeListener = [ + 'resize', + debounce(this._calculateOptionsMaxHeight, 300), + ]; + document.addEventListener(...scrollListener); + window.addEventListener(...resizeListener); + }; + // calculates max-height for Options, max-height shouldn't be greater than distance + // from Options rootRef to edge of window (up or down) else Options run off page + _calculateOptionsMaxHeight = () => { + const { documentElement } = document; + const { + rootRef, + optionsIsOpeningUpward, + optionsIsOpen, + optionsRef, + toggleOpen, + mouseIsOverOptions, + mouseIsOverRoot, + hasSearch, + } = this.props; + // checks if Options are open & being scrolled upon via mouse position prior to toggling closed + const isOptionsInDOM = optionsRef && optionsRef.current; + const doDocumentStylesExist = documentElement && documentElement.style; + + if ( + !rootRef || + !rootRef.current || + !doDocumentStylesExist || + !isOptionsInDOM + ) { + return; + } + + optionsIsOpen && !mouseIsOverOptions && !mouseIsOverRoot && toggleOpen(); + const { height, top } = rootRef.current.getBoundingClientRect(); + const searchHeight = hasSearch ? 52 : 0; + + // opening upwards case + if (optionsIsOpeningUpward && top < window.innerHeight) { + this.setState({ + optionsMaxHeight: top - 20, + }); + return; + } + + // opening downwards case + const optionsMaxHeight = + window.innerHeight - top - height - 30 - searchHeight; + + if (!optionsIsOpeningUpward && optionsMaxHeight > 0) { + this.setState({ + optionsMaxHeight, + }); + } + }; + + render() { + const { optionsMaxHeight } = this.state; + return ( + + {this.props.children({ + optionsMaxHeight, + })} + + ); + } +} diff --git a/source/renderer/react-polymorph/components/HOC/ThemeContext.js.map b/source/renderer/react-polymorph/components/HOC/ThemeContext.js.map new file mode 100644 index 0000000000..75ee312af3 --- /dev/null +++ b/source/renderer/react-polymorph/components/HOC/ThemeContext.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ThemeContext.js","sourceRoot":"","sources":["ThemeContext.ts"],"names":[],"mappings":";;;;;;AAGA,gFAAsD;AACtD,0CAAkD;AAWlD,MAAM,cAAc,GAAG;IACrB,KAAK,EAAE,EAAE;IACT,KAAK,EAAE,oBAAc;IACrB,cAAc,EAAd,oBAAc;CACf,CAAC;AACW,QAAA,YAAY,GAAmB,IAAA,8BAAkB,EAAC,cAAc,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/HOC/ThemeContext.ts b/source/renderer/react-polymorph/components/HOC/ThemeContext.ts new file mode 100644 index 0000000000..29dca75c2e --- /dev/null +++ b/source/renderer/react-polymorph/components/HOC/ThemeContext.ts @@ -0,0 +1,21 @@ +// @ts-nocheck +import React from 'react'; +import type { Context } from 'create-react-context'; +import createReactContext from 'create-react-context'; +import { ROOT_THEME_API } from '../../themes/API'; +// components that are NOT directly nested within a ThemeProvider +// can access simple theme as "this.props.context.theme", +// same goes for "this.props.context.ROOT_THEME_API" +// if the user passes ThemeProvider a theme and/or ROOT_THEME_API, +// these default values are overwritten +type Theme = { + skins: Record; + theme: Record; + ROOT_THEME_API: Record; +}; +const defaultContext = { + skins: {}, + theme: ROOT_THEME_API, + ROOT_THEME_API, +}; +export const ThemeContext: Context = createReactContext(defaultContext); diff --git a/source/renderer/react-polymorph/components/HOC/withTheme.js.map b/source/renderer/react-polymorph/components/HOC/withTheme.js.map new file mode 100644 index 0000000000..8555ac5419 --- /dev/null +++ b/source/renderer/react-polymorph/components/HOC/withTheme.js.map @@ -0,0 +1 @@ +{"version":3,"file":"withTheme.js","sourceRoot":"","sources":["withTheme.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,iDAA8C;AAC9C,6CAAmD;AAOnD,SAAgB,kBAAkB;IAChC,OAAO;QACL,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,EAAE;QACT,cAAc,EAAE,EAAE;KACnB,CAAC;AACJ,CAAC;AAND,gDAMC;AACD,2DAA2D;AAC3D,mEAAmE;AACnE,yEAAyE;AACzE,SAAgB,SAAS,CAA+B,SAAY;IAClE,IAAI,gBAAgB,CAAC;IAErB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE;QACnC,kCAAkC;QAClC,gBAAgB,GAAG,UAAU,KAAS;YACpC,OAAO,CACL,8BAAC,2BAAY,CAAC,QAAQ,QACnB,CAAC,OAAO,EAAE,EAAE,CAAC,8BAAC,SAAS,IAAC,OAAO,EAAE,OAAO,KAAM,KAAK,GAAI,CAClC,CACzB,CAAC;QACJ,CAAC,CAAC;KACH;SAAM;QACL,4CAA4C;QAC5C,gBAAgB,GAAG,eAAK,CAAC,UAAU,CAAC,CAAC,KAAS,EAAE,GAAa,EAAE,EAAE,CAAC,CAChE,8BAAC,2BAAY,CAAC,QAAQ,QACnB,CAAC,OAAO,EAAE,EAAE,CAAC,8BAAC,SAAS,IAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAM,KAAK,GAAI,CAC5C,CACzB,CAAC,CAAC;KACJ;IAED,qDAAqD;IACrD,gBAAgB,CAAC,WAAW,GAAG,aAAa,IAAA,sBAAc,EAAC,SAAS,CAAC,GAAG,CAAC;IACzE,4CAA4C;IAC5C,OAAQ,gBAA6B,CAAC;AACxC,CAAC;AAzBD,8BAyBC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/HOC/withTheme.tsx b/source/renderer/react-polymorph/components/HOC/withTheme.tsx new file mode 100644 index 0000000000..a640b470ee --- /dev/null +++ b/source/renderer/react-polymorph/components/HOC/withTheme.tsx @@ -0,0 +1,47 @@ +// @ts-nocheck +import React from 'react'; +import type { ComponentType, Ref } from 'react'; +import { ThemeContext } from './ThemeContext'; +import { getDisplayName } from '../../utils/props'; + +export type ThemeContextProp = { + skins: Record; + theme: Record; + ROOT_THEME_API: Record; +}; +export function createEmptyContext(): ThemeContextProp { + return { + skins: {}, + theme: {}, + ROOT_THEME_API: {}, + }; +} +// withTheme is a HOC that takes a Component as a parameter +// and returns that Component wrapped within ThemeContext.Consumer. +// Any additional props and refs are forwarded to the returned Component. +export function withTheme>(Component: C): C { + let WrappedComponent; + + if (process.env.NODE_ENV === 'test') { + // wraps component in context only + WrappedComponent = function (props: {}) { + return ( + + {(context) => } + + ); + }; + } else { + // wraps component in context AND forwardRef + WrappedComponent = React.forwardRef((props: {}, ref: Ref) => ( + + {(context) => } + + )); + } + + // create a new displayName for the wrapped component + WrappedComponent.displayName = `withTheme(${getDisplayName(Component)})`; + // Cast type to our desired custom component + return (WrappedComponent as any) as C; +} diff --git a/source/renderer/react-polymorph/components/Header.js.map b/source/renderer/react-polymorph/components/Header.js.map new file mode 100644 index 0000000000..9ba9545722 --- /dev/null +++ b/source/renderer/react-polymorph/components/Header.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Header.js","sourceRoot":"","sources":["Header.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Header.tsx b/source/renderer/react-polymorph/components/Header.tsx new file mode 100644 index 0000000000..11b43efd6e --- /dev/null +++ b/source/renderer/react-polymorph/components/Header.tsx @@ -0,0 +1,170 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Node } from 'react'; +import { pickBy, isEmpty } from 'lodash'; +// utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + bold?: boolean; + center?: boolean; + children?: Node; + className?: string; + context: ThemeContextProp; + h1?: boolean; + h2?: boolean; + h3?: boolean; + h4?: boolean; + light?: boolean; + lowerCase?: boolean; + medium?: boolean; + regular?: boolean; + right?: boolean; + left?: boolean; + skin?: ComponentType; + theme: Record | null | undefined; + themeId: string; + themeOverrides: Record; + thin?: boolean; + upperCase?: boolean; +}; +type State = { + composedTheme: Record; +}; + +class HeaderBase extends Component { + // define static properties + static displayName = 'Header'; + static defaultProps = { + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.HEADER, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + _assembleInlineStyles = ({ center, lowerCase, left, right, upperCase }) => { + const inlineStyles = {}; + const textAlign = pickBy({ + center, + left, + right, + }); + const textTransform = pickBy({ + lowerCase, + upperCase, + }); + + if (!isEmpty(textAlign)) { + inlineStyles.textAlign = Object.keys(textAlign)[0]; + } + + if (!isEmpty(textTransform)) { + inlineStyles.textTransform = Object.keys(textTransform)[0]; + } + + return inlineStyles; + }; + _assembleHeaderTheme = (styleProps: Record) => { + const activeClasses = this._getActiveClasses(styleProps); + + const theme = this.state.composedTheme[this.props.themeId]; + return activeClasses.reduce((reducedTheme, activeClass) => { + if (activeClass && Object.hasOwnProperty.call(theme, activeClass)) { + reducedTheme[activeClass] = theme[activeClass]; + } + + return reducedTheme; + }, {}); + }; + _getActiveFont = ({ light, medium, regular, thin, bold }) => { + const fontProps = pickBy({ + light, + medium, + regular, + thin, + bold, + }); + + if (isEmpty(fontProps)) { + return; + } + + // returns the first active font if more than 1 is passed + return Object.keys(fontProps)[0]; + }; + _getActiveTheme = ({ h1, h2, h3, h4 }) => { + const themeProps = pickBy({ + h1, + h2, + h3, + h4, + }); + + if (isEmpty(themeProps)) { + return; + } + + // returns the first active theme if more than 1 is passed + return Object.keys(themeProps)[0]; + }; + _getActiveClasses = (styleProps: Record) => { + const activeClasses = ['header']; + + const activeTheme = this._getActiveTheme(styleProps); + + const activeFont = this._getActiveFont(styleProps); + + if (activeTheme) { + return [...activeClasses, activeTheme]; + } + + if (activeFont) { + return [...activeClasses, activeFont]; + } + + return [...activeClasses, activeTheme, activeFont].filter((val) => val); + }; + + render() { + const { children, className, skin, context, ...styleProps } = this.props; + const HeaderSkin = skin || context.skins[IDENTIFIERS.HEADER]; + + const reducedTheme = this._assembleHeaderTheme(styleProps); + + const inlineStyles = this._assembleInlineStyles(styleProps); + + return ( + + {children} + + ); + } +} + +export const Header = withTheme(HeaderBase); diff --git a/source/renderer/react-polymorph/components/InfiniteScroll.js.map b/source/renderer/react-polymorph/components/InfiniteScroll.js.map new file mode 100644 index 0000000000..b412d58545 --- /dev/null +++ b/source/renderer/react-polymorph/components/InfiniteScroll.js.map @@ -0,0 +1 @@ +{"version":3,"file":"InfiniteScroll.js","sourceRoot":"","sources":["InfiniteScroll.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/InfiniteScroll.tsx b/source/renderer/react-polymorph/components/InfiniteScroll.tsx new file mode 100644 index 0000000000..44d3f51002 --- /dev/null +++ b/source/renderer/react-polymorph/components/InfiniteScroll.tsx @@ -0,0 +1,141 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Node } from 'react'; +// @ts-expect-error +// utilities +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// constants +import { IDENTIFIERS } from '.'; +import type { ReactElementRef } from '../utils/types'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + className?: string; + context: ThemeContextProp; + fetchData: (...args: Array) => any; + renderItems?: (...args: Array) => any; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; + threshold: number; +}; +type State = { + composedTheme: Record; + data: Record | Array<{}>; + error: boolean | string | Node; + hasMoreData: boolean; + isLoading: boolean; +}; + +class InfiniteScrollBase extends Component { + // declare ref types + scrollContainer: ReactElementRef; + // define static properties + static displayName = 'InfiniteScroll'; + static defaultProps = { + context: createEmptyContext(), + + fetchData() {}, + + theme: null, + themeId: IDENTIFIERS.INFINITE_SCROLL, + themeOverrides: {}, + threshold: 250, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + // refs + this.scrollContainer = React.createRef(); + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + data: [], + error: false, + isLoading: false, + hasMoreData: true, + }; + } + + componentDidMount() { + const { scrollContainer } = this; + + this._handleFetchData(); + + if (!scrollContainer.current) return; + scrollContainer.current.addEventListener('scroll', this._handleScroll); + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + // calls user's fetchData function from props + _handleFetchData = () => this.props.fetchData(this.setState.bind(this)); + // scroll event listener attached to scrollContainer element + _handleScroll = () => { + const { error, isLoading, hasMoreData } = this.state; + + // return early for error, loading, or lack of future data + if (error || isLoading || !hasMoreData) { + return; + } + + return this._checkForScrollBottom(); + }; + // prevents new data fetch until user has scrolled near bottom of existing data + _checkForScrollBottom = () => { + const { + scrollContainer, + props: { threshold }, + } = this; + if (!scrollContainer.current) return; + const { offsetHeight, scrollTop, scrollHeight } = scrollContainer.current; + + if (offsetHeight + scrollTop >= scrollHeight - threshold) { + return this._handleFetchData(); + } + }; + _isFunction = ( + renderProp: ((...args: Array) => any) | null | undefined + ) => renderProp && typeof renderProp === 'function'; + + render() { + const { + props: { className, context, renderItems, skin, themeId }, + state: { composedTheme, data, error, hasMoreData, isLoading }, + scrollContainer, + } = this; + + if (!this._isFunction(renderItems)) { + return null; + } + + const InfiniteScrollSkin = + skin || context.skins[IDENTIFIERS.INFINITE_SCROLL]; + return ( + + ); + } +} + +export const InfiniteScroll = withTheme(InfiniteScrollBase); diff --git a/source/renderer/react-polymorph/components/Input.js.map b/source/renderer/react-polymorph/components/Input.js.map new file mode 100644 index 0000000000..6b9a072cea --- /dev/null +++ b/source/renderer/react-polymorph/components/Input.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Input.js","sourceRoot":"","sources":["Input.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Input.tsx b/source/renderer/react-polymorph/components/Input.tsx new file mode 100644 index 0000000000..bfa14924dd --- /dev/null +++ b/source/renderer/react-polymorph/components/Input.tsx @@ -0,0 +1,173 @@ +// @ts-nocheck +import React, { Component, RefObject } from 'react'; +// @ts-expect-error +import type { ComponentType, Element } from 'react'; +// external libraries +import { isString, flow } from 'lodash'; +// utilities +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +export type InputProps = { + autoFocus: boolean; + className?: string | null | undefined; + context: ThemeContextProp; + disabled?: boolean; + error?: string | Element; + inputRef?: RefObject; + showErrorState?: boolean; + hideErrorState?: boolean; + id?: string; + isShowingErrorOnFocus: boolean; + isShowingErrorOnHover: boolean; + label?: string | Element; + maxLength?: number; + minLength?: number; + onChange?: (...args: Array) => any; + placeholder?: string; + readOnly: boolean; + setError?: (...args: Array) => any; + selectedOption?: any; + selectionRenderer?: (...args: Array) => any; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; + themeVariables?: Record; + value: string; +}; +type State = { + error: string; + composedTheme: Record; +}; + +class InputBase extends Component { + inputElement: RefObject; + static displayName = 'Input'; + static defaultProps = { + autoFocus: false, + context: createEmptyContext(), + error: '', + isShowingErrorOnFocus: true, + isShowingErrorOnHover: true, + readOnly: false, + theme: null, + themeId: IDENTIFIERS.INPUT, + themeOverrides: {}, + value: '', + }; + + constructor(props: InputProps) { + super(props); + this.inputElement = props.inputRef ?? React.createRef(); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + error: '', + }; + } + + componentDidMount() { + if (this.props.autoFocus) this.focus(); + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + onChange = (event: React.SyntheticEvent>) => { + const { onChange, disabled, readOnly } = this.props; + if (disabled || readOnly) return; + if (onChange) onChange(this._processValue(event.target.value), event); + }; + focus = () => { + const { inputElement } = this; + if (!inputElement.current) return; + inputElement.current.focus(); + }; + _setError = (error: string) => { + const { setError } = this.props; + // checks for setError func from FormField component + // if this Input instance is being used within the render function + // of a FormField instance, the error field within FormField's local state + // will be set + if (setError) setError(error); + this.setState({ + error, + }); + }; + + _processValue(value: string) { + return flow([ + this._enforceStringValue, + this._enforceMaxLength, + this._enforceMinLength, + ]).call(this, value); + } + + _enforceStringValue(value) { + if (!isString(value)) { + throw new Error('Values passed to Input::onChange must be strings'); + } + + return value; + } + + _enforceMaxLength(value: string) { + const { maxLength } = this.props; + const isTooLong = maxLength != null && value.length > maxLength; + return isTooLong ? value.substring(0, maxLength) : value; + } + + _enforceMinLength(value: string) { + const { minLength } = this.props; + const isTooShort = minLength != null && value.length < minLength; + + if (isTooShort) { + this._setError('Please enter a valid input'); + } else if (this.state.error !== '') { + this._setError(''); + } + + return value; + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { + skin, + context, + theme, + themeOverrides, + onChange, + error, + maxLength, + minLength, + setError, + autoFocus, + ...rest + } = this.props; + const InputSkin = skin || context.skins[IDENTIFIERS.INPUT]; + return ( + + ); + } +} + +export const Input = withTheme(InputBase); diff --git a/source/renderer/react-polymorph/components/Link.js.map b/source/renderer/react-polymorph/components/Link.js.map new file mode 100644 index 0000000000..abd732203d --- /dev/null +++ b/source/renderer/react-polymorph/components/Link.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Link.js","sourceRoot":"","sources":["Link.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAsBhC,MAAM,QAAS,SAAQ,iBAAuB;IAC5C,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC;IAC5B,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,IAAI;QACzB,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,KAAK;QACpB,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,IAAI;QAClB,gBAAgB,EAAE,KAAK;KACxB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,QAAQ,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3D,OAAO,8BAAC,QAAQ,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IACjE,CAAC;;AAGU,QAAA,IAAI,GAAG,IAAA,qBAAS,EAAC,QAAQ,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Link.tsx b/source/renderer/react-polymorph/components/Link.tsx new file mode 100644 index 0000000000..72d060bd69 --- /dev/null +++ b/source/renderer/react-polymorph/components/Link.tsx @@ -0,0 +1,70 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + label: string; + hasIconBefore?: boolean; + hasIconAfter?: boolean; + isUnderlined?: boolean; + underlineOnHover?: boolean; + onClick?: (...args: Array) => any; + className?: string; + context: ThemeContextProp; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; +}; + +class LinkBase extends Component { + // define static properties + static displayName = 'Link'; + static defaultProps = { + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.LINK, + themeOverrides: {}, + hasIconBefore: false, + hasIconAfter: true, + isUnderlined: true, + underlineOnHover: false, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const LinkSkin = skin || context.skins[this.props.themeId]; + return ; + } +} + +export const Link = withTheme(LinkBase); diff --git a/source/renderer/react-polymorph/components/LoadingSpinner.js.map b/source/renderer/react-polymorph/components/LoadingSpinner.js.map new file mode 100644 index 0000000000..02526ae904 --- /dev/null +++ b/source/renderer/react-polymorph/components/LoadingSpinner.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LoadingSpinner.js","sourceRoot":"","sources":["LoadingSpinner.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAkBhC,MAAM,kBAAmB,SAAQ,iBAAuB;IACtD,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,gBAAgB,CAAC;IACtC,MAAM,CAAC,YAAY,GAAG;QACpB,GAAG,EAAE,KAAK;QACV,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,eAAe;QACpC,cAAc,EAAE,EAAE;QAClB,OAAO,EAAE,IAAI;KACd,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,kBAAkB,GACtB,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,eAAe,CAAC,CAAC;QACrD,OAAO,8BAAC,kBAAkB,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IAC3E,CAAC;;AAGU,QAAA,cAAc,GAAG,IAAA,qBAAS,EAAC,kBAAkB,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/LoadingSpinner.tsx b/source/renderer/react-polymorph/components/LoadingSpinner.tsx new file mode 100644 index 0000000000..9ca3a69a35 --- /dev/null +++ b/source/renderer/react-polymorph/components/LoadingSpinner.tsx @@ -0,0 +1,65 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + big: boolean; + className?: string; + context: ThemeContextProp; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; + visible: boolean; +}; +type State = { + composedTheme: Record; +}; + +class LoadingSpinnerBase extends Component { + // define static properties + static displayName = 'LoadingSpinner'; + static defaultProps = { + big: false, + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.LOADING_SPINNER, + themeOverrides: {}, + visible: true, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const LoadingSpinnerSkin = + skin || context.skins[IDENTIFIERS.LOADING_SPINNER]; + return ; + } +} + +export const LoadingSpinner = withTheme(LoadingSpinnerBase); diff --git a/source/renderer/react-polymorph/components/Modal.js.map b/source/renderer/react-polymorph/components/Modal.js.map new file mode 100644 index 0000000000..5d9225caa1 --- /dev/null +++ b/source/renderer/react-polymorph/components/Modal.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Modal.js","sourceRoot":"","sources":["Modal.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAmBhC,MAAM,SAAU,SAAQ,iBAAuB;IAC7C,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC;IAC7B,MAAM,CAAC,YAAY,GAAG;QACpB,YAAY,EAAE,cAAc;QAC5B,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,MAAM,EAAE,KAAK;QACb,0BAA0B,EAAE,IAAI;QAChC,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,KAAK;QAC1B,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,SAAS,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,KAAK,CAAC,CAAC;QAC3D,OAAO,8BAAC,SAAS,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IAClE,CAAC;;AAGU,QAAA,KAAK,GAAG,IAAA,qBAAS,EAAC,SAAS,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Modal.tsx b/source/renderer/react-polymorph/components/Modal.tsx new file mode 100644 index 0000000000..329623eed8 --- /dev/null +++ b/source/renderer/react-polymorph/components/Modal.tsx @@ -0,0 +1,66 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + contentLabel: string | Element; + context: ThemeContextProp; + isOpen: boolean; + onClose?: (...args: Array) => any; + skin?: ComponentType; + triggerCloseOnOverlayClick: boolean; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; +}; + +class ModalBase extends Component { + // define static properties + static displayName = 'Modal'; + static defaultProps = { + contentLabel: 'Modal Dialog', + context: createEmptyContext(), + isOpen: false, + triggerCloseOnOverlayClick: true, + theme: null, + themeId: IDENTIFIERS.MODAL, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const ModalSkin = skin || context.skins[IDENTIFIERS.MODAL]; + return ; + } +} + +export const Modal = withTheme(ModalBase); diff --git a/source/renderer/react-polymorph/components/NumericInput.js.map b/source/renderer/react-polymorph/components/NumericInput.js.map new file mode 100644 index 0000000000..08d7c9270f --- /dev/null +++ b/source/renderer/react-polymorph/components/NumericInput.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NumericInput.js","sourceRoot":"","sources":["NumericInput.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/NumericInput.tsx b/source/renderer/react-polymorph/components/NumericInput.tsx new file mode 100644 index 0000000000..8fc6325e35 --- /dev/null +++ b/source/renderer/react-polymorph/components/NumericInput.tsx @@ -0,0 +1,456 @@ +// @ts-nocheck +import { escapeRegExp } from 'lodash/string'; +import React, { Component } from 'react'; +import BigNumber from 'bignumber.js'; +// @ts-expect-error +import type { Element, ElementRef } from 'react'; +// external libraries +// internal utility functions +import { withTheme } from './HOC/withTheme'; +// import constants +import { removeCharAtPosition } from '../utils/strings'; +import type { InputEvent } from '../utils/types'; +import { Input } from './Input'; +import type { InputProps } from './Input'; + +type NumericInputValue = null | number | string | BigNumber.Instance; +export type NumericInputProps = InputProps & { + allowSigns?: boolean; + allowOnlyIntegers?: boolean; + bigNumberFormat?: BigNumber.Format; + decimalPlaces?: number; + roundingMode?: BigNumber.RoundingMode; + value: NumericInputValue; +}; +type State = { + composedTheme: Record; + inputCaretPosition: number; + fallbackInputValue: string | null | undefined; +}; + +class NumericInputBase extends Component { + inputElement: { + current: null | ElementRef<'input'>; + }; + _hasInputBeenChanged: boolean = false; + static displayName = 'NumericInput'; + static defaultProps = { + allowSigns: true, + allowOnlyIntegers: false, + readOnly: false, + roundingMode: BigNumber.ROUND_FLOOR, + value: null, + }; + + constructor(props: NumericInputProps) { + super(props); + this.inputElement = React.createRef(); + this.state = { + inputCaretPosition: 0, + fallbackInputValue: null, + }; + } + + componentDidMount() { + const { inputElement } = this; + const { autoFocus } = this.props; + + if (autoFocus) { + this.focus(); + + if (inputElement && inputElement.current) { + this.setState({ + inputCaretPosition: inputElement.current.selectionStart, + }); + } + } + } + + componentDidUpdate(prevProps: NumericInputProps, prevState: State) { + const { value } = this.props; + const { inputCaretPosition } = this.state; + const hasValueBeenChanged = value !== prevProps.value; + const hasCaretBeenChanged = + inputCaretPosition !== prevState.inputCaretPosition; + + if ( + this._hasInputBeenChanged || + hasValueBeenChanged || + hasCaretBeenChanged + ) { + this.setInputCaretPosition(inputCaretPosition); + } + + this._hasInputBeenChanged = false; + } + + onChange = (newValue, event: React.SyntheticEvent>) => { + const { value, onChange } = this.props; + const result = this.processValueChange(event.nativeEvent); + + if (result) { + this._hasInputBeenChanged = true; + const hasValueChanged = value !== result.value; + + if (hasValueChanged && onChange) { + onChange(result.value, event); + } + + this.setState({ + inputCaretPosition: result.caretPosition, + fallbackInputValue: result.fallbackInputValue, + }); + } + }; + + /** + * 1. Handle edge cases that don't need further processing + * 2. Clean the given value + * 3. Final processing + */ + processValueChange( + event: InputEvent + ): + | { + value: NumericInputValue; + caretPosition: number; + fallbackInputValue?: string | null | undefined; + } + | null + | undefined { + const { allowSigns, allowOnlyIntegers, decimalPlaces, value } = this.props; + const { inputType, target } = event; + const { decimalSeparator, groupSeparator } = this.getBigNumberFormat(); + const changedCaretPosition = target.selectionStart; + const valueToProcess = target.value; + const { fallbackInputValue } = this.state; + const isBackwardDelete = inputType === 'deleteContentBackward'; + const isForwardDelete = inputType === 'deleteContentForward'; + const isDeletion = isForwardDelete || isBackwardDelete; + const isInsert = inputType === 'insertText'; + const deleteCaretCorrection = isBackwardDelete ? 0 : 1; + const validInputSignsRegExp = new RegExp( + `^([-])?([0-9${decimalSeparator}${groupSeparator}]+)?$` + ); + const validInputNoSignsRegExp = new RegExp( + `^([0-9${decimalSeparator}${groupSeparator}]+)?$` + ); + const validInputOnlyIntegersRegExp = new RegExp(`^([0-9]+)?$`); + let validInputRegex = allowSigns + ? validInputSignsRegExp + : validInputNoSignsRegExp; + validInputRegex = allowOnlyIntegers + ? validInputOnlyIntegersRegExp + : validInputRegex; + const valueHasLeadingZero = /^0[1-9]/.test(valueToProcess); + + /** + * ========= HANDLE HARD EDGE-CASES ============= + */ + // Case: invalid characters entered -> refuse! + if (!validInputRegex.test(valueToProcess)) { + return { + caretPosition: changedCaretPosition - 1, + fallbackInputValue, + value, + }; + } + + // Case: Everything was deleted -> reset state + if (valueToProcess === '') { + return { + value: null, + caretPosition: 0, + fallbackInputValue: null, + }; + } + + // Case: value is the same as the fallback (which is always shown if defined) + if (valueToProcess === this.state.fallbackInputValue) return null; + + // Case: Just minus sign was entered + if (valueToProcess === '-') { + return { + value: null, + caretPosition: 1, + fallbackInputValue: '-', + }; + } + + // Case: Just minus sign was entered + if (valueToProcess === groupSeparator) { + return { + value: null, + caretPosition: 0, + fallbackInputValue: null, + }; + } + + /** + * ========= CLEAN THE INPUT ============= + */ + const currentNumber = + value == null ? new BigNumber('0') : new BigNumber(value); + const currentValue = + fallbackInputValue ?? this.valueToFormattedString(currentNumber); + const currentNumberOfDecimalSeparators = this.getNumberOfDecimalSeparators( + currentValue + ); + const hadDecimalSeparatorBefore = currentNumberOfDecimalSeparators > 0; + // New Value + let newValue = valueToProcess; + let newCaretPosition = changedCaretPosition; + const newNumberOfDecimalSeparators = this.getNumberOfDecimalSeparators( + newValue + ); + + // Case: A second decimal separator was added somewhere + if (hadDecimalSeparatorBefore && newNumberOfDecimalSeparators === 2) { + const oldFirstIndex = currentValue.indexOf(decimalSeparator); + const newFirstIndex = newValue.indexOf(decimalSeparator); + const wasSeparatorAddedBeforeOldOne = newFirstIndex < oldFirstIndex; + // Remove the second decimal point and set caret position + newValue = removeCharAtPosition( + newValue, + wasSeparatorAddedBeforeOldOne + ? newValue.lastIndexOf(decimalSeparator) + : oldFirstIndex + ); + newCaretPosition = newValue.indexOf(decimalSeparator) + 1; + } + + // Case: Decimal separator was replaced with a number + const newValueHasTrailingZeros = new RegExp('^[1-9]+0+$'); + + if ( + !!decimalPlaces && + value != null && + hadDecimalSeparatorBefore && + newNumberOfDecimalSeparators === 0 && + isInsert && + newValue.length > 1 && + !newValueHasTrailingZeros.test(newValue) + ) { + return { + caretPosition: changedCaretPosition - 1, + fallbackInputValue, + value, + }; + } + + /** + * ========= PROCESS CLEANED INPUT ============= + */ + // Case: Just a decimal separator was entered + if (newValue === decimalSeparator) { + return { + value: '0', + caretPosition: 2, + fallbackInputValue: decimalPlaces > 0 ? null : `0${decimalSeparator}`, + }; + } + + // Case: Decimal separator was added at the beginning of number + if (newValue.charAt(0) === decimalSeparator) { + const newCaretPos = isInsert ? 2 : 1; + return { + value: this.bigNumberToFixed(new BigNumber(`0.${newValue.substr(1)}`)), + caretPosition: newCaretPos, + fallbackInputValue: null, + }; + } + + const newNumber = + newValue === '' ? null : this.formattedValueToBigNumber(newValue); + + // Case: Invalid change has been made -> ignore it + if (newNumber == null) { + const deleteAdjustment = isBackwardDelete ? 0 : 1; // special cases when deleting dot + + const insertAdjustment = -1; // don't move caret if numbers are "inserted" + + return { + caretPosition: + changedCaretPosition + + (isDeletion ? deleteAdjustment : insertAdjustment), + fallbackInputValue, + value: this.bigNumberToFixed(currentNumber), + }; + } + + const formattedNewNumber = this.valueToFormattedString(newNumber); + + // Case: Dot was added at the end of number + if ( + !isDeletion && + newValue.charAt(newValue.length - 1) === decimalSeparator + ) { + return { + value: this.bigNumberToFixed(newNumber), + caretPosition: changedCaretPosition, + fallbackInputValue: + decimalPlaces > 0 ? null : formattedNewNumber + decimalSeparator, + minimumFractionDigits: 0, + }; + } + + // Case: Decimal separator was deleted while number of decimal places specified + const hasDecimalPlaces = decimalPlaces != null; + const wasDecimalSeparatorRemoved = + hadDecimalSeparatorBefore && !newNumberOfDecimalSeparators; + const newValueSlicedAtNewInputtedNumber = newValue.slice( + 1, + newValue.length + ); + const newTrailingNumbersAreAllZero = /^0+$/.test( + newValueSlicedAtNewInputtedNumber + ); + + if (wasDecimalSeparatorRemoved && hasDecimalPlaces && !isInsert) { + return { + caretPosition: newCaretPosition + deleteCaretCorrection, + fallbackInputValue: null, + value: this.bigNumberToFixed(currentNumber), + }; + } + + // Edge case for inserts with trailing zeros + if ( + wasDecimalSeparatorRemoved && + hasDecimalPlaces && + isInsert && + !newTrailingNumbersAreAllZero && + newValue.length > 1 + ) { + return { + caretPosition: newCaretPosition + deleteCaretCorrection, + fallbackInputValue: null, + value: this.bigNumberToFixed(currentNumber), + }; + } + + // Case: Valid change has been made + const hasNumberChanged = !this.isSameValue(currentNumber, newNumber); + const groupSeparatorsDiff = + this.getNumberOfGroupSeparators(formattedNewNumber) - + this.getNumberOfGroupSeparators(newValue); + const hasNumberOfGroupSeparatorsChanged = groupSeparatorsDiff > 0; + const onlyNumberOfGroupSeparatorsChanged = + !hasNumberChanged && hasNumberOfGroupSeparatorsChanged; + const leadingZeroCorrection = valueHasLeadingZero ? -1 : 0; + const caretCorrection = + (onlyNumberOfGroupSeparatorsChanged + ? deleteCaretCorrection + : groupSeparatorsDiff) + leadingZeroCorrection; + return { + caretPosition: Math.max(newCaretPosition + caretCorrection, 0), + fallbackInputValue: null, + value: this.bigNumberToFixed(newNumber), + }; + } + + setInputCaretPosition = (position: number) => { + const { inputElement } = this; + if (!inputElement.current) return; + const input = inputElement.current; + input.selectionStart = position; + input.selectionEnd = position; + }; + focus = () => { + const { inputElement } = this; + if (!inputElement.current) return; + inputElement.current.focus(); + }; + onBlur = (event) => { + this.setState({ + fallbackInputValue: null, + }); + this.props.onBlur?.(event); + }; + + getBigNumberFormat(): BigNumber.Config { + return this.props.bigNumberFormat ?? BigNumber.config().FORMAT; + } + + valueToFormattedString(number: NumericInputValue) { + const { + bigNumberFormat, + decimalPlaces, + roundingMode, + allowOnlyIntegers, + } = this.props; + const debugSetting = BigNumber.DEBUG; + if (BigNumber.isBigNumber(number) && number.isNaN()) return ''; + + try { + BigNumber.DEBUG = true; + return allowOnlyIntegers + ? new BigNumber(number).toString() + : new BigNumber(number).toFormat(decimalPlaces, roundingMode, { + ...BigNumber.config().FORMAT, + // defaults + ...bigNumberFormat, // custom overrides; + }); + } catch (e) { + return ''; + } finally { + BigNumber.DEBUG = debugSetting; + } + } + + bigNumberToFixed(number: BigNumber.Instance) { + const { decimalPlaces, roundingMode } = this.props; + return number.toFixed(decimalPlaces, roundingMode); + } + + formattedValueToBigNumber(value: string) { + const { decimalSeparator, groupSeparator } = this.getBigNumberFormat(); + return new BigNumber( + value + .replace(escapedGlobalRegExp(groupSeparator), '') + .replace(escapedGlobalRegExp(decimalSeparator), '.') + ); + } + + getNumberOfGroupSeparators(value: string): number { + const { groupSeparator } = this.getBigNumberFormat(); + return (value.match(escapedGlobalRegExp(groupSeparator)) || []).length; + } + + getNumberOfDecimalSeparators(value: string): number { + const { decimalSeparator } = this.getBigNumberFormat(); + return (value.match(escapedGlobalRegExp(decimalSeparator)) || []).length; + } + + isSameValue( + first: BigNumber.Instance | null | undefined, + second: BigNumber.Instance | null | undefined + ) { + return BigNumber.isBigNumber(first) + ? first.isEqualTo(second) + : first === second; + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { onChange, value, ...rest } = this.props; + const inputValue = this.state.fallbackInputValue + ? this.state.fallbackInputValue + : this.valueToFormattedString(value); + return ( + + ); + } +} + +export const NumericInput = withTheme(NumericInputBase); + +function escapedGlobalRegExp(regex) { + return new RegExp(escapeRegExp(regex), 'g'); +} diff --git a/source/renderer/react-polymorph/components/Options.js.map b/source/renderer/react-polymorph/components/Options.js.map new file mode 100644 index 0000000000..d7af6ecf7c --- /dev/null +++ b/source/renderer/react-polymorph/components/Options.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Options.js","sourceRoot":"","sources":["Options.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAMzC,qBAAqB;AACrB,mCAAuD;AACvD,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,0CAAkD;AAClD,mBAAmB;AACnB,wBAAgC;AAgDhC,MAAM,WAAY,SAAQ,iBAAuB;IAC/C,oBAAoB;IACpB,cAAc,CAAkC,CAAC,4CAA4C;IAE7F,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,MAAM,EAAE,KAAK;QACb,eAAe,EAAE,KAAK;QACtB,cAAc,EAAE,KAAK;QACrB,kBAAkB,EAAE,KAAK;QACzB,gBAAgB,EAAE,YAAY;QAC9B,YAAY,EAAE,EAAE;QAChB,OAAO,EAAE,EAAE;QACX,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,EAAE;QAChB,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,OAAO;QAC5B,cAAc,EAAE,EAAE;QAElB,UAAU,KAAI,CAAC;KAChB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,cAAc,GAAG,eAAK,CAAC,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;YACD,sBAAsB,EAAE,CAAC;YACzB,kBAAkB,EAAE,KAAK;SAC1B,CAAC;IACJ,CAAC;IAED,iBAAiB;QACf,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YACrB,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;SAClE;IACH,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;gBAC1C,IAAI,CAAC,oBAAoB,EAAE,CAAC;aAC7B;iBAAM,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;gBACjD,IAAI,CAAC,qBAAqB,EAAE,CAAC;aAC9B;YAED,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,oBAAoB;QAClB,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACtE,CAAC;IAED,oBAAoB,GAAG,GAAG,EAAE;QAC1B,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;QACjE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC;QAE/C,IAAI,KAAK,EAAE;YACT,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7B,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;SAChC;IACH,CAAC,CAAC;IACF,qBAAqB,GAAG,GAAG,EAAE;QAC3B,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACtE,CAAC,CAAC;IACF,KAAK,GAAG,GAAG,EAAE;QACX,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACjE,IAAI,MAAM,IAAI,UAAU;YAAE,UAAU,EAAE,CAAC;QACvC,IAAI,CAAC,QAAQ,CAAC;YACZ,sBAAsB,EAAE,YAAY;gBAClC,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,sBAAsB;SACtC,CAAC,CAAC;QACH,IAAI,OAAO;YAAE,OAAO,EAAE,CAAC;IACzB,CAAC,CAAC;IACF,yBAAyB,GAAG,GAAG,EAAE;QAC/B,sDAAsD;QACtD,sDAAsD;QACtD,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAChD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC;QACvD,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,IAAI,YAAY,KAAK,IAAI,EAAE;YACzB,KAAK,GAAG,YAAY,CAAC;SACtB;QAED,IAAI,eAAe;YAAE,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC;QACvD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,yBAAyB,GAAG,CAAC,WAAmB,EAAE,EAAE;QAClD,IACE,CAAC,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC;YACtC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,EAClC;YACA,IAAI,CAAC,QAAQ,CAAC;gBACZ,sBAAsB,EAAE,WAAW;aACpC,CAAC,CAAC;SACJ;IACH,CAAC,CAAC;IACF,gBAAgB,GAAG,CAAC,WAAmB,EAAE,EAAE;QACzC,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,eAAe;YAC3B,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,WAAW;YAClC,CAAC,CAAC,WAAW,CAAC;QAChB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,OAAO,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,KAAK,MAAM,CAAC;IACxD,CAAC,CAAC;IACF,mBAAmB,GAAG,CAAC,WAAmB,EAAE,EAAE,CAC5C,IAAI,CAAC,KAAK,CAAC,sBAAsB,KAAK,WAAW,CAAC;IACpD,gBAAgB,GAAG,CAAC,WAAmB,EAAE,EAAE;QACzC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;IACtC,CAAC,CAAC;IACF,mBAAmB,GAAG,CACpB,MAA8C,EAC9C,KAA2B,EAC3B,EAAE;QACF,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAE5D,IAAI,MAAM,EAAE;YACV,IAAI,MAAM,CAAC,UAAU;gBAAE,OAAO;YAC9B,IAAI,QAAQ;gBAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;SACvC;QAED,IAAI,MAAM;YAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAE1B,IAAI,CAAC,kBAAkB,EAAE;YACvB,IAAI,CAAC,sBAAsB,EAAE,CAAC;SAC/B;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC;IACF,YAAY,GAAG,CAAC,WAAmB,EAAE,EAAE;QACrC,IAAI,CAAC,QAAQ,CAAC;YACZ,WAAW;SACZ,CAAC,CAAC;IACL,CAAC,CAAC;IACF,sBAAsB,GAAG,GAAG,EAAE;QAC5B,IAAI,CAAC,QAAQ,CAAC;YACZ,WAAW,EAAE,EAAE;SAChB,CAAC,CAAC;IACL,CAAC,CAAC;IACF,kBAAkB,GAAG,GAAG,EAAE;QACxB,MAAM,EACJ,SAAS,EACT,QAAQ,EACR,OAAO,EACP,eAAe,EACf,cAAc,GACf,GAAG,IAAI,CAAC,KAAK,CAAC;QACf,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAEnC,IAAI,CAAC,SAAS,IAAI,CAAC,WAAW,EAAE;YAC9B,OAAO,OAAO,CAAC;SAChB;QAED,IAAI,SAAS,IAAI,IAAA,mBAAU,EAAC,QAAQ,CAAC,EAAE;YACrC,OAAO,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;SACvC;QAED,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;YAChD,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAA,qBAAY,EAAC,WAAW,CAAC,EAAE,GAAG,CAAC,CAAC;YACzD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,OAAO,eAAe,CAAC;IACzB,CAAC,CAAC;IACF,iEAAiE;IACjE,kEAAkE;IAClE,iDAAiD;IACjD,cAAc,GAAG,CAAC,EAChB,OAAO,EACP,YAAY,EACZ,GAAG,IAAI,KAIL,EAAE,EAAE,EAAE;QACR,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACjE,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrC,MAAM,EACJ,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,yBAAyB,GAC1B,GAAG,IAAI,CAAC;QACT,OAAO;YACL,OAAO;YACP,eAAe;YACf,MAAM;YACN,mBAAmB;YACnB,gBAAgB;YAChB,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC;YAC7B,OAAO,EAAE,CACP,MAA8C,EAC9C,KAA2B,CAAC,2DAA2D;cACvF,EAAE;YACF,mEAAmE;YACnE,IAAA,wBAAgB,EAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC;YAC/D,YAAY,EAAE,CACZ,KAAa,EACb,KAAuB,CAAC,6EAA6E;cACrG,EAAE,CACF,IAAA,wBAAgB,EAAC,YAAY,EAAE,yBAAyB,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC;YACzE,GAAG,IAAI;SACR,CAAC;IACJ,CAAC,CAAC;IACF,YAAY,GAAG,GAAG,EAAE;QAClB,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1C,OAAO,SAAS,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC,CAAC;IACF,sCAAsC;IACtC,yBAAyB,GAAG,CAAC,KAA0B,EAAE,EAAE;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1C,IAAI,OAAO,CAAC,MAAM,EAAE;YAClB,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YACvC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC;YACvD,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,YAAY,CAAC;YACvD,MAAM,iBAAiB,GACrB,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,CAAC,mBAAmB,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;SACpD;aAAM;YACL,KAAK,CAAC,cAAc,EAAE,CAAC;SACxB;IACH,CAAC,CAAC;IACF,oBAAoB,GAAG,CAAC,YAAoB,EAAE,SAAiB,EAAE,EAAE;QACjE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAE/B,IAAI,OAAO,CAAC,MAAM,EAAE;YAClB,MAAM,eAAe,GAAG,CAAC,CAAC;YAC1B,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;YAC3C,IAAI,QAAQ,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC;YACxE,+CAA+C;YAC/C,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YAE1E,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE;gBAChC,oCAAoC;gBACpC,MAAM,SAAS,GAAG,QAAQ,GAAG,eAAe,CAAC;gBAC7C,MAAM,WAAW,GAAG,QAAQ,GAAG,eAAe,CAAC;gBAE/C,IACE,CAAC,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC;oBACjC,CAAC,SAAS,KAAK,MAAM,IAAI,WAAW,CAAC,EACrC;oBACA,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;iBAChD;aACF;iBAAM;gBACL,IAAI,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC;aAC1C;SACF;IACH,CAAC,CAAC;IACF,yEAAyE;IACzE,cAAc,GAAG,CAAC,KAA0B,EAAE,EAAE;QAC9C,MAAM,aAAa,GAAG,IAAA,YAAG,EAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;QACnD,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC;QAE/D,QAAQ,KAAK,CAAC,OAAO,EAAE;YACrB,KAAK,CAAC;gBACJ,gDAAgD;gBAChD,KAAK,CAAC,cAAc,EAAE,CAAC;gBAEvB,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC;gBAEtC,MAAM;YAER,KAAK,EAAE;gBACL,kDAAkD;gBAClD,KAAK,CAAC,cAAc,EAAE,CAAC;gBAEvB,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC;gBAEtC,MAAM;YAER,KAAK,EAAE;gBACL,kDAAkD;gBAClD,IAAI,aAAa,KAAK,OAAO,EAAE;oBAC7B,KAAK,CAAC,cAAc,EAAE,CAAC;oBAEvB,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC;iBACvC;gBAED,MAAM;YAER,KAAK,EAAE;gBACL,qCAAqC;gBACrC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,MAAM;YAER,KAAK,EAAE;gBACL,yDAAyD;gBACzD,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,qBAAqB;gBAE7C,IAAI,CAAC,oBAAoB,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;gBAEtD,MAAM;YAER,KAAK,EAAE;gBACL,6DAA6D;gBAC7D,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,qBAAqB;gBAE7C,IAAI,CAAC,oBAAoB,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;gBAExD,MAAM;YAER;gBACE,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC;SAChE;IACH,CAAC,CAAC;IACF,sBAAsB,GAAG,CAAC,kBAA2B,EAAE,EAAE;QACvD,MAAM,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAElE,IACE,IAAI,CAAC,KAAK,CAAC,kBAAkB,KAAK,kBAAkB;YACpD,mBAAmB,EACnB;YACA,mBAAmB,EAAE,CAAC;SACvB;QAED,IAAI,qBAAqB,EAAE;YACzB,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;SAC3C;QAED,IAAI,CAAC,QAAQ,CAAC;YACZ,kBAAkB;SACnB,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM;QACJ,iEAAiE;QACjE,MAAM,EACJ,eAAe,EACf,IAAI,EACJ,SAAS,EACT,KAAK,EACL,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,OAAO,EACP,UAAU,EACV,MAAM,EACN,GAAG,IAAI,EACR,GAAG,IAAI,CAAC,KAAK,CAAC;QACf,MAAM,EAAE,aAAa,EAAE,sBAAsB,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1E,MAAM,WAAW,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,OAAO,CAAC,CAAC;QAC/D,OAAO,CACL,8BAAC,WAAW,IACV,yBAAyB,EAAE,IAAI,CAAC,yBAAyB,EACzD,cAAc,EAAE,IAAI,CAAC,cAAc,EACnC,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAC7C,eAAe,EAAE,eAAe,EAChC,sBAAsB,EAAE,sBAAsB,EAC9C,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAC7C,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EACvC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,EAClC,UAAU,EAAE,UAAU,EACtB,kBAAkB,EAAE,IAAI,CAAC,sBAAsB,EAC/C,cAAc,EAAE,IAAI,CAAC,cAAc,EACnC,WAAW,EAAE,WAAW,EACxB,yBAAyB,EAAE,IAAI,CAAC,yBAAyB,EACzD,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,aAAa,EACpB,qBAAqB,EAAE,IAAI,CAAC,sBAAsB,EAClD,QAAQ,EAAE,IAAI,CAAC,YAAY,EAC3B,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,KAC1B,IAAI,GACR,CACH,CAAC;IACJ,CAAC;;AAGU,QAAA,OAAO,GAAG,IAAA,qBAAS,EAAC,WAAW,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Options.tsx b/source/renderer/react-polymorph/components/Options.tsx new file mode 100644 index 0000000000..4b0d14501a --- /dev/null +++ b/source/renderer/react-polymorph/components/Options.tsx @@ -0,0 +1,449 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { + ComponentType, // $FlowFixMe // $FlowFixMe + Element, + ElementRef, +} from 'react'; +// external libraries +import { isFunction, get, escapeRegExp } from 'lodash'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +import { composeFunctions } from '../utils/props'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + className?: string; + context: ThemeContextProp; + hasSearch?: boolean; + hideSearchClearButton?: boolean; + highlightSearch?: boolean; + isOpen: boolean; + isOpeningUpward: boolean; + noOptionsArrow?: boolean; + noOptionsCheckmark?: boolean; + noResults?: boolean; + noResultsMessage: string | Element; + onBlur?: (...args: Array) => any; + onChange?: (...args: Array) => any; + onClose?: (...args: Array) => any; + onSearch?: (...args: Array) => any; + optionHeight: number | null | undefined; + options: Array; + optionRenderer?: (...args: Array) => any; + optionsRef?: ElementRef; + optionsMaxHeight?: number; + persistSearchValue?: boolean; + render?: (...args: Array) => any; + resetOnClose: boolean; + searchHeight: number | null | undefined; + // TODO: Why do we have two separate props for selection? + selectedOption?: any; + selectedOptions?: Array; + setMouseIsOverOptions?: (arg0: boolean) => void; + skin?: ComponentType; + targetRef?: ElementRef; + theme: Record | null | undefined; + // if passed by user, it will take precedence over this.props.context.theme + themeId: string; + themeOverrides: Record; + toggleMouseLocation?: (...args: Array) => any; + toggleOpen?: (...args: Array) => any; +}; +type State = { + composedTheme: Record; + highlightedOptionIndex: number; + isMouseOverOptions: boolean; + searchValue: string; +}; + +class OptionsBase extends Component { + // declare ref types + optionsElement: Element | null | undefined; // TODO: Does this get used? Don't think so. + + // define static properties + static displayName = 'Options'; + static defaultProps = { + context: createEmptyContext(), + isOpen: false, + isOpeningUpward: false, + noOptionsArrow: false, + noOptionsCheckmark: false, + noResultsMessage: 'No results', + optionHeight: 46, + options: [], + resetOnClose: false, + searchHeight: 52, + theme: null, + themeId: IDENTIFIERS.OPTIONS, + themeOverrides: {}, + + toggleOpen() {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.searchInputRef = React.createRef(); + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + highlightedOptionIndex: 0, + isMouseOverOptions: false, + }; + } + + componentDidMount() { + if (this.props.isOpen) { + document.addEventListener('keydown', this._handleKeyDown, false); + } + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + if (!prevProps.isOpen && this.props.isOpen) { + this.setupOnOpenListeners(); + } else if (prevProps.isOpen && !this.props.isOpen) { + this.setupOnCloseListeners(); + } + + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + componentWillUnmount() { + document.removeEventListener('keydown', this._handleKeyDown, false); + } + + setupOnOpenListeners = () => { + document.addEventListener('keydown', this._handleKeyDown, false); + const { current: input } = this.searchInputRef; + + if (input) { + input.focus && input.focus(); + input.select && input.select(); + } + }; + setupOnCloseListeners = () => { + document.removeEventListener('keydown', this._handleKeyDown, false); + }; + close = () => { + const { isOpen, onClose, resetOnClose, toggleOpen } = this.props; + if (isOpen && toggleOpen) toggleOpen(); + this.setState({ + highlightedOptionIndex: resetOnClose + ? 0 + : this.state.highlightedOptionIndex, + }); + if (onClose) onClose(); + }; + getHighlightedOptionIndex = () => { + // If nothing is higlighted, highlight selected option + // In case nothing is selected, highlight first option + const { options, isOpeningUpward } = this.props; + const currentIndex = this.state.highlightedOptionIndex; + let index = 0; + + if (currentIndex !== null) { + index = currentIndex; + } + + if (isOpeningUpward) return options.length - 1 - index; + return index; + }; + setHighlightedOptionIndex = (optionIndex: number) => { + if ( + !this.isHighlightedOption(optionIndex) && + this.isDisabledOption(optionIndex) + ) { + this.setState({ + highlightedOptionIndex: optionIndex, + }); + } + }; + isSelectedOption = (optionIndex: number) => { + const { isOpeningUpward } = this.props; + const options = this.getFilteredOptions() || []; + const index = isOpeningUpward + ? options.length - 1 - optionIndex + : optionIndex; + const option = options[index]; + return option && this.props.selectedOption === option; + }; + isHighlightedOption = (optionIndex: number) => + this.state.highlightedOptionIndex === optionIndex; + isDisabledOption = (optionIndex: number) => { + const { options } = this.props; + const option = options[optionIndex]; + return option && !option.isDisabled; + }; + handleClickOnOption = ( + option: Record | null | undefined, + event: React.SyntheticEvent + ) => { + const { onChange, onBlur, persistSearchValue } = this.props; + + if (option) { + if (option.isDisabled) return; + if (onChange) onChange(option, event); + } + + if (onBlur) onBlur(event); + + if (!persistSearchValue) { + this.handleClearSearchValue(); + } + + this.close(); + }; + handleSearch = (searchValue: string) => { + this.setState({ + searchValue, + }); + }; + handleClearSearchValue = () => { + this.setState({ + searchValue: '', + }); + }; + getFilteredOptions = () => { + const { + hasSearch, + onSearch, + options, + highlightSearch, + optionRenderer, + } = this.props; + const { searchValue } = this.state; + + if (!hasSearch || !searchValue) { + return options; + } + + if (hasSearch && isFunction(onSearch)) { + return onSearch(searchValue, options); + } + + const filteredOptions = options.filter((option) => { + const { label } = option; + const regex = new RegExp(escapeRegExp(searchValue), 'i'); + return regex.test(label); + }); + return filteredOptions; + }; + // returns an object containing props, theme, and method handlers + // associated with rendering this.props.options, the user can call + // this in the body of the renderOptions function + getOptionProps = ({ + onClick, + onMouseEnter, + ...rest + }: { + onClick: (...args: Array) => any; + onMouseEnter: (...args: Array) => any; + } = {}) => { + const { isOpen, themeId, options, selectedOptions } = this.props; + const { composedTheme } = this.state; + const { + isHighlightedOption, + isDisabledOption, + handleClickOnOption, + setHighlightedOptionIndex, + } = this; + return { + options, + selectedOptions, + isOpen, + isHighlightedOption, + isDisabledOption, + theme: composedTheme[themeId], + onClick: ( + option: Record | null | undefined, + event: React.SyntheticEvent // the user's custom onClick event handler is composed with + ) => + // the internal functionality of Options (this.handleClickOnOption) + composeFunctions(onClick, handleClickOnOption)(option, event), + onMouseEnter: ( + index: number, + event: React.MouseEvent // user's custom onMouseEnter is composed with this.setHighlightedOptionIndex + ) => + composeFunctions(onMouseEnter, setHighlightedOptionIndex)(index, event), + ...rest, + }; + }; + getNoResults = () => { + const { noResults, hasSearch } = this.props; + const options = this.getFilteredOptions(); + return noResults || (hasSearch && !options.length); + }; + // ========= PRIVATE HELPERS ========= + _handleSelectionOnKeyDown = (event: React.KeyboardEvent) => { + const options = this.getFilteredOptions(); + + if (options.length) { + const { isOpeningUpward } = this.props; + const currentIndex = this.state.highlightedOptionIndex; + const reverseIndex = options.length - 1 - currentIndex; + const highlightedOption = + options[isOpeningUpward ? reverseIndex : currentIndex]; + this.handleClickOnOption(highlightedOption, event); + } else { + event.preventDefault(); + } + }; + _handleHighlightMove = (currentIndex: number, direction: string) => { + const { options } = this.props; + + if (options.length) { + const lowerIndexBound = 0; + const upperIndexBound = options.length - 1; + let newIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1; + // Make sure new index is within options bounds + newIndex = Math.max(lowerIndexBound, Math.min(newIndex, upperIndexBound)); + + if (options[newIndex].isDisabled) { + // Try to jump over disabled options + const canMoveUp = newIndex > lowerIndexBound; + const canMoveDown = newIndex < upperIndexBound; + + if ( + (direction === 'up' && canMoveUp) || + (direction === 'down' && canMoveDown) + ) { + this._handleHighlightMove(newIndex, direction); + } + } else { + this.setHighlightedOptionIndex(newIndex); + } + } + }; + // this needs to get passed to OptionsSkin and attached to each Option Li + _handleKeyDown = (event: React.KeyboardEvent) => { + const targetTagName = get(event, 'target.tagName'); + const highlightOptionIndex = this.state.highlightedOptionIndex; + + switch (event.keyCode) { + case 9: + // Tab key: selects currently highlighted option + event.preventDefault(); + + this._handleSelectionOnKeyDown(event); + + break; + + case 13: + // Enter key: selects currently highlighted option + event.preventDefault(); + + this._handleSelectionOnKeyDown(event); + + break; + + case 32: + // Space key: selects currently highlighted option + if (targetTagName !== 'INPUT') { + event.preventDefault(); + + this._handleSelectionOnKeyDown(event); + } + + break; + + case 27: + // Escape key: closes options if open + this.close(); + break; + + case 38: + // Up Arrow key: moves highlighted selection 'up' 1 index + event.preventDefault(); // prevent caret move + + this._handleHighlightMove(highlightOptionIndex, 'up'); + + break; + + case 40: + // Down Arrow key: moves highlighted selection 'down' 1 index + event.preventDefault(); // prevent caret move + + this._handleHighlightMove(highlightOptionIndex, 'down'); + + break; + + default: + this.props.resetOnClose && this.setHighlightedOptionIndex(0); + } + }; + _setMouseIsOverOptions = (isMouseOverOptions: boolean) => { + const { toggleMouseLocation, setMouseIsOverOptions } = this.props; + + if ( + this.state.isMouseOverOptions !== isMouseOverOptions && + toggleMouseLocation + ) { + toggleMouseLocation(); + } + + if (setMouseIsOverOptions) { + setMouseIsOverOptions(isMouseOverOptions); + } + + this.setState({ + isMouseOverOptions, + }); + }; + + render() { + // destructuring props ensures only the "...rest" get passed down + const { + highlightSearch, + skin, + targetRef, + theme, + themeOverrides, + toggleMouseLocation, + noResults, + onChange, + onSearch, + options, + context, + optionsRef, + isOpen, + ...rest + } = this.props; + const { composedTheme, highlightedOptionIndex, searchValue } = this.state; + const OptionsSkin = skin || context.skins[IDENTIFIERS.OPTIONS]; + return ( + + ); + } +} + +export const Options = withTheme(OptionsBase); diff --git a/source/renderer/react-polymorph/components/PasswordInput.js.map b/source/renderer/react-polymorph/components/PasswordInput.js.map new file mode 100644 index 0000000000..1483b42b48 --- /dev/null +++ b/source/renderer/react-polymorph/components/PasswordInput.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PasswordInput.js","sourceRoot":"","sources":["PasswordInput.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/PasswordInput.tsx b/source/renderer/react-polymorph/components/PasswordInput.tsx new file mode 100644 index 0000000000..b2640b4d82 --- /dev/null +++ b/source/renderer/react-polymorph/components/PasswordInput.tsx @@ -0,0 +1,144 @@ +// @ts-nocheck +import { $Values } from 'utility-types'; +import stringEntropy from 'fast-password-entropy'; +import React, { StatelessFunctionalComponent, useContext } from 'react'; +import { ThemeContext } from './HOC/ThemeContext'; +// internal utility functions +import { composeTheme, addThemeId } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { InputProps } from './Input'; + +export const calculatePasswordScore = ( + password: string, + entropyFactor: number = 0.01 +): number => Math.min((stringEntropy(password) * entropyFactor).toFixed(2), 1); +const STATE = { + DEFAULT: 'default', + ERROR: 'error', + INSECURE: 'insecure', + WEAK: 'weak', + STRONG: 'strong', +}; +export type PasswordInputProps = InputProps & { + debounceDelay?: number; + entropyFactor?: number; + isPasswordRepeat?: boolean; + isShowingTooltipOnFocus: boolean; + isShowingTooltipOnHover: boolean; + isTooltipOpen: boolean; + minLength?: number; + minStrongScore?: number; + repeatPassword?: string; + state?: $Values; + passwordFeedbacks?: { + insecure: string; + weak: string; + strong: string; + noMatch: string; + }; + tooltip?: string | boolean; + useDebounce?: boolean; +}; +export const PasswordInput: StatelessFunctionalComponent = ( + props +) => { + const { + context, + passwordFeedbacks, + entropyFactor, + error, + minLength, + maxLength, + minStrongScore, + isPasswordRepeat, + repeatPassword, + skin, + state, + theme, + themeOverrides, + tooltip, + ...rest + } = props; + // Theme + const themeContext = context || useContext(ThemeContext); + const composedTheme = composeTheme( + addThemeId(theme || themeContext.theme, props.themeId), + addThemeId(props.themeOverrides, props.themeId), + themeContext.ROOT_THEME_API + ); + // Skin + const PasswordInputSkin = + skin || themeContext.skins[IDENTIFIERS.PASSWORD_INPUT]; + // Logic + let dynamicState = PasswordInput.STATE.DEFAULT; + let passwordFeedback = null; + const password = props.value; + const score = calculatePasswordScore(password, entropyFactor); + const isValidPassword = + password.length >= minLength && password.length <= maxLength; + const isNotEmpty = password.length > 0; + + if (error) { + dynamicState = PasswordInput.STATE.ERROR; + passwordFeedback = error; + } else if (tooltip) { + passwordFeedback = tooltip; + } else if (isPasswordRepeat) { + if (repeatPassword === props.value) { + dynamicState = PasswordInput.STATE.DEFAULT; + passwordFeedback = null; + } else { + dynamicState = PasswordInput.STATE.ERROR; + passwordFeedback = passwordFeedbacks.noMatch; + } + } else if (isValidPassword) { + if (score < minStrongScore) { + dynamicState = PasswordInput.STATE.WEAK; + passwordFeedback = passwordFeedbacks[PasswordInput.STATE.WEAK]; + } else { + dynamicState = PasswordInput.STATE.STRONG; + passwordFeedback = passwordFeedbacks[PasswordInput.STATE.STRONG]; + } + } else if (isNotEmpty) { + dynamicState = PasswordInput.STATE.INSECURE; + passwordFeedback = passwordFeedbacks[PasswordInput.STATE.INSECURE]; + } + + return ( + + ); +}; +// Static Properties +PasswordInput.STATE = STATE; +PasswordInput.displayName = 'PasswordInput'; +PasswordInput.defaultProps = { + debounceDelay: 1000, + entropyFactor: 0.01, + passwordFeedbacks: { + insecure: 'insecure', + weak: 'weak', + strong: 'strong', + noMatch: "doesn't match", + }, + isPasswordRepeat: false, + isTooltipOpen: false, + isShowingTooltipOnFocus: true, + isShowingTooltipOnHover: true, + minLength: 10, + maxLength: 255, + minStrongScore: 0.75, + readOnly: false, + theme: null, + themeId: IDENTIFIERS.PASSWORD_INPUT, + themeOverrides: {}, + useDebounce: true, + value: '', +}; diff --git a/source/renderer/react-polymorph/components/PopOver.js.map b/source/renderer/react-polymorph/components/PopOver.js.map new file mode 100644 index 0000000000..934fdbea49 --- /dev/null +++ b/source/renderer/react-polymorph/components/PopOver.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PopOver.js","sourceRoot":"","sources":["PopOver.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,+CAMe;AACf,4CAA2D;AAC3D,qDAAkD;AAElD,mCAAsC;AA+BtC,SAAgB,OAAO,CACrB,KAAmB;IAEnB,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;IAChE,QAAQ;IACR,MAAM,YAAY,GAAG,OAAO,IAAI,IAAA,kBAAU,EAAC,2BAAY,CAAC,CAAC;IAEzD,IAAI,CAAC,YAAY,EAAE;QACjB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;KACjD;IAED,MAAM,aAAa,GAAG,IAAA,qBAAY,EAChC,IAAA,mBAAU,EAAC,KAAK,IAAI,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,EAChD,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,YAAY,CAAC,cAAc,CAC5B,CAAC;IACF,OAAO;IACP,MAAM,WAAW,GAAG,IAAI,IAAI,YAAY,CAAC,KAAK,CAAC,mBAAW,CAAC,QAAQ,CAAC,CAAC;IACrE,OAAO,8BAAC,WAAW,OAAK,KAAK,EAAE,KAAK,EAAE,aAAa,GAAI,CAAC;AAC1D,CAAC;AAnBD,0BAmBC;AACD,OAAO,CAAC,YAAY,GAAG;IACrB,SAAS,EAAE,KAAK;IAChB,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,mBAAW,CAAC,QAAQ;IAC7B,cAAc,EAAE,EAAE;IAClB,cAAc,EAAE,EAAE;IAClB,aAAa,EAAE,EAAE;CAClB,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/PopOver.tsx b/source/renderer/react-polymorph/components/PopOver.tsx new file mode 100644 index 0000000000..0079f45553 --- /dev/null +++ b/source/renderer/react-polymorph/components/PopOver.tsx @@ -0,0 +1,71 @@ +// @ts-nocheck +import { PopperOptions } from 'popper.js'; +import React, { + ComponentType, + ReactElement, + ReactNode, + StatelessFunctionalComponent, + useContext, +} from 'react'; +import { addThemeId, composeTheme } from '../utils/themes'; +import { ThemeContext } from './HOC/ThemeContext'; +import type { ThemeContextProp } from './HOC/withTheme'; +import { IDENTIFIERS } from './index'; + +/** + * PopOver UI component + * Use cases: Tooltips, Menus, Flyovers, Bubbles etc. + * + * Based on https://github.com/atomiks/tippyjs-react + * which is based on https://popper.js.org/ + * + * Supports all props as listed here: + * https://atomiks.github.io/tippyjs/v6/all-props/ + * + * Known limitations: + * Translucent backgrounds cannot be combined with borders because popper.js + * handles the main box and arrow as two separate elements and thus the border + * of the main box shines through the arrow bg + */ +export type PopOverProps = { + allowHTML?: boolean; + children?: ReactElement; + className?: string; + contentClassName?: string; + content: ReactNode; + context?: ThemeContextProp; + popperOptions: PopperOptions; + skin?: ComponentType; + theme?: Record | null | undefined; + themeId?: string; + themeOverrides?: Record; + themeVariables?: Record; +}; +export function PopOver( + props: PopOverProps +): StatelessFunctionalComponent { + const { context, skin, theme, themeId, themeOverrides } = props; + // Theme + const themeContext = context || useContext(ThemeContext); + + if (!themeContext) { + throw new Error('No theming context provided.'); + } + + const composedTheme = composeTheme( + addThemeId(theme || themeContext.theme, themeId), + addThemeId(themeOverrides, themeId), + themeContext.ROOT_THEME_API + ); + // Skin + const PopOverSkin = skin || themeContext.skins[IDENTIFIERS.POP_OVER]; + return ; +} +PopOver.defaultProps = { + allowHTML: false, + theme: null, + themeId: IDENTIFIERS.POP_OVER, + themeOverrides: {}, + themeVariables: {}, + popperOptions: {}, +}; diff --git a/source/renderer/react-polymorph/components/ProgressBar.js.map b/source/renderer/react-polymorph/components/ProgressBar.js.map new file mode 100644 index 0000000000..f4cb8257ea --- /dev/null +++ b/source/renderer/react-polymorph/components/ProgressBar.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ProgressBar.js","sourceRoot":"","sources":["ProgressBar.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAkBhC,MAAM,eAAgB,SAAQ,iBAAuB;IACnD,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,aAAa,CAAC;IACnC,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,QAAQ,EAAE,GAAG;QACb,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,YAAY;QACjC,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,eAAe,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,YAAY,CAAC,CAAC;QACxE,OAAO,8BAAC,eAAe,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IACxE,CAAC;;AAGU,QAAA,WAAW,GAAG,IAAA,qBAAS,EAAC,eAAe,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/ProgressBar.tsx b/source/renderer/react-polymorph/components/ProgressBar.tsx new file mode 100644 index 0000000000..7eb7fab8c1 --- /dev/null +++ b/source/renderer/react-polymorph/components/ProgressBar.tsx @@ -0,0 +1,63 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + className?: string; + context: ThemeContextProp; + label?: string; + progress: number; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; // custom css/scss from user that adheres to component's theme API +}; +type State = { + composedTheme: Record; +}; + +class ProgressBarBase extends Component { + // define static properties + static displayName = 'ProgressBar'; + static defaultProps = { + context: createEmptyContext(), + progress: 100, + theme: null, + themeId: IDENTIFIERS.PROGRESS_BAR, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const ProgressBarSkin = skin || context.skins[IDENTIFIERS.PROGRESS_BAR]; + return ; + } +} + +export const ProgressBar = withTheme(ProgressBarBase); diff --git a/source/renderer/react-polymorph/components/Radio.js.map b/source/renderer/react-polymorph/components/Radio.js.map new file mode 100644 index 0000000000..d65c28e785 --- /dev/null +++ b/source/renderer/react-polymorph/components/Radio.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Radio.js","sourceRoot":"","sources":["Radio.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAqBhC,MAAM,SAAU,SAAQ,iBAAuB;IAC7C,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC;IAC7B,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,QAAQ,EAAE,KAAK;QACf,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,KAAK;QAC1B,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,SAAS,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,KAAK,CAAC,CAAC;QAC3D,OAAO,8BAAC,SAAS,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IAClE,CAAC;;AAGU,QAAA,KAAK,GAAG,IAAA,qBAAS,EAAC,SAAS,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Radio.tsx b/source/renderer/react-polymorph/components/Radio.tsx new file mode 100644 index 0000000000..6bd23e2a28 --- /dev/null +++ b/source/renderer/react-polymorph/components/Radio.tsx @@ -0,0 +1,66 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + context: ThemeContextProp; + disabled?: boolean; + label?: string | Element; + onBlur?: (...args: Array) => any; + onChange?: (...args: Array) => any; + onFocus?: (...args: Array) => any; + selected: boolean; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; +}; + +class RadioBase extends Component { + // define static properties + static displayName = 'Radio'; + static defaultProps = { + context: createEmptyContext(), + selected: false, + theme: null, + themeId: IDENTIFIERS.RADIO, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const RadioSkin = skin || context.skins[IDENTIFIERS.RADIO]; + return ; + } +} + +export const Radio = withTheme(RadioBase); diff --git a/source/renderer/react-polymorph/components/ScrollBar.js.map b/source/renderer/react-polymorph/components/ScrollBar.js.map new file mode 100644 index 0000000000..c16ab95268 --- /dev/null +++ b/source/renderer/react-polymorph/components/ScrollBar.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ScrollBar.js","sourceRoot":"","sources":["ScrollBar.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAgBhC,MAAM,aAAc,SAAQ,iBAAuB;IACjD,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,SAAS;QAC9B,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,aAAa,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,SAAS,CAAC,CAAC;QACnE,OAAO,8BAAC,aAAa,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IACtE,CAAC;;AAGU,QAAA,SAAS,GAAG,IAAA,qBAAS,EAAC,aAAa,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/ScrollBar.tsx b/source/renderer/react-polymorph/components/ScrollBar.tsx new file mode 100644 index 0000000000..a396ea2e3b --- /dev/null +++ b/source/renderer/react-polymorph/components/ScrollBar.tsx @@ -0,0 +1,60 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + className?: string; + context: ThemeContextProp; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; // custom css/scss from user that adheres to component's theme API +}; +type State = { + composedTheme: Record; +}; + +class ScrollBarBase extends Component { + // define static properties + static displayName = 'ScrollBar'; + static defaultProps = { + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.SCROLLBAR, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const ScrollBarSkin = skin || context.skins[IDENTIFIERS.SCROLLBAR]; + return ; + } +} + +export const ScrollBar = withTheme(ScrollBarBase); diff --git a/source/renderer/react-polymorph/components/Select.js.map b/source/renderer/react-polymorph/components/Select.js.map new file mode 100644 index 0000000000..d7155286d1 --- /dev/null +++ b/source/renderer/react-polymorph/components/Select.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Select.js","sourceRoot":"","sources":["Select.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Select.tsx b/source/renderer/react-polymorph/components/Select.tsx new file mode 100644 index 0000000000..e46ea81cee --- /dev/null +++ b/source/renderer/react-polymorph/components/Select.tsx @@ -0,0 +1,199 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element } from 'react'; +// internal components +import { GlobalListeners } from './HOC/GlobalListeners'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + allowBlank: boolean; + autoFocus: boolean; + className?: string; + context: ThemeContextProp; + disabled?: boolean; + error?: string | Element; + hasSearch?: boolean; + hideSearchClearButton?: boolean; + highlightSearch?: boolean; + isOpeningUpward: boolean; + label?: string | Element; + noResultsMessage?: string; + onBlur?: (...args: Array) => any; + onChange?: (...args: Array) => any; + onFocus?: (...args: Array) => any; + onSearch?: (...args: Array) => any; + optionHeight?: number; + optionRenderer?: (...args: Array) => any; + options: Array; + placeholder?: string; + selectionRenderer?: (...args: Array) => any; + searchHeight?: number; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; + value: string; +}; +type State = { + composedTheme: Record; + isOpen: boolean; + mouseIsOverOptions: boolean; +}; + +class SelectBase extends Component { + // declare ref types + rootElement: Element | null | undefined; + inputElement: Element<'input'>; + optionsElement: Element | null | undefined; + // define static properties + static displayName = 'Select'; + static defaultProps = { + allowBlank: true, + autoFocus: false, + context: createEmptyContext(), + isOpeningUpward: false, + options: [], + theme: null, + themeOverrides: {}, + themeId: IDENTIFIERS.SELECT, + value: '', + }; + + constructor(props: Props) { + super(props); + // define ref + this.rootElement = React.createRef(); + this.inputElement = React.createRef(); + this.optionsElement = React.createRef(); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + isOpen: false, + mouseIsOverOptions: false, + }; + } + + componentDidMount() { + // check for autoFocus of input element + if (this.props.autoFocus) { + return this.focus(); + } + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + // ========= PUBLIC SKIN API ========= + // applying focus to the input element will + // toggle options open because Select's input is read only + focus = () => this.toggleOpen(); + toggleOpen = () => { + if ( + this.state.isOpen && + this.optionsElement && + this.optionsElement.current + ) { + // set Options scroll position to top on close + this.optionsElement.current.scrollTop = 0; + } + + this.setState({ + isOpen: !this.state.isOpen, + }); + }; + toggleMouseLocation = () => + this.setState({ + mouseIsOverOptions: !this.state.mouseIsOverOptions, + }); + handleInputClick = (event: React.MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + const { inputElement } = this; + + if ( + inputElement.current && + document.activeElement === inputElement.current + ) { + inputElement.current.blur(); + } + + this.toggleOpen(); + }; + handleChange = (option: Record, event: React.SyntheticEvent) => { + // check if the user passed an onChange handler and call it + if (this.props.onChange) this.props.onChange(option.value, event); + // onChange is called when an option is selected, so close options + this.toggleOpen(); + }; + getSelectedOption = () => { + const { options, value, allowBlank } = this.props; + + for (const option of options) { + if (option.value === value) return option; + } + + if (!allowBlank) return options[0]; + }; + + render() { + // destructuring props ensures only the "...rest" get passed down + const { + skin, + theme, + themeOverrides, + autoFocus, + context, + allowBlank, + optionHeight, + searchHeight, + ...rest + } = this.props; + const SelectSkin = skin || context.skins[IDENTIFIERS.SELECT]; + return ( + + {({ optionsMaxHeight }) => ( + + )} + + ); + } +} + +export const Select = withTheme(SelectBase); diff --git a/source/renderer/react-polymorph/components/Stepper.js.map b/source/renderer/react-polymorph/components/Stepper.js.map new file mode 100644 index 0000000000..f053015ad0 --- /dev/null +++ b/source/renderer/react-polymorph/components/Stepper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Stepper.js","sourceRoot":"","sources":["Stepper.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AAqBhC,MAAM,WAAY,SAAQ,iBAAuB;IAC/C,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,OAAO;QAC5B,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,WAAW,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9D,OAAO,8BAAC,WAAW,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IACpE,CAAC;;AAGU,QAAA,OAAO,GAAG,IAAA,qBAAS,EAAC,WAAW,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Stepper.tsx b/source/renderer/react-polymorph/components/Stepper.tsx new file mode 100644 index 0000000000..1f376fa578 --- /dev/null +++ b/source/renderer/react-polymorph/components/Stepper.tsx @@ -0,0 +1,65 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + activeStep?: number; + className?: string; + context: ThemeContextProp; + label?: string; + labelDisabled?: boolean; + onStepClick?: (...args: Array) => any; + skin?: ComponentType; + steps: Array; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; +}; + +class StepperBase extends Component { + // define static properties + static displayName = 'Stepper'; + static defaultProps = { + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.STEPPER, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const StepperSkin = skin || context.skins[this.props.themeId]; + return ; + } +} + +export const Stepper = withTheme(StepperBase); diff --git a/source/renderer/react-polymorph/components/TextArea.js.map b/source/renderer/react-polymorph/components/TextArea.js.map new file mode 100644 index 0000000000..2a63162146 --- /dev/null +++ b/source/renderer/react-polymorph/components/TextArea.js.map @@ -0,0 +1 @@ +{"version":3,"file":"TextArea.js","sourceRoot":"","sources":["TextArea.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,qBAAqB;AACrB,mCAAwC;AACxC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AA8BhC,MAAM,YAAa,SAAQ,iBAAuB;IAChD,oBAAoB;IACpB,eAAe,CAAsB;IACrC,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,MAAM,CAAC,YAAY,GAAG;QACpB,SAAS,EAAE,KAAK;QAChB,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,SAAS;QAC9B,cAAc,EAAE,EAAE;QAClB,KAAK,EAAE,EAAE;KACV,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,aAAa;QACb,IAAI,CAAC,eAAe,GAAG,eAAK,CAAC,SAAS,EAAE,CAAC;QACzC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;YACD,KAAK,EAAE,EAAE;SACV,CAAC;IACJ,CAAC;IAED,iBAAiB;QACf,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAE7C,IAAI,UAAU,EAAE;YACd,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAE1D,IAAI,CAAC,iBAAiB,EAAE,CAAC;SAC1B;QAED,IAAI,SAAS,EAAE;YACb,IAAI,CAAC,KAAK,EAAE,CAAC;SACd;IACH,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU;YAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEpD,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAI,CAAC,SAAS,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;gBAClD,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;aAC3D;iBAAM,IAAI,SAAS,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;gBACzD,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;aAC9D;YAED,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,oBAAoB;QAClB,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;YACzB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;SAC9D;IACH,CAAC;IAED,KAAK,GAAG,GAAG,EAAE;QACX,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,eAAe,CAAC,OAAO;YAAE,OAAO;QACrC,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAClC,CAAC,CAAC;IACF,QAAQ,GAAG,CAAC,KAA2B,EAAE,EAAE;QACzC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1C,IAAI,QAAQ;YAAE,OAAO;QACrB,IAAI,QAAQ;YAAE,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IACxE,CAAC,CAAC;IACF,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAC5B,IAAI,CAAC,QAAQ,CAAC;QACZ,KAAK;KACN,CAAC,CAAC;IAEL,aAAa,CAAC,KAAa;QACzB,OAAO,IAAA,aAAI,EAAC;YACV,IAAI,CAAC,mBAAmB;YACxB,IAAI,CAAC,iBAAiB;YACtB,IAAI,CAAC,iBAAiB;SACvB,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,mBAAmB,CAAC,KAAa;QAC/B,IAAI,CAAC,IAAA,iBAAQ,EAAC,KAAK,CAAC,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;SACxE;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iBAAiB,CAAC,KAAa;QAC7B,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACjC,MAAM,SAAS,GAAG,SAAS,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;QAChE,OAAO,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC3D,CAAC;IAED,iBAAiB,CAAC,KAAa;QAC7B,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACjC,MAAM,UAAU,GAAG,SAAS,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;QAEjE,IAAI,UAAU,EAAE;YACd,IAAI,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;SAC9C;aAAM,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,EAAE,EAAE;YAClC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;SACpB;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iBAAiB;QACf,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,eAAe,CAAC,OAAO;YAAE,OAAO;QACrC,sEAAsE;QACtE,MAAM,KAAK,GAAG,gBAAgB,CAAC,eAAe,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,YAAY,GAChB,KAAK,CAAC,SAAS,KAAK,aAAa;YAC/B,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YACnE,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC;gBAChC,UAAU,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC1C,uCAAuC;QACvC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QAC9C,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,GACrC,eAAe,CAAC,OAAO,CAAC,YAAY,GAAG,YACzC,IAAI,CAAC;IACP,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EACJ,IAAI,EACJ,KAAK,EACL,cAAc,EACd,QAAQ,EACR,KAAK,EACL,OAAO,EACP,SAAS,EACT,UAAU,EACV,GAAG,IAAI,EACR,GAAG,IAAI,CAAC,KAAK,CAAC;QACf,MAAM,YAAY,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,SAAS,CAAC,CAAC;QAClE,OAAO,CACL,8BAAC,YAAY,IACX,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,WAAW,EAAE,IAAI,CAAC,eAAe,EACjC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAC3B,IAAI,GACR,CACH,CAAC;IACJ,CAAC;;AAGU,QAAA,QAAQ,GAAG,IAAA,qBAAS,EAAC,YAAY,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/TextArea.tsx b/source/renderer/react-polymorph/components/TextArea.tsx new file mode 100644 index 0000000000..058dd6b185 --- /dev/null +++ b/source/renderer/react-polymorph/components/TextArea.tsx @@ -0,0 +1,197 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Node, Element } from 'react'; +// external libraries +import { isString, flow } from 'lodash'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +type Props = { + autoFocus: boolean; + autoResize: boolean; + className?: string; + context: ThemeContextProp; + disabled?: boolean; + label?: string | Element; + error?: string | Node; + maxLength?: number; + minLength?: number; + onBlur?: (...args: Array) => any; + onChange?: (...args: Array) => any; + onFocus?: (...args: Array) => any; + placeholder?: string; + rows?: number; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeId: string; + themeOverrides: Record; + value: string; +}; +type State = { + error: string; + composedTheme: Record; +}; + +class TextAreaBase extends Component { + // declare ref types + textareaElement: Element<'textarea'>; + // define static properties + static displayName = 'TextArea'; + static defaultProps = { + autoFocus: false, + autoResize: true, + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.TEXT_AREA, + themeOverrides: {}, + value: '', + }; + + constructor(props: Props) { + super(props); + // define ref + this.textareaElement = React.createRef(); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + error: '', + }; + } + + componentDidMount() { + const { autoResize, autoFocus } = this.props; + + if (autoResize) { + window.addEventListener('resize', this._handleAutoresize); + + this._handleAutoresize(); + } + + if (autoFocus) { + this.focus(); + } + } + + componentDidUpdate(prevProps: Props) { + if (this.props.autoResize) this._handleAutoresize(); + + if (prevProps !== this.props) { + if (!prevProps.autoResize && this.props.autoResize) { + window.addEventListener('resize', this._handleAutoresize); + } else if (prevProps.autoResize && !this.props.autoResize) { + window.removeEventListener('resize', this._handleAutoresize); + } + + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + componentWillUnmount() { + if (this.props.autoResize) { + window.removeEventListener('resize', this._handleAutoresize); + } + } + + focus = () => { + const { textareaElement } = this; + if (!textareaElement.current) return; + textareaElement.current.focus(); + }; + onChange = (event: React.SyntheticEvent) => { + const { onChange, disabled } = this.props; + if (disabled) return; + if (onChange) onChange(this._processValue(event.target.value), event); + }; + _setError = (error: string) => + this.setState({ + error, + }); + + _processValue(value: string) { + return flow([ + this._enforceStringValue, + this._enforceMaxLength, + this._enforceMinLength, + ]).call(this, value); + } + + _enforceStringValue(value: string) { + if (!isString(value)) { + throw new Error('Values passed to TextArea::onChange must be strings'); + } + + return value; + } + + _enforceMaxLength(value: string) { + const { maxLength } = this.props; + const isTooLong = maxLength != null && value.length > maxLength; + return isTooLong ? value.substring(0, maxLength) : value; + } + + _enforceMinLength(value: string) { + const { minLength } = this.props; + const isTooShort = minLength != null && value.length < minLength; + + if (isTooShort) { + this._setError('Please enter a valid input'); + } else if (this.state.error !== '') { + this._setError(''); + } + + return value; + } + + _handleAutoresize() { + const { textareaElement } = this; + if (!textareaElement.current) return; + // compute the height difference between inner height and outer height + const style = getComputedStyle(textareaElement.current, ''); + const heightOffset = + style.boxSizing === 'content-box' + ? -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)) + : parseFloat(style.borderTopWidth) + + parseFloat(style.borderBottomWidth); + // resize the input to its content size + textareaElement.current.style.height = 'auto'; + textareaElement.current.style.height = `${ + textareaElement.current.scrollHeight + heightOffset + }px`; + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { + skin, + theme, + themeOverrides, + onChange, + error, + context, + autoFocus, + autoResize, + ...rest + } = this.props; + const TextAreaSkin = skin || context.skins[IDENTIFIERS.TEXT_AREA]; + return ( + + ); + } +} + +export const TextArea = withTheme(TextAreaBase); diff --git a/source/renderer/react-polymorph/components/ThemeProvider.js.map b/source/renderer/react-polymorph/components/ThemeProvider.js.map new file mode 100644 index 0000000000..a5cbf34cb5 --- /dev/null +++ b/source/renderer/react-polymorph/components/ThemeProvider.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ThemeProvider.js","sourceRoot":"","sources":["ThemeProvider.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/ThemeProvider.tsx b/source/renderer/react-polymorph/components/ThemeProvider.tsx new file mode 100644 index 0000000000..fb5e3b6c15 --- /dev/null +++ b/source/renderer/react-polymorph/components/ThemeProvider.tsx @@ -0,0 +1,117 @@ +// @ts-nocheck +import React, { useMemo, useState, useEffect } from 'react'; + +// external libraries +import { isEmpty, cloneDeep } from 'lodash'; + +// contains default theme and context provider +import { ThemeContext } from './HOC/ThemeContext'; + +// imports the Root Theme API object which specifies the shape +// of a complete theme for every component in this library, used in this.composeLibraryTheme +import { ROOT_THEME_API } from '../themes/API'; + +// internal utility functions +import { appendToProperty } from '../utils/themes'; +import { hasProperty } from '../utils/props'; +import { ThemeVariablesProvider } from './ThemeVariablesProvider'; + +function ThemeProvider({ + children, + skins, + theme, + variables, + isRoot, + themeOverrides, +}) { + const [themeState, setThemeState] = useState(() => + composeLibraryTheme(theme, themeOverrides) + ); + + useEffect(() => { + setThemeState(composeLibraryTheme(theme, themeOverrides)); + }, [theme, themeOverrides]); + + const providerValue = useMemo( + () => ({ + skins, + theme: themeState, + ROOT_THEME_API, + }), + [skins, themeState] + ); + + return ( + + + {children} + + + ); +} + +ThemeProvider.defaultProps = { + isRoot: true, + skins: {}, + theme: {}, + variables: {}, + themeOverrides: {}, +}; + +const composeLibraryTheme = (theme, themeOverrides) => { + if (isEmpty(themeOverrides)) { + return theme; + } + + const composedTheme = {}; + + Object.keys(ROOT_THEME_API).forEach((componentName) => { + if (hasProperty(theme, componentName)) { + composedTheme[componentName] = theme[componentName]; + } + + if (hasProperty(themeOverrides, componentName)) { + composedTheme[componentName] = applyThemeOverrides( + theme[componentName], + themeOverrides[componentName], + ROOT_THEME_API[componentName] + ); + } + }); + + return composedTheme; +}; + +const applyThemeOverrides = ( + componentTheme, + componentThemeOverrides, + componentThemeAPI +) => { + if (isEmpty(componentThemeOverrides)) { + return componentTheme; + } + + const composedComponentTheme = cloneDeep(componentThemeAPI); + + Object.keys(componentThemeAPI).forEach((className) => { + if (hasProperty(componentTheme, className)) { + appendToProperty( + composedComponentTheme, + className, + componentTheme[className] + ); + } + + if (hasProperty(componentThemeOverrides, className)) { + appendToProperty( + composedComponentTheme, + className, + componentThemeOverrides[className] + ); + } + }); + + return composedComponentTheme; +}; + +export { ThemeProvider }; diff --git a/source/renderer/react-polymorph/components/ThemeVariablesProvider.js.map b/source/renderer/react-polymorph/components/ThemeVariablesProvider.js.map new file mode 100644 index 0000000000..0a5d2f773b --- /dev/null +++ b/source/renderer/react-polymorph/components/ThemeVariablesProvider.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ThemeVariablesProvider.js","sourceRoot":"","sources":["ThemeVariablesProvider.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAQzC,SAAgB,sBAAsB,CAAC,KAA0B;IAC/D,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACpC,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,MAAM,EAAE;YACV,qCAAqC;YACrC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;gBACrC,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;SACJ;IACH,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,CAAC,CAAC,CACd,8DAAG,KAAK,CAAC,QAAQ,CAAI,CACtB,CAAC,CAAC,CAAC,CACF,uCAAK,KAAK,EAAE,KAAK,CAAC,SAAS,IAAG,KAAK,CAAC,QAAQ,CAAO,CACpD,CAAC;AACJ,CAAC;AAfD,wDAeC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/ThemeVariablesProvider.tsx b/source/renderer/react-polymorph/components/ThemeVariablesProvider.tsx new file mode 100644 index 0000000000..978c5afa57 --- /dev/null +++ b/source/renderer/react-polymorph/components/ThemeVariablesProvider.tsx @@ -0,0 +1,25 @@ +// @ts-nocheck +import React, { useEffect } from 'react'; + +export type ThemeVariables = Record; +type ThemeVariablesProps = { + children: Node; + isRoot?: boolean; + variables: ThemeVariables; +}; +export function ThemeVariablesProvider(props: ThemeVariablesProps) { + const { isRoot, variables } = props; + useEffect(() => { + if (isRoot) { + // Set css variables on document root + Object.keys(variables).forEach((key) => { + document.documentElement.style.setProperty(key, variables[key]); + }); + } + }); + return isRoot ? ( + <>{props.children} + ) : ( +
    {props.children}
    + ); +} diff --git a/source/renderer/react-polymorph/components/Tooltip.js.map b/source/renderer/react-polymorph/components/Tooltip.js.map new file mode 100644 index 0000000000..439972e149 --- /dev/null +++ b/source/renderer/react-polymorph/components/Tooltip.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Tooltip.js","sourceRoot":"","sources":["Tooltip.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,6BAA6B;AAC7B,+CAAgE;AAChE,4CAAgF;AAChF,mBAAmB;AACnB,wBAAgC;AA2BhC,MAAM,WAAY,SAAQ,iBAA8B;IACtD,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,SAAS,EAAE,KAAK;QAChB,UAAU,EAAE,KAAK;QACjB,eAAe,EAAE,IAAI;QACrB,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,KAAK;QAChB,kBAAkB,EAAE,KAAK;QACzB,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,cAAW,CAAC,OAAO;QAC5B,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAmB;QAC7B,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAuB;QACxC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,iEAAiE;QACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrE,MAAM,WAAW,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAW,CAAC,OAAO,CAAC,CAAC;QAC/D,OAAO,8BAAC,WAAW,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,KAAM,IAAI,GAAI,CAAC;IACpE,CAAC;;AAGU,QAAA,OAAO,GAAG,IAAA,qBAAS,EAAC,WAAW,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/Tooltip.tsx b/source/renderer/react-polymorph/components/Tooltip.tsx new file mode 100644 index 0000000000..f5d6cb0873 --- /dev/null +++ b/source/renderer/react-polymorph/components/Tooltip.tsx @@ -0,0 +1,78 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { ComponentType, Element, Node } from 'react'; +// internal utility functions +import { createEmptyContext, withTheme } from './HOC/withTheme'; +import { composeTheme, addThemeId, didThemePropsChange } from '../utils/themes'; +// import constants +import { IDENTIFIERS } from '.'; +import type { ThemeContextProp } from './HOC/withTheme'; + +export type TooltipProps = { + children: Node | null | undefined; + className?: string; + context: ThemeContextProp; + isAligningRight?: boolean; + isBounded: boolean; + isCentered: boolean; + isOpeningUpward: boolean; + isShowingOnHover: boolean; + isTransparent: boolean; + isVisible: boolean; + arrowRelativeToTip: boolean; + skin?: ComponentType; + theme: Record | null | undefined; + // will take precedence over theme in context if passed + themeOverrides: Record; + // custom css/scss from user that adheres to component's theme API + themeId: string; + tip?: string | Element; +}; +type State = { + composedTheme: Record; +}; + +class TooltipBase extends Component { + // define static properties + static displayName = 'Tooltip'; + static defaultProps = { + context: createEmptyContext(), + isBounded: false, + isCentered: false, + isOpeningUpward: true, + isShowingOnHover: true, + isTransparent: true, + isVisible: false, + arrowRelativeToTip: false, + theme: null, + themeId: IDENTIFIERS.TOOLTIP, + themeOverrides: {}, + }; + + constructor(props: TooltipProps) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: TooltipProps) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + // destructuring props ensures only the "...rest" get passed down + const { skin, theme, themeOverrides, context, ...rest } = this.props; + const TooltipSkin = skin || context.skins[IDENTIFIERS.TOOLTIP]; + return ; + } +} + +export const Tooltip = withTheme(TooltipBase); diff --git a/source/renderer/react-polymorph/components/index.js.map b/source/renderer/react-polymorph/components/index.js.map new file mode 100644 index 0000000000..3e0cf53d54 --- /dev/null +++ b/source/renderer/react-polymorph/components/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;AAAA,cAAc;AACD,QAAA,WAAW,GAAG;IACzB,YAAY,EAAE,cAAc;IAC5B,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,WAAW;IACvB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,eAAe,EAAE,gBAAgB;IACjC,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,eAAe,EAAE,gBAAgB;IACjC,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;IAClB,cAAc,EAAE,eAAe;IAC/B,QAAQ,EAAE,SAAS;IACnB,YAAY,EAAE,aAAa;IAC3B,KAAK,EAAE,OAAO;IACd,SAAS,EAAE,WAAW;IACtB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,UAAU;IACrB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACnB,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/index.ts b/source/renderer/react-polymorph/components/index.ts new file mode 100644 index 0000000000..81822ec7e3 --- /dev/null +++ b/source/renderer/react-polymorph/components/index.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +export const IDENTIFIERS = { + AUTOCOMPLETE: 'autocomplete', + BUBBLE: 'bubble', + BUTTON: 'button', + CHECKBOX: 'checkbox', + DROPDOWN: 'dropdown', + FLEX: 'flex', + FORM_FIELD: 'formfield', + GRID: 'grid', + GUTTER: 'gutter', + HEADER: 'header', + INFINITE_SCROLL: 'infinitescroll', + INPUT: 'input', + LINK: 'link', + LOADING_SPINNER: 'loadingspinner', + MODAL: 'modal', + OPTIONS: 'options', + PASSWORD_INPUT: 'passwordInput', + POP_OVER: 'popOver', + PROGRESS_BAR: 'progressbar', + RADIO: 'radio', + SCROLLBAR: 'scrollbar', + SELECT: 'select', + STEPPER: 'stepper', + SWITCH: 'switch', + TEXT_AREA: 'textarea', + TOGGLER: 'toggler', + TOOLTIP: 'tooltip', +}; diff --git a/source/renderer/react-polymorph/components/layout/Base.js.map b/source/renderer/react-polymorph/components/layout/Base.js.map new file mode 100644 index 0000000000..b7dc6443ba --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/Base.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Base.js","sourceRoot":"","sources":["Base.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,4DAAoC;AACpC,SAAS;AACT,+EAAwD;AACxD,YAAY;AACZ,+CAAuD;AAYvD,MAAa,IAAK,SAAQ,iBAAuB;IAC/C,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC;IAC5B,MAAM,CAAC,YAAY,GAAG;QACpB,YAAY,EAAE,EAAE;QAChB,WAAW,EAAE,EAAE;KAChB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;QAC7C,IAAI,CAAC,KAAK,GAAG;YACX,cAAc,EAAE,IAAA,0BAAiB,EAAC,mBAAU,EAAE,WAAW,EAAE,aAAa,CAAC;SAC1E,CAAC;IACJ,CAAC;IAED,MAAM;QACJ,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACzD,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACtC,OAAO,CACL,uCACE,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,SAAS,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC,IAEtD,QAAQ,CACL,CACP,CAAC;IACJ,CAAC;;AA3BH,oBA4BC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/layout/Base.tsx b/source/renderer/react-polymorph/components/layout/Base.tsx new file mode 100644 index 0000000000..5b2176ee22 --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/Base.tsx @@ -0,0 +1,48 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { Node } from 'react'; +import classnames from 'classnames'; +// styles +import baseStyles from '../../themes/helpers/Base.scss'; +// utilities +import { composeBaseStyles } from '../../utils/layout'; + +type Props = { + activeClasses: Array; + children?: Node | null | undefined; + className?: string; + inlineStyles: Record; + stylesToAdd?: Record; +}; +type State = { + composedStyles: Record; +}; +export class Base extends Component { + // define static properties + static displayName = 'Base'; + static defaultProps = { + inlineStyles: {}, + stylesToAdd: {}, + }; + + constructor(props: Props) { + super(props); + const { activeClasses, stylesToAdd } = props; + this.state = { + composedStyles: composeBaseStyles(baseStyles, stylesToAdd, activeClasses), + }; + } + + render() { + const { className, children, inlineStyles } = this.props; + const { composedStyles } = this.state; + return ( +
    + {children} +
    + ); + } +} diff --git a/source/renderer/react-polymorph/components/layout/Flex.js.map b/source/renderer/react-polymorph/components/layout/Flex.js.map new file mode 100644 index 0000000000..85116d7809 --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/Flex.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Flex.js","sourceRoot":"","sources":["Flex.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/layout/Flex.tsx b/source/renderer/react-polymorph/components/layout/Flex.tsx new file mode 100644 index 0000000000..ee1a1c2877 --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/Flex.tsx @@ -0,0 +1,130 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { Node } from 'react'; +import { pickBy } from 'lodash'; +// components +import { Base } from './Base'; +// utilities +import { createEmptyContext, withTheme } from '../HOC/withTheme'; +import { + composeTheme, + addThemeId, + didThemePropsChange, +} from '../../utils/themes'; +// constants +import { IDENTIFIERS } from '..'; +import type { ThemeContextProp } from '../HOC/withTheme'; + +type Props = { + alignItems?: string; + className?: string; + center?: boolean; + children?: Node; + column?: boolean; + columnReverse?: boolean; + context: ThemeContextProp; + justifyContent?: string; + row?: boolean; + rowReverse?: boolean; + theme: Record | null | undefined; + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; +}; + +class FlexBase extends Component { + // define static properties + static displayName = 'Flex'; + static defaultProps = { + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.FLEX, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + _getActiveClasses = ({ center, column, columnReverse, row, rowReverse }) => { + const activeClasses = ['container']; + const activeProps = pickBy({ + center, + column, + columnReverse, + row, + rowReverse, + }); + return [...activeClasses, ...Object.keys(activeProps)].filter((val) => val); + }; + _assembleFlexTheme = (activeClasses: Array) => { + const theme = this.state.composedTheme[this.props.themeId]; + return activeClasses.reduce((reducedTheme, activeClass) => { + if (Object.hasOwnProperty.call(theme, activeClass)) { + reducedTheme[activeClass] = theme[activeClass]; + } + + return reducedTheme; + }, {}); + }; + + renderChildren(theme: Record) { + return React.Children.map(this.props.children, (child) => { + if (child.type.displayName === 'FlexItem') { + return React.cloneElement(child, { + theme, + }); + } + + return child; + }); + } + + render() { + const { + alignItems, + className, + justifyContent, + themeId, + ...directionProps + } = this.props; + const inlineStyles = pickBy({ + alignItems, + justifyContent, + }); + + const activeClasses = this._getActiveClasses(directionProps); + + const flexTheme = this._assembleFlexTheme(activeClasses); + + const fullTheme = this.state.composedTheme[themeId]; + return ( + + {this.renderChildren(fullTheme)} + + ); + } +} + +export const Flex = withTheme(FlexBase); diff --git a/source/renderer/react-polymorph/components/layout/FlexItem.js.map b/source/renderer/react-polymorph/components/layout/FlexItem.js.map new file mode 100644 index 0000000000..1bb0d50cb5 --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/FlexItem.js.map @@ -0,0 +1 @@ +{"version":3,"file":"FlexItem.js","sourceRoot":"","sources":["FlexItem.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,aAAa;AACb,iCAA8B;AAU9B,MAAa,QAAS,SAAQ,iBAAgB;IAC5C,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,MAAM,CAAC,YAAY,GAAG;QACpB,KAAK,EAAE,EAAE;KACV,CAAC;IAEF,MAAM;QACJ,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1E,OAAO,CACL,8BAAC,WAAI,IACH,aAAa,EAAE,CAAC,MAAM,CAAC,EACvB,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE;gBACZ,KAAK;gBACL,SAAS;gBACT,IAAI;aACL,EACD,WAAW,EAAE,KAAK,IAEjB,QAAQ,CACJ,CACR,CAAC;IACJ,CAAC;;AAvBH,4BAwBC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/layout/FlexItem.tsx b/source/renderer/react-polymorph/components/layout/FlexItem.tsx new file mode 100644 index 0000000000..6be0e4c0c1 --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/FlexItem.tsx @@ -0,0 +1,39 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { Node } from 'react'; +// components +import { Base } from './Base'; + +type Props = { + alignSelf?: string; + children?: Node | null | undefined; + className?: string; + flex?: number; + order?: number; + theme?: Record; +}; +export class FlexItem extends Component { + // define static properties + static displayName = 'FlexItem'; + static defaultProps = { + theme: {}, + }; + + render() { + const { children, className, alignSelf, flex, order, theme } = this.props; + return ( + + {children} + + ); + } +} diff --git a/source/renderer/react-polymorph/components/layout/Grid.js.map b/source/renderer/react-polymorph/components/layout/Grid.js.map new file mode 100644 index 0000000000..110f111b43 --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/Grid.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Grid.js","sourceRoot":"","sources":["Grid.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/layout/Grid.tsx b/source/renderer/react-polymorph/components/layout/Grid.tsx new file mode 100644 index 0000000000..7daeb78d6c --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/Grid.tsx @@ -0,0 +1,148 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { Node } from 'react'; +import { pickBy, isEmpty } from 'lodash'; +// components +import { Base } from './Base'; +// utilities +import { createEmptyContext, withTheme } from '../HOC/withTheme'; +import { numberToPx } from '../../utils/props'; +import { formatTemplateAreas } from '../../utils/layout'; +import { + composeTheme, + addThemeId, + didThemePropsChange, +} from '../../utils/themes'; +// constants +import { IDENTIFIERS } from '..'; +import type { ThemeContextProp } from '../HOC/withTheme'; + +type Props = { + alignItems?: string; + autoColumns?: string; + autoRows?: string; + className?: string; + center?: boolean; + children?: Node; + columnGap: string | number; + columns?: string; + context: ThemeContextProp; + gap: string | number; + justifyItems?: string; + rowGap: string | number; + rows?: string; + template?: string; + templateAreas: Array; + theme: Record | null | undefined; + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; +}; + +class GridBase extends Component { + // define static properties + static displayName = 'Grid'; + static defaultProps = { + columnGap: 5, + context: createEmptyContext(), + gap: 0, + rowGap: 5, + templateAreas: [], + theme: null, + themeId: IDENTIFIERS.GRID, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + // creates obj passed Base component's inline styles (see render) + _assembleInlineGrid = () => { + const { className, ...gridProps } = this.props; + + // return early if gridProps are empty + if (isEmpty(pickBy({ ...gridProps }))) { + return; + } + + const { + alignItems, + autoColumns, + autoRows, + center, + columnGap, + columns, + gap, + justifyItems, + rowGap, + rows, + template, + templateAreas, + } = gridProps; + // obj with correct css grid class names + const inlineClasses = { + alignItems: center ? 'center' : alignItems, + gridAutoColumns: autoColumns, + gridAutoRows: autoRows, + gridTemplateColumns: columns, + gridTemplateRows: rows, + gridColumnGap: gap ? false : numberToPx(columnGap), + gridGap: numberToPx(gap), + gridRowGap: gap ? false : numberToPx(rowGap), + gridTemplate: template, + gridTemplateAreas: formatTemplateAreas(templateAreas), + justifyItems: center ? 'center' : justifyItems, + }; + // filters out keys with false(sy) values + return pickBy(inlineClasses); + }; + + renderChildren(theme: Record) { + return React.Children.map(this.props.children, (child) => { + if (child.type.displayName === 'GridItem') { + return React.cloneElement(child, { + theme, + }); + } + + return child; + }); + } + + render() { + const { themeId, className } = this.props; + + const inlineGrid = this._assembleInlineGrid(); + + const theme = this.state.composedTheme[themeId]; + return ( + + {this.renderChildren(theme)} + + ); + } +} + +export const Grid = withTheme(GridBase); diff --git a/source/renderer/react-polymorph/components/layout/GridItem.js.map b/source/renderer/react-polymorph/components/layout/GridItem.js.map new file mode 100644 index 0000000000..b6505cec3e --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/GridItem.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GridItem.js","sourceRoot":"","sources":["GridItem.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,mCAAyC;AACzC,aAAa;AACb,iCAA8B;AAiB9B,MAAa,QAAS,SAAQ,iBAAgB;IAC5C,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,uBAAuB,GAAG,CAAC,aAAkC,EAAE,EAAE;QAC/D,sCAAsC;QACtC,IAAI,IAAA,gBAAO,EAAC,IAAA,eAAM,EAAC,EAAE,GAAG,aAAa,EAAE,CAAC,CAAC,EAAE;YACzC,OAAO;SACR;QAED,MAAM,EACJ,SAAS,EACT,MAAM,EACN,WAAW,EACX,SAAS,EACT,QAAQ,EACR,WAAW,EACX,SAAS,EACT,GAAG,EACH,MAAM,EACN,QAAQ,GACT,GAAG,aAAa,CAAC;QAClB,6CAA6C;QAC7C,MAAM,aAAa,GAAG;YACpB,SAAS;YACT,QAAQ;YACR,UAAU,EAAE,MAAM;YAClB,eAAe,EAAE,WAAW;YAC5B,aAAa,EAAE,SAAS;YACxB,OAAO,EAAE,GAAG;YACZ,UAAU,EAAE,MAAM;YAClB,YAAY,EAAE,QAAQ;YACtB,WAAW;YACX,SAAS;SACV,CAAC;QACF,yCAAyC;QACzC,OAAO,IAAA,eAAM,EAAC,aAAa,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,MAAM;QACJ,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,aAAa,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAEpE,MAAM,cAAc,GAAG,IAAI,CAAC,uBAAuB,CAAC,EAAE,GAAG,aAAa,EAAE,CAAC,CAAC;QAE1E,OAAO,CACL,8BAAC,WAAI,IACH,aAAa,EAAE,CAAC,MAAM,CAAC,EACvB,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,cAAc,EAC5B,WAAW,EAAE,KAAK,IAEjB,QAAQ,CACJ,CACR,CAAC;IACJ,CAAC;;AApDH,4BAqDC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/layout/GridItem.tsx b/source/renderer/react-polymorph/components/layout/GridItem.tsx new file mode 100644 index 0000000000..67cd6fc76e --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/GridItem.tsx @@ -0,0 +1,76 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { Node } from 'react'; +import { pickBy, isEmpty } from 'lodash'; +// components +import { Base } from './Base'; + +type Props = { + alignSelf?: string; + className?: string; + children?: Node | null | undefined; + column?: string | number; + columnStart?: string | number; + columnEnd?: string | number; + gridArea?: string; + justifySelf?: string; + placeSelf?: string; + row?: string | number; + rowEnd?: string | number; + rowStart?: string | number; + theme?: Record; +}; +export class GridItem extends Component { + static displayName = 'GridItem'; + _assembleInlineGridItem = (gridItemProps: Record) => { + // return early if gridProps are empty + if (isEmpty(pickBy({ ...gridItemProps }))) { + return; + } + + const { + alignSelf, + column, + columnStart, + columnEnd, + gridArea, + justifySelf, + placeSelf, + row, + rowEnd, + rowStart, + } = gridItemProps; + // obj with correct css grid-item class names + const inlineClasses = { + alignSelf, + gridArea, + gridColumn: column, + gridColumnStart: columnStart, + gridColumnEnd: columnEnd, + gridRow: row, + gridRowEnd: rowEnd, + gridRowStart: rowStart, + justifySelf, + placeSelf, + }; + // filters out keys with false(sy) values + return pickBy(inlineClasses); + }; + + render() { + const { children, className, theme, ...gridItemProps } = this.props; + + const inlineGridItem = this._assembleInlineGridItem({ ...gridItemProps }); + + return ( + + {children} + + ); + } +} diff --git a/source/renderer/react-polymorph/components/layout/Gutter.js.map b/source/renderer/react-polymorph/components/layout/Gutter.js.map new file mode 100644 index 0000000000..050e44361d --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/Gutter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Gutter.js","sourceRoot":"","sources":["Gutter.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,+CAAyC;AAEzC,aAAa;AACb,iCAA8B;AAC9B,oBAAoB;AACpB,gDAAiE;AACjE,+CAI4B;AAC5B,6CAA+C;AAC/C,YAAY;AACZ,0BAAiC;AAgBjC,MAAM,UAAW,SAAQ,iBAAuB;IAC9C,2BAA2B;IAC3B,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC;IAC9B,MAAM,CAAC,YAAY,GAAG;QACpB,OAAO,EAAE,IAAA,8BAAkB,GAAE;QAC7B,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,eAAW,CAAC,MAAM;QAC3B,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,YAAY,KAAY;QACtB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,IAAA,qBAAY,EACzB,IAAA,mBAAU,EAAC,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAC3C,IAAA,mBAAU,EAAC,cAAc,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,cAAc,CACvB;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,SAAgB;QACjC,IAAI,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;YAC5B,IAAA,4BAAmB,EAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACtE;IACH,CAAC;IAED,MAAM;QACJ,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5E,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,IAAA,kBAAU,EAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACjE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAChD,OAAO,CACL,8BAAC,WAAI,IACH,aAAa,EAAE,CAAC,QAAQ,CAAC,EACzB,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE;gBACZ,OAAO;aACR,EACD,WAAW,EAAE,KAAK,IAEjB,QAAQ,CACJ,CACR,CAAC;IACJ,CAAC;;AAGU,QAAA,MAAM,GAAG,IAAA,qBAAS,EAAC,UAAU,CAAC,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/components/layout/Gutter.tsx b/source/renderer/react-polymorph/components/layout/Gutter.tsx new file mode 100644 index 0000000000..182034bbd4 --- /dev/null +++ b/source/renderer/react-polymorph/components/layout/Gutter.tsx @@ -0,0 +1,78 @@ +// @ts-nocheck +import React, { Component } from 'react'; +import type { Node } from 'react'; +// components +import { Base } from './Base'; +// utility functions +import { createEmptyContext, withTheme } from '../HOC/withTheme'; +import { + composeTheme, + addThemeId, + didThemePropsChange, +} from '../../utils/themes'; +import { numberToPx } from '../../utils/props'; +// constants +import { IDENTIFIERS } from '..'; +import type { ThemeContextProp } from '../HOC/withTheme'; + +type Props = { + className?: string; + children?: Node; + context: ThemeContextProp; + padding?: string | number; + theme: Record | null | undefined; + themeId: string; + themeOverrides: Record; +}; +type State = { + composedTheme: Record; +}; + +class GutterBase extends Component { + // define static properties + static displayName = 'Gutter'; + static defaultProps = { + context: createEmptyContext(), + theme: null, + themeId: IDENTIFIERS.GUTTER, + themeOverrides: {}, + }; + + constructor(props: Props) { + super(props); + const { context, themeId, theme, themeOverrides } = props; + this.state = { + composedTheme: composeTheme( + addThemeId(theme || context.theme, themeId), + addThemeId(themeOverrides, themeId), + context.ROOT_THEME_API + ), + }; + } + + componentDidUpdate(prevProps: Props) { + if (prevProps !== this.props) { + didThemePropsChange(prevProps, this.props, this.setState.bind(this)); + } + } + + render() { + const { children, className, themeId, padding: inlinePadding } = this.props; + const padding = inlinePadding ? numberToPx(inlinePadding) : null; + const theme = this.state.composedTheme[themeId]; + return ( + + {children} + + ); + } +} + +export const Gutter = withTheme(GutterBase); diff --git a/source/renderer/react-polymorph/skins/simple/AutocompleteSkin.js.map b/source/renderer/react-polymorph/skins/simple/AutocompleteSkin.js.map new file mode 100644 index 0000000000..632865a9cd --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/AutocompleteSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AutocompleteSkin.js","sourceRoot":"","sources":["AutocompleteSkin.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/AutocompleteSkin.tsx b/source/renderer/react-polymorph/skins/simple/AutocompleteSkin.tsx new file mode 100644 index 0000000000..dcbbf19112 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/AutocompleteSkin.tsx @@ -0,0 +1,149 @@ +// @ts-nocheck +import React from 'react'; +import type { ElementRef } from 'react'; +// external libraries +import { slice, some } from 'lodash'; +import classnames from 'classnames'; +import type { AutocompleteProps } from '../../components/Autocomplete'; +// components +import { FormField } from '../../components/FormField'; +import { Options } from '../../components/Options'; +// skins +import { OptionsSkin } from './OptionsSkin'; + +type Props = AutocompleteProps & { + filteredOptions: Array; + getSelectionProps: (...args: Array) => any; + handleAutocompleteClick: (...args: Array) => any; + handleChange: (...args: Array) => any; + handleInputChange: (...args: Array) => any; + inputRef: ElementRef; + inputValue: string; + isOpen: boolean; + onKeyDown: (...args: Array) => any; + optionsRef: ElementRef; + optionsMaxHeight: number; + removeOption: (...args: Array) => any; + rootRef: ElementRef; + selectedOptions: Array; + suggestionsRef: ElementRef; + toggleMouseLocation: (...args: Array) => any; + toggleOpen: (...args: Array) => any; + optionHeight: number | null | undefined; +}; +export function AutocompleteSkin(props: Props) { + const theme = props.theme[props.themeId]; + const filteredAndLimitedOptions = slice( + props.filteredOptions, + 0, + props.maxVisibleOptions + ); + // show placeholder only if no maximum selections declared or maximum not reached + const canMoreOptionsBeSelected = + props.selectedOptions.length < props.maxSelections; + const placeholder = + !props.maxSelections || canMoreOptionsBeSelected ? props.placeholder : ''; + + const renderSelectedOptions = () => { + // check if the user passed a renderSelections function + if (props.selectedOptions && props.renderSelections) { + // call custom renderSelections function + return props.renderSelections(props.getSelectionProps); + } + + if (props.selectedOptions && !props.renderSelections) { + // render default skin + return props.selectedOptions.map((selectedOption, index) => ( + + + {selectedOption} + + × + + + + )); + } + + return null; + }; + + const selectedOptionsCount = props.selectedOptions.length; + const hasSelectedRequiredNumberOfOptions = + props.requiredSelections.length === 0 || + some( + props.requiredSelections, + (requiredCount) => selectedOptionsCount === requiredCount + ); + const error = hasSelectedRequiredNumberOfOptions ? props.error : null; + return ( +
    + {props.requiredSelections.length > 0 && + props.requiredSelectionsInfo != null && ( +
    + {props.requiredSelectionsInfo( + props.requiredSelections, + selectedOptionsCount + )} +
    + )} + ( +
    +
    + {renderSelectedOptions()} + +
    +
    + )} + /> + + +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/BubbleSkin.js.map b/source/renderer/react-polymorph/skins/simple/BubbleSkin.js.map new file mode 100644 index 0000000000..4b45ceb36d --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/BubbleSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"BubbleSkin.js","sourceRoot":"","sources":["BubbleSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,6BAA6B;AAC7B,6CAAiD;AAUjD,SAAgB,UAAU,CAAC,KAAY;IACrC,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC9D,MAAM,cAAc,GAAG,kBAAkB;QACvC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,YAAY;QAC7B,CAAC,CAAC,IAAI,CAAC;IACT,OAAO,CACL,uCACE,GAAG,EAAE,KAAK,CAAC,OAAO,KACd,IAAA,oBAAY,EAAC,KAAK,CAAC,EACvB,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;YACnB,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;YACxD,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;YACnD,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;YACvD,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;YACnD,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;YAC/C,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;SACxC,CAAC,EACF,KAAK,EACH,KAAK,CAAC,QAAQ,IAAI;YAChB,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS;YACpE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS;YAC9B,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK;SAC5B;QAGH,uCACE,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,2BACxC,MAAM;YAE3B,KAAK,CAAC,QAAQ;YACd,kBAAkB,IAAI,CACrB,wCACE,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,uBACZ,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAC7C,CACH,CACG;QACL,CAAC,kBAAkB,IAAI,CACtB,wCACE,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,uBACZ,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAC7C,CACH,CACG,CACP,CAAC;AACJ,CAAC;AA/CD,gCA+CC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/BubbleSkin.tsx b/source/renderer/react-polymorph/skins/simple/BubbleSkin.tsx new file mode 100644 index 0000000000..cec89593c0 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/BubbleSkin.tsx @@ -0,0 +1,64 @@ +// @ts-nocheck +import React from 'react'; +import type { Node, ElementRef } from 'react'; +// external libraries +import classnames from 'classnames'; +// internal utility functions +import { pickDOMProps } from '../../utils/props'; +import type { BubblePosition, BubbleProps } from '../../components/Bubble'; + +type Props = BubbleProps & { + children?: Node | null | undefined; + position: BubblePosition; + rootRef: ElementRef; + theme: Record; + themeId: string; +}; +export function BubbleSkin(props: Props) { + const { arrowRelativeToTip, noArrow, theme, themeId } = props; + const autoWidthClass = arrowRelativeToTip + ? theme[themeId].hasAutoWidth + : null; + return ( +
    +
    + {props.children} + {arrowRelativeToTip && ( + + )} +
    + {!arrowRelativeToTip && ( + + )} +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/ButtonSkin.js.map b/source/renderer/react-polymorph/skins/simple/ButtonSkin.js.map new file mode 100644 index 0000000000..f380b069ff --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ButtonSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ButtonSkin.js","sourceRoot":"","sources":["ButtonSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,6BAA6B;AAC7B,6CAAiD;AASjD,SAAgB,UAAU,CAAC,KAAY;IACrC,OAAO,CACL,6CACM,IAAA,oBAAY,EAAC,KAAK,CAAC,EACvB,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;YAC/B,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;SAC5D,CAAC,IAED,KAAK,CAAC,KAAK,CACL,CACV,CAAC;AACJ,CAAC;AAbD,gCAaC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/ButtonSkin.tsx b/source/renderer/react-polymorph/skins/simple/ButtonSkin.tsx new file mode 100644 index 0000000000..d9a28e9ece --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ButtonSkin.tsx @@ -0,0 +1,29 @@ +// @ts-nocheck +import React from 'react'; +import type { Element } from 'react'; +// external libraries +import classnames from 'classnames'; +// internal utility functions +import { pickDOMProps } from '../../utils/props'; + +type Props = { + className: string; + disabled: boolean; + label: string | Element; + theme: Record; + themeId: string; +}; +export function ButtonSkin(props: Props) { + return ( + + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/ButtonSpinnerSkin.js.map b/source/renderer/react-polymorph/skins/simple/ButtonSpinnerSkin.js.map new file mode 100644 index 0000000000..89b30a5ed1 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ButtonSpinnerSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ButtonSpinnerSkin.js","sourceRoot":"","sources":["ButtonSpinnerSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,8BAA8B;AAC9B,oEAAiE;AACjE,6DAA0D;AAC1D,6BAA6B;AAC7B,6CAAiD;AACjD,YAAY;AACZ,iDAA+C;AAU/C,SAAgB,iBAAiB,CAAC,KAAY;IAC5C,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC/D,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,wBAAW,CAAC,eAAe,CAAC,CAAC;IAE9D,MAAM,oBAAoB,GAAG,GAAG,EAAE,CAAC,CACjC,8BAAC,+BAAc,IAAC,IAAI,EAAE,uCAAkB,EAAE,KAAK,EAAE,YAAY,GAAI,CAClE,CAAC;IAEF,OAAO,CACL,6CACM,IAAA,oBAAY,EAAC,KAAK,CAAC,EACvB,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,SAAS;YACT,WAAW,CAAC,IAAI;YAChB,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;SACvC,CAAC,IAED,OAAO,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC,KAAK,CAClC,CACV,CAAC;AACJ,CAAC;AArBD,8CAqBC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/ButtonSpinnerSkin.tsx b/source/renderer/react-polymorph/skins/simple/ButtonSpinnerSkin.tsx new file mode 100644 index 0000000000..7808e83242 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ButtonSpinnerSkin.tsx @@ -0,0 +1,43 @@ +// @ts-nocheck +import React from 'react'; +import type { Element } from 'react'; +// external libraries +import classnames from 'classnames'; +// internal components & skins +import { LoadingSpinner } from '../../components/LoadingSpinner'; +import { LoadingSpinnerSkin } from './LoadingSpinnerSkin'; +// internal utility functions +import { pickDOMProps } from '../../utils/props'; +// constants +import { IDENTIFIERS } from '../../components'; + +type Props = { + className: string; + disabled: boolean; + label: string | Element; + loading: boolean; + theme: Record; + themeId: string; +}; +export function ButtonSpinnerSkin(props: Props) { + const { className, disabled, label, loading, themeId } = props; + const buttonTheme = props.theme[themeId]; + const spinnerTheme = props.theme[IDENTIFIERS.LOADING_SPINNER]; + + const renderLoadingSpinner = () => ( + + ); + + return ( + + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/CheckboxSkin.js.map b/source/renderer/react-polymorph/skins/simple/CheckboxSkin.js.map new file mode 100644 index 0000000000..16110ce523 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/CheckboxSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CheckboxSkin.js","sourceRoot":"","sources":["CheckboxSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,mCAA8B;AAC9B,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,6BAA6B;AAC7B,6CAAiD;AAWjD,SAAgB,YAAY,CAAC,KAAY;IACvC,OAAO,CACL,uCACE,IAAI,EAAC,cAAc,uBAEnB,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;YAC/B,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;YAC3D,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;SAC1D,CAAC,EACF,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,EAAE;gBACrC,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;aACvC;QACH,CAAC;QAED,4CACM,IAAA,oBAAY,EAAC,IAAA,aAAI,EAAC,KAAK,EAAE,UAAU,CAAC,CAAC,EACzC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAC3C,IAAI,EAAC,UAAU,EACf,QAAQ,SACR;QACF,uCACE,SAAS,EAAE,IAAA,oBAAU,EAAC;gBACpB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK;gBAChC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;aAC1D,CAAC,GACF;QACD,KAAK,CAAC,KAAK,IAAI,CACd,yCAAO,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,IAC/C,KAAK,CAAC,KAAK,CACN,CACT,CACG,CACP,CAAC;AACJ,CAAC;AApCD,oCAoCC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/CheckboxSkin.tsx b/source/renderer/react-polymorph/skins/simple/CheckboxSkin.tsx new file mode 100644 index 0000000000..8bb578e36d --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/CheckboxSkin.tsx @@ -0,0 +1,55 @@ +// @ts-nocheck +import { omit } from 'lodash'; +import React from 'react'; +import type { Element } from 'react'; +// external libraries +import classnames from 'classnames'; +// internal utility functions +import { pickDOMProps } from '../../utils/props'; + +type Props = { + checked: boolean; + className: string; + disabled: boolean; + onChange: (...args: Array) => any; + label: string | Element; + theme: Record; + themeId: string; +}; +export function CheckboxSkin(props: Props) { + return ( +
    { + if (!props.disabled && props.onChange) { + props.onChange(!props.checked, event); + } + }} + > + +
    + {props.label && ( + + )} +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/DropdownSkin.js.map b/source/renderer/react-polymorph/skins/simple/DropdownSkin.js.map new file mode 100644 index 0000000000..8164f7a1c8 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/DropdownSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DropdownSkin.js","sourceRoot":"","sources":["DropdownSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,aAAa;AACb,sDAAmD;AACnD,QAAQ;AACR,+CAA4C;AAsB5C,SAAgB,YAAY,CAAC,KAAY;IACvC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;IACtE,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,CACL,uCACE,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAC3D,YAAY,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAC1C,YAAY,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAC3C,GAAG,EAAE,KAAK,CAAC,OAAO;QAElB,uCACE,IAAI,EAAC,cAAc,uBAEnB,SAAS,EAAE,QAAQ,CAAC,KAAK,EACzB,OAAO,EAAE,KAAK,CAAC,YAAY,IAE1B,KAAK,CAAC,KAAK,CACR;QACN,8BAAC,iBAAO,IACN,UAAU,QACV,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,eAAe,EAAE,KAAK,CAAC,eAAe,EACtC,cAAc,EAAE,KAAK,CAAC,OAAO,EAC7B,yBAAyB,QACzB,QAAQ,EAAE,KAAK,CAAC,cAAc,EAC9B,OAAO,EAAE,KAAK,CAAC,KAAK,EACpB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EACxC,YAAY,EAAE,KAAK,CAAC,YAAY,EAChC,UAAU,EAAE,KAAK,CAAC,UAAU,EAC5B,cAAc,EAAE,KAAK,CAAC,cAAc,EACpC,cAAc,EAAE,KAAK,CAAC,UAAU,EAChC,qBAAqB,EAAE,iBAAiB,EACxC,IAAI,EAAE,yBAAW,EACjB,KAAK,EAAE,KAAK,CAAC,KAAK,GAClB,CACE,CACP,CAAC;AACJ,CAAC;AArCD,oCAqCC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/DropdownSkin.tsx b/source/renderer/react-polymorph/skins/simple/DropdownSkin.tsx new file mode 100644 index 0000000000..7b305631c7 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/DropdownSkin.tsx @@ -0,0 +1,68 @@ +// @ts-nocheck +import React from 'react'; +import type { Element, ElementRef } from 'react'; +// external libraries +import classnames from 'classnames'; +// components +import { Options } from '../../components/Options'; +// skins +import { OptionsSkin } from './OptionsSkin'; + +type Props = { + activeItem: any; + className: string; + isOpen: boolean; + isOpeningUpward: boolean; + items: Array; + label: string | Element; + noArrow?: boolean; + onItemSelected?: (...args: Array) => any; + onLabelClick: () => void; + optionsRef: ElementRef; + optionsMaxHeight: number; + optionRenderer?: (...args: Array) => any; + rootRef: ElementRef; + setMouseOverItems: (...args: Array) => any; + setMouseOverRoot: (...args: Array) => any; + theme: Record; + themeId: string; + optionHeight: number | null | undefined; +}; +export function DropdownSkin(props: Props) { + const { theme, themeId, setMouseOverItems, setMouseOverRoot } = props; + const themeApi = theme[themeId]; + return ( +
    setMouseOverRoot(true)} + onMouseLeave={() => setMouseOverRoot(false)} + ref={props.rootRef} + > +
    + {props.label} +
    + +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/FormFieldSkin.js.map b/source/renderer/react-polymorph/skins/simple/FormFieldSkin.js.map new file mode 100644 index 0000000000..a19c49ccb7 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/FormFieldSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"FormFieldSkin.js","sourceRoot":"","sources":["FormFieldSkin.tsx"],"names":[],"mappings":";;;;;;AAEA,kDAA0B;AAC1B,4DAAoC;AAEpC,sDAAmD;AACnD,yEAA+E;AAC/E,6CAA8D;AAO9D,SAAgB,aAAa,CAAC,KAAY;IACxC,MAAM,kBAAkB,GAAG,IAAA,iBAAS,EAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACzD,MAAM,kBAAkB,GAAG,IAAA,sBAAc,EAAC,KAAK,CAAC,YAAY,EAAE;QAC5D,EAAE,EAAE,OAAO;QACX,GAAG,EAAE,MAAM;KACZ,CAAC,CAAC;IACH,MAAM,kBAAkB,GAAG,IAAA,sBAAc,EAAC,KAAK,CAAC,YAAY,EAAE;QAC5D,EAAE,EAAE,YAAY;QAChB,GAAG,EAAE,YAAY;KAClB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC;IACrC,OAAO,CACL,uCACE,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;YAC/B,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;YAC3D,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;SACxD,CAAC,EACF,KAAK,EAAE,KAAK,CAAC,cAAc;QAE1B,KAAK,CAAC,KAAK,IAAI,CACd,yCACE,IAAI,EAAC,cAAc,uBAEnB,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAC3C,OAAO,EAAE,KAAK,CAAC,UAAU,EACzB,OAAO,EAAE,KAAK,CAAC,EAAE,IAEhB,KAAK,CAAC,KAAK,CACN,CACT;QACD,8BAAC,iBAAO,IACN,OAAO,EACL,KAAK,CAAC,YAAY,KAAK,IAAI;gBAC3B,CAAC,KAAK,CAAC,aAAa,KAAK,IAAI;oBAC3B,QAAQ;oBACR,CAAC,CAAC,KAAK,CAAC,qBAAqB,IAAI,kBAAkB,CAAC;wBAClD,CAAC,KAAK,CAAC,qBAAqB,IAAI,kBAAkB,CAAC,CAAC,CAAC,EAE3D,OAAO,EAAE,KAAK,CAAC,KAAK,EACpB,cAAc,EAAE;gBACd,wBAAwB,EAAE,OAAO,0CAAwB,CAAC,UAAU,EAAE;gBACtE,GAAG,KAAK,CAAC,cAAc;aACxB,EACD,SAAS,EAAC,QAAQ,EAClB,aAAa,EAAE;gBACb,SAAS,EAAE;oBACT;wBACE,IAAI,EAAE,MAAM;wBACZ,OAAO,EAAE,KAAK;qBACf;iBACF;aACF,EACD,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;YAElB,uCAAK,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,YAAY,IACpD,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAC7B,CACE,CACN,CACP,CAAC;AACJ,CAAC;AA9DD,sCA8DC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/FormFieldSkin.tsx b/source/renderer/react-polymorph/skins/simple/FormFieldSkin.tsx new file mode 100644 index 0000000000..de8b267875 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/FormFieldSkin.tsx @@ -0,0 +1,77 @@ +// @ts-nocheck +import type { ElementRef } from 'react'; +import React from 'react'; +import classnames from 'classnames'; +import type { FormFieldProps } from '../../components/FormField'; +import { PopOver } from '../../components/PopOver'; +import { SimpleFormFieldVariables } from '../../themes/simple/SimpleFormField'; +import { handleRefState, manageRef } from '../../utils/hooks'; + +type Props = FormFieldProps & { + formFieldRef: ElementRef; + focusChild: (...args: Array) => any; + setError: (...args: Array) => any; +}; +export function FormFieldSkin(props: Props) { + const updateOnRefChanges = manageRef(props.formFieldRef); + const isFormFieldFocused = handleRefState(props.formFieldRef, { + on: 'focus', + off: 'blur', + }); + const isFormFieldHovered = handleRefState(props.formFieldRef, { + on: 'mouseenter', + off: 'mouseleave', + }); + const hasError = props.error != null; + return ( +
    + {props.label && ( + + )} + +
    + {props.render(updateOnRefChanges)} +
    +
    +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/HeaderSkin.js.map b/source/renderer/react-polymorph/skins/simple/HeaderSkin.js.map new file mode 100644 index 0000000000..bf45867fe1 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/HeaderSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"HeaderSkin.js","sourceRoot":"","sources":["HeaderSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,4DAAoC;AAQpC,SAAgB,UAAU,CAAC,KAAY;IACrC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC3D,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,CACL,0CACE,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,SAAS,EAAE,GAAG,YAAY,CAAC,CAAC,EACnD,KAAK,EAAE,YAAY,IAElB,QAAQ,CACF,CACV,CAAC;AACJ,CAAC;AAXD,gCAWC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/HeaderSkin.tsx b/source/renderer/react-polymorph/skins/simple/HeaderSkin.tsx new file mode 100644 index 0000000000..6394a672fd --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/HeaderSkin.tsx @@ -0,0 +1,23 @@ +// @ts-nocheck +import React from 'react'; +import type { Node } from 'react'; +import classnames from 'classnames'; + +type Props = { + children?: Node | null | undefined; + className: string; + inlineStyles: Record; + theme: Record; +}; +export function HeaderSkin(props: Props) { + const { children, className, inlineStyles, theme } = props; + const themeClasses = Object.values(theme); + return ( +
    + {children} +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/InfiniteScrollSkin.js.map b/source/renderer/react-polymorph/skins/simple/InfiniteScrollSkin.js.map new file mode 100644 index 0000000000..29259ef6af --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/InfiniteScrollSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"InfiniteScrollSkin.js","sourceRoot":"","sources":["InfiniteScrollSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AAapC,SAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,IAAI,EACJ,KAAK,EACL,WAAW,EACX,SAAS,EACT,WAAW,EACX,kBAAkB,EAClB,KAAK,EACL,OAAO,GACD;IACN,OAAO,CACL,uCACE,GAAG,EAAE,kBAAkB,EACvB,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAEtD,WAAW,CAAC;QACX,IAAI;QACJ,KAAK;QACL,WAAW;QACX,SAAS;QACT,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC;KACtB,CAAC,CACE,CACP,CAAC;AACJ,CAAC;AAzBD,gDAyBC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/InfiniteScrollSkin.tsx b/source/renderer/react-polymorph/skins/simple/InfiniteScrollSkin.tsx new file mode 100644 index 0000000000..a7d4752624 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/InfiniteScrollSkin.tsx @@ -0,0 +1,43 @@ +// @ts-nocheck +import React from 'react'; +import type { ElementRef, Element } from 'react'; +// external libraries +import classnames from 'classnames'; + +type Props = { + className: string; + data: Record | Array<{}>; + error: boolean | string | Element; + hasMoreData: boolean; + isLoading: boolean; + renderItems: (...args: Array) => any; + scrollContainerRef: ElementRef; + theme: Record; + themeId: string; +}; +export function InfiniteScrollSkin({ + className, + data, + error, + hasMoreData, + isLoading, + renderItems, + scrollContainerRef, + theme, + themeId, +}: Props) { + return ( +
    + {renderItems({ + data, + error, + hasMoreData, + isLoading, + theme: theme[themeId], + })} +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/InputSkin.js.map b/source/renderer/react-polymorph/skins/simple/InputSkin.js.map new file mode 100644 index 0000000000..a409870cf6 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/InputSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"InputSkin.js","sourceRoot":"","sources":["InputSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAC1B,qBAAqB;AACrB,4DAAoC;AACpC,mCAAoC;AACpC,aAAa;AACb,0DAAuD;AAEvD,6BAA6B;AAC7B,6CAAiD;AAMjD,SAAgB,SAAS,CAAC,KAAY;IACpC,MAAM,WAAW,GAAG,CAAC,eAAe,EAAE,EAAE,CAAC,CACvC,yCACE,GAAG,EAAE,eAAe,KAChB,IAAA,oBAAY,EAAC,KAAK,CAAC,EACvB,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK;YAChC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;YAC3D,CAAC,KAAK,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,cAAc,CAAC;gBAC5D,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO;gBACpC,CAAC,CAAC,IAAI;SACT,CAAC,EACF,QAAQ,EAAE,KAAK,CAAC,QAAQ,GACxB,CACH,CAAC;IAEF,MAAM,oBAAoB,GAAG,CAAC,eAAe,EAAE,MAAM,EAAE,EAAE,CAAC,CACxD,uCAAK,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,kBAAkB;QAC1D,WAAW,CAAC,eAAe,CAAC;QAC7B,uCAAK,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,gBAAgB,IACxD,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CACjE,CACF,CACP,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,eAAe,EAAE,EAAE;QACjC,2DAA2D;QAC3D,MAAM,oBAAoB,GACxB,KAAK,CAAC,iBAAiB,IAAI,IAAA,mBAAU,EAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAEjE,IAAI,oBAAoB,EAAE;YACxB,OAAO,oBAAoB,CAAC,eAAe,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;SACpE;QAED,OAAO,WAAW,CAAC,eAAe,CAAC,CAAC;IACtC,CAAC,CAAC;IAEF,OAAO,CACL,8BAAC,qBAAS,IACR,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,EAAE,EAAE,KAAK,CAAC,EAAE,EACZ,qBAAqB,EAAE,KAAK,CAAC,qBAAqB,EAClD,qBAAqB,EAAE,KAAK,CAAC,qBAAqB,EAClD,YAAY,EAAE,KAAK,CAAC,QAAQ,EAC5B,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,KAAK,CAAC,cAAc,GACpC,CACH,CAAC;AACJ,CAAC;AApDD,8BAoDC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/InputSkin.tsx b/source/renderer/react-polymorph/skins/simple/InputSkin.tsx new file mode 100644 index 0000000000..319d8f6966 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/InputSkin.tsx @@ -0,0 +1,68 @@ +// @ts-nocheck +import React from 'react'; +// external libraries +import classnames from 'classnames'; +import { isFunction } from 'lodash'; +// components +import { FormField } from '../../components/FormField'; +import type { InputProps } from '../../components/Input'; +// internal utility functions +import { pickDOMProps } from '../../utils/props'; + +type Props = InputProps & { + theme: Record; + themeId: string; +}; +export function InputSkin(props: Props) { + const renderInput = (setFormFieldRef) => ( + + ); + + const useSelectionRenderer = (setFormFieldRef, option) => ( +
    + {renderInput(setFormFieldRef)} +
    + {option && props.selectionRenderer && props.selectionRenderer(option)} +
    +
    + ); + + const render = (setFormFieldRef) => { + // check if user has passed render prop "selectionRenderer" + const hasSelectionRenderer = + props.selectionRenderer && isFunction(props.selectionRenderer); + + if (hasSelectionRenderer) { + return useSelectionRenderer(setFormFieldRef, props.selectedOption); + } + + return renderInput(setFormFieldRef); + }; + + return ( + + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/LinkSkin.js.map b/source/renderer/react-polymorph/skins/simple/LinkSkin.js.map new file mode 100644 index 0000000000..150d14835f --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/LinkSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LinkSkin.js","sourceRoot":"","sources":["LinkSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAC1B,qBAAqB;AACrB,4DAAoC;AAapC,SAAgB,QAAQ,CAAC,KAAY;IACnC,MAAM,EACJ,KAAK,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACT,KAAK,EACL,OAAO,EACP,gBAAgB,GACjB,GAAG,KAAK,CAAC;IACV,OAAO,CACL,wCACE,IAAI,EAAC,cAAc,uBAEnB,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,SAAS;YACT,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;YACnB,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI;YACpD,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI;YAClD,YAAY,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;YACpE,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI;SAC3D,CAAC,EACF,OAAO,EAAE,OAAO,IAEf,KAAK,CACD,CACR,CAAC;AACJ,CAAC;AA7BD,4BA6BC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/LinkSkin.tsx b/source/renderer/react-polymorph/skins/simple/LinkSkin.tsx new file mode 100644 index 0000000000..464661c8a4 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/LinkSkin.tsx @@ -0,0 +1,46 @@ +// @ts-nocheck +import React from 'react'; +// external libraries +import classnames from 'classnames'; + +type Props = { + label: string; + isUnderlined: boolean; + underlineOnHover: boolean; + hasIconBefore: boolean; + hasIconAfter: boolean; + onClick?: (...args: Array) => any; + className: string; + theme: Record; + themeId: string; +}; +export function LinkSkin(props: Props) { + const { + label, + isUnderlined, + hasIconBefore, + hasIconAfter, + onClick, + className, + theme, + themeId, + underlineOnHover, + } = props; + return ( + + {label} + + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/LoadingSpinnerSkin.js.map b/source/renderer/react-polymorph/skins/simple/LoadingSpinnerSkin.js.map new file mode 100644 index 0000000000..86400b46b4 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/LoadingSpinnerSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LoadingSpinnerSkin.js","sourceRoot":"","sources":["LoadingSpinnerSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAC1B,qBAAqB;AACrB,4DAAoC;AASpC,SAAgB,kBAAkB,CAAC,KAAY;IAC7C,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACnD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IAC3C,OAAO,OAAO,CAAC,CAAC,CAAC,CACf,uCAAK,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,GAAI,CAC9D,CAAC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAPD,gDAOC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/LoadingSpinnerSkin.tsx b/source/renderer/react-polymorph/skins/simple/LoadingSpinnerSkin.tsx new file mode 100644 index 0000000000..7b1804556a --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/LoadingSpinnerSkin.tsx @@ -0,0 +1,20 @@ +// @ts-nocheck +import React from 'react'; +// external libraries +import classnames from 'classnames'; + +type Props = { + big: boolean; + className: string; + theme: Record; + themeId: string; + visible: boolean; +}; +export function LoadingSpinnerSkin(props: Props) { + const { big, className, themeId, visible } = props; + const theme = props.theme[themeId]; + const size = big ? theme.big : theme.small; + return visible ? ( +
    + ) : null; +} diff --git a/source/renderer/react-polymorph/skins/simple/ModalSkin.js.map b/source/renderer/react-polymorph/skins/simple/ModalSkin.js.map new file mode 100644 index 0000000000..5f61f06c4a --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ModalSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ModalSkin.js","sourceRoot":"","sources":["ModalSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,8DAAqC;AAWrC,SAAgB,SAAS,CAAC,KAAY;IACpC,OAAO,CACL,8BAAC,qBAAU,IACT,YAAY,EAAE,KAAK,CAAC,YAAY,EAChC,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,cAAc,EAAE,KAAK,CAAC,OAAO,EAC7B,yBAAyB,EAAE,KAAK,CAAC,0BAA0B,EAC3D,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAC3C,gBAAgB,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EACpD,WAAW,EAAE,KAAK,IAEjB,KAAK,CAAC,QAAQ,CACJ,CACd,CAAC;AACJ,CAAC;AAdD,8BAcC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/ModalSkin.tsx b/source/renderer/react-polymorph/skins/simple/ModalSkin.tsx new file mode 100644 index 0000000000..47ce21b357 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ModalSkin.tsx @@ -0,0 +1,30 @@ +// @ts-nocheck +import React from 'react'; +import type { Node, Element } from 'react'; +// external libraries +import ReactModal from 'react-modal'; + +type Props = { + children?: Node | null | undefined; + contentLabel: string | Element; + isOpen: boolean; + onClose: (...args: Array) => any; + triggerCloseOnOverlayClick: boolean; + theme: Record; + themeId: string; +}; +export function ModalSkin(props: Props) { + return ( + + {props.children} + + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/OptionsSkin.js.map b/source/renderer/react-polymorph/skins/simple/OptionsSkin.js.map new file mode 100644 index 0000000000..c0cb4970c1 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/OptionsSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"OptionsSkin.js","sourceRoot":"","sources":["OptionsSkin.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/OptionsSkin.tsx b/source/renderer/react-polymorph/skins/simple/OptionsSkin.tsx new file mode 100644 index 0000000000..71f57af5af --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/OptionsSkin.tsx @@ -0,0 +1,239 @@ +// @ts-nocheck +import React from 'react'; +import type { Element, ElementRef, RefObject } from 'react'; +// external libraries +import classnames from 'classnames'; +import { isFunction, isObject, escapeRegExp } from 'lodash'; +// components +import { Bubble } from '../../components/Bubble'; +import { Input } from '../../components/Input'; +import { ScrollBar } from '../../components/ScrollBar'; +// skins +import { BubbleSkin } from './BubbleSkin'; + +type Props = { + getOptionProps: (...args: Array) => any; + getHighlightedOptionIndex: (...args: Array) => any; + handleClickOnOption: (...args: Array) => any; + hasSearch?: boolean; + hideSearchClearButton?: boolean; + highlightSearch?: boolean; + isHighlightedOption: (...args: Array) => any; + isOpen: boolean; + isOpeningUpward: boolean; + isSelectedOption: (...args: Array) => any; + noOptionsArrow?: boolean; + noResults: boolean; + noResultsMessage: string | Element; + noSelectedOptionCheckmark?: boolean; + onClearSearchValue: (...args: Array) => any; + onSearch: (...args: Array) => any; + optionHeight: number; + optionRenderer: (...args: Array) => any; + options: Array; + optionsRef: ElementRef; + optionsMaxHeight: number; + render: (...args: Array) => any; + searchHeight: number; + searchInputRef?: RefObject; + searchValue: string; + selectedOption: any; + setHighlightedOptionIndex: (...args: Array) => any; + setMouseIsOverOptions?: (arg0: boolean) => void; + targetRef: ElementRef; + theme: Record; + themeId: string; +}; +export function OptionsSkin(props: Props) { + const { + getOptionProps, + getHighlightedOptionIndex, + handleClickOnOption, + hasSearch, + highlightSearch, + isHighlightedOption, + isOpen, + isOpeningUpward, + isSelectedOption, + noOptionsArrow, + noResults, + noResultsMessage, + noSelectedOptionCheckmark, + optionHeight, + optionsMaxHeight, + optionRenderer, + options, + optionsRef, + render, + searchHeight, + searchValue, + setHighlightedOptionIndex, + setMouseIsOverOptions, + targetRef, + theme, + themeId, + } = props; + const highlightedOptionIndex = getHighlightedOptionIndex(); + const isFirstOptionHighlighted = !hasSearch && highlightedOptionIndex === 0; + const sortedOptions = isOpeningUpward ? options.slice().reverse() : options; + + const renderOptions = () => { + // check for user's custom render function + // if Options is being rendered via Autocomplete, + // the value of props.render is renderOptions passed down from AutocompleteSkin + if (!noResults && render) { + // call user's custom render function + return render(getOptionProps); + } + + if (!noResults && !render) { + // render default simple skin + return sortedOptions.map((option, index) => { + // set reference of event handlers in memory to prevent excess re-renders + const boundSetHighlightedOptionIndex = setHighlightedOptionIndex.bind( + null, + index + ); + const boundHandleClickOnOption = handleClickOnOption.bind(null, option); + return ( +
  • + {renderOption(option)} +
  • + ); + }); + } + + // render no results message + return
  • {noResultsMessage}
  • ; + }; + + const renderOption = (option) => { + const escapedSearchValue = escapeRegExp(searchValue) || ''; + + // check if user has passed render prop "optionRenderer" + if (optionRenderer && isFunction(optionRenderer)) { + // call user's custom rendering logic + return optionRenderer(option); + } + + if (isObject(option)) { + let { label } = option; + + // in case `highlightSearch` then `searchValue` is wrapped in an `em` tag + if (highlightSearch !== false && escapedSearchValue !== '') { + const splitter = new RegExp(`(${escapedSearchValue})`, 'i'); + const parts = typeof label === 'string' ? label.split(splitter) : label; + + for (let i = 1; i < parts.length; i += 2) { + if ( + escapeRegExp(parts[i].toLowerCase()) === + `${escapedSearchValue}`.toLowerCase() + ) + parts[i] = {parts[i]}; + label = parts; + } + } + + return {label}; + } + + return option; + }; + + const renderSearch = () => { + return ( +
    + + {!props.hideSearchClearButton && ( +
    + ); + }; + + const getScrollBarHeight = (): number => { + if (!options.length) return optionHeight; + + if (optionsMaxHeight < options.length * optionHeight) { + return optionsMaxHeight; + } + + return options.length * optionHeight; + }; + + // Enforce max height of options dropdown if necessary + const optionsStyle = + optionsMaxHeight == null + ? null + : { + maxHeight: `${optionsMaxHeight}px`, + }; + return ( + + {hasSearch && renderSearch()} +
      + setMouseIsOverOptions && setMouseIsOverOptions(true) + } + onMouseLeave={() => + setMouseIsOverOptions && setMouseIsOverOptions(false) + } + > + + {renderOptions()} + +
    +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/PasswordInputSkin.js.map b/source/renderer/react-polymorph/skins/simple/PasswordInputSkin.js.map new file mode 100644 index 0000000000..61f43b6ede --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/PasswordInputSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PasswordInputSkin.js","sourceRoot":"","sources":["PasswordInputSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,4DAAoC;AACpC,kDAA0B;AAC1B,kDAA+C;AAC/C,kEAA+D;AAE/D,iFAAuF;AACvF,6CAAsE;AAOtE,SAAS,yBAAyB,CAAC,KAA0B;IAC3D,QAAQ,KAAK,EAAE;QACb,KAAK,6BAAa,CAAC,KAAK,CAAC,KAAK,CAAC;QAC/B,KAAK,6BAAa,CAAC,KAAK,CAAC,QAAQ;YAC/B,OAAO,OAAO,kDAA4B,CAAC,UAAU,GAAG,CAAC;QAE3D,KAAK,6BAAa,CAAC,KAAK,CAAC,IAAI;YAC3B,OAAO,OAAO,kDAA4B,CAAC,YAAY,GAAG,CAAC;QAE7D,KAAK,6BAAa,CAAC,KAAK,CAAC,MAAM;YAC7B,OAAO,OAAO,kDAA4B,CAAC,YAAY,GAAG,CAAC;QAE7D,KAAK,6BAAa,CAAC,KAAK,CAAC,OAAO,CAAC;QACjC;YACE,OAAO,6BAA6B,CAAC;KACxC;AACH,CAAC;AAED,SAAgB,iBAAiB,CAAC,KAAY;IAC5C,MAAM,EACJ,SAAS,EACT,KAAK,EACL,aAAa,EACb,uBAAuB,EACvB,uBAAuB,EACvB,aAAa,EACb,KAAK,EACL,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,KAAK,EACL,GAAG,UAAU,EACd,GAAG,KAAK,CAAC;IACV,MAAM,sBAAsB,GAAG,WAAW;QACxC,CAAC,CAAC,IAAA,yCAAiC,EAAC,KAAK,EAAE,aAAa,CAAC;QACzD,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,UAAU,GAAG,sBAAsB,IAAI,OAAO,IAAI,IAAI,CAAC;IAC7D,MAAM,UAAU,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,KAAK,KAAK,6BAAa,CAAC,KAAK,CAAC,KAAK,CAAC;IACzD,OAAO,CACL,uCACE,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;YACnB,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC;YACrB,SAAS;SACV,CAAC;QAEF,8BAAC,aAAK,OACA,UAAU,EACd,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAClC,cAAc,EAAE,UAAU,IAAI,YAAY,EAC1C,cAAc,EAAE,CAAC,YAAY,EAC7B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAC,UAAU,EACf,cAAc,EAAE;gBACd,wBAAwB,EAAE,UAAU;aACrC,GACD;QACF,uCAAK,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS;YACtC,uCACE,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAC/B,KAAK,EAAE;oBACL,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG;iBAChC,GACD,CACE,CACF,CACP,CAAC;AACJ,CAAC;AApDD,8CAoDC;AACD,oBAAoB;AACpB,iBAAiB,CAAC,WAAW,GAAG,mBAAmB,CAAC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/PasswordInputSkin.tsx b/source/renderer/react-polymorph/skins/simple/PasswordInputSkin.tsx new file mode 100644 index 0000000000..4143f593df --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/PasswordInputSkin.tsx @@ -0,0 +1,87 @@ +// @ts-nocheck +import classnames from 'classnames'; +import React from 'react'; +import { Input } from '../../components/Input'; +import { PasswordInput } from '../../components/PasswordInput'; +import type { PasswordInputProps } from '../../components/PasswordInput'; +import { SimplePasswordInputVariables } from '../../themes/simple/SimplePasswordInput'; +import { useDebouncedValueChangedIndicator } from '../../utils/hooks'; + +type Props = PasswordInputProps & { + score: number; + theme: Record; +}; + +function getPopOverBgColorForState(state: PasswordInput.STATE): string { + switch (state) { + case PasswordInput.STATE.ERROR: + case PasswordInput.STATE.INSECURE: + return `var(${SimplePasswordInputVariables.errorColor})`; + + case PasswordInput.STATE.WEAK: + return `var(${SimplePasswordInputVariables.warningColor})`; + + case PasswordInput.STATE.STRONG: + return `var(${SimplePasswordInputVariables.successColor})`; + + case PasswordInput.STATE.DEFAULT: + default: + return 'var(--rp-pop-over-bg-color)'; + } +} + +export function PasswordInputSkin(props: Props) { + const { + className, + error, + debounceDelay, + isShowingTooltipOnFocus, + isShowingTooltipOnHover, + isTooltipOpen, + score, + state, + theme, + themeId, + tooltip, + useDebounce, + value, + ...inputProps + } = props; + const hasInitialValueChanged = useDebounce + ? useDebouncedValueChangedIndicator(value, debounceDelay) + : true; + const hasTooltip = hasInitialValueChanged && tooltip != null; + const stateColor = getPopOverBgColorForState(state); + const isErrorState = state === PasswordInput.STATE.ERROR; + return ( +
    + +
    +
    +
    +
    + ); +} +// Static Properties +PasswordInputSkin.displayName = 'PasswordInputSkin'; diff --git a/source/renderer/react-polymorph/skins/simple/PopOverSkin.js.map b/source/renderer/react-polymorph/skins/simple/PopOverSkin.js.map new file mode 100644 index 0000000000..79a0d5f5bd --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/PopOverSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PopOverSkin.js","sourceRoot":"","sources":["PopOverSkin.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,2DAAmC;AACnC,mCAAkC;AAClC,4DAAoC;AAEpC,+CAAgD;AAGhD,MAAM,cAAc,GAAG,IAAA,kBAAU,EAC/B,CACE,KAGC,EACD,GAAG,EACH,EAAE;IACF,OAAO,CACL,wCAAM,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,IACvC,KAAK,CAAC,QAAQ,CACV,CACR,CAAC;AACJ,CAAC,CACF,CAAC;AACF,SAAgB,WAAW,CAAC,KAAmB;IAC7C,MAAM,EACJ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,aAAa,EACb,KAAK,EACL,OAAO,EACP,cAAc,EACd,cAAc,EACd,OAAO,EACP,GAAG,UAAU,EACd,GAAG,KAAK,CAAC;IACV,MAAM,UAAU,GACd,eAAK,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,IAAA,iBAAQ,EAAC,OAAO,CAAC,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC;IACzE,OAAO,CACL,8BAAC,eAAK,IACJ,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KACX,UAAU,EACd,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EACrC,OAAO,EACL,IAAA,iBAAQ,EAAC,OAAO,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAC/B,wCACE,uBAAuB,EAAE;gBACvB,MAAM,EAAE,OAAO;aAChB,GACD,CACH,CAAC,CAAC,CAAC,CACF,OAAO,CACR,EAEH,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EACvD,KAAK,EAAC,WAAW,EACjB,OAAO,EAAE;YACP;gBACE,6DAA6D;gBAC7D,IAAI,EAAE,cAAc;gBACpB,YAAY,EAAE,EAAE;gBAEhB,EAAE,CAAC,QAAgB;oBACjB,OAAO;wBACL,aAAa;4BACX,MAAM,EAAE,YAAY,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC;4BACxC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;gCACxC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;4BAC5D,CAAC,CAAC,CAAC;wBACL,CAAC;qBACF,CAAC;gBACJ,CAAC;aACF;SACF,EACD,aAAa,EAAE;YACb,GAAG,aAAa;YAChB,SAAS,EAAE;gBACT;oBACE,IAAI,EAAE,eAAe;oBACrB,OAAO,EAAE;wBACP,4DAA4D;wBAC5D,eAAe,EAAE,KAAK,EAAE,kBAAkB;qBAC3C;iBACF;gBACD;oBACE,IAAI,EAAE,iBAAiB;oBACvB,OAAO,EAAE;wBACP,8CAA8C;wBAC9C,OAAO,EAAE,CAAC;qBACX;iBACF;aACF,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,IAAI,EAAE,CAAC;SACxC,EACD,YAAY,EAAE,cAAc;QAE5B,8BAAC,cAAc,IAAC,SAAS,EAAE,gBAAgB,IAAG,QAAQ,CAAkB,CAClE,CACT,CAAC;AACJ,CAAC;AA7ED,kCA6EC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/PopOverSkin.tsx b/source/renderer/react-polymorph/skins/simple/PopOverSkin.tsx new file mode 100644 index 0000000000..aa2c85acc2 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/PopOverSkin.tsx @@ -0,0 +1,101 @@ +// @ts-nocheck +import Tippy from '@tippyjs/react'; +import { isString } from 'lodash'; +import classnames from 'classnames'; +import Popper from 'popper.js'; +import React, { forwardRef, Node } from 'react'; +import type { PopOverProps } from '../../components/PopOver'; + +const PopOverWrapper = forwardRef( + ( + props: { + children?: Node | null | undefined; + className?: string; + }, + ref + ) => { + return ( + + {props.children} + + ); + } +); +export function PopOverSkin(props: PopOverProps) { + const { + allowHTML, + children, + className, + content, + contentClassName, + popperOptions, + theme, + themeId, + themeOverrides, + themeVariables, + visible, + ...tippyProps + } = props; + const hasContent = + React.isValidElement(content) || (isString(content) && content !== ''); + return ( + + ) : ( + content + ) + } + className={classnames([theme[themeId].root, className])} + theme="polymorph" + plugins={[ + { + // Makes it possible to pass themeVariables props to PopOvers + name: 'cssVariables', + defaultValue: {}, + + fn(instance: Popper) { + return { + onAfterUpdate() { + const { cssVariables } = instance.props; + Object.keys(cssVariables).forEach((key) => { + instance.popper.style.setProperty(key, cssVariables[key]); + }); + }, + }; + }, + }, + ]} + popperOptions={{ + ...popperOptions, + modifiers: [ + { + name: 'computeStyles', + options: { + // Necessary to avoid sub-pixel rendering issues with arrows + gpuAcceleration: false, // true by default + }, + }, + { + name: 'preventOverflow', + options: { + // Keep a 4px distance from the viewport edges + padding: 4, + }, + }, + ].concat(popperOptions.modifiers ?? []), + }} + cssVariables={themeVariables} + > + {children} + + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/ProgressBarSkin.js.map b/source/renderer/react-polymorph/skins/simple/ProgressBarSkin.js.map new file mode 100644 index 0000000000..8b30e239cd --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ProgressBarSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ProgressBarSkin.js","sourceRoot":"","sources":["ProgressBarSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAC1B,qBAAqB;AACrB,4DAAoC;AASpC,SAAgB,eAAe,CAAC,KAAY;IAC1C,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,CACL,uCAAK,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAClD,uCACE,SAAS,EAAE,KAAK,CAAC,QAAQ,EACzB,KAAK,EAAE;gBACL,KAAK,EAAE,GAAG,QAAQ,GAAG;aACtB,GACD;QACF,uCAAK,SAAS,EAAE,KAAK,CAAC,KAAK,IACxB,QAAQ,KAAK,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CACrC,CACF,CACP,CAAC;AACJ,CAAC;AAhBD,0CAgBC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/ProgressBarSkin.tsx b/source/renderer/react-polymorph/skins/simple/ProgressBarSkin.tsx new file mode 100644 index 0000000000..f90e0dffd9 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ProgressBarSkin.tsx @@ -0,0 +1,29 @@ +// @ts-nocheck +import React from 'react'; +// external libraries +import classnames from 'classnames'; + +type Props = { + className: string; + label: string; + progress: number; + theme: Record; + themeId: string; +}; +export function ProgressBarSkin(props: Props) { + const { className, label, progress, themeId } = props; + const theme = props.theme[themeId]; + return ( +
    +
    +
    + {progress === 100 && label ? label : null} +
    +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/RadioSkin.js.map b/source/renderer/react-polymorph/skins/simple/RadioSkin.js.map new file mode 100644 index 0000000000..81c5653bb2 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/RadioSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RadioSkin.js","sourceRoot":"","sources":["RadioSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,6BAA6B;AAC7B,6CAAiD;AAajD,SAAgB,SAAS,CAAC,KAAY;IACpC,MAAM,EACJ,KAAK,EACL,OAAO,EACP,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,KAAK,GACN,GAAG,KAAK,CAAC;IACV,OAAO,CACL,uCACE,IAAI,EAAC,cAAc,uBAEnB,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,SAAS;YACT,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;YACnB,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;YACzC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;SAC1C,CAAC,EACF,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,IAAI,CAAC,QAAQ,IAAI,QAAQ,EAAE;gBACzB,QAAQ,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;aAC5B;QACH,CAAC;QAED,4CACM,IAAA,oBAAY,EAAC,KAAK,CAAC,EACvB,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAC/B,IAAI,EAAC,OAAO,GACZ;QACF,uCACE,SAAS,EAAE,IAAA,oBAAU,EAAC;gBACpB,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM;gBACrB,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;aAC1C,CAAC,GACF;QACD,KAAK,CAAC,CAAC,CAAC,yCAAO,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,IAAG,KAAK,CAAS,CAAC,CAAC,CAAC,IAAI,CACnE,CACP,CAAC;AACJ,CAAC;AAxCD,8BAwCC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/RadioSkin.tsx b/source/renderer/react-polymorph/skins/simple/RadioSkin.tsx new file mode 100644 index 0000000000..1c04d729f7 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/RadioSkin.tsx @@ -0,0 +1,60 @@ +// @ts-nocheck +import React from 'react'; +import type { Element } from 'react'; +// external libraries +import classnames from 'classnames'; +// internal utility functions +import { pickDOMProps } from '../../utils/props'; + +type Props = { + className: string; + disabled: boolean; + selected: boolean; + onBlur: (...args: Array) => any; + onChange: (...args: Array) => any; + onFocus: (...args: Array) => any; + label: string | Element; + theme: Record; + themeId: string; +}; +export function RadioSkin(props: Props) { + const { + theme, + themeId, + className, + disabled, + selected, + onChange, + label, + } = props; + return ( +
    { + if (!disabled && onChange) { + onChange(!selected, event); + } + }} + > + +
    + {label ? : null} +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/ScrollBarSkin.js.map b/source/renderer/react-polymorph/skins/simple/ScrollBarSkin.js.map new file mode 100644 index 0000000000..8780f766e5 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ScrollBarSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ScrollBarSkin.js","sourceRoot":"","sources":["ScrollBarSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAC1B,qBAAqB;AACrB,sFAAsD;AAStD,SAAgB,aAAa,CAAC,KAAY;IACxC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,CACL,uCAAK,SAAS,EAAE,KAAK,CAAC,IAAI;QACxB,8BAAC,iCAAe,IAAC,KAAK,EAAE,KAAK,EAAE,eAAe,UAC3C,QAAQ,CACO,CACd,CACP,CAAC;AACJ,CAAC;AAVD,sCAUC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/ScrollBarSkin.tsx b/source/renderer/react-polymorph/skins/simple/ScrollBarSkin.tsx new file mode 100644 index 0000000000..0c14c19713 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/ScrollBarSkin.tsx @@ -0,0 +1,23 @@ +// @ts-nocheck +import React from 'react'; +// external libraries +import CustomScrollBar from 'react-scrollbars-custom'; + +type Props = { + children: Node; + className: string; + style: Record; + theme: Record; + themeId: string; +}; +export function ScrollBarSkin(props: Props) { + const { children, style, themeId } = props; + const theme = props.theme[themeId]; + return ( +
    + + {children} + +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/SelectSkin.js.map b/source/renderer/react-polymorph/skins/simple/SelectSkin.js.map new file mode 100644 index 0000000000..232d43d482 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/SelectSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SelectSkin.js","sourceRoot":"","sources":["SelectSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,aAAa;AACb,sDAAmD;AACnD,kDAA+C;AAC/C,QAAQ;AACR,+CAA4C;AAC5C,2CAAwC;AAwCxC,SAAgB,UAAU,CAAC,KAAY;IACrC,MAAM,cAAc,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC;IACjD,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACjC,OAAO,CACL,uCACE,GAAG,EAAE,KAAK,CAAC,OAAO,EAClB,SAAS,EAAE,IAAA,oBAAU,EACnB,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EACrB,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EACrC,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,UAAU,EAClD,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAC1C;QAED,uCAAK,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,WAAW;YACxC,8BAAC,aAAK,IACJ,IAAI,EAAE,qBAAS,EACf,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,KAAK,CAAC,gBAAgB,EAC/B,WAAW,EAAE,KAAK,CAAC,WAAW,EAC9B,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,EAC1C,QAAQ,QACR,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,cAAc,EAAE,cAAc,GAC9B,CACE;QACN,8BAAC,iBAAO,IACN,IAAI,EAAE,yBAAW,EACjB,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,qBAAqB,EAAE,KAAK,CAAC,qBAAqB,EAClD,eAAe,EAAE,KAAK,CAAC,eAAe,EACtC,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,UAAU,EAAE,KAAK,CAAC,UAAU,EAC5B,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EACxC,OAAO,EAAE,KAAK,CAAC,OAAO,EACtB,eAAe,EAAE,KAAK,CAAC,eAAe,EACtC,QAAQ,EAAE,KAAK,CAAC,YAAY,EAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,cAAc,EAAE,KAAK,CAAC,cAAc,EACpC,cAAc,EAAE,cAAc,EAC9B,SAAS,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAChC,SAAS,EAAE,KAAK,CAAC,QAAQ,EACzB,mBAAmB,EAAE,KAAK,CAAC,mBAAmB,EAC9C,UAAU,EAAE,KAAK,CAAC,UAAU,EAC5B,YAAY,EAAE,KAAK,CAAC,YAAY,EAChC,YAAY,EAAE,KAAK,CAAC,YAAY,GAChC,CACE,CACP,CAAC;AACJ,CAAC;AAvDD,gCAuDC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/SelectSkin.tsx b/source/renderer/react-polymorph/skins/simple/SelectSkin.tsx new file mode 100644 index 0000000000..1c705eecf5 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/SelectSkin.tsx @@ -0,0 +1,106 @@ +// @ts-nocheck +import React from 'react'; +import type { Element, ElementRef } from 'react'; +// external libraries +import classnames from 'classnames'; +// components +import { Options } from '../../components/Options'; +import { Input } from '../../components/Input'; +// skins +import { OptionsSkin } from './OptionsSkin'; +import { InputSkin } from './InputSkin'; + +type Props = { + className: string; + disabled?: boolean; + error: string | Element; + getSelectedOption: (...args: Array) => any; + handleChange: (...args: Array) => any; + handleInputClick: (...args: Array) => any; + hasSearch?: boolean; + hideSearchClearButton?: boolean; + highlightSearch?: boolean; + inputRef: ElementRef<'input'>; + isOpen: boolean; + isOpeningUpward: boolean; + label: string | Element; + noResultsMessage?: string; + onBlur: (...args: Array) => any; + onChange: (...args: Array) => any; + onFocus: (...args: Array) => any; + onSearch?: (...args: Array) => any; + options: Array<{ + isDisabled: boolean; + value: any; + }>; + optionRenderer: (...args: Array) => any; + optionsRef: ElementRef; + optionsMaxHeight: number; + placeholder: string; + rootRef: ElementRef; + selectionRenderer?: (...args: Array) => any; + theme: Record; + // will take precedence over theme in context if passed + themeId: string; + toggleOpen: (...args: Array) => any; + toggleMouseLocation: (...args: Array) => any; + value: string; + optionHeight: number | null | undefined; + searchHeight: number | null | undefined; +}; +export function SelectSkin(props: Props) { + const selectedOption = props.getSelectedOption(); + const inputValue = selectedOption ? selectedOption.label : ''; + const { theme, themeId } = props; + return ( +
    +
    + +
    + +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/StepperSkin.js.map b/source/renderer/react-polymorph/skins/simple/StepperSkin.js.map new file mode 100644 index 0000000000..0b3ce3fb17 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/StepperSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"StepperSkin.js","sourceRoot":"","sources":["StepperSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAC1B,mBAAmB;AACnB,qBAAqB;AACrB,4DAAoC;AACpC,mCAAyC;AAYzC,SAAgB,WAAW,CAAC,KAAY;IACtC,IAAI,KAAK,CAAC;IACV,IAAI,CAAC,KAAK,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAE9B,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE;QACxB,MAAM,eAAe,GACnB,KAAK,CAAC,UAAU,IAAI,QAAQ,KAAK,CAAC,UAAU,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAC1E,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC;KACrD;IAED,OAAO,CACL,uCACE,IAAI,EAAC,cAAc,uBAEnB,SAAS,EAAE,IAAA,oBAAU,EAAC,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC;QAEzE,uCAAK,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO;YAC/C,KAAK,IAAI,CACR,uCAAK,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK;gBAC9C,0CAAK,KAAK,CAAM,CACZ,CACP;YACD,sCAAI,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,YAAY,IACnD,IAAA,YAAG,EAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;gBAChC,IAAI,SAAS,CAAC;gBAEd,IAAI,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,EAAE;oBAChC,SAAS,GAAG,UAAU,CAAC;iBACxB;qBAAM,IAAI,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC,UAAU,EAAE;oBACzC,SAAS,GAAG,QAAQ,CAAC;iBACtB;qBAAM;oBACL,SAAS,GAAG,UAAU,CAAC;iBACxB;gBAED,MAAM,eAAe,GAAG,CAAC,KAAuB,EAAE,EAAE;oBAClD,IAAI,KAAK,CAAC,WAAW,IAAI,IAAA,mBAAU,EAAC,KAAK,CAAC,WAAW,CAAC,EAAE;wBACtD,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;qBACvC;gBACH,CAAC,CAAC;gBAEF,OAAO,CACL,sCACE,GAAG,EAAE,KAAK,EACV,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,EAChD,KAAK,EAAE;wBACL,KAAK,EAAE,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG;qBACtC,EACD,IAAI,EAAC,cAAc,uBAEnB,OAAO,EAAE,eAAe,IAEvB,IAAI,CACF,CACN,CAAC;YACJ,CAAC,CAAC,CACC,CACD,CACF,CACP,CAAC;AACJ,CAAC;AA3DD,kCA2DC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/StepperSkin.tsx b/source/renderer/react-polymorph/skins/simple/StepperSkin.tsx new file mode 100644 index 0000000000..1bbd622654 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/StepperSkin.tsx @@ -0,0 +1,77 @@ +// @ts-nocheck +import React from 'react'; +// @ts-expect-error +// external libraries +import classnames from 'classnames'; +import { map, isFunction } from 'lodash'; + +type Props = { + activeStep?: number; + className: string; + label?: string; + labelDisabled?: boolean; + onStepClick?: (...args: Array) => any; + steps: Array; + theme: Record; + themeId: string; +}; +export function StepperSkin(props: Props) { + let label; + if (!props.steps) return null; + + if (!props.labelDisabled) { + const calculatedLabel = + props.activeStep && `STEP ${props.activeStep} OF ${props.steps.length}`; + label = props.label ? props.label : calculatedLabel; + } + + return ( +
    +
    + {label && ( +
    +

    {label}

    +
    + )} +
      + {map(props.steps, (step, index) => { + let classname; + + if (index + 1 < props.activeStep) { + classname = 'finished'; + } else if (index + 1 === props.activeStep) { + classname = 'active'; + } else { + classname = 'disabled'; + } + + const handleStepClick = (event: React.MouseEvent) => { + if (props.onStepClick && isFunction(props.onStepClick)) { + props.onStepClick(step, index, event); + } + }; + + return ( +
    • + {step} +
    • + ); + })} +
    +
    +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/SwitchSkin.js.map b/source/renderer/react-polymorph/skins/simple/SwitchSkin.js.map new file mode 100644 index 0000000000..c490c0a60d --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/SwitchSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SwitchSkin.js","sourceRoot":"","sources":["SwitchSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,6BAA6B;AAC7B,6CAAiD;AAWjD,SAAgB,UAAU,CAAC,KAAY;IACrC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACjC,OAAO,CACL,uCACE,IAAI,EAAC,cAAc,uBAEnB,SAAS,EAAE,IAAA,oBAAU,EAAC;YACpB,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI;YACnB,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;YAC/C,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;SAC9C,CAAC,EACF,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,EAAE;gBACrC,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;aACvC;QACH,CAAC;QAED,4CACM,IAAA,oBAAY,EAAC,KAAK,CAAC,EACvB,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAC/B,QAAQ,QACR,IAAI,EAAC,UAAU,GACf;QACF,uCACE,SAAS,EAAE,IAAA,oBAAU,EAAC;gBACpB,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM;gBACrB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;aAC9C,CAAC;YAEF,wCAAM,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,GAAI,CACrC;QACL,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CACb,yCAAO,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,IAAG,KAAK,CAAC,KAAK,CAAS,CAC9D,CAAC,CAAC,CAAC,IAAI,CACJ,CACP,CAAC;AACJ,CAAC;AArCD,gCAqCC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/SwitchSkin.tsx b/source/renderer/react-polymorph/skins/simple/SwitchSkin.tsx new file mode 100644 index 0000000000..2c565ac43d --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/SwitchSkin.tsx @@ -0,0 +1,55 @@ +// @ts-nocheck +import React from 'react'; +import type { Element } from 'react'; +// external libraries +import classnames from 'classnames'; +// internal utility functions +import { pickDOMProps } from '../../utils/props'; + +type Props = { + checked: boolean; + className: string; + disabled: boolean; + onChange: (...args: Array) => any; + label: string | Element; + theme: Record; + themeId: string; +}; +export function SwitchSkin(props: Props) { + const { theme, themeId } = props; + return ( +
    { + if (!props.disabled && props.onChange) { + props.onChange(!props.checked, event); + } + }} + > + +
    + +
    + {props.label ? ( + + ) : null} +
    + ); +} diff --git a/source/renderer/react-polymorph/skins/simple/TextAreaSkin.js.map b/source/renderer/react-polymorph/skins/simple/TextAreaSkin.js.map new file mode 100644 index 0000000000..2fc1f1c76e --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/TextAreaSkin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"TextAreaSkin.js","sourceRoot":"","sources":["TextAreaSkin.tsx"],"names":[],"mappings":";;;;;;AAAA,cAAc;AACd,kDAA0B;AAE1B,qBAAqB;AACrB,4DAAoC;AACpC,aAAa;AACb,0DAAuD;AACvD,QAAQ;AACR,mDAAgD;AAChD,2BAA2B;AAC3B,6CAAiD;AAiBjD,SAAgB,YAAY,CAAC,KAAY;IACvC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACjC,OAAO,CACL,8BAAC,qBAAS,IACR,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,YAAY,EAAE,KAAK,CAAC,WAAW,EAC/B,IAAI,EAAE,6BAAa,EACnB,MAAM,EAAE,CAAC,eAAe,EAAE,EAAE,CAAC,CAC3B,4CACE,GAAG,EAAE,eAAe,KAChB,IAAA,oBAAY,EAAC,KAAK,CAAC,EACvB,SAAS,EAAE,IAAA,oBAAU,EAAC;gBACpB,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ;gBACvB,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;gBAC/C,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;aAC5C,CAAC,GACF,CACH,GACD,CACH,CAAC;AACJ,CAAC;AAvBD,oCAuBC"} \ No newline at end of file diff --git a/source/renderer/react-polymorph/skins/simple/TextAreaSkin.tsx b/source/renderer/react-polymorph/skins/simple/TextAreaSkin.tsx new file mode 100644 index 0000000000..68f7cd2eb0 --- /dev/null +++ b/source/renderer/react-polymorph/skins/simple/TextAreaSkin.tsx @@ -0,0 +1,51 @@ +// @ts-nocheck +import React from 'react'; +import type { ElementRef, Element } from 'react'; +// external libraries +import classnames from 'classnames'; +// components +import { FormField } from '../../components/FormField'; +// skins +import { FormFieldSkin } from './FormFieldSkin'; +// import utility functions +import { pickDOMProps } from '../../utils/props'; + +type Props = { + className?: string; + disabled: boolean; + error?: string | Element; + label?: string | Element; + onBlur?: (...args: Array) => any; + onChange?: (...args: Array) => any; + onFocus?: (...args: Array) => any; + placeholder?: string; + rows: number; + textareaRef?: ElementRef<'textarea'>; + theme: Record; + themeId: string; + value: string; +}; +export function TextAreaSkin(props: Props) { + const { theme, themeId } = props; + return ( + ( +