From 934c85f8ca9829d15996b6ee2c89d924f6cbf22c Mon Sep 17 00:00:00 2001 From: Ali Beikverdi Date: Wed, 20 Jul 2022 11:34:35 +0900 Subject: [PATCH 001/132] cleanup repo --- .sequelizerc | 9 --- Dockerfile.webtest | 18 ------ LICENSE.md | 137 --------------------------------------------- README.md | 4 ++ 4 files changed, 4 insertions(+), 164 deletions(-) delete mode 100644 .sequelizerc delete mode 100644 Dockerfile.webtest delete mode 100644 LICENSE.md diff --git a/.sequelizerc b/.sequelizerc deleted file mode 100644 index 5dba470f5b..0000000000 --- a/.sequelizerc +++ /dev/null @@ -1,9 +0,0 @@ -const path = require('path'); - -// NOTE: __dirname doesn't work with binary building tools for nodejs projects -module.exports = { - 'config': path.resolve(__dirname, 'server/config', 'db.js'), - 'migrations-path': path.resolve(__dirname, 'server/db', 'migrations'), - 'models-path': path.resolve(__dirname, 'server/db', 'models'), - 'seeders-path': path.resolve(__dirname, 'server/db', 'seeders'), -}; diff --git a/Dockerfile.webtest b/Dockerfile.webtest deleted file mode 100644 index 171328f62d..0000000000 --- a/Dockerfile.webtest +++ /dev/null @@ -1,18 +0,0 @@ -FROM node:10.24.1-buster-slim - -RUN apt-get update && \ - apt-get install -y --no-install-recommends curl openssl ca-certificates git python build-essential && \ - rm -rf /var/lib/apt/lists/* && \ - npm config set unsafe-perm true && \ - npm install mocha -g --loglevel=error - -ENV NODE_ENV=production - -COPY . /app - -WORKDIR /app - -RUN npm install --save chai - -ENTRYPOINT ["mocha"] -CMD ["test/selenium/Scenario/newUser.js"] diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 2719a4b60a..0000000000 --- a/LICENSE.md +++ /dev/null @@ -1,137 +0,0 @@ -# HollaEx Kit License - -This License Agreement (Agreement) is between HollaEx. and its subsidiaries and affiliated companies (“HollaEx,” ‘’The Company’’, “we” or “us”), and the entity or individual entering into this Agreement (User, Users). This HollaEx Kit software, materials and documentation provided to User (Software, materials, products, Works) are licensed and are not directly sold or assigned in any manner. -This Agreement is part of a package that includes the Software, materials and certain electronic and/or written content. -This Agreement covers the permitted use of the licensed materials, products and/or the Software provided by us. By "using" our products the user acknowledges and agrees that she/he has read all the terms and conditions of this agreement, understand them, and agree to be legally bound by them. -HollaEx Kit includes tools and services to develop and run a crypto exchange node all subject to this license. If the user does not agree with the terms of this license, she/he may not use any of our licensed products. - -## 1. SCOPE. - -This Agreement describes the licensing of our products provided to User on a non-commercial basis. You are not allowed to take our materials and commercialize them as it were developed by you. -If User desires to use our products on a commercial basis, it must notify us and provide a potential offer. The provision of a potential offer does not guarantee our acceptance of such a proposal. - -## 2. LICENSE. - -Subject to the user’s compliance with all of the terms and conditions of this Agreement, we grant to our users a revocable, non-exclusive, non-transferable, non-sublicensable, non-commercial perpetual right and license (i) for Designated User(s) to use our products to contribute and create Modifications and Applications. - -The user may: - -Copy and Contribute to the Work. -Create one or more Derivative Works. -Incorporate the Work into one or more Collective Works. -Copy Derivative Works or the Work as incorporated in any Collective Work; and -Publish, distribute, archive, perform or otherwise disseminate the Work, Derivative Works or the Work as incorporated in any Collective Work, to the public in any material form in any media whether now known or hereafter created. - -The user must do: - -When the user uses the source code, she/he must retain the above copyright notice, this list of conditions and the following disclaimer. -The user must give any other recipients of the Work or Derivative Works a copy of this License; and -The user must cause any modified files to carry prominent notices stating that it changed the files; and -The user must retain, in the Source form of any Derivative Works that she/he distributes, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works. -The user must retain all attribution to us regarding any Derivative Work. -The user may add its own copyright statement to the modifications performed and may provide additional or different license terms and conditions for use, reproduction, or distribution of the modifications, or for any such Derivative Works as a whole, provided the use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. -Wallet Integration: the wallet integration is made with HollaEx Network, that allows our HollaExKit function properly. By using HollaExKit you need to comply with HollaEx Network requests as a proprietary software. - -The user is not permitted to: - -Impose any terms on the use to be made of the Work, the Derivative Work or the Work as incorporated in a Collective Work that alter or restrict the terms of this License or any rights granted under it or has the effect or intent of restricting the ability to exercise those rights. -Impose any digital rights management technology on the Work, the Derivative Work or the Work as incorporated in a Collective Work that alters or restricts the terms of this License or any rights granted under it or has the effect or intent of restricting the ability to exercise those rights. -Sublicense, sell, resell, or commercialize or Software without our permission. - -## 3. NO ENDORSEMENT. - -Nothing in this Public License constitutes or may be construed as permission to assert or imply that the user is, or that the use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution. - -## 4. OTHER RIGHTS. - -We reserve ownership of all intellectual property rights inherent in or relating to the Software or any material, which include, but are not limited to, all copyright, patent rights, all rights in relation to registered and unregistered trademarks (including service marks), confidential information (including trade secrets and know-how) and all rights other than those expressly granted by this Agreement. - -Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow users to exercise the Licensed Rights, but not otherwise. - -Patent and trademark rights are not licensed under this Public License. - -To the extent possible, this Company waives any right to collect royalties from users under this non-commercial license, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases this Company expressly reserves any right to collect such royalties, including when the Licensed Material is used for Commercial purposes. - -## 5. SUBMISSION OF CONTRIBUTIONS. - -Unless the user explicitly states otherwise, any Contribution intentionally submitted for inclusion in the Work by the user to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement users may have executed with Licensor regarding such Contributions. - -## 6. TRADEMARKS. - -This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the contributions and reproducing the content of the NOTICE file. - -## 7. DISCLAIMER OF WARRANTY. - -Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. The user is solely responsible for determining the appropriateness of using the software, or any other material and assume any risks associated with its exercise of permissions under this License. - -## 8. LIMITATION OF LIABILITY. - -In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall HollaEx be liable to the users for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, loss of data, work stoppage, computer failure or malfunction, business interruption or any and all other damages or losses), even if HollaEx has been advised of the possibility of such damages. - -## 9. RESTRICTIONS. - -The license granted to the users is expressly made subject to and limited by the following restrictions: - -Users agree not to remove any of the original copyright, patent, trademark, and attribution notices and associated disclaimers that may appear in the Source Code or Executable Files. - -Users agree not to advertise or in any way imply that this Work is a product of their own. - -The name of this company may not be used to endorse or promote products derived from the Work made by users without our prior written consent. - -Users agree not to sell, lease, or rent any part of the Work. This does not restrict users from including the Work or any part of the Work inside a larger products distribution that itself is being sold. The Work by itself, though, cannot be sold, leased, or rented. - -Users may use the Executable Files and Source Code only under the terms of this License, and they must include a copy of, or the Uniform Resource Identifier for, this License with every copy of the Executable Files or Source Code users disclose and ensure that anyone receiving such Executable Files and Source Code agrees that the terms of this License apply to such Executable Files and/or Source Code. - -Users may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. Users may not sublicense the Work. - -Users must keep intact all notices that refer to this License and to the disclaimer of warranties. Users may not distribute the Executable Files or Source Code with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License. - -Users agree not to use the Work for illegal, immoral, or improper purposes, or on pages containing illegal, immoral, or improper material. The Work is subject to applicable export laws. Users agree to comply with all such laws and regulations that may apply to the Work after their receipt of the Work. - -## 10. REPRESENTATIONS, WARRANTIES AND DISCLAIMER. - -This work is provided "AS IS", "where is" and "as available", without any express or implied warranties or conditions or guarantees. The users, assume all risk in its use, including copyright infringement, patent infringement, suitability, etc. This company expressly disclaims all express, implied, or statutory warranties or conditions, including without limitation, warranties or conditions of merchantability, merchantable quality or fitness for a particular purpose, or any warranty of title or non-infringement, or that the work (or any portion thereof) is correct, useful, bug-free, or free of viruses. The users must pass this disclaimer on whenever users distribute the work or derivative works. - -## 11. INDEMNITY. - -The users agree to defend, indemnify, and hold harmless this company from and against any claims, suits, losses, damages, liabilities, costs, and expenses (including reasonable legal or attorneys' fees) resulting from or relating to any use of the Work by users. - -## 12. TERMINATION. - -This License and the rights granted hereunder will terminate automatically upon any breach by users of any term of this License. Individuals or entities who have received Derivative Works from users under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. - -Subject to the above terms and conditions, this License is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, this company reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. - -## 13. COMMERCIAL USE. - -Rights granted herein apply only to those using our products for non-commercial purposes. In the case an individual or a company desires to use our products for commercial purposes, or if there is existence of a potential commercial opportunity, the interested party must contact our company to inform about the proposal and pay the applicable fee for the commercial license offered under that new agreement. This company reserves the right to accept or decline such a request. - -## 14. COPYRIGHT (C) 2022 HollaEx. - -Permission is hereby granted, free of charge, to any person obtaining a copy of our products, and associated documentation files, to deal in our products without restriction, including the rights to use, copy, modify, merge, publish, distribute, with a limitation to sublicense, and/or sell copies of our products, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of our products. This copyright notice is extended to all the products produced by HollaEx including, but not limited to: HollaEx Kit, HollaEx Network: (HollaEx Core), Vault, and Robolla. - -THE PRODUCTS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE PRODUCTS OR THE USE OR OTHER DEALINGS RELATED TO THE PRODUCTS. - -## 15. MISCELLANEOUS. - -If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this License, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. - -No term or provision of this License shall be deemed waived, and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. - -This License constitutes the entire agreement between the parties with respect to the products licensed herein. There are no understandings, agreements, or representations with respect to the products not specified herein. The Company shall not be bound by any additional provisions that may appear in any communication from the users. - -The work (as defined below) is provided under the terms of this creative commons public license ("CCPL" or "license"). The work is protected by copyright and/or other applicable law. Any use of the work other than as authorized under this license or copyright law is prohibited. By exercising any rights to the work provided here, users accept and agree to be bound by the terms of this license. The licensor grants users the rights contained here in consideration of their acceptance of such terms and conditions. - -Compliance with Law. User represents and warrants that it will comply in all material respects with all local, state, and international laws and regulations relating to its activities hereunder, including the development, and fair use of Licensed Products. Without limiting the foregoing, Licensee represents and warrants that it shall comply with all laws and regulations controlling the export of technical data. - -Support. The Company will provide to Users the following support with respect to the Products: (i) If during the term of this Agreement, user notifies this company of a substantial program error respecting any of our products, or this Company has reason to believe that error exists in the products and so notifies Licensee, Licensor shall at its expense verify and attempt to correct such error. If the User is not satisfied with the correction provided, then he/she may terminate this Agreement, without the right to any monetary or material claim; and (ii) In the case that User has technical questions in the use of our products. - -This Creative Commons Public License enables the users to view, edit, and modify, works worldwide, under the terms of this license, provided that, users credit the Original Author (HollaEx). - -This License may not be modified without the mutual written agreement of this Company. - - - - diff --git a/README.md b/README.md index 5e97666c92..2abdd8dfe1 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ Check out [Plugins](https://github.com/hollaex/hollaex-kit/tree/2.0-develop/serv Check out [HollaEx CLI](https://github.com/hollaex/hollaex-cli) (Command Line Interface) for interacting and deploying your exchange. +Check out [https://github.com/hollaex/hollaex-kit/tree/master/server/utils/hollaex-network-lib] (HollaEx Network Library) for tools and functions used to communicate with HollaEx Network. + +Check out [https://github.com/hollaex/hollaex-kit/tree/master/server/utils/hollaex-tools-lib] (HollaEx Tools Library) for developers as a suite of all useful functions in the HollaEx Kit. + ## Community Join us on the [Forum](https://forum.hollaex.com), [Discord](https://discord.gg/RkRHU8RbyM) and [Twitter](http://www.twitter.com/hollaex). From ac4db22c8e642e899643e62eea9d04d0b204928c Mon Sep 17 00:00:00 2001 From: Ali Beikverdi Date: Wed, 20 Jul 2022 11:36:31 +0900 Subject: [PATCH 002/132] link fixes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2abdd8dfe1..d6b94af334 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ Check out [Plugins](https://github.com/hollaex/hollaex-kit/tree/2.0-develop/serv Check out [HollaEx CLI](https://github.com/hollaex/hollaex-cli) (Command Line Interface) for interacting and deploying your exchange. -Check out [https://github.com/hollaex/hollaex-kit/tree/master/server/utils/hollaex-network-lib] (HollaEx Network Library) for tools and functions used to communicate with HollaEx Network. +Check out [HollaEx Network Library](https://github.com/hollaex/hollaex-kit/tree/master/server/utils/hollaex-network-lib) for tools and functions used to communicate with HollaEx Network. -Check out [https://github.com/hollaex/hollaex-kit/tree/master/server/utils/hollaex-tools-lib] (HollaEx Tools Library) for developers as a suite of all useful functions in the HollaEx Kit. +Check out [HollaEx Tools Library](https://github.com/hollaex/hollaex-kit/tree/master/server/utils/hollaex-tools-lib) for developers as a suite of all useful functions in the HollaEx Kit. ## Community Join us on the [Forum](https://forum.hollaex.com), [Discord](https://discord.gg/RkRHU8RbyM) and [Twitter](http://www.twitter.com/hollaex). From fa099724babcc9d974f1d8900ee8a5f9c2faf052 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar Date: Wed, 24 Aug 2022 17:43:30 +0430 Subject: [PATCH 003/132] embedded plugins system initial commit --- .gitignore | 3 + package-lock.json | 233 + package.json | 10 + plugins/.babelrc | 42 + plugins/.eslintignore | 2 + plugins/.eslintrc.json | 35 + plugins/.eslintrc.yml | 3 + plugins/LICENSE | 21 + plugins/README.md | 58 + plugins/README_WEB_VIEW.md | 155 + plugins/generateJson.js | 80 + plugins/media/icon-large.png | Bin 0 -> 6114 bytes plugins/media/icon-small.png | Bin 0 -> 4073 bytes plugins/package-lock.json | 9421 +++++++++++++++++ plugins/package.json | 108 + plugins/remote-component.config.js | 25 + plugins/scripts/addBundles.js | 28 + plugins/scripts/addJsons.js | 24 + plugins/scripts/addPlugin.js | 66 + plugins/scripts/addView.js | 36 + plugins/scripts/generateJson.js | 51 + plugins/scripts/patterns.js | 70 + plugins/scripts/utils.js | 39 + plugins/server.js | 10 + .../src/components/Dialog/DesktopDialog.js | 87 + plugins/src/components/Dialog/MobileDialog.js | 73 + plugins/src/components/Dialog/index.js | 14 + plugins/src/components/Form/factoryFields.js | 27 + plugins/src/components/HeaderSection/index.js | 45 + .../KitContext/KitContextProvider.js | 15 + plugins/src/components/KitContext/index.js | 4 + plugins/src/components/KitContext/withKit.js | 17 + plugins/src/components/OtpForm/index.js | 93 + plugins/src/components/Tab/index.js | 30 + plugins/src/components/Title.js | 11 + plugins/src/constants/index.js | 1 + plugins/src/index.html | 9 + plugins/src/lib/__tests__/prop.test.js | 39 + plugins/src/lib/prop.js | 10 + .../plugins/hello-exchange/assets/icons.json | 3 + .../hello-exchange/assets/strings.json | 6 + .../plugins/hello-exchange/server/config.json | 35 + .../hello-exchange/server/hello-exchange.json | 40 + .../plugins/hello-exchange/server/script.js | 44 + .../plugins/hello-exchange/views/view/App.js | 20 + .../plugins/hello-exchange/views/view/Form.js | 37 + .../hello-exchange/views/view/index.js | 1 + .../hello-exchange/views/view/view.json | 18 + plugins/src/store/index.js | 11 + plugins/src/templates/assets/icons.json | 3 + plugins/src/templates/assets/strings.json | 5 + .../bank-verification/assets/icons.json | 3 + .../bank-verification/assets/strings.json | 8 + .../bank-verification/views/home/App.js | 20 + .../bank-verification/views/home/Form.js | 32 + .../bank-verification/views/home/index.js | 1 + .../bank-verification/views/home/view.json | 4 + .../views/verification/App.js | 20 + .../views/verification/Form.js | 54 + .../views/verification/index.js | 1 + .../views/verification/view.json | 4 + .../templates/fiat-wallet/assets/icons.json | 3 + .../templates/fiat-wallet/assets/strings.json | 5 + .../fiat-wallet/views/deposit/App.js | 20 + .../fiat-wallet/views/deposit/Form.js | 26 + .../fiat-wallet/views/deposit/index.js | 1 + .../fiat-wallet/views/deposit/view.json | 7 + .../fiat-wallet/views/withdraw/App.js | 20 + .../fiat-wallet/views/withdraw/Form.js | 26 + .../fiat-wallet/views/withdraw/index.js | 1 + .../fiat-wallet/views/withdraw/view.json | 7 + .../kyc-verification/assets/icons.json | 3 + .../kyc-verification/assets/strings.json | 8 + .../kyc-verification/views/home/App.js | 20 + .../kyc-verification/views/home/Form.js | 32 + .../kyc-verification/views/home/index.js | 1 + .../kyc-verification/views/home/view.json | 4 + .../views/verification/App.js | 20 + .../views/verification/Form.js | 56 + .../views/verification/index.js | 1 + .../views/verification/view.json | 4 + .../src/templates/new-page/assets/icons.json | 3 + .../templates/new-page/assets/strings.json | 6 + .../src/templates/new-page/views/view/App.js | 20 + .../src/templates/new-page/views/view/Form.js | 37 + .../templates/new-page/views/view/index.js | 1 + .../templates/new-page/views/view/view.json | 18 + .../src/templates/onramp/assets/icons.json | 3 + .../src/templates/onramp/assets/strings.json | 6 + .../src/templates/onramp/views/view/App.js | 20 + .../src/templates/onramp/views/view/Form.js | 23 + .../src/templates/onramp/views/view/index.js | 1 + .../src/templates/onramp/views/view/view.json | 6 + .../verification-tab/assets/icons.json | 3 + .../verification-tab/assets/strings.json | 8 + .../verification-tab/views/home/App.js | 20 + .../verification-tab/views/home/Form.js | 32 + .../verification-tab/views/home/index.js | 1 + .../verification-tab/views/home/view.json | 14 + .../views/verification/App.js | 20 + .../views/verification/Form.js | 56 + .../views/verification/index.js | 1 + .../views/verification/view.json | 6 + plugins/src/templates/view/App.js | 20 + plugins/src/templates/view/index.js | 1 + plugins/src/templates/view/view.json | 4 + plugins/src/utils/index.js | 154 + plugins/src/utils/link.js | 11 + plugins/test.js | 917 ++ plugins/webpack-dev.config.js | 96 + plugins/webpack-main.config.js | 90 + plugins/webpack.config.js | 3 + 112 files changed, 13231 insertions(+) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 plugins/.babelrc create mode 100644 plugins/.eslintignore create mode 100644 plugins/.eslintrc.json create mode 100644 plugins/.eslintrc.yml create mode 100644 plugins/LICENSE create mode 100644 plugins/README.md create mode 100644 plugins/README_WEB_VIEW.md create mode 100644 plugins/generateJson.js create mode 100644 plugins/media/icon-large.png create mode 100644 plugins/media/icon-small.png create mode 100644 plugins/package-lock.json create mode 100644 plugins/package.json create mode 100644 plugins/remote-component.config.js create mode 100644 plugins/scripts/addBundles.js create mode 100644 plugins/scripts/addJsons.js create mode 100644 plugins/scripts/addPlugin.js create mode 100644 plugins/scripts/addView.js create mode 100644 plugins/scripts/generateJson.js create mode 100644 plugins/scripts/patterns.js create mode 100644 plugins/scripts/utils.js create mode 100644 plugins/server.js create mode 100644 plugins/src/components/Dialog/DesktopDialog.js create mode 100644 plugins/src/components/Dialog/MobileDialog.js create mode 100644 plugins/src/components/Dialog/index.js create mode 100644 plugins/src/components/Form/factoryFields.js create mode 100644 plugins/src/components/HeaderSection/index.js create mode 100644 plugins/src/components/KitContext/KitContextProvider.js create mode 100644 plugins/src/components/KitContext/index.js create mode 100644 plugins/src/components/KitContext/withKit.js create mode 100644 plugins/src/components/OtpForm/index.js create mode 100644 plugins/src/components/Tab/index.js create mode 100644 plugins/src/components/Title.js create mode 100644 plugins/src/constants/index.js create mode 100644 plugins/src/index.html create mode 100644 plugins/src/lib/__tests__/prop.test.js create mode 100644 plugins/src/lib/prop.js create mode 100644 plugins/src/plugins/hello-exchange/assets/icons.json create mode 100644 plugins/src/plugins/hello-exchange/assets/strings.json create mode 100644 plugins/src/plugins/hello-exchange/server/config.json create mode 100644 plugins/src/plugins/hello-exchange/server/hello-exchange.json create mode 100644 plugins/src/plugins/hello-exchange/server/script.js create mode 100644 plugins/src/plugins/hello-exchange/views/view/App.js create mode 100644 plugins/src/plugins/hello-exchange/views/view/Form.js create mode 100644 plugins/src/plugins/hello-exchange/views/view/index.js create mode 100644 plugins/src/plugins/hello-exchange/views/view/view.json create mode 100644 plugins/src/store/index.js create mode 100644 plugins/src/templates/assets/icons.json create mode 100644 plugins/src/templates/assets/strings.json create mode 100644 plugins/src/templates/bank-verification/assets/icons.json create mode 100644 plugins/src/templates/bank-verification/assets/strings.json create mode 100644 plugins/src/templates/bank-verification/views/home/App.js create mode 100644 plugins/src/templates/bank-verification/views/home/Form.js create mode 100644 plugins/src/templates/bank-verification/views/home/index.js create mode 100644 plugins/src/templates/bank-verification/views/home/view.json create mode 100644 plugins/src/templates/bank-verification/views/verification/App.js create mode 100644 plugins/src/templates/bank-verification/views/verification/Form.js create mode 100644 plugins/src/templates/bank-verification/views/verification/index.js create mode 100644 plugins/src/templates/bank-verification/views/verification/view.json create mode 100644 plugins/src/templates/fiat-wallet/assets/icons.json create mode 100644 plugins/src/templates/fiat-wallet/assets/strings.json create mode 100644 plugins/src/templates/fiat-wallet/views/deposit/App.js create mode 100644 plugins/src/templates/fiat-wallet/views/deposit/Form.js create mode 100644 plugins/src/templates/fiat-wallet/views/deposit/index.js create mode 100644 plugins/src/templates/fiat-wallet/views/deposit/view.json create mode 100644 plugins/src/templates/fiat-wallet/views/withdraw/App.js create mode 100644 plugins/src/templates/fiat-wallet/views/withdraw/Form.js create mode 100644 plugins/src/templates/fiat-wallet/views/withdraw/index.js create mode 100644 plugins/src/templates/fiat-wallet/views/withdraw/view.json create mode 100644 plugins/src/templates/kyc-verification/assets/icons.json create mode 100644 plugins/src/templates/kyc-verification/assets/strings.json create mode 100644 plugins/src/templates/kyc-verification/views/home/App.js create mode 100644 plugins/src/templates/kyc-verification/views/home/Form.js create mode 100644 plugins/src/templates/kyc-verification/views/home/index.js create mode 100644 plugins/src/templates/kyc-verification/views/home/view.json create mode 100644 plugins/src/templates/kyc-verification/views/verification/App.js create mode 100644 plugins/src/templates/kyc-verification/views/verification/Form.js create mode 100644 plugins/src/templates/kyc-verification/views/verification/index.js create mode 100644 plugins/src/templates/kyc-verification/views/verification/view.json create mode 100644 plugins/src/templates/new-page/assets/icons.json create mode 100644 plugins/src/templates/new-page/assets/strings.json create mode 100644 plugins/src/templates/new-page/views/view/App.js create mode 100644 plugins/src/templates/new-page/views/view/Form.js create mode 100644 plugins/src/templates/new-page/views/view/index.js create mode 100644 plugins/src/templates/new-page/views/view/view.json create mode 100644 plugins/src/templates/onramp/assets/icons.json create mode 100644 plugins/src/templates/onramp/assets/strings.json create mode 100644 plugins/src/templates/onramp/views/view/App.js create mode 100644 plugins/src/templates/onramp/views/view/Form.js create mode 100644 plugins/src/templates/onramp/views/view/index.js create mode 100644 plugins/src/templates/onramp/views/view/view.json create mode 100644 plugins/src/templates/verification-tab/assets/icons.json create mode 100644 plugins/src/templates/verification-tab/assets/strings.json create mode 100644 plugins/src/templates/verification-tab/views/home/App.js create mode 100644 plugins/src/templates/verification-tab/views/home/Form.js create mode 100644 plugins/src/templates/verification-tab/views/home/index.js create mode 100644 plugins/src/templates/verification-tab/views/home/view.json create mode 100644 plugins/src/templates/verification-tab/views/verification/App.js create mode 100644 plugins/src/templates/verification-tab/views/verification/Form.js create mode 100644 plugins/src/templates/verification-tab/views/verification/index.js create mode 100644 plugins/src/templates/verification-tab/views/verification/view.json create mode 100644 plugins/src/templates/view/App.js create mode 100644 plugins/src/templates/view/index.js create mode 100644 plugins/src/templates/view/view.json create mode 100644 plugins/src/utils/index.js create mode 100644 plugins/src/utils/link.js create mode 100644 plugins/test.js create mode 100644 plugins/webpack-dev.config.js create mode 100644 plugins/webpack-main.config.js create mode 100644 plugins/webpack.config.js diff --git a/.gitignore b/.gitignore index 0cc1d73179..59b0aa0d80 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,9 @@ web/yarn-error.log* web/src/**/*.css +plugins/dist +plugins/json + #HollaEx CLI related templates/local/nginx/conf.d/* templates/local/logs/* diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..f5e113eb55 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,233 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concurrently": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.3.0.tgz", + "integrity": "sha512-IiDwm+8DOcFEInca494A8V402tNTQlJaYq78RF2rijOrKEk/AOHTxhN4U1cp7GYKYX5Q6Ymh1dLTBlzIMN0ikA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^17.3.1" + } + }, + "date-fns": { + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.2.tgz", + "integrity": "sha512-0VNbwmWJDS/G3ySwFSJA3ayhbURMTJLtwM2DTxf9CWondCnh6DTNlO9JgRSq6ibf4eD0lfMJNBxUdEAHHix+bA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "rxjs": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000..b5965175dc --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "plugin:kit": "npm run --prefix ./web dev:plugin --plugin=$npm_config_plugin", + "plugin:plugin": "npm run --prefix ./plugins start --plugin=$npm_config_plugin", + "plugin:dev": "concurrently \"npm run plugin:kit --plugin=$npm_config_plugin\" \"npm run plugin:plugin --plugin=$npm_config_plugin\"" + }, + "devDependencies": { + "concurrently": "7.3.0" + } +} diff --git a/plugins/.babelrc b/plugins/.babelrc new file mode 100644 index 0000000000..864bd1ed98 --- /dev/null +++ b/plugins/.babelrc @@ -0,0 +1,42 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "usage", + "debug": false + } + ], + "@babel/preset-react" + ], + "plugins": [ + [ + "@babel/plugin-transform-runtime", + { + "regenerator": true + } + ], + [ + "@babel/plugin-proposal-class-properties", + { + "loose": true + } + ], + [ + "transform-react-remove-prop-types", + { + "removeImport": true + } + ] + ], + "env": { + "development": { + "sourceMaps": true, + "retainLines": true + }, + "test": { + "sourceMaps": true, + "retainLines": true + } + } +} \ No newline at end of file diff --git a/plugins/.eslintignore b/plugins/.eslintignore new file mode 100644 index 0000000000..d64c4ca286 --- /dev/null +++ b/plugins/.eslintignore @@ -0,0 +1,2 @@ +coverage/ +dist/ diff --git a/plugins/.eslintrc.json b/plugins/.eslintrc.json new file mode 100644 index 0000000000..84befc479e --- /dev/null +++ b/plugins/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true, + "mocha": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 9, + "sourceType": "module" + }, + "rules": { + "indent": [ + "warn", + "tab", + { + "SwitchCase": 1 + } + ], + "linebreak-style": [ + "warn", + "unix" + ], + "quotes": [ + "warn", + "single" + ], + "semi": [ + "warn", + "always" + ] + } +} \ No newline at end of file diff --git a/plugins/.eslintrc.yml b/plugins/.eslintrc.yml new file mode 100644 index 0000000000..6ef13503d4 --- /dev/null +++ b/plugins/.eslintrc.yml @@ -0,0 +1,3 @@ +extends: ["@paciolan/react"] +rules: + react/prop-types: off diff --git a/plugins/LICENSE b/plugins/LICENSE new file mode 100644 index 0000000000..dd6bdf83ca --- /dev/null +++ b/plugins/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) +Copyright (c) 2019 Paciolan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 0000000000..f6a5888973 --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,58 @@ +# hollaex-plugins + +Usage: + +1. Install dependencies: + + ```bash + npm install + cd web/ && npm install + ``` +2. Run `npm run build --plugin=` to generate plugin JSON object: + + ```bash + npm run build --plugin=hello-exchange + + /* + { + "name": "hello-exchange", + "version": 1, + "type": null, + "author": "bitHolla", + "bio": "Say hello from an exchange", + "description": "Demo plugin for proof of concept", + "documentation": null, + "logo": null, + "icon": null, + "url": null, + "meta": { + "private": { + "type": "string", + "required": false, + "description": "A secret", + "value": "hello exchange..." + } + }, + "public_meta": { + "public": { + "type": "string", + "required": false, + "description": "Not a secret", + "value": "Hello Exchange!" + } + }, + "prescript": { + "install": [ + "hello-world-npm" + ], + "run": null + }, + "postscript": { + "run": null + }, + "web_view": null, + "admin_view": null, + "script": "const helloWorld=installedLibraries[\"hello-world-npm\"];app.get(\"/plugins/hello-exchange\",(e,l)=>l.json({publicMessage:publicMeta.public.value,privateMessage:meta.private.value,libraryMessage:helloWorld(),timestamp:moment().toISOString()}));" + } + */ + ``` diff --git a/plugins/README_WEB_VIEW.md b/plugins/README_WEB_VIEW.md new file mode 100644 index 0000000000..f2a876de65 --- /dev/null +++ b/plugins/README_WEB_VIEW.md @@ -0,0 +1,155 @@ +# Hollaex Plugin Starter + +Hollaex plugin starter is a package with some pre-defined configurations out of the box to create custom plugins for the [Hollaex kit software](https://github.com/bitholla/hollaex-kit). These plugins can be installed on the fly to add features to your exchange. + +Creating plugins has two different aspects, the server-side script and the client-side component. + +# Client-side component + +Client side codes in the plugins are actually nothing but a remote react component that is injected into the kit on the fly. We are using a package called [remote component](https://github.com/Paciolan/remote-component) to get a component by providing the url for that which is the address of the commonjs module bundle and add it to the kit using the smart target component. + + +## Smart Target component +To decide where to inject remote components, we are using the [smart target](https://github.com/bitholla/hollaex-kit/blob/master/web/src/components/SmartTarget/index.js) component in the kit. + +Smart target is actually a react component with a unique target id that renders a remote bundle when the id matches. + +Smart targets are also responsible for passing props to the remote components. These props are divided into two different categories. + +### Common props +Common props are passed to all remote components within the smart target component. They include but not limited to strings, icons, generateId function, renderFields function to generate forms, store values, edit and config context. +You can always check the latest available common props for remote components by checking the [smart target](https://github.com/bitholla/hollaex-kit/blob/master/web/src/components/SmartTarget/index.js) component in codebase. + +### Target specific props +Target specific props are passed to the smart target component from the parent component and may be different for each target. +To get the latest available target-specific props, you can check the parent component of each smart target component below: + +- [New Page](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/routes.js#L240) +- Fiat Wallet + - [Deposit](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/containers/Deposit/utils.js#L169) + - [Withdraw](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/containers/Withdraw/form.js#L257) +- New Verification Tab + - [Home](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/containers/Verification/index.js#L234) + - [Page Content](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/containers/Verification/index.js#L256) +- Bank Verification Tab + - [Home](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/containers/Verification/index.js#L355) + - [Page Content](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/containers/Verification/index.js#L506) +- KYC Verification Tab + - [Home](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/containers/Verification/index.js#L380) + - [Page Content](https://github.com/bitholla/hollaex-kit/blob/8e617abf15c503e2e8483ffc5f4ae3e7befa4401/web/src/containers/Verification/index.js#L523) + + +## Targets + +### Static Targets + +- Verification Page Bank Tab + - REMOTE_COMPONENT__BANK_VERIFICATION + - REMOTE_COMPONENT__BANK_VERIFICATION_HOME + +- Verification Page KYC Tab + - REMOTE_COMPONENT__KYC_VERIFICATION + - REMOTE_COMPONENT__KYC_VERIFICATION_HOME + + +### Dynamic Targets + +- New Page +- New verification tab +- Fiat wallet deposit and withdrawal page + +# Meta Object +Dynamic targets are generated based on meta object values. Below you can see essential fields to deine each plugin type. These values should be added to the meta object under the view.json file to define the type of the plugin. These valuse are already set when you are using templates. See Develop section for more information. + +#### New page: +```sh +{ + "meta": { + "is_page": true, + "path": "/route-name" + } +} +``` + +#### New verification tab: + +```sh + "meta": { + "is_verification_tab": true, + "type": "home" or "verification", + } +``` + +#### Fiat Wallet +```sh + "meta": { + "is_wallet": true, + "type": "deposit" or "withdraw", + "currency": "USD" /* currency symbol /* + } +``` + +# Develop +To start developing a plugin, you first need to decide about the type of the plugin. Below is the list of available plugin types: + +| Type | | +| ------ | ------ | +| page | adds a new page with customizable access from the side and top menus | +| verification-tab | adds a new verification tab to the user verification page | +| fiat-wallet | adds a deposit and withdraw page for a fiat currency | +| kyc | adds KYC tab to the user verification page | +| bank | adds bank verification tab to the user verification page | +| raw | adds a template without initial meta object values | + +To initialize a plugin template run: +```sh +npm run add-page --plugin= --type= +``` + +To add a view to a plugin template run: +```sh +npm run add-view --plugin= --webveiw= +``` + +Once the plugin is initialized, run the following commands: + +On the starter kit: + +```sh +npm start --plugin= +``` + +On the main kit: +```sh +npm run dev:plugin --plugin= +``` + +A page reload is required to reflect bundle changes in the browser. + +## Strings and icons + +You can use strings and icons from the main kit. + +You also can define new strings and icons by adding them to strings.json and icons.json under the assets folder respectively. + +These values are added to the kit strings and icons object during kit initialization. To use local assets in your component, you should convert the local id to the global one by using generateId function from the kit context. See kit context. + +## Kit context + +We can always directly use props passed from the kit to the remote component. However in order to prevent passing some props through many levels, a context is provided to make these props globally accessible. +You can partially subscribe to the context to access props from the kit in a more efficient way. + +```sh +import React from "react"; +import { withKit } from 'components/KitContext'; + +const Title = ({ user: { username } = {}, strings: STRINGS }) => ( +
+ {STRINGS.formatString(STRINGS[generateId('hello')], username)} +
+); + +const mapContextToProps = ({ user, generateId, strings }) => ({ user, generateId, strings }); + +export default withKit(mapContextToProps)(Title); +``` \ No newline at end of file diff --git a/plugins/generateJson.js b/plugins/generateJson.js new file mode 100644 index 0000000000..a08f071f7f --- /dev/null +++ b/plugins/generateJson.js @@ -0,0 +1,80 @@ +const fs = require('fs'); +const path = require('path'); +const uglifyEs = require('uglify-es'); +const { minify: htmlMinify } = require('html-minifier-terser'); +const { FILES, PATHS } = require('./scripts/patterns'); + +const { env: { PLUGIN: plugin } } = process; + +if (!plugin) { + console.error('No plugin name given'); + process.exit(1); +} + +const pluginPath = path.resolve(__dirname, PATHS.ROOT, plugin, PATHS.SERVER); + +if (!fs.existsSync(pluginPath)) { + console.error(`Plugin ${plugin} does not exist`); + process.exit(1); +} + +const scriptPath = path.resolve(pluginPath, FILES.SCRIPT); +const adminViewPath = path.resolve(pluginPath, FILES.ADMIN_VIEW); +const webViewPath = path.resolve(pluginPath, FILES.WEB_VIEW); + +console.log(`Generating plugin JSON object for plugin ${plugin}...\n`); + +const config = fs.readFileSync(path.resolve(pluginPath, FILES.CONFIG)); +let script = null; +let admin_view = null; +let web_view = null; + +if (fs.existsSync(scriptPath)) { + const rawScript = fs.readFileSync(scriptPath, 'utf-8'); + script = uglifyEs.minify(rawScript); + + if (script.error) { + console.error('Error occured while minifying script\n'); + console.error(script.error); + process.exit(1); + } + + script = script.code; +} + +if (fs.existsSync(adminViewPath)) { + const rawAdminView = fs.readFileSync(adminViewPath, 'utf-8'); + + try { + admin_view = htmlMinify(rawAdminView, { + minifyJS: true, + minifyCSS: true, + collapseWhitespace: true + }); + } catch (err) { + console.error('Error occured while minifying admin_view\n'); + console.error(err); + process.exit(1); + } +} + +if (fs.existsSync(webViewPath)) { + const web_view_json = fs.readFileSync(webViewPath); + const { web_view: view } = JSON.parse(web_view_json); + web_view = view +} + +const pluginJson = JSON.stringify({ + ...JSON.parse(config), + script, + admin_view, + web_view +}, null, '\t'); + +const finalJsonPath = path.resolve(pluginPath, `${plugin}.json`); + +fs.writeFileSync(finalJsonPath, pluginJson); + +console.log(pluginJson); + +process.exit(0); \ No newline at end of file diff --git a/plugins/media/icon-large.png b/plugins/media/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..06bc4d2565815493d8ce711d6ed097574a550595 GIT binary patch literal 6114 zcmds5cTkgEmrp{Zi6+t!kpMx8K0tKyzVCjYGdnx8f9&iZo0-f!&%O7w-zoQ;C(h)w!2#C8tPlv~ z00ECPgFv9{;LpI!2v)Z069m9ZxQDK;2|-sEW#aGS=HclIfrzKLI66uaq{Q2uoE#n7 zdyY%6`UjiE#*)n(8=I*;)MiwRV=F2r!`}WgFYN0yqPYcY7gZ&E|(GW5WBOt37W3vxB*^!1i_~In6RZQ(NvHVoh z`)0kSF{;_e6jCjL@s(=6tUC5#>+QQdhuQp@kX^DNvM*)DWiu3bWv%7s~tV5QYC){aO zMNYPKe1}f2ILyGXgJz6rcK3V?xwA!}KEdAOs!k@tJ&y9Rd81?O|aZXl-ny z=HlZe<3#jvc9jY7@&$f^Kr}+sz@wLIpc5*@%hNkREkslJuM=wE`S&tL81>hYz)PCK z*2X3%T_1l}l#-01jI6L0D+-0u@F%*dnc?*RAqQ`o!WRMqebq3S;NW1HU}xdyoSd-w)=_;{m!^L29e2@2E{7XB^h zU%$WgbPe(NwtAnXwR&Nd>UOX$%pOI4lGM1p2StAuy!)U5LJ!q7EDdfs(KHLf|M9 zMJPG%!gW9a<7K%IC?8ya@e=x&4*_{<*+61ER6oBHUGFkt714rfF`q8_o_;VL1K zFfjh_zYq?4Uf;$(4f+^LHvAa+(Ow9}vWqqGuDw2N+vS0SqVX+UIRfl{9HKUDA{O;zB~it6QG3? zVD$Q>94ic&aS|eu35~V{y2=7woyK_gL7BTzq?B$_lO(W+7b-JyoP4j(bfRYGF=VPW zzJF-6@7%y9ijh8wsD=wg$H1Uu9JDy*i(c1=B60OY-G$-TbvqXW!>1^tO2j_4SjbIf zXcUeSCc{C++dtC^xQWFA=_IT$1C+wzh?Y5f|CKHZ1!QAUDN&>@02Jj(@0bu02_>UZ z$?aIGD~va;Yta2Wk~tQl&yK_sQJ>|ZA?X}>?VH=&Wm>}~ajp`n*~`ftLMo|F{*)O^gzG@$L* zIA2LVSIIL`eHLLnsWnYzPrqF(m&2-~#N?3@itbB$5Nr+`;+hO$b~bc!3Xp0mQ*BI~Bjno~sQ< zB?IU?usRHI)MpAre~QAp2iyg`Nnm6@3__|AAko+|hk!B@kyN~V@;eYSo1&zYO;P=; z%vcm~W@^-RMrJG(1Hiz@ZR86vB=?( zlBS1=IuXut`%KY7j&dM$9;)2l(mZ1?B%g|8z6sHPM9tP@-&csY1<7VAjtE1unxs^M zJaZ1ZfZTaalQ(jF4_06F*U;uxCYCq|jdv&m zQ>*y_&C;EwJk)xLZ3&S;mQwf=xDu7{N18|0#|)Q5?Z=Uaq6gWzjPR8ufyE(CFAu_AmB zdIFf`J&u}P3dQ6ioz`b_{AZIZj$%`{fYT*DWM0afV)}~RN!O4LoZF; z-B_TOfaO0bxdh`Y_PLvz*Gm^!-!}*kv7Rb2OgLd-l z#UtTiAElN;d}-M-%=ejb?XUIkc^X+)7EOI&80hmNHf6}v-NgnS z7MdSA>s>TbZ&17*7!o6we0EVGk@)Q7N_n19P6akb##Z{Kc4YQXOug^3XO;YDQVQ?& zbHxwY_uk}xPy3j+J}6-Jik5xtapv=sEQzB&o~ay9WGK-mB2LC|%V-5C&<=oF%oZin zDr;Fgnm@Tk$@`#?;!M#=B)W}dY+Q&D`S=NSZb4hUA~T2qV91bcD2?k`@HsV2|7BY| zKF&Wha7{L~h{)cvnm0Sb@7h>RdF>nnvLGgafFsbN6t&mL8+!&}pI2G>@sCpzdM~9k zpNKH6dFF(0jgR}_K*l9_vV2QlTkERpIYanVHBsDisu}{Yg-;*!2wRT}zxl2g278+=>;qkWWOiYw#@P1iy07WsDnt%G5xiirStB#C5WnGzu5dp+Y2oq~Ju?HJ+Rb5x#O2J%~uzVlcCxF@Fy-{<9$kioW|b^;86@Ch zVRImNiS~r}49!1&Q!!c9s_hJ0NAcC|uSe`WltGDnmA!#9%UwiWZq=sCA$35STxZr6sPUhm)$hdD9rfq#L)>pj>bSDK?KRBqv6i`f zz8WtD3iowW8#0=0zQDy9-PDX3-E&)hB%TARpuZJPO`yMbG7*t97pwkMO;t;V$1{}b z1e-Jx0Fc!VcuyQM`_hjX-TbjXUz^b77MpV2B6(gwT{nIaJM@gKrW~u zabX3QE9Q_HSsBTCcFGH}xp#qtRIUW?+&0~YAcb2wq37WeKN*u60{K&<;COMt zWsjp5kLNg5*KMcX)p%V2ig3l1!p7rQtL-!yq2ppE6Ak-0W0-wQ5{2HGJEh)CI$cuy zYpG`TwB??}%EQ%k{LUHW)Ac<&G$!S-tl$Y9b*mKx--mPINzM3hk>lRjANAu?@Nja@ z0!%}Y-F->z&Qr}x_Z8F!$Om$U^L{iutv1l(=`(^I86nD>dPyuSoEVyr<3=kjpH19Eq$T0bBKBQ*Fy#bs{nUN&ZsxmiL{R@1b@MQPPtUJ*AGm<-@xB#= zH(L(!{M^}9E4S5Z3f=w(ZBCi+PSVGrz}hduVlG?D*5zYA*bK!q$t}LpVi$`K7zC;u z(RY5j$r~p(Kx0y~q0x;@nkCYWUUpnG8nuviz`^G0=e;DXAd+R3&B{;}-3$;^O~ zM5oVUc3=78tY5f4&vS^RjPq;FTx#Eq@Y$UADPO&CX}y5qZg*3fHE-PHsU(>UAGGu8 zv|accf_8aDa%;N5<($kN7Fs8fTLG?8Dc5{Ot@Fa@>u*6;fh<2u>~r}cYaXCOS7cQ< z?jqeIf8N3USVptyR7W1TTHZOq+zM``WXwcJjc-AnwRdnU+)Va1%?_*L?HKbM zsA9u76hBT}-hszxm%=z10dR~?XKtvKId2U}UQJ^@a<29?fp6Folp#-xJEQueZ@J7H z$HmXIp4!4$zNoAI+HCdJs+iz}7P7oN7yU#h-hSK3fD&NsQq##B=P^|?41Y-9@OmpzZP8#GZWRlJ;&UP_-=H zXayvad!_|XKFp6jYtd#$L3evFh^0bM+|rW~XZypZ3lh@(EFaKE7S3hYD6O{P zr6gLZBM27v+HH*f?wIm$@WR<$1QGY(_YL94E%qT`HBmnE%MLAEqRrXw+skR6)uq$g zdZ5t}wr}irS+)BzcmL{JnymW{BJ`$WMAun0^{e-e-AWm^M>@(Xtso$SR4)-s+f|@p5t-IG4u zqk8IgD_+>7q{;AXS8Z^F5-f8*Z)hj~c{`2%GeK>tkWmNREAZTg_IJez`K$MWyE?ue z+jC6E#Zu1VH$Hw(W4K>GoJz*K$bfLMCrWt=+Jr_0j)zg$9A0+6Uc6>3F+L`|G_jQv z=nxcC`ns?Pq`gJyeB9#=e9iY)=G)JI6`oH{P7|}T7aqO3Ok&IGjc(DA(Hoz9RVw0R zaXHB7{;B&~9wjqP^puv*56*RPWf<2zHQIhgSNOK6;UL2kD_iNrOSmwXfY4ZeLm_Mz zMM(1t-mamXmpCtLw9kh&y;Ms-9m8FDBXeKbesE!NGQUlCnhtv5zgcA`x|fypbI
Ya2Ds9 z&u0JP{JQUb%lC7ZQ%-0}>t>E6hhQ)WdFXm<4z8L-7OK|~%Wn7Z5!-3J zyvZYHCz9_9G~S-iu61AQ3+3?9e4{>AW29Ek(YeWPnNTiJ1G?wAyf_Ora{`A`Yzk9U zh@|N6KD4-AS5j@=a3<<>T;i>-N3JYO^}dL^f_NPN@kHm`qt!2)qLj{uXTojU>M&+D zizwfvRJlPM7^PY-6>Q@4Zd6nJJBmJ9#DM-ge|Pzi#KO0<@`gyl5y9k^<`zj0<(GfAK0(yn;o&2Fb?ElQbaYxF5_i>8FyJ;KD`_kvQKKya22U1dC@8{QuuE zm!w5SrO5%D(XCsAEcON@48-}N8zei>rd7}PcnUoLrNOi*UK~CK zzM{Rf^l{q{5aTTBhs5jq_d^*ueut?*w0{S!2jgZq=G>Un_CV zXM2$pfymoFN>o&$pyd2K^SnGpmnGnxW*^NJaoulBVB6-Gr9c;q!X<%|;yj(pEJfY; zg*8_2Z|qq^U6fWF?4Ouit^)5U5o}GgarrDxwh@zkeDTt{O`u*#oIrcTc;}Ul>2~u! zQzBXrw9hq2idwow-Dx>I{7k+ewgJT;#b=6Aido9Miq=ZYO6nPe7CVS`ws5xhfL0c8sB=xrYXN?ADF*-9>fB2C;mwH~kyIQF)qD+b%! zr=!`f=Jd4JhO$yccS+yP>er^F4a3sL;+7cL6WBkpxF7ZC#X07=c~U#=s6!QNXAVzJ z>iMat4#j9=+c-{6PF^jaoU|oGexq7g(`5m~0x@$$EIaF|9Q58sC_eyTJjG7q45ckRY7*X*2+62Wg&eajyPO;2@kQ-cK_~N93;v&C~sv zSwRH+SBO7WN5tC146N_v>k7UIQGqCmz!<<_u(q#@o2I#e;Xm-xKOGSde}8XH1%<%C zKuDl6#LL%R0ji;)p`fUwprj;!8X@mT!23G|$>aS*|4#D%c??|roP9Cg{unPj_$;r} zEw2E79TAbULjQXH?$b30^Y2P{zkkL$9Z=!SqX2~{D*QK?KgR8Uz|K5>!~UAcxa75R(j7vxzdnx?)O*HeBaPrSCm8Bf3P_O~d1Sx;4gF=#9NOJNwp=fs9N0AQj*7+kjSpdq2KM0-oF zpTi5V&of#ZaIFVp(vd|ZF$TR_sRzkYIh)a!c#lNGjo+fXgp-rA&?*wktooEtv7@oB z@E=AhP-z>k$1&Ch9(+j8Rp6_!`xN2LE9LKmq}k>-8s7uf8^%`(rgtWTs~Z1oo|wYc zg!W9yq=!!H?r7Joy2pH9<#0oB@4mF?#2!R}Hdk)*EjpvfS5t=Un(Q@UY`}d8O~10q!RFHyU03{;yA`Yr_2m z!|$v*Gks3A{_vz7ud1+eE#DEuEu))5MZT=vJH@+ZipD&TVoic2c0@W@ti*q~Yc8k@ z!us@2xRGbX&LEtFWVymxvhKm|?dYvV=gyr$`*&EY|3PR7RY;DC^eB?X}ma^$mjbe;t z(%aXjMOu&76rgQa0@Z&*6J`<*T{5qkSXBsAUuFY=WwQpy!1phu7?OGQR9EcBBkP4!)Dr3bqgQf>8%_}h+^;^?7eWm>FGi?Q-yM9>$6(R}wck^bxk|d> zJ*&gK)C{M9$mKkV*ZNtWY@LJB^}%r*AKpt5Sc`?5U1l_HdigZYx5DTH<%1Q6qRb3a z$B3I1PRql`Mgi`Wt#p|878$E#Ta^C%PGHfYfFrA+s|V%nAYE=tTibibQ}lClLJ@V? zL7qe!&O{gkZyzawT6yH1X~Z~}`crc$JF6$5LEP!4nNFgY@v}$oUeT}RucZpwvas1>dHHTv+3=?vzK>w3k`{0X-Tbh z--t~gFnqNaOr`r)Jl0p-tW|`SI-`!a-h48sYlML*Tu`l+6Mub|S5q39%6>yizbDh_ zt+1-A=*2dRE94q-(GfVj@9LmM6L!5LZ_FtTdrjk6V>d)EkFL*}Q~}(#HcU?Xwhs z<@I6in_URfNzRw}wJ04&RJf5`jC zI@wLHRlnWqBPAemydId5~7;eD%}TZ(Njk2dV(!I!AMXhn*Uwl|gTr zOnwOZXH`N100zF?T*S{!rzdUOl%DrQ`b3+=D5``Gc~W1`z;O|LBr`q2Gf=&0c;cdd zPO#ZDqoJshBl>&;YWG=TU&av9OwrLEvfizB4gCE8VR5hJ-{q$a-&O#8SO9RVpcwJgiYp z)7L+e!$`XNOrlS6Up9vsmE_5EcLCy>d5!sDg5UdiYIbgAZm!wMK!A%oG_^{nb};VC z7m+kQ#MmOLi5A%D59_JCQD+ON6AOKv7z>B11b~6c+}_%_dZf5|JW~iEqTc%BaFs8a zRs1UESS8=y+*4WANY*=Q`3}qD7|Nf@uZY3clJ+QTc(oeiy_V)xH)G9AP=lDD+S3*% z)l6O>n#kB^?D=^Pk8G`o25o+L)Y^Yb4uhP9*z}o5WPLLd;~3R)7;}>E5^s1BvwK9j zft}1zKzFX|Vf+R6Q_clFNt`w>hb@h6t9yGeygB|sFNW&3$krXphAc4GGf`W8glo>S zJFe4>iw) zX)}6e)T$p-G(onEy*D_wzh5H8z=b-$L?YBj5gA2uYz>}l7o}|%fAU-$x>+_8tOSFp z^<>??4F#~&wPUopyOig!HjL&loTyGPO-ap9hWO?g&;e>`)AONRqoP{E*cX}3)Rx1Q zgw02Wir=FP(d(_CNuJ&{aCYQKEt)C<4iLpvD@0QzrX%k#H_hq1K%&VDGjBqF z?l^+CHZVWZN;ds$aHZ~iPF|lduyw%|mD_N9^c%Lkply2Mt7BG+8>K}O(B~%o@SWG| zMa576O8%;;UT4~7tJ1rq`L2&GMVyj+m6-4?2YfinkjYSQM$}_FQ~cvL9{E@;G+) zWD2=n)G%FrWc+#i@L;Y$J0@kJMqsqe^Sbd`aW`OBL!92_yMg=iiR8B85`dwfFpSK0 zS5WPFu|hBOJNHa_o-E3vs)qLi)adra*iL0AxON^{Vdvi{mN2~qEhR)L3j@3i zD>vK~@2uh6N-u;QjW40zlqzoZTqhna=az?j8(=DQ;J{n8Mtph8iF|cDebs}AB@M@} zM;cy;niNU08%Jzpz+9Pqd02!kp7RGqP?vSBzk%A139Jg(m0reeL(hdt}yv;hY#0l&hjo+CO?bpUAW|98;-uJjj$6=%#qGE$qEj1V`sa z$yZxU+iVrY$yyBtJT_cd!|4o4UCf}aAO$e}-1q7g3Kvjq3;*KzN?j*$Ub~QW=57^8 zh)gbd;Jo#%({O(|4UyY0S@}EjHMcWLqR9b%`FG29M*6VT^F;-= 2.1.2 < 3.0.0" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "eslint": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz", + "integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + }, + "eslint-plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-babel/-/eslint-plugin-babel-5.3.1.tgz", + "integrity": "sha512-VsQEr6NH3dj664+EyxJwO4FCYm/00JhYb3Sk3ft8o+fpKuIfQ9TaW6uVUfvwMXHcf/lsnRIoyFPsLMyiWCSL/g==", + "dev": true, + "requires": { + "eslint-rule-composer": "^0.3.0" + } + }, + "eslint-plugin-prettier": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.0.tgz", + "integrity": "sha512-tMTwO8iUWlSRZIwS9k7/E4vrTsfvsrcM5p1eftyuqWH25nKsz/o6/54I7jwQ/3zobISyC7wMy9ZsFwgTxOcOpQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-plugin-react": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz", + "integrity": "sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flatmap": "^1.2.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "object.entries": "^1.1.2", + "object.fromentries": "^2.0.2", + "object.values": "^1.1.1", + "prop-types": "^15.7.2", + "resolve": "^1.18.1", + "string.prototype.matchall": "^4.0.2" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-rule-composer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fraction.js": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz", + "integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, + "hollaex-web-lib": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/hollaex-web-lib/-/hollaex-web-lib-0.3.0.tgz", + "integrity": "sha512-DDMFM5cVNAgWYwmsgJTck30xSBfCQB/Xe+kK2liVfQgkOeoFVj7F65uLi72IB7LBO55hfxmPj2bSt8XkN87ydA==", + "requires": { + "@material/button": "0.7.0", + "keycode": "2.2.0", + "react-copy-to-clipboard": "5.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "requires": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + } + }, + "html-webpack-plugin": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz", + "integrity": "sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw==", + "dev": true, + "requires": { + "@types/html-minifier-terser": "^5.0.0", + "@types/tapable": "^1.0.5", + "@types/webpack": "^4.41.8", + "html-minifier-terser": "^5.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.15", + "pretty-error": "^2.1.1", + "tapable": "^1.1.3", + "util.promisify": "1.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-server": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.0.0.tgz", + "integrity": "sha512-XTePIXAo5x72bI8SlKFSqsg7UuSHwsOa4+RJIe56YeMUvfTvGDy7TxFkTEhfIRmM/Dnf6x29ut541ythSBZdkQ==", + "dev": true, + "requires": { + "basic-auth": "^2.0.1", + "colors": "^1.4.0", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.5", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "insert-css": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz", + "integrity": "sha1-610Ql7dUL0x56jBg067gfQU4gPQ=" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-beautify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/json-beautify/-/json-beautify-1.1.1.tgz", + "integrity": "sha512-17j+Hk2lado0xqKtUcyAjK0AtoHnPSIgktWRsEXgdFQFG9UnaGw6CHa0J7xsvulxRpFl6CrkDFHght1p5ZJc4A==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=", + "requires": { + "string-convert": "^0.2.0" + } + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "dev": true, + "requires": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + } + }, + "keycode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz", + "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ=" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "lodash.has": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", + "integrity": "sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "mathjs": { + "version": "5.10.3", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-5.10.3.tgz", + "integrity": "sha512-ySjg30BC3dYjQm73ILZtwcWzFJde0VU6otkXW/57IjjuYRa3Qaf0Kb8pydEuBZYtqW2OxreAtsricrAmOj3jIw==", + "requires": { + "complex.js": "2.0.11", + "decimal.js": "10.2.0", + "escape-latex": "1.2.0", + "fraction.js": "4.0.12", + "javascript-natural-sort": "0.7.1", + "seed-random": "2.2.0", + "tiny-emitter": "2.1.0", + "typed-function": "1.1.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mini-store": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/mini-store/-/mini-store-3.0.6.tgz", + "integrity": "sha512-YzffKHbYsMQGUWQRKdsearR79QsMzzJcDDmZKlJBqt5JNkqpyJHYlK6gP61O36X+sLf76sO9G6mhKBe83gIZIQ==", + "requires": { + "hoist-non-react-statics": "^3.3.2", + "shallowequal": "^1.0.2" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "nocache": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/nocache/-/nocache-3.0.1.tgz", + "integrity": "sha512-Gh39xwJwBKy0OvFmWfBs/vDO4Nl7JhnJtkqNP76OUinQz7BiMoszHYrIDHHAaqVl/QKVxCEy4ZxC/XZninu7nQ==", + "dev": true + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + } + } + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "numbro": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/numbro/-/numbro-1.11.1.tgz", + "integrity": "sha512-qL0Etqbunz4RtPx4bNjMONe9HyUpgbrM4Sa3VpWY5oRdp9ry5DufAj6lCvnIcluRBA9QUacrllYc73QK0G6VAw==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", + "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", + "dev": true, + "requires": { + "array.prototype.reduce": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "omit.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/omit.js/-/omit.js-2.0.2.tgz", + "integrity": "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onchange": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/onchange/-/onchange-7.1.0.tgz", + "integrity": "sha512-ZJcqsPiWUAUpvmnJri5TPBooqJOPmC0ttN65juhN15Q8xA+Nbg3BaxBHXQ45EistKKlKElb0edmbPWnKSBkvMg==", + "dev": true, + "requires": { + "@blakeembrey/deque": "^1.0.5", + "@blakeembrey/template": "^1.0.0", + "arg": "^4.1.3", + "chokidar": "^3.3.1", + "cross-spawn": "^7.0.1", + "ignore": "^5.1.4", + "tree-kill": "^1.2.2" + }, + "dependencies": { + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + } + } + }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true + }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dev": true, + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true, + "optional": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "portfinder": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "dev": true, + "requires": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true + }, + "postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "dev": true, + "requires": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc-align": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.11.tgz", + "integrity": "sha512-n9mQfIYQbbNTbefyQnRHZPWuTEwG1rY4a9yKlIWHSTbgwI+XUMGRYd0uJ5pE2UbrNX0WvnMBA1zJ3Lrecpra/A==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "dom-align": "^1.7.0", + "lodash": "^4.17.21", + "rc-util": "^5.3.0", + "resize-observer-polyfill": "^1.5.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + } + } + }, + "rc-animate": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-3.1.1.tgz", + "integrity": "sha512-8wg2Zg3EETy0k/9kYuis30NJNQg1D6/WSQwnCiz6SvyxQXNet/rVraRz3bPngwY6rcU2nlRvoShiYOorXyF7Sg==", + "requires": { + "@ant-design/css-animation": "^1.7.2", + "classnames": "^2.2.6", + "raf": "^3.4.0", + "rc-util": "^4.15.3" + }, + "dependencies": { + "rc-util": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.21.1.tgz", + "integrity": "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==", + "requires": { + "add-dom-event-listener": "^1.1.0", + "prop-types": "^15.5.10", + "react-is": "^16.12.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.1.0" + } + } + } + }, + "rc-cascader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-1.3.0.tgz", + "integrity": "sha512-wayuMo/dSZixvdpiRFZB4Q6A3omKRXQcJ3CxN02+PNiTEcRnK2KDqKUzrx7GwgMsyH5tz90lUZ91lLaEPNFv0A==", + "requires": { + "array-tree-filter": "^2.1.0", + "rc-trigger": "^4.0.0", + "rc-util": "^5.0.1", + "warning": "^4.0.1" + } + }, + "rc-checkbox": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.3.2.tgz", + "integrity": "sha512-afVi1FYiGv1U0JlpNH/UaEXdh6WUJjcWokj/nUN2TgG80bfG+MDdbfHKlLcNNba94mbjy2/SXJ1HDgrOkXGAjg==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1" + } + }, + "rc-collapse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-2.0.1.tgz", + "integrity": "sha512-sRNqwQovzQoptTh7dCwj3kfxrdor2oNXrGSBz+QJxSFS7N3Ujgf8X/KlN2ElCkwBKf7nNv36t9dwH0HEku4wJg==", + "requires": { + "@ant-design/css-animation": "^1.7.2", + "classnames": "2.x", + "rc-animate": "3.x", + "rc-util": "^5.2.1", + "shallowequal": "^1.1.0" + } + }, + "rc-dialog": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-8.1.2.tgz", + "integrity": "sha512-yhyy3bxnornjrUPOiCXFdTt/nRKjQ/qhR+MMcQavRYWh1LPcxB8y1LbgrvYX7SV/lY/Mib237xf2q6WYXQ1kpA==", + "requires": { + "rc-animate": "3.x", + "rc-util": "^5.0.1" + } + }, + "rc-drawer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-4.1.0.tgz", + "integrity": "sha512-kjeQFngPjdzAFahNIV0EvEBoIKMOnvUsAxpkSPELoD/1DuR4nLafom5ryma+TIxGwkFJ92W6yjsMi1U9aiOTeQ==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.0.1" + } + }, + "rc-dropdown": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-3.1.3.tgz", + "integrity": "sha512-sqVMDZcyV32y2YIEUBfxzgRzOLXqi/v5JB1GPe0CMyGMadPvbi+YIRF8toKdQf26tcHZobZUOyFk8OOV2BRusw==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-trigger": "^4.0.0" + } + }, + "rc-field-form": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.10.1.tgz", + "integrity": "sha512-aosTtNTqLYX2jsG5GyCv7axe+b57XH73T7TmmrX/cmhemhtFjvNE6RkRkmtP9VOJnZg5YGC5HfK172cnJ1Ij7Q==", + "requires": { + "@babel/runtime": "^7.8.4", + "async-validator": "^3.0.3", + "rc-util": "^5.0.0" + } + }, + "rc-image": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-3.0.6.tgz", + "integrity": "sha512-Dn8mTSlcgKJko417OX8+6yyNIL9+DEa81aexBfT78qWlEpcxtR4GgdsU0+zJLNqa2rnGZyjaBLFtaPw9tUuxYA==", + "requires": { + "@ant-design/icons": "^4.2.2", + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "rc-dialog": "~8.2.2", + "rc-util": "^5.0.6" + }, + "dependencies": { + "rc-dialog": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-8.2.2.tgz", + "integrity": "sha512-U4jR5bE7XpIbMC20JAIv91254b+vQ8LODd8Kxco0XvkL+eJ1aCYkOfRqevJ1ipOIzF3s6F08jSH8YvJqxvpAvA==", + "requires": { + "@babel/runtime": "^7.10.1", + "rc-animate": "3.x", + "rc-util": "^5.0.1" + } + } + } + }, + "rc-input-number": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-6.0.1.tgz", + "integrity": "sha512-cS1k6IB/V84VUQd5qWzGFrLHvZjWGHGmYbrvR0QP/C1Ju1SlBqlhqhOBTc6w+dpPs84PCH5caZtNzsHeWZ1zYA==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + } + }, + "rc-mentions": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-1.4.2.tgz", + "integrity": "sha512-wSmHRF9kFwrbj59mR+u4yVr0KtcrfPw53PYOVizYxYeDfmwaCcSgk29F8OjlDy5jVqUaMhHX5nIiYCePu5Aytg==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-menu": "^8.0.1", + "rc-textarea": "^0.3.0", + "rc-trigger": "^4.3.0", + "rc-util": "^5.0.1" + } + }, + "rc-menu": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-8.5.3.tgz", + "integrity": "sha512-OLdN+jwhabgyRZDvWYjYpO7RP7wLybhNuAulgGqx1oUPBJrtgVlG/X4HtPb7nypRx/n+eicj6H8CtbCs0L4m/Q==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "mini-store": "^3.0.1", + "omit.js": "^2.0.0", + "rc-motion": "^1.0.1", + "rc-trigger": "^4.4.0", + "rc-util": "^5.0.1", + "resize-observer-polyfill": "^1.5.0", + "shallowequal": "^1.1.0" + } + }, + "rc-motion": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-1.1.2.tgz", + "integrity": "sha512-YC/E7SSWKBFakYg4PENhSRWD4ZLDqkI7FKmutJcrMewZ91/ZIWfoZSDvPaBdKO0hsFrrzWepFhXQIq0FNnCMWA==", + "requires": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "raf": "^3.4.1", + "rc-util": "^5.0.6" + } + }, + "rc-notification": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-4.4.0.tgz", + "integrity": "sha512-IDeNAFGVeOsy1tv4zNVqMAXB9tianR80ewQbtObaAQfjwAjWfONdqdyjFkEU6nc6UQhSUYA5OcTGb7kwwbnh0g==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-animate": "3.x", + "rc-util": "^5.0.1" + } + }, + "rc-pagination": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-3.0.4.tgz", + "integrity": "sha512-9v9mmB7FTWS4kWRLFfWafm6LtvB+xdNi+pTIwUODSevzImrlrmMOIhDrOB3u2tEXiy8LyqvCnoyPYt5jQBapxA==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1" + } + }, + "rc-picker": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-2.0.11.tgz", + "integrity": "sha512-pUEne2fikHvzqmOjWNLa1P/ss4/1J9lpHrtSU1IrueRiEbUPLIgXmnzoUeVt3syOZGBnWok6nNDu1FvIQfVMCw==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "date-fns": "^2.15.0", + "dayjs": "^1.8.30", + "moment": "^2.24.0", + "rc-trigger": "^4.0.0", + "rc-util": "^5.0.1", + "shallowequal": "^1.1.0" + } + }, + "rc-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-3.0.0.tgz", + "integrity": "sha512-dQv1KU3o6Vay604FMYMF4S0x4GNXAgXf1tbQ1QoxeIeQt4d5fUeB7Ri82YPu+G+aRvH/AtxYAlEcnxyVZ1/4Hw==", + "requires": { + "classnames": "^2.2.6" + } + }, + "rc-rate": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.8.2.tgz", + "integrity": "sha512-f9T/D+ZwWQrWHkpidpQbnXpnVMGMC4eSRAkwuu88a8Qv1C/9LNc4AErazoh8tpnZBFqq19F3j0Glv+sDgkfEig==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + } + }, + "rc-resize-observer": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-0.2.6.tgz", + "integrity": "sha512-YX6nYnd6fk7zbuvT6oSDMKiZjyngjHoy+fz+vL3Tez38d/G5iGdaDJa2yE7345G6sc4Mm1IGRUIwclvltddhmA==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-util": "^5.0.0", + "resize-observer-polyfill": "^1.5.1" + } + }, + "rc-select": { + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-11.1.7.tgz", + "integrity": "sha512-HZozKGhFbLDI995OUKQW2uZ2ZcGyzNhWN6gTQqpW5/Z0rae1zftpgkI1yxZ79LukOc8lO2NKlDDKCGH/SJH2Cg==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^1.0.1", + "rc-trigger": "^4.3.0", + "rc-util": "^5.0.1", + "rc-virtual-list": "^3.0.3", + "warning": "^4.0.3" + } + }, + "rc-slider": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-9.3.1.tgz", + "integrity": "sha512-c52PWPyrfJWh28K6dixAm0906L3/4MUIxqrNQA4TLnC/Z+cBNycWJUZoJerpwSOE1HdM3XDwixCsmtFc/7aWlQ==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-tooltip": "^4.0.0", + "rc-util": "^5.0.0", + "shallowequal": "^1.1.0" + } + }, + "rc-steps": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-4.1.4.tgz", + "integrity": "sha512-qoCqKZWSpkh/b03ASGx1WhpKnuZcRWmvuW+ZUu4mvMdfvFzVxblTwUM+9aBd0mlEUFmt6GW8FXhMpHkK3Uzp3w==", + "requires": { + "@babel/runtime": "^7.10.2", + "classnames": "^2.2.3", + "rc-util": "^5.0.1" + } + }, + "rc-switch": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-3.2.2.tgz", + "integrity": "sha512-+gUJClsZZzvAHGy1vZfnwySxj+MjLlGRyXKXScrtCTcmiYNPzxDFOxdQ/3pK1Kt/0POvwJ/6ALOR8gwdXGhs+A==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-util": "^5.0.1" + } + }, + "rc-table": { + "version": "7.9.10", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.9.10.tgz", + "integrity": "sha512-WtPBxYsBU/a5MIglilbMlVkiXkPKXpUM/CPCFaqA2veh1b7J40mbTGQmU8VT6S0FClkI5jm0QBtSp6LstPkOMQ==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "raf": "^3.4.1", + "rc-resize-observer": "^0.2.0", + "rc-util": "^5.0.4", + "shallowequal": "^1.1.0" + } + }, + "rc-tabs": { + "version": "11.6.2", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-11.6.2.tgz", + "integrity": "sha512-7Z5Lg+nP/H4V7dIlewrOC0+aogRVH3ASjTy4VIletYOeStGPWYSfwBnUTBdcCXcUuWuyyKnNkYrUD0yaRqUCIA==", + "requires": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "raf": "^3.4.1", + "rc-dropdown": "^3.1.3", + "rc-menu": "^8.6.1", + "rc-resize-observer": "^0.2.1", + "rc-util": "^5.0.0" + }, + "dependencies": { + "rc-menu": { + "version": "8.10.8", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-8.10.8.tgz", + "integrity": "sha512-0gnSR0nmR/60NnK+72EGd+QheHyPSQ3wYg1TwX1zl0JJ9Gm0purFFykCXVv/G0Jynpt0QySPAos+bpHpjMZdoQ==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "mini-store": "^3.0.1", + "rc-motion": "^2.0.1", + "rc-trigger": "^5.1.2", + "rc-util": "^5.7.0", + "resize-observer-polyfill": "^1.5.0", + "shallowequal": "^1.1.0" + } + }, + "rc-motion": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.4.4.tgz", + "integrity": "sha512-ms7n1+/TZQBS0Ydd2Q5P4+wJTSOrhIrwNxLXCZpR7Fa3/oac7Yi803HDALc2hLAKaCTQtw9LmQeB58zcwOsqlQ==", + "requires": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.2.1" + } + }, + "rc-trigger": { + "version": "5.2.10", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-5.2.10.tgz", + "integrity": "sha512-FkUf4H9BOFDaIwu42fvRycXMAvkttph9AlbCZXssZDVzz2L+QZ0ERvfB/4nX3ZFPh1Zd+uVGr1DEDeXxq4J1TA==", + "requires": { + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "rc-align": "^4.0.0", + "rc-motion": "^2.0.0", + "rc-util": "^5.5.0" + } + } + } + }, + "rc-textarea": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.3.5.tgz", + "integrity": "sha512-qa+k5vDn9ct65qr+SgD2KwJ9Xz6P84lG2z+TDht/RBr71WnM/K61PqHUAcUyU6YqTJD26IXgjPuuhZR7HMw7eA==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.7.0" + }, + "dependencies": { + "rc-resize-observer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.0.1.tgz", + "integrity": "sha512-OxO2mJI9e8610CAWBFfm52SPvWib0eNKjaSsRbbKHmLaJIxw944P+D61DlLJ/w2vuOjGNcalJu8VdqyNm/XCRg==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-util": "^5.0.0", + "resize-observer-polyfill": "^1.5.1" + } + } + } + }, + "rc-tooltip": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-4.2.3.tgz", + "integrity": "sha512-7ySkaPGeqLLM4a/QYrKQ280aDthPxyvjJqQMstWX/AWX7/b1p23HIdHXdjBkziuvcnvXkW4lgZdFTVsylDiX1w==", + "requires": { + "@babel/runtime": "^7.11.2", + "rc-trigger": "^4.2.1" + } + }, + "rc-tree": { + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-3.9.5.tgz", + "integrity": "sha512-ZGVl1o83hZoz971pzY9Y7yZM+f9qcia1Gym+QNyc3zMGQVshbr6CX2WZ8xUK18tTkdRSqbTXmZFnvYf1lfqT8A==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^1.0.0", + "rc-util": "^5.0.0", + "rc-virtual-list": "^3.0.1" + } + }, + "rc-tree-select": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-4.1.3.tgz", + "integrity": "sha512-vk/T1vHNvuBZyoq8CvOF6iaiyVe6Y8QmQflTYFgabVsTJ1d/obkO9tAXOvJELZgKJ9ljduDVaAZAgcq0Yap+mg==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-select": "^11.1.1", + "rc-tree": "^3.8.0", + "rc-util": "^5.0.5" + } + }, + "rc-trigger": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-4.4.3.tgz", + "integrity": "sha512-yq/WyuiPwxd2q6jy+VPyy0GUCRFJ2eFqAaCwPE27AOftXeIupOcJ/2t1wakSq63cfk7qtzev5DKHUAjb8LOJCw==", + "requires": { + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "raf": "^3.4.1", + "rc-align": "^4.0.0", + "rc-motion": "^1.0.0", + "rc-util": "^5.0.1" + } + }, + "rc-upload": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-3.2.1.tgz", + "integrity": "sha512-gmIy08tco2YFTSiru9zgeTmUcDKPyUMUUBdUIjG2CcHz4jdbpaPx/RL/Wz9CMD6ppQizK0gES0rcQ1+o5frK2Q==", + "requires": { + "classnames": "^2.2.5" + } + }, + "rc-util": { + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.13.2.tgz", + "integrity": "sha512-eYc71XXGlp96RMzg01Mhq/T3BL6OOVTDSS0urFEuvpi+e7slhJRhaHGCKy2hqJm18m9ff7VoRoptplKu60dYog==", + "requires": { + "@babel/runtime": "^7.12.5", + "react-is": "^16.12.0", + "shallowequal": "^1.1.0" + } + }, + "rc-virtual-list": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.2.tgz", + "integrity": "sha512-OyVrrPvvFcHvV0ssz5EDZ+7Rf5qLat/+mmujjchNw5FfbJWNDwkpQ99EcVE6+FtNRmX9wFa1LGNpZLUTvp/4GQ==", + "requires": { + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.0.7" + }, + "dependencies": { + "rc-resize-observer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.0.1.tgz", + "integrity": "sha512-OxO2mJI9e8610CAWBFfm52SPvWib0eNKjaSsRbbKHmLaJIxw944P+D61DlLJ/w2vuOjGNcalJu8VdqyNm/XCRg==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-util": "^5.0.0", + "resize-observer-polyfill": "^1.5.1" + } + } + } + }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-copy-to-clipboard": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz", + "integrity": "sha512-ELKq31/E3zjFs5rDWNCfFL4NvNFQvGRoJdAKReD/rUPA+xxiLPQmZBZBvy2vgH7V0GE9isIQpT9WXbwIVErYdA==", + "requires": { + "copy-to-clipboard": "^3", + "prop-types": "^15.5.8" + } + }, + "react-device-detect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-1.6.2.tgz", + "integrity": "sha512-XIBgwIfpGAknm7tXe/YNbx4ieIR7IyFI3KNfSQk4UjHVy97UHe/nB7iJj8R/dDsI+I/ZzPR4HJ39Gh5tI4nhxw==" + }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-event-listener": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.6.tgz", + "integrity": "sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==", + "requires": { + "@babel/runtime": "^7.2.0", + "prop-types": "^15.6.0", + "warning": "^4.0.1" + } + }, + "react-ionicons": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/react-ionicons/-/react-ionicons-2.1.6.tgz", + "integrity": "sha1-ycRywkL0HyKKYmH9bOFW1U4Odnc=", + "requires": { + "prop-types": "15.5.10", + "react": "15.4.2", + "react-dom": "15.4.2", + "styled-components": "2.2.3" + }, + "dependencies": { + "prop-types": { + "version": "15.5.10", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1" + } + }, + "react": { + "version": "15.4.2", + "resolved": "https://registry.npmjs.org/react/-/react-15.4.2.tgz", + "integrity": "sha1-QfeZGyYYU5K6m66WyIiefgGDl+8=", + "requires": { + "fbjs": "^0.8.4", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0" + } + }, + "react-dom": { + "version": "15.4.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.4.2.tgz", + "integrity": "sha1-AVNj8FsKH9Uq6e/dOgBg2QaVII8=", + "requires": { + "fbjs": "^0.8.1", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0" + } + } + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-modal": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.8.1.tgz", + "integrity": "sha512-aLKeZM9pgXpIKVwopRHMuvqKWiBajkqisDA8UzocdCF6S4fyKVfLWmZR5G1Q0ODBxxxxf2XIwiCP8G/11GJAuw==", + "requires": { + "exenv": "^1.2.0", + "prop-types": "^15.5.10", + "react-lifecycles-compat": "^3.0.0", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "react-redux": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.1.tgz", + "integrity": "sha512-T52I52Kxhbqy/6TEfBv85rQSDz6+Y28V/pf52vDWs1YRXG19mcFOGfHnY2HsNFHyhP+ST34Aih98fvt6tqwVcQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.8.2" + } + }, + "react-svg": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/react-svg/-/react-svg-11.2.2.tgz", + "integrity": "sha512-Q9lAuUgaI8c55OluftgrAyE4GAmIvmqVHJXt1fIzXFHcuIauKqVjhUH/BiizOqjPmY+R27kiTkv9g7AxY7O+wA==", + "requires": { + "@babel/runtime": "^7.12.5", + "@tanem/svg-injector": "^8.2.1", + "prop-types": "^15.7.2" + } + }, + "read-pkg": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", + "integrity": "sha512-+UBirHHDm5J+3WDmLBZYSklRYg82nMlz+enn+GMZ22nSR2f4bzxmhso6rzQW/3mT2PVzpzDTiYIZahk8UmZ44w==", + "dev": true, + "requires": { + "normalize-package-data": "^2.3.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "redux": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", + "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, + "redux-form": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/redux-form/-/redux-form-8.1.0.tgz", + "integrity": "sha512-d2+0OaJpSq3kwkbPtFlG3W/HENWLxX8NqqTHSOnfgIrID/9faH/rxejLa1X3HChilCTm71zWe/g9zaLPCMCofQ==", + "requires": { + "@babel/runtime": "^7.2.0", + "es6-error": "^4.1.1", + "hoist-non-react-statics": "^3.2.1", + "invariant": "^2.2.4", + "is-promise": "^2.1.0", + "lodash": "^4.17.11", + "lodash-es": "^4.17.11", + "prop-types": "^15.6.1", + "react-is": "^16.7.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true, + "optional": true + }, + "renderkid": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "dev": true, + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true + } + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "scroll-into-view-if-needed": { + "version": "2.2.28", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz", + "integrity": "sha512-8LuxJSuFVc92+0AdNv4QOxRL4Abeo1DgLnGNkn1XlaujPH/3cCFz3QI60r2VNu4obJJROzgnIUw5TKQkZvZI1w==", + "requires": { + "compute-scroll-into-view": "^1.0.17" + } + }, + "secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true + }, + "seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "styled-components": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-2.2.3.tgz", + "integrity": "sha512-KzdZv4zyZPLoM4V90Tu+3evqTBZt1quFC1DBt5SA7k4dY3ANWmK+LZiIk/Q99GzLisBiEjV+Fn9nyty9rrZ1jw==", + "requires": { + "buffer": "^5.0.3", + "css-to-react-native": "^2.0.3", + "fbjs": "^0.8.9", + "hoist-non-react-statics": "^1.2.0", + "is-function": "^1.0.1", + "is-plain-object": "^2.0.1", + "prop-types": "^15.5.4", + "stylis": "3.x", + "supports-color": "^3.2.3" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "hoist-non-react-statics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "stylis": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz", + "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "tinycolor2": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz", + "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==" + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.0.tgz", + "integrity": "sha512-TuQzwiT4DDg19beHam3E66oRXhyqlyfgjHB/5fcvsRXbfmWPJfto9B4a0TBdTrQAPGlGmXh/k7iUI+WsObgORA==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.28", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", + "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==" + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true + }, + "union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "requires": { + "qs": "^6.4.0" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "optional": true + }, + "update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true + } + } + }, + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true + } + } + }, + "webpack": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", + "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.3.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-assets-manifest": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/webpack-assets-manifest/-/webpack-assets-manifest-3.1.1.tgz", + "integrity": "sha512-JV9V2QKc5wEWQptdIjvXDUL1ucbPLH2f27toAY3SNdGZp+xSaStAgpoMcvMZmqtFrBc9a5pTS1058vxyMPOzRQ==", + "dev": true, + "requires": { + "chalk": "^2.0", + "lodash.get": "^4.0", + "lodash.has": "^4.0", + "mkdirp": "^0.5", + "schema-utils": "^1.0.0", + "tapable": "^1.0.0", + "webpack-sources": "^1.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz", + "integrity": "sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.19", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + } + } + }, + "webpack-cli": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz", + "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.1.1", + "findup-sync": "^3.0.0", + "global-modules": "^2.0.0", + "import-local": "^2.0.0", + "interpret": "^1.4.0", + "loader-utils": "^1.4.0", + "supports-color": "^6.1.0", + "v8-compile-cache": "^2.1.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/plugins/package.json b/plugins/package.json new file mode 100644 index 0000000000..5ac163bc9c --- /dev/null +++ b/plugins/package.json @@ -0,0 +1,108 @@ +{ + "name": "hollaex-plugins", + "version": "1.0.0", + "description": "HollaEx Official Plugins Repo", + "private": true, + "browser": "dist/main.js", + "repository": { + "type": "git", + "url": "git+https://github.com/bitholla/hollaex-plugins.git" + }, + "author": "bitHolla", + "license": "ISC", + "scripts": { + "kill-8080": "lsof -ti:8080 | xargs kill || exit 0", + "build:web": "npm run clean && cross-env NODE_ENV=production webpack --env.plugin=$npm_config_plugin --mode production", + "build:dev": "npm run clean && cross-env NODE_ENV=development webpack --env.plugin=$npm_config_plugin --mode development", + "start": "cross-env NODE_ENV=development concurrently \"npm run dev --plugin=$npm_config_plugin\" \"npm run json:watch --plugin=$npm_config_plugin\" \"npm run server\"", + "dev": "npm run clean && cross-env NODE_ENV=development webpack --env.plugin=$npm_config_plugin --mode development --config webpack-dev.config.js", + "clean": "rimraf dist", + "remove:json": "rimraf json", + "lint": "eslint .", + "json": "PLUGIN=$npm_config_plugin node scripts/generateJson.js", + "generate-json": "npm run remove:json && PLUGIN=$npm_config_plugin node scripts/generateJson.js", + "add-plugin": "PLUGIN=$npm_config_plugin TYPE=$npm_config_type node scripts/addPlugin.js", + "add-view": "PLUGIN=$npm_config_plugin WEB_VIEW=$npm_config_webview node scripts/addView.js", + "publish": "npm run build:web --plugin=$npm_config_plugin && npm run generate-json --plugin=$npm_config_plugin", + "publish:dev": "npm run build:dev --plugin=$npm_config_plugin && npm run generate-json --plugin=$npm_config_plugin", + "json:watch": "npm run remove:json && onchange -i 'src/plugins/**/assets/*.json' 'src/plugins/**/views/**/*.json' -- npm run json --plugin=$npm_config_plugin", + "server": "npm run kill-8080 && node server.js", + "test": "echo \"Error: no test specified\" && exit 1", + "build": "npm run publish:web --plugin=$npm_config_plugin && npm run generate:json --plugin=$npm_config_plugin", + "generate:json": "PLUGIN=$npm_config_plugin node generateJson.js", + "add-bundles": "node scripts/addBundles.js", + "add-jsons": "node scripts/addJsons.js", + "publish:web": "npm run publish --plugin=$npm_config_plugin && npm run add-bundles && npm run add-jsons" + }, + "dependencies": { + "@ant-design/icons": "4.2.2", + "antd": "4.6.2", + "axios": "0.21.1", + "classnames": "2.2.6", + "hollaex-web-lib": "0.3.0", + "html-minifier-terser": "5.1.1", + "lodash.debounce": "4.0.8", + "mathjs": "5.10.3", + "moment": "2.24.0", + "numbro": "1.11.1", + "prop-types": "15.7.2", + "react": "16.13.1", + "react-device-detect": "1.6.2", + "react-event-listener": "0.6.6", + "react-ionicons": "2.1.6", + "react-modal": "3.8.1", + "react-redux": "6.0.1", + "react-svg": "11.2.2", + "redux": "4.0.1", + "redux-form": "8.1.0", + "uglify-es": "3.3.9", + "validator": "10.11.0" + }, + "devDependencies": { + "@babel/cli": "7.12.10", + "@babel/core": "7.12.10", + "@babel/plugin-proposal-class-properties": "7.12.1", + "@babel/plugin-transform-runtime": "7.12.10", + "@babel/preset-env": "7.12.10", + "@babel/preset-react": "7.12.10", + "@babel/runtime": "7.12.5", + "@paciolan/eslint-config-react": "1.0.4", + "@paciolan/remote-component": "2.10.2", + "babel-eslint": "10.1.0", + "babel-loader": "8.2.2", + "babel-plugin-transform-react-remove-prop-types": "0.4.24", + "concurrently": "5.3.0", + "copy-dir": "1.3.0", + "core-js": "2.6.12", + "cross-env": "7.0.3", + "css-loader": "5.2.6", + "eslint": "7.15.0", + "eslint-config-prettier": "6.15.0", + "eslint-plugin-babel": "5.3.1", + "eslint-plugin-prettier": "3.3.0", + "eslint-plugin-react": "7.21.5", + "express": "4.17.1", + "glob": "7.1.7", + "html-webpack-plugin": "4.5.0", + "http-server": "14.0.0", + "json-beautify": "1.1.1", + "lodash.merge": "4.6.2", + "mkdirp": "1.0.4", + "nocache": "3.0.1", + "onchange": "7.1.0", + "path": "0.12.7", + "prettier": "2.2.1", + "react-dom": "16.14.0", + "regenerator-runtime": "0.13.7", + "rimraf": "3.0.2", + "style-loader": "2.0.0", + "webpack": "4.44.2", + "webpack-assets-manifest": "3.1.1", + "webpack-bundle-analyzer": "3.9.0", + "webpack-cli": "3.3.12" + }, + "bugs": { + "url": "https://github.com/bitholla/hollaex-plugins/issues" + }, + "homepage": "https://github.com/bitholla/hollaex-plugins#readme" +} diff --git a/plugins/remote-component.config.js b/plugins/remote-component.config.js new file mode 100644 index 0000000000..8b9f079fb7 --- /dev/null +++ b/plugins/remote-component.config.js @@ -0,0 +1,25 @@ +/** + * Dependencies for Remote Components + */ + +module.exports = { + resolve: { + axios: require('axios'), + classnames: require('classnames'), + mathjs: require('mathjs'), + numbro: require('numbro'), + 'prop-types': require('prop-types'), + react: require('react'), + 'react-device-detect': require('react-device-detect'), + 'react-redux': require('react-redux'), + 'react-svg': require('react-svg'), + redux: require('redux'), + 'redux-form': require('redux-form'), + validator: require('validator'), + '@ant-design/icons': require('@ant-design/icons'), + 'react-event-listener': require('react-event-listener'), + 'hollaex-web-lib': require('hollaex-web-lib'), + 'antd': require('antd'), + moment: require('moment'), + } +}; diff --git a/plugins/scripts/addBundles.js b/plugins/scripts/addBundles.js new file mode 100644 index 0000000000..cfbb6fb85f --- /dev/null +++ b/plugins/scripts/addBundles.js @@ -0,0 +1,28 @@ +const fs = require("fs"); +const path = require("path"); +const glob = require("glob"); +const mkdirp = require('mkdirp'); +const { PATTERNS, PATHS } = require("./patterns"); + +const bundles = glob.sync(PATTERNS.BUNDLES); + +bundles.forEach((pathname) => { + const bundle = path.basename(pathname, '.js'); + const [plugin, view] = bundle.split('__'); + const pluginPath = path.resolve(__dirname, '..', PATHS.ROOT, plugin); + const bundlesPath = path.resolve(__dirname, '..', PATHS.ROOT, plugin, PATHS.BUNDLES); + + if (view) { + const destinationPath = path.resolve(bundlesPath, `${bundle}.js`); + + if (!fs.existsSync(pluginPath)) { + mkdirp.sync(pluginPath); + } + + if (!fs.existsSync(bundlesPath)) { + mkdirp.sync(bundlesPath); + } + + fs.copyFileSync(pathname, destinationPath); + } +}) \ No newline at end of file diff --git a/plugins/scripts/addJsons.js b/plugins/scripts/addJsons.js new file mode 100644 index 0000000000..2e193bde87 --- /dev/null +++ b/plugins/scripts/addJsons.js @@ -0,0 +1,24 @@ +const fs = require("fs"); +const path = require("path"); +const glob = require("glob"); +const mkdirp = require('mkdirp'); +const { PATTERNS, FILES, PATHS } = require("./patterns"); + +const jsons = glob.sync(PATTERNS.JSONS); + +jsons.forEach((pathname) => { + const plugin = path.basename(pathname, '.json'); + const pluginPath = path.resolve(__dirname, '..', PATHS.ROOT, plugin); + const serverPath = path.resolve(__dirname, '..', PATHS.ROOT, plugin, PATHS.SERVER); + const destinationPath = path.resolve(__dirname, '..', PATHS.ROOT, plugin, PATHS.SERVER, FILES.WEB_VIEW); + + if (!fs.existsSync(pluginPath)) { + mkdirp.sync(pluginPath); + } + + if (!fs.existsSync(serverPath)) { + mkdirp.sync(serverPath); + } + + fs.copyFileSync(pathname, destinationPath); +}) \ No newline at end of file diff --git a/plugins/scripts/addPlugin.js b/plugins/scripts/addPlugin.js new file mode 100644 index 0000000000..a1efe029df --- /dev/null +++ b/plugins/scripts/addPlugin.js @@ -0,0 +1,66 @@ +const glob = require("glob"); +const path = require("path"); +const copydir = require('copy-dir'); +const mkdirp = require('mkdirp'); +const { PATTERNS, TEMPLATES, PATHS } = require("./patterns"); + +const { env: { PLUGIN: plugin, TYPE } } = process; +const type = TYPE ? TYPE.toLowerCase() : TYPE; + +if (!plugin) { + console.log('You must pass plugin argument'); + console.log('npm run add-plugin --plugin=PLUGIN_NAME --type=PLUGIN_TYPE'); +} else if (!type) { + console.log('You must pass type argument'); + console.log('npm run add-plugin --plugin=PLUGIN_NAME --type=PLUGIN_TYPE'); +} else { + const plugins = glob.sync(PATTERNS.PLUGINS) + .reduce((acc, curr) => { + const pluginName = curr.split(path.sep)[2]; + return [...acc, pluginName]; + }, []) + + if (plugins.includes(plugin)) { + console.log('This plugin already exists, try another plugin name'); + } else { + switch (type) { + case TEMPLATES.RAW.type: + return ( + mkdirp(`${PATHS.ROOT}/${plugin}/views`) + .then(() => { + mkdirp(`${PATHS.ROOT}/${plugin}/${PATHS.SERVER}`) + .then(() => { + copydir.sync(TEMPLATES.RAW.template.VIEW, `${PATHS.ROOT}/${plugin}/views/view`); + copydir.sync(TEMPLATES.RAW.template.ASSETS, `${PATHS.ROOT}/${plugin}/assets`); + console.log('Plugin has been added successfully'); + }) + }) + ); + case TEMPLATES.PAGE.type: + case TEMPLATES.VERIFICATION_TAB.type: + case TEMPLATES.FIAT_WALLET.type: + case TEMPLATES.KYC.type: + case TEMPLATES.BANK.type: + case TEMPLATES.ONRAMP.type: + return ( + mkdirp(`${PATHS.ROOT}/${plugin}`) + .then(() => { + mkdirp(`${PATHS.ROOT}/${plugin}/${PATHS.SERVER}`) + .then(() => { + const [, { template }] = Object.entries(TEMPLATES).find(([, { type: templateType }]) => type === templateType); + copydir.sync(template, `${PATHS.ROOT}/${plugin}`); + console.log('Plugin has been added successfully'); + }) + }) + ); + case TEMPLATES.SERVER.type: + return ( + mkdirp(`${PATHS.ROOT}/${plugin}/${PATHS.SERVER}`) + ); + default: + return ( + console.log(`type ${type} does not exist, try another type. See doc for supported plugin types.`) + ); + } + } +} \ No newline at end of file diff --git a/plugins/scripts/addView.js b/plugins/scripts/addView.js new file mode 100644 index 0000000000..2d16a7cee3 --- /dev/null +++ b/plugins/scripts/addView.js @@ -0,0 +1,36 @@ +const glob = require("glob"); +const path = require("path"); +const copydir = require('copy-dir'); +const { PATTERNS, PATHS } = require("./patterns") + +const { env: { PLUGIN: plugin, WEB_VIEW: view = 'view' } } = process; +const viewsPattern = `${PATHS.ROOT}/${plugin}/views/*`; + + +if (!plugin) { + console.log('You must pass plugin argument') + console.log('npm run add-view --plugin=PLUGIN_NAME'); +} else { + const plugins = glob.sync(PATTERNS.PLUGINS) + .reduce((acc, curr) => { + const pluginName = curr.split(path.sep)[2]; + return [...acc, pluginName]; + }, []) + + if (plugins.includes(plugin)) { + const views = glob.sync(viewsPattern) + .reduce((acc, curr) => { + const viewName = curr.split(path.sep)[4]; + return [...acc, viewName]; + }, []); + + if (views.includes(view)) { + console.log('This view already exists, try another view name'); + } else { + copydir.sync('src/templates/view', `${PATHS.ROOT}/${plugin}/views/${view}`); + console.log(`A view has been successfully added to ${plugin} plugin`); + } + } else { + console.log(`Plugin ${plugin} does not exist`); + } +} \ No newline at end of file diff --git a/plugins/scripts/generateJson.js b/plugins/scripts/generateJson.js new file mode 100644 index 0000000000..0538379f12 --- /dev/null +++ b/plugins/scripts/generateJson.js @@ -0,0 +1,51 @@ +const fs = require("fs"); +const path = require("path"); +const glob = require("glob"); +const merge = require("lodash.merge"); +const mkdirp = require('mkdirp'); +const { PATTERNS, FILES, PATHS } = require("./patterns"); +const { readFile, saveFile, getBundlePath } = require("./utils"); + +const { env: { PLUGIN: plugin }} = process; +const pluginsPattern = plugin ? `${PATHS.ROOT}/${plugin}` : PATTERNS.PLUGINS; + +const plugins = glob.sync(pluginsPattern); + +if (!fs.existsSync('json')) { + mkdirp.sync('json') +} + +plugins.forEach(pluginPath => { + const pluginName = pluginPath.split(path.sep)[2] + const assetsPath = `${PATHS.ROOT}/${pluginName}/${PATHS.ASSETS}` + const pluginPattern = `${PATHS.ROOT}/${pluginName}/${PATTERNS.VIEW}`; + + let assetsAdded = false; + const assets = { + strings: readFile(`${assetsPath}/${FILES.STRINGS}`), + icons: readFile(`${assetsPath}/${FILES.ICONS}`), + }; + + const webViews = glob.sync(pluginPattern, { noglobstar: true }) + .reduce((acc, curr) => { + const view = readFile(`${path.dirname(curr)}/${FILES.VIEW}`); + const generatedView = { + src: getBundlePath(curr), + ...(assetsAdded ? {} : { meta: { ...assets }}), + }; + + // development + // const webView = merge({}, view, generatedView); + const webView = merge({}, generatedView, view); + + assetsAdded = true; + + return [...acc, webView] + }, []); + + const plugin = { + web_view: webViews + } + + saveFile(`json/${pluginName}.json`, plugin); +}) \ No newline at end of file diff --git a/plugins/scripts/patterns.js b/plugins/scripts/patterns.js new file mode 100644 index 0000000000..5f4562d424 --- /dev/null +++ b/plugins/scripts/patterns.js @@ -0,0 +1,70 @@ +const PLUGINS_PATH = 'https://bitholla.s3.ap-northeast-2.amazonaws.com/scripts/plugins'; + +const PATTERNS = { + BUNDLES: "dist/**.js", + JSONS: "json/**.json", + PLUGINS: "src/plugins/*", + VIEW: "views/**/index.js" +}; + +const FILES = { + SCRIPT: 'script.js', + ADMIN_VIEW: 'admin_view.html', + WEB_VIEW: 'web_view.json', + CONFIG: 'config.json', + STRINGS: "strings.json", + ICONS: "icons.json", + VIEW: "view.json", +}; + +const PATHS = { + ROOT: 'src/plugins', + SERVER: 'server', + BUNDLES: 'bundles', + ASSETS: 'assets', +}; + +const TEMPLATES = { + RAW: { + type: 'raw', + template: { + VIEW: "src/templates/view", + ASSETS: "src/templates/assets", + }, + }, + PAGE: { + type: 'page', + template: 'src/templates/new-page', + }, + VERIFICATION_TAB: { + type: 'verification-tab', + template: 'src/templates/verification-tab', + }, + FIAT_WALLET: { + type: 'fiat-wallet', + template: 'src/templates/fiat-wallet', + }, + KYC: { + type: 'kyc', + template: 'src/templates/kyc-verification', + }, + BANK: { + type: 'bank', + template: 'src/templates/bank-verification', + }, + ONRAMP: { + type: 'onramp', + template: 'src/templates/onramp', + }, + SERVER: { + type: 'server', + } +}; + +module.exports = { + PLUGINS_PATH, + FILES, + TEMPLATES, + PATHS, + PATTERNS, +} \ No newline at end of file diff --git a/plugins/scripts/utils.js b/plugins/scripts/utils.js new file mode 100644 index 0000000000..b4487dfcd5 --- /dev/null +++ b/plugins/scripts/utils.js @@ -0,0 +1,39 @@ +const fs = require("fs"); +const path = require("path"); +const beautify = require("json-beautify"); +const { PLUGINS_PATH, PATHS, FILES } = require("./patterns"); + +const saveFile = (output, content) => { + fs.writeFileSync(output, beautify(content, null, 4, 100)); +} + +const readFile = (pathname) => { + try { + const contents = fs.readFileSync(pathname, "utf-8") + return JSON.parse(contents); + } catch(err) { + console.log(err); + return {}; + } +} + +const getBundlePath = (pathname) => { + const dir = pathname.split(path.sep); + const pluginName = dir[2]; + const bundleName = `${dir[2]}__${dir[4]}`; + + if(process.env.NODE_ENV === "development") { + return `http://localhost:8080/${bundleName}.js` + } + + const configFile = path.resolve(__dirname, '..', PATHS.ROOT, pluginName, PATHS.SERVER, FILES.CONFIG); + const { version = 0 } = fs.existsSync(configFile) ? readFile(configFile) : {}; + + return `${PLUGINS_PATH}/${pluginName}/v${version}/${bundleName}.js` +} + +module.exports = { + saveFile, + readFile, + getBundlePath, +} \ No newline at end of file diff --git a/plugins/server.js b/plugins/server.js new file mode 100644 index 0000000000..8234599259 --- /dev/null +++ b/plugins/server.js @@ -0,0 +1,10 @@ +const express = require('express'); +const nocache = require('nocache'); +const app = express(); +const PORT = 8080; + +app.use(nocache()); +app.use(express.static('dist')); +app.use(express.static('json')); + +app.listen(PORT, () => console.log(`Server listening on port: ${PORT}`)); \ No newline at end of file diff --git a/plugins/src/components/Dialog/DesktopDialog.js b/plugins/src/components/Dialog/DesktopDialog.js new file mode 100644 index 0000000000..1cdd7afd9b --- /dev/null +++ b/plugins/src/components/Dialog/DesktopDialog.js @@ -0,0 +1,87 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import Modal from 'react-modal'; +import Ionicon from 'react-ionicons'; +import { ActionNotification, Button } from 'hollaex-web-lib'; + +class Dialog extends PureComponent { + static propTypes = { + isOpen: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + closeButton: PropTypes.func, + onCloseDialog: PropTypes.func, + children: PropTypes.node.isRequired, + disableTheme: PropTypes.bool, + }; + + onRequestClose = (e) => { + if (this.props.onCloseDialog) { + this.props.onCloseDialog(e); + } + }; + + render() { + const { + isOpen, + children, + label, + closeButton, + shouldCloseOnOverlayClick, + showCloseText, + dialogId, + theme, + className, + disableTheme, + bodyOpenClassName, + strings: STRINGS, + } = this.props; + + return ( + + {showCloseText && !closeButton && ( + + } + onClick={this.onRequestClose} + className="close-button" + /> + )} + {children} + {closeButton && ( +
+
+ )} +
+ ); + } +} + +Modal.setAppElement('#root'); + +Dialog.defaultProps = { + disableTheme: false, + shouldCloseOnOverlayClick: true, + showCloseText: true, + theme: '', + className: '', + strings: {}, +}; + +export default Dialog; diff --git a/plugins/src/components/Dialog/MobileDialog.js b/plugins/src/components/Dialog/MobileDialog.js new file mode 100644 index 0000000000..8fa31921b4 --- /dev/null +++ b/plugins/src/components/Dialog/MobileDialog.js @@ -0,0 +1,73 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import Modal from 'react-modal'; + +const RegularContent = ({ children }) => { + return ( +
+
{children}
+
+ ); +}; + +class Dialog extends PureComponent { + static propTypes = { + isOpen: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + closeButton: PropTypes.func, + onCloseDialog: PropTypes.func, + children: PropTypes.node.isRequired, + }; + + onRequestClose = (e) => { + if (this.props.onCloseDialog) { + this.props.onCloseDialog(e); + } + }; + + render() { + const { + isOpen, + children, + label, + dialogId, + className, + useFullScreen = false, + compressed = false, + } = this.props; + + return ( + + + {children} + + + ); + } +} + +Modal.setAppElement('#root'); + +Dialog.defaultProps = { + shouldCloseOnOverlayClick: true, + showCloseText: true, + theme: '', + className: '', +}; + +export default Dialog; diff --git a/plugins/src/components/Dialog/index.js b/plugins/src/components/Dialog/index.js new file mode 100644 index 0000000000..ef0da3afcb --- /dev/null +++ b/plugins/src/components/Dialog/index.js @@ -0,0 +1,14 @@ +import React from 'react'; +import { isMobile } from 'react-device-detect'; +import MobileDialog from './MobileDialog'; +import DesktopDialog from './DesktopDialog'; + +const Dialog = (props) => { + if (isMobile) { + return ; + } else { + return ; + } +}; + +export default Dialog; diff --git a/plugins/src/components/Form/factoryFields.js b/plugins/src/components/Form/factoryFields.js new file mode 100644 index 0000000000..c419dc03d0 --- /dev/null +++ b/plugins/src/components/Form/factoryFields.js @@ -0,0 +1,27 @@ +import React from 'react'; +import { Field } from 'redux-form'; +import { getFormFieldComponentByType } from 'hollaex-web-lib'; + +const renderFields = (fields = {}, callback) => { + return ( +
+ {Object.keys(fields).map((key, index) => { + const { type, validate = [], ishorizontalfield, ...rest } = fields[key]; + const commonProps = { + callback, + key, + name: key, + type, + validate, + ishorizontalfield, + ...rest, + }; + + const component = getFormFieldComponentByType(type); + return () + })} +
+ ); +}; + +export default renderFields; diff --git a/plugins/src/components/HeaderSection/index.js b/plugins/src/components/HeaderSection/index.js new file mode 100644 index 0000000000..966eff5d7d --- /dev/null +++ b/plugins/src/components/HeaderSection/index.js @@ -0,0 +1,45 @@ +import React from "react"; +import { Image, ActionNotification, Editable } from "hollaex-web-lib"; + +export const HeaderSection = ({ + title, + children, + openContactForm, + icon, + strings: STRINGS = {}, + icons: ICONS = {}, + stringId + }) => { + return ( +
+
+ {!!icon && ( +
+ +
+ )} +
+
+
+ {title} + {stringId && } +
+ {!!openContactForm && ( +
+ +
+ )} +
+ {children &&
{children}
} +
+
+
+ ); +}; diff --git a/plugins/src/components/KitContext/KitContextProvider.js b/plugins/src/components/KitContext/KitContextProvider.js new file mode 100644 index 0000000000..8594a4a570 --- /dev/null +++ b/plugins/src/components/KitContext/KitContextProvider.js @@ -0,0 +1,15 @@ +import React, { Component } from 'react'; +import { KitContext } from './index'; + +class KitContextProvider extends Component { + render() { + const { children, ...rest } = this.props; + return ( + + {children} + + ); + } +} + +export default KitContextProvider; diff --git a/plugins/src/components/KitContext/index.js b/plugins/src/components/KitContext/index.js new file mode 100644 index 0000000000..267c1c595a --- /dev/null +++ b/plugins/src/components/KitContext/index.js @@ -0,0 +1,4 @@ +import { createContext } from 'react'; +export { default as KitContextProvider } from './KitContextProvider'; +export { default as withKit } from './withKit'; +export const KitContext = createContext(); \ No newline at end of file diff --git a/plugins/src/components/KitContext/withKit.js b/plugins/src/components/KitContext/withKit.js new file mode 100644 index 0000000000..cf5b4d59ea --- /dev/null +++ b/plugins/src/components/KitContext/withKit.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { KitContext } from './index'; + +const withKit = (mapContextToProps) => (Component) => { + return (props) => ( + + {(context) => ( + + )} + + ); +}; + +export default withKit; diff --git a/plugins/src/components/OtpForm/index.js b/plugins/src/components/OtpForm/index.js new file mode 100644 index 0000000000..d6e8d6afaa --- /dev/null +++ b/plugins/src/components/OtpForm/index.js @@ -0,0 +1,93 @@ +import React, { Component } from 'react'; +import { reduxForm } from 'redux-form'; +import { required, validateOtp } from 'utils'; +import renderFields from 'components/Form/factoryFields'; +import { Button, IconTitle } from 'hollaex-web-lib'; + +class Form extends Component { + state = { + formValues: {}, + }; + + componentWillMount() { + this.setFormValues(); + } + + UNSAFE_componentWillReceiveProps = (nextProps) => { + if ( + this.props.dirty !== nextProps.dirty || + this.props.submitFailed !== nextProps.submitFailed || + this.props.valid !== nextProps.valid + ) { + if (nextProps.dirty && nextProps.submitFailed && !nextProps.valid) { + this.setFormRef(this.otpFormRef); + } + } + }; + setFormValues = () => { + const { strings: STRINGS } = this.props; + const formValues = { + otp_code: { + type: 'number', + stringId: + 'OTP_FORM.OTP_LABEL,OTP_FORM.OTP_PLACEHOLDER,OTP_FORM.ERROR_INVALID', + label: STRINGS['OTP_FORM.OTP_LABEL'], + placeholder: STRINGS['OTP_FORM.OTP_PLACEHOLDER'], + validate: [required, validateOtp(STRINGS['OTP_FORM.ERROR_INVALID'])], + fullWidth: true, + }, + }; + + this.setState({ formValues }); + }; + + setFormRef = (el) => { + if (el) { + this.otpFormRef = el; + el.getElementsByTagName('input')[0].focus(); + } + }; + + render() { + const { + handleSubmit, + submitting, + pristine, + error, + valid, + icons: ICONS = {}, + strings: STRINGS = {}, + } = this.props; + const { formValues } = this.state; + + return ( +
+ +
+ + {STRINGS['OTP_FORM.OTP_FORM_TITLE']} + +
+
+
+ {renderFields(formValues)} + {error &&
{error}
} +
+
+ ); + } +} + +export default reduxForm({ + form: 'FiatOtpForm', +})(Form); diff --git a/plugins/src/components/Tab/index.js b/plugins/src/components/Tab/index.js new file mode 100644 index 0000000000..400dd32fba --- /dev/null +++ b/plugins/src/components/Tab/index.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { Image } from 'hollaex-web-lib'; +import classnames from 'classnames'; + +export const Tab = ({ + icon, + title, + className, + }) => { + return ( +
+
+ +
+ {title && ( +
+ {title} +
+ )} +
+ ); +}; \ No newline at end of file diff --git a/plugins/src/components/Title.js b/plugins/src/components/Title.js new file mode 100644 index 0000000000..31fc6170fd --- /dev/null +++ b/plugins/src/components/Title.js @@ -0,0 +1,11 @@ +import React from "react"; +import { withKit } from 'components/KitContext'; + +const Title = ({ user: { username } = {}, strings: STRINGS, generateId }) => ( +
+ {STRINGS.formatString(STRINGS[generateId('hello')], username)} +
+); + +const mapContextToProps = ({ user, generateId, strings }) => ({ user, generateId, strings }); +export default withKit(mapContextToProps)(Title); diff --git a/plugins/src/constants/index.js b/plugins/src/constants/index.js new file mode 100644 index 0000000000..595768ca02 --- /dev/null +++ b/plugins/src/constants/index.js @@ -0,0 +1 @@ +export const verifiedBankStatus = 3; \ No newline at end of file diff --git a/plugins/src/index.html b/plugins/src/index.html new file mode 100644 index 0000000000..132678c44d --- /dev/null +++ b/plugins/src/index.html @@ -0,0 +1,9 @@ + + + + + + +
+ + diff --git a/plugins/src/lib/__tests__/prop.test.js b/plugins/src/lib/__tests__/prop.test.js new file mode 100644 index 0000000000..b9fcbb947a --- /dev/null +++ b/plugins/src/lib/__tests__/prop.test.js @@ -0,0 +1,39 @@ +const { prop } = require("../prop"); + +describe("lib/prop", () => { + test("prop on undefined returns undefined", () => { + const expected = undefined; + const actual = prop(["a"], undefined); + expect(actual).toBe(expected); + }); + + test("prop on null returns undefined", () => { + const expected = undefined; + const actual = prop(["a"], null); + expect(actual).toBe(expected); + }); + + test("prop[] returns object", () => { + const expected = {}; + const actual = prop([], expected); + expect(actual).toBe(expected); + }); + + test("prop[a] returns object[a]", () => { + const expected = "SUCCESS"; + const actual = prop(["a"], { a: expected }); + expect(actual).toBe(expected); + }); + + test("prop missing prop returns undefined", () => { + const expected = undefined; + const actual = prop(["a", "b"], { a: 123 }); + expect(actual).toBe(expected); + }); + + test("prop nested returns value", () => { + const expected = "SUCCESS"; + const actual = prop(["a", "b"], { a: { b: expected } }); + expect(actual).toBe(expected); + }); +}); diff --git a/plugins/src/lib/prop.js b/plugins/src/lib/prop.js new file mode 100644 index 0000000000..6ec0da268b --- /dev/null +++ b/plugins/src/lib/prop.js @@ -0,0 +1,10 @@ +/** + * Safely gets nested properties from an object + * @param {Array} key List of properties + * @param {object} object Object to query + * @returns {*} value + */ +export const prop = ([key, ...rest], object) => + key == null ? object + : object == null ? undefined + : prop(rest, object[key]); // prettier-ignore diff --git a/plugins/src/plugins/hello-exchange/assets/icons.json b/plugins/src/plugins/hello-exchange/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/plugins/hello-exchange/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/plugins/hello-exchange/assets/strings.json b/plugins/src/plugins/hello-exchange/assets/strings.json new file mode 100644 index 0000000000..ab9c4de4f6 --- /dev/null +++ b/plugins/src/plugins/hello-exchange/assets/strings.json @@ -0,0 +1,6 @@ +{ + "en": { + "title": "New Page", + "content": "{0} content" + } +} \ No newline at end of file diff --git a/plugins/src/plugins/hello-exchange/server/config.json b/plugins/src/plugins/hello-exchange/server/config.json new file mode 100644 index 0000000000..6345223a3d --- /dev/null +++ b/plugins/src/plugins/hello-exchange/server/config.json @@ -0,0 +1,35 @@ +{ + "name": "hello-exchange", + "version": 1, + "type": null, + "author": "bitHolla", + "bio": "Say hello from an exchange", + "description": "Demo plugin for proof of concept", + "documentation": null, + "logo": null, + "icon": null, + "url": null, + "public_meta": { + "public_message": { + "type": "string", + "required": false, + "description": "Not a secret", + "value": "Hello Exchange!" + } + }, + "meta": { + "private_message": { + "type": "string", + "required": false, + "description": "A secret", + "value": "hello exchange..." + } + }, + "prescript": { + "install": ["hello-world-npm"], + "run": null + }, + "postscript": { + "run": null + } +} \ No newline at end of file diff --git a/plugins/src/plugins/hello-exchange/server/hello-exchange.json b/plugins/src/plugins/hello-exchange/server/hello-exchange.json new file mode 100644 index 0000000000..97701b3788 --- /dev/null +++ b/plugins/src/plugins/hello-exchange/server/hello-exchange.json @@ -0,0 +1,40 @@ +{ + "name": "hello-exchange", + "version": 1, + "type": null, + "author": "bitHolla", + "bio": "Say hello from an exchange", + "description": "Demo plugin for proof of concept", + "documentation": null, + "logo": null, + "icon": null, + "url": null, + "public_meta": { + "public_message": { + "type": "string", + "required": false, + "description": "Not a secret", + "value": "Hello Exchange!" + } + }, + "meta": { + "private_message": { + "type": "string", + "required": false, + "description": "A secret", + "value": "hello exchange..." + } + }, + "prescript": { + "install": [ + "hello-world-npm" + ], + "run": null + }, + "postscript": { + "run": null + }, + "script": "\"use strict\";const{publicMeta:publicMeta,meta:meta}=this.configValues,{app:app,loggerPlugin:loggerPlugin,toolsLib:toolsLib}=this.pluginLibraries,helloWorld=require(\"hello-world-npm\"),moment=require(\"moment\"),init=async()=>{if(loggerPlugin.info(\"HELLO-EXCHANGE PLUGIN initializing...\"),!meta.private.value)throw new Error(\"Configuration value private required\")};init().then(()=>{app.get(\"/plugins/hello-exchange/info\",(e,i)=>(loggerPlugin.verbose(e.uuid,\"GET /plugins/hello-exchange/info\"),i.json({public_message:publicMeta.public.value,private_message:meta.private.value,library_message:helloWorld(),moment_timestamp:moment().toISOString(),exchange_info:toolsLib.getKitConfig().info})))}).catch(e=>{loggerPlugin.error(\"HELLO-EXCHANGE PLUGIN error during initialization\",e.message)});", + "admin_view": null, + "web_view": null +} \ No newline at end of file diff --git a/plugins/src/plugins/hello-exchange/server/script.js b/plugins/src/plugins/hello-exchange/server/script.js new file mode 100644 index 0000000000..29de80528b --- /dev/null +++ b/plugins/src/plugins/hello-exchange/server/script.js @@ -0,0 +1,44 @@ +'use strict'; + +const { publicMeta, meta } = this.configValues; +const { + app, + loggerPlugin, + toolsLib +} = this.pluginLibraries; +const helloWorld = require('hello-world-npm'); +const moment = require('moment'); + +const init = async () => { + loggerPlugin.info( + 'HELLO-EXCHANGE PLUGIN initializing...' + ); + + if (!meta.private_message.value) { + throw new Error('Configuration value private required'); + } +}; + +init() + .then(() => { + app.get('/plugins/hello-exchange/info', (req, res) => { + loggerPlugin.verbose( + req.uuid, + 'GET /plugins/hello-exchange/info' + ); + + return res.json({ + public_message: publicMeta.public_message.value, + private_message: meta.private_message.value, + library_message: helloWorld(), + moment_timestamp: moment().toISOString(), + exchange_info: toolsLib.getKitConfig().info + }); + }); + }) + .catch((err) => { + loggerPlugin.error( + 'HELLO-EXCHANGE PLUGIN error during initialization', + err.message + ); + }); diff --git a/plugins/src/plugins/hello-exchange/views/view/App.js b/plugins/src/plugins/hello-exchange/views/view/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/plugins/hello-exchange/views/view/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + +
+ + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/plugins/hello-exchange/views/view/Form.js b/plugins/src/plugins/hello-exchange/views/view/Form.js new file mode 100644 index 0000000000..ab0498fcb5 --- /dev/null +++ b/plugins/src/plugins/hello-exchange/views/view/Form.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { IconTitle } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; + +const Form = ({ + strings: STRINGS, + icons: ICONS, + generateId +}) => { + + return ( +
+ + +
+
+ {STRINGS.formatString(STRINGS[generateId('content')], STRINGS[generateId('title')])} +
+
+ +
+ ) +} + +const mapContextToProps = ({ strings, activeLanguage, icons, generateId }) => ({ + strings, + activeLanguage, + icons, + generateId, +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/plugins/hello-exchange/views/view/index.js b/plugins/src/plugins/hello-exchange/views/view/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/plugins/hello-exchange/views/view/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/plugins/hello-exchange/views/view/view.json b/plugins/src/plugins/hello-exchange/views/view/view.json new file mode 100644 index 0000000000..b4b2b347de --- /dev/null +++ b/plugins/src/plugins/hello-exchange/views/view/view.json @@ -0,0 +1,18 @@ +{ + "meta": { + "is_page": true, + "path": "/hello-exchange", + "hide_from_appbar": true, + "hide_from_sidebar": false, + "hide_from_menulist": false, + "hide_from_bottom_nav": true, + "string": { + "id": "title", + "is_global": false + }, + "icon": { + "id": "SIDEBAR_HELP", + "is_global": true + } + } +} \ No newline at end of file diff --git a/plugins/src/store/index.js b/plugins/src/store/index.js new file mode 100644 index 0000000000..3dbd7fd70d --- /dev/null +++ b/plugins/src/store/index.js @@ -0,0 +1,11 @@ +import { createStore, combineReducers } from "redux"; +import { reducer as formReducer } from "redux-form"; + +const rootReducer = combineReducers({ + // ...your other reducers here + // you have to pass formReducer under 'form' key, + // for custom keys look up the docs for 'getFormState' + form: formReducer +}); + +export default createStore(rootReducer); diff --git a/plugins/src/templates/assets/icons.json b/plugins/src/templates/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/templates/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/templates/assets/strings.json b/plugins/src/templates/assets/strings.json new file mode 100644 index 0000000000..6a691ed1b8 --- /dev/null +++ b/plugins/src/templates/assets/strings.json @@ -0,0 +1,5 @@ +{ + "en": { + "hello": "Hello {0}" + } +} \ No newline at end of file diff --git a/plugins/src/templates/bank-verification/assets/icons.json b/plugins/src/templates/bank-verification/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/templates/bank-verification/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/templates/bank-verification/assets/strings.json b/plugins/src/templates/bank-verification/assets/strings.json new file mode 100644 index 0000000000..c85476dd7f --- /dev/null +++ b/plugins/src/templates/bank-verification/assets/strings.json @@ -0,0 +1,8 @@ +{ + "en": { + "title": "Bank verification", + "go_to_tab": "Go to {0}", + "content": "{0} content", + "home-content": "{0} home content" + } +} \ No newline at end of file diff --git a/plugins/src/templates/bank-verification/views/home/App.js b/plugins/src/templates/bank-verification/views/home/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/bank-verification/views/home/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + +
+ + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/bank-verification/views/home/Form.js b/plugins/src/templates/bank-verification/views/home/Form.js new file mode 100644 index 0000000000..f201372eee --- /dev/null +++ b/plugins/src/templates/bank-verification/views/home/Form.js @@ -0,0 +1,32 @@ +import React from 'react'; +import { Button, Editable as EditWrapper } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; + +export const PLUGIN_NAME = 'bank'; + +const Form = ({ setActivePageContent, strings: STRINGS, generateId }) => { + return ( +
+
+ {STRINGS.formatString(STRINGS[generateId('home-content')], STRINGS[generateId('title')])} +
+
+
+ +
+
+
+ ) +} + +const mapContextToProps = ({ setActivePageContent, strings, generateId }) => ({ + setActivePageContent, + strings, + generateId +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/bank-verification/views/home/index.js b/plugins/src/templates/bank-verification/views/home/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/bank-verification/views/home/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/bank-verification/views/home/view.json b/plugins/src/templates/bank-verification/views/home/view.json new file mode 100644 index 0000000000..9f9d14cfc1 --- /dev/null +++ b/plugins/src/templates/bank-verification/views/home/view.json @@ -0,0 +1,4 @@ +{ + "target": "REMOTE_COMPONENT__BANK_VERIFICATION_HOME", + "meta": {} +} \ No newline at end of file diff --git a/plugins/src/templates/bank-verification/views/verification/App.js b/plugins/src/templates/bank-verification/views/verification/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/bank-verification/views/verification/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + + + + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/bank-verification/views/verification/Form.js b/plugins/src/templates/bank-verification/views/verification/Form.js new file mode 100644 index 0000000000..1ccc583c71 --- /dev/null +++ b/plugins/src/templates/bank-verification/views/verification/Form.js @@ -0,0 +1,54 @@ +import React from 'react'; +import { Button, IconTitle, Editable as EditWrapper } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; +import { PLUGIN_NAME } from '../home/Form'; + +const Form = ({ + strings: STRINGS, + handleBack, + generateId, + setActivePageContent, +}) => { + + const onGoBack = () => { + setActivePageContent('email'); + handleBack(PLUGIN_NAME); + }; + + return ( +
+ + +
+
+ {STRINGS.formatString(STRINGS[generateId('content')], STRINGS[generateId('title')])} +
+
+
+
+ +
+
+ +
+ ) +} + +const mapContextToProps = ({ strings, handleBack, activeLanguage, icons, generateId, setActivePageContent }) => ({ + strings, + handleBack, + activeLanguage, + icons, + generateId, + setActivePageContent, +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/bank-verification/views/verification/index.js b/plugins/src/templates/bank-verification/views/verification/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/bank-verification/views/verification/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/bank-verification/views/verification/view.json b/plugins/src/templates/bank-verification/views/verification/view.json new file mode 100644 index 0000000000..a7faa5b752 --- /dev/null +++ b/plugins/src/templates/bank-verification/views/verification/view.json @@ -0,0 +1,4 @@ +{ + "target": "REMOTE_COMPONENT__BANK_VERIFICATION", + "meta": {} +} \ No newline at end of file diff --git a/plugins/src/templates/fiat-wallet/assets/icons.json b/plugins/src/templates/fiat-wallet/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/templates/fiat-wallet/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/templates/fiat-wallet/assets/strings.json b/plugins/src/templates/fiat-wallet/assets/strings.json new file mode 100644 index 0000000000..6a691ed1b8 --- /dev/null +++ b/plugins/src/templates/fiat-wallet/assets/strings.json @@ -0,0 +1,5 @@ +{ + "en": { + "hello": "Hello {0}" + } +} \ No newline at end of file diff --git a/plugins/src/templates/fiat-wallet/views/deposit/App.js b/plugins/src/templates/fiat-wallet/views/deposit/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/fiat-wallet/views/deposit/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + +
+ + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/fiat-wallet/views/deposit/Form.js b/plugins/src/templates/fiat-wallet/views/deposit/Form.js new file mode 100644 index 0000000000..ec24b3d832 --- /dev/null +++ b/plugins/src/templates/fiat-wallet/views/deposit/Form.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { Image } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; + + +const Form = ({ icons: ICONS, currency, titleSection }) => { + return ( +
+
+ + {titleSection} +
+
+ ) +} + +const mapContextToProps = ({ icons, currency, titleSection }) => ({ + icons, + currency, + titleSection, +}) + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/fiat-wallet/views/deposit/index.js b/plugins/src/templates/fiat-wallet/views/deposit/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/fiat-wallet/views/deposit/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/fiat-wallet/views/deposit/view.json b/plugins/src/templates/fiat-wallet/views/deposit/view.json new file mode 100644 index 0000000000..063cfb71c0 --- /dev/null +++ b/plugins/src/templates/fiat-wallet/views/deposit/view.json @@ -0,0 +1,7 @@ +{ + "meta": { + "is_wallet": true, + "type": "deposit", + "currency": "USD" + } +} \ No newline at end of file diff --git a/plugins/src/templates/fiat-wallet/views/withdraw/App.js b/plugins/src/templates/fiat-wallet/views/withdraw/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/fiat-wallet/views/withdraw/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + + + + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/fiat-wallet/views/withdraw/Form.js b/plugins/src/templates/fiat-wallet/views/withdraw/Form.js new file mode 100644 index 0000000000..ec24b3d832 --- /dev/null +++ b/plugins/src/templates/fiat-wallet/views/withdraw/Form.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { Image } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; + + +const Form = ({ icons: ICONS, currency, titleSection }) => { + return ( +
+
+ + {titleSection} +
+
+ ) +} + +const mapContextToProps = ({ icons, currency, titleSection }) => ({ + icons, + currency, + titleSection, +}) + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/fiat-wallet/views/withdraw/index.js b/plugins/src/templates/fiat-wallet/views/withdraw/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/fiat-wallet/views/withdraw/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/fiat-wallet/views/withdraw/view.json b/plugins/src/templates/fiat-wallet/views/withdraw/view.json new file mode 100644 index 0000000000..79e02c426a --- /dev/null +++ b/plugins/src/templates/fiat-wallet/views/withdraw/view.json @@ -0,0 +1,7 @@ +{ + "meta": { + "is_wallet": true, + "type": "withdraw", + "currency": "USD" + } +} \ No newline at end of file diff --git a/plugins/src/templates/kyc-verification/assets/icons.json b/plugins/src/templates/kyc-verification/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/templates/kyc-verification/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/templates/kyc-verification/assets/strings.json b/plugins/src/templates/kyc-verification/assets/strings.json new file mode 100644 index 0000000000..a3cd5e0beb --- /dev/null +++ b/plugins/src/templates/kyc-verification/assets/strings.json @@ -0,0 +1,8 @@ +{ + "en": { + "title": "KYC", + "go_to_tab": "Go to {0}", + "content": "{0} content", + "home-content": "{0} home content" + } +} \ No newline at end of file diff --git a/plugins/src/templates/kyc-verification/views/home/App.js b/plugins/src/templates/kyc-verification/views/home/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/kyc-verification/views/home/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + + + + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/kyc-verification/views/home/Form.js b/plugins/src/templates/kyc-verification/views/home/Form.js new file mode 100644 index 0000000000..0b122adf6c --- /dev/null +++ b/plugins/src/templates/kyc-verification/views/home/Form.js @@ -0,0 +1,32 @@ +import React from 'react'; +import { Button, Editable as EditWrapper } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; + +export const PLUGIN_NAME = 'kyc'; + +const Form = ({ setActivePageContent, strings: STRINGS, generateId }) => { + return ( +
+
+ {STRINGS.formatString(STRINGS[generateId('home-content')], STRINGS[generateId('title')])} +
+
+
+ +
+
+
+ ) +} + +const mapContextToProps = ({ setActivePageContent, strings, generateId }) => ({ + setActivePageContent, + strings, + generateId +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/kyc-verification/views/home/index.js b/plugins/src/templates/kyc-verification/views/home/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/kyc-verification/views/home/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/kyc-verification/views/home/view.json b/plugins/src/templates/kyc-verification/views/home/view.json new file mode 100644 index 0000000000..94d8ab5c9f --- /dev/null +++ b/plugins/src/templates/kyc-verification/views/home/view.json @@ -0,0 +1,4 @@ +{ + "target": "REMOTE_COMPONENT__KYC_VERIFICATION_HOME", + "meta": {} +} \ No newline at end of file diff --git a/plugins/src/templates/kyc-verification/views/verification/App.js b/plugins/src/templates/kyc-verification/views/verification/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/kyc-verification/views/verification/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + + + + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/kyc-verification/views/verification/Form.js b/plugins/src/templates/kyc-verification/views/verification/Form.js new file mode 100644 index 0000000000..8d71a2fab5 --- /dev/null +++ b/plugins/src/templates/kyc-verification/views/verification/Form.js @@ -0,0 +1,56 @@ +import React from 'react'; +import { Button, IconTitle, Editable as EditWrapper } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; +import { PLUGIN_NAME } from '../home/Form'; + +const Form = ({ + strings: STRINGS, + handleBack, + icons: ICONS, + generateId, + setActivePageContent, +}) => { + + const onGoBack = () => { + setActivePageContent('email'); + handleBack(PLUGIN_NAME); + }; + + return ( +
+ + +
+
+ {STRINGS.formatString(STRINGS[generateId('content')], STRINGS[generateId('title')])} +
+
+
+
+ +
+
+ +
+ ) +} + +const mapContextToProps = ({ strings, handleBack, activeLanguage, icons, generateId, setActivePageContent }) => ({ + strings, + handleBack, + activeLanguage, + icons, + generateId, + setActivePageContent, +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/kyc-verification/views/verification/index.js b/plugins/src/templates/kyc-verification/views/verification/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/kyc-verification/views/verification/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/kyc-verification/views/verification/view.json b/plugins/src/templates/kyc-verification/views/verification/view.json new file mode 100644 index 0000000000..de8e6ec7eb --- /dev/null +++ b/plugins/src/templates/kyc-verification/views/verification/view.json @@ -0,0 +1,4 @@ +{ + "target": "REMOTE_COMPONENT__KYC_VERIFICATION", + "meta": {} +} \ No newline at end of file diff --git a/plugins/src/templates/new-page/assets/icons.json b/plugins/src/templates/new-page/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/templates/new-page/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/templates/new-page/assets/strings.json b/plugins/src/templates/new-page/assets/strings.json new file mode 100644 index 0000000000..ab9c4de4f6 --- /dev/null +++ b/plugins/src/templates/new-page/assets/strings.json @@ -0,0 +1,6 @@ +{ + "en": { + "title": "New Page", + "content": "{0} content" + } +} \ No newline at end of file diff --git a/plugins/src/templates/new-page/views/view/App.js b/plugins/src/templates/new-page/views/view/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/new-page/views/view/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + +
+ + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/new-page/views/view/Form.js b/plugins/src/templates/new-page/views/view/Form.js new file mode 100644 index 0000000000..ab0498fcb5 --- /dev/null +++ b/plugins/src/templates/new-page/views/view/Form.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { IconTitle } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; + +const Form = ({ + strings: STRINGS, + icons: ICONS, + generateId +}) => { + + return ( +
+ + +
+
+ {STRINGS.formatString(STRINGS[generateId('content')], STRINGS[generateId('title')])} +
+
+ +
+ ) +} + +const mapContextToProps = ({ strings, activeLanguage, icons, generateId }) => ({ + strings, + activeLanguage, + icons, + generateId, +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/new-page/views/view/index.js b/plugins/src/templates/new-page/views/view/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/new-page/views/view/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/new-page/views/view/view.json b/plugins/src/templates/new-page/views/view/view.json new file mode 100644 index 0000000000..9bf040d9de --- /dev/null +++ b/plugins/src/templates/new-page/views/view/view.json @@ -0,0 +1,18 @@ +{ + "meta": { + "is_page": true, + "path": "/custom-route", + "hide_from_appbar": true, + "hide_from_sidebar": false, + "hide_from_menulist": false, + "hide_from_bottom_nav": true, + "string": { + "id": "title", + "is_global": false + }, + "icon": { + "id": "SIDEBAR_HELP", + "is_global": true + } + } +} \ No newline at end of file diff --git a/plugins/src/templates/onramp/assets/icons.json b/plugins/src/templates/onramp/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/templates/onramp/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/templates/onramp/assets/strings.json b/plugins/src/templates/onramp/assets/strings.json new file mode 100644 index 0000000000..cc9964b402 --- /dev/null +++ b/plugins/src/templates/onramp/assets/strings.json @@ -0,0 +1,6 @@ +{ + "en": { + "title": "Onramp plugin", + "content": "{0} content" + } +} \ No newline at end of file diff --git a/plugins/src/templates/onramp/views/view/App.js b/plugins/src/templates/onramp/views/view/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/onramp/views/view/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + +
+ + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/onramp/views/view/Form.js b/plugins/src/templates/onramp/views/view/Form.js new file mode 100644 index 0000000000..63699e74a3 --- /dev/null +++ b/plugins/src/templates/onramp/views/view/Form.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { withKit } from 'components/KitContext'; + +const Form = ({ + strings: STRINGS, + generateId +}) => { + return ( +
+
+ {STRINGS.formatString(STRINGS[generateId('content')], STRINGS[generateId('title')])} +
+
+ ) +} + +const mapContextToProps = ({ strings, activeLanguage, generateId }) => ({ + strings, + activeLanguage, + generateId, +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/onramp/views/view/index.js b/plugins/src/templates/onramp/views/view/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/onramp/views/view/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/onramp/views/view/view.json b/plugins/src/templates/onramp/views/view/view.json new file mode 100644 index 0000000000..c73b426aec --- /dev/null +++ b/plugins/src/templates/onramp/views/view/view.json @@ -0,0 +1,6 @@ +{ + "meta": { + "is_ultimate_fiat": true, + "type": "onramp" + } +} \ No newline at end of file diff --git a/plugins/src/templates/verification-tab/assets/icons.json b/plugins/src/templates/verification-tab/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/templates/verification-tab/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/templates/verification-tab/assets/strings.json b/plugins/src/templates/verification-tab/assets/strings.json new file mode 100644 index 0000000000..9e26c53970 --- /dev/null +++ b/plugins/src/templates/verification-tab/assets/strings.json @@ -0,0 +1,8 @@ +{ + "en": { + "title": "Verification", + "go_to_tab": "Go to {0}", + "content": "{0} content", + "home-content": "{0} home content" + } +} \ No newline at end of file diff --git a/plugins/src/templates/verification-tab/views/home/App.js b/plugins/src/templates/verification-tab/views/home/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/verification-tab/views/home/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + + + + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/verification-tab/views/home/Form.js b/plugins/src/templates/verification-tab/views/home/Form.js new file mode 100644 index 0000000000..c59d7adf85 --- /dev/null +++ b/plugins/src/templates/verification-tab/views/home/Form.js @@ -0,0 +1,32 @@ +import React from 'react'; +import { Button, Editable as EditWrapper } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; + +export const PLUGIN_NAME = 'verification-tab'; + +const Form = ({ setActivePageContent, strings: STRINGS, generateId }) => { + return ( +
+
+ {STRINGS.formatString(STRINGS[generateId('home-content')], STRINGS[generateId('title')])} +
+
+
+ +
+
+
+ ) +} + +const mapContextToProps = ({ setActivePageContent, strings, generateId }) => ({ + setActivePageContent, + strings, + generateId +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/verification-tab/views/home/index.js b/plugins/src/templates/verification-tab/views/home/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/verification-tab/views/home/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/verification-tab/views/home/view.json b/plugins/src/templates/verification-tab/views/home/view.json new file mode 100644 index 0000000000..70ad04103b --- /dev/null +++ b/plugins/src/templates/verification-tab/views/home/view.json @@ -0,0 +1,14 @@ +{ + "meta": { + "is_verification_tab": true, + "type": "home", + "string": { + "id": "title", + "is_global": false + }, + "icon": { + "id": "SIDEBAR_HELP", + "is_global": true + } + } +} \ No newline at end of file diff --git a/plugins/src/templates/verification-tab/views/verification/App.js b/plugins/src/templates/verification-tab/views/verification/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/verification-tab/views/verification/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + + + + + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/verification-tab/views/verification/Form.js b/plugins/src/templates/verification-tab/views/verification/Form.js new file mode 100644 index 0000000000..e4e9a96df4 --- /dev/null +++ b/plugins/src/templates/verification-tab/views/verification/Form.js @@ -0,0 +1,56 @@ +import React from 'react'; +import { Button, IconTitle, Editable as EditWrapper } from 'hollaex-web-lib'; +import { withKit } from 'components/KitContext'; +import { PLUGIN_NAME } from '../home/Form'; + +const Form = ({ + strings: STRINGS, + handleBack, + icons: ICONS, + generateId, + setActivePageContent, +}) => { + + const onGoBack = () => { + setActivePageContent('email'); + handleBack(PLUGIN_NAME); + }; + + return ( +
+ + +
+
+ {STRINGS.formatString(STRINGS[generateId('content')], STRINGS[generateId('title')])} +
+
+
+
+ +
+
+ +
+ ) +} + +const mapContextToProps = ({ strings, handleBack, activeLanguage, icons, generateId, setActivePageContent }) => ({ + strings, + handleBack, + activeLanguage, + icons, + generateId, + setActivePageContent, +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/verification-tab/views/verification/index.js b/plugins/src/templates/verification-tab/views/verification/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/verification-tab/views/verification/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/verification-tab/views/verification/view.json b/plugins/src/templates/verification-tab/views/verification/view.json new file mode 100644 index 0000000000..c99e7e3a89 --- /dev/null +++ b/plugins/src/templates/verification-tab/views/verification/view.json @@ -0,0 +1,6 @@ +{ + "meta": { + "is_verification_tab": true, + "type": "verification" + } +} \ No newline at end of file diff --git a/plugins/src/templates/view/App.js b/plugins/src/templates/view/App.js new file mode 100644 index 0000000000..fbe8743c25 --- /dev/null +++ b/plugins/src/templates/view/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Title from 'components/Title'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + + + + </KitContextProvider> + </Provider> + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/view/index.js b/plugins/src/templates/view/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/view/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/view/view.json b/plugins/src/templates/view/view.json new file mode 100644 index 0000000000..c123454499 --- /dev/null +++ b/plugins/src/templates/view/view.json @@ -0,0 +1,4 @@ +{ + "target": "", + "meta": {} +} \ No newline at end of file diff --git a/plugins/src/utils/index.js b/plugins/src/utils/index.js new file mode 100644 index 0000000000..f5aa416850 --- /dev/null +++ b/plugins/src/utils/index.js @@ -0,0 +1,154 @@ +import React from 'react'; +import math from 'mathjs'; +import numbro from 'numbro'; +import validator from 'validator'; +import isEmail from 'validator/lib/isEmail'; + +export const required = value => (!value ? "Required" : undefined); + +export const editableRequired = (string) => (value) => (!value ? (string || "Required") : undefined); + +export const validateOtp = (message) => ( + value = '' +) => { + let error = undefined; + if (value.length !== 6 || !validator.isNumeric(value)) { + error = message; + } + return error; +}; + +export const DEFAULT_COIN_DATA = { + fullname: '', + symbol: '', + min: 0.001, +}; + +const local_base_currnecy = localStorage.getItem('base_currnecy'); + +export const BASE_CURRENCY = local_base_currnecy + ? local_base_currnecy.toLowerCase() + : 'usdt'; + +export const CURRENCY_PRICE_FORMAT = '{0} {1}'; + +export const formatToCurrency = (amount = 0, min = 0, fullFormat = false) => { + let formatObj = getFormat(min, fullFormat); + return numbro(roundNumber(amount, formatObj.digit)).format(formatObj.format); +}; + +export const roundNumber = (number = 0, decimals = 4) => { + if (number === 0) { + return 0; + } else if (decimals > 0) { + const multipliedNumber = math.multiply( + math.fraction(number), + math.pow(10, decimals) + ); + const dividedNumber = math.divide( + math.floor(multipliedNumber), + math.pow(10, decimals) + ); + return math.number(dividedNumber); + } else { + return math.floor(number); + } +}; + +export const getFormat = (min = 0, fullFormat) => { + let value = math.format(min, { notation: 'fixed' }); + if (fullFormat) { + return { digit: 8, format: '0,0.[00000000]' }; + } else if (min % 1) { + let point = value.toString().split('.')[1] + ? value.toString().split('.')[1] + : ''; + let res = point + .split('') + .map((val) => 0) + .join(''); + return { digit: point.length, format: `0,0.[${res}]` }; + } else { + return { digit: 4, format: `0,0.[0000]` }; + } +}; + +export const getDecimals = (value = 0) => { + let result = math.format(math.number(value), { notation: 'fixed' }); + return value % 1 + ? result.toString().split('.')[1] + ? result.toString().split('.')[1].length + : 0 + : 0; +}; + +export const normalizeBTC = (value = 0) => (value ? roundNumber(value, 8) : ''); + +export const maxValue = (maxValue, message) => (value = 0) => + value > maxValue + ? message + : undefined; + +export const minValue = (minValue, message) => (value = 0) => + value < minValue + ? message + : undefined; + +export const checkBalance = (available, message, fee = 0) => (value = 0) => { + const operation = + fee > 0 + ? math.number( + math.add( + math.fraction(value), + math.multiply(math.fraction(value), math.fraction(fee)) + ) + ) + : value; + + if (operation > available) { + return message; + } + return undefined; +}; + +export const checkBalance_new = (available, message, fee = 0) => (value = 0) => { + const operation = + fee > 0 + ? math.number( + math.add( + math.fraction(value), + math.fraction(fee) + ) + ) + : value; + + if (operation > available) { + return message; + } + return undefined; +}; + +export const toFixed = (exponential) => { + if (Math.abs(exponential) < 1.0) { + let e = parseInt(exponential.toString().split('e-')[1], 10); + if (e) { + exponential *= Math.pow(10, e - 1); + exponential = + '0.' + new Array(e).join('0') + exponential.toString().substring(2); + } + } else { + let e = parseInt(exponential.toString().split('+')[1], 10); + if (e > 20) { + e -= 20; + exponential /= Math.pow(10, e); + exponential += new Array(e + 1).join('0'); + } + } + return exponential; +}; + +export const email = (value = '') => + value && !isEmail(value) ? 'Invalid email address' : undefined; + +export const maxLength = (length, message) => (value = "") => + value.length > length ? message : undefined; \ No newline at end of file diff --git a/plugins/src/utils/link.js b/plugins/src/utils/link.js new file mode 100644 index 0000000000..0f8ee72a88 --- /dev/null +++ b/plugins/src/utils/link.js @@ -0,0 +1,11 @@ +export const open = (url, target = '_blank') => { + const a = document.createElement('a'); + a.style = 'display: none'; + a.href = url; + a.target = target; + a.rel = 'noopener noreferrer'; + + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); +}; \ No newline at end of file diff --git a/plugins/test.js b/plugins/test.js new file mode 100644 index 0000000000..3cffb46496 --- /dev/null +++ b/plugins/test.js @@ -0,0 +1,917 @@ +'use strict'; + +const crypto = require('crypto'); + +const { + meta, + publicMeta, + toolsLib, + lodash, + moment, + app, + loggerPlugin, + expressValidator, + bluebird, + rp +} = this; + +const { + webhook_secret: { value: WEBHOOK_SECRET }, + client_secret: { value: CLIENT_SECRET }, + manual_review: { value: MANUAL_REVIEW } +} = meta; +const { + client_id: { value: CLIENT_ID }, + flow_id: { value: FLOW_ID } +} = publicMeta; + +const VERIFY_STATUS = { + EMPTY: 0, + PENDING: 1, + REJECTED: 2, + COMPLETED: 3 +}; + +const + MATI_API_URL_AUTH = 'https://api.getmati.com/oauth', + getDomainUrl = (path) => `${toolsLib.getDomain()}${path}`; + +const init = async () => { + if (!WEBHOOK_SECRET) { + throw new Error('webhook_secret required value'); + } + if (!CLIENT_SECRET) { + throw new Error('client_secret required value'); + } + if (!CLIENT_ID) { + throw new Error('client_id required value'); + } + if (!FLOW_ID) { + throw new Error('flow_id required value'); + } + if (typeof MANUAL_REVIEW !== 'boolean') { + throw new Error('manual_review invalid value'); + } +}; + +const convertGender = (gender) => { + return gender ? 'Female' : 'Male'; +}; + +const parseObjToList = (data) => { + const result = { + html: '', + text: '' + }; + + for (let field in data) { + if (!lodash.isNil(data[field])) { + if (field === 'gender') { + data[field] = convertGender(data[field]); + } + + if (toolsLib.isDatetime(data[field])) { + data[field] = moment(data[field]).format('YYYY/MM/DD'); + } + result.html += `<li>${lodash.startCase(field)}: ${data[field]}</li>`; + result.text += `${lodash.startCase(field)}: ${data[field]}\n`; + } + } + + return result; +}; + +const documentApprovedEmail = (email, data = {}) => { + const idData = parseObjToList(data); + + const html = ` + <div> + <p> + Dear ${email}, + </p> + <p> + Your uploaded KYC documents have been approved.<br> + You now have access to all exchange features that require identity verification. + </p> + <ul> + ${idData.html} + </ul> + <p> + To view your approved documents, visit your <a href=${getDomainUrl('/verification')}>Verification page</a>. + </p> + <p> + Regards,<br> + ${toolsLib.getKitConfig().api_name} team + </p> + </div> + `; + + const text = ` + Dear ${email}, + + Your uploaded KYC documents have been approved. + You now have access to all exchange features that require identity verification. + + ${idData.text} + + To view your approved documents, visit your Verification page. + + Regards, + ${toolsLib.getKitConfig().api_name} team + `; + + const subject = 'KYC Documents Approved'; + + return toolsLib.sendCustomEmail( + email, + subject, + toolsLib.emailHtmlBoilerplate(html), + { + bcc: 'default', + text + } + ); +}; + +const documentPendingEmail = (email) => { + const html = ` + <div> + <p> + Dear ${email}, + </p> + <p> + Your uploaded documents are currently being processed.<br> + We will notify you when your documents are approved or denied. + </p> + <p> + To view the status of your pending documents, visit your <a href=${getDomainUrl('/verification')}>Verification page</a>. + </p> + <p> + Regards,<br> + ${toolsLib.getKitConfig().api_name} team + </p> + </div> + `; + + const text = ` + Dear ${email}, + + Your uploaded documents are currently being processed. + We will notify you when your documents are approved or denied. + + To view the status of your pending documents, visit your Verification page. + + Regards, + ${toolsLib.getKitConfig().api_name} team + `; + + const subject = 'KYC Documents Pending'; + + return toolsLib.sendCustomEmail( + email, + subject, + toolsLib.emailHtmlBoilerplate(html), + { + bcc: 'default', + text + } + ); +}; + +const documentRejectedEmail = (email, reasons = {}) => { + const parsedReasons = parseObjToList(reasons); + + const html = ` + <div> + <p> + Dear ${email}, + </p> + <p> + Unfortunately, your uploaded KYC documents have been rejected.<br> + The reasons for your documents being rejected are listed below.<br> + </p> + <div> + ${parsedReasons.html} + </div> + <p> + If you feel these reasons are invalid, please feel free to reply to this email.<br> + Otherwise, please reupload valid documents in order to verify your identity. + </p> + <p> + Regards,<br> + ${toolsLib.getKitConfig().api_name} team + </p> + </div> + `; + + const text = ` + Dear ${email}, + + Unfortunately, your uploaded KYC documents have been rejected. + The reasons for your documents being rejected are listed below. + + ${parsedReasons.text} + + If you feel these reasons are invalid, please feel free to reply to this email. + Otherwise, please reupload valid documents in order to verify your identity. + + Regards, + ${toolsLib.getKitConfig().api_name} team + `; + + const subject = 'KYC Documents Rejected'; + + return toolsLib.sendCustomEmail( + email, + subject, + toolsLib.emailHtmlBoilerplate(html), + { + bcc: 'default', + text + } + ); +}; + +const documentExpiredEmail = (email) => { + const html = ` + <div> + <p> + Dear ${email}, + </p> + <p> + Your iDenfy document upload token as expired.<br> + You will no longer be able to use your token to upload your documents.<br> + To generate a new token, please go through the KYC process in your <a href=${getDomainUrl('/verification')}>Verification page</a>. + </p> + <p> + Regards,<br> + ${toolsLib.getKitConfig().api_name} team + </p> + </div> + `; + + const text = ` + Dear ${email}, + + Your iDenfy document upload token as expired. + You will no longer be able to use your token to upload your documents. + To generate a new token, please go through the KYC process in your Verification page. + + Regards, + ${toolsLib.getKitConfig().api_name} team + `; + + const subject = 'iDenfy Token Expired'; + + return toolsLib.sendCustomEmail( + email, + subject, + toolsLib.emailHtmlBoilerplate(html), + { + bcc: 'default', + text + } + ); +}; + +const adminAlertEmail = async (data = {}) => { + const { email, id } = data; + + const html = ` + <div> + <p> + Documents provided by user ${email} with ID ${id} have been automatically approved and are awaiting manual approval.<br> + <br> + You can manually approve or reject these documents through the <a href=${getDomainUrl(`/admin/user?id=${id}`)}>Operator Controls Panel</a>.<br> + Once reviewed, the user will be notified of the updated status. + </p> + </div> + `; + + const text = ` + Documents provided by user ${email} with ID ${id} have been automatically approved and are awaiting manual approval. + + You can manually approve or reject these documents through the Operator Controls Panel. + Once reviewed, the user will be notified of the updated status. + `; + + const subject = 'KYC Documents Awaiting Manual Review'; + + const kycOperators = await toolsLib.database.findAll('user', { + where: { + is_kyc: true + }, + attributes: ['email'], + raw: true + }); + + let cc = null; + + if (kycOperators.length > 0) { + cc = kycOperators.map((operator) => operator.email).join(','); + } + + return toolsLib.sendCustomEmail( + toolsLib.getKitSecrets().emails.audit, + subject, + toolsLib.emailHtmlBoilerplate(html), + { + cc, + text + } + ); +}; + +const getGender = (docSex) => { + if (docSex && docSex !== 'UNDEFINED') { + return docSex === 'F'; + } + + return false; +}; + +const generateUpdatedUserData = (status, data, eventName) => { + let result = {}; + if (eventName === 'verification_started' || eventName === 'verification_inputs_completed') { + result = { + id_data: { + status + } + }; + return result; + + } else if (eventName === 'verification_expired') { + result = { + id_data: { + status + } + }; + result.id_data.mati.resource = data.resource; + + return result; + + } else if (eventName === 'verification_updated' || eventName === 'verification_completed') { + bluebird.all([ + data.resource, + generateMatiToken() + ]) + .then(([resourceURL, responseToken]) => { + if (!responseToken) + throw new Error('Not authentication Mati'); + + loggerPlugin.verbose( + data.metadata.user_id, + 'GET /plugins/mati/admin/files request getMatiFiles', + responseToken + ); + responseToken = JSON.parse(responseToken); + return getMatiFiles(resourceURL, responseToken.access_token); + }) + .then((documentUser) => { + + result = { + full_name: '', + nationality: '', + gender: false, + dob: null, + id_data: { + status + } + }; + + loggerPlugin.verbose( + data.metadata.user_id, + 'GET /plugins/mati/admin/files response data', + documentUser + ); + + const { + fullName: { value: fullName }, + documentNumber: { value: documentNumber }, + dateOfBirt: { value: dateOfBirt }, + expirationDate: { value: expirationDate }, + documentType: { value: documentType }, + firstName: { value: firstName }, + issueCountry: { value: issueCountry }, + nationality: { value: nationality }, + personalNumber: { value: personalNumber }, + sex: { value: sex }, + surname: { value: surname } + } = documentUser.documents.fields; + + result.id_data.mati = documentUser.documents.data; + result.id_data.mati.resource = documentUser.resource; + if (documentType) { + result.id_data.type = documentType.toLowerCase(); + } + + if (documentNumber) { + result.id_data.number = documentNumber; + } + + if (expirationDate) { + result.id_data.expiration_date = moment(expirationDate).toISOString(); + } + + if (nationality) { + result.nationality = nationality; + } + + if (dateOfBirt) { + result.dob = moment(dateOfBirt).toISOString(); + } + + if (fullName) { + result.full_name = fullName; + } + if (sex) { + result.gender = getGender(sex); + } + return result; + }); + } +}; + +const generateMatiToken = async () => { + const method = 'POST'; + const headers = { + 'content-type': 'application/x-www-form-urlencoded', + 'Authorization': 'Basic ' + new Buffer.from(CLIENT_ID + ':' + CLIENT_SECRET, 'utf8').toString('base64') + }; + const form = { + 'grant_type': 'client_credentials' + }; + const option = { + headers, + method, + uri: MATI_API_URL_AUTH, + form + }; + + return rp(option); +}; + +const getMatiFiles = async (resource, accessToken) => { + + const method = 'GET'; + const headers = { + 'content-type': 'application/x-www-form-urlencoded', + 'Authorization': 'Bearer ' + accessToken + }; + const url = resource; + loggerPlugin.verbose( + resource, + accessToken, + 'getMatiFiles request' + ); + return rp({ + headers, + method, + url + }); +}; + +const verifyRequest = async (signature, secret, payloadBody) => { + let hash = crypto.createHmac('sha256', secret); + hash = hash.update(payloadBody).digest('hex'); + const isValid = crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(signature)); + + loggerPlugin.error( + 'MATI PLUGIN cannot verify notification request' + ); + + if (!isValid) { + throw new Error('Invalid request'); + } +}; + +init() + .then(() => { + app.post('/plugins/mati/notification', (req, res) => { + loggerPlugin.verbose( + req.uuid, + 'POST /plugins/mati/notification body URL webhook', + req.body + ); + + const signature = req.headers['x-signature']; + const data = req.body; + + verifyRequest(signature, WEBHOOK_SECRET, JSON.stringify(data)) + .then(() => { + if (!data.metadata || !data.metadata.user_id) { + throw new Error('Meta data user_id not given'); + } + + return toolsLib.database.findOne('user', { + where: { + id: data.metadata.user_id + }, + attributes: [ + 'id', + 'email', + 'network_id', + 'full_name', + 'gender', + 'nationality', + 'dob', + 'id_data' + ] + }); + }) + .then(async (user) => { + if (!user) { + throw new Error('User not found'); + } + + const { eventName } = data; + + let updateStatus; + + if (eventName === 'verification_started') { + updateStatus = VERIFY_STATUS.PENDING; + } else if (eventName === 'verification_inputs_completed') { + updateStatus = VERIFY_STATUS.PENDING; + } else if (eventName === 'verification_updated') { + if (MANUAL_REVIEW) { + updateStatus = VERIFY_STATUS.PENDING; + } else { + updateStatus = VERIFY_STATUS.COMPLETED; + } + } else if (eventName === 'verification_completed') { + if (MANUAL_REVIEW) { + updateStatus = VERIFY_STATUS.PENDING; + } else { + updateStatus = VERIFY_STATUS.COMPLETED; + } + } else if (eventName === 'verification_expired') { + updateStatus = VERIFY_STATUS.REJECTED; + } + + const previousStatus = user.id_data.status; + + loggerPlugin.verbose( + req.uuid, + 'POST /plugins/mati/notification status', + 'mati eventName', + eventName, + 'previous id data status:', + previousStatus, + 'updating status:', + updateStatus + ); + + if (updateStatus) { + const updateData = await generateUpdatedUserData(updateStatus, data, eventName); + + loggerPlugin.verbose( + req.uuid, + 'POST /plugins/mati/notification updated user', + updateData + ); + const updatedUser = await user.update(updateData); + + loggerPlugin.verbose( + req.uuid, + 'POST /plugins/mati/notification updated user', + updatedUser + ); + + try { + if (updateStatus === VERIFY_STATUS.PENDING && MANUAL_REVIEW) { + adminAlertEmail({ + email: user.email, + id: user.id + }); + } + } catch (err) { + loggerPlugin.error( + req.uuid, + 'POST /plugins/mati/notification error while sending manual review required email', + err.message + ); + } + + try { + if (updateStatus === VERIFY_STATUS.REJECTED) { + documentRejectedEmail( + user.email + ); + } else if (updateStatus === VERIFY_STATUS.PENDING) { + documentPendingEmail(user.email); + } else if (updateStatus === VERIFY_STATUS.COMPLETED) { + documentApprovedEmail(user.email, { + full_name: updatedUser.full_name ? updatedUser.full_name : null, + dob: updatedUser.dob, + gender: lodash.isBoolean(updatedUser.gender) ? updatedUser.gender : null, + nationality: updatedUser.nationality ? updatedUser.nationality : null, + ...lodash.omit(updatedUser.id_data, ['status', 'mati']) + }); + } + } catch (err) { + loggerPlugin.error( + req.uuid, + 'POST /plugins/mati/notification error while sending email', + err.message + ); + } + } + return res.json({ message: 'Success' }); + + }) + .catch((err) => { + loggerPlugin.error( + req.uuid, + 'POST /plugins/mati/notification err', + err + ); + return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); + }); + }); + + app.get('/plugins/mati/admin/files', [ + toolsLib.security.verifyBearerTokenExpressMiddleware(['admin', 'supervisor', 'support', 'kyc']), + expressValidator.checkSchema({ + user_id: { + in: ['query'], + errorMessage: 'must be an integer', + isInt: true, + optional: false + } + }) + ], (req, res) => { + const errors = expressValidator.validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + + loggerPlugin.verbose( + req.uuid, + 'GET /plugins/mati/admin/files auth', + req.auth.sub + ); + + const { user_id } = req.query; + toolsLib.user.getUserByKitId(user_id) + .then((user) => { + if (!user) { + throw new Error('User not found'); + } + + if ( + !user.id_data + || !user.id_data.mati + || !user.id_data.mati.resource + || user.id_data.status === VERIFY_STATUS.EMPTY + ) { + throw new Error('User does not have uploaded documents'); + } + loggerPlugin.verbose( + user_id, + 'GET /plugins/mati/admin/files request generateMatiToken' + ); + + return bluebird.all([ + user, + generateMatiToken() + ]); + }) + .then(([user, responseToken]) => { + if (!responseToken) + throw new Error('Not authentication Mati'); + loggerPlugin.verbose( + user_id, + 'GET /plugins/mati/admin/files request getMatiFiles', + responseToken + ); + responseToken = JSON.parse(responseToken); + return getMatiFiles(user.id_data.mati.resource, responseToken.access_token); + }) + .then((data) => { + + loggerPlugin.verbose( + user_id, + 'GET /plugins/mati/admin/files response data', + data + ); + + return res.json(data); + }) + .catch((err) => { + loggerPlugin.error( + req.uuid, + 'GET /plugins/mati/admin/files err', + err.message + ); + return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); + }); + }); + + app.get('/plugins/mati/admin/file1/:location', [], (req, res) => { + const { location } = req.params; + + generateMatiToken() + .then((responseToken) => { + if (!responseToken) + throw new Error('Not authentication Mati'); + responseToken = JSON.parse(responseToken); + return + const method = 'GET'; + const headers = { + 'Authorization': 'Bearer ' + responseToken + }; + const url = 'https://media.getmati.com/file?location='+location; + + return rp({ + headers, + method, + url + }) + }) + .then((data) => { + console.log(data, "data"); + res.contentType('image/jpeg'); + return res.send(data); + }) + .catch((err) => { + loggerPlugin.error( + req.uuid, + 'GET /plugins/mati/admin/file err', + err.message + ); + return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); + }); + }); + + app.post('/plugins/mati/verify', [ + toolsLib.security.verifyBearerTokenExpressMiddleware(['admin', 'kyc', 'support', 'supervisor']), + expressValidator.checkSchema({ + user_id: { + in: ['body'], + errorMessage: 'must be an integer', + isInt: true, + optional: false + } + }) + ], (req, res) => { + const errors = expressValidator.validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + + loggerPlugin.verbose( + req.uuid, + 'POST /plugins/mati/verify auth', + req.auth.sub + ); + + const { user_id } = req.body; + + toolsLib.user.getUserByKitId(user_id, false, false) + .then((user) => { + if (!user) { + throw new Error('User not found'); + } + + if ( + !user.id_data + || lodash.isNil(user.id_data.status) + || user.id_data.status === VERIFY_STATUS.EMPTY + ) { + throw new Error('User documents are not pending'); + } + + if (user.id_data.status === VERIFY_STATUS.COMPLETED) { + throw new Error('User documents are already verified'); + } + + return user.update({ + id_data: { + ...user.id_data, + status: VERIFY_STATUS.COMPLETED + } + }); + }) + .then((data) => { + try { + documentApprovedEmail(data.email, { + full_name: data.full_name ? data.full_name : null, + dob: data.dob, + gender: lodash.isBoolean(data.gender) ? data.gender : null, + nationality: data.nationality ? data.nationality : null, + ...lodash.omit(data.id_data, ['status', 'idenfy']) + }); + } catch (err) { + loggerPlugin.error( + req.uuid, + 'POST /plugins/mati/verify err while sending email', + err.message + ); + } + return res.json(lodash.pick(data, [ + 'id', + 'email', + 'full_name', + 'dob', + 'nationality', + 'gender', + 'id_data' + ])); + }) + .catch((err) => { + loggerPlugin.error( + req.uuid, + 'POST /plugins/mati/verify err', + err.message + ); + return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); + }); + }); + + app.post('/plugins/mati/revoke', [ + toolsLib.security.verifyBearerTokenExpressMiddleware(['admin', 'kyc', 'support', 'supervisor']), + expressValidator.checkSchema({ + user_id: { + in: ['body'], + errorMessage: 'must be an integer', + isInt: true, + optional: false + } + }) + ], (req, res) => { + const errors = expressValidator.validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + + loggerPlugin.verbose( + req.uuid, + 'POST /plugins/mati/revoke auth', + req.auth.sub + ); + + const user_id = req.body.user_id; + + toolsLib.user.getUserByKitId(user_id, false, false) + .then((user) => { + if (!user) { + throw new Error('User not found'); + } + + if ( + !user.id_data + || lodash.isNil(user.id_data.status) + || user.id_data.status === VERIFY_STATUS.EMPTY + ) { + throw new Error('User documents are not pending'); + } + + if (user.id_data.status === VERIFY_STATUS.REJECTED) { + throw new Error('User documents are already rejected'); + } + + return user.update({ + id_data: { + ...user.id_data, + status: VERIFY_STATUS.REJECTED + } + }); + }) + .then((data) => { + try { + documentRejectedEmail( + data.email + ); + } catch (err) { + loggerPlugin.error( + req.uuid, + 'POST /plugins/mati/revoke err while sending email', + err.message + ); + } + return res.json(lodash.pick(data, [ + 'id', + 'email', + 'full_name', + 'dob', + 'nationality', + 'gender', + 'id_data' + ])); + }) + .catch((err) => { + loggerPlugin.error( + req.uuid, + 'GET /plugins/mati/revoke err', + err.message + ); + return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); + }); + }); + }) + .catch((err) => { + loggerPlugin.error( + 'DOMINICAN EXCHANGE KYC PLUGIN err', + err.message + ); + }); \ No newline at end of file diff --git a/plugins/webpack-dev.config.js b/plugins/webpack-dev.config.js new file mode 100644 index 0000000000..a48cc1e6e6 --- /dev/null +++ b/plugins/webpack-dev.config.js @@ -0,0 +1,96 @@ +/** + * generates: + * - dist/main.js + * - dist/manifest.json + * - dist/webpack-bundle-analyzer-report.html + */ +const webpack = require("webpack"); +const fs = require('fs'); +const WebpackAssetsManifest = require("webpack-assets-manifest"); +const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); +const glob = require("glob") +const path = require("path") +const remoteComponentConfig = require("./remote-component.config").resolve; + +const generateEntires = (plugin) => { + const pattern = `src/plugins/${plugin ? plugin : '**'}/views/**/index.js`; + const entry = glob.sync(pattern, { noglobstar: true }) + .reduce((x, y) => { + const dir = y.split(path.sep); + + if (fs.existsSync(y)) { + return Object.assign(x, { + [`${dir[2]}__${dir[4]}`]: `./${y}`, + }) + } else { + return Object.assign(x, {}) + } + + }, {}); + + if ( Object.keys(entry).length === 0) { + process.exit(); + } else { + return entry; + } +} + +const externals = Object.keys(remoteComponentConfig).reduce( + (obj, key) => ({ ...obj, [key]: key }), + {} +); + +module.exports = (env) => { + const { plugin } = env; + const entry = generateEntires(plugin); + return { + plugins: [ + new webpack.EnvironmentPlugin({ + "process.env.NODE_ENV": process.env.NODE_ENV + }), + new BundleAnalyzerPlugin({ + analyzerMode: "static", + openAnalyzer: false, + reportFilename: "webpack-bundle-analyzer-report.html" + }), + new WebpackAssetsManifest() + ], + entry, + watch: true, + watchOptions: { + aggregateTimeout: 200, + poll: 1000, + ignored: /node_modules/, + }, + output: { + libraryTarget: "commonjs" + }, + externals: { + ...externals, + "remote-component.config.js": "remote-component.config.js" + }, + module: { + rules: [ + { + test: /\.m?js$/, + exclude: /(node_modules|bower_components)/, + use: { + loader: "babel-loader" + } + }, + { + test: /\.(sass|less|css)$/, + use: ["style-loader", "css-loader"] + }, + ] + }, + resolve: { + alias: { + "components": path.resolve("./src/components"), + "utils": path.resolve("./src/utils"), + "store": path.resolve("./src/store"), + "constants": path.resolve("./src/constants") + } + } + } +}; diff --git a/plugins/webpack-main.config.js b/plugins/webpack-main.config.js new file mode 100644 index 0000000000..2465115523 --- /dev/null +++ b/plugins/webpack-main.config.js @@ -0,0 +1,90 @@ +/** + * generates: + * - dist/main.js + * - dist/manifest.json + * - dist/webpack-bundle-analyzer-report.html + */ +const webpack = require("webpack"); +const fs = require('fs'); +const WebpackAssetsManifest = require("webpack-assets-manifest"); +const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); +const glob = require("glob") +const path = require("path") +const remoteComponentConfig = require("./remote-component.config").resolve; + +const generateEntires = (plugin) => { + const pattern = `src/plugins/${plugin ? plugin : '**'}/views/**/index.js`; + const entry = glob.sync(pattern, { noglobstar: true }) + .reduce((x, y) => { + const dir = y.split(path.sep); + + if (fs.existsSync(y)) { + return Object.assign(x, { + [`${dir[2]}__${dir[4]}`]: `./${y}`, + }) + } else { + return Object.assign(x, {}) + } + + }, {}); + + if ( Object.keys(entry).length === 0) { + process.exit(); + } else { + return entry; + } +} + +const externals = Object.keys(remoteComponentConfig).reduce( + (obj, key) => ({ ...obj, [key]: key }), + {} +); + +module.exports = (env) => { + const { plugin } = env; + const entry = generateEntires(plugin); + return { + plugins: [ + new webpack.EnvironmentPlugin({ + "process.env.NODE_ENV": process.env.NODE_ENV + }), + new BundleAnalyzerPlugin({ + analyzerMode: "static", + openAnalyzer: false, + reportFilename: "webpack-bundle-analyzer-report.html" + }), + new WebpackAssetsManifest() + ], + entry, + output: { + libraryTarget: "commonjs" + }, + externals: { + ...externals, + "remote-component.config.js": "remote-component.config.js" + }, + module: { + rules: [ + { + test: /\.m?js$/, + exclude: /(node_modules|bower_components)/, + use: { + loader: "babel-loader" + } + }, + { + test: /\.(sass|less|css)$/, + use: ["style-loader", "css-loader"] + }, + ] + }, + resolve: { + alias: { + "components": path.resolve("./src/components"), + "utils": path.resolve("./src/utils"), + "store": path.resolve("./src/store"), + "constants": path.resolve("./src/constants") + } + } + } +}; diff --git a/plugins/webpack.config.js b/plugins/webpack.config.js new file mode 100644 index 0000000000..aea8894bd1 --- /dev/null +++ b/plugins/webpack.config.js @@ -0,0 +1,3 @@ +const webpackMainConfig = require("./webpack-main.config"); + +module.exports = [webpackMainConfig]; From b23352c60c8141d19dfcf3b9f4cf9c6cc71d29be Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 31 Aug 2022 12:09:10 +0430 Subject: [PATCH 004/132] plugin updates for server script development --- .gitignore | 1 + package-lock.json | 233 -------------- package.json | 10 - plugins/package-lock.json | 296 ++++++++++-------- plugins/package.json | 42 +-- plugins/scripts/generateConfig.js | 76 +++++ plugins/scripts/generateJson.js | 42 +-- .../generatePlugin.js} | 4 +- plugins/scripts/utils.js | 35 ++- plugins/server.js | 1 + 10 files changed, 313 insertions(+), 427 deletions(-) delete mode 100644 package-lock.json delete mode 100644 package.json create mode 100644 plugins/scripts/generateConfig.js rename plugins/{generateJson.js => scripts/generatePlugin.js} (93%) diff --git a/.gitignore b/.gitignore index 59b0aa0d80..fd1540df11 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ web/src/**/*.css plugins/dist plugins/json +plugins/config #HollaEx CLI related templates/local/nginx/conf.d/* diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f5e113eb55..0000000000 --- a/package-lock.json +++ /dev/null @@ -1,233 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concurrently": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.3.0.tgz", - "integrity": "sha512-IiDwm+8DOcFEInca494A8V402tNTQlJaYq78RF2rijOrKEk/AOHTxhN4U1cp7GYKYX5Q6Ymh1dLTBlzIMN0ikA==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "date-fns": "^2.16.1", - "lodash": "^4.17.21", - "rxjs": "^7.0.0", - "shell-quote": "^1.7.3", - "spawn-command": "^0.0.2-1", - "supports-color": "^8.1.0", - "tree-kill": "^1.2.2", - "yargs": "^17.3.1" - } - }, - "date-fns": { - "version": "2.29.2", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.2.tgz", - "integrity": "sha512-0VNbwmWJDS/G3ySwFSJA3ayhbURMTJLtwM2DTxf9CWondCnh6DTNlO9JgRSq6ibf4eD0lfMJNBxUdEAHHix+bA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "rxjs": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", - "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, - "shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", - "dev": true - }, - "spawn-command": { - "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index b5965175dc..0000000000 --- a/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "scripts": { - "plugin:kit": "npm run --prefix ./web dev:plugin --plugin=$npm_config_plugin", - "plugin:plugin": "npm run --prefix ./plugins start --plugin=$npm_config_plugin", - "plugin:dev": "concurrently \"npm run plugin:kit --plugin=$npm_config_plugin\" \"npm run plugin:plugin --plugin=$npm_config_plugin\"" - }, - "devDependencies": { - "concurrently": "7.3.0" - } -} diff --git a/plugins/package-lock.json b/plugins/package-lock.json index 93b770a3cd..7966f37aab 100644 --- a/plugins/package-lock.json +++ b/plugins/package-lock.json @@ -2820,30 +2820,174 @@ } }, "concurrently": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.3.0.tgz", - "integrity": "sha512-8MhqOB6PWlBfA2vJ8a0bSFKATOdWlHiQlk11IfmQBPaHVP8oP2gsh2MObE6UR3hqDHqvaIvLTyceNW6obVuFHQ==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.3.0.tgz", + "integrity": "sha512-IiDwm+8DOcFEInca494A8V402tNTQlJaYq78RF2rijOrKEk/AOHTxhN4U1cp7GYKYX5Q6Ymh1dLTBlzIMN0ikA==", "dev": true, "requires": { - "chalk": "^2.4.2", - "date-fns": "^2.0.1", - "lodash": "^4.17.15", - "read-pkg": "^4.0.1", - "rxjs": "^6.5.2", + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", "spawn-command": "^0.0.2-1", - "supports-color": "^6.1.0", + "supports-color": "^8.1.0", "tree-kill": "^1.2.2", - "yargs": "^13.3.0" + "yargs": "^17.3.1" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true } } }, @@ -3545,15 +3689,6 @@ "prr": "~1.0.1" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, "es-abstract": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", @@ -4763,12 +4898,6 @@ "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", "dev": true }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, "html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -5067,12 +5196,6 @@ } } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -5942,18 +6065,6 @@ "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6248,16 +6359,6 @@ "safe-buffer": "^5.1.1" } }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -7327,25 +7428,6 @@ "prop-types": "^15.7.2" } }, - "read-pkg": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", - "integrity": "sha512-+UBirHHDm5J+3WDmLBZYSklRYg82nMlz+enn+GMZ22nSR2f4bzxmhso6rzQW/3mT2PVzpzDTiYIZahk8UmZ44w==", - "dev": true, - "requires": { - "normalize-package-data": "^2.3.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - } - } - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -7673,12 +7755,12 @@ } }, "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "^2.1.0" } }, "safe-buffer": { @@ -7867,6 +7949,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -8061,38 +8149,6 @@ "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", "dev": true }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", - "dev": true - }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -8608,9 +8664,9 @@ "dev": true }, "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "tty-browserify": { @@ -8919,16 +8975,6 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "validator": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", diff --git a/plugins/package.json b/plugins/package.json index 5ac163bc9c..df71b8305a 100644 --- a/plugins/package.json +++ b/plugins/package.json @@ -12,27 +12,29 @@ "license": "ISC", "scripts": { "kill-8080": "lsof -ti:8080 | xargs kill || exit 0", - "build:web": "npm run clean && cross-env NODE_ENV=production webpack --env.plugin=$npm_config_plugin --mode production", - "build:dev": "npm run clean && cross-env NODE_ENV=development webpack --env.plugin=$npm_config_plugin --mode development", - "start": "cross-env NODE_ENV=development concurrently \"npm run dev --plugin=$npm_config_plugin\" \"npm run json:watch --plugin=$npm_config_plugin\" \"npm run server\"", - "dev": "npm run clean && cross-env NODE_ENV=development webpack --env.plugin=$npm_config_plugin --mode development --config webpack-dev.config.js", - "clean": "rimraf dist", + "generate:bundle:production": "npm run remove:dist && cross-env NODE_ENV=production webpack --env.plugin=$npm_config_plugin --mode production", + "generate:bundle:development": "npm run remove:dist && cross-env NODE_ENV=development webpack --env.plugin=$npm_config_plugin --mode development", + "plugin:development": "cross-env NODE_ENV=development concurrently \"npm run webpack:development --plugin=$npm_config_plugin\" \"npm run watch:webview:json --plugin=$npm_config_plugin\" \"npm run watch:config:json --plugin=$npm_config_plugin\" \"npm run server\"", + "webpack:development": "npm run remove:dist && cross-env NODE_ENV=development webpack --env.plugin=$npm_config_plugin --mode development --config webpack-dev.config.js", + "remove:dist": "rimraf dist", "remove:json": "rimraf json", - "lint": "eslint .", - "json": "PLUGIN=$npm_config_plugin node scripts/generateJson.js", - "generate-json": "npm run remove:json && PLUGIN=$npm_config_plugin node scripts/generateJson.js", - "add-plugin": "PLUGIN=$npm_config_plugin TYPE=$npm_config_type node scripts/addPlugin.js", - "add-view": "PLUGIN=$npm_config_plugin WEB_VIEW=$npm_config_webview node scripts/addView.js", - "publish": "npm run build:web --plugin=$npm_config_plugin && npm run generate-json --plugin=$npm_config_plugin", - "publish:dev": "npm run build:dev --plugin=$npm_config_plugin && npm run generate-json --plugin=$npm_config_plugin", - "json:watch": "npm run remove:json && onchange -i 'src/plugins/**/assets/*.json' 'src/plugins/**/views/**/*.json' -- npm run json --plugin=$npm_config_plugin", + "remove:config": "rimraf config", + "generate:webview:json": "PLUGIN=$npm_config_plugin node scripts/generateJson.js", + "generate:webview:json:clean": "npm run remove:json && npm run generate:webview:json --plugin=$npm_config_plugin", + "generate:config:json": "PLUGIN=$npm_config_plugin node scripts/generateConfig.js", + "add:plugin": "PLUGIN=$npm_config_plugin TYPE=$npm_config_type node scripts/addPlugin.js", + "add:view": "PLUGIN=$npm_config_plugin WEB_VIEW=$npm_config_webview node scripts/addView.js", + "generate:webview:production": "npm run generate:bundle:production --plugin=$npm_config_plugin && npm run generate:webview:json:clean --plugin=$npm_config_plugin", + "generate:webview:development": "npm run generate:bundle:development --plugin=$npm_config_plugin && npm run generate:webview:json:clean --plugin=$npm_config_plugin", + "watch:webview:json": "npm run remove:json && onchange -i 'src/plugins/**/assets/*.json' 'src/plugins/**/views/**/*.json' -- npm run generate:webview:json --plugin=$npm_config_plugin", + "watch:config:json": "npm run remove:config && onchange -i 'src/plugins/**/assets/*.json' 'src/plugins/**/views/**/*.json' 'src/plugins/**/server/*' -- npm run generate:config:json --plugin=$npm_config_plugin", "server": "npm run kill-8080 && node server.js", - "test": "echo \"Error: no test specified\" && exit 1", - "build": "npm run publish:web --plugin=$npm_config_plugin && npm run generate:json --plugin=$npm_config_plugin", - "generate:json": "PLUGIN=$npm_config_plugin node generateJson.js", - "add-bundles": "node scripts/addBundles.js", - "add-jsons": "node scripts/addJsons.js", - "publish:web": "npm run publish --plugin=$npm_config_plugin && npm run add-bundles && npm run add-jsons" + "build": "npm run build:webview --plugin=$npm_config_plugin && npm run generate:plugin:json --plugin=$npm_config_plugin", + "generate:plugin:json": "PLUGIN=$npm_config_plugin node scripts/generatePlugin.js", + "add:webview:bundles": "node scripts/addBundles.js", + "add:webview:jsons": "node scripts/addJsons.js", + "build:webview": "npm run generate:webview:production --plugin=$npm_config_plugin && npm run add:webview:bundles && npm run add:webview:jsons", + "start": "concurrently \"npm run --prefix ../web dev:plugin --plugin=$npm_config_plugin\" \"npm run plugin:development --plugin=$npm_config_plugin\"" }, "dependencies": { "@ant-design/icons": "4.2.2", @@ -71,7 +73,7 @@ "babel-eslint": "10.1.0", "babel-loader": "8.2.2", "babel-plugin-transform-react-remove-prop-types": "0.4.24", - "concurrently": "5.3.0", + "concurrently": "7.3.0", "copy-dir": "1.3.0", "core-js": "2.6.12", "cross-env": "7.0.3", diff --git a/plugins/scripts/generateConfig.js b/plugins/scripts/generateConfig.js new file mode 100644 index 0000000000..ad3184d453 --- /dev/null +++ b/plugins/scripts/generateConfig.js @@ -0,0 +1,76 @@ +const fs = require('fs'); +const path = require('path'); +const uglifyEs = require('uglify-es'); +const mkdirp = require('mkdirp'); +const { minify: htmlMinify } = require('html-minifier-terser'); +const { FILES, PATHS } = require('./patterns'); +const { getWebView } = require('./utils') + +const { env: { PLUGIN: plugin } } = process; + +if (!fs.existsSync('config')) { + mkdirp.sync('config') +} + +if (!plugin) { + console.error('No plugin name given'); + process.exit(1); +} + +const pluginPath = path.resolve(__dirname, '..', PATHS.ROOT, plugin, PATHS.SERVER); + +if (!fs.existsSync(pluginPath)) { + console.error(`Plugin ${plugin} does not exist`); + process.exit(1); +} + +const scriptPath = path.resolve(pluginPath, FILES.SCRIPT); +const adminViewPath = path.resolve(pluginPath, FILES.ADMIN_VIEW); + +console.log(`Generating plugin JSON object for plugin ${plugin}...\n`); + +const config = fs.readFileSync(path.resolve(pluginPath, FILES.CONFIG)); +let script = null; +let admin_view = null; + +if (fs.existsSync(scriptPath)) { + const rawScript = fs.readFileSync(scriptPath, 'utf-8'); + script = uglifyEs.minify(rawScript); + + if (script.error) { + console.error('Error occured while minifying script\n'); + console.error(script.error); + process.exit(1); + } + + script = script.code; +} + +if (fs.existsSync(adminViewPath)) { + const rawAdminView = fs.readFileSync(adminViewPath, 'utf-8'); + + try { + admin_view = htmlMinify(rawAdminView, { + minifyJS: true, + minifyCSS: true, + collapseWhitespace: true + }); + } catch (err) { + console.error('Error occured while minifying admin_view\n'); + console.error(err); + process.exit(1); + } +} + +const pluginJson = JSON.stringify({ + ...JSON.parse(config), + script, + admin_view, + web_view: getWebView(plugin) +}, null, '\t'); + +fs.writeFileSync('config/config.json', pluginJson); + +console.log(pluginJson); + +process.exit(0); \ No newline at end of file diff --git a/plugins/scripts/generateJson.js b/plugins/scripts/generateJson.js index 0538379f12..2b7368a6e5 100644 --- a/plugins/scripts/generateJson.js +++ b/plugins/scripts/generateJson.js @@ -1,10 +1,9 @@ const fs = require("fs"); const path = require("path"); const glob = require("glob"); -const merge = require("lodash.merge"); const mkdirp = require('mkdirp'); -const { PATTERNS, FILES, PATHS } = require("./patterns"); -const { readFile, saveFile, getBundlePath } = require("./utils"); +const { PATTERNS, PATHS } = require("./patterns"); +const { saveFile, getWebView } = require("./utils"); const { env: { PLUGIN: plugin }} = process; const pluginsPattern = plugin ? `${PATHS.ROOT}/${plugin}` : PATTERNS.PLUGINS; @@ -15,37 +14,12 @@ if (!fs.existsSync('json')) { mkdirp.sync('json') } -plugins.forEach(pluginPath => { - const pluginName = pluginPath.split(path.sep)[2] - const assetsPath = `${PATHS.ROOT}/${pluginName}/${PATHS.ASSETS}` - const pluginPattern = `${PATHS.ROOT}/${pluginName}/${PATTERNS.VIEW}`; +plugins.forEach((pluginPath) => { + const plugin = pluginPath.split(path.sep)[2] - let assetsAdded = false; - const assets = { - strings: readFile(`${assetsPath}/${FILES.STRINGS}`), - icons: readFile(`${assetsPath}/${FILES.ICONS}`), - }; - - const webViews = glob.sync(pluginPattern, { noglobstar: true }) - .reduce((acc, curr) => { - const view = readFile(`${path.dirname(curr)}/${FILES.VIEW}`); - const generatedView = { - src: getBundlePath(curr), - ...(assetsAdded ? {} : { meta: { ...assets }}), - }; - - // development - // const webView = merge({}, view, generatedView); - const webView = merge({}, generatedView, view); - - assetsAdded = true; - - return [...acc, webView] - }, []); - - const plugin = { - web_view: webViews + const content = { + web_view: getWebView(plugin) } - saveFile(`json/${pluginName}.json`, plugin); -}) \ No newline at end of file + saveFile(`json/${plugin}.json`, content); +}); \ No newline at end of file diff --git a/plugins/generateJson.js b/plugins/scripts/generatePlugin.js similarity index 93% rename from plugins/generateJson.js rename to plugins/scripts/generatePlugin.js index a08f071f7f..5c80621776 100644 --- a/plugins/generateJson.js +++ b/plugins/scripts/generatePlugin.js @@ -2,7 +2,7 @@ const fs = require('fs'); const path = require('path'); const uglifyEs = require('uglify-es'); const { minify: htmlMinify } = require('html-minifier-terser'); -const { FILES, PATHS } = require('./scripts/patterns'); +const { FILES, PATHS } = require('./patterns'); const { env: { PLUGIN: plugin } } = process; @@ -11,7 +11,7 @@ if (!plugin) { process.exit(1); } -const pluginPath = path.resolve(__dirname, PATHS.ROOT, plugin, PATHS.SERVER); +const pluginPath = path.resolve(__dirname, '..', PATHS.ROOT, plugin, PATHS.SERVER); if (!fs.existsSync(pluginPath)) { console.error(`Plugin ${plugin} does not exist`); diff --git a/plugins/scripts/utils.js b/plugins/scripts/utils.js index b4487dfcd5..4afbbdaebb 100644 --- a/plugins/scripts/utils.js +++ b/plugins/scripts/utils.js @@ -1,7 +1,9 @@ const fs = require("fs"); const path = require("path"); const beautify = require("json-beautify"); -const { PLUGINS_PATH, PATHS, FILES } = require("./patterns"); +const glob = require("glob"); +const merge = require("lodash.merge"); +const { PLUGINS_PATH, PATHS, FILES, PATTERNS } = require("./patterns"); const saveFile = (output, content) => { fs.writeFileSync(output, beautify(content, null, 4, 100)); @@ -32,8 +34,35 @@ const getBundlePath = (pathname) => { return `${PLUGINS_PATH}/${pluginName}/v${version}/${bundleName}.js` } +const getWebView = (plugin) => { + const assetsPath = `${PATHS.ROOT}/${plugin}/${PATHS.ASSETS}`; + const viewsPattern = `${PATHS.ROOT}/${plugin}/${PATTERNS.VIEW}`; + + let assetsAdded = false; + const assets = { + strings: readFile(`${assetsPath}/${FILES.STRINGS}`), + icons: readFile(`${assetsPath}/${FILES.ICONS}`), + }; + + const webViews = glob.sync(viewsPattern, { noglobstar: true }) + .reduce((acc, curr) => { + const view = readFile(`${path.dirname(curr)}/${FILES.VIEW}`); + const generatedView = { + src: getBundlePath(curr), + ...(assetsAdded ? {} : { meta: { ...assets }}), + }; + + const webView = merge({}, generatedView, view); + + assetsAdded = true; + + return [...acc, webView] + }, []); + + return webViews.length !== 0 ? webViews : null; +} + module.exports = { saveFile, - readFile, - getBundlePath, + getWebView, } \ No newline at end of file diff --git a/plugins/server.js b/plugins/server.js index 8234599259..9266217980 100644 --- a/plugins/server.js +++ b/plugins/server.js @@ -6,5 +6,6 @@ const PORT = 8080; app.use(nocache()); app.use(express.static('dist')); app.use(express.static('json')); +app.use(express.static('config')); app.listen(PORT, () => console.log(`Server listening on port: ${PORT}`)); \ No newline at end of file From e181da47feb1173231ed3f186b5a4f456f6db2e6 Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Fri, 2 Sep 2022 01:36:23 +0300 Subject: [PATCH 005/132] Prevent Postplugin process exit --- server/plugins/controllers.js | 14 ++- server/plugins/index.js | 216 ++++++++++++++++++++-------------- 2 files changed, 136 insertions(+), 94 deletions(-) diff --git a/server/plugins/controllers.js b/server/plugins/controllers.js index c4c5fd572d..1b4ea77e00 100644 --- a/server/plugins/controllers.js +++ b/server/plugins/controllers.js @@ -38,7 +38,7 @@ const getPlugins = async (req, res) => { 'postscript' ] }, - order: [[ 'id', 'asc' ]] + order: [['id', 'asc']] }; if (name) { @@ -59,7 +59,7 @@ const getPlugins = async (req, res) => { count: data.count, data: data.rows.map((plugin) => { plugin.enabled_admin_view = !!plugin.admin_view; - return lodash.omit(plugin, [ 'admin_view' ]); + return lodash.omit(plugin, ['admin_view']); }) }; @@ -225,7 +225,7 @@ const postPlugin = async (req, res) => { throw new Error(`Error while minifying script: ${minifiedScript.error.message}`); } - pluginConfig[field] = minifiedScript.code; + pluginConfig[field] = minifiedScript.code; } break; case 'description': @@ -281,7 +281,10 @@ const postPlugin = async (req, res) => { ); if (plugin.enabled && plugin.script) { - process.exit(); + const { startPlugin } = require('./index'); + + startPlugin(plugin); + // process.exit(); } } catch (err) { loggerPlugin.error( @@ -411,7 +414,7 @@ const putPlugin = async (req, res) => { if ( lodash.isPlainObject(plugin[field]) && plugin[field][key].overwrite === false - && (!value[key] || value[key].overwrite === false) + && (!value[key] || value[key].overwrite === false) ) { value[key] = plugin[field][key]; } @@ -764,6 +767,7 @@ const enablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { + // startPlugin(plugin); process.exit(); } } catch (err) { diff --git a/server/plugins/index.js b/server/plugins/index.js index 4dc4af3a2e..343f169481 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -48,6 +48,9 @@ const tripleBeam = require('triple-beam'); const uglifyEs = require('uglify-es'); const bodyParser = require('body-parser'); +let app; +let activePlugins = {}; + const getInstalledLibrary = async (name, version) => { const jsonFilePath = path.resolve(__dirname, '../node_modules', name, 'package.json'); @@ -102,6 +105,113 @@ const installLibrary = async (library) => { } }; +const stopPlugin = async (plugin) => { + + try { + delete activePlugins[plugin.name] + + } catch (err) { + loggerPlugin.error( + 'plugins/index/kill_plugin', + `error while stopping plugin ${plugin.name}`, + err.message + ); + } +} + +const startPlugin = async (plugin) => { + try { + loggerPlugin.verbose( + 'plugins/index/initialization', + `starting plugin ${plugin.name}` + ); + + const context = { + configValues: { + publicMeta: plugin.public_meta, + meta: plugin.meta + }, + pluginLibraries: { + app, + loggerPlugin, + toolsLib + }, + app, + toolsLib, + lodash, + expressValidator, + loggerPlugin, + multer, + moment, + mathjs, + bluebird, + umzug, + rp, + sequelize, + uuid, + jwt, + momentTz, + json2csv, + flat, + ws, + cron, + randomString, + bcryptjs, + expectCt, + validator, + uglifyEs, + otp, + latestVersion, + geoipLite, + nodemailer, + wsHeartbeatServer, + wsHeartbeatClient, + cors, + winston, + elasticApmNode, + winstonElasticsearchApm, + tripleBeam, + bodyParser, + morgan, + meta: plugin.meta, + publicMeta: plugin.public_meta, + installedLibraries: {} + }; + + if (plugin.prescript && lodash.isArray(plugin.prescript.install) && !lodash.isEmpty(plugin.prescript.install)) { + loggerPlugin.verbose( + 'plugins/index/initialization', + `Installing packages for plugin ${plugin.name}` + ); + + for (const library of plugin.prescript.install) { + context.installedLibraries[library] = await installLibrary(library); + } + + loggerPlugin.verbose( + 'plugins/index/initialization', + `Plugin ${plugin.name} packages installed` + ); + } + + _eval(plugin.script, plugin.name, context, true); + + activePlugins[plugin.name] = true; + + loggerPlugin.verbose( + 'plugins/index/initialization', + `Plugin ${plugin.name} running` + ); + } catch (err) { + loggerPlugin.error( + 'plugins/index/initialization', + `error while starting plugin ${plugin.name}`, + err.message + ); + } +} + + checkStatus() .then(async () => { loggerPlugin.info( @@ -109,7 +219,7 @@ checkStatus() 'Initializing Plugin Server...' ); - const app = express(); + app = express(); app.use(morgan(morganType, { stream })); app.listen(PORT); @@ -120,6 +230,15 @@ checkStatus() app.use(domainMiddleware); helmetMiddleware(app); + app.use((req, res, next) => { + if (req.path.length > 1 && req.path.includes('/plugins/')) { + //Check if plugin is disabled + next(); + } else { + next(); + } + }) + app.use('/plugins', routes); const plugins = await Plugin.findAll({ @@ -133,93 +252,7 @@ checkStatus() }); for (const plugin of plugins) { - try { - loggerPlugin.verbose( - 'plugins/index/initialization', - `starting plugin ${plugin.name}` - ); - - const context = { - configValues: { - publicMeta: plugin.public_meta, - meta: plugin.meta - }, - pluginLibraries: { - app, - loggerPlugin, - toolsLib - }, - app, - toolsLib, - lodash, - expressValidator, - loggerPlugin, - multer, - moment, - mathjs, - bluebird, - umzug, - rp, - sequelize, - uuid, - jwt, - momentTz, - json2csv, - flat, - ws, - cron, - randomString, - bcryptjs, - expectCt, - validator, - uglifyEs, - otp, - latestVersion, - geoipLite, - nodemailer, - wsHeartbeatServer, - wsHeartbeatClient, - cors, - winston, - elasticApmNode, - winstonElasticsearchApm, - tripleBeam, - bodyParser, - morgan, - meta: plugin.meta, - publicMeta: plugin.public_meta, - installedLibraries: {} - }; - - if (plugin.prescript && lodash.isArray(plugin.prescript.install) && !lodash.isEmpty(plugin.prescript.install)) { - loggerPlugin.verbose( - 'plugins/index/initialization', - `Installing packages for plugin ${plugin.name}` - ); - - for (const library of plugin.prescript.install) { - context.installedLibraries[library] = await installLibrary(library); - } - - loggerPlugin.verbose( - 'plugins/index/initialization', - `Plugin ${plugin.name} packages installed` - ); - } - - _eval(plugin.script, plugin.name, context, true); - - loggerPlugin.verbose( - 'plugins/index/initialization', - `Plugin ${plugin.name} running` - ); - } catch (err) { - loggerPlugin.error( - 'plugins/index/initialization', - `error while starting plugin ${plugin.name}`, - err.message - ); - } + startPlugin(plugin); } loggerPlugin.info( @@ -244,4 +277,9 @@ checkStatus() ); setTimeout(() => { process.exit(1); }, 5000); - }); \ No newline at end of file + }); + +module.exports = { + startPlugin, + stopPlugin +} \ No newline at end of file From 480f9c887936c39609498aaa26b411fd32ae25f3 Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Fri, 2 Sep 2022 15:57:15 +0530 Subject: [PATCH 006/132] Fix for the carousel loading issue in landing page --- web/src/containers/Home/index.js | 33 +++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/web/src/containers/Home/index.js b/web/src/containers/Home/index.js index 882e9c5343..329e0692ef 100644 --- a/web/src/containers/Home/index.js +++ b/web/src/containers/Home/index.js @@ -749,8 +749,31 @@ class Home extends Component { case 'carousel_section': { const { markets } = this.props; const { chartData } = this.state; - const marketsData = [...markets, ...markets, ...markets]; - const items = marketsData.map((market, index) => ( + let testMarket = []; + let loopCnt = 0; + if (markets.length < 12) { + if (markets.length === 1) { + loopCnt = 12; + } else if (markets.length === 2) { + loopCnt = 6; + } else if (markets.length === 3) { + loopCnt = 4; + } else if (markets.length === 4 || markets.length === 5) { + loopCnt = 3; + } else if (markets.length > 5 || markets.length < 12) { + loopCnt = 2; + } + + for (let i = 0; i < loopCnt; i++) { + testMarket = [...testMarket, ...markets]; + } + } else { + testMarket = [...markets]; + } + + const duration = parseInt((50 / 12) * testMarket.length); + const marketsData = [...testMarket, ...testMarket, ...testMarket]; + const items = marketsData.map((market) => ( <MarketCard market={market} onDragStart={this.handleDragStart} @@ -762,11 +785,15 @@ class Home extends Component { return ( <div className="home_carousel_section "> <div class="slideshow-wrapper"> - <div class="parent-slider d-flex"> + <div + class="parent-slider d-flex" + style={{ animationDuration: `${duration}s` }} + > {items.map((sec, index) => { return ( <div className="section" + key={index} onClick={() => this.sectionToNav(sec)} > {sec} From 8e8b1143653d209e42bc79191b50e1f3185a1274 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Sat, 3 Sep 2022 13:01:03 +0300 Subject: [PATCH 007/132] hotfix --- web/src/components/Form/FormFields/FieldWrapper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/Form/FormFields/FieldWrapper.js b/web/src/components/Form/FormFields/FieldWrapper.js index ef1b972884..3d25073ef3 100644 --- a/web/src/components/Form/FormFields/FieldWrapper.js +++ b/web/src/components/Form/FormFields/FieldWrapper.js @@ -66,13 +66,13 @@ export const FieldContent = ({ className="field-valid" /> )} - {showCross && hasValue && ( + {/* {showCross && hasValue && ( <ReactSVG onClick={onCrossClick} src={STATIC_ICONS.CANCEL_CROSS_ACTIVE} className="clear-field" /> - )} + )} */} </div> {!hideUnderline && ( <span From 4cd65ede725b3d917e66215b25754cc9023db1f7 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Sat, 3 Sep 2022 13:19:05 +0300 Subject: [PATCH 008/132] hot fix v2 --- web/src/components/Form/FormFields/FieldWrapper.js | 4 ++-- web/src/components/Form/FormFields/_FieldWrapper.scss | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/web/src/components/Form/FormFields/FieldWrapper.js b/web/src/components/Form/FormFields/FieldWrapper.js index 3d25073ef3..ef1b972884 100644 --- a/web/src/components/Form/FormFields/FieldWrapper.js +++ b/web/src/components/Form/FormFields/FieldWrapper.js @@ -66,13 +66,13 @@ export const FieldContent = ({ className="field-valid" /> )} - {/* {showCross && hasValue && ( + {showCross && hasValue && ( <ReactSVG onClick={onCrossClick} src={STATIC_ICONS.CANCEL_CROSS_ACTIVE} className="clear-field" /> - )} */} + )} </div> {!hideUnderline && ( <span diff --git a/web/src/components/Form/FormFields/_FieldWrapper.scss b/web/src/components/Form/FormFields/_FieldWrapper.scss index a95fb9c42f..67b9aa599a 100644 --- a/web/src/components/Form/FormFields/_FieldWrapper.scss +++ b/web/src/components/Form/FormFields/_FieldWrapper.scss @@ -77,6 +77,8 @@ $outline-height: 1.5px; } .clear-field { + width: 1.25rem !important; + height: 1.25rem !important; @include size($valid-icon-size); svg { @include size($valid-icon-size); From 0ef23f26a61c47a7bb453f9917a78657e961ba1c Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Sun, 4 Sep 2022 19:26:25 +0900 Subject: [PATCH 009/132] use npmi instead of npm programmatic for 3rd party plugin installation --- server/package.json | 4 +-- server/plugins/index.js | 60 ++++++++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/server/package.json b/server/package.json index 5ced19e23f..f03df4c04a 100644 --- a/server/package.json +++ b/server/package.json @@ -48,8 +48,8 @@ "multicoin-address-validator": "0.5.5", "node-cron": "2.0.3", "nodemailer": "6.4.6", - "npm": "5.7.1", - "npm-programmatic": "0.0.12", + "npm": "8.19.1", + "npmi": "4.0.0", "otp": "0.1.3", "pg": "6.4.2", "pg-hstore": "2.3.2", diff --git a/server/plugins/index.js b/server/plugins/index.js index 4dc4af3a2e..ff34cb02cb 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -13,7 +13,7 @@ const { Plugin } = require('../db/models'); const path = require('path'); const fs = require('fs'); const latestVersion = require('latest-version'); -const npm = require('npm-programmatic'); +const npmi = require('npmi'); const sequelize = require('sequelize'); const _eval = require('eval'); const toolsLib = require('hollaex-tools-lib'); @@ -76,30 +76,48 @@ const getInstalledLibrary = async (name, version) => { const installLibrary = async (library) => { const [name, version = 'latest'] = library.split('@'); + const options = { + name, + version, + path: '.', + forceInstall: false, + npmLoad: { + loglevel: 'silent' + } + }; - try { - const data = await getInstalledLibrary(name, version); - return data; - } catch (err) { - loggerPlugin.verbose( - 'plugins/index/installLibrary', - `${name} version ${version} installing` - ); - - await npm.install([`${name}@${version}`], { - cwd: path.resolve(__dirname, '../'), - save: true, - output: true - }); + + npmi(options, function (err, result) { + if (err) { + if (err.code === npmi.LOAD_ERR) { + loggerPlugin.error( + 'plugins/installLibrary', + `Error installing packages for plugin ${name}`, + err.code, + err.message + ); + } + else if (err.code === npmi.INSTALL_ERR) { + loggerPlugin.error( + 'plugins/installLibrary', + `Error installing packages for plugin ${name}`, + err.code, + err.message + ); + } + throw new Error(err.message); + } - loggerPlugin.verbose( - 'plugins/index/installLibrary', - `${name} version ${version} installed` + // installed + loggerPlugin.info( + 'plugins/installLibrary', + `Successful installation of packages for plugin ${name}`, + options.name+'@'+options.version+' installed successfully in '+path.resolve(options.path) ); + }); - const lib = require(name); - return lib; - } + const lib = require(name); + return lib; }; checkStatus() From 3089372e11eff98edaa2a641611c50982226b937 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Sun, 4 Sep 2022 22:26:24 +0900 Subject: [PATCH 010/132] index.css update --- web/src/index.css | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/web/src/index.css b/web/src/index.css index 4ead8902b4..9d5117fbbb 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -362,8 +362,8 @@ table th { height: auto; flex: 1; } .app_container.layout-mobile .app_container-content { - min-height: calc( 100vh - 10rem); - max-height: calc( 100vh - 10rem); + min-height: calc( 100vh - 10rem); + max-height: calc( 100vh - 10rem); max-width: 100vw; overflow-y: scroll; } .app_container.layout-mobile .app_container-content.chart-embed { @@ -379,8 +379,8 @@ table th { .app_container.layout-mobile .app_container-content .app_container-main.no_bottom_navigation { height: 100%; } .app_container.layout-mobile .content-with-bar { - min-height: calc( 100vh - 17rem); - max-height: calc( 100vh - 17rem); + min-height: calc( 100vh - 17rem); + max-height: calc( 100vh - 17rem); max-width: 100vw; padding: 1rem; margin: 0; @@ -3238,8 +3238,8 @@ table th { .layout-mobile .quote-container { background-color: transparent; overflow-y: scroll; - min-height: calc( 100vh - 17rem); - max-height: calc( 100vh - 17rem); } + min-height: calc( 100vh - 17rem); + max-height: calc( 100vh - 17rem); } .layout-mobile .quote-container .quick_trade-wrapper { flex: 1 1; min-width: 95vw !important; @@ -3393,7 +3393,7 @@ table th { .layout-mobile .presentation_container.verification_container { padding-top: 0 !important; - max-height: calc( 100vh - 10rem); } + max-height: calc( 100vh - 10rem); } .layout-mobile .presentation_container.verification_container .header-content { font-size: 13px !important; } @@ -5533,8 +5533,7 @@ table th { height: 0.5px; opacity: 0.7; } .modal-market-menu .app-bar-search-field { - flex-direction: row; - padding-left: 0.5rem; } + flex-direction: row; } .modal-market-menu .app-bar-add-tab-search { height: 4rem; } .modal-market-menu .app-bar-tab-menu-list { @@ -5556,7 +5555,7 @@ table th { padding: 0.3rem 0; } .modal-market-menu .app-bar-add-tab-content .scroll-view { overflow-y: auto; - height: calc( 100vh - 19rem - 8rem); } + height: calc( 100vh - 19rem - 8rem); } .modal-market-menu .app-bar-tab-overflow-content { background-color: var(--base_background); color: var(--labels_secondary-inactive-label-text-graphics); @@ -7706,6 +7705,17 @@ table th { .field-children .field-valid svg { width: 1.25rem; height: 1.25rem; } + .field-children .clear-field { + width: 1.25rem !important; + height: 1.25rem !important; + width: 1.25rem; + height: 1.25rem; + position: absolute; + right: 0px; + bottom: 0.5rem; } + .field-children .clear-field svg { + width: 1.25rem; + height: 1.25rem; } .input-box-field { width: 100%; @@ -8276,14 +8286,14 @@ table th { right: 0 !important; min-width: 100vw; max-width: 100vw; - min-height: calc( 100vh - 4rem); - max-height: calc( 100vh - 4rem); + min-height: calc( 100vh - 4rem); + max-height: calc( 100vh - 4rem); border-radius: 0 !important; padding: 0 !important; } .layout-mobile .ReactModal__Content .dialog-mobile-content { padding: 2.5rem !important; - min-height: calc( 100vh - 8rem); - max-height: calc( 100vh - 8rem); + min-height: calc( 100vh - 8rem); + max-height: calc( 100vh - 8rem); display: flex; } .layout-mobile.LogoutModal .ReactModal__Content { @@ -10430,7 +10440,7 @@ table th { margin-left: 0.5rem; } .app_footer-container .footer-row-content .footer_separter .footer-txt { width: 23rem; - margin-left: 1.5rem; + margin-left: 2rem !important; margin-top: 0.5rem; } .app_footer-container .footer-row-content .footer_separter .footer-logo { background-size: contain; @@ -10943,7 +10953,11 @@ body { color: var(--labels_important-active-labels-text-graphics); font-size: 1.1rem !important; font-family: "Open Sans" !important; - height: 100%; } + height: 100%; + transition: all 0.3s; } + +* { + transition: all 0.3s; } .direction_rtl.apply_rtl { direction: rtl; } From ecdea7515cdc085ea95b8152cf4baf1f60369df9 Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Mon, 5 Sep 2022 15:56:27 +0530 Subject: [PATCH 011/132] Changes for the flat line instead of chart if it's not available --- web/src/containers/Summary/_Summary.scss | 1 + .../TradeTabs/components/MarketRow.js | 9 +- .../TradeTabs/components/_MarketList.scss | 2 + web/src/index.css | 125 +++++++++--------- 4 files changed, 76 insertions(+), 61 deletions(-) diff --git a/web/src/containers/Summary/_Summary.scss b/web/src/containers/Summary/_Summary.scss index f0d2aac4c2..9f18731143 100644 --- a/web/src/containers/Summary/_Summary.scss +++ b/web/src/containers/Summary/_Summary.scss @@ -442,6 +442,7 @@ $trade-tab--arrow-size: 0.4rem; width: 3rem; svg { + width: 2.5rem; .btc0, .btc1 { fill: $coin-btc; diff --git a/web/src/containers/TradeTabs/components/MarketRow.js b/web/src/containers/TradeTabs/components/MarketRow.js index bf1a916e0b..51f56c9d65 100644 --- a/web/src/containers/TradeTabs/components/MarketRow.js +++ b/web/src/containers/TradeTabs/components/MarketRow.js @@ -49,7 +49,14 @@ class MarketRow extends Component { </td> <td className="td-chart"> <SparkLine - data={chartData[key] || []} + data={ + !chartData[key] || + (chartData[key] && + chartData[key].close && + chartData[key].close.length < 2) + ? { close: [0.1, 0.1, 0.1], open: [] } + : chartData[key] + } containerProps={{ style: { height: '100%', width: '100%' } }} /> </td> diff --git a/web/src/containers/TradeTabs/components/_MarketList.scss b/web/src/containers/TradeTabs/components/_MarketList.scss index f52cc69eae..f97ee33fb6 100644 --- a/web/src/containers/TradeTabs/components/_MarketList.scss +++ b/web/src/containers/TradeTabs/components/_MarketList.scss @@ -11,6 +11,8 @@ margin: 0.3rem 0.3rem 0 0.3rem; svg { + width: 2.5rem !important; + height: 2.5rem !important; .btc0, .btc1 { fill: $coin-btc; diff --git a/web/src/index.css b/web/src/index.css index eb09065afa..302554ca42 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -3762,36 +3762,38 @@ table th { margin-bottom: 8px; } .summary-container .assets-wrapper .coin-price-container .coin-price { width: 3rem; } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .btc0, - .summary-container .assets-wrapper .coin-price-container .coin-price svg .btc1 { - fill: var(--coin-btc); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .eth0, - .summary-container .assets-wrapper .coin-price-container .coin-price svg .eth1 { - fill: var(--coin-eth); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .bch-icon { - fill: var(--coin-bch); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .xrp-icon { - fill: var(--coin-xrp); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .xmr-icon { - fill: var(--coin-xmr); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .ada-icon { - fill: var(--coin-ada); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .bnb-icon { - fill: var(--coin-bnb); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .dai-icon { - fill: var(--coin-dai); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .eos-icon { - fill: var(--coin-eos); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .ltc-icon { - fill: var(--coin-ltc); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .trx-icon { - fill: var(--coin-trx); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .tusd-icon { - fill: var(--coin-tusd); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .xlm-icon { - fill: var(--coin-xlm); } - .summary-container .assets-wrapper .coin-price-container .coin-price svg .default-icon { - fill: var(--labels_secondary-inactive-label-text-graphics) !important; } + .summary-container .assets-wrapper .coin-price-container .coin-price svg { + width: 2.5rem; } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .btc0, + .summary-container .assets-wrapper .coin-price-container .coin-price svg .btc1 { + fill: var(--coin-btc); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .eth0, + .summary-container .assets-wrapper .coin-price-container .coin-price svg .eth1 { + fill: var(--coin-eth); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .bch-icon { + fill: var(--coin-bch); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .xrp-icon { + fill: var(--coin-xrp); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .xmr-icon { + fill: var(--coin-xmr); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .ada-icon { + fill: var(--coin-ada); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .bnb-icon { + fill: var(--coin-bnb); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .dai-icon { + fill: var(--coin-dai); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .eos-icon { + fill: var(--coin-eos); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .ltc-icon { + fill: var(--coin-ltc); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .trx-icon { + fill: var(--coin-trx); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .tusd-icon { + fill: var(--coin-tusd); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .xlm-icon { + fill: var(--coin-xlm); } + .summary-container .assets-wrapper .coin-price-container .coin-price svg .default-icon { + fill: var(--labels_secondary-inactive-label-text-graphics) !important; } .summary-container .asset_wrapper_width { width: 100%; } .summary-container .xht_asset_wrapper_width { @@ -4094,36 +4096,39 @@ table th { .market-list__container .market-list__block .market-list__coin-icons { width: 2.8rem; margin: 0.3rem 0.3rem 0 0.3rem; } - .market-list__container .market-list__block .market-list__coin-icons svg .btc0, - .market-list__container .market-list__block .market-list__coin-icons svg .btc1 { - fill: var(--coin-btc); } - .market-list__container .market-list__block .market-list__coin-icons svg .eth0, - .market-list__container .market-list__block .market-list__coin-icons svg .eth1 { - fill: var(--coin-eth); } - .market-list__container .market-list__block .market-list__coin-icons svg .bch-icon { - fill: var(--coin-bch); } - .market-list__container .market-list__block .market-list__coin-icons svg .xrp-icon { - fill: var(--coin-xrp); } - .market-list__container .market-list__block .market-list__coin-icons svg .xmr-icon { - fill: var(--coin-xmr); } - .market-list__container .market-list__block .market-list__coin-icons svg .ada-icon { - fill: var(--coin-ada); } - .market-list__container .market-list__block .market-list__coin-icons svg .bnb-icon { - fill: var(--coin-bnb); } - .market-list__container .market-list__block .market-list__coin-icons svg .dai-icon { - fill: var(--coin-dai); } - .market-list__container .market-list__block .market-list__coin-icons svg .eos-icon { - fill: var(--coin-eos); } - .market-list__container .market-list__block .market-list__coin-icons svg .ltc-icon { - fill: var(--coin-ltc); } - .market-list__container .market-list__block .market-list__coin-icons svg .trx-icon { - fill: var(--coin-trx); } - .market-list__container .market-list__block .market-list__coin-icons svg .tusd-icon { - fill: var(--coin-tusd); } - .market-list__container .market-list__block .market-list__coin-icons svg .xlm-icon { - fill: var(--coin-xlm); } - .market-list__container .market-list__block .market-list__coin-icons svg .default-icon { - fill: var(--labels_secondary-inactive-label-text-graphics) !important; } + .market-list__container .market-list__block .market-list__coin-icons svg { + width: 2.5rem !important; + height: 2.5rem !important; } + .market-list__container .market-list__block .market-list__coin-icons svg .btc0, + .market-list__container .market-list__block .market-list__coin-icons svg .btc1 { + fill: var(--coin-btc); } + .market-list__container .market-list__block .market-list__coin-icons svg .eth0, + .market-list__container .market-list__block .market-list__coin-icons svg .eth1 { + fill: var(--coin-eth); } + .market-list__container .market-list__block .market-list__coin-icons svg .bch-icon { + fill: var(--coin-bch); } + .market-list__container .market-list__block .market-list__coin-icons svg .xrp-icon { + fill: var(--coin-xrp); } + .market-list__container .market-list__block .market-list__coin-icons svg .xmr-icon { + fill: var(--coin-xmr); } + .market-list__container .market-list__block .market-list__coin-icons svg .ada-icon { + fill: var(--coin-ada); } + .market-list__container .market-list__block .market-list__coin-icons svg .bnb-icon { + fill: var(--coin-bnb); } + .market-list__container .market-list__block .market-list__coin-icons svg .dai-icon { + fill: var(--coin-dai); } + .market-list__container .market-list__block .market-list__coin-icons svg .eos-icon { + fill: var(--coin-eos); } + .market-list__container .market-list__block .market-list__coin-icons svg .ltc-icon { + fill: var(--coin-ltc); } + .market-list__container .market-list__block .market-list__coin-icons svg .trx-icon { + fill: var(--coin-trx); } + .market-list__container .market-list__block .market-list__coin-icons svg .tusd-icon { + fill: var(--coin-tusd); } + .market-list__container .market-list__block .market-list__coin-icons svg .xlm-icon { + fill: var(--coin-xlm); } + .market-list__container .market-list__block .market-list__coin-icons svg .default-icon { + fill: var(--labels_secondary-inactive-label-text-graphics) !important; } .market-list__container .market-list__block .market-list__block-table { width: 100%; From 30344a8e4fe542d8ddd4c7ef80f5af4834b93e5d Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Mon, 5 Sep 2022 22:36:10 +0300 Subject: [PATCH 012/132] Add regex for disabled plugins --- server/plugins/controllers.js | 17 +++++++++++++---- server/plugins/index.js | 16 ++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/server/plugins/controllers.js b/server/plugins/controllers.js index 1b4ea77e00..3989d1cf2b 100644 --- a/server/plugins/controllers.js +++ b/server/plugins/controllers.js @@ -124,7 +124,10 @@ const deletePlugin = async (req, res) => { 'restarting plugin process' ); - process.exit(); + const { stopPlugin } = require('./index'); + + stopPlugin(plugin); + // process.exit(); } } catch (err) { loggerPlugin.error( @@ -712,7 +715,10 @@ const disablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { - process.exit(); + // process.exit(); + const { stopPlugin } = require('./index'); + + stopPlugin(plugin); } } catch (err) { loggerPlugin.error( @@ -767,8 +773,11 @@ const enablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { - // startPlugin(plugin); - process.exit(); + //process.exit(); + + const { startPlugin } = require('./index'); + + startPlugin(plugin); } } catch (err) { loggerPlugin.error( diff --git a/server/plugins/index.js b/server/plugins/index.js index 343f169481..cfb6c2ec5e 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -49,7 +49,7 @@ const uglifyEs = require('uglify-es'); const bodyParser = require('body-parser'); let app; -let activePlugins = {}; +let disabledPlugins = {}; const getInstalledLibrary = async (name, version) => { const jsonFilePath = path.resolve(__dirname, '../node_modules', name, 'package.json'); @@ -108,7 +108,8 @@ const installLibrary = async (library) => { const stopPlugin = async (plugin) => { try { - delete activePlugins[plugin.name] + const subStr = plugin.script.match(/\"\/plugins(.*?)\"/g); + disabledPlugins[plugin.name] = subStr || []; } catch (err) { loggerPlugin.error( @@ -196,7 +197,8 @@ const startPlugin = async (plugin) => { _eval(plugin.script, plugin.name, context, true); - activePlugins[plugin.name] = true; + if (disabledPlugins[plugin.name]) delete disabledPlugins[plugin.name]; + loggerPlugin.verbose( 'plugins/index/initialization', @@ -233,10 +235,12 @@ checkStatus() app.use((req, res, next) => { if (req.path.length > 1 && req.path.includes('/plugins/')) { //Check if plugin is disabled - next(); - } else { - next(); + for (let endpoints of Object.values(disabledPlugins)) { + console.log(endpoints.some(endpoint => endpoint.includes(req.path))) + if (endpoints.some(endpoint => endpoint.includes(req.path))) { return res.status(404).send() } + } } + next(); }) app.use('/plugins', routes); From 8be03c85d2cda8e3fccd7bd46eca7905cf0b11d8 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Tue, 6 Sep 2022 11:33:50 +0900 Subject: [PATCH 013/132] revert npmi to npm programmatic --- server/package.json | 2 +- server/plugins/index.js | 60 +++++++++++++++-------------------------- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/server/package.json b/server/package.json index f03df4c04a..f024048731 100644 --- a/server/package.json +++ b/server/package.json @@ -49,7 +49,7 @@ "node-cron": "2.0.3", "nodemailer": "6.4.6", "npm": "8.19.1", - "npmi": "4.0.0", + "npm-programmatic": "0.0.12", "otp": "0.1.3", "pg": "6.4.2", "pg-hstore": "2.3.2", diff --git a/server/plugins/index.js b/server/plugins/index.js index ff34cb02cb..4dc4af3a2e 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -13,7 +13,7 @@ const { Plugin } = require('../db/models'); const path = require('path'); const fs = require('fs'); const latestVersion = require('latest-version'); -const npmi = require('npmi'); +const npm = require('npm-programmatic'); const sequelize = require('sequelize'); const _eval = require('eval'); const toolsLib = require('hollaex-tools-lib'); @@ -76,48 +76,30 @@ const getInstalledLibrary = async (name, version) => { const installLibrary = async (library) => { const [name, version = 'latest'] = library.split('@'); - const options = { - name, - version, - path: '.', - forceInstall: false, - npmLoad: { - loglevel: 'silent' - } - }; - - npmi(options, function (err, result) { - if (err) { - if (err.code === npmi.LOAD_ERR) { - loggerPlugin.error( - 'plugins/installLibrary', - `Error installing packages for plugin ${name}`, - err.code, - err.message - ); - } - else if (err.code === npmi.INSTALL_ERR) { - loggerPlugin.error( - 'plugins/installLibrary', - `Error installing packages for plugin ${name}`, - err.code, - err.message - ); - } - throw new Error(err.message); - } + try { + const data = await getInstalledLibrary(name, version); + return data; + } catch (err) { + loggerPlugin.verbose( + 'plugins/index/installLibrary', + `${name} version ${version} installing` + ); - // installed - loggerPlugin.info( - 'plugins/installLibrary', - `Successful installation of packages for plugin ${name}`, - options.name+'@'+options.version+' installed successfully in '+path.resolve(options.path) + await npm.install([`${name}@${version}`], { + cwd: path.resolve(__dirname, '../'), + save: true, + output: true + }); + + loggerPlugin.verbose( + 'plugins/index/installLibrary', + `${name} version ${version} installed` ); - }); - const lib = require(name); - return lib; + const lib = require(name); + return lib; + } }; checkStatus() From dd53cc493946cba432214b56e001d78ddb1161c2 Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Tue, 6 Sep 2022 08:22:44 +0300 Subject: [PATCH 014/132] Remove console log --- server/plugins/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/plugins/index.js b/server/plugins/index.js index cfb6c2ec5e..ce23c65fee 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -236,7 +236,6 @@ checkStatus() if (req.path.length > 1 && req.path.includes('/plugins/')) { //Check if plugin is disabled for (let endpoints of Object.values(disabledPlugins)) { - console.log(endpoints.some(endpoint => endpoint.includes(req.path))) if (endpoints.some(endpoint => endpoint.includes(req.path))) { return res.status(404).send() } } } From 1aea5b79a5baac18246d2f1298bb9020ec309461 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 6 Sep 2022 11:07:21 +0430 Subject: [PATCH 015/132] fix for manual kyc plugins util function issue --- web/src/containers/Verification/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/containers/Verification/utils.js b/web/src/containers/Verification/utils.js index 4b0679eb45..79ebc7d817 100644 --- a/web/src/containers/Verification/utils.js +++ b/web/src/containers/Verification/utils.js @@ -65,8 +65,8 @@ export const documentInitialValues = ({ nationality, id_data = {} }) => { export const getCountry = (country) => { const filterValue = COUNTRIES.filter( ({ value, name }) => - value?.toUpperCase() === country.toUpperCase() || - name?.toUpperCase() === country.toUpperCase() + value?.toUpperCase() === country?.toUpperCase() || + name?.toUpperCase() === country?.toUpperCase() ); if (filterValue.length) return filterValue[0]; return initialCountry; From 80d58b57dce50ae5bbc3dbf2c299bd90f83ab169 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Tue, 6 Sep 2022 22:21:29 +0900 Subject: [PATCH 016/132] setEmail command added to tools --- server/tools/dbs/setEmail.js | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 server/tools/dbs/setEmail.js diff --git a/server/tools/dbs/setEmail.js b/server/tools/dbs/setEmail.js new file mode 100644 index 0000000000..5d19349ad5 --- /dev/null +++ b/server/tools/dbs/setEmail.js @@ -0,0 +1,39 @@ +const { Status } = require('../../db/models'); +const { CONFIGURATION_CHANNEL } = require('../../constants'); +const { + SMTP_SERVER, + SMTP_PORT, + SMTP_USER, + SMTP_PASSWORD +} = process.env; +const { publisher } = require('../../db/pubsub'); + +Status.findOne({}) + .then((status) => { + const secrets = { + ...status.secrets, + smtp: { + server: SMTP_SERVER, + port: SMTP_PORT, + user: SMTP_USER, + password: SMTP_PASSWORD + } + }; + + return status.update({ secrets }, { fields: ['secrets'], returning: true }); + }) + .then((data) => { + publisher.publish( + CONFIGURATION_CHANNEL, + JSON.stringify({ + type: 'update', + data: { secrets: data.secrets } + }) + ); + console.log('Emails values are reset'); + process.exit(0); + }) + .catch((err) => { + console.error('Error', err); + process.exit(1); + }); \ No newline at end of file From c99491d7ca9172fa9a1401ef3bea4fa32b3fa0e7 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Tue, 6 Sep 2022 22:23:46 +0900 Subject: [PATCH 017/132] version update --- server/api/swagger/swagger.yaml | 2 +- server/package.json | 2 +- version | 2 +- web/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/api/swagger/swagger.yaml b/server/api/swagger/swagger.yaml index f157b0eeed..5d372506c5 100644 --- a/server/api/swagger/swagger.yaml +++ b/server/api/swagger/swagger.yaml @@ -1,6 +1,6 @@ swagger: "2.0" info: - version: "2.4.1" + version: "2.4.2" title: HollaEx Kit host: api.hollaex.com basePath: /v2 diff --git a/server/package.json b/server/package.json index f024048731..ec402c1f6f 100644 --- a/server/package.json +++ b/server/package.json @@ -1,5 +1,5 @@ { - "version": "2.4.1", + "version": "2.4.2", "private": false, "description": "HollaEx Kit", "keywords": [ diff --git a/version b/version index 58073ef8d7..acdc3f1b0b 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.4.1 \ No newline at end of file +2.4.2 \ No newline at end of file diff --git a/web/package.json b/web/package.json index 4dca3cf51c..bcc101e34a 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "hollaex-kit", - "version": "2.4.1", + "version": "2.4.2", "private": true, "dependencies": { "@ant-design/compatible": "1.0.5", From 1949ac1776c273abe47dbc871752e26f69869d63 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 6 Sep 2022 19:34:51 +0430 Subject: [PATCH 018/132] add booting logic to initialization for failed plugin requests --- web/public/index.html | 32 +++++++++++++++++++++++++++++++- web/src/helpers/boot.js | 36 ++++++++++++++++++++++++++++++++++++ web/src/index.js | 38 ++++++++++++++++++++++++++++---------- web/src/utils/icon.js | 2 +- 4 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 web/src/helpers/boot.js diff --git a/web/public/index.html b/web/public/index.html index 5d96bb2788..31484cf341 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -8,12 +8,42 @@ <link rel="shortcut icon" href="/favicon.ico"> <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="/css/bootstrap-grid.min.css"> - <script src="%PUBLIC_URL%/datafeeds/udf/dist/bundle.js"></script> + <style> + .app-booting { + display: none; + height: 100vh; + width: 100vw; + align-items: center; + justify-content: center; + } + .booting-container { + width: 5rem; + height: 5rem; + max-width: 100%; + max-height: 100%; + position: absolute; + pointer-events: none; + + background-size: contain; + background-repeat: no-repeat; + background-position: center; + } + #app-booting-image { + width: 100%; + height: 100%; + } + </style> + <script src="%PUBLIC_URL%/datafeeds/udf/dist/bundle.js"></script> </head> <body> <noscript> You need to enable JavaScript to run this app. </noscript> <div id="root"></div> + <div class="app-booting"> + <div class="booting-container"> + <img id="app-booting-image" alt="Loading ..."> + </div> + </div> </body> </html> \ No newline at end of file diff --git a/web/src/helpers/boot.js b/web/src/helpers/boot.js new file mode 100644 index 0000000000..b6ba92e09b --- /dev/null +++ b/web/src/helpers/boot.js @@ -0,0 +1,36 @@ +import ThemeBuilder from 'helpers/themeBuilder'; +import { defaultIconsKey } from 'utils/icon'; + +const getBooting = () => { + return document.querySelector('.app-booting'); +}; + +const getRoot = () => { + return document.getElementById('root'); +}; + +const getLoadingImage = () => { + return document.getElementById('app-booting-image'); +}; + +export const setLoadingImage = ({ icons }) => { + const theme = localStorage.getItem('theme'); + getLoadingImage().src = + icons[theme]['EXCHANGE_LOADER'] || + icons[defaultIconsKey]['EXCHANGE_LOADER']; +}; + +export const setLoadingStyle = ({ color: themes }) => { + const theme = localStorage.getItem('theme'); + ThemeBuilder(theme, themes); +}; + +export const showBooting = () => { + getBooting().style.display = 'flex'; + getRoot().style.display = 'none'; +}; + +export const hideBooting = () => { + getBooting().style.display = 'none'; + getRoot().style.display = 'block'; +}; diff --git a/web/src/index.js b/web/src/index.js index 03d0b495f7..30b4462b4c 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -75,6 +75,12 @@ import { } from 'utils/plugin'; import { drawFavIcon } from 'helpers/vanilla'; import { setupManifest } from 'helpers/manifest'; +import { + hideBooting, + showBooting, + setLoadingImage, + setLoadingStyle, +} from 'helpers/boot'; consoleKitInfo(); consolePluginDevModeInfo(); @@ -191,16 +197,6 @@ const getConfigs = async () => { store.dispatch(setInjectedValues(injected_values)); store.dispatch(setInjectedHTML(injected_html)); - const { - data: { data: plugins = [] } = { data: [] }, - } = await requestPlugins(); - - const allPlugins = IS_PLUGIN_DEV_MODE ? await mergePlugins(plugins) : plugins; - - store.dispatch(setPlugins(allPlugins)); - store.dispatch(setWebViews(allPlugins)); - store.dispatch(setHelpdeskInfo(allPlugins)); - const appConfigs = merge({}, defaultConfig, remoteConfigs, { coin_icons, captcha, @@ -208,6 +204,27 @@ const getConfigs = async () => { defaults, }); + setLoadingStyle(appConfigs); + setLoadingImage(appConfigs); + + try { + const { + data: { data: plugins = [] } = { data: [] }, + } = await requestPlugins(); + + const allPlugins = IS_PLUGIN_DEV_MODE + ? await mergePlugins(plugins) + : plugins; + + store.dispatch(setPlugins(allPlugins)); + store.dispatch(setWebViews(allPlugins)); + store.dispatch(setHelpdeskInfo(allPlugins)); + } catch (err) { + console.error(err); + showBooting(); + throw err; + } + const { app: { plugins_injected_html }, } = store.getState(); @@ -274,6 +291,7 @@ const initialize = async () => { injected_html, plugins_injected_html ); + hideBooting(); } catch (err) { console.error('Initialization failed!\n', err); setTimeout(initialize, 3000); diff --git a/web/src/utils/icon.js b/web/src/utils/icon.js index 928fbd39f2..b080ff8487 100644 --- a/web/src/utils/icon.js +++ b/web/src/utils/icon.js @@ -2,7 +2,7 @@ import defaultIcons from 'config/icons'; import merge from 'lodash.merge'; import { generateGlobalId } from 'utils/id'; -const defaultIconsKey = 'dark'; +export const defaultIconsKey = 'dark'; export const generateCoinIconId = (symbol) => `${symbol.toUpperCase()}_ICON`; From 50a28ae099c5c1980541f6f07024621fd557c789 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 7 Sep 2022 02:23:04 +0900 Subject: [PATCH 019/132] email typo fix --- server/mail/strings/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/mail/strings/en.json b/server/mail/strings/en.json index d05bb68f92..f788612601 100644 --- a/server/mail/strings/en.json +++ b/server/mail/strings/en.json @@ -10,7 +10,7 @@ "title": "KYC Documents Approved" }, "CONFIRM_EMAIL": { - "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immidiately and proceed to change your crendetials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", "title": "Security Verification" }, "LOGIN": { From 1f78e5a34b57cca89253b5736b3429694a731360 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 7 Sep 2022 15:53:55 +0430 Subject: [PATCH 020/132] set default layout for tools when added to the pro trade page --- web/src/containers/Trade/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/web/src/containers/Trade/index.js b/web/src/containers/Trade/index.js index aff60446ca..e464230f14 100644 --- a/web/src/containers/Trade/index.js +++ b/web/src/containers/Trade/index.js @@ -133,11 +133,11 @@ const defaultLayout = [ }, ]; -const getDefaultLayoutByTool = (tool) => - defaultLayout.find(({ i }) => i === tool) || {}; +const getLayoutByTool = (tool, layout = defaultLayout) => + layout.find(({ i }) => i === tool) || {}; -const layout = getLayout().map(({ w, h, x, y, i }) => { - const defaultItemLayout = getDefaultLayoutByTool(i); +const LAYOUT = getLayout().map(({ w, h, x, y, i }) => { + const defaultItemLayout = getLayoutByTool(i); const itemLayout = { ...defaultItemLayout }; if (defaultItemLayout.isResizable) { itemLayout.w = w; @@ -163,7 +163,7 @@ class Trade extends PureComponent { chartHeight: 0, chartWidth: 0, symbol: '', - layout: layout.length > 0 ? layout : defaultLayout, + layout: LAYOUT.length > 0 ? LAYOUT : defaultLayout, rowHeight, }; this.priceTimeOut = ''; @@ -208,8 +208,8 @@ class Trade extends PureComponent { .filter(([, { is_visible }]) => !!is_visible) .forEach(([tool]) => { if (!layout.find(({ i }) => i === tool)) { - const defaultItemLayout = getDefaultLayoutByTool(tool); - newItemsLayout.push({ ...defaultItemLayout, x: 0, y: Infinity }); + const defaultItemLayout = getLayoutByTool(tool); + newItemsLayout.push(defaultItemLayout); } }); this.setState({ layout: [...layout, ...newItemsLayout] }); From 98e3b12417c707384c8e256884736633ff8be057 Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Wed, 7 Sep 2022 19:04:35 +0530 Subject: [PATCH 021/132] Fix for custom Email issue in general page. --- web/src/containers/Admin/General/action.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/src/containers/Admin/General/action.js b/web/src/containers/Admin/General/action.js index 4c7745cbde..fd8f1eb023 100644 --- a/web/src/containers/Admin/General/action.js +++ b/web/src/containers/Admin/General/action.js @@ -28,8 +28,7 @@ export const getEmailStrings = (param) => { }; export const updateEmailStrings = (values) => { - values.html = values?.html?.replace(/"/g, '@@_BIT_@@'); - values.html = values?.html?.replace(/'/g, '@@_BIT_@@'); + values.html = values?.html?.replace(/\\"/g, "'"); const options = { method: 'PUT', body: JSON.stringify(values), From 550ec9e35a435b70bcaeabc96c3fd3177f9e417e Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 7 Sep 2022 21:08:29 +0430 Subject: [PATCH 022/132] improve relative path imports --- web/src/actions/orderAction.js | 4 +- web/src/components/AppBar/AnnouncementList.js | 4 +- web/src/components/AppBar/HamburgerMenu.js | 2 +- web/src/components/AppBar/LinkButton.js | 2 +- web/src/components/AppBar/ThemeSwitcher.js | 2 +- web/src/components/AppFooter/index.js | 3 +- web/src/components/Chat/ChatFooter.js | 6 +-- web/src/components/Chat/ChatHeader.js | 2 +- web/src/components/Chat/ChatMessage.js | 6 +-- web/src/components/CheckDeposit/index.js | 2 +- .../components/CheckTitle/HeaderSection.js | 5 +-- web/src/components/CheckTitle/index.js | 2 +- web/src/components/Countdown/index.js | 4 +- web/src/components/CurrencyBall/index.js | 2 +- web/src/components/CurrencyBall/withPrice.js | 6 +-- web/src/components/Dialog/DesktopDialog.js | 8 ++-- web/src/components/Dialog/MessageDisplay.js | 6 +-- web/src/components/Dialog/MobileDialog.js | 10 ++--- web/src/components/ElapsedTimer/index.js | 2 +- .../components/Form/FormFields/DumbField.js | 6 +-- .../components/Notification/ContactForm.js | 6 +-- .../Notification/GenerateAddress.js | 6 +-- .../components/Notification/GenerateApiKey.js | 5 +-- .../components/Notification/InviteFriends.js | 12 +++--- web/src/components/Notification/Logout.js | 5 +-- .../components/Notification/Notification.js | 6 +-- web/src/components/Notification/Order.js | 9 ++-- web/src/components/Notification/Trade.js | 9 ++-- .../components/Notification/Verification.js | 5 +-- .../Notification/VerificationWarning.js | 8 ++-- web/src/components/Notification/Withdraw.js | 8 ++-- web/src/components/Notification/constants.js | 6 +-- web/src/components/OtpForm/index.js | 10 ++--- web/src/components/QuickTrade/InputBlock.js | 10 ++--- web/src/components/QuickTrade/ToogleButton.js | 2 +- web/src/components/QuickTrade/utils.js | 2 +- .../components/SidebarHub/CurrencySelector.js | 1 - web/src/components/SidebarHub/index.js | 6 +-- web/src/components/Table/index.js | 2 +- web/src/components/Table/paginator.js | 4 +- web/src/components/Wallet/Section.js | 6 +-- web/src/components/Wallet/index.js | 9 ++-- .../components/WarningVerification/index.js | 2 +- web/src/containers/Admin/paths.js | 2 +- web/src/containers/AuthContainer/index.js | 6 +-- web/src/containers/Chat/index.js | 11 ++--- .../containers/ConfirmChangePassword/index.js | 4 +- web/src/containers/ContactForm/index.js | 8 ++-- web/src/containers/Deposit/index.js | 11 ++--- web/src/containers/ExpiredExchange/index.js | 13 +++--- .../containers/HelpfulResourcesForm/index.js | 4 +- web/src/containers/Login/LoginForm.js | 9 ++-- web/src/containers/Login/index.js | 12 +++--- web/src/containers/MobileHome/index.js | 8 ++-- .../RequestResetPasswordSuccess.js | 6 +-- .../RequestResetPassword/ResetPasswordForm.js | 8 ++-- .../containers/RequestResetPassword/index.js | 10 ++--- .../ResetPassword/ResetPasswordForm.js | 6 +-- .../ResetPassword/ResetPasswordSuccess.js | 4 +- web/src/containers/ResetPassword/index.js | 9 ++-- web/src/containers/Signup/SignupForm.js | 6 +-- web/src/containers/Signup/SignupSuccess.js | 4 +- web/src/containers/Signup/index.js | 8 ++-- .../Summary/MobileAccountTypeList.js | 7 ++- web/src/containers/Summary/MobileSummary.js | 6 +-- web/src/containers/Summary/index.js | 10 ++--- .../containers/TermsOfService/DepositFunds.js | 4 +- web/src/containers/TermsOfService/Form.js | 10 ++--- web/src/containers/TermsOfService/index.js | 4 +- web/src/containers/Trade/MobileChart.js | 2 +- web/src/containers/Trade/MobileOrders.js | 4 +- web/src/containers/Trade/MobileTrade.js | 7 ++- .../TransactionsHistory/HistoryDisplay.js | 18 ++++---- .../containers/TransactionsHistory/utils.js | 9 ++-- web/src/containers/UserProfile/FeesBlock.js | 4 +- .../UserProfile/IdentificationFormValues.js | 7 +-- .../UserProfile/InformationSection.js | 2 +- web/src/containers/UserProfile/LevelsBlock.js | 4 +- .../UserProfile/MobileFormValues.js | 4 +- .../containers/UserProfile/UpgradeWarning.js | 2 +- web/src/containers/UserProfile/index.js | 6 +-- web/src/containers/UserSecurity/ApiKeyForm.js | 8 ++-- .../containers/UserSecurity/ApiKeyHeaders.js | 6 +-- .../UserSecurity/ChangePasswordForm.js | 10 ++--- .../UserSecurity/DeveloperSection.js | 8 ++-- .../containers/UserSecurity/FreezeSection.js | 6 +-- web/src/containers/UserSecurity/OTP.js | 6 +-- web/src/containers/UserSecurity/OTPForm.js | 9 ++-- web/src/containers/UserSecurity/index.js | 4 +- .../containers/UserSecurity/utils_logins.js | 4 +- web/src/containers/UserSettings/AudioForm.js | 8 ++-- .../containers/UserSettings/LanguageForm.js | 12 +++--- .../UserSettings/NotificationForm.js | 9 ++-- .../UserSettings/SetOrderPortfolio.js | 14 +++--- .../containers/UserSettings/SettingsForm.js | 11 +++-- .../containers/UserSettings/UsernameForm.js | 13 +++--- web/src/containers/UserSettings/index.js | 43 +++++++++++++------ .../Verification/DocumentsVerification.js | 12 +++--- .../Verification/DocumentsVerificationHome.js | 6 +-- .../Verification/IdentityVerification.js | 16 +++---- .../Verification/IdentityVerificationHome.js | 6 +-- .../Verification/MobileVerification.js | 19 ++++---- .../Verification/MobileVerificationHome.js | 6 +-- .../containers/VerificationEmailCode/index.js | 8 ++-- .../EmailRequestForm.js | 6 +-- .../EmailRequestSuccess.js | 6 +-- .../VerificationEmailRequest/index.js | 10 ++--- web/src/containers/Wallet/CurrencyWallet.js | 12 +++--- .../containers/Withdraw/ReviewEmailContent.js | 6 +-- .../containers/WithdrawConfirmation/index.js | 8 ++-- 110 files changed, 362 insertions(+), 412 deletions(-) diff --git a/web/src/actions/orderAction.js b/web/src/actions/orderAction.js index aa73069055..a0dd3e3c6b 100644 --- a/web/src/actions/orderAction.js +++ b/web/src/actions/orderAction.js @@ -1,8 +1,8 @@ import axios from 'axios'; import ICONS from 'config/icons'; import { requestAuthenticated } from 'utils'; -import STRINGS from '../config/localizedStrings'; -import { playBackgroundAudioNotification } from '../utils/utils'; +import STRINGS from 'config/localizedStrings'; +import { playBackgroundAudioNotification } from 'utils/utils'; // Set orders from websocket export function setUserOrders(orders) { diff --git a/web/src/components/AppBar/AnnouncementList.js b/web/src/components/AppBar/AnnouncementList.js index 5185f9c8a4..860faaee27 100644 --- a/web/src/components/AppBar/AnnouncementList.js +++ b/web/src/components/AppBar/AnnouncementList.js @@ -4,10 +4,10 @@ import { bindActionCreators } from 'redux'; import Scrollbars from 'react-custom-scrollbars'; import { EditWrapper, NotificationsList, Image } from 'components'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { getAnnouncement } from 'actions/appActions'; -import { LAST_UPDATED_NOTIFICATION_KEY } from '../../config/constants'; +import { LAST_UPDATED_NOTIFICATION_KEY } from 'config/constants'; const AnnouncementList = ({ icons: ICONS, diff --git a/web/src/components/AppBar/HamburgerMenu.js b/web/src/components/AppBar/HamburgerMenu.js index 9e1d58e52a..87fb2eda3d 100644 --- a/web/src/components/AppBar/HamburgerMenu.js +++ b/web/src/components/AppBar/HamburgerMenu.js @@ -1,6 +1,6 @@ import React from 'react'; import classnames from 'classnames'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; class HamburgerMenu extends React.Component { constructor(props) { diff --git a/web/src/components/AppBar/LinkButton.js b/web/src/components/AppBar/LinkButton.js index 2ccaf1154d..7a3c32acbe 100644 --- a/web/src/components/AppBar/LinkButton.js +++ b/web/src/components/AppBar/LinkButton.js @@ -1,7 +1,7 @@ import React from 'react'; import classnames from 'classnames'; import { Link } from 'react-router'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; const COMMON_CLASSES = [ 'text-uppercase', diff --git a/web/src/components/AppBar/ThemeSwitcher.js b/web/src/components/AppBar/ThemeSwitcher.js index 8b14cef97a..ae7c765168 100644 --- a/web/src/components/AppBar/ThemeSwitcher.js +++ b/web/src/components/AppBar/ThemeSwitcher.js @@ -1,7 +1,7 @@ import React from 'react'; import classnames from 'classnames'; import Image from 'components/Image'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; import withConfig from 'components/ConfigProvider/withConfig'; import { Select } from 'antd'; import { CaretDownOutlined } from '@ant-design/icons'; diff --git a/web/src/components/AppFooter/index.js b/web/src/components/AppFooter/index.js index d5c9068509..8076e5d728 100644 --- a/web/src/components/AppFooter/index.js +++ b/web/src/components/AppFooter/index.js @@ -1,9 +1,8 @@ import React from 'react'; import classnames from 'classnames'; import { isMobile } from 'react-device-detect'; -// import { PUBLIC_URL } from '../../config/constants'; import withConfig from 'components/ConfigProvider/withConfig'; -import Image from 'components/Image'; +import { Image } from 'components'; import withEdit from 'components/EditProvider/withEdit'; import STRINGS from 'config/localizedStrings'; diff --git a/web/src/components/Chat/ChatFooter.js b/web/src/components/Chat/ChatFooter.js index bb85196ca4..24a3a2b4c6 100644 --- a/web/src/components/Chat/ChatFooter.js +++ b/web/src/components/Chat/ChatFooter.js @@ -1,10 +1,10 @@ import React from 'react'; import classnames from 'classnames'; -import { ChatMessageBox, ButtonLink } from '../'; +import { ChatMessageBox, ButtonLink } from 'components'; import ChatEmoji from './ChatEmoji'; -import STRINGS from '../../config/localizedStrings'; -import { isLoggedIn } from '../../utils/token'; +import STRINGS from 'config/localizedStrings'; +import { isLoggedIn } from 'utils/token'; const ChatFooter = ({ sendMessage, diff --git a/web/src/components/Chat/ChatHeader.js b/web/src/components/Chat/ChatHeader.js index 6b756fd52a..229f498d6e 100644 --- a/web/src/components/Chat/ChatHeader.js +++ b/web/src/components/Chat/ChatHeader.js @@ -1,6 +1,6 @@ import React from 'react'; import classnames from 'classnames'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; export const MinimalizeChat = ({ minimized, onClick }) => ( <div className={classnames('d-flex', 'minimize-button')} onClick={onClick}> diff --git a/web/src/components/Chat/ChatMessage.js b/web/src/components/Chat/ChatMessage.js index ce9b7b6eca..57b54a8e27 100644 --- a/web/src/components/Chat/ChatMessage.js +++ b/web/src/components/Chat/ChatMessage.js @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import classnames from 'classnames'; import moment from 'moment'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import TruncateMarkup from 'react-truncate-markup'; -import { ICONS } from '../../config/constants'; -import { USER_TYPES } from '../../actions/appActions'; +import { ICONS } from 'config/constants'; +import { USER_TYPES } from 'actions/appActions'; import { ReactSVG } from 'react-svg'; moment.updateLocale('en', { diff --git a/web/src/components/CheckDeposit/index.js b/web/src/components/CheckDeposit/index.js index 58e17831ff..b640cf01a2 100644 --- a/web/src/components/CheckDeposit/index.js +++ b/web/src/components/CheckDeposit/index.js @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { reduxForm, getFormValues } from 'redux-form'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import { Button } from '../../components'; import { STATIC_ICONS } from 'config/icons'; import renderFields from '../../components/Form/factoryFields'; diff --git a/web/src/components/CheckTitle/HeaderSection.js b/web/src/components/CheckTitle/HeaderSection.js index aab6e2d1c8..d4bd5c787f 100644 --- a/web/src/components/CheckTitle/HeaderSection.js +++ b/web/src/components/CheckTitle/HeaderSection.js @@ -1,10 +1,9 @@ import React from 'react'; import Image from 'components/Image'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; -import { ActionNotification } from '../../components'; -import { EditWrapper } from 'components'; +import { ActionNotification, EditWrapper } from 'components'; const HeaderSection = ({ title, diff --git a/web/src/components/CheckTitle/index.js b/web/src/components/CheckTitle/index.js index 840081c52b..ca57d35856 100644 --- a/web/src/components/CheckTitle/index.js +++ b/web/src/components/CheckTitle/index.js @@ -4,7 +4,7 @@ import { ReactSVG } from 'react-svg'; import Image from 'components/Image'; import { EditWrapper } from 'components'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; import { STATIC_ICONS as ICONS } from 'config/icons'; export const renderStatusIcon = (statusCode = -1, className = '') => { diff --git a/web/src/components/Countdown/index.js b/web/src/components/Countdown/index.js index f322370247..e6a4c3e50c 100644 --- a/web/src/components/Countdown/index.js +++ b/web/src/components/Countdown/index.js @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import classnames from 'classnames'; import moment from 'moment'; -import { playBackgroundAudioNotification } from '../../utils/utils'; -import STRINGS from '../../config/localizedStrings'; +import { playBackgroundAudioNotification } from 'utils/utils'; +import STRINGS from 'config/localizedStrings'; import { Button, Loader } from '../'; diff --git a/web/src/components/CurrencyBall/index.js b/web/src/components/CurrencyBall/index.js index 36698ad52e..52c71b701a 100644 --- a/web/src/components/CurrencyBall/index.js +++ b/web/src/components/CurrencyBall/index.js @@ -1,6 +1,6 @@ import React from 'react'; import classnames from 'classnames'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; const getSizeClass = (size) => { switch (size) { diff --git a/web/src/components/CurrencyBall/withPrice.js b/web/src/components/CurrencyBall/withPrice.js index e8f78e9da5..33949a283d 100644 --- a/web/src/components/CurrencyBall/withPrice.js +++ b/web/src/components/CurrencyBall/withPrice.js @@ -1,8 +1,8 @@ import React from 'react'; import { connect } from 'react-redux'; -import { CurrencyBall } from '../'; -import { formatToCurrency } from '../../utils/currency'; -import { DEFAULT_COIN_DATA } from '../../config/constants'; +import { CurrencyBall } from 'components'; +import { formatToCurrency } from 'utils/currency'; +import { DEFAULT_COIN_DATA } from 'config/constants'; const CurrencyBallWithPrice = ({ symbol, diff --git a/web/src/components/Dialog/DesktopDialog.js b/web/src/components/Dialog/DesktopDialog.js index 4df97e636b..057d68b41c 100644 --- a/web/src/components/Dialog/DesktopDialog.js +++ b/web/src/components/Dialog/DesktopDialog.js @@ -3,10 +3,10 @@ import PropTypes from 'prop-types'; import classnames from 'classnames'; import Modal from 'react-modal'; import Ionicon from 'react-ionicons'; -import { Button, ActionNotification } from '../'; -import STRINGS from '../../config/localizedStrings'; -import { getClasesForLanguage, getLanguage } from '../../utils/string'; -import { getThemeClass } from '../../utils/theme'; +import { Button, ActionNotification } from 'components'; +import STRINGS from 'config/localizedStrings'; +import { getClasesForLanguage, getLanguage } from 'utils/string'; +import { getThemeClass } from 'utils/theme'; import withEdit from 'components/EditProvider/withEdit'; class Dialog extends PureComponent { diff --git a/web/src/components/Dialog/MessageDisplay.js b/web/src/components/Dialog/MessageDisplay.js index 93957b59c1..36a3677814 100644 --- a/web/src/components/Dialog/MessageDisplay.js +++ b/web/src/components/Dialog/MessageDisplay.js @@ -1,8 +1,6 @@ import React from 'react'; -import { Button } from '../'; -import STRINGS from '../../config/localizedStrings'; -import Image from 'components/Image'; -import { EditWrapper } from 'components'; +import { Image, Button, EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; const MessageDisplay = ({ stringId, diff --git a/web/src/components/Dialog/MobileDialog.js b/web/src/components/Dialog/MobileDialog.js index 3c93673b9f..1572a52104 100644 --- a/web/src/components/Dialog/MobileDialog.js +++ b/web/src/components/Dialog/MobileDialog.js @@ -2,11 +2,11 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import Modal from 'react-modal'; -import { isLoggedIn } from '../../utils/token'; -import { MobileBarBack } from '../'; -import { getClasesForLanguage, getLanguage } from '../../utils/string'; -import { getThemeClass } from '../../utils/theme'; -import { ICONS } from '../../config/constants'; +import { isLoggedIn } from 'utils/token'; +import { MobileBarBack } from 'components'; +import { getClasesForLanguage, getLanguage } from 'utils/string'; +import { getThemeClass } from 'utils/theme'; +import { ICONS } from 'config/constants'; import { ReactSVG } from 'react-svg'; const CompressedContent = ({ children, onClose }) => { diff --git a/web/src/components/ElapsedTimer/index.js b/web/src/components/ElapsedTimer/index.js index c89065d5b1..9ed512b684 100644 --- a/web/src/components/ElapsedTimer/index.js +++ b/web/src/components/ElapsedTimer/index.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import Image from 'components/Image'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; let timerInterval = ''; diff --git a/web/src/components/Form/FormFields/DumbField.js b/web/src/components/Form/FormFields/DumbField.js index a56a59cfbe..638c179590 100644 --- a/web/src/components/Form/FormFields/DumbField.js +++ b/web/src/components/Form/FormFields/DumbField.js @@ -2,9 +2,9 @@ import React from 'react'; import classnames from 'classnames'; import { CopyToClipboard } from 'react-copy-to-clipboard'; import FieldWrapper, { FieldContent } from './FieldWrapper'; -import { ActionNotification } from '../../'; -import { ICONS } from '../../../config/constants'; -import STRINGS from '../../../config/localizedStrings'; +import { ActionNotification } from 'components'; +import { ICONS } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; export const renderCopy = (text, onCopy, component) => { return ( diff --git a/web/src/components/Notification/ContactForm.js b/web/src/components/Notification/ContactForm.js index 81fc09318f..572ae93b90 100644 --- a/web/src/components/Notification/ContactForm.js +++ b/web/src/components/Notification/ContactForm.js @@ -1,9 +1,7 @@ import React from 'react'; -import { Button } from '../'; +import { Button, EditWrapper } from 'components'; import { NotificationWraper, NotificationContent } from './Notification'; -import { EditWrapper } from 'components'; - -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; const ContactFormNotification = ({ onClose, icons: ICONS }) => { return ( diff --git a/web/src/components/Notification/GenerateAddress.js b/web/src/components/Notification/GenerateAddress.js index baec91e4ec..01974d5ca6 100644 --- a/web/src/components/Notification/GenerateAddress.js +++ b/web/src/components/Notification/GenerateAddress.js @@ -1,10 +1,8 @@ import React from 'react'; import { DEFAULT_COIN_DATA } from 'config/constants'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import { NotificationWraper, NotificationContent } from './Notification'; -import { Button, Loader } from '../'; -import { EditWrapper } from 'components'; -import Image from 'components/Image'; +import { Button, Loader, EditWrapper, Image } from 'components'; const GenerateAddressNotification = ({ type, diff --git a/web/src/components/Notification/GenerateApiKey.js b/web/src/components/Notification/GenerateApiKey.js index 230e211d2f..2d5e7b5d94 100644 --- a/web/src/components/Notification/GenerateApiKey.js +++ b/web/src/components/Notification/GenerateApiKey.js @@ -1,9 +1,8 @@ import React from 'react'; -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; +import { Button, EditWrapper } from 'components'; import { NotificationWraper, NotificationContent } from './Notification'; -import { Button } from '../'; export const GenerateApiKey = (props) => { const { diff --git a/web/src/components/Notification/InviteFriends.js b/web/src/components/Notification/InviteFriends.js index daa162dc21..f328995221 100644 --- a/web/src/components/Notification/InviteFriends.js +++ b/web/src/components/Notification/InviteFriends.js @@ -2,13 +2,11 @@ import React, { Component } from 'react'; import { CopyToClipboard } from 'react-copy-to-clipboard'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; - -import IconTitle from '../IconTitle'; -import DumbField from '../Form/FormFields/DumbField'; -import Button from '../Button'; -import STRINGS from '../../config/localizedStrings'; -import { getUserReferralCount } from '../../actions/userAction'; -import { setSnackNotification } from '../../actions/appActions'; +import DumbField from 'components/Form/FormFields/DumbField'; +import { Button, IconTitle } from 'components'; +import STRINGS from 'config/localizedStrings'; +import { getUserReferralCount } from 'actions/userAction'; +import { setSnackNotification } from 'actions/appActions'; import { EditWrapper } from 'components'; const RenderDumbField = (props) => <DumbField {...props} />; diff --git a/web/src/components/Notification/Logout.js b/web/src/components/Notification/Logout.js index 5d98f01b67..f0cec3377b 100644 --- a/web/src/components/Notification/Logout.js +++ b/web/src/components/Notification/Logout.js @@ -1,8 +1,7 @@ import React from 'react'; -import { Button } from '../'; +import { Button } from 'components'; import { NotificationWraper, NotificationContent } from './Notification'; - -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; const ERROR_DENIED = 'Error Denied: '; const ERROR_INVALID_TOKEN = 'Invalid token'; diff --git a/web/src/components/Notification/Notification.js b/web/src/components/Notification/Notification.js index ed361a2338..61e4af4c63 100644 --- a/web/src/components/Notification/Notification.js +++ b/web/src/components/Notification/Notification.js @@ -1,10 +1,8 @@ import React from 'react'; import classnames from 'classnames'; import { isMobile } from 'react-device-detect'; -import { Button } from '../'; -import STRINGS from '../../config/localizedStrings'; -import Image from 'components/Image'; -import { EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; +import { EditWrapper, Button, Image } from 'components'; export const NotificationWraperDesktop = ({ icon, diff --git a/web/src/components/Notification/Order.js b/web/src/components/Notification/Order.js index 00301b107c..31e230b9b8 100644 --- a/web/src/components/Notification/Order.js +++ b/web/src/components/Notification/Order.js @@ -1,12 +1,9 @@ import React from 'react'; import math from 'mathjs'; import { connect } from 'react-redux'; -import { - CURRENCY_PRICE_FORMAT, - DEFAULT_COIN_DATA, -} from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; -import { formatBtcAmount, formatToCurrency } from '../../utils/currency'; +import { CURRENCY_PRICE_FORMAT, DEFAULT_COIN_DATA } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; +import { formatBtcAmount, formatToCurrency } from 'utils/currency'; import { NotificationWraper, NotificationContent, diff --git a/web/src/components/Notification/Trade.js b/web/src/components/Notification/Trade.js index ea42702a20..7926b2f53c 100644 --- a/web/src/components/Notification/Trade.js +++ b/web/src/components/Notification/Trade.js @@ -1,17 +1,14 @@ import React from 'react'; import math from 'mathjs'; import { connect } from 'react-redux'; -import { - CURRENCY_PRICE_FORMAT, - DEFAULT_COIN_DATA, -} from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; +import { CURRENCY_PRICE_FORMAT, DEFAULT_COIN_DATA } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; import { NotificationWraper, NotificationContent, InformationRow, } from './Notification'; -import { formatToCurrency } from '../../utils/currency'; +import { formatToCurrency } from 'utils/currency'; const SIDE_BUY = 'buy'; const SIDE_SELL = 'sell'; diff --git a/web/src/components/Notification/Verification.js b/web/src/components/Notification/Verification.js index 555cfaa982..b8084aa91a 100644 --- a/web/src/components/Notification/Verification.js +++ b/web/src/components/Notification/Verification.js @@ -1,9 +1,8 @@ import React from 'react'; -import { Button } from '../'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import { NotificationWraper, NotificationContent } from './Notification'; -import { EditWrapper } from 'components'; +import { EditWrapper, Button } from 'components'; const getTitleAndIcon = (type, ICONS) => { const data = { diff --git a/web/src/components/Notification/VerificationWarning.js b/web/src/components/Notification/VerificationWarning.js index 87c708a182..cdae4fb5ce 100644 --- a/web/src/components/Notification/VerificationWarning.js +++ b/web/src/components/Notification/VerificationWarning.js @@ -1,9 +1,7 @@ import React from 'react'; -import { CurrencyBallWithPrice, ActionNotification } from '../'; -import { ICONS } from '../../config/constants'; -import { NotificationWraper, NotificationContent } from './Notification'; - -import STRINGS from '../../config/localizedStrings'; +import { ICONS } from 'config/constants'; +import { NotificationWraper } from './Notification'; +import STRINGS from 'config/localizedStrings'; const VerificationWarningNotification = ({}) => { return ( diff --git a/web/src/components/Notification/Withdraw.js b/web/src/components/Notification/Withdraw.js index 7677063c1c..2293652642 100644 --- a/web/src/components/Notification/Withdraw.js +++ b/web/src/components/Notification/Withdraw.js @@ -1,10 +1,8 @@ import React from 'react'; import { NotificationWraper, NotificationContent } from './Notification'; -import { EXPLORERS_ENDPOINT } from '../../config/constants'; -import { Button } from '../'; - -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import { EXPLORERS_ENDPOINT } from 'config/constants'; +import { Button, EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; const WithdrawNotification = ({ data, onClose, icons: ICONS }) => { const notificationProps = { diff --git a/web/src/components/Notification/constants.js b/web/src/components/Notification/constants.js index 960740968e..69fdbc4030 100644 --- a/web/src/components/Notification/constants.js +++ b/web/src/components/Notification/constants.js @@ -1,6 +1,6 @@ -// import { BASE_CURRENCY } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; -import { DEFAULT_COIN_DATA } from '../../config/constants'; +// import { BASE_CURRENCY } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; +import { DEFAULT_COIN_DATA } from 'config/constants'; // const generateBaseDepositTexts = (strings) => ({ // TITLE: strings.formatString( diff --git a/web/src/components/OtpForm/index.js b/web/src/components/OtpForm/index.js index 529f6bb6e4..90f00ce742 100644 --- a/web/src/components/OtpForm/index.js +++ b/web/src/components/OtpForm/index.js @@ -1,11 +1,9 @@ import React, { Component } from 'react'; import { reduxForm } from 'redux-form'; -import { required, validateOtp } from '../../components/Form/validations'; -import renderFields from '../../components/Form/factoryFields'; -import { Button, IconTitle, ActionNotification } from '../'; - -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import { required, validateOtp } from 'components/Form/validations'; +import renderFields from 'components/Form/factoryFields'; +import { Button, IconTitle, ActionNotification, EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; class Form extends Component { diff --git a/web/src/components/QuickTrade/InputBlock.js b/web/src/components/QuickTrade/InputBlock.js index d269136475..e83a09e20a 100644 --- a/web/src/components/QuickTrade/InputBlock.js +++ b/web/src/components/QuickTrade/InputBlock.js @@ -2,12 +2,10 @@ import React, { Component } from 'react'; import classnames from 'classnames'; import math from 'mathjs'; import { isNumeric, isFloat } from 'validator'; - -import { CurrencyBall } from '../../components'; - -import { minValue, maxValue } from '../../components/Form/validations'; -import { FieldError } from '../../components/Form/FormFields/FieldWrapper'; -import { FLEX_CENTER_CLASSES, DEFAULT_COIN_DATA } from '../../config/constants'; +import { CurrencyBall } from 'components'; +import { minValue, maxValue } from 'components/Form/validations'; +import { FieldError } from 'components/Form/FormFields/FieldWrapper'; +import { FLEX_CENTER_CLASSES, DEFAULT_COIN_DATA } from 'config/constants'; import { translateError } from './utils'; const PLACEHOLDER = '0.00'; diff --git a/web/src/components/QuickTrade/ToogleButton.js b/web/src/components/QuickTrade/ToogleButton.js index dc6e6006bd..84b62c9364 100644 --- a/web/src/components/QuickTrade/ToogleButton.js +++ b/web/src/components/QuickTrade/ToogleButton.js @@ -1,7 +1,7 @@ import React from 'react'; import classnames from 'classnames'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; const ToogleButton = ({ onToogle, values, selected }) => ( <div className={classnames('toogle_button-wrapper', 'd-flex')}> diff --git a/web/src/components/QuickTrade/utils.js b/web/src/components/QuickTrade/utils.js index 2d9205c74e..f3650adc46 100644 --- a/web/src/components/QuickTrade/utils.js +++ b/web/src/components/QuickTrade/utils.js @@ -1,4 +1,4 @@ -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; const QUICK_TRADE_OUT_OF_LIMITS = 'Order size is out of the limits'; const QUICK_TRADE_TOKEN_USED = 'Token has been used'; diff --git a/web/src/components/SidebarHub/CurrencySelector.js b/web/src/components/SidebarHub/CurrencySelector.js index a1ffc6f0d9..b40a5591e0 100644 --- a/web/src/components/SidebarHub/CurrencySelector.js +++ b/web/src/components/SidebarHub/CurrencySelector.js @@ -1,7 +1,6 @@ import React from 'react'; import classnames from 'classnames'; import { connect } from 'react-redux'; -import STRINGS from '../../config/localizedStrings'; const CurrencySelector = ({ activeCurrency, changeCurrency, coins }) => { return ( diff --git a/web/src/components/SidebarHub/index.js b/web/src/components/SidebarHub/index.js index 4e1ee18f22..50cb4b380c 100644 --- a/web/src/components/SidebarHub/index.js +++ b/web/src/components/SidebarHub/index.js @@ -1,10 +1,8 @@ import React, { Component } from 'react'; import classnames from 'classnames'; import withConfig from 'components/ConfigProvider/withConfig'; -import STRINGS from '../../config/localizedStrings'; -import { IconTitle } from '../../components'; - -import { ButtonLink, Wallet } from '../'; +import STRINGS from 'config/localizedStrings'; +import { ButtonLink, Wallet, IconTitle } from 'components'; class SidebarHub extends Component { render() { diff --git a/web/src/components/Table/index.js b/web/src/components/Table/index.js index 44c7dfcaed..fb7c61edce 100644 --- a/web/src/components/Table/index.js +++ b/web/src/components/Table/index.js @@ -6,7 +6,7 @@ import TableBody from './TableBody'; // import TableFooter from './TableFooter'; import Paginator from './paginator'; import { EditWrapper } from 'components'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; class Table extends Component { state = { diff --git a/web/src/components/Table/paginator.js b/web/src/components/Table/paginator.js index 9a9fbe40f2..43d1fbb6ab 100644 --- a/web/src/components/Table/paginator.js +++ b/web/src/components/Table/paginator.js @@ -1,8 +1,8 @@ import React from 'react'; import classnames from 'classnames'; -import { ActionNotification } from '../'; +import { ActionNotification } from 'components'; import withConfig from 'components/ConfigProvider/withConfig'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; const renderPageCount = (currentPage, totalPages) => { if (totalPages > 0) { diff --git a/web/src/components/Wallet/Section.js b/web/src/components/Wallet/Section.js index 67a38777af..6bf9f774c4 100644 --- a/web/src/components/Wallet/Section.js +++ b/web/src/components/Wallet/Section.js @@ -1,12 +1,12 @@ import React from 'react'; import math from 'mathjs'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import { BASE_CURRENCY, CURRENCY_PRICE_FORMAT, DEFAULT_COIN_DATA, -} from '../../config/constants'; -import { formatToCurrency } from '../../utils/currency'; +} from 'config/constants'; +import { formatToCurrency } from 'utils/currency'; const TextHolders = ({ ordersOfSymbol, currencySymbol, hold, name }) => { const ordersText = diff --git a/web/src/components/Wallet/index.js b/web/src/components/Wallet/index.js index 8cddf8815a..420cc5ba6c 100644 --- a/web/src/components/Wallet/index.js +++ b/web/src/components/Wallet/index.js @@ -3,12 +3,11 @@ import { connect } from 'react-redux'; import { Link } from 'react-router'; import classnames from 'classnames'; -import { Accordion, ControlledScrollbar } from 'components'; -import { BASE_CURRENCY, DEFAULT_COIN_DATA } from '../../config/constants'; -import { formatToCurrency } from '../../utils/currency'; +import { Accordion, ControlledScrollbar, DonutChart } from 'components'; +import { BASE_CURRENCY, DEFAULT_COIN_DATA } from 'config/constants'; +import { formatToCurrency } from 'utils/currency'; import WalletSection from './Section'; -import { DonutChart } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; class Wallet extends Component { state = { diff --git a/web/src/components/WarningVerification/index.js b/web/src/components/WarningVerification/index.js index be5367bb99..c4527081f6 100644 --- a/web/src/components/WarningVerification/index.js +++ b/web/src/components/WarningVerification/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; const generateTexts = (level = 0) => { if (level === 1) { diff --git a/web/src/containers/Admin/paths.js b/web/src/containers/Admin/paths.js index 8a344d0532..6ea53965ed 100644 --- a/web/src/containers/Admin/paths.js +++ b/web/src/containers/Admin/paths.js @@ -1,4 +1,4 @@ -import { APP_TITLE } from '../../config/constants'; +import { APP_TITLE } from 'config/constants'; export const PATHS = [ { diff --git a/web/src/containers/AuthContainer/index.js b/web/src/containers/AuthContainer/index.js index b24744ad8e..44960c8eb9 100644 --- a/web/src/containers/AuthContainer/index.js +++ b/web/src/containers/AuthContainer/index.js @@ -7,9 +7,9 @@ import withConfig from 'components/ConfigProvider/withConfig'; import { AppFooter, Dialog } from 'components'; import { HelpfulResourcesForm } from 'containers'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; -import { getClasesForLanguage } from '../../utils/string'; -import { getThemeClass } from '../../utils/theme'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; +import { getClasesForLanguage } from 'utils/string'; +import { getThemeClass } from 'utils/theme'; import { getExchangeInfo, closeNotification, diff --git a/web/src/containers/Chat/index.js b/web/src/containers/Chat/index.js index 0272d457fa..e4f723151f 100644 --- a/web/src/containers/Chat/index.js +++ b/web/src/containers/Chat/index.js @@ -4,13 +4,10 @@ import { bindActionCreators } from 'redux'; import { isMobile } from 'react-device-detect'; import { setWsHeartbeat } from 'ws-heartbeat/client'; -import { ChatWrapper } from '../../components'; -import { WS_URL } from '../../config/constants'; -import { - setAnnouncements, - setChatUnreadMessages, -} from '../../actions/appActions'; -import { getToken } from '../../utils/token'; +import { ChatWrapper } from 'components'; +import { WS_URL } from 'config/constants'; +import { setAnnouncements, setChatUnreadMessages } from 'actions/appActions'; +import { getToken } from 'utils/token'; import { NORMAL_CLOSURE_CODE, isIntentionalClosure } from 'utils/webSocket'; const ENTER_KEY = 'Enter'; diff --git a/web/src/containers/ConfirmChangePassword/index.js b/web/src/containers/ConfirmChangePassword/index.js index bd9f184efe..26da083995 100644 --- a/web/src/containers/ConfirmChangePassword/index.js +++ b/web/src/containers/ConfirmChangePassword/index.js @@ -1,9 +1,9 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { Dialog } from '../../components'; +import { Dialog } from 'components'; import ResetPasswordSuccess from 'containers/ResetPassword/ResetPasswordSuccess'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; class ConfirmChangePassword extends Component { state = { diff --git a/web/src/containers/ContactForm/index.js b/web/src/containers/ContactForm/index.js index da0fcf8d83..3371a892bc 100644 --- a/web/src/containers/ContactForm/index.js +++ b/web/src/containers/ContactForm/index.js @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import { SubmissionError } from 'redux-form'; import { connect } from 'react-redux'; -import { HocForm, IconTitle, Notification } from '../../components'; -import { email as isEmail, required } from '../../components/Form/validations'; -import STRINGS from '../../config/localizedStrings'; -import { sendSupportMail, NOTIFICATIONS } from '../../actions/appActions'; +import { HocForm, IconTitle, Notification } from 'components'; +import { email as isEmail, required } from 'components/Form/validations'; +import STRINGS from 'config/localizedStrings'; +import { sendSupportMail, NOTIFICATIONS } from 'actions/appActions'; import withConfig from 'components/ConfigProvider/withConfig'; const FORM_NAME = 'ContactForm'; diff --git a/web/src/containers/Deposit/index.js b/web/src/containers/Deposit/index.js index 885bf21921..dab827277c 100644 --- a/web/src/containers/Deposit/index.js +++ b/web/src/containers/Deposit/index.js @@ -4,17 +4,14 @@ import { bindActionCreators } from 'redux'; import { formValueSelector } from 'redux-form'; import { connect } from 'react-redux'; import { isMobile } from 'react-device-detect'; -import { BALANCE_ERROR } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; -import { getCurrencyFromName } from '../../utils/currency'; +import { BALANCE_ERROR } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; +import { getCurrencyFromName } from 'utils/currency'; import { createAddress, cleanCreateAddress } from 'actions/userAction'; import { NOTIFICATIONS } from 'actions/appActions'; import { DEFAULT_COIN_DATA } from 'config/constants'; -import { - openContactForm, - setSnackNotification, -} from '../../actions/appActions'; +import { openContactForm, setSnackNotification } from 'actions/appActions'; import { MobileBarBack, Dialog, Notification } from 'components'; import { diff --git a/web/src/containers/ExpiredExchange/index.js b/web/src/containers/ExpiredExchange/index.js index 6cb219ac48..13b0ff5a58 100644 --- a/web/src/containers/ExpiredExchange/index.js +++ b/web/src/containers/ExpiredExchange/index.js @@ -4,13 +4,12 @@ import { ReactSVG } from 'react-svg'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import moment from 'moment'; -import { getThemeClass } from '../../utils/theme'; - -import { FLEX_CENTER_CLASSES, EXCHANGE_URL } from '../../config/constants'; -import { getExchangeInfo } from '../../actions/appActions'; -import { logout } from '../../actions/authAction'; -import STRINGS from '../../config/localizedStrings'; -import { Button } from '../../components'; +import { getThemeClass } from 'utils/theme'; +import { FLEX_CENTER_CLASSES, EXCHANGE_URL } from 'config/constants'; +import { getExchangeInfo } from 'actions/appActions'; +import { logout } from 'actions/authAction'; +import STRINGS from 'config/localizedStrings'; +import { Button } from 'components'; import withConfig from 'components/ConfigProvider/withConfig'; diff --git a/web/src/containers/HelpfulResourcesForm/index.js b/web/src/containers/HelpfulResourcesForm/index.js index 7bf81f0c75..57465f0c57 100644 --- a/web/src/containers/HelpfulResourcesForm/index.js +++ b/web/src/containers/HelpfulResourcesForm/index.js @@ -4,8 +4,8 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import classnames from 'classnames'; import Image from 'components/Image'; -import { IconTitle, Notification, Button, BlueLink } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, Notification, Button, BlueLink } from 'components'; +import STRINGS from 'config/localizedStrings'; import { sendSupportMail, NOTIFICATIONS, diff --git a/web/src/containers/Login/LoginForm.js b/web/src/containers/Login/LoginForm.js index 763e5f33a8..da9d91c5fe 100644 --- a/web/src/containers/Login/LoginForm.js +++ b/web/src/containers/Login/LoginForm.js @@ -5,11 +5,10 @@ import { password, email, normalizeEmail, -} from '../../components/Form/validations'; -import { AuthForm } from '../../components'; -import { getLanguage } from '../../utils/string'; - -import STRINGS from '../../config/localizedStrings'; +} from 'components/Form/validations'; +import { AuthForm } from 'components'; +import { getLanguage } from 'utils/string'; +import STRINGS from 'config/localizedStrings'; export const FORM_NAME = 'LoginForm'; diff --git a/web/src/containers/Login/index.js b/web/src/containers/Login/index.js index 9ad68884be..9c21eba5ad 100644 --- a/web/src/containers/Login/index.js +++ b/web/src/containers/Login/index.js @@ -10,14 +10,14 @@ import { performLogin, storeLoginResult, setLogoutMessage, -} from '../../actions/authAction'; +} from 'actions/authAction'; import LoginForm, { FORM_NAME } from './LoginForm'; -import { Dialog, OtpForm, IconTitle, Notification } from '../../components'; -import { NOTIFICATIONS } from '../../actions/appActions'; -import { errorHandler } from '../../components/OtpForm/utils'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { Dialog, OtpForm, IconTitle, Notification } from 'components'; +import { NOTIFICATIONS } from 'actions/appActions'; +import { errorHandler } from 'components/OtpForm/utils'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; let errorTimeOut = null; diff --git a/web/src/containers/MobileHome/index.js b/web/src/containers/MobileHome/index.js index db271802b5..de2838025a 100644 --- a/web/src/containers/MobileHome/index.js +++ b/web/src/containers/MobileHome/index.js @@ -3,10 +3,10 @@ import classnames from 'classnames'; import { connect } from 'react-redux'; import { isBrowser, isMobile } from 'react-device-detect'; -import STRINGS from '../../config/localizedStrings'; -import { AppFooter, NotificationsList } from '../../components'; -import { getClasesForLanguage } from '../../utils/string'; -import { getThemeClass } from '../../utils/theme'; +import STRINGS from 'config/localizedStrings'; +import { AppFooter, NotificationsList } from 'components'; +import { getClasesForLanguage } from 'utils/string'; +import { getThemeClass } from 'utils/theme'; class Home extends Component { render() { diff --git a/web/src/containers/RequestResetPassword/RequestResetPasswordSuccess.js b/web/src/containers/RequestResetPassword/RequestResetPasswordSuccess.js index 666cf5a9c2..a9cdcd0e14 100644 --- a/web/src/containers/RequestResetPassword/RequestResetPasswordSuccess.js +++ b/web/src/containers/RequestResetPassword/RequestResetPasswordSuccess.js @@ -1,8 +1,8 @@ import React from 'react'; -import { ICONS } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; +import { ICONS } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; -import { IconTitle, Button } from '../../components'; +import { IconTitle, Button } from 'components'; const RequestResetPasswordSuccess = ({ onLoginClick, onContactUs }) => ( <div className="auth_wrapper d-flex justify-content-center align-items-center flex-column"> diff --git a/web/src/containers/RequestResetPassword/ResetPasswordForm.js b/web/src/containers/RequestResetPassword/ResetPasswordForm.js index 50168494af..a53f158141 100644 --- a/web/src/containers/RequestResetPassword/ResetPasswordForm.js +++ b/web/src/containers/RequestResetPassword/ResetPasswordForm.js @@ -5,10 +5,10 @@ import { email, normalizeEmail, required, -} from '../../components/Form/validations'; -import { AuthForm } from '../../components'; -import STRINGS from '../../config/localizedStrings'; -import { getLanguage } from '../../utils/string'; +} from 'components/Form/validations'; +import { AuthForm } from 'components'; +import STRINGS from 'config/localizedStrings'; +import { getLanguage } from 'utils/string'; export const generateFormFields = (theme) => ({ email: { diff --git a/web/src/containers/RequestResetPassword/index.js b/web/src/containers/RequestResetPassword/index.js index d46cfaada5..9bb9af036c 100644 --- a/web/src/containers/RequestResetPassword/index.js +++ b/web/src/containers/RequestResetPassword/index.js @@ -5,12 +5,12 @@ import { bindActionCreators } from 'redux'; import { isMobile } from 'react-device-detect'; import { SubmissionError, change } from 'redux-form'; -import { requestResetPassword } from '../../actions/authAction'; +import { requestResetPassword } from 'actions/authAction'; import ResetPasswordForm, { generateFormFields } from './ResetPasswordForm'; -import { IconTitle, Dialog, MobileBarBack } from '../../components'; -import { ContactForm } from '../'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, Dialog, MobileBarBack } from 'components'; +import { ContactForm } from 'containers'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; import RequestResetPasswordSuccess from './RequestResetPasswordSuccess'; import withConfig from 'components/ConfigProvider/withConfig'; import { openContactForm } from 'actions/appActions'; diff --git a/web/src/containers/ResetPassword/ResetPasswordForm.js b/web/src/containers/ResetPassword/ResetPasswordForm.js index 1960678ddf..3a18063f1b 100644 --- a/web/src/containers/ResetPassword/ResetPasswordForm.js +++ b/web/src/containers/ResetPassword/ResetPasswordForm.js @@ -1,8 +1,8 @@ import React from 'react'; import { reduxForm } from 'redux-form'; -import { required, password } from '../../components/Form/validations'; -import { AuthForm } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import { required, password } from 'components/Form/validations'; +import { AuthForm } from 'components'; +import STRINGS from 'config/localizedStrings'; export const generateFormFields = () => ({ password: { diff --git a/web/src/containers/ResetPassword/ResetPasswordSuccess.js b/web/src/containers/ResetPassword/ResetPasswordSuccess.js index 33986fbf93..19df63f5e5 100644 --- a/web/src/containers/ResetPassword/ResetPasswordSuccess.js +++ b/web/src/containers/ResetPassword/ResetPasswordSuccess.js @@ -1,6 +1,6 @@ import React from 'react'; -import { IconTitle, Button } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, Button } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; const ResetPasswordSuccess = ({ diff --git a/web/src/containers/ResetPassword/index.js b/web/src/containers/ResetPassword/index.js index 0a209bb7c2..5af80efaca 100644 --- a/web/src/containers/ResetPassword/index.js +++ b/web/src/containers/ResetPassword/index.js @@ -3,13 +3,12 @@ import classnames from 'classnames'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { SubmissionError } from 'redux-form'; -import { resetPassword } from '../../actions/authAction'; +import { resetPassword } from 'actions/authAction'; import ResetPasswordForm from './ResetPasswordForm'; import ResetPasswordSuccess from './ResetPasswordSuccess'; -import { IconTitle, Dialog } from '../../components'; -import { ContactForm } from '../'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, Dialog, ContactForm } from 'components'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { openContactForm } from 'actions/appActions'; diff --git a/web/src/containers/Signup/SignupForm.js b/web/src/containers/Signup/SignupForm.js index b012aabebe..19545627f1 100644 --- a/web/src/containers/Signup/SignupForm.js +++ b/web/src/containers/Signup/SignupForm.js @@ -6,9 +6,9 @@ import { email, requiredWithCustomMessage, normalizeEmail, -} from '../../components/Form/validations'; -import { AuthForm, BlueLink } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +} from 'components/Form/validations'; +import { AuthForm, BlueLink } from 'components'; +import STRINGS from 'config/localizedStrings'; export const FORM_NAME = 'SignForm'; diff --git a/web/src/containers/Signup/SignupSuccess.js b/web/src/containers/Signup/SignupSuccess.js index 1e465f0337..44a30d4de0 100644 --- a/web/src/containers/Signup/SignupSuccess.js +++ b/web/src/containers/Signup/SignupSuccess.js @@ -1,7 +1,7 @@ import React from 'react'; import { Link } from 'react-router'; -import { IconTitle } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; const SignupSuccess = ({ icons: ICONS, ...rest }) => { diff --git a/web/src/containers/Signup/index.js b/web/src/containers/Signup/index.js index fc23ff6b3a..4c02f863ac 100644 --- a/web/src/containers/Signup/index.js +++ b/web/src/containers/Signup/index.js @@ -5,13 +5,13 @@ import { connect } from 'react-redux'; import { isMobile } from 'react-device-detect'; import { SubmissionError, change } from 'redux-form'; import { bindActionCreators } from 'redux'; -import { performSignup } from '../../actions/authAction'; +import { performSignup } from 'actions/authAction'; import SignupForm, { generateFormFields, FORM_NAME } from './SignupForm'; import SignupSuccess from './SignupSuccess'; -import { ContactForm } from '../'; -import { IconTitle, Dialog, MobileBarBack } from '../../components'; +import { ContactForm } from 'containers'; +import { IconTitle, Dialog, MobileBarBack } from 'components'; import { FLEX_CENTER_CLASSES } from 'config/constants'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { openContactForm } from 'actions/appActions'; diff --git a/web/src/containers/Summary/MobileAccountTypeList.js b/web/src/containers/Summary/MobileAccountTypeList.js index f364c8a99b..330a03ed58 100644 --- a/web/src/containers/Summary/MobileAccountTypeList.js +++ b/web/src/containers/Summary/MobileAccountTypeList.js @@ -1,10 +1,9 @@ import React from 'react'; import classnames from 'classnames'; -import Image from 'components/Image'; - -import STRINGS from '../../config/localizedStrings'; +import { Image } from 'components'; +import STRINGS from 'config/localizedStrings'; import AccountTypeDetails from './components/AccountTypeDetails'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; import withConfig from 'components/ConfigProvider/withConfig'; const MobileAccountTypeList = ({ diff --git a/web/src/containers/Summary/MobileSummary.js b/web/src/containers/Summary/MobileSummary.js index d36f8a87d1..1926f7994b 100644 --- a/web/src/containers/Summary/MobileSummary.js +++ b/web/src/containers/Summary/MobileSummary.js @@ -12,9 +12,9 @@ import { BASE_CURRENCY, DEFAULT_COIN_DATA, SHOW_TOTAL_ASSETS, -} from '../../config/constants'; -// import { formatAverage, formatBaseAmount } from '../../utils/currency'; -import STRINGS from '../../config/localizedStrings'; +} from 'config/constants'; +// import { formatAverage, formatBaseAmount } from 'utils/currency'; +import STRINGS from 'config/localizedStrings'; const MobileSummary = ({ user, diff --git a/web/src/containers/Summary/index.js b/web/src/containers/Summary/index.js index 93c8540615..4155701a5c 100644 --- a/web/src/containers/Summary/index.js +++ b/web/src/containers/Summary/index.js @@ -12,7 +12,7 @@ import AccountDetails from './components/AccountDetails'; import Markets from './components/Markets'; import MobileSummary from './MobileSummary'; -import { IconTitle } from '../../components'; +import { IconTitle } from 'components'; // import { logout } from '../../actions/authAction'; import { openFeesStructureandLimits, @@ -20,17 +20,17 @@ import { logoutconfirm, setNotification, NOTIFICATIONS, -} from '../../actions/appActions'; +} from 'actions/appActions'; import { BASE_CURRENCY, DEFAULT_COIN_DATA, // SHOW_SUMMARY_ACCOUNT_DETAILS, SHOW_TOTAL_ASSETS, -} from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; +} from 'config/constants'; +import STRINGS from 'config/localizedStrings'; import { formatAverage, formatBaseAmount } from 'utils/currency'; import { getLastMonthVolume } from './components/utils'; -import { getUserReferralCount } from '../../actions/userAction'; +import { getUserReferralCount } from 'actions/userAction'; import withConfig from 'components/ConfigProvider/withConfig'; import { openContactForm } from 'actions/appActions'; diff --git a/web/src/containers/TermsOfService/DepositFunds.js b/web/src/containers/TermsOfService/DepositFunds.js index 371d5c48b9..ef406a833b 100644 --- a/web/src/containers/TermsOfService/DepositFunds.js +++ b/web/src/containers/TermsOfService/DepositFunds.js @@ -1,8 +1,8 @@ import React from 'react'; import { ReactSVG } from 'react-svg'; -import { IconTitle, BlueLink, Button } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, BlueLink, Button } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; const DepositFunds = (props) => { diff --git a/web/src/containers/TermsOfService/Form.js b/web/src/containers/TermsOfService/Form.js index f0a3713ef4..fe986555d1 100644 --- a/web/src/containers/TermsOfService/Form.js +++ b/web/src/containers/TermsOfService/Form.js @@ -2,11 +2,11 @@ import React from 'react'; import { reduxForm } from 'redux-form'; import { ReactSVG } from 'react-svg'; -import { Button } from '../../components'; -import renderFields from '../../components/Form/factoryFields'; -import STRINGS from '../../config/localizedStrings'; -import { ICONS } from '../../config/constants'; -import { getErrorLocalized } from '../../utils/errors'; +import { Button } from 'components'; +import renderFields from 'components/Form/factoryFields'; +import STRINGS from 'config/localizedStrings'; +import { ICONS } from 'config/constants'; +import { getErrorLocalized } from 'utils/errors'; export const FORM_NAME = 'termsAndConditionForm'; diff --git a/web/src/containers/TermsOfService/index.js b/web/src/containers/TermsOfService/index.js index 0b3d914114..551d8d632c 100644 --- a/web/src/containers/TermsOfService/index.js +++ b/web/src/containers/TermsOfService/index.js @@ -1,8 +1,8 @@ import React from 'react'; import TermsForm from './Form'; -import { requiredWithCustomMessage } from '../../components/Form/validations'; -import STRINGS from '../../config/localizedStrings'; +import { requiredWithCustomMessage } from 'components/Form/validations'; +import STRINGS from 'config/localizedStrings'; const formFields = { agreeTerms: { diff --git a/web/src/containers/Trade/MobileChart.js b/web/src/containers/Trade/MobileChart.js index 9db9f8dfdc..b281132b57 100644 --- a/web/src/containers/Trade/MobileChart.js +++ b/web/src/containers/Trade/MobileChart.js @@ -3,7 +3,7 @@ import classnames from 'classnames'; // import { Link } from 'react-router'; import { connect } from 'react-redux'; import TradeBlock from './components/TradeBlock'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import TradeHistory from './components/TradeHistory'; import TVChartContainer from './ChartContainer'; diff --git a/web/src/containers/Trade/MobileOrders.js b/web/src/containers/Trade/MobileOrders.js index 7beb1c2de8..a63a0221f9 100644 --- a/web/src/containers/Trade/MobileOrders.js +++ b/web/src/containers/Trade/MobileOrders.js @@ -3,8 +3,8 @@ import classnames from 'classnames'; import TradeBlock from './components/TradeBlock'; import ActiveOrders from './components/ActiveOrders'; import UserTrades from './components/UserTrades'; -import { ActionNotification } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import { ActionNotification } from 'components'; +import STRINGS from 'config/localizedStrings'; import LogoutInfoOrder from './components/LogoutInfoOrder'; import withConfig from 'components/ConfigProvider/withConfig'; diff --git a/web/src/containers/Trade/MobileTrade.js b/web/src/containers/Trade/MobileTrade.js index 7e0da73df3..86b6cf861b 100644 --- a/web/src/containers/Trade/MobileTrade.js +++ b/web/src/containers/Trade/MobileTrade.js @@ -4,7 +4,7 @@ import classnames from 'classnames'; import TradeBlock from './components/TradeBlock'; import Orderbook from './components/Orderbook'; import OrderEntry from './components/OrderEntry'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; const MobileTrade = ({ orderbookReady, @@ -50,7 +50,10 @@ const MobileTrade = ({ setSizeRef={setSizeRef} /> </TradeBlock> - <TradeBlock title={STRINGS['ORDERBOOK']} className="order-entry w-50 orderBook-wrapper"> + <TradeBlock + title={STRINGS['ORDERBOOK']} + className="order-entry w-50 orderBook-wrapper" + > {orderbookReady && <Orderbook {...orderbookProps} />} </TradeBlock> </div> diff --git a/web/src/containers/TransactionsHistory/HistoryDisplay.js b/web/src/containers/TransactionsHistory/HistoryDisplay.js index f49dc1cd31..ea4ce076b8 100644 --- a/web/src/containers/TransactionsHistory/HistoryDisplay.js +++ b/web/src/containers/TransactionsHistory/HistoryDisplay.js @@ -7,16 +7,15 @@ import { // CsvDownload, Loader, Dialog, -} from '../../components'; + EditWrapper, +} from 'components'; import classnames from 'classnames'; import { SubmissionError } from 'redux-form'; - -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { STATIC_ICONS } from 'config/icons'; import { searchTransaction } from 'actions/walletActions'; -import CheckDeposit from '../../components/CheckDeposit'; +import CheckDeposit from 'components/CheckDeposit'; const HistoryDisplay = (props) => { const { @@ -76,7 +75,7 @@ const HistoryDisplay = (props) => { <div className="title text-capitalize"> <EditWrapper stringId={stringId}>{title}</EditWrapper> {count > 0 && ( - <div className='download-icon'> + <div className="download-icon"> <ActionNotification stringId="TRANSACTION_HISTORY.TEXT_DOWNLOAD" text={STRINGS['TRANSACTION_HISTORY.TEXT_DOWNLOAD']} @@ -126,8 +125,8 @@ const HistoryDisplay = (props) => { shouldCloseOnOverlayClick={false} style={{ 'z-index': 100 }} > - {dialogIsOpen - ? <CheckDeposit + {dialogIsOpen ? ( + <CheckDeposit onCloseDialog={onCloseDialog} onSubmit={requestDeposit} message={statusMessage} @@ -135,8 +134,7 @@ const HistoryDisplay = (props) => { initialValues={initialValue} props={props} /> - : null - } + ) : null} </Dialog> </div> ); diff --git a/web/src/containers/TransactionsHistory/utils.js b/web/src/containers/TransactionsHistory/utils.js index 0c2f0c474c..27913392aa 100644 --- a/web/src/containers/TransactionsHistory/utils.js +++ b/web/src/containers/TransactionsHistory/utils.js @@ -5,16 +5,15 @@ import classnames from 'classnames'; import mathjs from 'mathjs'; import { isMobile } from 'react-device-detect'; -import STRINGS from '../../config/localizedStrings'; - -import { Image } from '../../components'; +import STRINGS from 'config/localizedStrings'; +import { Image } from 'components'; import { EXPLORERS_ENDPOINT, BASE_CURRENCY, CURRENCY_PRICE_FORMAT, DEFAULT_COIN_DATA, -} from '../../config/constants'; -import { getFormatTimestamp } from '../../utils/utils'; +} from 'config/constants'; +import { getFormatTimestamp } from 'utils/utils'; import { formatToCurrency, formatBaseAmount } from 'utils/currency'; notification.config({ diff --git a/web/src/containers/UserProfile/FeesBlock.js b/web/src/containers/UserProfile/FeesBlock.js index 18366e360e..0e075523c4 100644 --- a/web/src/containers/UserProfile/FeesBlock.js +++ b/web/src/containers/UserProfile/FeesBlock.js @@ -1,7 +1,7 @@ import React from 'react'; import classnames from 'classnames'; -import STRINGS from '../../config/localizedStrings'; -import { formatPercentage } from '../../utils/currency'; +import STRINGS from 'config/localizedStrings'; +import { formatPercentage } from 'utils/currency'; const FeeRow = ({ data = {}, headers = [], isUserLevel = false, row }) => { const { verification_level, ...rest } = data; diff --git a/web/src/containers/UserProfile/IdentificationFormValues.js b/web/src/containers/UserProfile/IdentificationFormValues.js index 022cdc1773..e2c0b532d6 100644 --- a/web/src/containers/UserProfile/IdentificationFormValues.js +++ b/web/src/containers/UserProfile/IdentificationFormValues.js @@ -1,8 +1,5 @@ -import { - COUNTRIES_OPTIONS, - NATIONAL_COUNTRY_VALUE, -} from '../../utils/countries'; -import STRINGS from '../../config/localizedStrings'; +import { COUNTRIES_OPTIONS, NATIONAL_COUNTRY_VALUE } from 'utils/countries'; +import STRINGS from 'config/localizedStrings'; import { isMobile } from 'react-device-detect'; export const generateFormValues = ( diff --git a/web/src/containers/UserProfile/InformationSection.js b/web/src/containers/UserProfile/InformationSection.js index 7aad3d93e5..e3fd6bc7fc 100644 --- a/web/src/containers/UserProfile/InformationSection.js +++ b/web/src/containers/UserProfile/InformationSection.js @@ -1,6 +1,6 @@ import React from 'react'; import Ionicon from 'react-ionicons'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; export const InformationSection = ({ text, onChangeValue, onChangeText }) => ( <div className="information_section d-flex flex-column"> diff --git a/web/src/containers/UserProfile/LevelsBlock.js b/web/src/containers/UserProfile/LevelsBlock.js index 7e44a27f2e..a564697a22 100644 --- a/web/src/containers/UserProfile/LevelsBlock.js +++ b/web/src/containers/UserProfile/LevelsBlock.js @@ -1,7 +1,7 @@ import React from 'react'; import classnames from 'classnames'; -import STRINGS from '../../config/localizedStrings'; -import { formatBaseAmount, formatBtcAmount } from '../../utils/currency'; +import STRINGS from 'config/localizedStrings'; +import { formatBaseAmount, formatBtcAmount } from 'utils/currency'; const getLimitValue = (limit = -1, format) => { if (limit === 0) { diff --git a/web/src/containers/UserProfile/MobileFormValues.js b/web/src/containers/UserProfile/MobileFormValues.js index f3bc7aa54e..cafee1fa07 100644 --- a/web/src/containers/UserProfile/MobileFormValues.js +++ b/web/src/containers/UserProfile/MobileFormValues.js @@ -1,5 +1,5 @@ -import { PHONE_OPTIONS } from '../../utils/countries'; -import STRINGS from '../../config/localizedStrings'; +import { PHONE_OPTIONS } from 'utils/countries'; +import STRINGS from 'config/localizedStrings'; import { isMobile } from 'react-device-detect'; export const generateFormValues = () => ({ diff --git a/web/src/containers/UserProfile/UpgradeWarning.js b/web/src/containers/UserProfile/UpgradeWarning.js index 0b2d38c639..a747686d15 100644 --- a/web/src/containers/UserProfile/UpgradeWarning.js +++ b/web/src/containers/UserProfile/UpgradeWarning.js @@ -1,5 +1,5 @@ import React from 'react'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; const UpgradeWarning = () => ( <div className="warning_text"> diff --git a/web/src/containers/UserProfile/index.js b/web/src/containers/UserProfile/index.js index bddffda618..da659f055e 100644 --- a/web/src/containers/UserProfile/index.js +++ b/web/src/containers/UserProfile/index.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { Accordion, Loader, Button } from '../../components'; +import { Accordion, Loader, Button } from 'components'; import Form from './Form'; import { generateFormValues as generateMobileFormValues } from './MobileFormValues'; import { @@ -10,8 +10,8 @@ import { } from './IdentificationFormValues'; import { InformationSection } from './InformationSection'; import { LevelSection } from './LevelSection'; -import { logout } from '../../actions/authAction'; -import STRINGS from '../../config/localizedStrings'; +import { logout } from 'actions/authAction'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { isMobile } from 'react-device-detect'; import { openContactForm } from 'actions/appActions'; diff --git a/web/src/containers/UserSecurity/ApiKeyForm.js b/web/src/containers/UserSecurity/ApiKeyForm.js index a27ee06813..0059d4cdb2 100644 --- a/web/src/containers/UserSecurity/ApiKeyForm.js +++ b/web/src/containers/UserSecurity/ApiKeyForm.js @@ -1,8 +1,8 @@ import React from 'react'; -import STRINGS from '../../config/localizedStrings'; -import { getErrorLocalized } from '../../utils/errors'; -import renderFields from '../../components/Form/factoryFields'; -import { tokenKeyValidation } from '../../components/Form/validations'; +import STRINGS from 'config/localizedStrings'; +import { getErrorLocalized } from 'utils/errors'; +import renderFields from 'components/Form/factoryFields'; +import { tokenKeyValidation } from 'components/Form/validations'; import { reduxForm } from 'redux-form'; import { TYPE_GENERATE } from './ApiKeyModal'; diff --git a/web/src/containers/UserSecurity/ApiKeyHeaders.js b/web/src/containers/UserSecurity/ApiKeyHeaders.js index c38022ecf0..be93a06411 100644 --- a/web/src/containers/UserSecurity/ApiKeyHeaders.js +++ b/web/src/containers/UserSecurity/ApiKeyHeaders.js @@ -1,9 +1,9 @@ import React from 'react'; import Image from 'components/Image'; import classnames from 'classnames'; -import STRINGS from '../../config/localizedStrings'; -import { getFormatTimestamp } from '../../utils/utils'; -import { Tooltip } from '../../components'; +import STRINGS from 'config/localizedStrings'; +import { getFormatTimestamp } from 'utils/utils'; +import { Tooltip } from 'components'; import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons'; const NOT_REVOKED = 'pointer blue-link'; diff --git a/web/src/containers/UserSecurity/ChangePasswordForm.js b/web/src/containers/UserSecurity/ChangePasswordForm.js index 520b8f5046..139a25657d 100644 --- a/web/src/containers/UserSecurity/ChangePasswordForm.js +++ b/web/src/containers/UserSecurity/ChangePasswordForm.js @@ -2,12 +2,10 @@ import React from 'react'; import { reduxForm } from 'redux-form'; import { isMobile } from 'react-device-detect'; -import renderFields from '../../components/Form/factoryFields'; -import { Button } from '../../components'; -import { required, password } from '../../components/Form/validations'; - -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import renderFields from 'components/Form/factoryFields'; +import { Button, EditWrapper } from 'components'; +import { required, password } from 'components/Form/validations'; +import STRINGS from 'config/localizedStrings'; const validate = (values) => { const errors = {}; diff --git a/web/src/containers/UserSecurity/DeveloperSection.js b/web/src/containers/UserSecurity/DeveloperSection.js index 813e2e33ab..f0ac5179cb 100644 --- a/web/src/containers/UserSecurity/DeveloperSection.js +++ b/web/src/containers/UserSecurity/DeveloperSection.js @@ -1,9 +1,9 @@ import React from 'react'; -import { MIN_LEVEL_FOR_TOKENS } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; -import { FieldError } from '../../components/Form/FormFields/FieldWrapper'; +import { MIN_LEVEL_FOR_TOKENS } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; +import { FieldError } from 'components/Form/FormFields/FieldWrapper'; import ApiKeyContainer from './ApiKey'; -import DumbField from '../../components/Form/FormFields/DumbField'; +import DumbField from 'components/Form/FormFields/DumbField'; import { EditWrapper } from 'components'; const NoLevel = () => ( diff --git a/web/src/containers/UserSecurity/FreezeSection.js b/web/src/containers/UserSecurity/FreezeSection.js index 64702daf6e..75c6ab3f7b 100644 --- a/web/src/containers/UserSecurity/FreezeSection.js +++ b/web/src/containers/UserSecurity/FreezeSection.js @@ -1,7 +1,7 @@ import React from 'react'; -import { FieldError } from '../../components/Form/FormFields/FieldWrapper'; -import { Button } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import { FieldError } from 'components/Form/FormFields/FieldWrapper'; +import { Button } from 'components'; +import STRINGS from 'config/localizedStrings'; export const FreezeSection = ({ handleSubmit }) => ( <div> diff --git a/web/src/containers/UserSecurity/OTP.js b/web/src/containers/UserSecurity/OTP.js index 85135b5c5c..de8d059ca0 100644 --- a/web/src/containers/UserSecurity/OTP.js +++ b/web/src/containers/UserSecurity/OTP.js @@ -1,10 +1,8 @@ import React from 'react'; -import { CheckboxButton, IconTitle } from '../../components'; +import { CheckboxButton, IconTitle, EditWrapper } from 'components'; import QRCode from 'qrcode.react'; import OTPForm from './OTPForm'; -import { EditWrapper } from 'components'; - -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; export const renderOTPForm = ( secret, diff --git a/web/src/containers/UserSecurity/OTPForm.js b/web/src/containers/UserSecurity/OTPForm.js index 0297aaa05d..f33d62aa41 100644 --- a/web/src/containers/UserSecurity/OTPForm.js +++ b/web/src/containers/UserSecurity/OTPForm.js @@ -1,10 +1,9 @@ import React from 'react'; import { reduxForm } from 'redux-form'; -import { required } from '../../components/Form/validations'; -import renderFields from '../../components/Form/factoryFields'; -import { Button } from '../../components'; -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import { required } from 'components/Form/validations'; +import renderFields from 'components/Form/factoryFields'; +import { Button, EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; const Form = ({ handleSubmit, submitting, pristine, error, valid }) => { const formFields = { diff --git a/web/src/containers/UserSecurity/index.js b/web/src/containers/UserSecurity/index.js index edaaf3fcce..c2b19ce4e5 100644 --- a/web/src/containers/UserSecurity/index.js +++ b/web/src/containers/UserSecurity/index.js @@ -35,9 +35,9 @@ import { DeveloperSection } from './DeveloperSection'; import { generateLogins } from './utils_logins'; import { RECORD_LIMIT } from './constants'; import LoginDisplay from './LoginDisplay'; -import { getUserLogins } from '../../actions/userAction'; +import { getUserLogins } from 'actions/userAction'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; // import { ICONS } from 'config/constants'; diff --git a/web/src/containers/UserSecurity/utils_logins.js b/web/src/containers/UserSecurity/utils_logins.js index aec2a0f02a..59f501f975 100644 --- a/web/src/containers/UserSecurity/utils_logins.js +++ b/web/src/containers/UserSecurity/utils_logins.js @@ -1,6 +1,6 @@ import React from 'react'; -import { getFormatTimestamp } from '../../utils/utils'; -import STRINGS from '../../config/localizedStrings'; +import { getFormatTimestamp } from 'utils/utils'; +import STRINGS from 'config/localizedStrings'; export const generateLogins = () => { return [ diff --git a/web/src/containers/UserSettings/AudioForm.js b/web/src/containers/UserSettings/AudioForm.js index b7d3cb9487..12b4a3af13 100644 --- a/web/src/containers/UserSettings/AudioForm.js +++ b/web/src/containers/UserSettings/AudioForm.js @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import { reduxForm } from 'redux-form'; -import renderFields from '../../components/Form/factoryFields'; -import { Button, IconTitle } from '../../components'; -import { getErrorLocalized } from '../../utils/errors'; -import STRINGS from '../../config/localizedStrings'; +import renderFields from 'components/Form/factoryFields'; +import { Button, IconTitle } from 'components'; +import { getErrorLocalized } from 'utils/errors'; +import STRINGS from 'config/localizedStrings'; import { DEFAULT_TOGGLE_OPTIONS } from 'config/options'; import { EditWrapper } from 'components'; diff --git a/web/src/containers/UserSettings/LanguageForm.js b/web/src/containers/UserSettings/LanguageForm.js index d9bb878a72..b1603d1864 100644 --- a/web/src/containers/UserSettings/LanguageForm.js +++ b/web/src/containers/UserSettings/LanguageForm.js @@ -1,13 +1,11 @@ import React, { Component } from 'react'; import { reduxForm } from 'redux-form'; import { isMobile } from 'react-device-detect'; - -import renderFields from '../../components/Form/factoryFields'; -import { Button, IconTitle } from '../../components'; -import { required } from '../../components/Form/validations'; -import { getErrorLocalized } from '../../utils/errors'; -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import renderFields from 'components/Form/factoryFields'; +import { Button, IconTitle, EditWrapper } from 'components'; +import { required } from 'components/Form/validations'; +import { getErrorLocalized } from 'utils/errors'; +import STRINGS from 'config/localizedStrings'; export const generateLanguageFormValues = (values = '') => { const langValues = STRINGS.SETTINGS_LANGUAGE_OPTIONS.filter((filterValue) => { diff --git a/web/src/containers/UserSettings/NotificationForm.js b/web/src/containers/UserSettings/NotificationForm.js index bf0202d138..d047a373f4 100644 --- a/web/src/containers/UserSettings/NotificationForm.js +++ b/web/src/containers/UserSettings/NotificationForm.js @@ -1,12 +1,11 @@ import React, { Component } from 'react'; import { reduxForm } from 'redux-form'; -import renderFields from '../../components/Form/factoryFields'; -import { Button, IconTitle } from '../../components'; -import { getErrorLocalized } from '../../utils/errors'; -import STRINGS from '../../config/localizedStrings'; +import renderFields from 'components/Form/factoryFields'; +import { Button, IconTitle, EditWrapper } from 'components'; +import { getErrorLocalized } from 'utils/errors'; +import STRINGS from 'config/localizedStrings'; import { DEFAULT_TOGGLE_OPTIONS } from 'config/options'; -import { EditWrapper } from 'components'; export const generateNotificationFormValues = () => ({ popup_order_confirmation: { diff --git a/web/src/containers/UserSettings/SetOrderPortfolio.js b/web/src/containers/UserSettings/SetOrderPortfolio.js index 3e58c1cf45..bcc7d8711f 100644 --- a/web/src/containers/UserSettings/SetOrderPortfolio.js +++ b/web/src/containers/UserSettings/SetOrderPortfolio.js @@ -1,15 +1,11 @@ import React from 'react'; import { reduxForm } from 'redux-form'; -import renderFields from '../../components/Form/factoryFields'; -import { getErrorLocalized } from '../../utils/errors'; -import { - required, - minValue, - maxValue, -} from '../../components/Form/validations'; -import { IconTitle, Button } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import renderFields from 'components/Form/factoryFields'; +import { getErrorLocalized } from 'utils/errors'; +import { required, minValue, maxValue } from 'components/Form/validations'; +import { IconTitle, Button } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; const fields = { diff --git a/web/src/containers/UserSettings/SettingsForm.js b/web/src/containers/UserSettings/SettingsForm.js index 3ec33ba18f..f665621135 100644 --- a/web/src/containers/UserSettings/SettingsForm.js +++ b/web/src/containers/UserSettings/SettingsForm.js @@ -1,17 +1,16 @@ import React, { Component } from 'react'; import { reduxForm } from 'redux-form'; import { isMobile } from 'react-device-detect'; -import renderFields from '../../components/Form/factoryFields'; -import { Button, IconTitle } from '../../components'; +import renderFields from 'components/Form/factoryFields'; +import { Button, IconTitle, EditWrapper } from 'components'; import { required, minValue, maxValue, step, -} from '../../components/Form/validations'; -import { getErrorLocalized } from '../../utils/errors'; -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +} from 'components/Form/validations'; +import { getErrorLocalized } from 'utils/errors'; +import STRINGS from 'config/localizedStrings'; const orderbook_level_step = 1; const orderbook_level_min = 1; diff --git a/web/src/containers/UserSettings/UsernameForm.js b/web/src/containers/UserSettings/UsernameForm.js index 787619c81d..d3945f94e2 100644 --- a/web/src/containers/UserSettings/UsernameForm.js +++ b/web/src/containers/UserSettings/UsernameForm.js @@ -2,13 +2,12 @@ import React, { Component } from 'react'; import { reduxForm } from 'redux-form'; import { isMobile } from 'react-device-detect'; -import renderFields from '../../components/Form/factoryFields'; -import { FieldError } from '../../components/Form/FormFields/FieldWrapper'; -import { Button, IconTitle } from '../../components'; -import { required, username } from '../../components/Form/validations'; -import { getErrorLocalized } from '../../utils/errors'; -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import renderFields from 'components/Form/factoryFields'; +import { FieldError } from 'components/Form/FormFields/FieldWrapper'; +import { Button, IconTitle, EditWrapper } from 'components'; +import { required, username } from 'components/Form/validations'; +import { getErrorLocalized } from 'utils/errors'; +import STRINGS from 'config/localizedStrings'; export const generateUsernameFormValues = (disabled = false) => ({ username: { diff --git a/web/src/containers/UserSettings/index.js b/web/src/containers/UserSettings/index.js index 46b39591c4..96fb51681f 100644 --- a/web/src/containers/UserSettings/index.js +++ b/web/src/containers/UserSettings/index.js @@ -12,13 +12,13 @@ import { openRiskPortfolioOrderWarning, closeNotification, } from 'actions/appActions'; -import { logout } from '../../actions/authAction'; +import { logout } from 'actions/authAction'; import { updateUserSettings, setUserData, setUsername, setUsernameStore, -} from '../../actions/userAction'; +} from 'actions/userAction'; import { IconTitle, HeaderSection, @@ -28,7 +28,8 @@ import { MobileTabBar, Loader, TabController, -} from '../../components'; + EditWrapper, +} from 'components'; import SettingsForm, { generateFormValues } from './SettingsForm'; import UsernameForm, { generateUsernameFormValues } from './UsernameForm'; import LanguageForm, { generateLanguageFormValues } from './LanguageForm'; @@ -38,9 +39,8 @@ import NotificationForm, { import AudioCueForm, { generateAudioCueFormValues } from './AudioForm'; import RiskForm, { generateWarningFormValues } from './RiskForm'; -import STRINGS from '../../config/localizedStrings'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; -import { EditWrapper } from 'components'; class UserSettings extends Component { state = { @@ -64,15 +64,30 @@ class UserSettings extends Component { } if (window.location.search && window.location.search.includes('signals')) { this.setState({ activeTab: 0 }); - } else if (window.location.search && window.location.search.includes('interface')) { + } else if ( + window.location.search && + window.location.search.includes('interface') + ) { this.setState({ activeTab: 1 }); - } else if (window.location.search && window.location.search.includes('language')) { + } else if ( + window.location.search && + window.location.search.includes('language') + ) { this.setState({ activeTab: 2 }); - } else if (window.location.search && window.location.search.includes('audioCue')) { + } else if ( + window.location.search && + window.location.search.includes('audioCue') + ) { this.setState({ activeTab: 3 }); - } else if (window.location.search && window.location.search.includes('manageRisk')) { + } else if ( + window.location.search && + window.location.search.includes('manageRisk') + ) { this.setState({ activeTab: 4 }); - } else if (window.location.search && window.location.search.includes('chat')) { + } else if ( + window.location.search && + window.location.search.includes('chat') + ) { this.setState({ activeTab: 5 }); } this.openCurrentTab(); @@ -112,7 +127,10 @@ class UserSettings extends Component { } componentDidUpdate(prevProps, prevState) { - if (JSON.stringify(prevState.activeTab) !== JSON.stringify(this.state.activeTab)) { + if ( + JSON.stringify(prevState.activeTab) !== + JSON.stringify(this.state.activeTab) + ) { this.openCurrentTab(); } } @@ -121,7 +139,8 @@ class UserSettings extends Component { let currentTab = ''; if (this.state.activeTab === 0) { currentTab = 'signals'; - } if (this.state.activeTab === 1) { + } + if (this.state.activeTab === 1) { currentTab = 'interface'; } else if (this.state.activeTab === 2) { currentTab = 'language'; diff --git a/web/src/containers/Verification/DocumentsVerification.js b/web/src/containers/Verification/DocumentsVerification.js index a4d0f77867..8e949cbc3d 100644 --- a/web/src/containers/Verification/DocumentsVerification.js +++ b/web/src/containers/Verification/DocumentsVerification.js @@ -4,18 +4,18 @@ import moment from 'moment'; import { isBefore, requiredWithCustomMessage, -} from '../../components/Form/validations'; -import renderFields from '../../components/Form/factoryFields'; -import { Button, IconTitle, HeaderSection } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +} from 'components/Form/validations'; +import renderFields from 'components/Form/factoryFields'; +import { Button, IconTitle, HeaderSection } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { IdentificationFormSection, PORSection, SelfieWithPhotoId, } from './HeaderSection'; -import { getErrorLocalized } from '../../utils/errors'; -import { updateDocuments } from '../../actions/userAction'; +import { getErrorLocalized } from 'utils/errors'; +import { updateDocuments } from 'actions/userAction'; import Image from 'components/Image'; import { isMobile } from 'react-device-detect'; diff --git a/web/src/containers/Verification/DocumentsVerificationHome.js b/web/src/containers/Verification/DocumentsVerificationHome.js index 90831706b1..8035e7fd3b 100644 --- a/web/src/containers/Verification/DocumentsVerificationHome.js +++ b/web/src/containers/Verification/DocumentsVerificationHome.js @@ -3,9 +3,9 @@ import moment from 'moment'; import Image from 'components/Image'; import classnames from 'classnames'; -import { Button, PanelInformationRow } from '../../components'; -import STRINGS from '../../config/localizedStrings'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { Button, PanelInformationRow } from 'components'; +import STRINGS from 'config/localizedStrings'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; import withConfig from 'components/ConfigProvider/withConfig'; import { EditWrapper } from 'components'; diff --git a/web/src/containers/Verification/IdentityVerification.js b/web/src/containers/Verification/IdentityVerification.js index d6e1fcb75e..7101c29d45 100644 --- a/web/src/containers/Verification/IdentityVerification.js +++ b/web/src/containers/Verification/IdentityVerification.js @@ -5,17 +5,15 @@ import { required, requiredBoolean, isBefore, -} from '../../components/Form/validations'; -import renderFields from '../../components/Form/factoryFields'; -import { Button, IconTitle, HeaderSection } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +} from 'components/Form/validations'; +import renderFields from 'components/Form/factoryFields'; +import { Button, IconTitle, HeaderSection, EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; -import { COUNTRIES_OPTIONS } from '../../utils/countries'; - +import { COUNTRIES_OPTIONS } from 'utils/countries'; import { isMobile } from 'react-device-detect'; -import { getErrorLocalized } from '../../utils/errors'; -import { updateUser } from '../../actions/userAction'; -import { EditWrapper } from 'components'; +import { getErrorLocalized } from 'utils/errors'; +import { updateUser } from 'actions/userAction'; const FORM_NAME = 'IdentityVerification'; diff --git a/web/src/containers/Verification/IdentityVerificationHome.js b/web/src/containers/Verification/IdentityVerificationHome.js index 2501ab76c0..5195a2c434 100644 --- a/web/src/containers/Verification/IdentityVerificationHome.js +++ b/web/src/containers/Verification/IdentityVerificationHome.js @@ -1,9 +1,9 @@ import React from 'react'; -import { Button, PanelInformationRow } from '../../components'; +import { Button, PanelInformationRow } from 'components'; import { getCountry } from './utils'; -import STRINGS from '../../config/localizedStrings'; -import { getFormatTimestamp } from '../../utils/utils'; +import STRINGS from 'config/localizedStrings'; +import { getFormatTimestamp } from 'utils/utils'; import { EditWrapper } from 'components'; const formatBirthday = { diff --git a/web/src/containers/Verification/MobileVerification.js b/web/src/containers/Verification/MobileVerification.js index 9c35773441..e02d86d05d 100644 --- a/web/src/containers/Verification/MobileVerification.js +++ b/web/src/containers/Verification/MobileVerification.js @@ -8,23 +8,20 @@ import { } from 'redux-form'; import { isMobile } from 'react-device-detect'; -import { required } from '../../components/Form/validations'; -import renderFields from '../../components/Form/factoryFields'; +import { required } from 'components/Form/validations'; +import renderFields from 'components/Form/factoryFields'; import { Button, IconTitle, ElapsedTimer, HeaderSection, -} from '../../components'; -import STRINGS from '../../config/localizedStrings'; + EditWrapper, +} from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; -import { PHONE_OPTIONS } from '../../utils/countries'; -import { getErrorLocalized } from '../../utils/errors'; -import { - verifySmsCode, - requestSmsCode, -} from '../../actions/verificationActions'; -import { EditWrapper } from 'components'; +import { PHONE_OPTIONS } from 'utils/countries'; +import { getErrorLocalized } from 'utils/errors'; +import { verifySmsCode, requestSmsCode } from 'actions/verificationActions'; const FORM_NAME = 'MobileVerification'; let loadingTimeOut = ''; diff --git a/web/src/containers/Verification/MobileVerificationHome.js b/web/src/containers/Verification/MobileVerificationHome.js index 7a20cadda0..7111e4c69c 100644 --- a/web/src/containers/Verification/MobileVerificationHome.js +++ b/web/src/containers/Verification/MobileVerificationHome.js @@ -1,9 +1,7 @@ import React from 'react'; - import { getCountryFromNumber } from './utils'; -import { Button, PanelInformationRow } from '../../components'; -import STRINGS from '../../config/localizedStrings'; -import { EditWrapper } from 'components'; +import { Button, PanelInformationRow, EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; const MobileVerificationHome = ({ user, setActivePageContent }) => { const { phone_number } = user; diff --git a/web/src/containers/VerificationEmailCode/index.js b/web/src/containers/VerificationEmailCode/index.js index 519b7cabf3..2e234b2cea 100644 --- a/web/src/containers/VerificationEmailCode/index.js +++ b/web/src/containers/VerificationEmailCode/index.js @@ -6,11 +6,11 @@ import { isUUID } from 'validator'; import { verifyVerificationCode, checkVerificationCode, -} from '../../actions/authAction'; +} from 'actions/authAction'; -import { IconTitle, Loader, Button } from '../../components'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, Loader, Button } from 'components'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; class VerifyEmailCode extends Component { diff --git a/web/src/containers/VerificationEmailRequest/EmailRequestForm.js b/web/src/containers/VerificationEmailRequest/EmailRequestForm.js index fb3361129a..a02835f4a8 100644 --- a/web/src/containers/VerificationEmailRequest/EmailRequestForm.js +++ b/web/src/containers/VerificationEmailRequest/EmailRequestForm.js @@ -4,9 +4,9 @@ import { requiredWithCustomMessage, email, normalizeEmail, -} from '../../components/Form/validations'; -import { AuthForm } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +} from 'components/Form/validations'; +import { AuthForm } from 'components'; +import STRINGS from 'config/localizedStrings'; export const generateFormFields = () => ({ email: { diff --git a/web/src/containers/VerificationEmailRequest/EmailRequestSuccess.js b/web/src/containers/VerificationEmailRequest/EmailRequestSuccess.js index 71e0844ee1..f375250225 100644 --- a/web/src/containers/VerificationEmailRequest/EmailRequestSuccess.js +++ b/web/src/containers/VerificationEmailRequest/EmailRequestSuccess.js @@ -1,9 +1,9 @@ import React from 'react'; import classnames from 'classnames'; -import { IconTitle, Button } from '../../components'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, Button } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; const EmailRequestSuccess = ({ onClick, icons: ICONS, ...rest }) => { return ( diff --git a/web/src/containers/VerificationEmailRequest/index.js b/web/src/containers/VerificationEmailRequest/index.js index b2bb56fdf9..7cdcc6231b 100644 --- a/web/src/containers/VerificationEmailRequest/index.js +++ b/web/src/containers/VerificationEmailRequest/index.js @@ -5,13 +5,13 @@ import { bindActionCreators } from 'redux'; import { Link } from 'react-router'; import { isMobile } from 'react-device-detect'; import { SubmissionError } from 'redux-form'; -import { requestVerificationEmail } from '../../actions/authAction'; +import { requestVerificationEmail } from 'actions/authAction'; import EmailRequestForm, { generateFormFields } from './EmailRequestForm'; import EmailRequestSuccess from './EmailRequestSuccess'; -import { IconTitle, Dialog, MobileBarBack } from '../../components'; -import { ContactForm } from '../'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, Dialog, MobileBarBack } from 'components'; +import { ContactForm } from 'containers'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { openContactForm } from 'actions/appActions'; diff --git a/web/src/containers/Wallet/CurrencyWallet.js b/web/src/containers/Wallet/CurrencyWallet.js index 2d3fcfe08e..e74b81359a 100644 --- a/web/src/containers/Wallet/CurrencyWallet.js +++ b/web/src/containers/Wallet/CurrencyWallet.js @@ -9,14 +9,14 @@ import { ActionNotification, MobileBarBack, Image, -} from '../../components'; -import { FLEX_CENTER_CLASSES, DEFAULT_COIN_DATA } from '../../config/constants'; +} from 'components'; +import { FLEX_CENTER_CLASSES, DEFAULT_COIN_DATA } from 'config/constants'; import { formatToCurrency, generateWalletActionsText, getCurrencyFromName, -} from '../../utils/currency'; -import STRINGS from '../../config/localizedStrings'; +} from 'utils/currency'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { STATIC_ICONS } from 'config/icons'; @@ -74,8 +74,8 @@ class Wallet extends Component { iconId={icon_id} icon={ICONS[icon_id]} wrapperClassName="coin-icons" - width='32px' - height='32px' + width="32px" + height="32px" imageWrapperClassName="currency-ball-image-wrapper" /> <div className="with_price-block_amount-value"> diff --git a/web/src/containers/Withdraw/ReviewEmailContent.js b/web/src/containers/Withdraw/ReviewEmailContent.js index 5977d68649..29a65a7abb 100644 --- a/web/src/containers/Withdraw/ReviewEmailContent.js +++ b/web/src/containers/Withdraw/ReviewEmailContent.js @@ -1,8 +1,6 @@ import React from 'react'; -import { Button, IconTitle } from '../../components'; -import { EditWrapper } from 'components'; - -import STRINGS from '../../config/localizedStrings'; +import { Button, IconTitle, EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; const ReviewEmailContent = ({ onConfirmEmail, icons: ICONS }) => { diff --git a/web/src/containers/WithdrawConfirmation/index.js b/web/src/containers/WithdrawConfirmation/index.js index 1986f78aea..af92e35ebd 100644 --- a/web/src/containers/WithdrawConfirmation/index.js +++ b/web/src/containers/WithdrawConfirmation/index.js @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import classnames from 'classnames'; -import { IconTitle, Button, Loader } from '../../components'; -import { performConfirmWithdrawal } from '../../actions/walletActions'; -import { FLEX_CENTER_CLASSES } from '../../config/constants'; -import STRINGS from '../../config/localizedStrings'; +import { IconTitle, Button, Loader } from 'components'; +import { performConfirmWithdrawal } from 'actions/walletActions'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; +import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { EditWrapper } from 'components'; From ff942e8598611f822262c8f67c111da8f5e990a0 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 7 Sep 2022 21:18:04 +0430 Subject: [PATCH 023/132] fix for contact form import issue --- web/src/containers/ResetPassword/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/containers/ResetPassword/index.js b/web/src/containers/ResetPassword/index.js index 5af80efaca..b1ca9d4aae 100644 --- a/web/src/containers/ResetPassword/index.js +++ b/web/src/containers/ResetPassword/index.js @@ -6,7 +6,8 @@ import { SubmissionError } from 'redux-form'; import { resetPassword } from 'actions/authAction'; import ResetPasswordForm from './ResetPasswordForm'; import ResetPasswordSuccess from './ResetPasswordSuccess'; -import { IconTitle, Dialog, ContactForm } from 'components'; +import { IconTitle, Dialog } from 'components'; +import { ContactForm } from 'containers'; import { FLEX_CENTER_CLASSES } from 'config/constants'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; From 2e3a4ea299d0afcf351d73ca26bf61807437fad3 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Thu, 8 Sep 2022 19:00:35 +0900 Subject: [PATCH 024/132] set email script sender support in env --- server/tools/dbs/setEmail.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/tools/dbs/setEmail.js b/server/tools/dbs/setEmail.js index 5d19349ad5..2b3890c2b6 100644 --- a/server/tools/dbs/setEmail.js +++ b/server/tools/dbs/setEmail.js @@ -4,7 +4,8 @@ const { SMTP_SERVER, SMTP_PORT, SMTP_USER, - SMTP_PASSWORD + SMTP_PASSWORD, + SMTP_SENDER } = process.env; const { publisher } = require('../../db/pubsub'); @@ -17,6 +18,10 @@ Status.findOne({}) port: SMTP_PORT, user: SMTP_USER, password: SMTP_PASSWORD + }, + emails: { + ...status.secrets.emails, + sender: SMTP_SENDER } }; From 49f283a5617e10ff654cc71638f510a166ec388d Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Tue, 13 Sep 2022 12:47:10 +0300 Subject: [PATCH 025/132] fixes --- .../assets/icons/coin-graphic-not-detected.svg | 15 +++++++++++++++ .../components/Form/FormFields/FieldWrapper.js | 2 +- web/src/config/icons/dark.js | 2 +- web/src/config/icons/light.js | 2 +- web/src/config/icons/static.js | 2 +- web/src/index.css | 3 +++ web/src/index.scss | 4 ++++ 7 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 web/public/assets/icons/coin-graphic-not-detected.svg diff --git a/web/public/assets/icons/coin-graphic-not-detected.svg b/web/public/assets/icons/coin-graphic-not-detected.svg new file mode 100644 index 0000000000..52c6f6e251 --- /dev/null +++ b/web/public/assets/icons/coin-graphic-not-detected.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 125 125" style="enable-background:new 0 0 100 100;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#8C8C8C;} + .st1{fill:#FFFFFF;} +</style> +<circle class="st0" cx="50" cy="50" r="50"/> +<g> + <path class="st1" d="M45.5,60l-0.1-1.8c-0.4-3.7,0.8-7.7,4.2-11.9c3.1-3.6,4.8-6.3,4.8-9.4c0-3.5-2.2-5.8-6.5-5.9 + c-2.5,0-5.2,0.8-6.9,2.1l-1.6-4.3c2.3-1.6,6.2-2.7,9.8-2.7c7.9,0,11.4,4.9,11.4,10.1c0,4.7-2.6,8-5.9,11.9c-3,3.6-4.1,6.6-3.9,10.1 + l0.1,1.8H45.5z M44,69.6c0-2.5,1.7-4.3,4.1-4.3c2.4,0,4,1.8,4,4.3c0,2.4-1.6,4.3-4.1,4.3C45.7,73.9,44,72,44,69.6z"/> +</g> +</svg> diff --git a/web/src/components/Form/FormFields/FieldWrapper.js b/web/src/components/Form/FormFields/FieldWrapper.js index ef1b972884..7dd46ecd8a 100644 --- a/web/src/components/Form/FormFields/FieldWrapper.js +++ b/web/src/components/Form/FormFields/FieldWrapper.js @@ -162,7 +162,7 @@ class FieldWrapper extends Component { preview, isEmail = false, emailMsg = '', - showCross = showCross, + showCross, onCrossClick = () => {}, } = this.props; diff --git a/web/src/config/icons/dark.js b/web/src/config/icons/dark.js index 5084dfecd8..080620d4b1 100644 --- a/web/src/config/icons/dark.js +++ b/web/src/config/icons/dark.js @@ -222,7 +222,7 @@ const nestedIcons = { REFER_ICON: '/assets/images/refer-icon.svg', STAKETOKEN_ICON: '/assets/images/stake.svg', - DEFAULT_ICON: '/assets/icons/missing-coin-light.svg', + DEFAULT_ICON: '/assets/icons/coin-graphic-not-detected.svg', EXPIRED_ICON: '/assets/images/expired.svg', HAP_ACCOUNT_ICON: '/assets/icons/hap-account-icon.svg', diff --git a/web/src/config/icons/light.js b/web/src/config/icons/light.js index c5bc219413..3a3b766872 100644 --- a/web/src/config/icons/light.js +++ b/web/src/config/icons/light.js @@ -205,7 +205,7 @@ const nestedIcons = { REFER_ICON: '/assets/images/refer-icon.svg', STAKETOKEN_ICON: '/assets/images/stake.svg', - DEFAULT_ICON: '/assets/icons/missing-coin-light.svg', + DEFAULT_ICON: '/assets/icons/coin-graphic-not-detected.svg', EXPIRED_ICON: '/assets/images/expired.svg', HAP_ACCOUNT_ICON: '/assets/icons/hap-account-icon.svg', diff --git a/web/src/config/icons/static.js b/web/src/config/icons/static.js index 9f50ac8efd..d3166c01d8 100644 --- a/web/src/config/icons/static.js +++ b/web/src/config/icons/static.js @@ -101,7 +101,7 @@ const icons = { xmr: '/assets/images/xmr-icon.svg', xrp: '/assets/images/xrp-icon.svg', }, - MISSING_ICON: '/assets/images/missing-coin.svg', + MISSING_ICON: '/assets/images/coin-graphic-not-detected.svg', SETTINGS: '/assets/images/noun_settings.svg', CURRENCY_SYMBOL: '/assets/images/noun_currency.svg', BLOCKCHAIN_BACKGROUND: '/assets/images/blockchain-background.svg', diff --git a/web/src/index.css b/web/src/index.css index 40e14b0579..5c9899ccf5 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -10963,6 +10963,9 @@ body { * { transition: all 0.3s; } + * .presentation_container, + * .presentation_container * { + transition: none !important; } .direction_rtl.apply_rtl { direction: rtl; } diff --git a/web/src/index.scss b/web/src/index.scss index 441253dea7..4df154d37b 100644 --- a/web/src/index.scss +++ b/web/src/index.scss @@ -55,6 +55,10 @@ body { * { transition: all 0.3s; + .presentation_container, + .presentation_container * { + transition: none !important; + } } .direction_rtl { From 04ad7b2a01ab988793706a6aaeaa91923cbb27ea Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Tue, 13 Sep 2022 14:23:55 +0300 Subject: [PATCH 026/132] feature --- web/src/_common.scss | 29 +++++++++++++++++++ .../containers/Admin/Transactions/index.js | 3 +- .../components/TradeAndOrderFilters.js | 19 +++++++++--- .../containers/TransactionsHistory/index.js | 6 +++- web/src/index.css | 21 ++++++++++++++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/web/src/_common.scss b/web/src/_common.scss index 6ec3dd9783..b3dd66723f 100644 --- a/web/src/_common.scss +++ b/web/src/_common.scss @@ -216,6 +216,24 @@ table th { } } +.filter-option { + gap: 1rem; + + .coin-icons { + width: 13px !important; + height: 13px !important; + } + + .currency-ball-image-wrapper { + width: 13px; + height: 13px; + } +} + +.filter-dropdown { + width: 150px !important; +} + .custom-select-input-style { &.no-border { .ant-select-selector { @@ -223,6 +241,17 @@ table th { } } + .coin-icons { + width: 16px; + height: 16px; + } + + .currency-ball-image-wrapper { + width: 16px; + height: 16px; + margin-right: 15px; + } + .ant-select-selector { border-radius: 0.35rem !important; border-color: $colors-main-border !important; diff --git a/web/src/containers/Admin/Transactions/index.js b/web/src/containers/Admin/Transactions/index.js index 66a4546f65..e7d62dcafb 100644 --- a/web/src/containers/Admin/Transactions/index.js +++ b/web/src/containers/Admin/Transactions/index.js @@ -267,7 +267,7 @@ class Transactions extends Component { currentTablePage, pageSize, } = this.state; - const { showFilters, coins } = this.props; + const { showFilters, coins, icons } = this.props; const { hideUserColumn, @@ -279,6 +279,7 @@ class Transactions extends Component { <div> {showFilters && ( <Filters + icons={icons} coins={coins} onChange={this.onChangeQuery} onClick={this.onClickFilters} diff --git a/web/src/containers/TransactionsHistory/components/TradeAndOrderFilters.js b/web/src/containers/TransactionsHistory/components/TradeAndOrderFilters.js index 298418d098..8f284c4793 100644 --- a/web/src/containers/TransactionsHistory/components/TradeAndOrderFilters.js +++ b/web/src/containers/TransactionsHistory/components/TradeAndOrderFilters.js @@ -5,11 +5,12 @@ import moment from 'moment'; import { dateFilters } from '../filterUtils'; import STRINGS from '../../../config/localizedStrings'; +import { Image } from 'hollaex-web-lib'; const { Option } = Select; const { RangePicker } = DatePicker; -const Filters = ({ pairs, onSearch, formName, activeTab }) => { +const Filters = ({ pairs, onSearch, formName, activeTab, icons: ICONS }) => { const [form] = Form.useForm(); const [click, setClick] = useState([]); const [customSel, setCustomSel] = useState(false); @@ -149,16 +150,26 @@ const Filters = ({ pairs, onSearch, formName, activeTab }) => { width: 100, }} size="small" - className="custom-select-input-style elevated" + className="custom-select-input-style elevated filter-dropdown" dropdownClassName="custom-select-style" bordered={false} suffixIcon={<CaretDownOutlined />} > <Option value={null}>{STRINGS['ALL']}</Option> {Object.entries(pairs).map( - ([_, { name, pair_base_display, pair_2_display }]) => ( + ([_, { name, pair_base_display, pair_2_display, icon_id }]) => ( <Option key={name} value={name}> - {`${pair_base_display}-${pair_2_display}`} + <div className="d-flex filter-option"> + <Image + width="16px" + height="16px" + iconId={icon_id} + icon={ICONS[icon_id]} + wrapperClassName="coin-icons" + imageWrapperClassName="currency-ball-image-wrapper" + /> + <div>{`${pair_base_display}-${pair_2_display}`}</div> + </div> </Option> ) )} diff --git a/web/src/containers/TransactionsHistory/index.js b/web/src/containers/TransactionsHistory/index.js index 4c8a7e64e5..c0ca4ea300 100644 --- a/web/src/containers/TransactionsHistory/index.js +++ b/web/src/containers/TransactionsHistory/index.js @@ -251,11 +251,12 @@ class TransactionsHistory extends Component { } generateFilters = () => { - const { pairs, coins } = this.props; + const { pairs, coins, icons } = this.props; this.setState({ filters: { orders: ( <TradeAndOrderFilters + icons={icons} pairs={pairs} onSearch={this.onSearch} formName="orders" @@ -264,6 +265,7 @@ class TransactionsHistory extends Component { ), trades: ( <TradeAndOrderFilters + icons={icons} pairs={pairs} onSearch={this.onSearch} formName="trades" @@ -272,6 +274,7 @@ class TransactionsHistory extends Component { ), deposits: ( <DepositAndWithdrawlFilters + icons={icons} coins={coins} onSearch={this.onSearch} formName="deposits" @@ -280,6 +283,7 @@ class TransactionsHistory extends Component { ), withdrawals: ( <DepositAndWithdrawlFilters + icons={icons} coins={coins} onSearch={this.onSearch} formName="withdrawals" diff --git a/web/src/index.css b/web/src/index.css index 5c9899ccf5..f9096c5139 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -253,9 +253,30 @@ table th { .custom-select-style.ant-select-dropdown { background-color: var(--base_background); } +.filter-option { + gap: 1rem; } + .filter-option .coin-icons { + width: 13px !important; + height: 13px !important; } + .filter-option .currency-ball-image-wrapper { + width: 13px; + height: 13px; } + +.filter-dropdown { + width: 150px !important; } + .custom-select-input-style.no-border .ant-select-selector { border: none !important; } +.custom-select-input-style .coin-icons { + width: 16px; + height: 16px; } + +.custom-select-input-style .currency-ball-image-wrapper { + width: 16px; + height: 16px; + margin-right: 15px; } + .custom-select-input-style .ant-select-selector { border-radius: 0.35rem !important; border-color: var(--calculated_important-border) !important; From 77cbb7af4dff4dac9897ea17787a2a964c4f2380 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 14 Sep 2022 19:28:47 +0430 Subject: [PATCH 027/132] apps section initial commit --- web/src/config/icons/dark.js | 6 ++ web/src/config/lang/en.json | 25 ++++++ web/src/config/menu.js | 6 ++ web/src/containers/AppDetails/index.js | 55 ++++++++++++ web/src/containers/Apps/All.js | 62 ++++++++++++++ web/src/containers/Apps/User.js | 75 ++++++++++++++++ web/src/containers/Apps/index.js | 114 +++++++++++++++++++++++++ web/src/containers/index.js | 2 + web/src/routes.js | 9 ++ 9 files changed, 354 insertions(+) create mode 100644 web/src/containers/AppDetails/index.js create mode 100644 web/src/containers/Apps/All.js create mode 100644 web/src/containers/Apps/User.js create mode 100644 web/src/containers/Apps/index.js diff --git a/web/src/config/icons/dark.js b/web/src/config/icons/dark.js index 5084dfecd8..79a86fb7b2 100644 --- a/web/src/config/icons/dark.js +++ b/web/src/config/icons/dark.js @@ -39,6 +39,7 @@ const nestedIcons = { SECURITY: '/assets/images/tab-security.svg', VERIFY: '/assets/images/tab-verify.svg', SETTING: '/assets/images/tab-setting.svg', + APPS: '/assets/images/tab-setting.svg', API: '/assets/images/tab-api.svg', STAKE: '/assets/images/stake-page-icon.svg', }, @@ -76,6 +77,11 @@ const nestedIcons = { RISK_MANAGE_WARNING_ICON: '/assets/images/risk-manage-pop-warning.svg', }, + APPS: { + ALL: '/assets/images/tab-setting.svg', + USER: '/assets/images/tab-setting.svg', + }, + SECURITY: { OTP_ICON: '/assets/images/2fa-security-icon.svg', CHANGE_PASSWORD_ICON: '/assets/images/password-security-icon.svg', diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index 297a45964e..b9f1a85934 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -60,6 +60,7 @@ "TAB_VERIFICATION": "Verification", "TAB_SECURITY": "Security", "TAB_SETTINGS": "Settings", + "TAB_APPS": "Apps", "TAB_WALLET": "Wallet", "TAB_SUMMARY": "Summary", "TAB_HISTORY": "History", @@ -471,6 +472,30 @@ "WARNING_POP_UP": "Warning pop ups" } }, + "USER_APPS": { + "TITLE": "Your exchange apps", + "SUBTITLE": "Your exchange account applications information and extra functionality below.", + "ALL_APPS": { + "TAB_TITLE": "All apps", + "TITLE": "Exchange apps", + "SUBTITLE": "Get more functionality from your exchange account by simply selecting an app below and clicking Add button." + }, + "MY_APPS": { + "TAB_TITLE": "My apps", + "TITLE": "My exchange apps", + "SUBTITLE": "Below are your active exchange applications. You can click to see expand on each applications information, functions and add/remove them. Apps are designed to provide more your functionality to your exchange experience." + }, + "TABLE": { + "APP_NAME": "App name", + "DESCRIPTION": "Description", + "ACTION": "Action", + "VIEW_APP": "View app" + }, + "APP_DETAILS": { + "BACK_TO_APPS": "{0} to my apps", + "BACK": "Back" + } + }, "TRANSACTION_HISTORY": { "TITLE": "History", "TITLE_TRADES": "Trades History", diff --git a/web/src/config/menu.js b/web/src/config/menu.js index f2b2125d5b..1d6c719d72 100644 --- a/web/src/config/menu.js +++ b/web/src/config/menu.js @@ -92,6 +92,12 @@ export const MENU_ITEMS = { hide_from_appbar: true, hide_from_bottom_nav: true, }, + { + path: '/apps', + icon_id: 'TAB_APPS', + string_id: 'ACCOUNTS.TAB_APPS', + hide_from_bottom_nav: true, + }, ], bottom: [ { diff --git a/web/src/containers/AppDetails/index.js b/web/src/containers/AppDetails/index.js new file mode 100644 index 0000000000..692d18d72a --- /dev/null +++ b/web/src/containers/AppDetails/index.js @@ -0,0 +1,55 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { isMobile } from 'react-device-detect'; +import { openContactForm } from 'actions/appActions'; +import { IconTitle, HeaderSection, EditWrapper } from 'components'; +import STRINGS from 'config/localizedStrings'; +import withConfig from 'components/ConfigProvider/withConfig'; + +const Index = ({ openContactForm, icons: ICONS, router }) => { + const { + params: { app }, + } = router; + const goBack = () => router.push('/apps'); + + return ( + <div className="presentation_container apply_rtl settings_container"> + {!isMobile && ( + <IconTitle + stringId="ACCOUNTS.TAB_APPS" + text={STRINGS['ACCOUNTS.TAB_APPS']} + textType="title" + iconPath={ICONS['TAB_SETTING']} + iconId={STRINGS['ACCOUNTS.TAB_APPS']} + /> + )} + + <HeaderSection title={app} openContactForm={openContactForm}> + <div className="header-content"> + <div> + <EditWrapper stringId="USER_APPS.APP_DETAILS.BACK_TO_APPS,USER_APPS.APP_DETAILS.BACK"> + {STRINGS.formatString( + STRINGS['USER_APPS.APP_DETAILS.BACK_TO_APPS'], + <span + className="blue-link underline-text pointer" + onClick={goBack} + > + {STRINGS['USER_APPS.APP_DETAILS.BACK']} + </span> + )} + </EditWrapper> + </div> + </div> + </HeaderSection> + </div> + ); +}; + +const mapStateToProps = (state) => ({}); + +const mapDispatchToProps = (dispatch) => ({ + openContactForm: bindActionCreators(openContactForm, dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(withConfig(Index)); diff --git a/web/src/containers/Apps/All.js b/web/src/containers/Apps/All.js new file mode 100644 index 0000000000..17390d7489 --- /dev/null +++ b/web/src/containers/Apps/All.js @@ -0,0 +1,62 @@ +import React from 'react'; +import { IconTitle, EditWrapper, Table } from 'components'; +import STRINGS from 'config/localizedStrings'; +import withConfig from 'components/ConfigProvider/withConfig'; + +const data = [ + { + id: 0, + name: 'Name of the app', + description: 'App description short sentence here', + }, +]; + +const generateHeaders = () => { + return [ + { + stringId: 'USER_APPS.TABLE.APP_NAME', + label: STRINGS['USER_APPS.TABLE.APP_NAME'], + key: 'name', + }, + { + stringId: 'USER_APPS.TABLE.DESCRIPTION', + label: STRINGS['USER_APPS.TABLE.DESCRIPTION'], + key: 'description', + }, + { + label: '', + key: 'name', + }, + ]; +}; + +const All = ({ icons: ICONS }) => { + return ( + <div> + <div className="settings-form-wrapper"> + <div className="settings-form"> + <IconTitle + stringId="USER_APPS.ALL_APPS.TITLE" + text={STRINGS['USER_APPS.ALL_APPS.TITLE']} + textType="title" + iconPath={ICONS['APPS_ALL']} + /> + <div> + <EditWrapper stringId="USER_APPS.ALL_APPS.SUBTITLE"> + {STRINGS['USER_APPS.ALL_APPS.SUBTITLE']} + </EditWrapper> + </div> + <Table + rowClassName="pt-2 pb-2" + headers={generateHeaders()} + count={data.length} + data={data} + rowKey={({ id }) => id} + /> + </div> + </div> + </div> + ); +}; + +export default withConfig(All); diff --git a/web/src/containers/Apps/User.js b/web/src/containers/Apps/User.js new file mode 100644 index 0000000000..075faf8667 --- /dev/null +++ b/web/src/containers/Apps/User.js @@ -0,0 +1,75 @@ +import React from 'react'; +import { IconTitle, EditWrapper, Table } from 'components'; +import STRINGS from 'config/localizedStrings'; +import withConfig from 'components/ConfigProvider/withConfig'; +import { withRouter } from 'react-router'; + +const data = [ + { + id: 0, + name: 'Name of the app', + description: 'App description short sentence here', + }, +]; + +const generateHeaders = (goToDetails) => { + return [ + { + stringId: 'USER_APPS.TABLE.APP_NAME', + label: STRINGS['USER_APPS.TABLE.APP_NAME'], + key: 'name', + }, + { + stringId: 'USER_APPS.TABLE.DESCRIPTION', + label: STRINGS['USER_APPS.TABLE.DESCRIPTION'], + key: 'description', + }, + { + stringId: 'USER_APPS.TABLE.ACTION', + label: STRINGS['USER_APPS.TABLE.ACTION'], + renderCell: ({ id, name }, key) => ( + <td key={`${key}-${id}-app`}> + <span + className="blue-link underline-text pointer" + onClick={() => goToDetails(name)} + > + {STRINGS['USER_APPS.TABLE.VIEW_APP']} + </span> + </td> + ), + }, + ]; +}; + +const User = ({ icons: ICONS, router }) => { + const goToDetails = (name) => router.push(`apps/details/${name}`); + + return ( + <div> + <div className="settings-form-wrapper"> + <div className="settings-form"> + <IconTitle + stringId="USER_APPS.MY_APPS.TITLE" + text={STRINGS['USER_APPS.MY_APPS.TITLE']} + textType="title" + iconPath={ICONS['APPS_USER']} + /> + <div> + <EditWrapper stringId="USER_APPS.MY_APPS.SUBTITLE"> + {STRINGS['USER_APPS.MY_APPS.SUBTITLE']} + </EditWrapper> + </div> + <Table + rowClassName="pt-2 pb-2" + headers={generateHeaders(goToDetails)} + count={data.length} + data={data} + rowKey={({ id }) => id} + /> + </div> + </div> + </div> + ); +}; + +export default withRouter(withConfig(User)); diff --git a/web/src/containers/Apps/index.js b/web/src/containers/Apps/index.js new file mode 100644 index 0000000000..f3d16e8cc0 --- /dev/null +++ b/web/src/containers/Apps/index.js @@ -0,0 +1,114 @@ +import React, { useState, useEffect } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { isMobile } from 'react-device-detect'; +import STRINGS from 'config/localizedStrings'; +import withConfig from 'components/ConfigProvider/withConfig'; +import { + IconTitle, + HeaderSection, + EditWrapper, + CustomMobileTabs, + TabController, + MobileTabBar, +} from 'components'; +import { openContactForm } from 'actions/appActions'; +import All from './All'; +import User from './User'; + +const Index = ({ icons: ICONS, openContactForm }) => { + const [tabs, setTabs] = useState([]); + const [activeTab, setActiveTab] = useState(); + + useEffect(() => { + updateTabs(); + }, []); + + const updateTabs = () => { + const tabs = [ + { + title: isMobile ? ( + <CustomMobileTabs + title={STRINGS['USER_APPS.ALL_APPS.TAB_TITLE']} + // icon={ICONS['SETTING_NOTIFICATION_ICON']} + /> + ) : ( + <div>{STRINGS['USER_APPS.ALL_APPS.TAB_TITLE']}</div> + ), + content: <All />, + }, + { + title: isMobile ? ( + <CustomMobileTabs + title={STRINGS['USER_APPS.MY_APPS.TAB_TITLE']} + // icon={ICONS['SETTING_NOTIFICATION_ICON']} + /> + ) : ( + <div>{STRINGS['USER_APPS.MY_APPS.TAB_TITLE']}</div> + ), + content: <User />, + }, + ]; + setTabs(tabs); + }; + + const renderContent = (tabs, activeTab) => + tabs[activeTab] && tabs[activeTab].content ? ( + tabs[activeTab].content + ) : ( + <div /> + ); + + return ( + <div className="presentation_container apply_rtl settings_container"> + {!isMobile && ( + <IconTitle + stringId="ACCOUNTS.TAB_APPS" + text={STRINGS['ACCOUNTS.TAB_APPS']} + textType="title" + iconPath={ICONS['TAB_SETTING']} + iconId={STRINGS['ACCOUNTS.TAB_APPS']} + /> + )} + + <HeaderSection + stringId="USER_APPS.TITLE" + title={STRINGS['USER_APPS.TITLE']} + openContactForm={openContactForm} + > + <div className="header-content"> + <div> + <EditWrapper stringId="USER_APPS.SUBTITLE"> + {STRINGS['USER_APPS.SUBTITLE']} + </EditWrapper> + </div> + </div> + </HeaderSection> + + {!isMobile ? ( + <TabController + activeTab={activeTab} + setActiveTab={setActiveTab} + tabs={tabs} + /> + ) : ( + <MobileTabBar + activeTab={activeTab} + renderContent={renderContent} + setActiveTab={setActiveTab} + tabs={tabs} + /> + )} + + {isMobile ? <div className="my-4" /> : renderContent(tabs, activeTab)} + </div> + ); +}; + +const mapStateToProps = (state) => ({}); + +const mapDispatchToProps = (dispatch) => ({ + openContactForm: bindActionCreators(openContactForm, dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(withConfig(Index)); diff --git a/web/src/containers/index.js b/web/src/containers/index.js index 0d72f6f0d3..33cd4d5027 100644 --- a/web/src/containers/index.js +++ b/web/src/containers/index.js @@ -31,6 +31,8 @@ export { default as TermsOfService } from './TermsOfService'; export { default as DepositFunds } from './TermsOfService/DepositFunds'; export { default as Stake } from './Stake'; export { default as StakeDetails } from './StakeDetails'; +export { default as Apps } from './Apps'; +export { default as AppDetails } from './AppDetails'; // ADMIN PAGE export { default as AdminDashboard } from './Admin/Dashboard'; diff --git a/web/src/routes.js b/web/src/routes.js index ace566c1e8..9b7adbfd1e 100644 --- a/web/src/routes.js +++ b/web/src/routes.js @@ -28,6 +28,8 @@ import { AddTradeTabs, Stake, StakeDetails, + Apps, + AppDetails, // ADMIN User, AppWrapper as AdminContainer, @@ -348,6 +350,13 @@ export const generateRoutes = (routes = []) => { component={Account} onEnter={requireAuth} /> + <Route path="apps" name="Apps" component={Apps} onEnter={requireAuth} /> + <Route + path="apps/details/:app" + name="AppDetails" + component={AppDetails} + onEnter={requireAuth} + /> <Route path="summary" name="Summary" From 932e53dd7e2d8e7ca46a4a39d7ec77565ca466a3 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 14 Sep 2022 18:01:31 +0300 Subject: [PATCH 028/132] fixes --- web/src/components/AppBar/_AppBar.scss | 1 + .../AppMenuSidebar/_AppMenuSidebar.scss | 1 + .../components/CheckTitle/_CheckTitle.scss | 4 +++- .../components/Form/FormFields/ToggleField.js | 2 +- .../TabController/_TabController.scss | 1 + web/src/containers/App/_App.scss | 1 + .../TransactionsHistory/_Filters.scss | 6 +++++ .../UserSecurity/_UserSecurity.scss | 1 + web/src/index.css | 22 +++++++++++++------ web/src/index.scss | 10 +++------ 10 files changed, 33 insertions(+), 16 deletions(-) diff --git a/web/src/components/AppBar/_AppBar.scss b/web/src/components/AppBar/_AppBar.scss index a1571f7815..d0162b0a85 100644 --- a/web/src/components/AppBar/_AppBar.scss +++ b/web/src/components/AppBar/_AppBar.scss @@ -613,6 +613,7 @@ $app-menu-width: calc(100vw - 40rem); max-width: $app-menu-width; .app-menu-bar-content { + transition: 0.3s; color: $base_top-bar-navigation_text-inactive; cursor: pointer; margin: 0 15px; diff --git a/web/src/components/AppMenuSidebar/_AppMenuSidebar.scss b/web/src/components/AppMenuSidebar/_AppMenuSidebar.scss index 7f7f1fbfe8..e12a2a35cc 100644 --- a/web/src/components/AppMenuSidebar/_AppMenuSidebar.scss +++ b/web/src/components/AppMenuSidebar/_AppMenuSidebar.scss @@ -23,6 +23,7 @@ } } .app-menu-bar-side_list { + transition: 0.3s; height: 60px; padding: 18px 17px; cursor: pointer; diff --git a/web/src/components/CheckTitle/_CheckTitle.scss b/web/src/components/CheckTitle/_CheckTitle.scss index 2579c49488..decc67e13d 100644 --- a/web/src/components/CheckTitle/_CheckTitle.scss +++ b/web/src/components/CheckTitle/_CheckTitle.scss @@ -3,6 +3,7 @@ $custom-title-img-size: 6rem; $notification-size: 1.5rem; .check_title-container { + transition: 0.3s; display: flex; flex-direction: column; justify-content: center; @@ -17,7 +18,7 @@ $notification-size: 1.5rem; .custom_title-label { font-size: $font-size-subhead3; line-height: $font-size-subhead3; - padding: 0.25rem 0 1.5rem 0; + padding: 0.25rem 0 1.5rem 0; text-align: center; } @@ -227,6 +228,7 @@ $notification-size: 1.5rem; } .tab_item-active { + transition: 0.3s; border: 1px solid $colors-main-border; color: $tab-active !important; font-weight: bold; diff --git a/web/src/components/Form/FormFields/ToggleField.js b/web/src/components/Form/FormFields/ToggleField.js index ac435ec14c..843c43eb3b 100644 --- a/web/src/components/Form/FormFields/ToggleField.js +++ b/web/src/components/Form/FormFields/ToggleField.js @@ -79,7 +79,7 @@ class ToggleField extends Component { {...rest} > <div className="d-flex justify-content-between"> - <div>{label}</div> + <div className={selected ? '' : 'half-opacity'}>{label}</div> <Toggle selected={selected} options={options} diff --git a/web/src/components/TabController/_TabController.scss b/web/src/components/TabController/_TabController.scss index 98829e6b22..737ef0a1af 100644 --- a/web/src/components/TabController/_TabController.scss +++ b/web/src/components/TabController/_TabController.scss @@ -20,6 +20,7 @@ $tab_controller-item-min-width: 8rem; .layout-mobile { .tab_controller-tabs, .tab_item { + transition: 0.3s; font-size: $extra-thick--font-size !important; } } diff --git a/web/src/containers/App/_App.scss b/web/src/containers/App/_App.scss index 8213dfbd39..5f7683cc05 100644 --- a/web/src/containers/App/_App.scss +++ b/web/src/containers/App/_App.scss @@ -61,6 +61,7 @@ $inner_container-border: 1px solid $colors-super-pale-black; } .app_container-main { + transition: 0.3s; &.no_bottom_navigation { height: 100%; } diff --git a/web/src/containers/TransactionsHistory/_Filters.scss b/web/src/containers/TransactionsHistory/_Filters.scss index d064f470ab..6a6dbd6d46 100644 --- a/web/src/containers/TransactionsHistory/_Filters.scss +++ b/web/src/containers/TransactionsHistory/_Filters.scss @@ -20,6 +20,12 @@ } } + #trades-filters_size { + * { + transition: 0.2s; + } + } + .ant-radio-button-wrapper-checked::before, .ant-radio-button-wrapper::before { background-color: transparent !important; diff --git a/web/src/containers/UserSecurity/_UserSecurity.scss b/web/src/containers/UserSecurity/_UserSecurity.scss index 56d97d4b97..103238bb4d 100644 --- a/web/src/containers/UserSecurity/_UserSecurity.scss +++ b/web/src/containers/UserSecurity/_UserSecurity.scss @@ -329,6 +329,7 @@ $padding-input: 4rem; .user-security-wrapper { .tab_controller-tabs { .tab_item { + transition: 0.3s; font-size: 12px !important; } } diff --git a/web/src/index.css b/web/src/index.css index f9096c5139..ed47a4ddc4 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -397,8 +397,10 @@ table th { max-height: calc(100vh); max-width: 100vw; overflow-y: scroll; } - .app_container.layout-mobile .app_container-content .app_container-main.no_bottom_navigation { - height: 100%; } + .app_container.layout-mobile .app_container-content .app_container-main { + transition: .3s; } + .app_container.layout-mobile .app_container-content .app_container-main.no_bottom_navigation { + height: 100%; } .app_container.layout-mobile .content-with-bar { min-height: calc( 100vh - 17rem); max-height: calc( 100vh - 17rem); @@ -1058,6 +1060,7 @@ table th { .change-password-form-wrapper .change-password-form .input_field-input::placeholder { font-size: 12px; } .user-security-wrapper .tab_controller-tabs .tab_item { + transition: .3s; font-size: 12px !important; } } .edit_token.overlay { @@ -2002,6 +2005,9 @@ table th { .transaction-history-wrapper .ant-radio-button-wrapper:active, .transaction-history-wrapper .ant-radio-button-wrapper:hover { color: var(--labels_important-active-labels-text-graphics); } +.transaction-history-wrapper #trades-filters_size * { + transition: .2s; } + .transaction-history-wrapper .ant-radio-button-wrapper-checked::before, .transaction-history-wrapper .ant-radio-button-wrapper::before { background-color: transparent !important; } @@ -5514,6 +5520,7 @@ table th { .layout-mobile .tab_controller-tabs, .layout-mobile .tab_item { + transition: .3s; font-size: 1.5rem !important; } .tab_controller-tabs { @@ -6227,6 +6234,7 @@ table th { position: relative; max-width: calc(100vw - 40rem); } .app-menu-bar .app-menu-bar-content { + transition: .3s; color: var(--calculated_base_top-bar-navigation_text-inactive); cursor: pointer; margin: 0 15px; @@ -6902,6 +6910,7 @@ table th { font-size: 1.2rem !important; } } .check_title-container { + transition: .3s; display: flex; flex-direction: column; justify-content: center; @@ -7060,6 +7069,7 @@ table th { min-width: 10rem; } .custom-tab-wrapper .tab_item-active { + transition: .3s; border: 1px solid var(--calculated_important-border); color: var(--labels_important-active-labels-text-graphics) !important; font-weight: bold; } @@ -10765,6 +10775,7 @@ table th { width: 20px; } .app-side-bar .app-menu-bar-side_list { + transition: 0.3s; height: 60px; padding: 18px 17px; cursor: pointer; @@ -10982,11 +10993,8 @@ body { height: 100%; transition: all 0.3s; } -* { - transition: all 0.3s; } - * .presentation_container, - * .presentation_container * { - transition: none !important; } +.half-opacity { + opacity: .5; } .direction_rtl.apply_rtl { direction: rtl; } diff --git a/web/src/index.scss b/web/src/index.scss index 4df154d37b..12f4b90df6 100644 --- a/web/src/index.scss +++ b/web/src/index.scss @@ -50,15 +50,11 @@ body { // padding-top: 3rem; // } -.language_rtl { +.half-opacity { + opacity: 0.5; } -* { - transition: all 0.3s; - .presentation_container, - .presentation_container * { - transition: none !important; - } +.language_rtl { } .direction_rtl { From cd466511882b86a44aba5a8895c7a85a83ae932f Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 14 Sep 2022 18:01:45 +0300 Subject: [PATCH 029/132] index.css --- web/src/index.css | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/web/src/index.css b/web/src/index.css index ed47a4ddc4..3ecbd28f5a 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -398,7 +398,7 @@ table th { max-width: 100vw; overflow-y: scroll; } .app_container.layout-mobile .app_container-content .app_container-main { - transition: .3s; } + transition: 0.3s; } .app_container.layout-mobile .app_container-content .app_container-main.no_bottom_navigation { height: 100%; } .app_container.layout-mobile .content-with-bar { @@ -1060,7 +1060,7 @@ table th { .change-password-form-wrapper .change-password-form .input_field-input::placeholder { font-size: 12px; } .user-security-wrapper .tab_controller-tabs .tab_item { - transition: .3s; + transition: 0.3s; font-size: 12px !important; } } .edit_token.overlay { @@ -2006,7 +2006,7 @@ table th { color: var(--labels_important-active-labels-text-graphics); } .transaction-history-wrapper #trades-filters_size * { - transition: .2s; } + transition: 0.2s; } .transaction-history-wrapper .ant-radio-button-wrapper-checked::before, .transaction-history-wrapper .ant-radio-button-wrapper::before { @@ -5520,7 +5520,7 @@ table th { .layout-mobile .tab_controller-tabs, .layout-mobile .tab_item { - transition: .3s; + transition: 0.3s; font-size: 1.5rem !important; } .tab_controller-tabs { @@ -6234,7 +6234,7 @@ table th { position: relative; max-width: calc(100vw - 40rem); } .app-menu-bar .app-menu-bar-content { - transition: .3s; + transition: 0.3s; color: var(--calculated_base_top-bar-navigation_text-inactive); cursor: pointer; margin: 0 15px; @@ -6910,7 +6910,7 @@ table th { font-size: 1.2rem !important; } } .check_title-container { - transition: .3s; + transition: 0.3s; display: flex; flex-direction: column; justify-content: center; @@ -7069,7 +7069,7 @@ table th { min-width: 10rem; } .custom-tab-wrapper .tab_item-active { - transition: .3s; + transition: 0.3s; border: 1px solid var(--calculated_important-border); color: var(--labels_important-active-labels-text-graphics) !important; font-weight: bold; } @@ -10994,7 +10994,7 @@ body { transition: all 0.3s; } .half-opacity { - opacity: .5; } + opacity: 0.5; } .direction_rtl.apply_rtl { direction: rtl; } From 885c26f09de48c0e489d10b06f77787b510e510a Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 14 Sep 2022 18:16:09 +0300 Subject: [PATCH 030/132] hover bug fix --- web/src/_common.scss | 4 ++++ web/src/components/AppBar/_AppBar.scss | 3 +++ web/src/components/CheckTitle/_CheckTitle.scss | 5 +++++ web/src/containers/StakeDetails/_StakeDetails.scss | 4 ++++ web/src/containers/TradeTabs/_TradeTabs.scss | 5 ++++- web/src/index.css | 12 ++++++++++++ 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/web/src/_common.scss b/web/src/_common.scss index b3dd66723f..59a1bb4bf4 100644 --- a/web/src/_common.scss +++ b/web/src/_common.scss @@ -96,6 +96,10 @@ table th { color: $link !important; } +.blue-link:hover { + opacity: 0.5; +} + .arrow { border: solid grey; border-width: 0 3px 3px 0; diff --git a/web/src/components/AppBar/_AppBar.scss b/web/src/components/AppBar/_AppBar.scss index d0162b0a85..f2b3c1f2c5 100644 --- a/web/src/components/AppBar/_AppBar.scss +++ b/web/src/components/AppBar/_AppBar.scss @@ -628,6 +628,9 @@ $app-menu-width: calc(100vw - 40rem); .app-menu-bar-content-item { margin: auto; position: relative; + &:hover { + opacity: 0.5; + } } .app-menu-bar-icon { diff --git a/web/src/components/CheckTitle/_CheckTitle.scss b/web/src/components/CheckTitle/_CheckTitle.scss index decc67e13d..5f09395f30 100644 --- a/web/src/components/CheckTitle/_CheckTitle.scss +++ b/web/src/components/CheckTitle/_CheckTitle.scss @@ -217,6 +217,11 @@ $notification-size: 1.5rem; } } } +.tab_item-deactive { + &:hover { + opacity: 0.5; + } +} .custom-tab-wrapper { .tab_item { diff --git a/web/src/containers/StakeDetails/_StakeDetails.scss b/web/src/containers/StakeDetails/_StakeDetails.scss index 25f89046ab..5bdc9e969b 100644 --- a/web/src/containers/StakeDetails/_StakeDetails.scss +++ b/web/src/containers/StakeDetails/_StakeDetails.scss @@ -180,6 +180,10 @@ border-radius: 0.5rem; margin: 1rem 0.5rem; font-weight: bold; + + &:hover { + opacity: 0.8; + } } .chart_mine { diff --git a/web/src/containers/TradeTabs/_TradeTabs.scss b/web/src/containers/TradeTabs/_TradeTabs.scss index e397ed91af..697c35df42 100644 --- a/web/src/containers/TradeTabs/_TradeTabs.scss +++ b/web/src/containers/TradeTabs/_TradeTabs.scss @@ -35,7 +35,7 @@ $toggle-margin: 2px; .toggle-action_button { width: $toggle-size * 3 !important; &.left { - opacity: 1 !important; + opacity: 1 !important; .toggle-action_button-display { $right-pos: $toggle-size + $toggle-margin * 2; right: calc(100% - #{$right-pos}) !important; @@ -54,6 +54,9 @@ $toggle-margin: 2px; margin: 0.5rem 0; padding: 0 0.5rem; text-transform: uppercase; + &:hover { + opacity: 0.5; + } } .link-separator { diff --git a/web/src/index.css b/web/src/index.css index 3ecbd28f5a..3cf31646d2 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -165,6 +165,9 @@ table th { .blue-link { color: var(--specials_buttons-links-and-highlights) !important; } +.blue-link:hover { + opacity: .5; } + .arrow { border: solid grey; border-width: 0 3px 3px 0; @@ -4285,6 +4288,8 @@ table th { margin: 0.5rem 0; padding: 0 0.5rem; text-transform: uppercase; } + .trade_tabs-container .trade_tabs-link:hover { + opacity: .5; } .trade_tabs-container .link-separator { border-right: 1px solid var(--calculated_secondary-border); } .trade_tabs-container .trade_tabs-search-field { @@ -5334,6 +5339,8 @@ table th { border-radius: 0.5rem; margin: 1rem 0.5rem; font-weight: bold; } + .staking-account__connect:hover { + opacity: .8; } .chart_mine { fill-opacity: 0.7; @@ -6247,6 +6254,8 @@ table th { .app-menu-bar .app-menu-bar-content-item { margin: auto; position: relative; } + .app-menu-bar .app-menu-bar-content-item:hover { + opacity: .5; } .app-menu-bar .app-menu-bar-icon { margin-right: 0.4rem; width: 1rem; } @@ -7061,6 +7070,9 @@ table th { .header_title-wrapper .header_title-icon svg .verification-bank { fill: var(--labels_important-active-labels-text-graphics); } +.tab_item-deactive:hover { + opacity: .5; } + .custom-tab-wrapper .tab_item { color: var(--labels_secondary-inactive-label-text-graphics); height: 11.5rem; From 4eadc6105d5dda54ae5bf3cf94b42a815b55dbaf Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 14 Sep 2022 18:16:24 +0300 Subject: [PATCH 031/132] index.css --- web/src/index.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/src/index.css b/web/src/index.css index 3cf31646d2..b3261b60ab 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -166,7 +166,7 @@ table th { color: var(--specials_buttons-links-and-highlights) !important; } .blue-link:hover { - opacity: .5; } + opacity: 0.5; } .arrow { border: solid grey; @@ -4289,7 +4289,7 @@ table th { padding: 0 0.5rem; text-transform: uppercase; } .trade_tabs-container .trade_tabs-link:hover { - opacity: .5; } + opacity: 0.5; } .trade_tabs-container .link-separator { border-right: 1px solid var(--calculated_secondary-border); } .trade_tabs-container .trade_tabs-search-field { @@ -5340,7 +5340,7 @@ table th { margin: 1rem 0.5rem; font-weight: bold; } .staking-account__connect:hover { - opacity: .8; } + opacity: 0.8; } .chart_mine { fill-opacity: 0.7; @@ -6255,7 +6255,7 @@ table th { margin: auto; position: relative; } .app-menu-bar .app-menu-bar-content-item:hover { - opacity: .5; } + opacity: 0.5; } .app-menu-bar .app-menu-bar-icon { margin-right: 0.4rem; width: 1rem; } @@ -7071,7 +7071,7 @@ table th { fill: var(--labels_important-active-labels-text-graphics); } .tab_item-deactive:hover { - opacity: .5; } + opacity: 0.5; } .custom-tab-wrapper .tab_item { color: var(--labels_secondary-inactive-label-text-graphics); From 3875400192c8bf77bdac2c1d4542de7deb114835 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 14 Sep 2022 18:52:58 +0300 Subject: [PATCH 032/132] fix evenness of market selector --- web/src/components/AppBar/PairTabs.js | 1 - web/src/components/AppBar/_AppBar.scss | 1 + web/src/index.css | 3 ++- web/src/index.scss | 3 --- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/web/src/components/AppBar/PairTabs.js b/web/src/components/AppBar/PairTabs.js index 415246085c..db97ba2475 100644 --- a/web/src/components/AppBar/PairTabs.js +++ b/web/src/components/AppBar/PairTabs.js @@ -105,7 +105,6 @@ class PairTabs extends Component { 'app_bar-pair-content', 'd-flex', 'justify-content-between', - 'px-2', 'market-trigger', { 'active-tab-pair': location.pathname === '/markets', diff --git a/web/src/components/AppBar/_AppBar.scss b/web/src/components/AppBar/_AppBar.scss index f2b3c1f2c5..120fa509b7 100644 --- a/web/src/components/AppBar/_AppBar.scss +++ b/web/src/components/AppBar/_AppBar.scss @@ -1379,6 +1379,7 @@ $app-menu-width: calc(100vw - 40rem); &.ant-dropdown-trigger { height: 100% !important; width: 100% !important; + box-sizing: content-box; } } diff --git a/web/src/index.css b/web/src/index.css index b3261b60ab..e12dd08dc9 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -6867,7 +6867,8 @@ table th { .market-selector-dropdown.ant-dropdown-trigger { height: 100% !important; - width: 100% !important; } + width: 100% !important; + box-sizing: content-box; } .app-menu-bar-wrapper { flex-grow: 1; diff --git a/web/src/index.scss b/web/src/index.scss index 12f4b90df6..6deb084243 100644 --- a/web/src/index.scss +++ b/web/src/index.scss @@ -54,9 +54,6 @@ body { opacity: 0.5; } -.language_rtl { -} - .direction_rtl { &.apply_rtl { direction: rtl; From f46682ad38e28f927523c0496ad9563521a5fcab Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 14 Sep 2022 19:09:21 +0300 Subject: [PATCH 033/132] animation added into profile menu --- web/src/components/AppBar/MenuList.js | 24 +++++++++++++++++++++--- web/src/components/AppBar/_AppBar.scss | 7 +++++++ web/src/index.css | 4 ++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/web/src/components/AppBar/MenuList.js b/web/src/components/AppBar/MenuList.js index 08aa6ed00a..3026544f1b 100644 --- a/web/src/components/AppBar/MenuList.js +++ b/web/src/components/AppBar/MenuList.js @@ -8,6 +8,7 @@ import MenuListItem from './MenuListItem'; class MenuList extends Component { state = { isOpen: false, + startTransition: false, }; element = null; @@ -29,7 +30,19 @@ class MenuList extends Component { event.target !== this.element && this.element.contains(event.target) ) { - this.setState({ isOpen: !this.state.isOpen }); + this.setState({ isOpen: !this.state.isOpen }, () => { + if (this.state.isOpen) { + setTimeout(() => { + this.setState({ + startTransition: true, + }); + }, 50); + } else { + this.setState({ + startTransition: false, + }); + } + }); } }; @@ -70,7 +83,7 @@ class MenuList extends Component { activePath, onMenuChange, } = this.props; - const { isOpen } = this.state; + const { isOpen, startTransition } = this.state; const totalPending = securityPending + verificationPending; return ( <div @@ -92,7 +105,12 @@ class MenuList extends Component { </div> <div>{user.email}</div> {isOpen && ( - <div id="tab-account-menu" className="app-bar-account-menu apply_rtl"> + <div + id="tab-account-menu" + className={`${ + startTransition ? 'opacity-1 ' : ' ' + } app-bar-account-menu apply_rtl`} + > {menuItems.map( ( { path, icon_id, string_id, hide_from_menulist, activePaths }, diff --git a/web/src/components/AppBar/_AppBar.scss b/web/src/components/AppBar/_AppBar.scss index 120fa509b7..fef9a23cc7 100644 --- a/web/src/components/AppBar/_AppBar.scss +++ b/web/src/components/AppBar/_AppBar.scss @@ -332,7 +332,13 @@ $app-menu-width: calc(100vw - 40rem); font-weight: bold; } + .opacity-1 { + opacity: 1 !important; + } + .app-bar-account-menu { + transition: 0.9s; + opacity: 0; display: block; position: absolute; // right: 0; @@ -1365,6 +1371,7 @@ $app-menu-width: calc(100vw - 40rem); left: unset; } } + .app_bar { .app-bar-account-menu { .app-bar-account-list-icon { diff --git a/web/src/index.css b/web/src/index.css index e12dd08dc9..007ba8256e 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -6020,7 +6020,11 @@ table th { font-size: 0.8rem; line-height: 1; font-weight: bold; } + .app_bar .opacity-1 { + opacity: 1 !important; } .app_bar .app-bar-account-menu { + transition: .9s; + opacity: 0; display: block; position: absolute; top: 36px; From d0000d82e355ddfa3b6ec05ffff6353f6f24f902 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Fri, 16 Sep 2022 16:42:45 +0300 Subject: [PATCH 034/132] Onboarding provide consistent way to navigate back --- web/src/config/lang/en.json | 4 +- .../containers/RequestResetPassword/index.js | 20 +++++++ .../VerificationEmailRequest/index.js | 52 ++++++++++++------- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index 297a45964e..16c8285730 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -234,7 +234,9 @@ }, "VERIFICATION_EMAIL_REQUEST": { "TITLE": "Resend Email Request", - "BUTTON": "Request Email" + "BUTTON": "Request Email", + "SUBTITLE": "Make another email verification request below", + "SUPPORT": "Contact Support" }, "VERIFICATION_EMAIL_REQUEST_SUCCESS": { "TITLE": "Resent Email", diff --git a/web/src/containers/RequestResetPassword/index.js b/web/src/containers/RequestResetPassword/index.js index 9bb9af036c..cc42c206d1 100644 --- a/web/src/containers/RequestResetPassword/index.js +++ b/web/src/containers/RequestResetPassword/index.js @@ -3,6 +3,7 @@ import classnames from 'classnames'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { isMobile } from 'react-device-detect'; +import { Link } from 'react-router'; import { SubmissionError, change } from 'redux-form'; import { requestResetPassword } from 'actions/authAction'; @@ -17,6 +18,23 @@ import { openContactForm } from 'actions/appActions'; let errorTimeOut = null; +const BottomLink = () => ( + <> + <div className={classnames('f-1', 'link_wrapper')}> + {STRINGS['SIGN_UP.HAVE_ACCOUNT']} + <Link to="/login" className={classnames('blue-link')}> + {STRINGS['SIGN_UP.GOTO_LOGIN']} + </Link> + </div> + <div className={classnames('f-1', 'link_wrapper')}> + {STRINGS['LOGIN.NO_ACCOUNT']} + <Link to="/signup" className={classnames('blue-link')}> + {STRINGS['LOGIN.CREATE_ACCOUNT']} + </Link> + </div> + </> +); + class RequestResetPassword extends Component { constructor(props) { super(props); @@ -131,6 +149,7 @@ class RequestResetPassword extends Component { onSubmit={this.onSubmitRequestResetPassword} formFields={formFields} /> + {isMobile && <BottomLink />} </div> </div> )} @@ -149,6 +168,7 @@ class RequestResetPassword extends Component { onClose={this.onCloseDialog} /> </Dialog> + {!isMobile && <BottomLink />} </div> ); } diff --git a/web/src/containers/VerificationEmailRequest/index.js b/web/src/containers/VerificationEmailRequest/index.js index 7cdcc6231b..57795bd28e 100644 --- a/web/src/containers/VerificationEmailRequest/index.js +++ b/web/src/containers/VerificationEmailRequest/index.js @@ -16,12 +16,20 @@ import withConfig from 'components/ConfigProvider/withConfig'; import { openContactForm } from 'actions/appActions'; const BottomLink = () => ( - <div className={classnames('f-1', 'link_wrapper')}> - {STRINGS['VERIFICATION_EMAIL_REQUEST.NO_EMAIL']} - <Link to="/verify" className={classnames('blue-link')}> - {STRINGS['VERIFICATION_EMAIL_REQUEST.REQUEST_EMAIL']} - </Link> - </div> + <> + <div className={classnames('f-1', 'link_wrapper')}> + {STRINGS['SIGN_UP.HAVE_ACCOUNT']} + <Link to="/login" className={classnames('blue-link')}> + {STRINGS['SIGN_UP.GOTO_LOGIN']} + </Link> + </div> + <div className={classnames('f-1', 'link_wrapper')}> + {STRINGS['LOGIN.NO_ACCOUNT']} + <Link to="/signup" className={classnames('blue-link')}> + {STRINGS['LOGIN.CREATE_ACCOUNT']} + </Link> + </div> + </> ); class VerifyEmailRequest extends Component { @@ -113,8 +121,9 @@ class VerifyEmailRequest extends Component { 'login_container' )} > - {isMobile && <MobileBarBack onBackClick={this.onGoBack} />} - + {isMobile && !showContactForm && ( + <MobileBarBack onBackClick={this.onGoBack} /> + )} <div className={classnames( ...FLEX_CENTER_CLASSES, @@ -124,16 +133,6 @@ class VerifyEmailRequest extends Component { 'w-100' )} > - <IconTitle - iconId="EXCHANGE_LOGO" - iconPath={ICONS['EXCHANGE_LOGO']} - stringId="VERIFICATION_EMAIL_REQUEST.TITLE" - text={STRINGS['VERIFICATION_EMAIL_REQUEST.TITLE']} - textType="title" - underline={true} - imageWrapperClassName="auth_logo-wrapper" - className="w-100 holla-logo" - /> <div className={classnames( ...FLEX_CENTER_CLASSES, @@ -143,6 +142,23 @@ class VerifyEmailRequest extends Component { 'w-100' )} > + <IconTitle + iconId="EXCHANGE_LOGO" + iconPath={ICONS['EXCHANGE_LOGO']} + stringId="VERIFICATION_EMAIL_REQUEST.TITLE" + text={STRINGS['VERIFICATION_EMAIL_REQUEST.TITLE']} + textType="title" + underline={true} + imageWrapperClassName="auth_logo-wrapper" + className="w-100 holla-logo" + subtitle={STRINGS['VERIFICATION_EMAIL_REQUEST.SUBTITLE']} + actionProps={{ + text: STRINGS['VERIFICATION_EMAIL_REQUEST.SUPPORT'], + iconPath: ICONS['BLUE_QUESTION'], + onClick: openContactForm, + useSvg: true, + }} + /> <EmailRequestForm onSubmit={this.onSubmitEmailRequest} formFields={formFields} From c4d2aed6b926d7f39c11fbe302b28f6bb32ee6c2 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Fri, 16 Sep 2022 16:50:59 +0300 Subject: [PATCH 035/132] Onboarding, add way to navigate to login or signup --- web/src/containers/Signup/SignupSuccess.js | 65 ++++++++++++++++------ 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/web/src/containers/Signup/SignupSuccess.js b/web/src/containers/Signup/SignupSuccess.js index 44a30d4de0..529f63d3cd 100644 --- a/web/src/containers/Signup/SignupSuccess.js +++ b/web/src/containers/Signup/SignupSuccess.js @@ -3,28 +3,57 @@ import { Link } from 'react-router'; import { IconTitle } from 'components'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; +import classnames from 'classnames'; +import { FLEX_CENTER_CLASSES } from 'config/constants'; + +const BottomLink = () => ( + <> + <div className={classnames('f-1', 'link_wrapper')}> + {STRINGS['SIGN_UP.HAVE_ACCOUNT']} + <Link to="/login" className={classnames('blue-link')}> + {STRINGS['SIGN_UP.GOTO_LOGIN']} + </Link> + </div> + <div className={classnames('f-1', 'link_wrapper')}> + {STRINGS['LOGIN.NO_ACCOUNT']} + <Link to="/signup" className={classnames('blue-link')}> + {STRINGS['LOGIN.CREATE_ACCOUNT']} + </Link> + </div> + </> +); const SignupSuccess = ({ icons: ICONS, ...rest }) => { return ( - <div className="signup_success-wrapper d-flex justify-content-center align-items-center flex-column auth_wrapper"> - <IconTitle - iconId="CHECK" - iconPath={ICONS['CHECK']} - stringId="VERIFICATION_TEXTS.TITLE" - text={STRINGS['VERIFICATION_TEXTS.TITLE']} - textType="title" - className="w-100" - /> - <div className="signup_success-content"> - <p>{STRINGS['VERIFICATION_TEXTS.TEXT_1']}</p> - <p>{STRINGS['VERIFICATION_TEXTS.TEXT_2']}</p> - </div> - <div> - {STRINGS['SIGN_UP.NO_EMAIL']} - <Link to="/verify" className="blue-link"> - {STRINGS['SIGN_UP.REQUEST_EMAIL']} - </Link> + <div + className={classnames( + ...FLEX_CENTER_CLASSES, + 'flex-column', + 'f-1', + 'login_container' + )} + > + <div className="signup_success-wrapper d-flex justify-content-center align-items-center flex-column auth_wrapper"> + <IconTitle + iconId="CHECK" + iconPath={ICONS['CHECK']} + stringId="VERIFICATION_TEXTS.TITLE" + text={STRINGS['VERIFICATION_TEXTS.TITLE']} + textType="title" + className="w-100" + /> + <div className="signup_success-content"> + <p>{STRINGS['VERIFICATION_TEXTS.TEXT_1']}</p> + <p>{STRINGS['VERIFICATION_TEXTS.TEXT_2']}</p> + </div> + <div> + {STRINGS['SIGN_UP.NO_EMAIL']} + <Link to="/verify" className="blue-link"> + {STRINGS['SIGN_UP.REQUEST_EMAIL']} + </Link> + </div> </div> + <BottomLink /> </div> ); }; From d349597007e196ce59c28d84e839f318cccd59a6 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Fri, 16 Sep 2022 16:58:13 +0300 Subject: [PATCH 036/132] After resetting password --- web/src/containers/ResetPassword/ResetPasswordSuccess.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/containers/ResetPassword/ResetPasswordSuccess.js b/web/src/containers/ResetPassword/ResetPasswordSuccess.js index 19df63f5e5..966a750e39 100644 --- a/web/src/containers/ResetPassword/ResetPasswordSuccess.js +++ b/web/src/containers/ResetPassword/ResetPasswordSuccess.js @@ -31,7 +31,7 @@ const ResetPasswordSuccess = ({ {STRINGS['RESET_PASSWORD_SUCCESS.TEXT_1']} </div> )} - <Button label={label} onClick={onClick} className="button-margin" /> + <Button label={label} onClick={onClick} className="button-margin mt-5" /> </div> ); }; From b32ed70e821cd2f6dae129ae92ab43265edc1136 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Fri, 16 Sep 2022 17:18:51 +0300 Subject: [PATCH 037/132] Deposit and withdraws change with diagonal arrows --- web/public/assets/images/blue-deposit-icon.svg | 18 ++++++++++++++++++ web/public/assets/images/blue_withrow_icon.svg | 16 ++++++++++++++++ web/src/config/icons/dark.js | 2 ++ web/src/config/icons/light.js | 2 ++ web/src/containers/Wallet/AssetsBlock.js | 4 ++-- 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 web/public/assets/images/blue-deposit-icon.svg create mode 100644 web/public/assets/images/blue_withrow_icon.svg diff --git a/web/public/assets/images/blue-deposit-icon.svg b/web/public/assets/images/blue-deposit-icon.svg new file mode 100644 index 0000000000..669efbec69 --- /dev/null +++ b/web/public/assets/images/blue-deposit-icon.svg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve"> +<style type="text/css"> + .icon-plus-1{fill:#0000FF;} + .st0{fill:#002BFF;} + .st1{fill:#FFFFFF;} +</style> +<circle id="XMLID_134_" class="icon-plus-1" cx="25" cy="25" r="25"/> +<path id="Path_3_00000016764327731827108580000010424901856094027934_" class="st1" d="M14.7,38.7c-0.9,0.9-2.3,0.9-3.2,0 + c-0.4-0.4-0.6-1-0.6-1.6l0-19.8c0-1.3,1-2.3,2.3-2.2s2.3,1,2.2,2.3l0,19.8C15.4,37.7,15.2,38.2,14.7,38.7L14.7,38.7z"/> +<path id="Path_3_00000047778029988004519910000017533062724939776692_" class="st1" d="M11.5,38.6c-0.9-0.9-0.9-2.3,0-3.2 + c0.4-0.4,1-0.6,1.6-0.6l19.8,0c1.3,0,2.3,1,2.2,2.3s-1,2.3-2.3,2.2l-19.8,0C12.5,39.3,11.9,39,11.5,38.6L11.5,38.6z"/> +<path id="Path_3_00000010272100705195573310000003847338887338401679_" class="st1" d="M13.1,39.3c-1.3,0-2.3-1.1-2.2-2.3 + c0-0.6,0.3-1.2,0.7-1.6l23.7-23.8c0.9-0.9,2.3-0.9,3.2,0c0.9,0.9,0.9,2.3,0,3.2L14.7,38.6C14.3,39.1,13.7,39.3,13.1,39.3L13.1,39.3z + "/> +</svg> diff --git a/web/public/assets/images/blue_withrow_icon.svg b/web/public/assets/images/blue_withrow_icon.svg new file mode 100644 index 0000000000..a7171332e5 --- /dev/null +++ b/web/public/assets/images/blue_withrow_icon.svg @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve"> + <style type="text/css"> + .icon-plus-1{fill:#0000FF;} + .st0{fill:#002BFF;} + .st1{fill:#FFFFFF;} +</style> + <circle id="XMLID_134_" class="icon-plus-1" cx="25" cy="25" r="25" /> + <path id="Path_3_00000016764327731827108580000010424901856094027934_" class="st1" d="M35.3,11.6c0.9-0.9,2.3-0.9,3.2,0 + c0.4,0.4,0.6,1,0.6,1.6l0,19.8c0,1.3-1,2.3-2.3,2.2s-2.3-1-2.2-2.3l0-19.8C34.6,12.6,34.8,12.1,35.3,11.6L35.3,11.6z" /> + <path id="Path_3_00000047778029988004519910000017533062724939776692_" class="st1" d="M38.5,11.7c0.9,0.9,0.9,2.3,0,3.2 + c-0.4,0.4-1,0.6-1.6,0.6l-19.8,0c-1.3,0-2.3-1-2.2-2.3c0-1.3,1-2.3,2.3-2.2l19.8,0C37.5,11,38.1,11.2,38.5,11.7L38.5,11.7z" /> + <path id="Path_3_00000010272100705195573310000003847338887338401679_" class="st1" d="M36.9,11c1.3,0,2.3,1.1,2.2,2.3 + c0,0.6-0.3,1.2-0.7,1.6L14.7,38.7c-0.9,0.9-2.3,0.9-3.2,0c-0.9-0.9-0.9-2.3,0-3.2l23.7-23.7C35.7,11.2,36.3,11,36.9,11L36.9,11z" /> +</svg> \ No newline at end of file diff --git a/web/src/config/icons/dark.js b/web/src/config/icons/dark.js index 080620d4b1..c3613bd335 100644 --- a/web/src/config/icons/dark.js +++ b/web/src/config/icons/dark.js @@ -182,6 +182,8 @@ const nestedIcons = { SESSION_TIMED_OUT: '/assets/images/session-timed-out.svg', BLUE_EDIT: '/assets/images/blue-edit-exir-icon.svg', BLUE_PLUS: '/assets/images/max-plus-blue-icon.svg', + BLUE_DEPOSIT_ICON: '/assets/images/blue-deposit-icon.svg', + BLUE_WITHROW_ICON: '/assets/images/blue_withrow_icon.svg', BLUE_TIMER: '/assets/images/timer-icon.svg', NOTIFICATION_VERIFICATION_WARNING: '/assets/images/verification.svg', diff --git a/web/src/config/icons/light.js b/web/src/config/icons/light.js index 3a3b766872..63ab53b150 100644 --- a/web/src/config/icons/light.js +++ b/web/src/config/icons/light.js @@ -167,6 +167,8 @@ const nestedIcons = { SESSION_TIMED_OUT: '/assets/images/session-timed-out.svg', BLUE_EDIT: '/assets/images/blue-edit-exir-icon.svg', BLUE_PLUS: '/assets/images/max-plus-blue-icon.svg', + BLUE_DEPOSIT_ICON: '/assets/images/blue-deposit-icon.svg', + BLUE_WITHROW_ICON: '/assets/images/blue_withrow_icon.svg', BLUE_TIMER: '/assets/images/timer-icon.svg', NOTIFICATION_VERIFICATION_WARNING: '/assets/images/verification.svg', diff --git a/web/src/containers/Wallet/AssetsBlock.js b/web/src/containers/Wallet/AssetsBlock.js index 9b99c555b6..9b3a22d0be 100644 --- a/web/src/containers/Wallet/AssetsBlock.js +++ b/web/src/containers/Wallet/AssetsBlock.js @@ -301,7 +301,7 @@ const AssetsBlock = ({ stringId="WALLET_BUTTON_BASE_DEPOSIT" text={STRINGS['WALLET_BUTTON_BASE_DEPOSIT']} iconId="BLUE_PLUS" - iconPath={ICONS['BLUE_PLUS']} + iconPath={ICONS['BLUE_DEPOSIT_ICON']} onClick={() => navigate(`wallet/${key}/deposit`)} className="csv-action action-button-wrapper" showActionText={isMobile} @@ -311,7 +311,7 @@ const AssetsBlock = ({ stringId="WALLET_BUTTON_BASE_WITHDRAW" text={STRINGS['WALLET_BUTTON_BASE_WITHDRAW']} iconId="BLUE_PLUS" - iconPath={ICONS['BLUE_PLUS']} + iconPath={ICONS['BLUE_WITHROW_ICON']} onClick={() => navigate(`wallet/${key}/withdraw`)} className="csv-action action-button-wrapper" showActionText={isMobile} From 1ac2f4de282a4370093abe739c58f5f4e799a5a8 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Fri, 16 Sep 2022 17:48:47 +0300 Subject: [PATCH 038/132] History icon --- web/public/assets/images/clock.svg | 12 ++++++++++++ web/src/config/icons/static.js | 1 + web/src/containers/Wallet/MainWallet.js | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 web/public/assets/images/clock.svg diff --git a/web/public/assets/images/clock.svg b/web/public/assets/images/clock.svg new file mode 100644 index 0000000000..ed49f1a34a --- /dev/null +++ b/web/public/assets/images/clock.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#FFFFFF;} +</style> +<path id="Path_3_00000049195749705309616170000002146436606352155561_" class="st0" d="M23.2,26.8c-0.9-0.9-0.9-2.3,0-3.2 + c0.4-0.4,1-0.7,1.6-0.7l18.5-0.1c1.3,0,2.3,1,2.3,2.3c0,1.3-1,2.3-2.3,2.3l-18.5,0.1C24.2,27.5,23.6,27.2,23.2,26.8L23.2,26.8z"/> +<path id="Path_3_00000141440866148849312550000016656160126446402465_" class="st0" d="M26.3,26.8c-0.9,0.9-2.3,0.9-3.2,0 + c-0.4-0.4-0.7-1-0.7-1.6l-0.2-13.8c0-1.3,1-2.3,2.3-2.3s2.3,1,2.3,2.3L27,25.1C27,25.7,26.7,26.3,26.3,26.8L26.3,26.8z"/> +</svg> diff --git a/web/src/config/icons/static.js b/web/src/config/icons/static.js index d3166c01d8..2ddc6e8dfb 100644 --- a/web/src/config/icons/static.js +++ b/web/src/config/icons/static.js @@ -144,6 +144,7 @@ const icons = { FIAT_PLUGIN: '/assets/images/fiat-plugin.svg', DOLLAR_GEAR: '/assets/images/single-dollar-gear.svg', SWITCH_ASSET_FOR_FEES: '/assets/images/switch-asset-for-fees.svg', + CLOCK: '/assets/images/clock.svg', }; export default icons; diff --git a/web/src/containers/Wallet/MainWallet.js b/web/src/containers/Wallet/MainWallet.js index e02e97f302..11ea2b6971 100644 --- a/web/src/containers/Wallet/MainWallet.js +++ b/web/src/containers/Wallet/MainWallet.js @@ -182,7 +182,7 @@ class Wallet extends Component { text: STRINGS['TRADE_HISTORY'], status: 'information', iconId: 'PAPER_CLIP', - iconPath: STATIC_ICONS['PAPER_CLIP'], + iconPath: STATIC_ICONS['CLOCK'], allowClick: true, className: isOpen ? 'paper-clip-icon' From c91987d644cece10c7b0c4e26f329c89ed219e52 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Fri, 16 Sep 2022 17:57:42 +0300 Subject: [PATCH 039/132] TRADE and EARN icon --- web/public/assets/images/earn.svg | 20 ++++++++++++++++++++ web/public/assets/images/trade.svg | 14 ++++++++++++++ web/src/config/icons/dark.js | 2 ++ web/src/config/icons/light.js | 2 ++ web/src/containers/Wallet/AssetsBlock.js | 8 ++++---- 5 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 web/public/assets/images/earn.svg create mode 100644 web/public/assets/images/trade.svg diff --git a/web/public/assets/images/earn.svg b/web/public/assets/images/earn.svg new file mode 100644 index 0000000000..2b4bdf18b6 --- /dev/null +++ b/web/public/assets/images/earn.svg @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#FFFFFF;} + .icon-plus-1{fill:#0000FF;} +</style> +<circle id="XMLID_134_" class="icon-plus-1" cx="25" cy="25" r="25"/> +<g id="Group_1" transform="translate(-1288.505 -307.413)"> + <path id="Path_3" class="st0" d="M1296.1,352.1c-1.3,0-2.3-1-2.3-2.3c0-0.6,0.2-1.2,0.7-1.6l35-35c0.9-0.9,2.3-0.9,3.2,0 + c0.9,0.9,0.9,2.3,0,3.2l-35,35C1297.3,351.9,1296.7,352.1,1296.1,352.1L1296.1,352.1z"/> + <path id="Path_4" class="st0" d="M1301.4,328.7c-4.8,0-8.6-3.9-8.6-8.6c0-4.8,3.9-8.6,8.6-8.6c4.8,0,8.6,3.9,8.6,8.6 + c0,2.3-0.9,4.5-2.5,6.1C1305.9,327.8,1303.7,328.7,1301.4,328.7z M1301.4,316c-2.2,0-4.1,1.8-4.1,4.1c0,2.2,1.8,4.1,4.1,4.1 + c2.2,0,4.1-1.8,4.1-4.1c0-1.1-0.4-2.1-1.2-2.9C1303.5,316.4,1302.5,316,1301.4,316z"/> + <path id="Path_5" class="st0" d="M1325.9,353.2c-2.3,0-4.5-0.9-6.1-2.5l0,0c-3.4-3.4-3.4-8.8,0-12.2c3.4-3.4,8.8-3.4,12.2,0 + c3.4,3.4,3.4,8.8,0,12.2C1330.4,352.3,1328.2,353.2,1325.9,353.2z M1323,347.5c1.6,1.6,4.2,1.6,5.7,0c1.6-1.6,1.6-4.2,0-5.7 + c-1.6-1.6-4.2-1.6-5.7,0c-1.7,1.5-1.9,3.9-0.4,5.6C1322.7,347.4,1322.8,347.5,1323,347.5L1323,347.5z"/> +</g> +</svg> diff --git a/web/public/assets/images/trade.svg b/web/public/assets/images/trade.svg new file mode 100644 index 0000000000..a3dfa468a1 --- /dev/null +++ b/web/public/assets/images/trade.svg @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#FFFFFF;} + .icon-plus-1{fill:#0000FF;} +</style> +<circle id="XMLID_134_" class="icon-plus-1" cx="25" cy="25" r="25"/> +<path id="Union_1" class="st0" d="M4.9,36.6c-0.8-1.1-0.8-2.5,0-3.6l11.1-14.3c0.3-0.5,0.9-0.8,1.5-0.7c0.6-0.1,1.2,0.2,1.6,0.6 + l9.4,10.2l13.4-16.2c0.7-1,2.1-1.2,3-0.4c0.2,0.1,0.3,0.3,0.4,0.4c1,1.2,1,3,0,4.2l-14,16.9c-0.4,0.5-1.1,0.9-1.7,0.9 + c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0-0.3,0c-0.5,0-1.1-0.2-1.4-0.6l-9.8-10.6L7.6,36.6c-0.3,0.4-0.8,0.7-1.4,0.7 + C5.7,37.3,5.2,37,4.9,36.6z"/> +</svg> diff --git a/web/src/config/icons/dark.js b/web/src/config/icons/dark.js index c3613bd335..7b6ad2c879 100644 --- a/web/src/config/icons/dark.js +++ b/web/src/config/icons/dark.js @@ -181,6 +181,8 @@ const nestedIcons = { BLUE_ARROW_RIGHT: '/assets/images/blue-arrow-right.svg', SESSION_TIMED_OUT: '/assets/images/session-timed-out.svg', BLUE_EDIT: '/assets/images/blue-edit-exir-icon.svg', + BLUE_TRADE_ICON: '/assets/images/trade.svg', + BLUE_EARN_ICON: '/assets/images/earn.svg', BLUE_PLUS: '/assets/images/max-plus-blue-icon.svg', BLUE_DEPOSIT_ICON: '/assets/images/blue-deposit-icon.svg', BLUE_WITHROW_ICON: '/assets/images/blue_withrow_icon.svg', diff --git a/web/src/config/icons/light.js b/web/src/config/icons/light.js index 63ab53b150..754d950158 100644 --- a/web/src/config/icons/light.js +++ b/web/src/config/icons/light.js @@ -168,6 +168,8 @@ const nestedIcons = { BLUE_EDIT: '/assets/images/blue-edit-exir-icon.svg', BLUE_PLUS: '/assets/images/max-plus-blue-icon.svg', BLUE_DEPOSIT_ICON: '/assets/images/blue-deposit-icon.svg', + BLUE_TRADE_ICON: '/assets/images/trade.svg', + BLUE_EARN_ICON: '/assets/images/earn.svg', BLUE_WITHROW_ICON: '/assets/images/blue_withrow_icon.svg', BLUE_TIMER: '/assets/images/timer-icon.svg', diff --git a/web/src/containers/Wallet/AssetsBlock.js b/web/src/containers/Wallet/AssetsBlock.js index 9b3a22d0be..560d6e9cc7 100644 --- a/web/src/containers/Wallet/AssetsBlock.js +++ b/web/src/containers/Wallet/AssetsBlock.js @@ -324,8 +324,8 @@ const AssetsBlock = ({ <ActionNotification stringId="TRADE_TAB_TRADE" text={STRINGS['TRADE_TAB_TRADE']} - iconId="BLUE_PLUS" - iconPath={ICONS['BLUE_PLUS']} + iconId="BLUE_TRADE_ICON" + iconPath={ICONS['BLUE_TRADE_ICON']} onClick={() => goToTrade(pair)} className="csv-action" showActionText={isMobile} @@ -338,8 +338,8 @@ const AssetsBlock = ({ <ActionNotification stringId="STAKE.EARN" text={STRINGS['STAKE.EARN']} - iconId="BLUE_PLUS" - iconPath={ICONS['BLUE_PLUS']} + iconId="BLUE_EARN_ICON" + iconPath={ICONS['BLUE_EARN_ICON']} onClick={() => navigate('/stake')} className="csv-action" showActionText={isMobile} From 59ed42c6a44da78625581c23c51ffcb8dffcb823 Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Fri, 16 Sep 2022 20:33:02 +0530 Subject: [PATCH 040/132] Changes for the common button component with mentioned behaviours --- web/src/components/AdminForm/hoc.js | 12 +++---- web/src/components/FormButton/Button.js | 31 +++++++++++++++++++ .../Admin/General/EmailVerificationForm.js | 13 ++++---- .../containers/Admin/General/FooterForm.js | 15 +++++---- .../containers/Admin/General/InterfaceForm.js | 10 ++++-- .../Admin/General/PublishSection.js | 16 +++++----- .../Admin/Settings/CustomizeEmailForm.js | 10 +++--- 7 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 web/src/components/FormButton/Button.js diff --git a/web/src/components/AdminForm/hoc.js b/web/src/components/AdminForm/hoc.js index d82451e089..b31de35f46 100644 --- a/web/src/components/AdminForm/hoc.js +++ b/web/src/components/AdminForm/hoc.js @@ -3,6 +3,7 @@ import renderFields from './utils'; import { reduxForm, reset, getFormValues } from 'redux-form'; import { Button } from 'antd'; import { connect } from 'react-redux'; +import FormButton from 'components/FormButton/Button'; const Form = (name, className = '', allowPristine = false) => { const HocForm = ({ @@ -49,9 +50,9 @@ const Form = (name, className = '', allowPristine = false) => { {secondaryBtnTxt} </Button> ) : null} - <Button + <FormButton type={buttonType ? buttonType : 'primary'} - onClick={handleSubmit(onSubmit)} + handleSubmit={handleSubmit(onSubmit)} disabled={ disableAllFields || (allowPristine ? false : fields && pristine) || @@ -63,9 +64,8 @@ const Form = (name, className = '', allowPristine = false) => { size={small ? 'small' : 'large'} className={small ? `${buttonClass}` : `w-100 ${buttonClass}`} style={small ? { float: 'right' } : null} - > - {buttonText} - </Button> + buttonText={buttonText} + /> </form> ); }; @@ -78,7 +78,7 @@ const Form = (name, className = '', allowPristine = false) => { })(HocForm); const mapStateToProps = (state) => ({ - formValues: getFormValues(name)(state) + formValues: getFormValues(name)(state), }); return connect(mapStateToProps)(CommonHocForm); }; diff --git a/web/src/components/FormButton/Button.js b/web/src/components/FormButton/Button.js new file mode 100644 index 0000000000..871e9988e1 --- /dev/null +++ b/web/src/components/FormButton/Button.js @@ -0,0 +1,31 @@ +import React from 'react'; +import { Button } from 'antd'; + +const FormButton = ({ + type = 'primary', + handleSubmit = () => {}, + disabled = true, + size, + className = '', + style = null, + buttonText = '', + htmlType = '', +}) => { + return ( + <div> + <Button + type={type} + onClick={handleSubmit} + disabled={disabled} + size={size} + className={className} + style={style} + htmlType={htmlType} + > + {buttonText} + </Button> + </div> + ); +}; + +export default FormButton; diff --git a/web/src/containers/Admin/General/EmailVerificationForm.js b/web/src/containers/Admin/General/EmailVerificationForm.js index c7be2202ec..ceddd9960e 100644 --- a/web/src/containers/Admin/General/EmailVerificationForm.js +++ b/web/src/containers/Admin/General/EmailVerificationForm.js @@ -1,7 +1,9 @@ import React, { useEffect, useState } from 'react'; -import { Button, Checkbox, Form } from 'antd'; +import { Checkbox, Form } from 'antd'; import { InfoCircleFilled } from '@ant-design/icons'; +import FormButton from 'components/FormButton/Button'; + const { Item } = Form; const EmailVerificationForm = ({ @@ -71,14 +73,13 @@ const EmailVerificationForm = ({ </div> </div> <div> - <Button + <FormButton type="primary" htmlType="submit" - className="green-btn minimal-btn" disabled={isDisable || buttonSubmitting} - > - Save - </Button> + className="green-btn minimal-btn" + buttonText="Save" + /> </div> </Form> </div> diff --git a/web/src/containers/Admin/General/FooterForm.js b/web/src/containers/Admin/General/FooterForm.js index a5ad37b2b3..9bafde762a 100644 --- a/web/src/containers/Admin/General/FooterForm.js +++ b/web/src/containers/Admin/General/FooterForm.js @@ -5,9 +5,10 @@ import { Button } from 'antd'; import _findLast from 'lodash/findLast'; import _findLastKey from 'lodash/findLastKey'; import isEqual from 'lodash.isequal'; +import debounce from 'lodash.debounce'; import renderFields from '../../../components/AdminForm/utils'; -import debounce from 'lodash.debounce'; +import FormButton from 'components/FormButton/Button'; class FormWrapper extends Component { componentDidMount() { @@ -114,16 +115,14 @@ class FormWrapper extends Component { {customFields ? this.renderCustomFields(fields) : renderFields(fields, getFieldDecorator, initialValues)} - <Button - block + <FormButton type="primary" + handleSubmit={handleSubmit(this.onSubmit)} htmlType="submit" - className="green-btn minimal-btn" - onClick={handleSubmit(this.onSubmit)} disabled={buttonSubmitting} - > - {buttonTxt} - </Button> + className="green-btn minimal-btn" + buttonText={buttonTxt} + /> </form> </div> ); diff --git a/web/src/containers/Admin/General/InterfaceForm.js b/web/src/containers/Admin/General/InterfaceForm.js index 46832cad84..e51fddcfb7 100644 --- a/web/src/containers/Admin/General/InterfaceForm.js +++ b/web/src/containers/Admin/General/InterfaceForm.js @@ -5,6 +5,7 @@ import classnames from 'classnames'; import _isEqual from 'lodash/isEqual'; import { STATIC_ICONS } from 'config/icons'; +import FormButton from 'components/FormButton/Button'; const { Item } = Form; @@ -264,9 +265,12 @@ const InterfaceForm = ({ </div> ) : null} <div> - <Button type="primary" htmlType="submit" disabled={isSubmit}> - Save - </Button> + <FormButton + type="primary" + htmlType="submit" + disabled={isSubmit} + buttonText="save" + /> </div> </Form> </div> diff --git a/web/src/containers/Admin/General/PublishSection.js b/web/src/containers/Admin/General/PublishSection.js index ebb2306292..92be29c659 100644 --- a/web/src/containers/Admin/General/PublishSection.js +++ b/web/src/containers/Admin/General/PublishSection.js @@ -1,5 +1,7 @@ import React from 'react'; -import { Button, Collapse } from 'antd'; +import { Collapse } from 'antd'; + +import FormButton from 'components/FormButton/Button'; const PublishSection = ({ title = '', @@ -54,17 +56,15 @@ const PublishSection = ({ ) : null} </Collapse> </div> - <Button + <FormButton type="primary" - className="green-btn minimal-btn" - loading={loadingButton && currentPublishType === currentkey} - onClick={() => handlePublish(currentkey)} + handleSubmit={() => handlePublish(currentkey)} disabled={ !isPublishDisable || (isPublishDisable && updatedKey !== currentkey) } - > - Publish - </Button> + className="green-btn minimal-btn" + buttonText="Publish" + /> </div> ); }; diff --git a/web/src/containers/Admin/Settings/CustomizeEmailForm.js b/web/src/containers/Admin/Settings/CustomizeEmailForm.js index 712a923835..d71792a2d2 100644 --- a/web/src/containers/Admin/Settings/CustomizeEmailForm.js +++ b/web/src/containers/Admin/Settings/CustomizeEmailForm.js @@ -7,6 +7,7 @@ import debounce from 'lodash.debounce'; import { updateEmailStrings } from '../General/action'; import { STATIC_ICONS } from 'config/icons'; +import FormButton from 'components/FormButton/Button'; const { TextArea } = Input; const { Option } = Select; @@ -383,14 +384,13 @@ const CustomizeEmailForm = ({ )} </div> </div> - <Button + <FormButton type="primary" - className="green-btn" htmlType="submit" disabled={buttonSubmitting} - > - Save - </Button> + className="green-btn" + buttonText="Save" + /> </Form> <Modal visible={isModalVisible} From 396630ea70775eed33d8f9ad698e291229a54c62 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Tue, 20 Sep 2022 05:50:39 +0300 Subject: [PATCH 041/132] Drop down arrow, flip it when active and flip it. --- web/src/components/AppBar/ThemeSwitcher.js | 8 +++++--- .../Form/TradeFormFields/DropDown.js | 8 +++++--- web/src/containers/Trade/components/Filters.js | 10 +++++++--- .../containers/Trade/components/Orderbook.js | 14 +++++++++++--- .../Trade/components/TradeHistory.js | 18 +++++++++++++----- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/web/src/components/AppBar/ThemeSwitcher.js b/web/src/components/AppBar/ThemeSwitcher.js index ae7c765168..ffe66657f6 100644 --- a/web/src/components/AppBar/ThemeSwitcher.js +++ b/web/src/components/AppBar/ThemeSwitcher.js @@ -1,14 +1,15 @@ -import React from 'react'; +import React, { useState } from 'react'; import classnames from 'classnames'; import Image from 'components/Image'; import { FLEX_CENTER_CLASSES } from 'config/constants'; import withConfig from 'components/ConfigProvider/withConfig'; import { Select } from 'antd'; -import { CaretDownOutlined } from '@ant-design/icons'; +import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'; const { Option } = Select; const ThemeSwitcher = ({ selected, options = [], toggle, icons: ICONS }) => { + const [isOpen, setIsOpen] = useState(false); const handleClick = () => { const theme = options[0].value === selected ? options[1].value : options[0].value; @@ -72,7 +73,8 @@ const ThemeSwitcher = ({ selected, options = [], toggle, icons: ICONS }) => { size="small" onSelect={toggle} bordered={false} - suffixIcon={<CaretDownOutlined />} + onClick={() => setIsOpen((prev) => !prev)} + suffixIcon={isOpen ? <CaretUpOutlined /> : <CaretDownOutlined />} className="custom-select-input-style appbar elevated" dropdownClassName="custom-select-style select-option-wrapper" > diff --git a/web/src/components/Form/TradeFormFields/DropDown.js b/web/src/components/Form/TradeFormFields/DropDown.js index 579971265f..1ed7787b98 100644 --- a/web/src/components/Form/TradeFormFields/DropDown.js +++ b/web/src/components/Form/TradeFormFields/DropDown.js @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Select } from 'antd'; -import { CaretDownOutlined } from '@ant-design/icons'; +import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'; import STRINGS from 'config/localizedStrings'; const { Option } = Select; @@ -8,6 +8,7 @@ const { Option } = Select; // todo: add antd component to redux form const DropDown = (props) => { + const [isOpen, setIsOpen] = useState(false); const { input: { onChange, value }, options, @@ -22,7 +23,8 @@ const DropDown = (props) => { bordered={false} size="small" onChange={onChange} - suffixIcon={<CaretDownOutlined />} + onClick={setIsOpen((prev) => !prev)} + suffixIcon={isOpen ? <CaretUpOutlined /> : <CaretDownOutlined />} className="custom-select-input-style w-100 elevated" dropdownClassName="custom-select-style select-option-wrapper" > diff --git a/web/src/containers/Trade/components/Filters.js b/web/src/containers/Trade/components/Filters.js index b45bdd5a58..6213d51d98 100644 --- a/web/src/containers/Trade/components/Filters.js +++ b/web/src/containers/Trade/components/Filters.js @@ -1,12 +1,13 @@ -import React from 'react'; +import React, { useState } from 'react'; import { connect } from 'react-redux'; import { Select } from 'antd'; -import { CaretDownOutlined } from '@ant-design/icons'; +import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'; import STRINGS from 'config/localizedStrings'; const { Option } = Select; const Filters = ({ pairs, pair, onChange }) => { + const [isOpen, setIsOpen] = useState(false); return ( <div> <Select @@ -17,7 +18,10 @@ const Filters = ({ pairs, pair, onChange }) => { className="custom-select-input-style elevated" dropdownClassName="custom-select-style" bordered={false} - suffixIcon={<CaretDownOutlined />} + suffixIcon={isOpen ? <CaretUpOutlined /> : <CaretDownOutlined />} + onClick={() => { + setIsOpen((prev) => !prev); + }} value={pair} onChange={onChange} > diff --git a/web/src/containers/Trade/components/Orderbook.js b/web/src/containers/Trade/components/Orderbook.js index f4920ef821..982d4648f0 100644 --- a/web/src/containers/Trade/components/Orderbook.js +++ b/web/src/containers/Trade/components/Orderbook.js @@ -55,6 +55,7 @@ class Orderbook extends Component { priceDiff: 0, inProp: false, isAnimated: false, + isDropdownOpen: false, }; componentDidMount() { @@ -296,10 +297,17 @@ class Orderbook extends Component { bordered={false} defaultValue={false} size="small" + onClick={() => { + this.setState({ isDropdownOpend: !this.state.isDropdownOpen }); + }} suffixIcon={ - <CaretDownOutlined - onClick={() => this.dropdownVisibleChange(!isOpen)} - /> + this.isDropdownOpen ? ( + <CaretUpOutlined /> + ) : ( + <CaretDownOutlined + onClick={() => this.dropdownVisibleChange(!isOpen)} + /> + ) } value={isBase} onSelect={this.onSelect} diff --git a/web/src/containers/Trade/components/TradeHistory.js b/web/src/containers/Trade/components/TradeHistory.js index c04a916871..6e772f0225 100644 --- a/web/src/containers/Trade/components/TradeHistory.js +++ b/web/src/containers/Trade/components/TradeHistory.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { Component, useState } from 'react'; import classnames from 'classnames'; import { connect } from 'react-redux'; import { ReactSVG } from 'react-svg'; @@ -12,7 +12,7 @@ import { tradeHistorySelector } from '../utils'; import withConfig from 'components/ConfigProvider/withConfig'; import { calcPercentage } from 'utils/math'; import { Select } from 'antd'; -import { CaretDownOutlined } from '@ant-design/icons'; +import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'; import math from 'mathjs'; import { opacifyNumber } from 'helpers/opacify'; @@ -86,6 +86,7 @@ class TradeHistory extends Component { const { isBase, isOpen } = this.state; const { pairData } = this.props; const { pair_base_display, pair_2_display } = pairData; + const { isDropdownOpen, setIsDropdownOpen } = useState(false); return [ { @@ -126,10 +127,17 @@ class TradeHistory extends Component { bordered={false} defaultValue={false} size="small" + onClick={() => { + setIsDropdownOpen((prev) => !prev); + }} suffixIcon={ - <CaretDownOutlined - onClick={() => this.dropdownVisibleChange(!isOpen)} - /> + isDropdownOpen ? ( + <CaretUpOutlined /> + ) : ( + <CaretDownOutlined + onClick={() => this.dropdownVisibleChange(!isOpen)} + /> + ) } value={isBase} onSelect={this.onSelect} From 68a9af1f253d2766570a316aa01441e7721f73a1 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Tue, 20 Sep 2022 06:00:35 +0300 Subject: [PATCH 042/132] Add a hover effect on the coin area in wallet --- web/src/containers/Wallet/AssetsBlock.js | 2 +- web/src/containers/Wallet/_Wallet.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/web/src/containers/Wallet/AssetsBlock.js b/web/src/containers/Wallet/AssetsBlock.js index 560d6e9cc7..82992aee10 100644 --- a/web/src/containers/Wallet/AssetsBlock.js +++ b/web/src/containers/Wallet/AssetsBlock.js @@ -245,7 +245,7 @@ const AssetsBlock = ({ </td> <td className="td-name td-fit"> {sortedSearchResults && loading ? ( - <div className="d-flex align-items-center"> + <div className="d-flex align-items-center wallet-hover cursor-pointer"> <Link to={`/wallet/${key.toLowerCase()}`}> <Image iconId={icon_id} diff --git a/web/src/containers/Wallet/_Wallet.scss b/web/src/containers/Wallet/_Wallet.scss index 147710c275..7f0d7a527d 100644 --- a/web/src/containers/Wallet/_Wallet.scss +++ b/web/src/containers/Wallet/_Wallet.scss @@ -2,6 +2,10 @@ $wrapper-border: 1px solid $colors-main-black; $wrapper-button-margin: 2.5rem; $header-margin: 2rem; +.wallet-hover:hover { + opacity: 0.5; +} + .layout-mobile { .button-container { min-height: 4.5rem; From 829b864e66e825f934ea551788c2e01e4d46559e Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 20 Sep 2022 10:26:55 +0430 Subject: [PATCH 043/132] change for default name and short_name in manifest json --- web/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/index.js b/web/src/index.js index 30b4462b4c..fcabd49b29 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -252,7 +252,7 @@ const bootstrapApp = ( app: { remoteRoutes, plugins, - info: { name }, + constants: { api_name: name }, }, } = store.getState(); From 3b010d057f170e24b4341c30d82f2d72bc104c37 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 21 Sep 2022 00:58:06 +0900 Subject: [PATCH 044/132] app key is added for user settings --- server/constants.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/constants.js b/server/constants.js index b9c7965805..c48499c94e 100644 --- a/server/constants.js +++ b/server/constants.js @@ -302,7 +302,8 @@ exports.SETTING_KEYS = [ 'interface', 'audio', 'risk', - 'chat' + 'chat', + 'app' ]; exports.OMITTED_USER_FIELDS = [ From acab2c7705617ceeea8897065e73e5e63c9d8928 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 21 Sep 2022 01:00:08 +0900 Subject: [PATCH 045/132] version update --- server/api/swagger/swagger.yaml | 2 +- server/package.json | 2 +- version | 2 +- web/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/api/swagger/swagger.yaml b/server/api/swagger/swagger.yaml index 5d372506c5..877101e4d2 100644 --- a/server/api/swagger/swagger.yaml +++ b/server/api/swagger/swagger.yaml @@ -1,6 +1,6 @@ swagger: "2.0" info: - version: "2.4.2" + version: "2.4.3" title: HollaEx Kit host: api.hollaex.com basePath: /v2 diff --git a/server/package.json b/server/package.json index ec402c1f6f..6056dc4527 100644 --- a/server/package.json +++ b/server/package.json @@ -1,5 +1,5 @@ { - "version": "2.4.2", + "version": "2.4.3", "private": false, "description": "HollaEx Kit", "keywords": [ diff --git a/version b/version index acdc3f1b0b..6550da6970 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.4.2 \ No newline at end of file +2.4.3 \ No newline at end of file diff --git a/web/package.json b/web/package.json index bcc101e34a..0aebc5b911 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "hollaex-kit", - "version": "2.4.2", + "version": "2.4.3", "private": true, "dependencies": { "@ant-design/compatible": "1.0.5", From 39783a6dda27e07a493de25b7f6d9e5c80f7f224 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 21 Sep 2022 16:42:22 +0900 Subject: [PATCH 046/132] removed files --- plugins/LICENSE | 21 -- plugins/test.js | 917 ------------------------------------------------ 2 files changed, 938 deletions(-) delete mode 100644 plugins/LICENSE delete mode 100644 plugins/test.js diff --git a/plugins/LICENSE b/plugins/LICENSE deleted file mode 100644 index dd6bdf83ca..0000000000 --- a/plugins/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2019 Paciolan - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE -OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/plugins/test.js b/plugins/test.js deleted file mode 100644 index 3cffb46496..0000000000 --- a/plugins/test.js +++ /dev/null @@ -1,917 +0,0 @@ -'use strict'; - -const crypto = require('crypto'); - -const { - meta, - publicMeta, - toolsLib, - lodash, - moment, - app, - loggerPlugin, - expressValidator, - bluebird, - rp -} = this; - -const { - webhook_secret: { value: WEBHOOK_SECRET }, - client_secret: { value: CLIENT_SECRET }, - manual_review: { value: MANUAL_REVIEW } -} = meta; -const { - client_id: { value: CLIENT_ID }, - flow_id: { value: FLOW_ID } -} = publicMeta; - -const VERIFY_STATUS = { - EMPTY: 0, - PENDING: 1, - REJECTED: 2, - COMPLETED: 3 -}; - -const - MATI_API_URL_AUTH = 'https://api.getmati.com/oauth', - getDomainUrl = (path) => `${toolsLib.getDomain()}${path}`; - -const init = async () => { - if (!WEBHOOK_SECRET) { - throw new Error('webhook_secret required value'); - } - if (!CLIENT_SECRET) { - throw new Error('client_secret required value'); - } - if (!CLIENT_ID) { - throw new Error('client_id required value'); - } - if (!FLOW_ID) { - throw new Error('flow_id required value'); - } - if (typeof MANUAL_REVIEW !== 'boolean') { - throw new Error('manual_review invalid value'); - } -}; - -const convertGender = (gender) => { - return gender ? 'Female' : 'Male'; -}; - -const parseObjToList = (data) => { - const result = { - html: '', - text: '' - }; - - for (let field in data) { - if (!lodash.isNil(data[field])) { - if (field === 'gender') { - data[field] = convertGender(data[field]); - } - - if (toolsLib.isDatetime(data[field])) { - data[field] = moment(data[field]).format('YYYY/MM/DD'); - } - result.html += `<li>${lodash.startCase(field)}: ${data[field]}</li>`; - result.text += `${lodash.startCase(field)}: ${data[field]}\n`; - } - } - - return result; -}; - -const documentApprovedEmail = (email, data = {}) => { - const idData = parseObjToList(data); - - const html = ` - <div> - <p> - Dear ${email}, - </p> - <p> - Your uploaded KYC documents have been approved.<br> - You now have access to all exchange features that require identity verification. - </p> - <ul> - ${idData.html} - </ul> - <p> - To view your approved documents, visit your <a href=${getDomainUrl('/verification')}>Verification page</a>. - </p> - <p> - Regards,<br> - ${toolsLib.getKitConfig().api_name} team - </p> - </div> - `; - - const text = ` - Dear ${email}, - - Your uploaded KYC documents have been approved. - You now have access to all exchange features that require identity verification. - - ${idData.text} - - To view your approved documents, visit your Verification page. - - Regards, - ${toolsLib.getKitConfig().api_name} team - `; - - const subject = 'KYC Documents Approved'; - - return toolsLib.sendCustomEmail( - email, - subject, - toolsLib.emailHtmlBoilerplate(html), - { - bcc: 'default', - text - } - ); -}; - -const documentPendingEmail = (email) => { - const html = ` - <div> - <p> - Dear ${email}, - </p> - <p> - Your uploaded documents are currently being processed.<br> - We will notify you when your documents are approved or denied. - </p> - <p> - To view the status of your pending documents, visit your <a href=${getDomainUrl('/verification')}>Verification page</a>. - </p> - <p> - Regards,<br> - ${toolsLib.getKitConfig().api_name} team - </p> - </div> - `; - - const text = ` - Dear ${email}, - - Your uploaded documents are currently being processed. - We will notify you when your documents are approved or denied. - - To view the status of your pending documents, visit your Verification page. - - Regards, - ${toolsLib.getKitConfig().api_name} team - `; - - const subject = 'KYC Documents Pending'; - - return toolsLib.sendCustomEmail( - email, - subject, - toolsLib.emailHtmlBoilerplate(html), - { - bcc: 'default', - text - } - ); -}; - -const documentRejectedEmail = (email, reasons = {}) => { - const parsedReasons = parseObjToList(reasons); - - const html = ` - <div> - <p> - Dear ${email}, - </p> - <p> - Unfortunately, your uploaded KYC documents have been rejected.<br> - The reasons for your documents being rejected are listed below.<br> - </p> - <div> - ${parsedReasons.html} - </div> - <p> - If you feel these reasons are invalid, please feel free to reply to this email.<br> - Otherwise, please reupload valid documents in order to verify your identity. - </p> - <p> - Regards,<br> - ${toolsLib.getKitConfig().api_name} team - </p> - </div> - `; - - const text = ` - Dear ${email}, - - Unfortunately, your uploaded KYC documents have been rejected. - The reasons for your documents being rejected are listed below. - - ${parsedReasons.text} - - If you feel these reasons are invalid, please feel free to reply to this email. - Otherwise, please reupload valid documents in order to verify your identity. - - Regards, - ${toolsLib.getKitConfig().api_name} team - `; - - const subject = 'KYC Documents Rejected'; - - return toolsLib.sendCustomEmail( - email, - subject, - toolsLib.emailHtmlBoilerplate(html), - { - bcc: 'default', - text - } - ); -}; - -const documentExpiredEmail = (email) => { - const html = ` - <div> - <p> - Dear ${email}, - </p> - <p> - Your iDenfy document upload token as expired.<br> - You will no longer be able to use your token to upload your documents.<br> - To generate a new token, please go through the KYC process in your <a href=${getDomainUrl('/verification')}>Verification page</a>. - </p> - <p> - Regards,<br> - ${toolsLib.getKitConfig().api_name} team - </p> - </div> - `; - - const text = ` - Dear ${email}, - - Your iDenfy document upload token as expired. - You will no longer be able to use your token to upload your documents. - To generate a new token, please go through the KYC process in your Verification page. - - Regards, - ${toolsLib.getKitConfig().api_name} team - `; - - const subject = 'iDenfy Token Expired'; - - return toolsLib.sendCustomEmail( - email, - subject, - toolsLib.emailHtmlBoilerplate(html), - { - bcc: 'default', - text - } - ); -}; - -const adminAlertEmail = async (data = {}) => { - const { email, id } = data; - - const html = ` - <div> - <p> - Documents provided by user ${email} with ID ${id} have been automatically approved and are awaiting manual approval.<br> - <br> - You can manually approve or reject these documents through the <a href=${getDomainUrl(`/admin/user?id=${id}`)}>Operator Controls Panel</a>.<br> - Once reviewed, the user will be notified of the updated status. - </p> - </div> - `; - - const text = ` - Documents provided by user ${email} with ID ${id} have been automatically approved and are awaiting manual approval. - - You can manually approve or reject these documents through the Operator Controls Panel. - Once reviewed, the user will be notified of the updated status. - `; - - const subject = 'KYC Documents Awaiting Manual Review'; - - const kycOperators = await toolsLib.database.findAll('user', { - where: { - is_kyc: true - }, - attributes: ['email'], - raw: true - }); - - let cc = null; - - if (kycOperators.length > 0) { - cc = kycOperators.map((operator) => operator.email).join(','); - } - - return toolsLib.sendCustomEmail( - toolsLib.getKitSecrets().emails.audit, - subject, - toolsLib.emailHtmlBoilerplate(html), - { - cc, - text - } - ); -}; - -const getGender = (docSex) => { - if (docSex && docSex !== 'UNDEFINED') { - return docSex === 'F'; - } - - return false; -}; - -const generateUpdatedUserData = (status, data, eventName) => { - let result = {}; - if (eventName === 'verification_started' || eventName === 'verification_inputs_completed') { - result = { - id_data: { - status - } - }; - return result; - - } else if (eventName === 'verification_expired') { - result = { - id_data: { - status - } - }; - result.id_data.mati.resource = data.resource; - - return result; - - } else if (eventName === 'verification_updated' || eventName === 'verification_completed') { - bluebird.all([ - data.resource, - generateMatiToken() - ]) - .then(([resourceURL, responseToken]) => { - if (!responseToken) - throw new Error('Not authentication Mati'); - - loggerPlugin.verbose( - data.metadata.user_id, - 'GET /plugins/mati/admin/files request getMatiFiles', - responseToken - ); - responseToken = JSON.parse(responseToken); - return getMatiFiles(resourceURL, responseToken.access_token); - }) - .then((documentUser) => { - - result = { - full_name: '', - nationality: '', - gender: false, - dob: null, - id_data: { - status - } - }; - - loggerPlugin.verbose( - data.metadata.user_id, - 'GET /plugins/mati/admin/files response data', - documentUser - ); - - const { - fullName: { value: fullName }, - documentNumber: { value: documentNumber }, - dateOfBirt: { value: dateOfBirt }, - expirationDate: { value: expirationDate }, - documentType: { value: documentType }, - firstName: { value: firstName }, - issueCountry: { value: issueCountry }, - nationality: { value: nationality }, - personalNumber: { value: personalNumber }, - sex: { value: sex }, - surname: { value: surname } - } = documentUser.documents.fields; - - result.id_data.mati = documentUser.documents.data; - result.id_data.mati.resource = documentUser.resource; - if (documentType) { - result.id_data.type = documentType.toLowerCase(); - } - - if (documentNumber) { - result.id_data.number = documentNumber; - } - - if (expirationDate) { - result.id_data.expiration_date = moment(expirationDate).toISOString(); - } - - if (nationality) { - result.nationality = nationality; - } - - if (dateOfBirt) { - result.dob = moment(dateOfBirt).toISOString(); - } - - if (fullName) { - result.full_name = fullName; - } - if (sex) { - result.gender = getGender(sex); - } - return result; - }); - } -}; - -const generateMatiToken = async () => { - const method = 'POST'; - const headers = { - 'content-type': 'application/x-www-form-urlencoded', - 'Authorization': 'Basic ' + new Buffer.from(CLIENT_ID + ':' + CLIENT_SECRET, 'utf8').toString('base64') - }; - const form = { - 'grant_type': 'client_credentials' - }; - const option = { - headers, - method, - uri: MATI_API_URL_AUTH, - form - }; - - return rp(option); -}; - -const getMatiFiles = async (resource, accessToken) => { - - const method = 'GET'; - const headers = { - 'content-type': 'application/x-www-form-urlencoded', - 'Authorization': 'Bearer ' + accessToken - }; - const url = resource; - loggerPlugin.verbose( - resource, - accessToken, - 'getMatiFiles request' - ); - return rp({ - headers, - method, - url - }); -}; - -const verifyRequest = async (signature, secret, payloadBody) => { - let hash = crypto.createHmac('sha256', secret); - hash = hash.update(payloadBody).digest('hex'); - const isValid = crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(signature)); - - loggerPlugin.error( - 'MATI PLUGIN cannot verify notification request' - ); - - if (!isValid) { - throw new Error('Invalid request'); - } -}; - -init() - .then(() => { - app.post('/plugins/mati/notification', (req, res) => { - loggerPlugin.verbose( - req.uuid, - 'POST /plugins/mati/notification body URL webhook', - req.body - ); - - const signature = req.headers['x-signature']; - const data = req.body; - - verifyRequest(signature, WEBHOOK_SECRET, JSON.stringify(data)) - .then(() => { - if (!data.metadata || !data.metadata.user_id) { - throw new Error('Meta data user_id not given'); - } - - return toolsLib.database.findOne('user', { - where: { - id: data.metadata.user_id - }, - attributes: [ - 'id', - 'email', - 'network_id', - 'full_name', - 'gender', - 'nationality', - 'dob', - 'id_data' - ] - }); - }) - .then(async (user) => { - if (!user) { - throw new Error('User not found'); - } - - const { eventName } = data; - - let updateStatus; - - if (eventName === 'verification_started') { - updateStatus = VERIFY_STATUS.PENDING; - } else if (eventName === 'verification_inputs_completed') { - updateStatus = VERIFY_STATUS.PENDING; - } else if (eventName === 'verification_updated') { - if (MANUAL_REVIEW) { - updateStatus = VERIFY_STATUS.PENDING; - } else { - updateStatus = VERIFY_STATUS.COMPLETED; - } - } else if (eventName === 'verification_completed') { - if (MANUAL_REVIEW) { - updateStatus = VERIFY_STATUS.PENDING; - } else { - updateStatus = VERIFY_STATUS.COMPLETED; - } - } else if (eventName === 'verification_expired') { - updateStatus = VERIFY_STATUS.REJECTED; - } - - const previousStatus = user.id_data.status; - - loggerPlugin.verbose( - req.uuid, - 'POST /plugins/mati/notification status', - 'mati eventName', - eventName, - 'previous id data status:', - previousStatus, - 'updating status:', - updateStatus - ); - - if (updateStatus) { - const updateData = await generateUpdatedUserData(updateStatus, data, eventName); - - loggerPlugin.verbose( - req.uuid, - 'POST /plugins/mati/notification updated user', - updateData - ); - const updatedUser = await user.update(updateData); - - loggerPlugin.verbose( - req.uuid, - 'POST /plugins/mati/notification updated user', - updatedUser - ); - - try { - if (updateStatus === VERIFY_STATUS.PENDING && MANUAL_REVIEW) { - adminAlertEmail({ - email: user.email, - id: user.id - }); - } - } catch (err) { - loggerPlugin.error( - req.uuid, - 'POST /plugins/mati/notification error while sending manual review required email', - err.message - ); - } - - try { - if (updateStatus === VERIFY_STATUS.REJECTED) { - documentRejectedEmail( - user.email - ); - } else if (updateStatus === VERIFY_STATUS.PENDING) { - documentPendingEmail(user.email); - } else if (updateStatus === VERIFY_STATUS.COMPLETED) { - documentApprovedEmail(user.email, { - full_name: updatedUser.full_name ? updatedUser.full_name : null, - dob: updatedUser.dob, - gender: lodash.isBoolean(updatedUser.gender) ? updatedUser.gender : null, - nationality: updatedUser.nationality ? updatedUser.nationality : null, - ...lodash.omit(updatedUser.id_data, ['status', 'mati']) - }); - } - } catch (err) { - loggerPlugin.error( - req.uuid, - 'POST /plugins/mati/notification error while sending email', - err.message - ); - } - } - return res.json({ message: 'Success' }); - - }) - .catch((err) => { - loggerPlugin.error( - req.uuid, - 'POST /plugins/mati/notification err', - err - ); - return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); - }); - }); - - app.get('/plugins/mati/admin/files', [ - toolsLib.security.verifyBearerTokenExpressMiddleware(['admin', 'supervisor', 'support', 'kyc']), - expressValidator.checkSchema({ - user_id: { - in: ['query'], - errorMessage: 'must be an integer', - isInt: true, - optional: false - } - }) - ], (req, res) => { - const errors = expressValidator.validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } - - loggerPlugin.verbose( - req.uuid, - 'GET /plugins/mati/admin/files auth', - req.auth.sub - ); - - const { user_id } = req.query; - toolsLib.user.getUserByKitId(user_id) - .then((user) => { - if (!user) { - throw new Error('User not found'); - } - - if ( - !user.id_data - || !user.id_data.mati - || !user.id_data.mati.resource - || user.id_data.status === VERIFY_STATUS.EMPTY - ) { - throw new Error('User does not have uploaded documents'); - } - loggerPlugin.verbose( - user_id, - 'GET /plugins/mati/admin/files request generateMatiToken' - ); - - return bluebird.all([ - user, - generateMatiToken() - ]); - }) - .then(([user, responseToken]) => { - if (!responseToken) - throw new Error('Not authentication Mati'); - loggerPlugin.verbose( - user_id, - 'GET /plugins/mati/admin/files request getMatiFiles', - responseToken - ); - responseToken = JSON.parse(responseToken); - return getMatiFiles(user.id_data.mati.resource, responseToken.access_token); - }) - .then((data) => { - - loggerPlugin.verbose( - user_id, - 'GET /plugins/mati/admin/files response data', - data - ); - - return res.json(data); - }) - .catch((err) => { - loggerPlugin.error( - req.uuid, - 'GET /plugins/mati/admin/files err', - err.message - ); - return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); - }); - }); - - app.get('/plugins/mati/admin/file1/:location', [], (req, res) => { - const { location } = req.params; - - generateMatiToken() - .then((responseToken) => { - if (!responseToken) - throw new Error('Not authentication Mati'); - responseToken = JSON.parse(responseToken); - return - const method = 'GET'; - const headers = { - 'Authorization': 'Bearer ' + responseToken - }; - const url = 'https://media.getmati.com/file?location='+location; - - return rp({ - headers, - method, - url - }) - }) - .then((data) => { - console.log(data, "data"); - res.contentType('image/jpeg'); - return res.send(data); - }) - .catch((err) => { - loggerPlugin.error( - req.uuid, - 'GET /plugins/mati/admin/file err', - err.message - ); - return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); - }); - }); - - app.post('/plugins/mati/verify', [ - toolsLib.security.verifyBearerTokenExpressMiddleware(['admin', 'kyc', 'support', 'supervisor']), - expressValidator.checkSchema({ - user_id: { - in: ['body'], - errorMessage: 'must be an integer', - isInt: true, - optional: false - } - }) - ], (req, res) => { - const errors = expressValidator.validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } - - loggerPlugin.verbose( - req.uuid, - 'POST /plugins/mati/verify auth', - req.auth.sub - ); - - const { user_id } = req.body; - - toolsLib.user.getUserByKitId(user_id, false, false) - .then((user) => { - if (!user) { - throw new Error('User not found'); - } - - if ( - !user.id_data - || lodash.isNil(user.id_data.status) - || user.id_data.status === VERIFY_STATUS.EMPTY - ) { - throw new Error('User documents are not pending'); - } - - if (user.id_data.status === VERIFY_STATUS.COMPLETED) { - throw new Error('User documents are already verified'); - } - - return user.update({ - id_data: { - ...user.id_data, - status: VERIFY_STATUS.COMPLETED - } - }); - }) - .then((data) => { - try { - documentApprovedEmail(data.email, { - full_name: data.full_name ? data.full_name : null, - dob: data.dob, - gender: lodash.isBoolean(data.gender) ? data.gender : null, - nationality: data.nationality ? data.nationality : null, - ...lodash.omit(data.id_data, ['status', 'idenfy']) - }); - } catch (err) { - loggerPlugin.error( - req.uuid, - 'POST /plugins/mati/verify err while sending email', - err.message - ); - } - return res.json(lodash.pick(data, [ - 'id', - 'email', - 'full_name', - 'dob', - 'nationality', - 'gender', - 'id_data' - ])); - }) - .catch((err) => { - loggerPlugin.error( - req.uuid, - 'POST /plugins/mati/verify err', - err.message - ); - return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); - }); - }); - - app.post('/plugins/mati/revoke', [ - toolsLib.security.verifyBearerTokenExpressMiddleware(['admin', 'kyc', 'support', 'supervisor']), - expressValidator.checkSchema({ - user_id: { - in: ['body'], - errorMessage: 'must be an integer', - isInt: true, - optional: false - } - }) - ], (req, res) => { - const errors = expressValidator.validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } - - loggerPlugin.verbose( - req.uuid, - 'POST /plugins/mati/revoke auth', - req.auth.sub - ); - - const user_id = req.body.user_id; - - toolsLib.user.getUserByKitId(user_id, false, false) - .then((user) => { - if (!user) { - throw new Error('User not found'); - } - - if ( - !user.id_data - || lodash.isNil(user.id_data.status) - || user.id_data.status === VERIFY_STATUS.EMPTY - ) { - throw new Error('User documents are not pending'); - } - - if (user.id_data.status === VERIFY_STATUS.REJECTED) { - throw new Error('User documents are already rejected'); - } - - return user.update({ - id_data: { - ...user.id_data, - status: VERIFY_STATUS.REJECTED - } - }); - }) - .then((data) => { - try { - documentRejectedEmail( - data.email - ); - } catch (err) { - loggerPlugin.error( - req.uuid, - 'POST /plugins/mati/revoke err while sending email', - err.message - ); - } - return res.json(lodash.pick(data, [ - 'id', - 'email', - 'full_name', - 'dob', - 'nationality', - 'gender', - 'id_data' - ])); - }) - .catch((err) => { - loggerPlugin.error( - req.uuid, - 'GET /plugins/mati/revoke err', - err.message - ); - return res.status(err.statusCode || 400).json({ message: toolsLib.errorMessageConverter(err) }); - }); - }); - }) - .catch((err) => { - loggerPlugin.error( - 'DOMINICAN EXCHANGE KYC PLUGIN err', - err.message - ); - }); \ No newline at end of file From a9639ea301a575c963dff2bb2279515c25ca7b76 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 21 Sep 2022 17:15:31 +0900 Subject: [PATCH 047/132] local dev plugin setup --- server/package.json | 3 +- server/plugins/dev.js | 256 ++++++++++++++++++------------------------ 2 files changed, 114 insertions(+), 145 deletions(-) diff --git a/server/package.json b/server/package.json index 6056dc4527..80aa579df2 100644 --- a/server/package.json +++ b/server/package.json @@ -89,7 +89,8 @@ "undomigrate": "sequelize db:migrate:undo:all", "migrate": "sequelize db:migrate", "start": "node app.js", - "test": "NODE_ENV=test nyc --reporter=html --reporter=text mocha --recursive --timeout 5000" + "test": "NODE_ENV=test nyc --reporter=html --reporter=text mocha --recursive --timeout 5000", + "dev:plugin": "docker exec -it server_hollaex-kit-server_1 node plugins/dev.js" }, "nyc": { "exclude": [ diff --git a/server/plugins/dev.js b/server/plugins/dev.js index b2b0655854..38a966f22f 100644 --- a/server/plugins/dev.js +++ b/server/plugins/dev.js @@ -1,152 +1,120 @@ - -/** - * Add your mock publicMeta and meta values in the object below - * In production, these values are stored in the configuration JSON file - - * Example mock configurations are included below -**/ - -this.configValues = { - // // ------------ CONFIG VALUES EXAMPLE START ------------ - - // publicMeta: { - // public_value: { - // type: 'string', - // description: 'Public meta value', - // required: false, - // value: 'i am public' - // } - // }, - // meta: { - // private_value: { - // type: 'string', - // description: 'Private meta value', - // required: true, - // value: 'i am private' - // } - // } - - // // ------------ CONFIG VALUES EXAMPLE END ------------ -}; - -const pluginScript = () => { - /** - * Add the plugin script here - * The script within this function should be in the script.js file for a plugin - - * An example of a plugin script is included below - **/ - - // // ------------ PLUGIN EXAMPLE START ------------ - - // const { app, loggerPlugin, toolsLib } = this.pluginLibraries; // this.pluginLibraries holds app, loggerPlugin, and toolsLib in the plugin script - // const { publicMeta, meta } = this.configValues; // this.configValues holds publicMeta and meta in the plugin script - - // const lodash = require('lodash'); - // const moment = require('moment'); - // const { public_value: { value: PUBLIC_VALUE } } = publicMeta; - // const { private_value: { value: PRIVATE_VALUE } } = meta; - - // // All endpoints for a plugin should follow the format: '/plugins/<PLUGIN_NAME>/...'. For this example, the plugin name is 'test' - // const HEALTH_ENDPOINT = '/plugins/test/health'; - // const CONFIG_VALUES_ENDPOINT = '/plugins/test/config-values'; - - // // We recommend creating an init function that checks for all required configuration values and all other requirements for this plugin to run - // const init = async () => { - // loggerPlugin.verbose( - // 'DEV PLUGIN initializing...' - // ); - - // if (!lodash.isString(PRIVATE_VALUE)) { - // throw new Error('Private Value must be configured for this plugin to run'); - // } - - // loggerPlugin.verbose( - // 'DEV PLUGIN initialized' - // ); - // }; - - // init() - // .then(() => { - // app.get(HEALTH_ENDPOINT, async (req, res) => { - // loggerPlugin.info( - // req.uuid, - // HEALTH_ENDPOINT - // ); - - // return res.json({ - // status: 'running', - // current_time: moment().toISOString(), - // exchange_name: toolsLib.getKitConfig().info.name - // }); - // }); - - // app.get(CONFIG_VALUES_ENDPOINT, async (req, res) => { - // loggerPlugin.info( - // req.uuid, - // CONFIG_VALUES_ENDPOINT - // ); - - // return res.json({ - // public_value: PUBLIC_VALUE, - // private_value: PRIVATE_VALUE - // }); - // }); - // }) - // .catch((err) => { - // // It's important to catch all errors in a script. If a thrown error is not caught, the plugin process will exit and continuously try to restart - // loggerPlugin.error( - // 'DEV PLUGIN initialization error', - // err.message - // ); - // }); - - // // ------------ PLUGIN EXAMPLE END ------------ -}; - - - - - - - - -// BELOW IS THE SCRIPT FOR RUNNING THE PLUGIN DEV ENVIRONMENT THAT IS NOT NECESSARY IN THE PLUGIN ITSELF +'use strict'; const { checkStatus } = require('../init'); +const express = require('express'); +const morgan = require('morgan'); +const PORT = process.env.PLUGIN_PORT || 10012; +const { logEntryRequest, stream, loggerPlugin } = require('../config/logger'); +const morganType = process.env.NODE_ENV === 'development' ? 'dev' : 'combined'; +const { domainMiddleware, helmetMiddleware } = require('../config/middleware'); +const cors = require('cors'); +const fs = require('fs'); +const path = require('path'); +const toolsLib = require('hollaex-tools-lib'); +const expressValidator = require('express-validator'); +const lodash = require('lodash'); +const npm = require('npm-programmatic'); +const _eval = require('eval'); +const rp = require('request-promise'); + +const getPluginConfig = () => { + return rp('http://host.docker.internal:8080/config.json'); +}; -const initializeDevPlugin = async () => { - await checkStatus(); - - const morgan = require('morgan'); - const { logEntryRequest, stream, loggerPlugin } = require('../config/logger'); - const { domainMiddleware, helmetMiddleware } = require('../config/middleware'); - const morganType = process.env.NODE_ENV === 'development' ? 'dev' : 'combined'; - const PORT = 10012; - const cors = require('cors'); - const toolsLib = require('hollaex-tools-lib'); - const express = require('express'); +let config, script; - const app = express(); - app.use(morgan(morganType, { stream })); - app.listen(PORT); - app.use(cors()); - app.use(express.urlencoded({ extended: true })); - app.use(express.json()); - app.use(logEntryRequest); - app.use(domainMiddleware); - helmetMiddleware(app); +const installLibrary = async (library) => { + const [name, version = 'latest'] = library.split('@'); + await npm.install([`${name}@${version}`], { + cwd: path.resolve(__dirname, '../'), + save: true, + output: true + }); - const pluginLibraries = { - app, - loggerPlugin, - toolsLib - }; + loggerPlugin.verbose( + 'plugins/index/installLibrary', + `${name} version ${version} installed` + ); - this.pluginLibraries = pluginLibraries; + const lib = require(name); + return lib; + }; -(async () => { - await initializeDevPlugin(); - pluginScript(); -})(); \ No newline at end of file +getPluginConfig() + .then((data) => { + data = JSON.parse(data); + config = data; + script = data.script; + return checkStatus(); + }) + .then(async () => { + const app = express(); + + app.use(morgan(morganType, { stream })); + app.listen(PORT); + app.use(cors()); + app.use(express.urlencoded({ extended: true })); + app.use(express.json()); + app.use(logEntryRequest); + app.use(domainMiddleware); + helmetMiddleware(app); + + const context = { + exports: exports, + require: require, + module: module, + toolsLib, + app, + loggerPlugin, + expressValidator, + pluginLibraries: { + app, + toolsLib, + loggerPlugin + }, + publicMeta: config.public_meta, + meta: config.meta, + configValues: { + publicMeta: config.public_meta, + meta: config.meta + }, + installedLibraries: {} + }; + + if (config.prescript && lodash.isArray(config.prescript.install) && !lodash.isEmpty(config.prescript.install)) { + loggerPlugin.verbose( + 'plugins/index/initialization', + `Installing packages for plugin ${config.name}` + ); + + for (const library of config.prescript.install) { + context.installedLibraries[library] = await installLibrary(library); + } + + loggerPlugin.verbose( + 'plugins/index/initialization', + `Plugin ${config.name} packages installed` + ); + } + + _eval(script, 'dev', context, true); + }) + .catch((err) => { + let message = 'Plugin Initialization failed'; + + if (err.message) { + message = err.message; + } + + if (err.statusCode && err.statusCode === 402) { + message = err.error.message; + } + + loggerPlugin.error( + '/plugins/index/initialization err', + message + ); + + setTimeout(() => { process.exit(1); }, 5000); + }); \ No newline at end of file From 140512dc189bc791bd9d458962d74c00884c51c2 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 21 Sep 2022 17:16:09 +0900 Subject: [PATCH 048/132] removed plugin eslint yml --- plugins/.eslintrc.yml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 plugins/.eslintrc.yml diff --git a/plugins/.eslintrc.yml b/plugins/.eslintrc.yml deleted file mode 100644 index 6ef13503d4..0000000000 --- a/plugins/.eslintrc.yml +++ /dev/null @@ -1,3 +0,0 @@ -extends: ["@paciolan/react"] -rules: - react/prop-types: off From 76f0e366b10939b4152e0c6585a6a2806efad0d2 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 21 Sep 2022 18:31:05 +0430 Subject: [PATCH 049/132] apps section functional version --- web/public/assets/images/all-apps.svg | 25 ++++ web/public/assets/images/apps.svg | 17 +++ web/public/assets/images/my-apps.svg | 15 ++ web/src/_common.scss | 4 + web/src/actions/appActions.js | 4 + web/src/components/AppBar/_AppBar.scss | 3 +- .../AppMenuSidebar/_AppMenuSidebar.scss | 3 +- web/src/config/icons/dark.js | 8 +- web/src/config/icons/static.js | 1 + web/src/config/lang/en.json | 24 +++- web/src/config/menu.js | 13 +- .../containers/Admin/General/InterfaceForm.js | 45 ++++++ web/src/containers/Admin/General/index.css | 16 +++ web/src/containers/App/App.js | 8 ++ web/src/containers/AppDetails/index.js | 29 +++- web/src/containers/Apps/All.js | 136 +++++++++++++++--- web/src/containers/Apps/ConfigureApps.js | 124 ++++++++++++++++ web/src/containers/Apps/User.js | 87 ++++++++--- web/src/containers/Apps/_Apps.scss | 19 +++ web/src/containers/Apps/index.js | 6 +- web/src/containers/Apps/utils.js | 16 +++ web/src/containers/_containers.scss | 1 + web/src/index.css | 28 +++- web/src/index.scss | 5 +- web/src/reducers/appReducer.js | 25 ++-- web/src/reducers/userReducer.js | 1 + web/src/utils/id.js | 2 + 27 files changed, 597 insertions(+), 68 deletions(-) create mode 100644 web/public/assets/images/all-apps.svg create mode 100644 web/public/assets/images/apps.svg create mode 100644 web/public/assets/images/my-apps.svg create mode 100644 web/src/containers/Apps/ConfigureApps.js create mode 100644 web/src/containers/Apps/_Apps.scss create mode 100644 web/src/containers/Apps/utils.js diff --git a/web/public/assets/images/all-apps.svg b/web/public/assets/images/all-apps.svg new file mode 100644 index 0000000000..7626228965 --- /dev/null +++ b/web/public/assets/images/all-apps.svg @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + x="0px" y="0px" viewBox="0 0 26.3 26.3" overflow="visible" xml:space="preserve"> +<g class="all-apps" id="Group_5258" transform="translate(-569.286 -408.426)"> + <path id="Path_4762" d="M575.6,425h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C576.1,424.8,575.9,425,575.6,425z"/> + <path id="Path_4762-2" d="M575.6,434.7h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C576.1,434.5,575.9,434.7,575.6,434.7z"/> + <path id="Path_4762-3" d="M585.3,415.3h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C585.8,415.1,585.6,415.3,585.3,415.3z"/> + <path id="Path_4762-4" d="M595.1,415.3h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C595.5,415.1,595.3,415.3,595.1,415.3z"/> + <path id="Path_4762-5" d="M585.3,425h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C585.8,424.8,585.6,425,585.3,425z"/> + <path id="Path_4762-6" d="M585.3,434.7h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C585.8,434.5,585.6,434.7,585.3,434.7z"/> + <path id="Path_4762-7" d="M595.1,425h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C595.5,424.8,595.3,425,595.1,425z"/> + <path id="Path_4762-8" d="M595.1,434.7h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C595.5,434.5,595.3,434.7,595.1,434.7z"/> + <path id="Path_4762-9" d="M575.6,415.3h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C576.1,415.1,575.9,415.3,575.6,415.3z"/> +</g> +</svg> diff --git a/web/public/assets/images/apps.svg b/web/public/assets/images/apps.svg new file mode 100644 index 0000000000..ea2feaba05 --- /dev/null +++ b/web/public/assets/images/apps.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + x="0px" y="0px" viewBox="0 0 39.3 39.4" overflow="visible" xml:space="preserve"> +<g class="apps" id="Group_5256" transform="translate(8676.22 -1331)"> + <g id="Group_5253" transform="translate(-8676.22 1331)"> + <path id="Path_4762" d="M16,17.2H1.2C0.6,17.2,0,16.7,0,16V1.2C0,0.6,0.6,0,1.2,0H16c0.7,0,1.2,0.6,1.2,1.2l0,0V16 + C17.2,16.7,16.6,17.2,16,17.2z"/> + <path id="Path_4762-2" d="M16,39.4H1.2c-0.7,0-1.2-0.6-1.2-1.2V23.4c0-0.7,0.5-1.2,1.2-1.2H16 + c0.7,0,1.2,0.6,1.2,1.2l0,0v14.8C17.2,38.8,16.6,39.4,16,39.4z"/> + <path id="Path_4762-3" d="M38.1,17.2H23.4c-0.7,0-1.2-0.6-1.2-1.2V1.2c0-0.7,0.5-1.2,1.2-1.2h14.7 + c0.7,0,1.2,0.6,1.2,1.2l0,0V16C39.3,16.7,38.8,17.2,38.1,17.2z"/> + <path id="Path_4762-4" d="M38.1,39.4H23.4c-0.7,0-1.2-0.6-1.2-1.2V23.4c0-0.7,0.5-1.2,1.2-1.2h14.7 + c0.7,0,1.2,0.6,1.2,1.2l0,0v14.8C39.3,38.8,38.8,39.4,38.1,39.4z"/> + </g> +</g> +</svg> diff --git a/web/public/assets/images/my-apps.svg b/web/public/assets/images/my-apps.svg new file mode 100644 index 0000000000..0546dc9262 --- /dev/null +++ b/web/public/assets/images/my-apps.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + x="0px" y="0px" viewBox="0 0 26.3 26.3" overflow="visible" xml:space="preserve"> +<g class="my-apps" id="Group_5258" transform="translate(-569.286 -408.426)"> + <path id="Path_4762" d="M575.6,425h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C576.1,424.8,575.9,425,575.6,425z"/> + <path id="Path_4762-2" d="M575.6,434.7h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C576.1,434.5,575.9,434.7,575.6,434.7z"/> + <path id="Path_4762-3" d="M575.6,415.3h-5.9c-0.3,0-0.5-0.2-0.5-0.5v-5.9c0-0.3,0.2-0.5,0.5-0.5h5.9 + c0.3,0,0.5,0.2,0.5,0.5v5.9C576.1,415.1,575.9,415.3,575.6,415.3z"/> + <path id="Path_4762-4" d="M594.4,434.7h-14.2c-0.7,0-1.2-0.8-1.2-1.9v-22.5c0-1,0.5-1.9,1.2-1.9h14.2 + c0.7,0,1.2,0.8,1.2,1.9v22.5C595.5,433.9,595,434.7,594.4,434.7z"/> +</g> +</svg> diff --git a/web/src/_common.scss b/web/src/_common.scss index 6ec3dd9783..f236d80d59 100644 --- a/web/src/_common.scss +++ b/web/src/_common.scss @@ -337,3 +337,7 @@ table th { .inline-flex { display: inline-flex !important; } + +.no-wrap { + white-space: nowrap !important; +} diff --git a/web/src/actions/appActions.js b/web/src/actions/appActions.js index ff05682d16..46ea2f12ff 100644 --- a/web/src/actions/appActions.js +++ b/web/src/actions/appActions.js @@ -38,6 +38,7 @@ export const NOTIFICATIONS = { UNSTAKE: 'UNSTAKE', MOVE_XHT: 'MOVE_XHT', METAMASK_ERROR: 'METAMASK_ERROR', + CONFIGURE_APPS: 'CONFIGURE_APPS', }; export const CONTACT_FORM = 'CONTACT_FORM'; export const HELPFUL_RESOURCES_FORM = 'HELPFUL_RESOURCES_FORM'; @@ -376,6 +377,9 @@ export const openConnectViaDesktop = (data = {}) => export const openMetamaskError = (data = {}) => setNotification(NOTIFICATIONS.METAMASK_ERROR, data, true); +export const openConfigureApps = (onRemove) => + setNotification(NOTIFICATIONS.CONFIGURE_APPS, { onRemove }, true); + export const openRiskPortfolioOrderWarning = (data = {}) => setNotification(RISK_PORTFOLIO_ORDER_WARING, data, true); diff --git a/web/src/components/AppBar/_AppBar.scss b/web/src/components/AppBar/_AppBar.scss index a1571f7815..1d720cd114 100644 --- a/web/src/components/AppBar/_AppBar.scss +++ b/web/src/components/AppBar/_AppBar.scss @@ -371,7 +371,8 @@ $app-menu-width: calc(100vw - 40rem); .tab-history-st, .help-question, .tab-signout, - .tab-api0 { + .tab-api0, + .apps { fill: $colors-black; } } diff --git a/web/src/components/AppMenuSidebar/_AppMenuSidebar.scss b/web/src/components/AppMenuSidebar/_AppMenuSidebar.scss index 7f7f1fbfe8..fd9f0aa6b8 100644 --- a/web/src/components/AppMenuSidebar/_AppMenuSidebar.scss +++ b/web/src/components/AppMenuSidebar/_AppMenuSidebar.scss @@ -50,7 +50,8 @@ .help-question, .tab-signout, .tab-history-st, - .stake-page-icon { + .stake-page-icon, + .apps { fill: $base_top-bar-navigation_text !important; } } diff --git a/web/src/config/icons/dark.js b/web/src/config/icons/dark.js index 79a86fb7b2..5b993e3503 100644 --- a/web/src/config/icons/dark.js +++ b/web/src/config/icons/dark.js @@ -39,7 +39,7 @@ const nestedIcons = { SECURITY: '/assets/images/tab-security.svg', VERIFY: '/assets/images/tab-verify.svg', SETTING: '/assets/images/tab-setting.svg', - APPS: '/assets/images/tab-setting.svg', + APPS: '/assets/images/apps.svg', API: '/assets/images/tab-api.svg', STAKE: '/assets/images/stake-page-icon.svg', }, @@ -78,8 +78,10 @@ const nestedIcons = { }, APPS: { - ALL: '/assets/images/tab-setting.svg', - USER: '/assets/images/tab-setting.svg', + ALL: '/assets/images/all-apps.svg', + USER: '/assets/images/my-apps.svg', + CONFIGURE: '/assets/images/interface-settings-icon.svg', + REMOVE: '/assets/images/cancel-cross-active.svg', }, SECURITY: { diff --git a/web/src/config/icons/static.js b/web/src/config/icons/static.js index 9f50ac8efd..6cf9e39d16 100644 --- a/web/src/config/icons/static.js +++ b/web/src/config/icons/static.js @@ -71,6 +71,7 @@ const icons = { CANCEL_CROSS_ACTIVE: '/assets/images/cancel-cross-active.svg', VERIFICATION_ICON: '/assets/images/verification-green-tick.svg', CHAT_FEATURE_ICON: '/assets/images/chat-feature-icon.svg', + APPS_FEATURE_ICON: '/assets/images/apps.svg', HOME_PAGE_FEATURE_ICON: '/assets/images/home-page.svg', USER_EMAIL: '/assets/images/user-email.svg', USER_EMAIL_VERIFIED: '/assets/images/user-email-verified.svg', diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index b9f1a85934..0c08f1a437 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -478,7 +478,8 @@ "ALL_APPS": { "TAB_TITLE": "All apps", "TITLE": "Exchange apps", - "SUBTITLE": "Get more functionality from your exchange account by simply selecting an app below and clicking Add button." + "SUBTITLE": "Get more functionality from your exchange account by simply selecting an app below and clicking Add button.", + "SEARCH_PLACEHOLDER": "Search apps..." }, "MY_APPS": { "TAB_TITLE": "My apps", @@ -489,11 +490,30 @@ "APP_NAME": "App name", "DESCRIPTION": "Description", "ACTION": "Action", - "VIEW_APP": "View app" + "CONFIGURE": "Configure", + "VIEW_APP": "View app", + "ADD": "Add", + "NOT_FOUND": "Can't find this app...", + "RETRY": "Try another search term" }, "APP_DETAILS": { "BACK_TO_APPS": "{0} to my apps", "BACK": "Back" + }, + "CONFIGURE": { + "TITLE": "Configure app", + "SUBTITLE": "Configure your app below:", + "REMOVE": "Remove app", + "TEXT": "If you are having issues with your app please contact us by clicking help below.", + "BACK": "BACK", + "HELP": "HELP" + }, + "REMOVE": { + "TITLE": "Remove app", + "SUBTITLE": "This will remove the app from your 'My apps' list.", + "TEXT": "Are you sure you want to remove this app?", + "BACK": "BACK", + "CONFIRM": "CONFIRM" } }, "TRANSACTION_HISTORY": { diff --git a/web/src/config/menu.js b/web/src/config/menu.js index 1d6c719d72..5c773a9d45 100644 --- a/web/src/config/menu.js +++ b/web/src/config/menu.js @@ -47,6 +47,13 @@ export const MENU_ITEMS = { hide_from_appbar: true, hide_from_bottom_nav: true, }, + { + id: 'apps', + path: '/apps', + icon_id: 'TAB_APPS', + string_id: 'ACCOUNTS.TAB_APPS', + hide_from_bottom_nav: true, + }, { id: 'chat', path: '/chat', @@ -92,12 +99,6 @@ export const MENU_ITEMS = { hide_from_appbar: true, hide_from_bottom_nav: true, }, - { - path: '/apps', - icon_id: 'TAB_APPS', - string_id: 'ACCOUNTS.TAB_APPS', - hide_from_bottom_nav: true, - }, ], bottom: [ { diff --git a/web/src/containers/Admin/General/InterfaceForm.js b/web/src/containers/Admin/General/InterfaceForm.js index 46832cad84..e53d0586ff 100644 --- a/web/src/containers/Admin/General/InterfaceForm.js +++ b/web/src/containers/Admin/General/InterfaceForm.js @@ -28,6 +28,7 @@ const InterfaceForm = ({ stake_page: !!values.stake_page, home_page: isUpgrade ? false : !!values.home_page, ultimate_fiat: !!values.ultimate_fiat, + apps: !!values.apps, }; handleSaveInterface(formValues); } @@ -50,6 +51,7 @@ const InterfaceForm = ({ if (isUpgrade) { initialValue.home_page = false; initialValue.chat = false; + initialValue.apps = false; } return ( <div className="general-wrapper"> @@ -239,6 +241,49 @@ const InterfaceForm = ({ </Checkbox> </Item> </div> + <div className={classnames({ 'disabled-area': isUpgrade })}> + <Item name="apps" valuePropName="checked"> + <Checkbox className="mt-3"> + <div className="d-flex align-items-center"> + <div className="feature-trade-box mr-1"> + <ReactSVG + src={STATIC_ICONS.APPS_FEATURE_ICON} + className="feature-apps-icon" + /> + </div> + <div className="ml-2 checkbox-txt"> + Apps + <div className="d-flex justify-content-between"> + <div className="small-text">(Apps ...)</div> + </div> + </div> + </div> + </Checkbox> + </Item> + </div> + {isUpgrade && ( + <div className="d-flex"> + <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> + <div> + <div className="font-weight-bold"> + Start your crypto culture + </div> + <div>Allow your users to socialize through chat</div> + </div> + <div className="ml-5 button-wrapper"> + <a + href="https://dash.bitholla.com/billing" + target="_blank" + rel="noopener noreferrer" + > + <Button type="primary" className="w-100"> + Upgrade Now + </Button> + </a> + </div> + </div> + </div> + )} </div> {isUpgrade ? ( <div className="d-flex"> diff --git a/web/src/containers/Admin/General/index.css b/web/src/containers/Admin/General/index.css index 8277affe30..2e4b963ad8 100644 --- a/web/src/containers/Admin/General/index.css +++ b/web/src/containers/Admin/General/index.css @@ -158,11 +158,27 @@ stroke: #808080; fill-opacity: 0.5; } +.interface-box .feature-apps-icon { + width: 42px; + margin: 4px 16px; + fill: #808080 !important; + fill-opacity: 0.5; +} +.feature-apps-icon .apps { + fill: #808080 !important; +} .interface-box .ant-checkbox-wrapper-checked .feature-icon, .interface-box .ant-checkbox-wrapper-checked .feature-chat-icon { stroke: #ffffff; fill-opacity: 1; } +.interface-box .ant-checkbox-wrapper-checked .feature-apps-icon { + fill: #ffffff !important; + fill-opacity: 1; +} +.feature-apps-icon .apps { + fill: #ffffff !important; +} .interface-box .checkbox-txt, .interface-box .small-text { color: #808080; diff --git a/web/src/containers/App/App.js b/web/src/containers/App/App.js index 9c03584f01..f087b80b33 100644 --- a/web/src/containers/App/App.js +++ b/web/src/containers/App/App.js @@ -64,6 +64,7 @@ import AppFooter from '../../components/AppFooter'; import OperatorControls from 'containers/OperatorControls'; import MarketSelector from 'components/AppBar/MarketSelector'; import ConnectViaDesktop from 'containers/Stake/components/ConnectViaDesktop'; +import ConfigureApps from 'containers/Apps/ConfigureApps'; import { getClasesForLanguage, @@ -511,6 +512,13 @@ class App extends Component { text={data} /> ); + case NOTIFICATIONS.CONFIGURE_APPS: + return ( + <ConfigureApps + onClose={this.onCloseDialog} + onRemove={data.onRemove} + /> + ); case CONNECT_VIA_DESKTOP: return <ConnectViaDesktop onClose={this.onCloseDialog} />; case RISK_PORTFOLIO_ORDER_WARING: diff --git a/web/src/containers/AppDetails/index.js b/web/src/containers/AppDetails/index.js index 692d18d72a..fa3886bd5d 100644 --- a/web/src/containers/AppDetails/index.js +++ b/web/src/containers/AppDetails/index.js @@ -1,17 +1,33 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { isMobile } from 'react-device-detect'; import { openContactForm } from 'actions/appActions'; -import { IconTitle, HeaderSection, EditWrapper } from 'components'; +import { IconTitle, HeaderSection, EditWrapper, SmartTarget } from 'components'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; +import { generateDynamicTarget } from 'utils/id'; +import { userAppsSelector } from 'containers/Apps/utils'; + +const Index = ({ openContactForm, icons: ICONS, router, userApps }) => { + const [mounted, setMounted] = useState(false); + const goBack = () => router.push('/apps'); -const Index = ({ openContactForm, icons: ICONS, router }) => { const { params: { app }, } = router; - const goBack = () => router.push('/apps'); + + if (!mounted) { + if (!app || userApps.map(({ name }) => name).includes(app)) { + goBack(); + } + } + + const id = generateDynamicTarget(app, 'app'); + + useEffect(() => { + setMounted(true); + }, []); return ( <div className="presentation_container apply_rtl settings_container"> @@ -42,11 +58,14 @@ const Index = ({ openContactForm, icons: ICONS, router }) => { </div> </div> </HeaderSection> + <SmartTarget id={id} /> </div> ); }; -const mapStateToProps = (state) => ({}); +const mapStateToProps = (state) => ({ + userApps: userAppsSelector(state), +}); const mapDispatchToProps = (dispatch) => ({ openContactForm: bindActionCreators(openContactForm, dispatch), diff --git a/web/src/containers/Apps/All.js b/web/src/containers/Apps/All.js index 17390d7489..261364bf06 100644 --- a/web/src/containers/Apps/All.js +++ b/web/src/containers/Apps/All.js @@ -1,17 +1,17 @@ -import React from 'react'; -import { IconTitle, EditWrapper, Table } from 'components'; +import React, { useState, useRef, useEffect } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { Input } from 'antd'; +import debounce from 'lodash.debounce'; +import { SearchOutlined } from '@ant-design/icons'; +import { updateUserSettings, setUserData } from 'actions/userAction'; +import { IconTitle, EditWrapper, Table, Button, Image } from 'components'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; +import { unique } from 'utils/data'; +import { isEnabled, appsSelector } from './utils'; -const data = [ - { - id: 0, - name: 'Name of the app', - description: 'App description short sentence here', - }, -]; - -const generateHeaders = () => { +const generateHeaders = (addApp, isAdded) => { return [ { stringId: 'USER_APPS.TABLE.APP_NAME', @@ -25,16 +25,85 @@ const generateHeaders = () => { }, { label: '', - key: 'name', + renderCell: ({ name }, key) => ( + <td key={`${key}-${name}-app`} className="text-align-right"> + {isAdded(name) ? ( + <Image icon={''} iconId={''} wrapperClassName={''} /> + ) : ( + <Button + label={STRINGS['USER_APPS.TABLE.ADD']} + className="add-app-button" + onClick={() => addApp(name)} + /> + )} + </td> + ), }, ]; }; -const All = ({ icons: ICONS }) => { +const NoData = ({ onClick }) => ( + <div className="text-align-center"> + <div>{STRINGS['USER_APPS.TABLE.NOT_FOUND']}</div> + <div onClick={onClick} className="blue-link underline-text pointer"> + {STRINGS['USER_APPS.TABLE.RETRY']} + </div> + </div> +); + +const All = ({ + icons: ICONS, + apps, + settings: { apps: user_apps = [] }, + setUserData, +}) => { + const [search, setSearch] = useState(); + const [data, setData] = useState(apps); + + const inputRef = useRef(null); + + const addApp = (name) => { + // send add app request and show toast notification + const settings = { + apps: unique([...user_apps, name]), + }; + updateUserSettings(settings) + .then(({ data }) => setUserData(data)) + .catch((err) => console.log('error')); + }; + + const isAdded = (name) => { + return isEnabled(name, user_apps); + }; + + const retry = () => { + setSearch(''); + inputRef.current.focus({ + cursor: 'start', + }); + }; + + const onSearch = (search, apps) => { + if (search) { + const result = apps.filter(({ name }) => + name.toLowerCase().includes(search.toLowerCase()) + ); + setData(result); + } else { + setData(apps); + } + }; + + const debounced = useRef(debounce(onSearch, 1000)); + + useEffect(() => { + debounced.current(search, apps); + }, [search, apps]); + return ( <div> <div className="settings-form-wrapper"> - <div className="settings-form"> + <div className="settings-form apps-form"> <IconTitle stringId="USER_APPS.ALL_APPS.TITLE" text={STRINGS['USER_APPS.ALL_APPS.TITLE']} @@ -46,12 +115,30 @@ const All = ({ icons: ICONS }) => { {STRINGS['USER_APPS.ALL_APPS.SUBTITLE']} </EditWrapper> </div> + <div> + <Input + prefix={<SearchOutlined className="secondary-text" />} + placeholder={STRINGS['USER_APPS.ALL_APPS.SEARCH_PLACEHOLDER']} + value={search} + onChange={({ target: { value } }) => setSearch(value)} + style={{ + width: 200, + }} + bordered={false} + className="kit-divider" + ref={inputRef} + /> + </div> <Table + showHeaderNoData={true} rowClassName="pt-2 pb-2" - headers={generateHeaders()} + headers={generateHeaders(addApp, isAdded)} count={data.length} + pageSize={data.length} data={data} - rowKey={({ id }) => id} + rowKey={({ name }) => name} + displayPaginator={false} + {...(search ? { noData: <NoData onClick={retry} /> } : {})} /> </div> </div> @@ -59,4 +146,19 @@ const All = ({ icons: ICONS }) => { ); }; -export default withConfig(All); +const mapStateToProps = (state) => { + const { + user: { settings }, + } = state; + + return { + apps: appsSelector(state), + settings, + }; +}; + +const mapDispatchToProps = (dispatch) => ({ + setUserData: bindActionCreators(setUserData, dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(withConfig(All)); diff --git a/web/src/containers/Apps/ConfigureApps.js b/web/src/containers/Apps/ConfigureApps.js new file mode 100644 index 0000000000..d7204da43b --- /dev/null +++ b/web/src/containers/Apps/ConfigureApps.js @@ -0,0 +1,124 @@ +import React, { useState } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { IconTitle, EditWrapper, Button, ActionNotification } from 'components'; +import STRINGS from 'config/localizedStrings'; +import withConfig from 'components/ConfigProvider/withConfig'; +import { openHelpdesk } from 'actions/appActions'; + +const ConfigureApps = ({ + onClose, + icons: ICONS, + openHelpdesk, + onRemove = () => {}, +}) => { + const [showRemove, setShowRemove] = useState(false); + const openNewForm = () => { + onClose(); + openHelpdesk({ category: 'bug' }); + }; + + return showRemove ? ( + <div className="help-wrapper"> + <IconTitle + iconId="APPS_REMOVE" + iconPath={ICONS['APPS_REMOVE']} + stringId="USER_APPS.REMOVE.TITLE" + text={STRINGS['USER_APPS.REMOVE.TITLE']} + textType="title" + underline={true} + className="w-100" + subtitle={STRINGS['USER_APPS.REMOVE.SUBTITLE']} + /> + <div> + <div className="my-5 mx-3"> + <div> + {STRINGS['USER_APPS.REMOVE.TEXT']} + <EditWrapper stringId="USER_APPS.REMOVE.TEXT" /> + </div> + </div> + + <div className="d-flex"> + <div className="w-50"> + <EditWrapper stringId="USER_APPS.REMOVE.BACK" /> + <Button + label={STRINGS['USER_APPS.REMOVE.BACK']} + onClick={() => setShowRemove(false)} + /> + </div> + <div className="separator" /> + <div className="w-50"> + <EditWrapper stringId="USER_APPS.REMOVE.CONFIRM" /> + <Button + label={STRINGS['USER_APPS.REMOVE.CONFIRM']} + onClick={onRemove} + /> + </div> + </div> + </div> + </div> + ) : ( + <div className="help-wrapper"> + <IconTitle + iconId="APPS_CONFIGURE" + iconPath={ICONS['APPS_CONFIGURE']} + stringId="USER_APPS.CONFIGURE.TITLE" + text={STRINGS['USER_APPS.CONFIGURE.TITLE']} + textType="title" + underline={true} + className="w-100" + subtitle={STRINGS['USER_APPS.CONFIGURE.SUBTITLE']} + /> + <div> + <div className="my-5 mx-3"> + <ActionNotification + stringId="USER_APPS.CONFIGURE.REMOVE" + text={STRINGS['USER_APPS.CONFIGURE.REMOVE']} + iconId="CANCEL_CROSS_ACTIVE" + iconPath={ICONS['CANCEL_CROSS_ACTIVE']} + onClick={() => setShowRemove(true)} + status="information" + textPosition="left" + className="remove-action" + /> + </div> + + <div className="my-5 mx-3"> + <div> + {STRINGS['USER_APPS.CONFIGURE.TEXT']} + <EditWrapper stringId="USER_APPS.CONFIGURE.TEXT" /> + </div> + </div> + + <div className="d-flex"> + <div className="w-50"> + <EditWrapper stringId="USER_APPS.CONFIGURE.BACK" /> + <Button + label={STRINGS['USER_APPS.CONFIGURE.BACK']} + onClick={onClose} + /> + </div> + <div className="separator" /> + <div className="w-50"> + <EditWrapper stringId="USER_APPS.CONFIGURE.HELP" /> + <Button + label={STRINGS['USER_APPS.CONFIGURE.HELP']} + onClick={openNewForm} + /> + </div> + </div> + </div> + </div> + ); +}; + +const mapStateToProps = () => ({}); + +const mapDispatchToProps = (dispatch) => ({ + openHelpdesk: bindActionCreators(openHelpdesk, dispatch), +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(withConfig(ConfigureApps)); diff --git a/web/src/containers/Apps/User.js b/web/src/containers/Apps/User.js index 075faf8667..4a118c4d32 100644 --- a/web/src/containers/Apps/User.js +++ b/web/src/containers/Apps/User.js @@ -1,18 +1,15 @@ import React from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import { IconTitle, EditWrapper, Table } from 'components'; +import { updateUserSettings, setUserData } from 'actions/userAction'; +import { openConfigureApps, closeNotification } from 'actions/appActions'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { withRouter } from 'react-router'; +import { userAppsSelector } from './utils'; -const data = [ - { - id: 0, - name: 'Name of the app', - description: 'App description short sentence here', - }, -]; - -const generateHeaders = (goToDetails) => { +const generateHeaders = (goToDetails, openConfigs) => { return [ { stringId: 'USER_APPS.TABLE.APP_NAME', @@ -24,13 +21,27 @@ const generateHeaders = (goToDetails) => { label: STRINGS['USER_APPS.TABLE.DESCRIPTION'], key: 'description', }, + { + stringId: 'USER_APPS.TABLE.CONFIGURE', + label: STRINGS['USER_APPS.TABLE.CONFIGURE'], + renderCell: ({ name }, key) => ( + <td key={`${key}-${name}-configure`}> + <span + className="blue-link underline-text pointer no-wrap" + onClick={() => openConfigs(name)} + > + {STRINGS['USER_APPS.TABLE.CONFIGURE']} + </span> + </td> + ), + }, { stringId: 'USER_APPS.TABLE.ACTION', label: STRINGS['USER_APPS.TABLE.ACTION'], - renderCell: ({ id, name }, key) => ( - <td key={`${key}-${id}-app`}> + renderCell: ({ name }, key) => ( + <td key={`${key}-${name}-action`}> <span - className="blue-link underline-text pointer" + className="blue-link underline-text pointer no-wrap" onClick={() => goToDetails(name)} > {STRINGS['USER_APPS.TABLE.VIEW_APP']} @@ -41,8 +52,29 @@ const generateHeaders = (goToDetails) => { ]; }; -const User = ({ icons: ICONS, router }) => { +const User = ({ + icons: ICONS, + router, + userApps: data, + settings: { apps = [] }, + setUserData, + openConfigureApps, + closeNotification, +}) => { const goToDetails = (name) => router.push(`apps/details/${name}`); + const onRemove = (name) => { + // send add app request and show toast notification + const settings = { + apps: apps.filter((app) => app !== name), + }; + updateUserSettings(settings) + .then(({ data }) => { + setUserData(data); + closeNotification(); + }) + .catch((err) => console.log('error')); + }; + const openConfigs = (name) => openConfigureApps(() => onRemove(name)); return ( <div> @@ -60,11 +92,14 @@ const User = ({ icons: ICONS, router }) => { </EditWrapper> </div> <Table + showHeaderNoData={true} rowClassName="pt-2 pb-2" - headers={generateHeaders(goToDetails)} + headers={generateHeaders(goToDetails, openConfigs)} count={data.length} + pageSize={data.length} data={data} - rowKey={({ id }) => id} + rowKey={({ name }) => name} + displayPaginator={false} /> </div> </div> @@ -72,4 +107,24 @@ const User = ({ icons: ICONS, router }) => { ); }; -export default withRouter(withConfig(User)); +const mapStateToProps = (state) => { + const { + user: { settings }, + } = state; + + return { + userApps: userAppsSelector(state), + settings, + }; +}; + +const mapDispatchToProps = (dispatch) => ({ + setUserData: bindActionCreators(setUserData, dispatch), + openConfigureApps: bindActionCreators(openConfigureApps, dispatch), + closeNotification: bindActionCreators(closeNotification, dispatch), +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(withRouter(withConfig(User))); diff --git a/web/src/containers/Apps/_Apps.scss b/web/src/containers/Apps/_Apps.scss new file mode 100644 index 0000000000..529bed33a3 --- /dev/null +++ b/web/src/containers/Apps/_Apps.scss @@ -0,0 +1,19 @@ +.add-app-button { + border-radius: 24px; + height: 24px !important; + min-width: 70px !important; + width: auto !important; + text-transform: none !important; +} + +.apps-form { + .ant-input { + color: $colors-main-black; + } +} + +.action_notification-wrapper { + &.remove-action { + position: relative !important; + } +} diff --git a/web/src/containers/Apps/index.js b/web/src/containers/Apps/index.js index f3d16e8cc0..1edabae983 100644 --- a/web/src/containers/Apps/index.js +++ b/web/src/containers/Apps/index.js @@ -18,7 +18,7 @@ import User from './User'; const Index = ({ icons: ICONS, openContactForm }) => { const [tabs, setTabs] = useState([]); - const [activeTab, setActiveTab] = useState(); + const [activeTab, setActiveTab] = useState(0); useEffect(() => { updateTabs(); @@ -66,8 +66,8 @@ const Index = ({ icons: ICONS, openContactForm }) => { stringId="ACCOUNTS.TAB_APPS" text={STRINGS['ACCOUNTS.TAB_APPS']} textType="title" - iconPath={ICONS['TAB_SETTING']} - iconId={STRINGS['ACCOUNTS.TAB_APPS']} + iconPath={ICONS['TAB_APPS']} + iconId="TAB_APPS" /> )} diff --git a/web/src/containers/Apps/utils.js b/web/src/containers/Apps/utils.js new file mode 100644 index 0000000000..df0e15103e --- /dev/null +++ b/web/src/containers/Apps/utils.js @@ -0,0 +1,16 @@ +import { createSelector } from 'reselect'; + +const getPlugins = (state) => state.app.plugins; +const getUserApps = (state) => state.user.settings.apps || []; +export const appsSelector = createSelector([getPlugins], (plugins) => + plugins.filter((type) => type === 'app') +); + +export const userAppsSelector = createSelector( + [getUserApps, appsSelector], + (user_apps, apps) => { + return apps.filter(({ name }) => user_apps.includes(name)); + } +); + +export const isEnabled = (app, apps) => apps.includes(app); diff --git a/web/src/containers/_containers.scss b/web/src/containers/_containers.scss index b3a47b0043..4b015bbec5 100644 --- a/web/src/containers/_containers.scss +++ b/web/src/containers/_containers.scss @@ -26,3 +26,4 @@ @import './ConfirmChangePassword/ConfirmChangePassword'; @import './StakeDetails/StakeDetails'; @import './Stake/Stake'; +@import './Apps/Apps'; diff --git a/web/src/index.css b/web/src/index.css index 40e14b0579..f5988d74c7 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -344,6 +344,9 @@ table th { .inline-flex { display: inline-flex !important; } +.no-wrap { + white-space: nowrap !important; } + .app_container { min-height: 100vh; background-color: var(--base_background); @@ -5396,6 +5399,19 @@ table th { .area-disabled { opacity: 0.5; } +.add-app-button { + border-radius: 24px; + height: 24px !important; + min-width: 70px !important; + width: auto !important; + text-transform: none !important; } + +.apps-form .ant-input { + color: var(--labels_important-active-labels-text-graphics); } + +.action_notification-wrapper.remove-action { + position: relative !important; } + .sidebar-row { min-height: 3rem; min-width: 10rem; } @@ -6017,7 +6033,8 @@ table th { .app_bar .app-bar-account-menu .app-bar-account-list-icon svg .tab-history-st, .app_bar .app-bar-account-menu .app-bar-account-list-icon svg .help-question, .app_bar .app-bar-account-menu .app-bar-account-list-icon svg .tab-signout, - .app_bar .app-bar-account-menu .app-bar-account-list-icon svg .tab-api0 { + .app_bar .app-bar-account-menu .app-bar-account-list-icon svg .tab-api0, + .app_bar .app-bar-account-menu .app-bar-account-list-icon svg .apps { fill: var(--labels_secondary-inactive-label-text-graphics); } .app_bar .app-bar-account-menu .app-bar-account-list-icon svg .account-active-1 { fill: var(--calculated_base_top-bar-navigation_text-inactive); } @@ -10770,6 +10787,7 @@ table th { .app-side-bar .list_active .tab-signout, .app-side-bar .list_active .tab-history-st, .app-side-bar .list_active .stake-page-icon, + .app-side-bar .list_active .apps, .app-side-bar .app-menu-bar-side_list:hover .tab-summary0, .app-side-bar .app-menu-bar-side_list:hover .tab-wallet0, .app-side-bar .app-menu-bar-side_list:hover .tab-security0, @@ -10779,7 +10797,8 @@ table th { .app-side-bar .app-menu-bar-side_list:hover .help-question, .app-side-bar .app-menu-bar-side_list:hover .tab-signout, .app-side-bar .app-menu-bar-side_list:hover .tab-history-st, - .app-side-bar .app-menu-bar-side_list:hover .stake-page-icon { + .app-side-bar .app-menu-bar-side_list:hover .stake-page-icon, + .app-side-bar .app-menu-bar-side_list:hover .apps { fill: var(--calculated_base_top-bar-navigation_text) !important; } .app-side-bar .list_active { @@ -11072,7 +11091,10 @@ svg .incoming-btc-01, svg .incoming-coin-01, svg .coin-btc-5, svg .stake-page-icon, -svg .fiat-kyc { +svg .fiat-kyc, +svg .all-apps, +svg .my-apps, +svg .apps { fill: var(--labels_secondary-inactive-label-text-graphics); } svg .account-inactive { diff --git a/web/src/index.scss b/web/src/index.scss index 441253dea7..4be3c6dac7 100644 --- a/web/src/index.scss +++ b/web/src/index.scss @@ -190,7 +190,10 @@ svg { .incoming-coin-01, .coin-btc-5, .stake-page-icon, - .fiat-kyc { + .fiat-kyc, + .all-apps, + .my-apps, + .apps { fill: $colors-black; } diff --git a/web/src/reducers/appReducer.js b/web/src/reducers/appReducer.js index 37f18fdb38..8241506288 100644 --- a/web/src/reducers/appReducer.js +++ b/web/src/reducers/appReducer.js @@ -448,15 +448,18 @@ const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { } case SET_WEB_VIEWS: { const allWebViews = []; - payload.enabledPlugins.forEach(({ name, web_view = [] }) => { - if (web_view && web_view.length) { - const named_web_views = web_view.map((viewObj) => ({ - ...viewObj, - name, - })); - allWebViews.push(...named_web_views); + payload.enabledPlugins.forEach( + ({ name, web_view = [], type: plugin_type }) => { + if (web_view && web_view.length) { + const named_web_views = web_view.map((viewObj) => ({ + ...viewObj, + name, + plugin_type, + })); + allWebViews.push(...named_web_views); + } } - }); + ); const remoteRoutes = []; allWebViews.forEach(({ meta, name }) => { @@ -484,9 +487,11 @@ const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { const CLUSTERED_WEB_VIEWS = {}; allWebViews.forEach((plugin) => { - const { target: staticTarget, meta, name } = plugin; + const { target: staticTarget, meta, name, plugin_type } = plugin; let target; - if (staticTarget) { + if (plugin_type === 'app') { + target = generateDynamicTarget(name, 'app'); + } else if (staticTarget) { target = staticTarget; } else if (meta) { const { diff --git a/web/src/reducers/userReducer.js b/web/src/reducers/userReducer.js index dd88c6e033..87c262f959 100644 --- a/web/src/reducers/userReducer.js +++ b/web/src/reducers/userReducer.js @@ -116,6 +116,7 @@ const INITIAL_STATE = { order_portfolio_percentage: 80, popup_warning: true, }, + apps: [], }, addressRequest: INITIAL_ADDRESS_OBJECT, limits: INITIAL_LIMIT_OBJECT, diff --git a/web/src/utils/id.js b/web/src/utils/id.js index e25c4c3b79..bc8a4e86df 100644 --- a/web/src/utils/id.js +++ b/web/src/utils/id.js @@ -10,6 +10,8 @@ export const generateDynamicTarget = ( const name = pluginName.toUpperCase(); const sub = subType.toUpperCase(); switch (type) { + case 'app': + return `APPLICATION__${name}`; case 'verification': return `REMOTE_VERIFICATION_TAB__${name}__${sub}`; case 'page': From d9dd67ee2247bb1cdb4023df764c7506d4f18608 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 18:44:43 +0300 Subject: [PATCH 050/132] Update check deposit status popup --- web/public/assets/images/search-blockchain.svg | 15 +++++++++++++++ web/src/components/CheckDeposit/index.js | 10 ++++------ web/src/config/icons/static.js | 1 + web/src/config/lang/ar.json | 1 + web/src/config/lang/en.json | 1 + web/src/config/lang/fa.json | 1 + web/src/config/lang/pt.json | 1 + web/src/config/lang/tr.json | 1 + web/src/index.scss | 4 ++++ 9 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 web/public/assets/images/search-blockchain.svg diff --git a/web/public/assets/images/search-blockchain.svg b/web/public/assets/images/search-blockchain.svg new file mode 100644 index 0000000000..6d9ebb1001 --- /dev/null +++ b/web/public/assets/images/search-blockchain.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 480 480" style="enable-background:new 0 0 480 480;" xml:space="preserve"> + <g> + <path d="M460.7,460.1l-55.6-64.9c18.8-21.9,26.6-51.2,21.1-79.5c-5.5-28.3-23.8-52.5-49.5-65.7c-25.7-13.2-56-13.8-82.2-1.7 + c-26.2,12.1-45.5,35.5-52.2,63.6c-6.7,28.1-0.2,57.7,17.7,80.3c17.9,22.6,45.2,35.8,74.1,35.7c21.2,0,41.8-7.2,58.4-20.5l55,64.1h0 + c1.4,2.1,3.6,3.5,6.1,3.8s5-0.4,6.9-2.1s3-4,3-6.5c0-2.5-1-4.9-2.9-6.6L460.7,460.1z M257.2,332.4c0.3-20.3,8.6-39.7,23.1-53.9 + s34.1-22.1,54.4-21.9c20.3,0.2,39.8,8.4,54,22.9c14.3,14.5,22.2,34,22.1,54.3c-0.1,20.3-8.2,39.8-22.6,54.1 + c-14.4,14.3-33.9,22.4-54.2,22.4c-20.5-0.2-40.1-8.5-54.5-23.1C265.1,372.7,257.1,352.9,257.2,332.4L257.2,332.4z" /> + <path d="M213.4,348.9L191.6,362V203.9l140-80.8v88.2c0,4.8,3.9,8.8,8.8,8.8s8.8-3.9,8.8-8.8v-107c0-3.1-1.7-6-4.4-7.6L187.2,5.9 + c-2.7-1.6-6-1.6-8.8,0L20.9,96.8c-2.7,1.6-4.4,4.5-4.4,7.6v182c0,3.1,1.7,6,4.4,7.6l157.5,90.7c2.7,1.6,6,1.6,8.8,0l35-20.8 + c3.6-2.6,4.7-7.6,2.4-11.5C222.4,348.6,217.5,347.1,213.4,348.9L213.4,348.9z M182.8,23.5l143.1,82.6l-143.1,82.6L39.7,106.1 + L182.8,23.5z M34.1,123.1l140,80.8V362l-140-80.9V123.1z" /> + </g> +</svg> \ No newline at end of file diff --git a/web/src/components/CheckDeposit/index.js b/web/src/components/CheckDeposit/index.js index b640cf01a2..11e048b2f8 100644 --- a/web/src/components/CheckDeposit/index.js +++ b/web/src/components/CheckDeposit/index.js @@ -94,17 +94,15 @@ const CheckDeposit = ({ <form className="check-deposit-modal-wrapper" onSubmit={handleSubmit}> <div className="d-flex justify-content-center align-items-center flex-column"> <img - src={STATIC_ICONS.SEARCH} + src={STATIC_ICONS.SEARCH_BLOCKCHAIN} alt="search" - className="search-icon mb-4" + className="search-icon mb-4 blockchain-search" /> <h2>{STRINGS['DEPOSIT_STATUS.CHECK_DEPOSIT_STATUS']}</h2> </div> <div className="inner-content"> - <span className="field-header"> - {STRINGS['DEPOSIT_STATUS.CHECK_DEPOSIT_STATUS']} - </span> - <div className="mb-5"> + <span>{STRINGS['DEPOSIT_STATUS.SEARCH_BLOCKCHAIN_FOR_DEPOSIT']}</span> + <div className="mb-5 field-header"> {STRINGS['DEPOSIT_STATUS.STATUS_DESCRIPTION']} </div> {renderFields(formFields)} diff --git a/web/src/config/icons/static.js b/web/src/config/icons/static.js index 2ddc6e8dfb..882d19f784 100644 --- a/web/src/config/icons/static.js +++ b/web/src/config/icons/static.js @@ -51,6 +51,7 @@ const icons = { DEPOSIT_TIERS_SECTION: '/assets/images/deposit-tier-section.svg', WITHDRAW_TIERS_SECTION: '/assets/images/withdraw-tier-section.svg', TAKER_TIERS_SECTION: '/assets/images/taker-tier-section.svg', + SEARCH_BLOCKCHAIN: '/assets/images/search-blockchain.svg', MAKER_TIERS_SECTION: '/assets/images/maker-tier-section.svg', LIMITS_SECTION_ICON: '/assets/images/limits-coin-tiers.svg', FEES_SECTION_ICON: '/assets/images/limits-pairs-tiers.svg', diff --git a/web/src/config/lang/ar.json b/web/src/config/lang/ar.json index ce82aa6e23..696daf18c1 100644 --- a/web/src/config/lang/ar.json +++ b/web/src/config/lang/ar.json @@ -1022,6 +1022,7 @@ "SEARCH": "بحث", "SEARCHING": "جاري البحث", "CHECK_DEPOSIT_STATUS": "تحقق من حالة الايداع", + "SEARCH_BLOCKCHAIN_FOR_DEPOSIT": "ابحث في blockchain عن الإيداع الخاص بك", "STATUS_DESCRIPTION": "يمكنك التحقق من حالة الإيداع الخاص بك عن طريق إضافة معرف المعاملة أدناه.", "TRANSACTION_ID": "معرف المعاملة ", "SEARCH_SUCCESS": "تم العثور على المعاملة!", diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index 16c8285730..e49d11d4b3 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -1118,6 +1118,7 @@ "NEW": "New", "SEARCH_FIELD_LABEL": "Paste your transaction ID", "CHECK_DEPOSIT_STATUS": "Check deposit status", + "SEARCH_BLOCKCHAIN_FOR_DEPOSIT": "Search blockchain for your deposit", "STATUS_DESCRIPTION": "You can check the status of your deposit by passing the transaction ID (hash) below.", "TRANSACTION_ID": "Transaction ID (hash)", "SEARCH_SUCCESS": "Search complete", diff --git a/web/src/config/lang/fa.json b/web/src/config/lang/fa.json index bed5d40011..6c3ed78df0 100644 --- a/web/src/config/lang/fa.json +++ b/web/src/config/lang/fa.json @@ -1294,6 +1294,7 @@ "NEW": "New", "SEARCH_FIELD_LABEL": "Paste your transaction ID", "CHECK_DEPOSIT_STATUS": "Check deposit status", + "SEARCH_BLOCKCHAIN_FOR_DEPOSIT": "Search blockchain for your deposit", "STATUS_DESCRIPTION": "You can check the status of your deposit by passing the transaction ID (hash) below.", "TRANSACTION_ID": "Transaction ID (hash)", "SEARCH_SUCCESS": "Search complete", diff --git a/web/src/config/lang/pt.json b/web/src/config/lang/pt.json index 32f2fef6c6..bb3b5d5c44 100644 --- a/web/src/config/lang/pt.json +++ b/web/src/config/lang/pt.json @@ -1012,6 +1012,7 @@ "SEARCH": "PESQUISAR", "SEARCHING": "PESQUISANDO", "CHECK_DEPOSIT_STATUS": "Verificar o status do depósito", + "SEARCH_BLOCKCHAIN_FOR_DEPOSIT": "Pesquise blockchain para seu depósito", "STATUS_DESCRIPTION": "Você pode verificar o status do seu depósito passando o ID da transação (hash) abaixo.", "TRANSACTION_ID": "ID de transação (hash)", "SEARCH_SUCCESS": "Pesquisa finalizada", diff --git a/web/src/config/lang/tr.json b/web/src/config/lang/tr.json index 82e8f2705f..c802b69781 100644 --- a/web/src/config/lang/tr.json +++ b/web/src/config/lang/tr.json @@ -1180,6 +1180,7 @@ "NEW": "Yeni", "SEARCH_FIELD_LABEL": "İşlem ID'sini yapıştırın", "CHECK_DEPOSIT_STATUS": "Yatırım durumunu kontrol et", + "SEARCH_BLOCKCHAIN_FOR_DEPOSIT": "Depozitonuz için blockchain arayın", "STATUS_DESCRIPTION": "Aşağıya İşlem ID'nizi (hash) yapıştırarak, yatırım durumunuzu kontrol edebilirsiniz.", "TRANSACTION_ID": "İşlem ID (hash)", "SEARCH_SUCCESS": "Arama tamamlandı", diff --git a/web/src/index.scss b/web/src/index.scss index 6deb084243..c8f1a46854 100644 --- a/web/src/index.scss +++ b/web/src/index.scss @@ -97,6 +97,10 @@ body { } } +.blockchain-search { + filter: brightness(0) invert(0.8); +} + input, input:focus { outline: none; From 3a4c2fdfc728935c719ae632c8e31c92b288a90a Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 18:58:10 +0300 Subject: [PATCH 051/132] Percent movement in quick trade will flashes green --- web/src/components/QuickTrade/index.js | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/web/src/components/QuickTrade/index.js b/web/src/components/QuickTrade/index.js index dd4ddfd694..eea752aa11 100644 --- a/web/src/components/QuickTrade/index.js +++ b/web/src/components/QuickTrade/index.js @@ -193,6 +193,23 @@ class QuickTrade extends Component { isUseBroker = false; } const increment_unit = isUseBroker ? SIZE && SIZE.STEP : increment_size; + + const getClassnameForPriceDifferences = (priceDifferences) => { + if (priceDifference === 0) + return 'price-diff-none trade-tab-price_diff_none'; + if (priceDifference < 0) + return 'price-diff-down trade-tab-price_diff_down'; + return 'price-diff-up trade-tab-price_diff_up'; + }; + + const getClassnameForTickDifferences = (tickDifferences, state) => { + if (tickDifferences === 0) + return `glance-price-diff-none glance-trade-tab-price_diff_none ${state}`; + if (tickDifferences < 0) + return `glance-price-diff-down glance-trade-tab-price_diff_down ${state}`; + return `glance-price-diff-up glance-trade-tab-price_diff_up ${state}`; + }; + return ( <div className="quick_trade-container"> <div @@ -276,15 +293,12 @@ class QuickTrade extends Component { <Transition in={inProp} timeout={1000}> {(state) => ( <div className="d-flex f-size-22"> + {console.log(tickerDiff)} <div className={classnames( 'title-font', - priceDifference < 0 - ? 'price-diff-down trade-tab-price_diff_down' - : 'price-diff-up trade-tab-price_diff_up', - tickerDiff < 0 - ? `glance-price-diff-down glance-trade-tab-price_diff_down ${state}` - : `glance-price-diff-up glance-trade-tab-price_diff_up ${state}` + getClassnameForPriceDifferences(priceDifference), + getClassnameForTickDifferences(tickerDiff, state) )} > {priceDifferencePercent} From 0467bf986f04a66b9dbe25f45a5c58afd2e5fbe8 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 19:59:07 +0300 Subject: [PATCH 052/132] undo --- .../containers/Trade/components/TradeHistory.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/web/src/containers/Trade/components/TradeHistory.js b/web/src/containers/Trade/components/TradeHistory.js index 6e772f0225..fa2169e8c9 100644 --- a/web/src/containers/Trade/components/TradeHistory.js +++ b/web/src/containers/Trade/components/TradeHistory.js @@ -86,7 +86,6 @@ class TradeHistory extends Component { const { isBase, isOpen } = this.state; const { pairData } = this.props; const { pair_base_display, pair_2_display } = pairData; - const { isDropdownOpen, setIsDropdownOpen } = useState(false); return [ { @@ -127,17 +126,10 @@ class TradeHistory extends Component { bordered={false} defaultValue={false} size="small" - onClick={() => { - setIsDropdownOpen((prev) => !prev); - }} suffixIcon={ - isDropdownOpen ? ( - <CaretUpOutlined /> - ) : ( - <CaretDownOutlined - onClick={() => this.dropdownVisibleChange(!isOpen)} - /> - ) + <CaretDownOutlined + onClick={() => this.dropdownVisibleChange(!isOpen)} + /> } value={isBase} onSelect={this.onSelect} From 00d022d5bd7164b6ebc00c98b2270ae972f84b29 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 20:04:18 +0300 Subject: [PATCH 053/132] Bug Fix --- web/src/components/Form/TradeFormFields/DropDown.js | 4 +++- web/src/containers/Trade/components/TradeHistory.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/web/src/components/Form/TradeFormFields/DropDown.js b/web/src/components/Form/TradeFormFields/DropDown.js index 1ed7787b98..f90021cf57 100644 --- a/web/src/components/Form/TradeFormFields/DropDown.js +++ b/web/src/components/Form/TradeFormFields/DropDown.js @@ -23,7 +23,9 @@ const DropDown = (props) => { bordered={false} size="small" onChange={onChange} - onClick={setIsOpen((prev) => !prev)} + onClick={() => { + setIsOpen((prev) => !prev); + }} suffixIcon={isOpen ? <CaretUpOutlined /> : <CaretDownOutlined />} className="custom-select-input-style w-100 elevated" dropdownClassName="custom-select-style select-option-wrapper" diff --git a/web/src/containers/Trade/components/TradeHistory.js b/web/src/containers/Trade/components/TradeHistory.js index fa2169e8c9..7c37dc7eff 100644 --- a/web/src/containers/Trade/components/TradeHistory.js +++ b/web/src/containers/Trade/components/TradeHistory.js @@ -12,7 +12,7 @@ import { tradeHistorySelector } from '../utils'; import withConfig from 'components/ConfigProvider/withConfig'; import { calcPercentage } from 'utils/math'; import { Select } from 'antd'; -import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'; +import { CaretDownOutlined } from '@ant-design/icons'; import math from 'mathjs'; import { opacifyNumber } from 'helpers/opacify'; From f9b5abff24ca641f901baace793f960d23d305dd Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 20:09:48 +0300 Subject: [PATCH 054/132] slider animation --- .../components/Form/TradeFormFields/_TradeFormFields.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/src/components/Form/TradeFormFields/_TradeFormFields.scss b/web/src/components/Form/TradeFormFields/_TradeFormFields.scss index f795c12dff..1fbaf70a85 100644 --- a/web/src/components/Form/TradeFormFields/_TradeFormFields.scss +++ b/web/src/components/Form/TradeFormFields/_TradeFormFields.scss @@ -97,6 +97,11 @@ $padding: 0.25rem; } .size-slider { + .ant-slider { + * { + transition: 0.1s; + } + } .ant-slider-with-marks { margin-bottom: 10px; } From 1fa6af49a303077854a4cd3f5f0609a82fb2a411 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 21:24:29 +0300 Subject: [PATCH 055/132] Clear order entry, should also clear amount slider --- .../components/Form/TradeFormFields/Slider.js | 18 +++++++++++++++--- .../containers/Trade/components/OrderEntry.js | 6 +++++- .../Trade/components/TradeHistory.js | 2 +- web/src/containers/Trade/index.js | 15 +++++++++++++++ web/src/index.css | 11 ++++++++++- web/src/utils/currency.js | 3 ++- 6 files changed, 48 insertions(+), 7 deletions(-) diff --git a/web/src/components/Form/TradeFormFields/Slider.js b/web/src/components/Form/TradeFormFields/Slider.js index 29c0deeb25..082d239ce6 100644 --- a/web/src/components/Form/TradeFormFields/Slider.js +++ b/web/src/components/Form/TradeFormFields/Slider.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Slider } from 'antd'; // todo: connect antd slider to redux form @@ -31,8 +31,18 @@ const marks = { }, }; -const SizeSlider = (props) => { - const { onClick } = props; +const SizeSlider = ({ onClick, value, setRef }) => { + const [val, setVal] = useState(0); + + useEffect(() => { + if (setRef) { + setRef({ + reset: () => { + setVal(0); + }, + }); + } + }, [setRef]); return ( <div className="size-slider px-1 pb-2"> @@ -42,6 +52,8 @@ const SizeSlider = (props) => { defaultValue={0} tooltipVisible={false} onAfterChange={onClick} + value={val} + onChange={setVal} /> </div> ); diff --git a/web/src/containers/Trade/components/OrderEntry.js b/web/src/containers/Trade/components/OrderEntry.js index 02df75aba0..2ab1fed875 100644 --- a/web/src/containers/Trade/components/OrderEntry.js +++ b/web/src/containers/Trade/components/OrderEntry.js @@ -411,10 +411,12 @@ class OrderEntry extends Component { }; reset = () => { - const { change } = this.props; + const { change, resetSlider } = this.props; + this.setState({ sliderVal: 0 }); change(FORM_NAME, 'stop', ''); change(FORM_NAME, 'price', ''); change(FORM_NAME, 'size', ''); + resetSlider(); }; handleOrderBookChange = (name, value) => { @@ -568,6 +570,8 @@ class OrderEntry extends Component { name: 'size-slider', type: 'slider', onClick: this.setMax, + value: 0, + setRef: this.props.setSliderRef, }, postOnly: { name: 'post_only', diff --git a/web/src/containers/Trade/components/TradeHistory.js b/web/src/containers/Trade/components/TradeHistory.js index 7c37dc7eff..c04a916871 100644 --- a/web/src/containers/Trade/components/TradeHistory.js +++ b/web/src/containers/Trade/components/TradeHistory.js @@ -1,4 +1,4 @@ -import React, { Component, useState } from 'react'; +import React, { Component } from 'react'; import classnames from 'classnames'; import { connect } from 'react-redux'; import { ReactSVG } from 'react-svg'; diff --git a/web/src/containers/Trade/index.js b/web/src/containers/Trade/index.js index e464230f14..a0fe61ab11 100644 --- a/web/src/containers/Trade/index.js +++ b/web/src/containers/Trade/index.js @@ -358,6 +358,12 @@ class Trade extends PureComponent { } }; + setSliderRef = (sliderRef) => { + if (sliderRef) { + this.sliderRef = sliderRef; + } + }; + setActiveTab = (activeTab) => { const { setTradeTab } = this.props; setTradeTab(activeTab); @@ -429,6 +435,13 @@ class Trade extends PureComponent { }; }; + resetSlider = () => { + console.log(this.sliderRef); + if (this.sliderRef) { + this.sliderRef.reset(); + } + }; + subscribe = (pair) => { const { orderbookWs, wsInitialized } = this.state; if (orderbookWs && wsInitialized) { @@ -573,6 +586,8 @@ class Trade extends PureComponent { showPopup={settings.notification.popup_order_confirmation} setPriceRef={this.setPriceRef} setSizeRef={this.setSizeRef} + setSliderRef={this.setSliderRef} + resetSlider={this.resetSlider} /> </TradeBlock> </div> diff --git a/web/src/index.css b/web/src/index.css index 007ba8256e..6b6f188c8e 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -1543,6 +1543,9 @@ table th { .home_container .html_card_section .card-section-wrapper .card-section .header_txt { font-size: 14px; } } +.wallet-hover:hover { + opacity: 0.5; } + .layout-mobile .button-container { min-height: 4.5rem; } @@ -6023,7 +6026,7 @@ table th { .app_bar .opacity-1 { opacity: 1 !important; } .app_bar .app-bar-account-menu { - transition: .9s; + transition: 0.9s; opacity: 0; display: block; position: absolute; @@ -8168,6 +8171,9 @@ table th { .direction_rtl .trade_input-input-currency { left: 0; } +.size-slider .ant-slider * { + transition: 0.1s; } + .size-slider .ant-slider-with-marks { margin-bottom: 10px; } @@ -11047,6 +11053,9 @@ body { left: 0; top: calc(50% - 0.75rem); } +.blockchain-search { + filter: brightness(0) invert(0.8); } + input, input:focus { outline: none; diff --git a/web/src/utils/currency.js b/web/src/utils/currency.js index c96b9dd90d..a34d495b35 100644 --- a/web/src/utils/currency.js +++ b/web/src/utils/currency.js @@ -4,6 +4,7 @@ import store from 'store'; import STRINGS from '../config/localizedStrings'; import { BASE_CURRENCY, DEFAULT_COIN_DATA } from '../config/constants'; import { findPath, convertPathToPairNames } from './data'; +import { isNaN } from 'lodash'; export const BTC_FORMAT = '0,0.[0000]'; export const ETH_FORMAT = '0,0.[0000]'; @@ -31,7 +32,7 @@ export const AVERAGE_FORMAT = '3a'; // }; export const roundNumber = (number = 0, decimals = 4) => { - if (number === 0) { + if (number === 0 || number === Infinity || isNaN(number)) { return 0; } else if (decimals > 0) { const multipliedNumber = math.multiply( From 59ec671d687f5401884bc425d9a3ec1e551d3cdf Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 21:35:31 +0300 Subject: [PATCH 056/132] Hover feedback order entry --- .../Form/TradeFormFields/ChoiceSelector.js | 1 + .../Form/TradeFormFields/TabSelector.js | 12 +++++++++--- web/src/config/options.js | 16 ++++++++++++---- web/src/index.css | 3 +++ web/src/index.scss | 4 ++++ 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/web/src/components/Form/TradeFormFields/ChoiceSelector.js b/web/src/components/Form/TradeFormFields/ChoiceSelector.js index f998a129f2..1a1444c199 100644 --- a/web/src/components/Form/TradeFormFields/ChoiceSelector.js +++ b/web/src/components/Form/TradeFormFields/ChoiceSelector.js @@ -15,6 +15,7 @@ const ChoiceSelector = (props) => { 'justify-content-center', 'align-items-center', 'pointer', + option.value !== input.value ? option.className : '', 'holla-button-font', { active: option.value === input.value } )} diff --git a/web/src/components/Form/TradeFormFields/TabSelector.js b/web/src/components/Form/TradeFormFields/TabSelector.js index 61bfb88286..d5dd1a7a98 100644 --- a/web/src/components/Form/TradeFormFields/TabSelector.js +++ b/web/src/components/Form/TradeFormFields/TabSelector.js @@ -9,9 +9,15 @@ const TabSelector = (props) => { {options.map((option, index) => ( <div key={`type-${index}`} - className={classnames('text-uppercase', 'text-center', 'pointer', { - active: input.value === option.value, - })} + className={classnames( + 'text-uppercase', + 'text-center', + 'pointer', + input.value !== option.value ? option.className : '', + { + active: input.value === option.value, + } + )} onClick={() => input.onChange(option.value)} > {option.label} diff --git a/web/src/config/options.js b/web/src/config/options.js index 2f8a20783d..a76c00f3c2 100644 --- a/web/src/config/options.js +++ b/web/src/config/options.js @@ -1,13 +1,21 @@ import STRINGS from 'config/localizedStrings'; export const SIDES = [ - { value: 'buy', label: STRINGS['SIDES.BUY'] }, - { value: 'sell', label: STRINGS['SIDES.SELL'] }, + { value: 'buy', label: STRINGS['SIDES.BUY'], className: 'onHoverOpacity' }, + { value: 'sell', label: STRINGS['SIDES.SELL'], className: 'onHoverOpacity' }, ]; export const TYPES = [ - { value: 'market', label: STRINGS['TYPES.MARKET'] }, - { value: 'limit', label: STRINGS['TYPES.LIMIT'] }, + { + value: 'market', + label: STRINGS['TYPES.MARKET'], + className: 'onHoverOpacity', + }, + { + value: 'limit', + label: STRINGS['TYPES.LIMIT'], + className: 'onHoverOpacity', + }, ]; export const DEFAULT_TOGGLE_OPTIONS = [ diff --git a/web/src/index.css b/web/src/index.css index 6b6f188c8e..b47768323f 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -11061,6 +11061,9 @@ input:focus { outline: none; border: none; } +.onHoverOpacity:hover { + opacity: .5; } + input[type='number'] { -moz-appearance: textfield; appearance: textfield; } diff --git a/web/src/index.scss b/web/src/index.scss index c8f1a46854..39b8a5feef 100644 --- a/web/src/index.scss +++ b/web/src/index.scss @@ -107,6 +107,10 @@ input:focus { border: none; } +.onHoverOpacity:hover { + opacity: 0.5; +} + input[type='number'] { -moz-appearance: textfield; appearance: textfield; From 471182e97dd2870880496039d7dd8c7647b6e70c Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 22:04:16 +0300 Subject: [PATCH 057/132] =?UTF-8?q?Fill=20=E2=80=98no=20data=E2=80=99=20hi?= =?UTF-8?q?story=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/package-lock.json | 2 +- .../assets/images/no-active-orders-01.svg | 11 +++++++++ .../assets/images/no-recent-deposits.svg | 9 ++++++++ .../assets/images/no-recent-trades-01.svg | 9 ++++++++ .../assets/images/no-recent-withdrawals.svg | 9 ++++++++ web/src/config/icons/static.js | 4 ++++ web/src/config/lang/ar.json | 1 + web/src/config/lang/de.json | 1 + web/src/config/lang/en.json | 4 ++++ web/src/config/lang/es.json | 1 + web/src/config/lang/fa.json | 1 + web/src/config/lang/fr.json | 1 + .../TransactionsHistory/HistoryDisplay.js | 1 + .../containers/TransactionsHistory/index.js | 23 +++++++++++++++++++ web/src/index.css | 2 +- 15 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 web/public/assets/images/no-active-orders-01.svg create mode 100644 web/public/assets/images/no-recent-deposits.svg create mode 100644 web/public/assets/images/no-recent-trades-01.svg create mode 100644 web/public/assets/images/no-recent-withdrawals.svg diff --git a/web/package-lock.json b/web/package-lock.json index 2d25043409..6bc6790c6f 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "hollaex-kit", - "version": "2.4.0", + "version": "2.4.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/public/assets/images/no-active-orders-01.svg b/web/public/assets/images/no-active-orders-01.svg new file mode 100644 index 0000000000..a9b6cac8dc --- /dev/null +++ b/web/public/assets/images/no-active-orders-01.svg @@ -0,0 +1,11 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="42.439" height="42.446" viewBox="0 0 42.439 42.446"> + <g id="no-active-orders-01" opacity="0.55"> + <path id="Path_42" data-name="Path 42" d="M354.107,229.122h7.214a4.088,4.088,0,0,0,4.082-4.082V211.51a2.285,2.285,0,0,0-3.24-2.072l-.394.186-.178-.394a2.283,2.283,0,0,0-3.039-1.144l-.394.186-.178-.394a2.27,2.27,0,0,0-2.837-1.222l-.564.2v-7.074a2.294,2.294,0,0,0-.665-1.616,2.262,2.262,0,0,0-1.616-.665,2.3,2.3,0,0,0-1.616.665,2.262,2.262,0,0,0-.665,1.616v15.687l-2.42-2.42a2.262,2.262,0,0,0-1.616-.665,2.294,2.294,0,0,0-1.616.665,2.262,2.262,0,0,0-.665,1.616,2.3,2.3,0,0,0,.665,1.616l5.257,5.257a1.329,1.329,0,0,1,.387.936v2.567A4.112,4.112,0,0,0,354.107,229.122ZM345.053,215.6a1.323,1.323,0,0,1,1.871-1.871l3.232,3.232a.463.463,0,0,0,.518.1.48.48,0,0,0,.294-.441V199.781a1.316,1.316,0,0,1,.387-.936,1.329,1.329,0,0,1,.936-.387,1.316,1.316,0,0,1,.936.387,1.329,1.329,0,0,1,.387.936v14.435a.479.479,0,0,0,.959,0V208.8a1.33,1.33,0,1,1,2.66,0v5.412a.479.479,0,0,0,.959.015v-4.075a1.33,1.33,0,1,1,2.66,0v4.075a.479.479,0,0,0,.959-.015V211.51a1.33,1.33,0,1,1,2.66,0v13.53a3.138,3.138,0,0,1-3.131,3.131h-7.214a3.138,3.138,0,0,1-3.131-3.131v-2.567a2.3,2.3,0,0,0-.665-1.616Z" transform="translate(-322.964 -186.676)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_43" data-name="Path 43" d="M97.628,57.5H77.781A2.28,2.28,0,0,0,75.5,59.781v13.53a2.28,2.28,0,0,0,2.281,2.281H97.628a2.28,2.28,0,0,0,2.281-2.281V59.781A2.285,2.285,0,0,0,97.628,57.5ZM98.95,73.311a1.33,1.33,0,0,1-1.33,1.33H77.781a1.33,1.33,0,0,1-1.33-1.33V59.781a1.33,1.33,0,0,1,1.33-1.33H97.628a1.33,1.33,0,0,1,1.33,1.33v13.53Z" transform="translate(-75.5 -57.5)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_44" data-name="Path 44" d="M320.979,127.5a.482.482,0,0,0-.479.479V134.3a.479.479,0,1,0,.959,0v-6.317A.487.487,0,0,0,320.979,127.5Z" transform="translate(-301.558 -122.088)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_45" data-name="Path 45" d="M274.279,104.6a.482.482,0,0,0-.479.479V111.4a.479.479,0,1,0,.959,0v-6.317A.477.477,0,0,0,274.279,104.6Z" transform="translate(-258.468 -100.958)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_46" data-name="Path 46" d="M227.579,151.6a.482.482,0,0,0-.479.479V158.4a.479.479,0,1,0,.959,0v-6.317A.477.477,0,0,0,227.579,151.6Z" transform="translate(-215.379 -144.325)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_47" data-name="Path 47" d="M180.979,104.6a.482.482,0,0,0-.479.479V111.4a.479.479,0,0,0,.959,0v-6.317A.482.482,0,0,0,180.979,104.6Z" transform="translate(-172.382 -100.958)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_48" data-name="Path 48" d="M134.779,127.5a.482.482,0,0,0-.479.479V134.3a.479.479,0,0,0,.959,0v-6.317A.482.482,0,0,0,134.779,127.5Z" transform="translate(-129.754 -122.088)" fill="#fff" fill-rule="evenodd"/> + </g> +</svg> diff --git a/web/public/assets/images/no-recent-deposits.svg b/web/public/assets/images/no-recent-deposits.svg new file mode 100644 index 0000000000..9eba8e665f --- /dev/null +++ b/web/public/assets/images/no-recent-deposits.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="31.263" height="40.277" viewBox="0 0 31.263 40.277"> + <g id="Group_7" data-name="Group 7" transform="translate(-1098 -418)"> + <g id="no-recent-trades-01" transform="translate(313.475 90.003)" opacity="0.55"> + <path id="Path_49" data-name="Path 49" d="M795.493,361.953h6.966a4.044,4.044,0,0,0,2.327-.727l8.922-6.177a4.829,4.829,0,0,0,2.08-3.974,2.019,2.019,0,0,0-1.129-1.809h0a2.02,2.02,0,0,0-2.118.209l-7.492,5.76-.155-.626a2.284,2.284,0,0,0-2.219-1.724h-7.94a4.075,4.075,0,0,0-3.44,1.879l-6.409,10a2.3,2.3,0,0,0-.085,2.327,2.274,2.274,0,0,0,2,1.183h2.319a2.273,2.273,0,0,0,1.856-.959l3.433-4.8A1.327,1.327,0,0,1,795.493,361.953Zm-1.856,0-3.433,4.809a1.329,1.329,0,0,1-1.082.557H786.8a1.326,1.326,0,0,1-1.121-2.042l6.409-9.989a3.107,3.107,0,0,1,2.636-1.438h7.948a1.315,1.315,0,0,1,1.307,1.137l.031.263-.07.294.008.008-.008-.008v.015a1.334,1.334,0,0,1-1.268.943h-5.412a.479.479,0,1,0,0,.959h5.7a2.3,2.3,0,0,0,1.392-.472l8.76-6.742a1.064,1.064,0,0,1,1.709.843h0a3.893,3.893,0,0,1-1.67,3.193l-8.922,6.177a3.133,3.133,0,0,1-1.786.557h-6.964A2.22,2.22,0,0,0,793.637,361.953Z" fill="#fff" fill-rule="evenodd"/> + </g> + <path id="Path_54" data-name="Path 54" d="M806.161,349H791.023a2.934,2.934,0,0,1-2.93-2.932V330.928a2.934,2.934,0,0,1,2.93-2.931h15.138a2.935,2.935,0,0,1,2.932,2.931v15.137A2.936,2.936,0,0,1,806.161,349Zm-15.138-20a1.933,1.933,0,0,0-1.93,1.931v15.137a1.933,1.933,0,0,0,1.93,1.932h15.138a1.934,1.934,0,0,0,1.932-1.932V330.928A1.933,1.933,0,0,0,806.161,329Z" transform="translate(313.475 90.003)" fill="#fff" opacity="0.55"/> + <path id="Path_55" data-name="Path 55" d="M803.113,333.976a.5.5,0,0,0-.707,0l-7.481,7.481v-5.363a.5.5,0,0,0-1,0v6.571a.5.5,0,0,0,.309.461.505.505,0,0,0,.191.039h6.57a.5.5,0,0,0,0-1h-5.363l7.481-7.482A.5.5,0,0,0,803.113,333.976Z" transform="translate(313.475 90.003)" fill="#fff" opacity="0.55"/> + </g> +</svg> diff --git a/web/public/assets/images/no-recent-trades-01.svg b/web/public/assets/images/no-recent-trades-01.svg new file mode 100644 index 0000000000..7fa67e65d9 --- /dev/null +++ b/web/public/assets/images/no-recent-trades-01.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="33.423" height="42.445" viewBox="0 0 33.423 42.445"> + <g id="no-recent-trades-01" opacity="0.55"> + <path id="Path_49" data-name="Path 49" d="M172.811,368.915h6.966a4.046,4.046,0,0,0,2.327-.727l8.922-6.177a4.828,4.828,0,0,0,2.08-3.974,2.017,2.017,0,0,0-1.129-1.809h0a2.021,2.021,0,0,0-2.118.209l-7.492,5.76-.155-.626a2.283,2.283,0,0,0-2.219-1.724h-7.94a4.077,4.077,0,0,0-3.44,1.879l-6.409,10a2.3,2.3,0,0,0-.085,2.327,2.275,2.275,0,0,0,2,1.183h2.319a2.273,2.273,0,0,0,1.856-.959l3.433-4.8A1.326,1.326,0,0,1,172.811,368.915Zm-1.856,0-3.433,4.809a1.327,1.327,0,0,1-1.082.557h-2.319A1.326,1.326,0,0,1,163,372.239l6.409-9.989a3.109,3.109,0,0,1,2.636-1.438h7.948a1.314,1.314,0,0,1,1.307,1.137l.031.263-.07.294.008.008-.008-.008v.015a1.332,1.332,0,0,1-1.268.943h-5.412a.479.479,0,1,0,0,.959h5.7a2.307,2.307,0,0,0,1.392-.472l8.76-6.742a1.064,1.064,0,0,1,1.709.843h0a3.891,3.891,0,0,1-1.67,3.193l-8.922,6.177a3.13,3.13,0,0,1-1.786.557H172.8A2.223,2.223,0,0,0,170.955,368.915Z" transform="translate(-159.683 -332.786)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_50" data-name="Path 50" d="M147.268,76.862a11.182,11.182,0,0,0,5.141,1.237,11.3,11.3,0,1,0-11.3-11.3,11.182,11.182,0,0,0,1.237,5.141l.139.278-7.917,7.925a2.262,2.262,0,0,0-.665,1.616,2.294,2.294,0,0,0,.665,1.616l1.276,1.276a2.262,2.262,0,0,0,1.616.665,2.294,2.294,0,0,0,1.616-.665h0l7.917-7.932Zm-8.876,7.113a1.316,1.316,0,0,1-.936.387h0a1.316,1.316,0,0,1-.935-.387L135.245,82.7a1.334,1.334,0,0,1-.387-.943,1.316,1.316,0,0,1,.387-.936L143,73.066l.294.4c.008.008.015.023.023.031a.169.169,0,0,0,.031.039c.031.046.07.093.1.139l.008.008c.062.077.124.162.193.24l.1.124c.054.07.116.139.17.2s.1.108.147.162a1.189,1.189,0,0,1,.131.147c.039.046.085.085.124.131l.2.2c.046.046.093.085.139.131s.093.085.139.131l.216.193c.039.039.085.07.124.108l.015.015.139.116c.07.054.131.108.2.162.054.046.116.085.17.131a.168.168,0,0,1,.039.031c.008.008.023.015.031.023l.4.294Zm8.474-8.443c-.085-.054-.17-.108-.255-.17l-.031-.023c-.07-.046-.139-.1-.209-.147l-.07-.046c-.062-.046-.124-.093-.178-.139l-.062-.046-.232-.186-.031-.031c-.062-.054-.131-.108-.193-.162-.023-.015-.039-.039-.062-.054l-.008-.008c-.054-.046-.1-.093-.155-.139l-.015-.015a.357.357,0,0,1-.054-.054c-.062-.062-.131-.124-.193-.186l-.031-.031c-.062-.062-.124-.124-.186-.193-.015-.023-.039-.039-.054-.062l-.015-.015c-.046-.046-.093-.1-.131-.147a.7.7,0,0,0-.07-.077l-.155-.178-.039-.046a2.515,2.515,0,0,1-.178-.224.235.235,0,0,1-.039-.054l-.015-.023c-.039-.054-.085-.108-.124-.17-.015-.023-.039-.054-.054-.077-.046-.062-.093-.131-.139-.193l-.031-.046c-.054-.085-.108-.162-.162-.247l-.039-.062c-.039-.07-.085-.131-.124-.2a10.359,10.359,0,1,1,3.6,3.6c-.077-.046-.147-.093-.224-.139Z" transform="translate(-133.9 -55.5)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_51" data-name="Path 51" d="M277.465,202.288v-5.165l1.894,1.894a.476.476,0,0,0,.673,0,.467.467,0,0,0,.139-.34.48.48,0,0,0-.139-.34l-2.706-2.706a.241.241,0,0,0-.07-.054l-.015-.008a.481.481,0,0,0-.2-.07h-.108a.416.416,0,0,0-.2.07c-.008.008-.015.008-.023.015a.431.431,0,0,0-.062.046l-2.706,2.706a.476.476,0,1,0,.673.673l1.894-1.894v5.165a.482.482,0,0,0,.479.479A.475.475,0,0,0,277.465,202.288Z" transform="translate(-262.984 -184.676)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_52" data-name="Path 52" d="M337.9,105.949a.475.475,0,0,0,.34-.812l-2.706-2.706a.447.447,0,0,0-.193-.116h-.008a.336.336,0,0,0-.039-.008h-.015a.666.666,0,0,0-.124-.008h-.015a.07.07,0,0,0-.039.008l-.015.008c-.008,0-.015.008-.031.008l-.015.008c-.008,0-.015.008-.031.008l-.015.008c-.008.008-.015.008-.031.015l-.015.008c-.008.008-.015.008-.023.015l-.015.008c-.008.008-.015.015-.023.015l-.039.039-2.706,2.706a.467.467,0,0,0-.139.34.48.48,0,0,0,.139.34.467.467,0,0,0,.34.139h0a.48.48,0,0,0,.34-.139h0l1.894-1.894v5.141a.479.479,0,1,0,.959,0v-5.165l1.894,1.894A.45.45,0,0,0,337.9,105.949Z" transform="translate(-316.684 -98.682)" fill="#fff" fill-rule="evenodd"/> + <path id="Path_53" data-name="Path 53" d="M396.632,198.926a.467.467,0,0,0,.139-.34.48.48,0,0,0-.139-.34l-2.706-2.706a.136.136,0,0,0-.039-.031.507.507,0,0,0-.294-.108h-.016a.463.463,0,0,0-.325.139l-.015.015-2.7,2.7a.476.476,0,0,0,.673.673l1.894-1.894V202.2a.479.479,0,0,0,.959,0v-5.165l1.894,1.894A.476.476,0,0,0,396.632,198.926Z" transform="translate(-370.569 -184.584)" fill="#fff" fill-rule="evenodd"/> + </g> +</svg> diff --git a/web/public/assets/images/no-recent-withdrawals.svg b/web/public/assets/images/no-recent-withdrawals.svg new file mode 100644 index 0000000000..20dd61cc5a --- /dev/null +++ b/web/public/assets/images/no-recent-withdrawals.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="31.262" height="40.277" viewBox="0 0 31.262 40.277"> + <g id="Group_5" data-name="Group 5" transform="translate(-884.428 -327.997)"> + <g id="no-recent-trades-01" opacity="0.55"> + <path id="Path_49" data-name="Path 49" d="M895.4,361.953h6.966a4.044,4.044,0,0,0,2.327-.727l8.922-6.177a4.829,4.829,0,0,0,2.08-3.974,2.018,2.018,0,0,0-1.129-1.809h0a2.02,2.02,0,0,0-2.118.209l-7.492,5.76-.155-.626a2.284,2.284,0,0,0-2.219-1.724h-7.94a4.075,4.075,0,0,0-3.44,1.879l-6.409,10a2.3,2.3,0,0,0-.085,2.327,2.273,2.273,0,0,0,2,1.183h2.319a2.273,2.273,0,0,0,1.856-.959l3.433-4.8A1.326,1.326,0,0,1,895.4,361.953Zm-1.856,0-3.433,4.809a1.329,1.329,0,0,1-1.082.557h-2.319a1.326,1.326,0,0,1-1.121-2.042l6.409-9.989a3.107,3.107,0,0,1,2.636-1.438h7.948a1.315,1.315,0,0,1,1.307,1.137l.031.263-.07.294.008.008-.008-.008v.015a1.334,1.334,0,0,1-1.268.943h-5.412a.479.479,0,1,0,0,.959h5.7a2.3,2.3,0,0,0,1.392-.472l8.76-6.742a1.064,1.064,0,0,1,1.709.843h0a3.893,3.893,0,0,1-1.67,3.193l-8.922,6.177a3.133,3.133,0,0,1-1.786.557h-6.964A2.22,2.22,0,0,0,893.54,361.953Z" fill="#fff" fill-rule="evenodd"/> + </g> + <path id="Path_56" data-name="Path 56" d="M906.064,349H890.927a2.936,2.936,0,0,1-2.932-2.932V330.928A2.935,2.935,0,0,1,890.927,328h15.137a2.934,2.934,0,0,1,2.931,2.931v15.137A2.934,2.934,0,0,1,906.064,349Zm-15.137-20a1.933,1.933,0,0,0-1.932,1.931v15.137A1.934,1.934,0,0,0,890.927,348h15.137a1.934,1.934,0,0,0,1.931-1.932V330.928A1.933,1.933,0,0,0,906.064,329Z" fill="#fff" opacity="0.55"/> + <path id="Path_57" data-name="Path 57" d="M903.125,334.138a.5.5,0,0,0-.271-.27.487.487,0,0,0-.191-.039h-6.57a.5.5,0,0,0,0,1h5.363l-7.481,7.482a.5.5,0,1,0,.707.707l7.481-7.482V340.9a.5.5,0,0,0,1,0v-6.571A.5.5,0,0,0,903.125,334.138Z" fill="#fff" opacity="0.55"/> + </g> +</svg> diff --git a/web/src/config/icons/static.js b/web/src/config/icons/static.js index 882d19f784..648c0f6aec 100644 --- a/web/src/config/icons/static.js +++ b/web/src/config/icons/static.js @@ -52,6 +52,10 @@ const icons = { WITHDRAW_TIERS_SECTION: '/assets/images/withdraw-tier-section.svg', TAKER_TIERS_SECTION: '/assets/images/taker-tier-section.svg', SEARCH_BLOCKCHAIN: '/assets/images/search-blockchain.svg', + NO_ACTIVE_ORDERS: '/assets/images/no-active-orders-01.svg', + NO_ACTIVE_TRADES: '/assets/images/no-recent-trades-01.svg', + NO_ACTIVE_DEPOSITS: '/assets/images/no-recent-deposits.svg', + NO_ACTIVE_WITHDRAWALS: '/assets/images/no-recent-withdrawals.svg', MAKER_TIERS_SECTION: '/assets/images/maker-tier-section.svg', LIMITS_SECTION_ICON: '/assets/images/limits-coin-tiers.svg', FEES_SECTION_ICON: '/assets/images/limits-pairs-tiers.svg', diff --git a/web/src/config/lang/ar.json b/web/src/config/lang/ar.json index 696daf18c1..491673b07f 100644 --- a/web/src/config/lang/ar.json +++ b/web/src/config/lang/ar.json @@ -23,6 +23,7 @@ "HELPFUL_RESOURCES_TEXT": "مصادر مفيدة", "HELP_TELEGRAM_TEXT": "تحقق من وثائق API المفتوحة:", "HELP_TELEGRAM_LINK": "https://apidocs.hollaex.com", + "NO_ACTIVE_ORDERS": "No orders detected. Place your orders on the Pro or Quick trade page", "NEED_HELP_TEXT": "تحتاج لمساعدة؟", "HELP_TEXT": "مساعدة", "SUCCESS_TEXT": "نجاح", diff --git a/web/src/config/lang/de.json b/web/src/config/lang/de.json index 85cb51fe53..e0e4adc0e9 100644 --- a/web/src/config/lang/de.json +++ b/web/src/config/lang/de.json @@ -7,6 +7,7 @@ "CANCEL_WITHDRAWAL": "Auszahlung abbrechen", "CANCEL_WITHDRAWAL_POPUP_CONFIRM": "Möchten Sie Ihre ausstehende Auszahlung stornieren?:", "TIMESTAMP_FORMAT": "YYYY/MM/DD HH:mm:ss", + "NO_ACTIVE_ORDERS": "No orders detected. Place your orders on the Pro or Quick trade page", "HOUR_FORMAT": "HH:mm:ss", "LOGIN_TEXT": "Login", "SIGN_IN": "Anmelden", diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index e49d11d4b3..47a3e7d708 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -7,6 +7,10 @@ "CANCEL_WITHDRAWAL": "Cancel Withdrawal", "CANCEL_WITHDRAWAL_POPUP_CONFIRM": "Do you want to cancel your pending withdrawal of:", "TIMESTAMP_FORMAT": "YYYY/MM/DD HH:mm:ss", + "NO_ACTIVE_ORDERS": "No orders detected. Place your orders on the Pro or Quick trade page", + "NO_ACTIVE_TRADES": "Looks like there aren't any trades yet", + "NO_ACTIVE_DEPOSITS": "Looks like there aren't any deposits yet.", + "NO_ACTIVE_WITHDRAWALS": "Looks like there aren't any withdrawals yet", "HOUR_FORMAT": "HH:mm:ss", "LOGIN_TEXT": "Login", "SIGN_IN": "Sign In", diff --git a/web/src/config/lang/es.json b/web/src/config/lang/es.json index ef62aa021d..9f3a2db1aa 100644 --- a/web/src/config/lang/es.json +++ b/web/src/config/lang/es.json @@ -6,6 +6,7 @@ "CANCEL_BASE_WITHDRAWAL": "Cancelar {0} Retiro", "CANCEL_WITHDRAWAL": "Cancelar Retiro", "CANCEL_WITHDRAWAL_POPUP_CONFIRM": "Quiere cancelar su retiro pendiente de:", + "NO_ACTIVE_ORDERS": "No orders detected. Place your orders on the Pro or Quick trade page", "TIMESTAMP_FORMAT": "YYYY/MM/DD HH:mm:ss", "HOUR_FORMAT": "HH:mm:ss", "LOGIN_TEXT": "Iniciar Sesión", diff --git a/web/src/config/lang/fa.json b/web/src/config/lang/fa.json index 6c3ed78df0..980cf62fc3 100644 --- a/web/src/config/lang/fa.json +++ b/web/src/config/lang/fa.json @@ -36,6 +36,7 @@ "GO_TRADE": "معامله رو شروع کن", "VIEW_INFO": "مشاهده صفحه اطلاعات", "APPLY_HERE": "اعمال تغییرات", + "NO_ACTIVE_ORDERS": "No orders detected. Place your orders on the Pro or Quick trade page", "HOME": { "SECTION_1_TITLE": "به HollaEx kit خوش آمدید", "SECTION_1_TEXT_1": "با خیالی آسوده اکسچنج تبادل دارایی های دیجیتال خود را بسازید و جزو آینده بازارهای مالی جهان شوید.", diff --git a/web/src/config/lang/fr.json b/web/src/config/lang/fr.json index fac0aa8ea3..93d8d6e2f3 100644 --- a/web/src/config/lang/fr.json +++ b/web/src/config/lang/fr.json @@ -11,6 +11,7 @@ "LOGIN_TEXT": "Connexion", "SIGN_IN": "S'identifier", "SIGNUP_TEXT": "S'enregistrer", + "NO_ACTIVE_ORDERS": "No orders detected. Place your orders on the Pro or Quick trade page", "REGISTER_TEXT": "S'inscrire", "ACCOUNT_TEXT": "Compte", "CLOSE_TEXT": "Fermer", diff --git a/web/src/containers/TransactionsHistory/HistoryDisplay.js b/web/src/containers/TransactionsHistory/HistoryDisplay.js index ea4ce076b8..f2fa5aee9e 100644 --- a/web/src/containers/TransactionsHistory/HistoryDisplay.js +++ b/web/src/containers/TransactionsHistory/HistoryDisplay.js @@ -115,6 +115,7 @@ const HistoryDisplay = (props) => { title={title} handleNext={handleNext} jumpToPage={jumpToPage} + noData={props.noData} /> )} <Dialog diff --git a/web/src/containers/TransactionsHistory/index.js b/web/src/containers/TransactionsHistory/index.js index c0ca4ea300..6fabc049b1 100644 --- a/web/src/containers/TransactionsHistory/index.js +++ b/web/src/containers/TransactionsHistory/index.js @@ -44,6 +44,8 @@ import { import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; +import { STATIC_ICONS } from 'config/icons'; +import { Image } from 'hollaex-web-lib'; const GROUP_CLASSES = [...FLEX_CENTER_CLASSES, 'flex-column']; @@ -419,6 +421,21 @@ class TransactionsHistory extends Component { withIcon: true, }; + const prepareNoData = (tab) => { + return ( + <div className="d-flex flex-column align-items-center"> + <Image + iconId={tab} + icon={STATIC_ICONS[tab]} + alt={tab} + width="40px" + height="40px" + /> + <span>{STRINGS[tab]}</span> + </div> + ); + }; + switch (activeTab) { case 1: props.stringId = 'ORDER_HISTORY'; @@ -431,6 +448,7 @@ class TransactionsHistory extends Component { props.jumpToPage = jumpToPage; props.handleDownload = () => downloadUserOrders(temp); props.filters = filters.orders; + props.noData = prepareNoData('NO_ACTIVE_ORDERS'); break; case 0: props.stringId = 'TRANSACTION_HISTORY.TITLE_TRADES'; @@ -443,6 +461,8 @@ class TransactionsHistory extends Component { props.jumpToPage = jumpToPage; props.handleDownload = () => downloadUserTrades(temp); props.filters = filters.trades; + props.noData = prepareNoData('NO_ACTIVE_TRADES'); + break; case 2: props.stringId = 'TRANSACTION_HISTORY.TITLE_DEPOSITS'; @@ -454,6 +474,8 @@ class TransactionsHistory extends Component { props.jumpToPage = jumpToPage; props.handleDownload = () => downloadUserDeposit(temp); props.filters = filters.deposits; + props.noData = prepareNoData('NO_ACTIVE_DEPOSITS'); + break; case 3: props.stringId = 'TRANSACTION_HISTORY.TITLE_WITHDRAWALS'; @@ -465,6 +487,7 @@ class TransactionsHistory extends Component { props.jumpToPage = jumpToPage; props.handleDownload = () => downloadUserWithdrawal(temp); props.filters = filters.withdrawals; + props.noData = prepareNoData('NO_ACTIVE_WITHDRAWALS'); break; default: return <div />; diff --git a/web/src/index.css b/web/src/index.css index b47768323f..7d6b25cf6b 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -11062,7 +11062,7 @@ input:focus { border: none; } .onHoverOpacity:hover { - opacity: .5; } + opacity: 0.5; } input[type='number'] { -moz-appearance: textfield; From 0954523eba77c32e183324f77e4fcb49dff5a5c4 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 21 Sep 2022 22:25:48 +0300 Subject: [PATCH 058/132] In cases where there is no movement, draw a line. --- web/src/components/QuickTrade/index.js | 2 +- .../containers/TradeTabs/components/SparkLine.js | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/web/src/components/QuickTrade/index.js b/web/src/components/QuickTrade/index.js index eea752aa11..76efadace1 100644 --- a/web/src/components/QuickTrade/index.js +++ b/web/src/components/QuickTrade/index.js @@ -293,7 +293,6 @@ class QuickTrade extends Component { <Transition in={inProp} timeout={1000}> {(state) => ( <div className="d-flex f-size-22"> - {console.log(tickerDiff)} <div className={classnames( 'title-font', @@ -315,6 +314,7 @@ class QuickTrade extends Component { containerProps={{ style: { height: '100%', width: '100%' }, }} + renderDefaultLine /> </div> <div className="d-flex pb-35"> diff --git a/web/src/containers/TradeTabs/components/SparkLine.js b/web/src/containers/TradeTabs/components/SparkLine.js index 9443f260d1..ddf4bd2d12 100644 --- a/web/src/containers/TradeTabs/components/SparkLine.js +++ b/web/src/containers/TradeTabs/components/SparkLine.js @@ -7,7 +7,6 @@ class SparkLine extends Component { constructor(props) { super(props); const { data: { close = [], open = 0 } = {} } = this.props; - this.state = { chartOptions: { tooltip: { @@ -60,7 +59,10 @@ class SparkLine extends Component { } componentWillReceiveProps(nextProps) { - const { data } = this.props; + const { data, renderDefaultLine } = this.props; + if (data?.close?.length === 1 && renderDefaultLine) { + data.close.push(data.close[0]); + } if (JSON.stringify(data) !== JSON.stringify(nextProps.data)) { this.setState((prevState) => ({ ...prevState, @@ -69,8 +71,12 @@ class SparkLine extends Component { series: [ { ...prevState.chartOptions.series, - name: _get(nextProps, 'data.name') ? _get(nextProps, 'data.name') : _get(prevState, 'chartOptions.series.name'), - type: _get(nextProps, 'data.type') ? _get(nextProps, 'data.type') : _get(prevState, 'chartOptions.series.type'), + name: _get(nextProps, 'data.name') + ? _get(nextProps, 'data.name') + : _get(prevState, 'chartOptions.series.name'), + type: _get(nextProps, 'data.type') + ? _get(nextProps, 'data.type') + : _get(prevState, 'chartOptions.series.type'), data: nextProps.data.close, threshold: nextProps.data.open, }, From 128c91f4e803debdc9353a2b07164491041c3dc5 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Thu, 22 Sep 2022 17:54:40 +0300 Subject: [PATCH 059/132] OTP Button fix --- web/src/components/Button/CheckboxButton.js | 13 ++++-- web/src/containers/UserSecurity/OTP.js | 43 ++++++++++++------- .../UserSecurity/_UserSecurity.scss | 13 ++++++ web/src/index.css | 10 +++++ 4 files changed, 61 insertions(+), 18 deletions(-) diff --git a/web/src/components/Button/CheckboxButton.js b/web/src/components/Button/CheckboxButton.js index 336cf8d42a..d8fc3a5e41 100644 --- a/web/src/components/Button/CheckboxButton.js +++ b/web/src/components/Button/CheckboxButton.js @@ -3,10 +3,16 @@ import classnames from 'classnames'; import { EditWrapper } from 'components'; import Image from 'components/Image'; -const renderCheckboxImage = (checked, ICONS = {}) => ( +const renderCheckboxImage = (checked, ICONS = {}, customCheckIcon) => ( <Image iconId={checked ? 'SUCCESS_BLACK' : 'SECURE'} - icon={checked ? ICONS['SUCCESS_BLACK'] : ICONS['SECURE']} + icon={ + checked + ? customCheckIcon + ? ICONS[customCheckIcon] + : ICONS['SUCCESS_BLACK'] + : ICONS['SECURE'] + } wrapperClassName="checkbutton-input-wrapper--image" /> ); @@ -27,6 +33,7 @@ const CheckboxButton = ({ loading = false, children, icons, + customCheckIcon = '', }) => ( <div className="checkbutton-wrapper"> <div @@ -43,7 +50,7 @@ const CheckboxButton = ({ {loading ? ( <div className="checkbutton-input-wrapper--loader" /> ) : ( - renderCheckboxImage(checked, icons) + renderCheckboxImage(checked, icons, customCheckIcon) )} <span className="checkbutton-input-wrapper--label"> <EditWrapper stringId={stringId}>{label}</EditWrapper> diff --git a/web/src/containers/UserSecurity/OTP.js b/web/src/containers/UserSecurity/OTP.js index de8d059ca0..6608c6bccd 100644 --- a/web/src/containers/UserSecurity/OTP.js +++ b/web/src/containers/UserSecurity/OTP.js @@ -3,6 +3,7 @@ import { CheckboxButton, IconTitle, EditWrapper } from 'components'; import QRCode from 'qrcode.react'; import OTPForm from './OTPForm'; import STRINGS from 'config/localizedStrings'; +import { Image } from 'hollaex-web-lib'; export const renderOTPForm = ( secret, @@ -86,20 +87,32 @@ export const OTP = ({ </div> )} </div> - <CheckboxButton - stringId="ACCOUNT_SECURITY.OTP.CONTENT.ENABLE,ACCOUNT_SECURITY.OTP.CONTENT.DISABLE" - label={ - STRINGS[ - `ACCOUNT_SECURITY.OTP.CONTENT.${otp_enabled ? 'DISABLE' : 'ENABLE'}` - ] - } - onClick={requestOTP} - disabled={data.requesting} - loading={data.requesting} - checked={otp_enabled} - icons={icons} - > - {children} - </CheckboxButton> + <div className="d-flex w-100 justify-content-center align-items-center mt-5"> + <CheckboxButton + stringId="ACCOUNT_SECURITY.OTP.CONTENT.ENABLE,ACCOUNT_SECURITY.OTP.CONTENT.DISABLE" + label={ + STRINGS[ + `ACCOUNT_SECURITY.OTP.CONTENT.${otp_enabled ? 'DISABLE' : 'ENABLE'}` + ] + } + onClick={requestOTP} + disabled={data.requesting} + loading={data.requesting} + checked={otp_enabled} + icons={icons} + customCheckIcon="SECURE" + > + {children} + </CheckboxButton> + {otp_enabled && ( + <Image + iconId={'SUCCESS_BLACK'} + icon={icons['SUCCESS_BLACK']} + wrapperClassName="OTP_Success ml-2" + width="20px" + height="20px" + /> + )} + </div> </div> ); diff --git a/web/src/containers/UserSecurity/_UserSecurity.scss b/web/src/containers/UserSecurity/_UserSecurity.scss index 103238bb4d..d45e8798cd 100644 --- a/web/src/containers/UserSecurity/_UserSecurity.scss +++ b/web/src/containers/UserSecurity/_UserSecurity.scss @@ -11,6 +11,15 @@ } } +.OTP_Success { + width: 30px; + height: 30px; + svg { + width: 30px; + height: 30px; + } +} + .otp_form-wrapper { display: flex; flex-direction: column; @@ -215,6 +224,10 @@ $padding-input: 4rem; flex-direction: row; align-items: center; } + + .checkbutton-input-wrapper { + margin-top: 0px !important; + } } .layout-mobile { diff --git a/web/src/index.css b/web/src/index.css index daad3b4292..d73be2273b 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -870,6 +870,13 @@ table th { .user_security-wrapper > *:not(:last-child) { margin-bottom: 2rem; } +.OTP_Success { + width: 30px; + height: 30px; } + .OTP_Success svg { + width: 30px; + height: 30px; } + .otp_form-wrapper { display: flex; flex-direction: column; @@ -1003,6 +1010,9 @@ table th { flex-direction: row; align-items: center; } +.user-security-container .checkbutton-input-wrapper { + margin-top: 0px !important; } + .layout-mobile .user_security-wrapper .warning_text { font-size: 13px !important; } From 04935dcc2692f9a3f22430da61e2ef91f98b9251 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Thu, 22 Sep 2022 18:42:56 +0300 Subject: [PATCH 060/132] Refresh buttons added into history --- web/public/assets/icons/refresh-icon.svg | 1 + web/src/actions/walletActions.js | 1 - web/src/config/icons/static.js | 1 + .../TransactionsHistory/HistoryDisplay.js | 13 ++++++++++- .../_TransactionsHistory.scss | 23 ++++++++++++++++++- .../containers/TransactionsHistory/index.js | 6 +++-- web/src/index.css | 16 ++++++++++++- 7 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 web/public/assets/icons/refresh-icon.svg diff --git a/web/public/assets/icons/refresh-icon.svg b/web/public/assets/icons/refresh-icon.svg new file mode 100644 index 0000000000..1502e06685 --- /dev/null +++ b/web/public/assets/icons/refresh-icon.svg @@ -0,0 +1 @@ +<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" width="30px" height="30px"><path d="M 15 3 C 12.031398 3 9.3028202 4.0834384 7.2070312 5.875 A 1.0001 1.0001 0 1 0 8.5058594 7.3945312 C 10.25407 5.9000929 12.516602 5 15 5 C 20.19656 5 24.450989 8.9379267 24.951172 14 L 22 14 L 26 20 L 30 14 L 26.949219 14 C 26.437925 7.8516588 21.277839 3 15 3 z M 4 10 L 0 16 L 3.0507812 16 C 3.562075 22.148341 8.7221607 27 15 27 C 17.968602 27 20.69718 25.916562 22.792969 24.125 A 1.0001 1.0001 0 1 0 21.494141 22.605469 C 19.74593 24.099907 17.483398 25 15 25 C 9.80344 25 5.5490109 21.062074 5.0488281 16 L 8 16 L 4 10 z"/></svg> \ No newline at end of file diff --git a/web/src/actions/walletActions.js b/web/src/actions/walletActions.js index 3fbae0b81d..042a7b5451 100644 --- a/web/src/actions/walletActions.js +++ b/web/src/actions/walletActions.js @@ -221,7 +221,6 @@ export const getOrdersHistory = ({ dataParams.open = open; } const query = querystring.stringify(dataParams); - return (dispatch) => { dispatch({ type: ACTION_KEYS.ORDER_HISTORY_PENDING, payload: { page } }); axios diff --git a/web/src/config/icons/static.js b/web/src/config/icons/static.js index 2d983ead1c..18aa3a14af 100644 --- a/web/src/config/icons/static.js +++ b/web/src/config/icons/static.js @@ -48,6 +48,7 @@ const icons = { HELP_FOOTER_POPUP: '/assets/images/terms_of_service_and_privacy_policy-tooltip.png', HELP_REFERRAL_BADGE_POPUP: '/assets/images/help-popup-footer.png', + REFRESH: '/assets/icons/refresh-icon.svg', DEPOSIT_TIERS_SECTION: '/assets/images/deposit-tier-section.svg', WITHDRAW_TIERS_SECTION: '/assets/images/withdraw-tier-section.svg', TAKER_TIERS_SECTION: '/assets/images/taker-tier-section.svg', diff --git a/web/src/containers/TransactionsHistory/HistoryDisplay.js b/web/src/containers/TransactionsHistory/HistoryDisplay.js index f2fa5aee9e..23553b1c26 100644 --- a/web/src/containers/TransactionsHistory/HistoryDisplay.js +++ b/web/src/containers/TransactionsHistory/HistoryDisplay.js @@ -29,6 +29,7 @@ const HistoryDisplay = (props) => { handleNext, jumpToPage, handleDownload, + refetchData, icons: ICONS, activeTab, } = props; @@ -81,7 +82,7 @@ const HistoryDisplay = (props) => { text={STRINGS['TRANSACTION_HISTORY.TEXT_DOWNLOAD']} iconId="DATA" iconPath={ICONS['DATA']} - className="csv-action" + className="download-history" onClick={handleDownload} /> </div> @@ -96,6 +97,16 @@ const HistoryDisplay = (props) => { onClick={openDialog} /> ) : null} + <div className="download-icon"> + <ActionNotification + stringId="RESFRESH" + text={STRINGS['REFRESH']} + iconId="REFRESH" + iconPath={STATIC_ICONS['REFRESH']} + className="refresh-history" + onClick={refetchData} + /> + </div> </div> )} {filters} diff --git a/web/src/containers/TransactionsHistory/_TransactionsHistory.scss b/web/src/containers/TransactionsHistory/_TransactionsHistory.scss index c8d4890bb4..2625a7f300 100644 --- a/web/src/containers/TransactionsHistory/_TransactionsHistory.scss +++ b/web/src/containers/TransactionsHistory/_TransactionsHistory.scss @@ -159,8 +159,29 @@ } } .check-deposit-txt { - right: 150px !important; + right: 230px !important; + & svg { + padding-right: 0.3rem; + box-sizing: border-box; + border-right: 1px solid $trade-fill-indicator-text; + height: 100%; + } + } + + .download-history { + right: 85px !important; + & svg { + padding-right: 0.1rem; + box-sizing: content-box; + border-right: 1px solid $trade-fill-indicator-text; + height: 100%; + } } + + .refresh-history { + right: 10px !important; + } + .loader_wrapper { top: 0; left: 0; diff --git a/web/src/containers/TransactionsHistory/index.js b/web/src/containers/TransactionsHistory/index.js index 6fabc049b1..b0e052bcc6 100644 --- a/web/src/containers/TransactionsHistory/index.js +++ b/web/src/containers/TransactionsHistory/index.js @@ -449,6 +449,7 @@ class TransactionsHistory extends Component { props.handleDownload = () => downloadUserOrders(temp); props.filters = filters.orders; props.noData = prepareNoData('NO_ACTIVE_ORDERS'); + props.refetchData = () => this.requestData(activeTab); break; case 0: props.stringId = 'TRANSACTION_HISTORY.TITLE_TRADES'; @@ -462,7 +463,7 @@ class TransactionsHistory extends Component { props.handleDownload = () => downloadUserTrades(temp); props.filters = filters.trades; props.noData = prepareNoData('NO_ACTIVE_TRADES'); - + props.refetchData = () => this.requestData(activeTab); break; case 2: props.stringId = 'TRANSACTION_HISTORY.TITLE_DEPOSITS'; @@ -475,7 +476,7 @@ class TransactionsHistory extends Component { props.handleDownload = () => downloadUserDeposit(temp); props.filters = filters.deposits; props.noData = prepareNoData('NO_ACTIVE_DEPOSITS'); - + props.refetchData = () => this.requestData(activeTab); break; case 3: props.stringId = 'TRANSACTION_HISTORY.TITLE_WITHDRAWALS'; @@ -488,6 +489,7 @@ class TransactionsHistory extends Component { props.handleDownload = () => downloadUserWithdrawal(temp); props.filters = filters.withdrawals; props.noData = prepareNoData('NO_ACTIVE_WITHDRAWALS'); + props.refetchData = () => this.requestData(activeTab); break; default: return <div />; diff --git a/web/src/index.css b/web/src/index.css index d73be2273b..7e4f34c1f7 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -2226,7 +2226,21 @@ table th { border-radius: 20px; margin-right: 10px; } .history_block-wrapper .check-deposit-txt { - right: 150px !important; } + right: 230px !important; } + .history_block-wrapper .check-deposit-txt svg { + padding-right: .3rem; + box-sizing: border-box; + border-right: 1px solid var(--labels_secondary-inactive-label-text-graphics); + height: 100%; } + .history_block-wrapper .download-history { + right: 85px !important; } + .history_block-wrapper .download-history svg { + padding-right: .1rem; + box-sizing: content-box; + border-right: 1px solid var(--labels_secondary-inactive-label-text-graphics); + height: 100%; } + .history_block-wrapper .refresh-history { + right: 10px !important; } .history_block-wrapper .loader_wrapper { top: 0; left: 0; } From bcfb2eeacb34db50ae23bce19a421fb778d3e9c7 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Thu, 22 Sep 2022 19:05:55 +0300 Subject: [PATCH 061/132] Add symbol in the fields for order entry --- web/src/components/Form/TradeFormFields/InputField.js | 2 +- web/src/containers/Trade/components/OrderEntry.js | 7 ++----- web/src/index.css | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/web/src/components/Form/TradeFormFields/InputField.js b/web/src/components/Form/TradeFormFields/InputField.js index f577e73ce1..b291b32207 100644 --- a/web/src/components/Form/TradeFormFields/InputField.js +++ b/web/src/components/Form/TradeFormFields/InputField.js @@ -21,7 +21,7 @@ const InputField = (props) => { > <input ref={setRef} {...input} {...rest} /> {currency && ( - <div className="trade_input-input-currency d-flex justify-content-center align-items-center"> + <div className="trade_input-input-currency d-flex justify-content-center align-items-center mr-2"> {currency} </div> )} diff --git a/web/src/containers/Trade/components/OrderEntry.js b/web/src/containers/Trade/components/OrderEntry.js index 2ab1fed875..b3cdd0d3f0 100644 --- a/web/src/containers/Trade/components/OrderEntry.js +++ b/web/src/containers/Trade/components/OrderEntry.js @@ -451,9 +451,6 @@ class OrderEntry extends Component { side = 'buy', } = props; - const { display_name } = coins[pair] || DEFAULT_COIN_DATA; - const { display_name: buy_display_name } = - coins[buyingPair] || DEFAULT_COIN_DATA; const { initialValues: { order_type }, } = this.state; @@ -528,7 +525,7 @@ class OrderEntry extends Component { maxValue(max_price), step(increment_price), ], - currency: buy_display_name, + currency: pair_2_display, setRef: this.props.setPriceRef, }, size: { @@ -563,7 +560,7 @@ class OrderEntry extends Component { min: min_size, max: max_size, validate: [required, minValue(min_size), maxValue(max_size)], - currency: display_name, + currency: pair_base_display, setRef: this.props.setSizeRef, }, slider: { diff --git a/web/src/index.css b/web/src/index.css index 7e4f34c1f7..10d97c9681 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -2228,14 +2228,14 @@ table th { .history_block-wrapper .check-deposit-txt { right: 230px !important; } .history_block-wrapper .check-deposit-txt svg { - padding-right: .3rem; + padding-right: 0.3rem; box-sizing: border-box; border-right: 1px solid var(--labels_secondary-inactive-label-text-graphics); height: 100%; } .history_block-wrapper .download-history { right: 85px !important; } .history_block-wrapper .download-history svg { - padding-right: .1rem; + padding-right: 0.1rem; box-sizing: content-box; border-right: 1px solid var(--labels_secondary-inactive-label-text-graphics); height: 100%; } From 5dacaf189200e8d28c2dc986acb49851d039bd7c Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Thu, 22 Sep 2022 19:18:32 +0300 Subject: [PATCH 062/132] In drop down, keep the currently selected --- .../Form/FormFields/DropdownField.js | 18 ++++++++++++++++++ .../Form/FormFields/_DropdownField.scss | 4 ++++ web/src/index.css | 3 +++ 3 files changed, 25 insertions(+) diff --git a/web/src/components/Form/FormFields/DropdownField.js b/web/src/components/Form/FormFields/DropdownField.js index ce020c3f0c..381152686e 100644 --- a/web/src/components/Form/FormFields/DropdownField.js +++ b/web/src/components/Form/FormFields/DropdownField.js @@ -102,6 +102,20 @@ class DropdownField extends Component { </div> ); + renderOptionWhenOpen = (option, index) => ( + <div + id={`${this.props.input.name}-${option.value}-${index}`} + key={`${this.props.input.name}-${option.value}-${index}`} + onClick={this.onSelectOption(option)} + className={classnames('dropdown-option-open', { + pointer: !this.props.disabled, + })} + > + {this.renderIcon(option)} + {option.label} + </div> + ); + renderOptions = (options) => ( <div className={classnames('dropdown-options-wrapper')}> {options.length > 0 @@ -195,6 +209,10 @@ class DropdownField extends Component { </div> )} </div> + {isOpen && + selectedItem && + !autocomplete && + this.renderOptionWhenOpen(selectedItem)} {isOpen && this.renderOptions(filteredOptions)} </FieldWrapper> ); diff --git a/web/src/components/Form/FormFields/_DropdownField.scss b/web/src/components/Form/FormFields/_DropdownField.scss index a7aa1e391d..30b0765c72 100644 --- a/web/src/components/Form/FormFields/_DropdownField.scss +++ b/web/src/components/Form/FormFields/_DropdownField.scss @@ -15,6 +15,10 @@ $dropdown-option--padding: 0.25rem; } } +.dropdown-option-open { + margin: 0px !important; +} + .disabled { .field-children { color: $colors-deactivate; diff --git a/web/src/index.css b/web/src/index.css index 10d97c9681..94cbff9043 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -7707,6 +7707,9 @@ table th { cursor: not-allowed; color: var(--labels_secondary-inactive-label-text-graphics); } +.dropdown-option-open { + margin: 0px !important; } + .disabled .field-children { color: var(--labels_secondary-inactive-label-text-graphics); } .disabled .field-children .dropdown-triangle:after { From 7db9e3c389a54d3aa9b957c85e9407042564e8e8 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Thu, 22 Sep 2022 19:29:21 +0300 Subject: [PATCH 063/132] Inconsistent capital letters in theme drop down --- web/src/components/AppBar/ThemeSwitcher.js | 2 +- web/src/components/AppBar/_AppBar.scss | 3 +++ web/src/index.css | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/web/src/components/AppBar/ThemeSwitcher.js b/web/src/components/AppBar/ThemeSwitcher.js index ffe66657f6..46e76dba25 100644 --- a/web/src/components/AppBar/ThemeSwitcher.js +++ b/web/src/components/AppBar/ThemeSwitcher.js @@ -79,7 +79,7 @@ const ThemeSwitcher = ({ selected, options = [], toggle, icons: ICONS }) => { dropdownClassName="custom-select-style select-option-wrapper" > {options.map(({ value }) => ( - <Option value={value} key={value}> + <Option value={value} key={value} className="capitalize"> {value} </Option> ))} diff --git a/web/src/components/AppBar/_AppBar.scss b/web/src/components/AppBar/_AppBar.scss index fe837b6872..883a16dd2b 100644 --- a/web/src/components/AppBar/_AppBar.scss +++ b/web/src/components/AppBar/_AppBar.scss @@ -513,6 +513,9 @@ $app-menu-width: calc(100vw - 40rem); font-size: 11px; } } +.capitalize { + text-transform: capitalize; +} .home_app_bar { position: relative; diff --git a/web/src/index.css b/web/src/index.css index 94cbff9043..d5642b068c 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -6207,6 +6207,9 @@ table th { cursor: pointer; font-size: 11px; } +.capitalize { + text-transform: capitalize; } + .home_app_bar { position: relative; width: 100%; From 69504622e8a64f2c3de4509e5465bf122315e124 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Mon, 26 Sep 2022 20:11:20 +0900 Subject: [PATCH 064/132] min coin length set to 2 --- server/api/swagger/swagger.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/api/swagger/swagger.yaml b/server/api/swagger/swagger.yaml index 877101e4d2..c35eecd4eb 100644 --- a/server/api/swagger/swagger.yaml +++ b/server/api/swagger/swagger.yaml @@ -1613,7 +1613,7 @@ paths: in: query required: true type: string - minLength: 3 + minLength: 2 maxLength: 15 - name: network in: query From 8836c4b899c6eaabf01c5b24e061cc1caeed1b62 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 04:50:44 +0330 Subject: [PATCH 065/132] admin features section refinements --- .../containers/Admin/General/InterfaceForm.js | 341 ++++++++++-------- web/src/containers/Admin/General/index.css | 4 + 2 files changed, 190 insertions(+), 155 deletions(-) diff --git a/web/src/containers/Admin/General/InterfaceForm.js b/web/src/containers/Admin/General/InterfaceForm.js index 34e92eb2a4..ad6192217d 100644 --- a/web/src/containers/Admin/General/InterfaceForm.js +++ b/web/src/containers/Admin/General/InterfaceForm.js @@ -131,184 +131,215 @@ const InterfaceForm = ({ </Checkbox> </Item> - <div className={classnames({ 'disabled-area': isFiatUpgrade })}> - <Item name="ultimate_fiat" valuePropName="checked"> - <Checkbox className="mt-3"> - <div className="d-flex align-items-center"> - <div className="feature-trade-box mr-1"> - <ReactSVG - src={STATIC_ICONS.MPESA_ICON} - className="d-flex feature-icon justify-content-center mr-2 mt-3 ml-1 pl-1" - beforeInjection={(svg) => { - svg.setAttribute('style', 'width: 60px'); - }} - /> + <div className="d-flex"> + <div + className={classnames('interface-item', { + 'disabled-area': isFiatUpgrade, + })} + > + <Item name="ultimate_fiat" valuePropName="checked"> + <Checkbox className="mt-3"> + <div className="d-flex align-items-center"> + <div className="feature-trade-box mr-1"> + <ReactSVG + src={STATIC_ICONS.MPESA_ICON} + className="d-flex feature-icon justify-content-center mr-2 mt-3 ml-1 pl-1" + beforeInjection={(svg) => { + svg.setAttribute('style', 'width: 60px'); + }} + /> + </div> + <div className="ml-2 checkbox-txt"> + Fiat Controls + <div className="small-text"> + (On & off ramping and tracking for fiat assets) + </div> + </div> </div> - <div className="ml-2 checkbox-txt"> - Ultimate fiat - <div className="small-text">(Ultimate fiat ...)</div> + </Checkbox> + </Item> + </div> + {isFiatUpgrade && ( + <div className="d-flex"> + <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> + <div> + <div className="font-weight-bold"> + Powerful fiat ramping + </div> + <div>Cash in and out with fiat ramps</div> </div> - </div> - </Checkbox> - </Item> - </div> - {isFiatUpgrade ? ( - <div className="d-flex"> - <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> - <div> - <div className="font-weight-bold"> - Make a good first impression + <div className="ml-5 button-wrapper"> + <a + href="https://dash.bitholla.com/billing" + target="_blank" + rel="noopener noreferrer" + > + <Button type="primary" className="w-100"> + Upgrade Now + </Button> + </a> </div> - <div>Add a customizable landing page</div> - </div> - <div className="ml-5 button-wrapper"> - <a - href="https://dash.bitholla.com/billing" - target="_blank" - rel="noopener noreferrer" - > - <Button type="primary" className="w-100"> - Upgrade Now - </Button> - </a> </div> </div> - </div> - ) : null} - <div className={classnames({ 'disabled-area': isUpgrade })}> - <Item name="chat" valuePropName="checked"> - <Checkbox className="mt-3"> - <div className="d-flex align-items-center"> - <div className="feature-trade-box mr-1"> - <ReactSVG - src={STATIC_ICONS.CHAT_FEATURE_ICON} - className="feature-chat-icon" - /> - </div> - <div className="ml-2 checkbox-txt"> - Chat system - <div className="d-flex justify-content-between"> - <div className="small-text"> - (Usernames, text and emoji communication) + )} + </div> + + <div className="d-flex"> + <div + className={classnames('interface-item', { + 'disabled-area': isUpgrade, + })} + > + <Item name="chat" valuePropName="checked"> + <Checkbox className="mt-3"> + <div className="d-flex align-items-center"> + <div className="feature-trade-box mr-1"> + <ReactSVG + src={STATIC_ICONS.CHAT_FEATURE_ICON} + className="feature-chat-icon" + /> + </div> + <div className="ml-2 checkbox-txt"> + Chat system + <div className="d-flex justify-content-between"> + <div className="small-text"> + (Usernames, text and emoji communication) + </div> </div> </div> </div> - </div> - </Checkbox> - </Item> - </div> - {isUpgrade ? ( - <div className="d-flex"> - <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> - <div> - <div className="font-weight-bold"> - Start your crypto culture - </div> - <div>Allow your users to socialize through chat</div> - </div> - <div className="ml-5 button-wrapper"> - <a - href="https://dash.bitholla.com/billing" - target="_blank" - rel="noopener noreferrer" - > - <Button type="primary" className="w-100"> - Upgrade Now - </Button> - </a> - </div> - </div> + </Checkbox> + </Item> </div> - ) : null} - <div className={classnames({ 'disabled-area': isUpgrade })}> - <Item name="home_page" valuePropName="checked"> - <Checkbox className="mt-3"> - <div className="d-flex align-items-center"> - <div className="feature-trade-box mr-1"> - <ReactSVG - src={STATIC_ICONS.HOME_PAGE_FEATURE_ICON} - className="feature-chat-icon" - /> - </div> - <div className="ml-2 checkbox-txt"> - Homepage - <div className="d-flex justify-content-between"> - <div className="small-text"> - (This will be the first page seen on your domain) - </div> + {isUpgrade && ( + <div className="d-flex"> + <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> + <div> + <div className="font-weight-bold"> + Start your crypto culture </div> + <div>Allow your users to socialize through chat</div> + </div> + <div className="ml-5 button-wrapper"> + <a + href="https://dash.bitholla.com/billing" + target="_blank" + rel="noopener noreferrer" + > + <Button type="primary" className="w-100"> + Upgrade Now + </Button> + </a> </div> </div> - </Checkbox> - </Item> + </div> + )} </div> - <div className={classnames({ 'disabled-area': isUpgrade })}> - <Item name="apps" valuePropName="checked"> - <Checkbox className="mt-3"> - <div className="d-flex align-items-center"> - <div className="feature-trade-box mr-1"> - <ReactSVG - src={STATIC_ICONS.APPS_FEATURE_ICON} - className="feature-apps-icon" - /> + + <div className="d-flex"> + <div + className={classnames('interface-item', { + 'disabled-area': isUpgrade, + })} + > + <Item name="home_page" valuePropName="checked"> + <Checkbox className="mt-3"> + <div className="d-flex align-items-center"> + <div className="feature-trade-box mr-1"> + <ReactSVG + src={STATIC_ICONS.HOME_PAGE_FEATURE_ICON} + className="feature-chat-icon" + /> + </div> + <div className="ml-2 checkbox-txt"> + Homepage + <div className="d-flex justify-content-between"> + <div className="small-text"> + (This will be the first page seen on your domain) + </div> + </div> + </div> </div> - <div className="ml-2 checkbox-txt"> - Apps - <div className="d-flex justify-content-between"> - <div className="small-text">(Apps ...)</div> + </Checkbox> + </Item> + </div> + {isUpgrade && ( + <div className="d-flex"> + <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> + <div> + <div className="font-weight-bold"> + Make a good first impression </div> + <div>Add a customizable landing page</div> </div> - </div> - </Checkbox> - </Item> - </div> - {isUpgrade && ( - <div className="d-flex"> - <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> - <div> - <div className="font-weight-bold"> - Start your crypto culture + <div className="ml-5 button-wrapper"> + <a + href="https://dash.bitholla.com/billing" + target="_blank" + rel="noopener noreferrer" + > + <Button type="primary" className="w-100"> + Upgrade Now + </Button> + </a> </div> - <div>Allow your users to socialize through chat</div> - </div> - <div className="ml-5 button-wrapper"> - <a - href="https://dash.bitholla.com/billing" - target="_blank" - rel="noopener noreferrer" - > - <Button type="primary" className="w-100"> - Upgrade Now - </Button> - </a> </div> </div> - </div> - )} - </div> - {isUpgrade ? ( + )} + </div> + <div className="d-flex"> - <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> - <div> - <div className="font-weight-bold"> - Make a good first impression + <div + className={classnames('interface-item', { + 'disabled-area': isUpgrade, + })} + > + <Item name="apps" valuePropName="checked"> + <Checkbox className="mt-3"> + <div className="d-flex align-items-center"> + <div className="feature-trade-box mr-1"> + <ReactSVG + src={STATIC_ICONS.APPS_FEATURE_ICON} + className="feature-apps-icon" + /> + </div> + <div className="ml-2 checkbox-txt"> + Apps + <div className="d-flex justify-content-between"> + <div className="small-text"> + (Give your users extra exchange applications) + </div> + </div> + </div> + </div> + </Checkbox> + </Item> + </div> + {isUpgrade && ( + <div className="d-flex"> + <div className="d-flex align-items-center justify-content-between upgrade-section mt-2 mb-5"> + <div> + <div className="font-weight-bold"> + First exchange app store + </div> + <div>Add more exchange functionality</div> + </div> + <div className="ml-5 button-wrapper"> + <a + href="https://dash.bitholla.com/billing" + target="_blank" + rel="noopener noreferrer" + > + <Button type="primary" className="w-100"> + Upgrade Now + </Button> + </a> + </div> </div> - <div>Add a customizable landing page</div> </div> - <div className="ml-5 button-wrapper"> - <a - href="https://dash.bitholla.com/billing" - target="_blank" - rel="noopener noreferrer" - > - <Button type="primary" className="w-100"> - Upgrade Now - </Button> - </a> - </div> - </div> + )} </div> - ) : null} + </div> <div> <FormButton type="primary" diff --git a/web/src/containers/Admin/General/index.css b/web/src/containers/Admin/General/index.css index 2e4b963ad8..c0359d7b4d 100644 --- a/web/src/containers/Admin/General/index.css +++ b/web/src/containers/Admin/General/index.css @@ -365,6 +365,10 @@ h2, pointer-events: none; } +.general-wrapper .interface-item { + min-width: 418px; +} + .general-wrapper .description a { text-decoration: underline !important; } From abed1333b153bcbd4697211888be874d446de376 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 06:04:35 +0330 Subject: [PATCH 066/132] apps section refinements --- web/src/config/lang/en.json | 6 +++++- web/src/containers/Apps/All.js | 30 ++++++++++++++++++++++++------ web/src/containers/Apps/User.js | 2 +- web/src/containers/Apps/_Apps.scss | 4 ++++ web/src/containers/Apps/index.js | 2 +- web/src/index.css | 3 +++ 6 files changed, 38 insertions(+), 9 deletions(-) diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index de36bb180a..017d4f1cc3 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -485,7 +485,11 @@ "TAB_TITLE": "All apps", "TITLE": "Exchange apps", "SUBTITLE": "Get more functionality from your exchange account by simply selecting an app below and clicking Add button.", - "SEARCH_PLACEHOLDER": "Search apps..." + "SEARCH_PLACEHOLDER": "Search apps...", + "ADD": { + "SUCCESSFUL": "You've successfully added a new app!", + "FAILED": "Something went wrong" + } }, "MY_APPS": { "TAB_TITLE": "My apps", diff --git a/web/src/containers/Apps/All.js b/web/src/containers/Apps/All.js index 261364bf06..6448083705 100644 --- a/web/src/containers/Apps/All.js +++ b/web/src/containers/Apps/All.js @@ -5,13 +5,14 @@ import { Input } from 'antd'; import debounce from 'lodash.debounce'; import { SearchOutlined } from '@ant-design/icons'; import { updateUserSettings, setUserData } from 'actions/userAction'; +import { setSnackNotification } from 'actions/appActions'; import { IconTitle, EditWrapper, Table, Button, Image } from 'components'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; import { unique } from 'utils/data'; import { isEnabled, appsSelector } from './utils'; -const generateHeaders = (addApp, isAdded) => { +const generateHeaders = (addApp, isAdded, ICONS) => { return [ { stringId: 'USER_APPS.TABLE.APP_NAME', @@ -28,7 +29,11 @@ const generateHeaders = (addApp, isAdded) => { renderCell: ({ name }, key) => ( <td key={`${key}-${name}-app`} className="text-align-right"> {isAdded(name) ? ( - <Image icon={''} iconId={''} wrapperClassName={''} /> + <Image + icon={ICONS['GREEN_CHECK']} + iconId={'GREEN_CHECK'} + wrapperClassName={'apps-table-check'} + /> ) : ( <Button label={STRINGS['USER_APPS.TABLE.ADD']} @@ -56,6 +61,8 @@ const All = ({ apps, settings: { apps: user_apps = [] }, setUserData, + setActiveTab, + setSnackNotification, }) => { const [search, setSearch] = useState(); const [data, setData] = useState(apps); @@ -68,8 +75,18 @@ const All = ({ apps: unique([...user_apps, name]), }; updateUserSettings(settings) - .then(({ data }) => setUserData(data)) - .catch((err) => console.log('error')); + .then(({ data }) => { + setUserData(data); + setActiveTab(1); + setSnackNotification({ + content: STRINGS['USER_APPS.ALL_APPS.ADD.SUCCESSFUL'], + }); + }) + .catch(() => { + setSnackNotification({ + content: STRINGS['USER_APPS.ALL_APPS.ADD.FAILED'], + }); + }); }; const isAdded = (name) => { @@ -110,7 +127,7 @@ const All = ({ textType="title" iconPath={ICONS['APPS_ALL']} /> - <div> + <div className="py-4"> <EditWrapper stringId="USER_APPS.ALL_APPS.SUBTITLE"> {STRINGS['USER_APPS.ALL_APPS.SUBTITLE']} </EditWrapper> @@ -132,7 +149,7 @@ const All = ({ <Table showHeaderNoData={true} rowClassName="pt-2 pb-2" - headers={generateHeaders(addApp, isAdded)} + headers={generateHeaders(addApp, isAdded, ICONS)} count={data.length} pageSize={data.length} data={data} @@ -159,6 +176,7 @@ const mapStateToProps = (state) => { const mapDispatchToProps = (dispatch) => ({ setUserData: bindActionCreators(setUserData, dispatch), + setSnackNotification: bindActionCreators(setSnackNotification, dispatch), }); export default connect(mapStateToProps, mapDispatchToProps)(withConfig(All)); diff --git a/web/src/containers/Apps/User.js b/web/src/containers/Apps/User.js index 4a118c4d32..81e4fc4626 100644 --- a/web/src/containers/Apps/User.js +++ b/web/src/containers/Apps/User.js @@ -86,7 +86,7 @@ const User = ({ textType="title" iconPath={ICONS['APPS_USER']} /> - <div> + <div className="py-4"> <EditWrapper stringId="USER_APPS.MY_APPS.SUBTITLE"> {STRINGS['USER_APPS.MY_APPS.SUBTITLE']} </EditWrapper> diff --git a/web/src/containers/Apps/_Apps.scss b/web/src/containers/Apps/_Apps.scss index 529bed33a3..031c5603d8 100644 --- a/web/src/containers/Apps/_Apps.scss +++ b/web/src/containers/Apps/_Apps.scss @@ -17,3 +17,7 @@ position: relative !important; } } + +.apps-table-check { + width: 10px !important; +} diff --git a/web/src/containers/Apps/index.js b/web/src/containers/Apps/index.js index 1edabae983..765a84d0f0 100644 --- a/web/src/containers/Apps/index.js +++ b/web/src/containers/Apps/index.js @@ -35,7 +35,7 @@ const Index = ({ icons: ICONS, openContactForm }) => { ) : ( <div>{STRINGS['USER_APPS.ALL_APPS.TAB_TITLE']}</div> ), - content: <All />, + content: <All setActiveTab={setActiveTab} />, }, { title: isMobile ? ( diff --git a/web/src/index.css b/web/src/index.css index d5642b068c..0d8caf4051 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -5473,6 +5473,9 @@ table th { .action_notification-wrapper.remove-action { position: relative !important; } +.apps-table-check { + width: 10px !important; } + .sidebar-row { min-height: 3rem; min-width: 10rem; } From 62b1ede6925e4d79cbe88536d05ea222c1449ca5 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 06:07:07 +0330 Subject: [PATCH 067/132] fix for snack notification scrolling issue --- web/src/components/SnackNotification/_SnackNotification.scss | 5 +++-- web/src/index.css | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/web/src/components/SnackNotification/_SnackNotification.scss b/web/src/components/SnackNotification/_SnackNotification.scss index a16cc69593..29eedbadba 100644 --- a/web/src/components/SnackNotification/_SnackNotification.scss +++ b/web/src/components/SnackNotification/_SnackNotification.scss @@ -7,9 +7,10 @@ height: 3.5rem; left: calc(50vw - 10rem); padding: 0.3rem 0.5rem !important; - position: absolute; + position: fixed; + min-width: 20rem; + max-width: 30rem; // top: 1rem; - width: 20rem; z-index: 9999; -webkit-box-shadow: 2px 2px 3px 2px rgba(0, 0, 0, 0.4); -moz-box-shadow: 2px 2px 3px 2px rgba(0, 0, 0, 0.4); diff --git a/web/src/index.css b/web/src/index.css index 0d8caf4051..bd518b91a6 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -10370,8 +10370,9 @@ table th { height: 3.5rem; left: calc(50vw - 10rem); padding: 0.3rem 0.5rem !important; - position: absolute; - width: 20rem; + position: fixed; + min-width: 20rem; + max-width: 30rem; z-index: 9999; -webkit-box-shadow: 2px 2px 3px 2px rgba(0, 0, 0, 0.4); -moz-box-shadow: 2px 2px 3px 2px rgba(0, 0, 0, 0.4); From 65259a26f4ce850f5d73bee4be8c8daacd51dbf4 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 06:27:35 +0330 Subject: [PATCH 068/132] fix for table component row key issue --- web/src/components/Table/TableBody.js | 2 ++ web/src/components/Table/index.js | 3 +++ 2 files changed, 5 insertions(+) diff --git a/web/src/components/Table/TableBody.js b/web/src/components/Table/TableBody.js index 45572d8247..443d49b457 100644 --- a/web/src/components/Table/TableBody.js +++ b/web/src/components/Table/TableBody.js @@ -82,6 +82,7 @@ const TableBody = ({ cancelDelayData, expandable, cssTransitionClassName, + rowKey, }) => ( <tbody className={classnames('table_body-wrapper', { @@ -113,6 +114,7 @@ const TableBody = ({ <Fragment> {data.map((row, rowIndex) => ( <TableRow + key={rowKey(row)} headers={headers} cancelDelayData={cancelDelayData} expandable={expandable} diff --git a/web/src/components/Table/index.js b/web/src/components/Table/index.js index fb7c61edce..2c59fa7e1f 100644 --- a/web/src/components/Table/index.js +++ b/web/src/components/Table/index.js @@ -105,6 +105,7 @@ class Table extends Component { className, expandable, cssTransitionClassName, + rowKey, } = this.props; const count = this.props.count || this.props.data.length; @@ -153,6 +154,7 @@ class Table extends Component { withIcon={withIcon} expandable={expandable} cssTransitionClassName={cssTransitionClassName} + rowKey={rowKey} /> </table> </div> @@ -190,6 +192,7 @@ Table.defaultProps = { rowExpandable: () => false, }, cssTransitionClassName: '', + rowKey: ({ id }) => id, }; export default Table; From b33b894dcbe36cd2a1344a71615c330164866f3c Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 06:28:16 +0330 Subject: [PATCH 069/132] fix for apps section search results issue --- web/src/containers/Apps/All.js | 3 +-- web/src/containers/Apps/User.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/web/src/containers/Apps/All.js b/web/src/containers/Apps/All.js index 6448083705..dde8de9773 100644 --- a/web/src/containers/Apps/All.js +++ b/web/src/containers/Apps/All.js @@ -150,8 +150,7 @@ const All = ({ showHeaderNoData={true} rowClassName="pt-2 pb-2" headers={generateHeaders(addApp, isAdded, ICONS)} - count={data.length} - pageSize={data.length} + showAll={true} data={data} rowKey={({ name }) => name} displayPaginator={false} diff --git a/web/src/containers/Apps/User.js b/web/src/containers/Apps/User.js index 81e4fc4626..62159421a8 100644 --- a/web/src/containers/Apps/User.js +++ b/web/src/containers/Apps/User.js @@ -95,8 +95,7 @@ const User = ({ showHeaderNoData={true} rowClassName="pt-2 pb-2" headers={generateHeaders(goToDetails, openConfigs)} - count={data.length} - pageSize={data.length} + showAll={true} data={data} rowKey={({ name }) => name} displayPaginator={false} From 2fde7e7b0b4d654753b7c2d60ef3b66ace5e3001 Mon Sep 17 00:00:00 2001 From: sahlabadi <sahlabadi2002@gmail.com> Date: Tue, 27 Sep 2022 11:39:58 +0800 Subject: [PATCH 070/132] precondition of testing fix, Audio cues --- test/Cypress/cypress/integration/Gherkin/pre/pre.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Cypress/cypress/integration/Gherkin/pre/pre.js b/test/Cypress/cypress/integration/Gherkin/pre/pre.js index b3ba2d222e..b17df16b67 100644 --- a/test/Cypress/cypress/integration/Gherkin/pre/pre.js +++ b/test/Cypress/cypress/integration/Gherkin/pre/pre.js @@ -69,7 +69,7 @@ And ('Show pop up when order has partially filled is off',()=>{ And ('All Audio cues is off',()=>{ cy.get('.tab_controller-tabs > :nth-child(2) > div').as('Interface').click() - cy.contains('Play Audio Cue').click() + cy.contains('Audio Cues').click() cy.get('.toggle-wrapper-all > :nth-child(1) > :nth-child(1) > .field-content > .field-children > .justify-content-between > .toggle_button-wrapper > .toggle-content > .toggle-action_button') .invoke('text') .then(text => { @@ -124,8 +124,8 @@ And ('chatbox is off',()=>{ And ('XHTT spread in market is less than 0.001',()=>{ cy.get('.top-box-menu').click() cy.contains('Pro trade').click() - cy.contains('XHTT/USDT').click() - cy.get('.app_bar-currency-txt').should('have.text', 'XHTT/USDT:') + cy.contains('XHT/USDT').click() + cy.get('.app_bar-currency-txt').should('have.text', 'XHT/USDT:') cy.get('.trade_orderbook-asks > :nth-child(1) > .d-flex > .trade_orderbook-cell-price') .should('be.visible') cy.log('check lowest aks exist and click on the price will send the price') From 9638587d767b3039bb9c0ecef95c3082251e4fa1 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 07:23:51 +0330 Subject: [PATCH 071/132] apps input clear button --- web/src/containers/Apps/All.js | 1 + web/src/containers/Apps/_Apps.scss | 3 +++ web/src/index.css | 3 +++ 3 files changed, 7 insertions(+) diff --git a/web/src/containers/Apps/All.js b/web/src/containers/Apps/All.js index dde8de9773..ba1d2d1d8f 100644 --- a/web/src/containers/Apps/All.js +++ b/web/src/containers/Apps/All.js @@ -134,6 +134,7 @@ const All = ({ </div> <div> <Input + allowClear prefix={<SearchOutlined className="secondary-text" />} placeholder={STRINGS['USER_APPS.ALL_APPS.SEARCH_PLACEHOLDER']} value={search} diff --git a/web/src/containers/Apps/_Apps.scss b/web/src/containers/Apps/_Apps.scss index 031c5603d8..4174315c2c 100644 --- a/web/src/containers/Apps/_Apps.scss +++ b/web/src/containers/Apps/_Apps.scss @@ -10,6 +10,9 @@ .ant-input { color: $colors-main-black; } + .ant-input-clear-icon { + color: $colors-black; + } } .action_notification-wrapper { diff --git a/web/src/index.css b/web/src/index.css index bd518b91a6..94c8f8d4e6 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -5470,6 +5470,9 @@ table th { .apps-form .ant-input { color: var(--labels_important-active-labels-text-graphics); } +.apps-form .ant-input-clear-icon { + color: var(--labels_secondary-inactive-label-text-graphics); } + .action_notification-wrapper.remove-action { position: relative !important; } From 112e87279ff675283613574edec0981d9d0a99ef Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 07:53:23 +0330 Subject: [PATCH 072/132] user reducer and actions updates --- web/src/containers/Apps/All.js | 2 +- web/src/containers/Apps/User.js | 2 +- web/src/utils/utils.js | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/web/src/containers/Apps/All.js b/web/src/containers/Apps/All.js index ba1d2d1d8f..66c4996195 100644 --- a/web/src/containers/Apps/All.js +++ b/web/src/containers/Apps/All.js @@ -72,7 +72,7 @@ const All = ({ const addApp = (name) => { // send add app request and show toast notification const settings = { - apps: unique([...user_apps, name]), + app: unique([...user_apps, name]), }; updateUserSettings(settings) .then(({ data }) => { diff --git a/web/src/containers/Apps/User.js b/web/src/containers/Apps/User.js index 62159421a8..65a4aeb619 100644 --- a/web/src/containers/Apps/User.js +++ b/web/src/containers/Apps/User.js @@ -65,7 +65,7 @@ const User = ({ const onRemove = (name) => { // send add app request and show toast notification const settings = { - apps: apps.filter((app) => app !== name), + app: apps.filter((app) => app !== name), }; updateUserSettings(settings) .then(({ data }) => { diff --git a/web/src/utils/utils.js b/web/src/utils/utils.js index d662a57c43..8fe84658f2 100644 --- a/web/src/utils/utils.js +++ b/web/src/utils/utils.js @@ -104,6 +104,9 @@ export const constructSettings = (state = {}, settings) => { if (settings.language) { settingsData.language = settings.language; } + if (settings.app) { + settingsData.apps = settings.app; + } // ToDo: need to these code after end point update. if ( From 7b072f221b36aae8044991c00390390e79f5cb14 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 08:02:33 +0330 Subject: [PATCH 073/132] apps section minor design improvement --- web/src/containers/Apps/All.js | 11 +++++++++-- web/src/containers/Apps/User.js | 4 ++-- web/src/containers/Apps/_Apps.scss | 2 +- web/src/index.css | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/web/src/containers/Apps/All.js b/web/src/containers/Apps/All.js index 66c4996195..7dcc699a09 100644 --- a/web/src/containers/Apps/All.js +++ b/web/src/containers/Apps/All.js @@ -27,7 +27,7 @@ const generateHeaders = (addApp, isAdded, ICONS) => { { label: '', renderCell: ({ name }, key) => ( - <td key={`${key}-${name}-app`} className="text-align-right"> + <td key={`${key}-${name}-app`} className="text-align-center"> {isAdded(name) ? ( <Image icon={ICONS['GREEN_CHECK']} @@ -70,10 +70,10 @@ const All = ({ const inputRef = useRef(null); const addApp = (name) => { - // send add app request and show toast notification const settings = { app: unique([...user_apps, name]), }; + updateUserSettings(settings) .then(({ data }) => { setUserData(data); @@ -81,6 +81,13 @@ const All = ({ setSnackNotification({ content: STRINGS['USER_APPS.ALL_APPS.ADD.SUCCESSFUL'], }); + if (window) { + window.scroll({ + top: 0, + left: 0, + behavior: 'smooth', + }); + } }) .catch(() => { setSnackNotification({ diff --git a/web/src/containers/Apps/User.js b/web/src/containers/Apps/User.js index 65a4aeb619..fbf7f87d79 100644 --- a/web/src/containers/Apps/User.js +++ b/web/src/containers/Apps/User.js @@ -62,11 +62,12 @@ const User = ({ closeNotification, }) => { const goToDetails = (name) => router.push(`apps/details/${name}`); + const openConfigs = (name) => openConfigureApps(() => onRemove(name)); const onRemove = (name) => { - // send add app request and show toast notification const settings = { app: apps.filter((app) => app !== name), }; + updateUserSettings(settings) .then(({ data }) => { setUserData(data); @@ -74,7 +75,6 @@ const User = ({ }) .catch((err) => console.log('error')); }; - const openConfigs = (name) => openConfigureApps(() => onRemove(name)); return ( <div> diff --git a/web/src/containers/Apps/_Apps.scss b/web/src/containers/Apps/_Apps.scss index 4174315c2c..a32acfa386 100644 --- a/web/src/containers/Apps/_Apps.scss +++ b/web/src/containers/Apps/_Apps.scss @@ -22,5 +22,5 @@ } .apps-table-check { - width: 10px !important; + width: 2rem !important; } diff --git a/web/src/index.css b/web/src/index.css index 94c8f8d4e6..a4de90f08d 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -5477,7 +5477,7 @@ table th { position: relative !important; } .apps-table-check { - width: 10px !important; } + width: 2rem !important; } .sidebar-row { min-height: 3rem; From aeadaaf6f7289aae49e9248274205573e5d788d2 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 27 Sep 2022 10:40:23 +0330 Subject: [PATCH 074/132] fix for apps selector issue --- web/src/containers/Apps/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/containers/Apps/utils.js b/web/src/containers/Apps/utils.js index df0e15103e..170fb47529 100644 --- a/web/src/containers/Apps/utils.js +++ b/web/src/containers/Apps/utils.js @@ -3,7 +3,7 @@ import { createSelector } from 'reselect'; const getPlugins = (state) => state.app.plugins; const getUserApps = (state) => state.user.settings.apps || []; export const appsSelector = createSelector([getPlugins], (plugins) => - plugins.filter((type) => type === 'app') + plugins.filter(({ type }) => type === 'app') ); export const userAppsSelector = createSelector( From fa9abac1e5d32610539bbb76ce50cbaf75760d21 Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Wed, 28 Sep 2022 00:13:25 +0300 Subject: [PATCH 075/132] Multiprocess plugins --- server/package.json | 3 +- server/plugins/controllers.js | 25 ++-- server/plugins/index.js | 207 +++++++------------------------ server/plugins/plugin-process.js | 193 ++++++++++++++++++++++++++++ 4 files changed, 247 insertions(+), 181 deletions(-) create mode 100644 server/plugins/plugin-process.js diff --git a/server/package.json b/server/package.json index 80aa579df2..9e2ca08efa 100644 --- a/server/package.json +++ b/server/package.json @@ -14,7 +14,6 @@ "license": "bitHolla Inc.", "main": "app.js", "dependencies": { - "JSONStream": "1.3.5", "bcryptjs": "2.4.3", "bluebird": "3.5.3", "body-parser": "1.19.0", @@ -34,9 +33,11 @@ "hollaex-network-lib": "file:utils/hollaex-network-lib", "hollaex-tools-lib": "file:utils/hollaex-tools-lib", "http": "0.0.0", + "http-proxy-middleware": "^2.0.6", "install": "0.10.4", "ip-range-check": "0.2.0", "json2csv": "4.5.4", + "JSONStream": "1.3.5", "jsonwebtoken": "7.4.3", "latest-version": "5.1.0", "lodash": "4.17.20", diff --git a/server/plugins/controllers.js b/server/plugins/controllers.js index 3989d1cf2b..b56ba5200c 100644 --- a/server/plugins/controllers.js +++ b/server/plugins/controllers.js @@ -124,10 +124,8 @@ const deletePlugin = async (req, res) => { 'restarting plugin process' ); - const { stopPlugin } = require('./index'); - + const { stopPlugin } = require('./index') stopPlugin(plugin); - // process.exit(); } } catch (err) { loggerPlugin.error( @@ -284,10 +282,8 @@ const postPlugin = async (req, res) => { ); if (plugin.enabled && plugin.script) { - const { startPlugin } = require('./index'); - + const { startPlugin } = require('./index') startPlugin(plugin); - // process.exit(); } } catch (err) { loggerPlugin.error( @@ -470,7 +466,9 @@ const putPlugin = async (req, res) => { ); if (updatedPlugin.enabled && updatedPlugin.script) { - process.exit(); + const { stopPlugin, startPlugin } = require('./index') + stopPlugin(plugin); + startPlugin(updatedPlugin.dataValues); } } catch (err) { loggerPlugin.error( @@ -610,7 +608,9 @@ const putPluginConfig = async (req, res) => { ); if (plugin.enabled && plugin.script) { - process.exit(); + const { stopPlugin, startPlugin } = require('./index') + stopPlugin(plugin); + startPlugin(updatedPlugin.dataValues); } } catch (err) { loggerPlugin.error( @@ -715,9 +715,7 @@ const disablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { - // process.exit(); - const { stopPlugin } = require('./index'); - + const { stopPlugin } = require('./index') stopPlugin(plugin); } } catch (err) { @@ -773,10 +771,7 @@ const enablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { - //process.exit(); - - const { startPlugin } = require('./index'); - + const { startPlugin } = require('./index') startPlugin(plugin); } } catch (err) { diff --git a/server/plugins/index.js b/server/plugins/index.js index ce23c65fee..4e8ca4b8f9 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -15,101 +15,23 @@ const fs = require('fs'); const latestVersion = require('latest-version'); const npm = require('npm-programmatic'); const sequelize = require('sequelize'); -const _eval = require('eval'); -const toolsLib = require('hollaex-tools-lib'); -const lodash = require('lodash'); -const expressValidator = require('express-validator'); -const multer = require('multer'); -const moment = require('moment'); -const mathjs = require('mathjs'); -const bluebird = require('bluebird'); -const umzug = require('umzug'); -const rp = require('request-promise'); -const uuid = require('uuid/v4'); -const jwt = require('jsonwebtoken'); -const momentTz = require('moment-timezone'); -const json2csv = require('json2csv'); -const flat = require('flat'); -const ws = require('ws'); -const cron = require('node-cron'); -const randomString = require('random-string'); -const bcryptjs = require('bcryptjs'); -const expectCt = require('expect-ct'); -const validator = require('validator'); -const otp = require('otp'); -const geoipLite = require('geoip-lite'); -const nodemailer = require('nodemailer'); -const wsHeartbeatServer = require('ws-heartbeat/server'); -const wsHeartbeatClient = require('ws-heartbeat/client'); -const winston = require('winston'); -const elasticApmNode = require('elastic-apm-node'); -const winstonElasticsearchApm = require('winston-elasticsearch-apm'); -const tripleBeam = require('triple-beam'); -const uglifyEs = require('uglify-es'); -const bodyParser = require('body-parser'); +const pluginProcess = path.join(__dirname, "./plugin-process.js"); +const fork = require('child_process').fork +const { createProxyMiddleware } = require('http-proxy-middleware'); let app; -let disabledPlugins = {}; +let activePlugins = {} -const getInstalledLibrary = async (name, version) => { - const jsonFilePath = path.resolve(__dirname, '../node_modules', name, 'package.json'); - - const fileData = fs.readFileSync(jsonFilePath); - const parsedFileData = JSON.parse(fileData); - - loggerPlugin.verbose( - 'plugins/index/getInstalledLibrary', - `${name} library found` - ); - - const checkVersion = version === 'latest' ? await latestVersion(name) : version; - - if (parsedFileData.version !== checkVersion) { - throw new Error('Version does not match'); - } - - loggerPlugin.verbose( - 'plugins/index/getInstalledLibrary', - `${name} version ${version} found` - ); - - const lib = require(name); - return lib; -}; - -const installLibrary = async (library) => { - const [name, version = 'latest'] = library.split('@'); +const stopPlugin = async (plugin) => { try { - const data = await getInstalledLibrary(name, version); - return data; - } catch (err) { loggerPlugin.verbose( - 'plugins/index/installLibrary', - `${name} version ${version} installing` - ); - - await npm.install([`${name}@${version}`], { - cwd: path.resolve(__dirname, '../'), - save: true, - output: true - }); - - loggerPlugin.verbose( - 'plugins/index/installLibrary', - `${name} version ${version} installed` + 'plugins/index/kill_plugin', + `killing plugin ${plugin.name}` ); - const lib = require(name); - return lib; - } -}; - -const stopPlugin = async (plugin) => { - - try { - const subStr = plugin.script.match(/\"\/plugins(.*?)\"/g); - disabledPlugins[plugin.name] = subStr || []; + activePlugins[plugin.name].process.kill(); + delete activePlugins[plugin.name]; } catch (err) { loggerPlugin.error( @@ -126,79 +48,17 @@ const startPlugin = async (plugin) => { 'plugins/index/initialization', `starting plugin ${plugin.name}` ); + const pluginData = { PORT: 10011 + plugin.id, plugin } + const childProcess = fork(pluginProcess); + childProcess.send(JSON.stringify(pluginData)); + const subStr = plugin.script.match(/\"\/plugins(.*?)\"/g); - const context = { - configValues: { - publicMeta: plugin.public_meta, - meta: plugin.meta - }, - pluginLibraries: { - app, - loggerPlugin, - toolsLib - }, - app, - toolsLib, - lodash, - expressValidator, - loggerPlugin, - multer, - moment, - mathjs, - bluebird, - umzug, - rp, - sequelize, - uuid, - jwt, - momentTz, - json2csv, - flat, - ws, - cron, - randomString, - bcryptjs, - expectCt, - validator, - uglifyEs, - otp, - latestVersion, - geoipLite, - nodemailer, - wsHeartbeatServer, - wsHeartbeatClient, - cors, - winston, - elasticApmNode, - winstonElasticsearchApm, - tripleBeam, - bodyParser, - morgan, - meta: plugin.meta, - publicMeta: plugin.public_meta, - installedLibraries: {} + activePlugins[plugin.name] = { + process: childProcess, + port: pluginData.PORT, + endpoints: subStr || [], }; - if (plugin.prescript && lodash.isArray(plugin.prescript.install) && !lodash.isEmpty(plugin.prescript.install)) { - loggerPlugin.verbose( - 'plugins/index/initialization', - `Installing packages for plugin ${plugin.name}` - ); - - for (const library of plugin.prescript.install) { - context.installedLibraries[library] = await installLibrary(library); - } - - loggerPlugin.verbose( - 'plugins/index/initialization', - `Plugin ${plugin.name} packages installed` - ); - } - - _eval(plugin.script, plugin.name, context, true); - - if (disabledPlugins[plugin.name]) delete disabledPlugins[plugin.name]; - loggerPlugin.verbose( 'plugins/index/initialization', @@ -232,17 +92,25 @@ checkStatus() app.use(domainMiddleware); helmetMiddleware(app); - app.use((req, res, next) => { + + const customRouter = function (req) { if (req.path.length > 1 && req.path.includes('/plugins/')) { - //Check if plugin is disabled - for (let endpoints of Object.values(disabledPlugins)) { - if (endpoints.some(endpoint => endpoint.includes(req.path))) { return res.status(404).send() } + for (let plugin of Object.values(activePlugins)) { + if (plugin.endpoints.some(endpoint => endpoint.includes(req.path))) { + return `http://localhost:${plugin.port}`; + } } } - next(); - }) + return `http://localhost:10012`; + + }; + + const options = { + target: 'http://localhost:10012', + router: customRouter, + }; - app.use('/plugins', routes); + app.use('/plugins', routes, createProxyMiddleware(options)); const plugins = await Plugin.findAll({ where: { @@ -255,7 +123,16 @@ checkStatus() }); for (const plugin of plugins) { - startPlugin(plugin); + const pluginData = { PORT: 10011 + plugin.id, plugin } + const childProcess = fork(pluginProcess); + childProcess.send(JSON.stringify(pluginData)); + const subStr = plugin.script.match(/\"\/plugins(.*?)\"/g); + + activePlugins[plugin.name] = { + process: childProcess, + port: pluginData.PORT, + endpoints: subStr || [], + }; } loggerPlugin.info( diff --git a/server/plugins/plugin-process.js b/server/plugins/plugin-process.js new file mode 100644 index 0000000000..96bce28d2a --- /dev/null +++ b/server/plugins/plugin-process.js @@ -0,0 +1,193 @@ +const express = require('express'); +const morgan = require('morgan'); +const morganType = process.env.NODE_ENV === 'development' ? 'dev' : 'combined'; +const { logEntryRequest, stream, loggerPlugin } = require('../config/logger'); +const cors = require('cors'); +const { domainMiddleware, helmetMiddleware } = require('../config/middleware'); +const path = require('path'); +const fs = require('fs'); +const latestVersion = require('latest-version'); +const npm = require('npm-programmatic'); +const sequelize = require('sequelize'); +const _eval = require('eval'); +const toolsLib = require('hollaex-tools-lib'); +const lodash = require('lodash'); +const expressValidator = require('express-validator'); +const multer = require('multer'); +const moment = require('moment'); +const mathjs = require('mathjs'); +const bluebird = require('bluebird'); +const umzug = require('umzug'); +const rp = require('request-promise'); +const uuid = require('uuid/v4'); +const jwt = require('jsonwebtoken'); +const momentTz = require('moment-timezone'); +const json2csv = require('json2csv'); +const flat = require('flat'); +const ws = require('ws'); +const cron = require('node-cron'); +const randomString = require('random-string'); +const bcryptjs = require('bcryptjs'); +const expectCt = require('expect-ct'); +const validator = require('validator'); +const otp = require('otp'); +const geoipLite = require('geoip-lite'); +const nodemailer = require('nodemailer'); +const wsHeartbeatServer = require('ws-heartbeat/server'); +const wsHeartbeatClient = require('ws-heartbeat/client'); +const winston = require('winston'); +const elasticApmNode = require('elastic-apm-node'); +const winstonElasticsearchApm = require('winston-elasticsearch-apm'); +const tripleBeam = require('triple-beam'); +const uglifyEs = require('uglify-es'); +const bodyParser = require('body-parser'); +// const PORT = workerData.PLUGIN_PORT || 10011; + + + +const getInstalledLibrary = async (name, version) => { + const jsonFilePath = path.resolve(__dirname, '../node_modules', name, 'package.json'); + + const fileData = fs.readFileSync(jsonFilePath); + const parsedFileData = JSON.parse(fileData); + + loggerPlugin.verbose( + 'plugins/index/getInstalledLibrary', + `${name} library found` + ); + + const checkVersion = version === 'latest' ? await latestVersion(name) : version; + + if (parsedFileData.version !== checkVersion) { + throw new Error('Version does not match'); + } + + loggerPlugin.verbose( + 'plugins/index/getInstalledLibrary', + `${name} version ${version} found` + ); + + const lib = require(name); + return lib; +}; + +const installLibrary = async (library) => { + const [name, version = 'latest'] = library.split('@'); + + try { + const data = await getInstalledLibrary(name, version); + return data; + } catch (err) { + loggerPlugin.verbose( + 'plugins/index/installLibrary', + `${name} version ${version} installing` + ); + + await npm.install([`${name}@${version}`], { + cwd: path.resolve(__dirname, '../'), + save: true, + output: true + }); + + loggerPlugin.verbose( + 'plugins/index/installLibrary', + `${name} version ${version} installed` + ); + + const lib = require(name); + return lib; + } +}; + + +const initPluginProcess = async ({ PORT, plugin }) => { + + const app = express(); + + app.use(morgan(morganType, { stream })); + // app.listen(10012); + app.listen(PORT); + app.use(cors()); + app.use(express.urlencoded({ extended: true })); + app.use(express.json()); + app.use(logEntryRequest); + app.use(domainMiddleware); + helmetMiddleware(app); + + const context = { + configValues: { + publicMeta: plugin.public_meta, + meta: plugin.meta + }, + pluginLibraries: { + app, + loggerPlugin, + toolsLib + }, + app, + toolsLib, + lodash, + expressValidator, + loggerPlugin, + multer, + moment, + mathjs, + bluebird, + umzug, + rp, + sequelize, + uuid, + jwt, + momentTz, + json2csv, + flat, + ws, + cron, + randomString, + bcryptjs, + expectCt, + validator, + uglifyEs, + otp, + latestVersion, + geoipLite, + nodemailer, + wsHeartbeatServer, + wsHeartbeatClient, + cors, + winston, + elasticApmNode, + winstonElasticsearchApm, + tripleBeam, + bodyParser, + morgan, + meta: plugin.meta, + publicMeta: plugin.public_meta, + installedLibraries: {} + }; + + if (plugin.prescript && lodash.isArray(plugin.prescript.install) && !lodash.isEmpty(plugin.prescript.install)) { + loggerPlugin.verbose( + 'plugins/index/initialization', + `Installing packages for plugin ${plugin.name}` + ); + + for (const library of plugin.prescript.install) { + context.installedLibraries[library] = await installLibrary(library); + } + + loggerPlugin.verbose( + 'plugins/index/initialization', + `Plugin ${plugin.name} packages installed` + ); + } + + _eval(plugin.script, plugin.name, context, true); + + +} + +process.on("message", function (message) { + initPluginProcess(JSON.parse(message)); + +}); From e3df60f19a4c3e44f928a49f8129851aa9c44a5a Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Wed, 28 Sep 2022 00:39:50 +0300 Subject: [PATCH 076/132] Plugin Default Url --- server/plugins/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/plugins/index.js b/server/plugins/index.js index 4e8ca4b8f9..4754fce675 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -93,6 +93,7 @@ checkStatus() helmetMiddleware(app); + const defaultURL = 'http://localhost:10012'; const customRouter = function (req) { if (req.path.length > 1 && req.path.includes('/plugins/')) { for (let plugin of Object.values(activePlugins)) { @@ -101,12 +102,12 @@ checkStatus() } } } - return `http://localhost:10012`; + return defaultURL; }; const options = { - target: 'http://localhost:10012', + target: defaultURL, router: customRouter, }; From f0d47e52a17f48fed955e99bf6e0df4e3c195b5e Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Wed, 28 Sep 2022 01:24:21 +0300 Subject: [PATCH 077/132] Remove commented out lines --- server/plugins/plugin-process.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/plugins/plugin-process.js b/server/plugins/plugin-process.js index 96bce28d2a..13003ff612 100644 --- a/server/plugins/plugin-process.js +++ b/server/plugins/plugin-process.js @@ -41,7 +41,6 @@ const winstonElasticsearchApm = require('winston-elasticsearch-apm'); const tripleBeam = require('triple-beam'); const uglifyEs = require('uglify-es'); const bodyParser = require('body-parser'); -// const PORT = workerData.PLUGIN_PORT || 10011; @@ -105,7 +104,6 @@ const initPluginProcess = async ({ PORT, plugin }) => { const app = express(); app.use(morgan(morganType, { stream })); - // app.listen(10012); app.listen(PORT); app.use(cors()); app.use(express.urlencoded({ extended: true })); From e37f04da958392756a96766e4ad49208dea21c90 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Sat, 1 Oct 2022 12:40:16 +0900 Subject: [PATCH 078/132] get user using id instead of email --- server/api/controllers/user.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/api/controllers/user.js b/server/api/controllers/user.js index 13e63238b6..2f4294ea29 100644 --- a/server/api/controllers/user.js +++ b/server/api/controllers/user.js @@ -505,9 +505,9 @@ const resetPassword = (req, res) => { const getUser = (req, res) => { loggerUser.debug(req.uuid, 'controllers/user/getUser', req.auth.sub); - const email = req.auth.sub.email; + const id = req.auth.sub.id; - toolsLib.user.getUserByEmail(email, true, true, { + toolsLib.user.getUserByKitId(id, true, true, { additionalHeaders: { 'x-forwarded-for': req.headers['x-forwarded-for'] } From 84860e855e4022693e2355e9b1aa57cc131c3425 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Mon, 3 Oct 2022 00:46:42 +0900 Subject: [PATCH 079/132] fiat withdrawal email confirmation flow --- server/api/controllers/withdrawal.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/server/api/controllers/withdrawal.js b/server/api/controllers/withdrawal.js index 2a184dd53f..021739c0cd 100644 --- a/server/api/controllers/withdrawal.js +++ b/server/api/controllers/withdrawal.js @@ -121,7 +121,24 @@ const performWithdrawal = (req, res) => { }), withdrawal ]); + } else if (toolsLib.getKitCoin(withdrawal.currency).type === 'fiat') { + // burn the asset + return all([ + toolsLib.wallet.burnAssetByKitId( + withdrawal.user_id, + withdrawal.currency, + withdrawal.amount, + { + transactionId: withdrawal.transaction_id, + address: withdrawal.address, + status: false, + fee: withdrawal.fee + } + ), + withdrawal + ]); } else { + // blockchain type to sent to the network return all([ toolsLib.wallet.performWithdrawal( withdrawal.user_id, From bd41034ce630a04f176bc032cecd01eee1ac4488 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Mon, 3 Oct 2022 00:47:20 +0900 Subject: [PATCH 080/132] withdrawal request flow improvement to accomodate fiat withdrawal --- server/utils/hollaex-tools-lib/tools/wallet.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/server/utils/hollaex-tools-lib/tools/wallet.js b/server/utils/hollaex-tools-lib/tools/wallet.js index a250c50b7e..05154dfe09 100644 --- a/server/utils/hollaex-tools-lib/tools/wallet.js +++ b/server/utils/hollaex-tools-lib/tools/wallet.js @@ -195,9 +195,15 @@ async function validateWithdrawal(user, address, amount, currency, network = nul const sendRequestWithdrawalEmail = (user_id, address, amount, currency, opts = { network: null, otpCode: null, + fee: null, + fee_coin: null, + skipValidate: false, // should be used with care if set to true ip: null, domain: null }) => { + let fee = opts.fee; + let fee_coin = opts.fee_coin; + return verifyOtpBeforeAction(user_id, opts.otpCode) .then((validOtp) => { if (!validOtp) { @@ -206,7 +212,12 @@ const sendRequestWithdrawalEmail = (user_id, address, amount, currency, opts = { return getUserByKitId(user_id); }) .then(async (user) => { - const { fee, fee_coin } = await validateWithdrawal(user, address, amount, currency, opts.network); + if (!opts.skipValidate) { + const withdrawal = await validateWithdrawal(user, address, amount, currency, opts.network); + fee = withdrawal.fee; + fee_coin = withdrawal.fee_coin; + } + return withdrawalRequestEmail( user, @@ -230,7 +241,7 @@ const sendRequestWithdrawalEmail = (user_id, address, amount, currency, opts = { const withdrawalRequestEmail = (user, data, domain, ip) => { data.timestamp = Date.now(); let stringData = JSON.stringify(data); - const token = crypto.randomBytes(60).toString('hex'); + const token = data.transaction_id || crypto.randomBytes(60).toString('hex'); return client.hsetAsync(WITHDRAWALS_REQUEST_KEY, token, stringData) .then(() => { From a8e06cef9e9c06d617ff63a5989d4ac4986e8d9a Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Mon, 3 Oct 2022 03:02:43 +0300 Subject: [PATCH 081/132] FEE Preview fix --- web/src/containers/Withdraw/ReviewModalContent.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/containers/Withdraw/ReviewModalContent.js b/web/src/containers/Withdraw/ReviewModalContent.js index 194efd3ebc..092f897111 100644 --- a/web/src/containers/Withdraw/ReviewModalContent.js +++ b/web/src/containers/Withdraw/ReviewModalContent.js @@ -49,7 +49,7 @@ const ReviewModalContent = ({ const fee_type = data.fee_type ? data.fee_type : ''; const isPercentage = fee_type === 'percentage'; const hasDifferentFeeCoin = - !isPercentage && fee_coin && fee_coin !== currency; + !isPercentage && !!fee_coin && fee_coin !== currency; let min_fee; let max_fee; @@ -76,7 +76,7 @@ const ReviewModalContent = ({ const feePrice = math.number(math.multiply(fee, price)); - const totalTransaction = hasDifferentFeeCoin + const totalTransaction = !!hasDifferentFeeCoin ? math.number(math.fraction(data.amount)) : math.number(math.add(math.fraction(data.amount), fee)); @@ -89,7 +89,7 @@ const ReviewModalContent = ({ const { display_name: fee_coin_display } = coins[fee_coin] || DEFAULT_COIN_DATA; - const withdrawFeeMessage = hasDifferentFeeCoin + const withdrawFeeMessage = !!hasDifferentFeeCoin ? STRINGS.formatString( STRINGS['WITHDRAW_PAGE.MESSAGE_FEE_COIN'], STRINGS.formatString(CURRENCY_PRICE_FORMAT, fee, fee_coin_display) From f606b0bea6d31ab3f7797eaa0e7d0c1c4774e46e Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Mon, 3 Oct 2022 03:26:06 +0300 Subject: [PATCH 082/132] 2FA, change password modal fix --- web/src/containers/UserSecurity/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/containers/UserSecurity/index.js b/web/src/containers/UserSecurity/index.js index c2b19ce4e5..17d2ff0d09 100644 --- a/web/src/containers/UserSecurity/index.js +++ b/web/src/containers/UserSecurity/index.js @@ -386,6 +386,9 @@ class UserVerification extends Component { if (otp_enabled) { this.setState({ dialogIsOpen: true, + modalText: + STRINGS['ACCOUNT_SECURITY.CHANGE_PASSWORD.DIALOG.EMAIL_CONFIRMATION'], + stringId: 'ACCOUNT_SECURITY.CHANGE_PASSWORD.DIALOG.EMAIL_CONFIRMATION', updatedPassword: { old_password: values.old_password, new_password: values.new_password, From a224557b9c52b00d3265f89c4407b0d234d4f4cd Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 4 Oct 2022 12:39:05 +0330 Subject: [PATCH 083/132] minor app details refinements --- web/src/config/lang/en.json | 3 ++- web/src/containers/AppDetails/index.js | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index 017d4f1cc3..0fccda8b04 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -507,7 +507,8 @@ "RETRY": "Try another search term" }, "APP_DETAILS": { - "BACK_TO_APPS": "{0} to my apps", + "BACK_PLACEHOLDER": "{0} {1}", + "BACK_TO_APPS": "to my apps", "BACK": "Back" }, "CONFIGURE": { diff --git a/web/src/containers/AppDetails/index.js b/web/src/containers/AppDetails/index.js index fa3886bd5d..2d167d759f 100644 --- a/web/src/containers/AppDetails/index.js +++ b/web/src/containers/AppDetails/index.js @@ -18,7 +18,7 @@ const Index = ({ openContactForm, icons: ICONS, router, userApps }) => { } = router; if (!mounted) { - if (!app || userApps.map(({ name }) => name).includes(app)) { + if (!app || !userApps.map(({ name }) => name).includes(app)) { goBack(); } } @@ -46,12 +46,15 @@ const Index = ({ openContactForm, icons: ICONS, router, userApps }) => { <div> <EditWrapper stringId="USER_APPS.APP_DETAILS.BACK_TO_APPS,USER_APPS.APP_DETAILS.BACK"> {STRINGS.formatString( - STRINGS['USER_APPS.APP_DETAILS.BACK_TO_APPS'], + STRINGS['USER_APPS.APP_DETAILS.BACK_PLACEHOLDER'], <span className="blue-link underline-text pointer" onClick={goBack} > {STRINGS['USER_APPS.APP_DETAILS.BACK']} + </span>, + <span className="px-1"> + {STRINGS['USER_APPS.APP_DETAILS.BACK_TO_APPS']} </span> )} </EditWrapper> From d97caa6b5ec2b74167048af66368db70de1fa31a Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 4 Oct 2022 12:40:36 +0330 Subject: [PATCH 084/132] admin apps initial commit --- web/src/containers/Admin/AppWrapper/index.js | 20 ++- web/src/containers/Admin/Apps/index.css | 43 ++++++ web/src/containers/Admin/Apps/index.js | 135 +++++++++++++++++++ web/src/containers/Admin/Apps/utils.js | 7 + web/src/containers/Admin/Plugins/index.js | 26 +++- web/src/containers/AppDetails/index.js | 2 +- web/src/containers/index.js | 1 + web/src/reducers/appReducer.js | 9 +- web/src/routes.js | 6 + web/src/utils/id.js | 2 +- 10 files changed, 240 insertions(+), 11 deletions(-) create mode 100644 web/src/containers/Admin/Apps/index.css create mode 100644 web/src/containers/Admin/Apps/index.js create mode 100644 web/src/containers/Admin/Apps/utils.js diff --git a/web/src/containers/Admin/AppWrapper/index.js b/web/src/containers/Admin/AppWrapper/index.js index 82a11fb2d7..5fb57807e5 100644 --- a/web/src/containers/Admin/AppWrapper/index.js +++ b/web/src/containers/Admin/AppWrapper/index.js @@ -426,6 +426,8 @@ class AppWrapper extends React.Component { return 'Markets'; } else if (location.pathname.includes('/admin/plugins')) { return 'Plugins'; + } else if (location.pathname.includes('/admin/apps')) { + return 'Apps'; } else if (location.pathname.includes('/admin/tiers')) { return 'Tiers'; } else if (location.pathname.includes('/admin/roles')) { @@ -549,7 +551,12 @@ class AppWrapper extends React.Component { }; render() { - const { children, router, user } = this.props; + const { + children, + router, + user, + constants: { features }, + } = this.props; const logout = () => { removeToken(); router.replace('/login'); @@ -576,6 +583,17 @@ class AppWrapper extends React.Component { } }); + if (features.apps) { + pathNames = [ + ...pathNames, + { + path: '/admin/apps', + label: 'Apps', + routeKey: 'apps', + }, + ]; + } + if (!isLoaded) return null; if (!isLoggedIn()) { router.replace('/login'); diff --git a/web/src/containers/Admin/Apps/index.css b/web/src/containers/Admin/Apps/index.css new file mode 100644 index 0000000000..6ec015a74f --- /dev/null +++ b/web/src/containers/Admin/Apps/index.css @@ -0,0 +1,43 @@ +.apps-list-container { + color: #ffffff; + margin: 0rem auto; + padding: 2rem 0rem; +} + +.apps-list-container .apps-icon { + height: 5rem; + margin: 0rem 1rem 0rem 0.5rem; + width: 4rem; +} + +.apps-list-container .apps-icon .apps { + fill: transparent !important; + stroke: #ffffff !important; +} + +.apps-list-container .small-font { + font-size: 9px; + font-weight: bold; +} + +.apps-list-container .apps-text { + width: 45rem; +} + +.apps-list-container .apps-title { + font-size: 18px; + font-weight: bold; +} + +.apps-list-container .anchor { + text-decoration: underline; + cursor: pointer; +} + +.apps-list-container .ant-breadcrumb-link { + cursor: pointer; +} + +.apps-list-container .ant-breadcrumb-link span:hover { + color: var(--admin_panel_main_font); +} diff --git a/web/src/containers/Admin/Apps/index.js b/web/src/containers/Admin/Apps/index.js new file mode 100644 index 0000000000..588f4765f5 --- /dev/null +++ b/web/src/containers/Admin/Apps/index.js @@ -0,0 +1,135 @@ +import React, { useState, useEffect, Fragment } from 'react'; +import { connect } from 'react-redux'; +import { ReactSVG } from 'react-svg'; +import { STATIC_ICONS } from 'config/icons'; +import { Table, Breadcrumb, Button } from 'antd'; +import { appsSelector } from './utils'; +import { SmartTarget } from 'components'; +import { generateDynamicTarget } from 'utils/id'; + +import './index.css'; + +const { Item } = Breadcrumb; + +const generateHeaders = (configure, viewApp) => { + return [ + { + title: 'App name', + key: 'name', + dataIndex: 'name', + }, + { + title: 'Description', + key: 'description', + dataIndex: 'description', + }, + { + title: 'Config', + key: '', + dataIndex: '', + render: (_, { name }, key) => ( + <td key={`${key}-${name}-configure`}> + <span + className="underline-text pointer no-wrap" + onClick={() => configure(name)} + > + Config + </span> + </td> + ), + }, + { + title: 'App details', + key: '', + dataIndex: '', + render: (_, { name }, key) => ( + <td key={`${key}-${name}-view`}> + <span + className="underline-text pointer no-wrap" + onClick={() => viewApp(name)} + > + View App + </span> + </td> + ), + }, + ]; +}; + +const Index = ({ router, apps }) => { + const [app, setApp] = useState(); + const [id, setId] = useState(); + + const back = () => setApp(); + const configure = (app) => router.push(`/admin/plugins?plugin=${app}`); + const goToPlugins = () => router.push('/admin/plugins'); + + useEffect(() => { + setId(generateDynamicTarget(app, 'app', 'admin')); + }, [app]); + + return ( + <div className="app_container-content admin-user-container admin-user-content apps-list-container"> + {app ? ( + <Fragment> + <Breadcrumb> + <Item> + <span onClick={back}>Apps</span> + </Item> + <Item>{app}</Item> + </Breadcrumb> + <SmartTarget id={id} onBack={back} /> + </Fragment> + ) : ( + <Fragment> + <div className="d-flex align-items-center"> + <div> + <ReactSVG + src={STATIC_ICONS.APPS_FEATURE_ICON} + className="apps-icon" + /> + </div> + <div className="apps-text"> + <div className="apps-title">Active apps</div> + <div> + <span> + Below is a list of exchange applications that were activated + from the{' '} + </span> + <span className="anchor" onClick={goToPlugins}> + plugins page. + </span> + <span> + {' '} + These apps are active and visible to your users and provide + extra functionality. + </span> + </div> + </div> + </div> + + <div className="pt-3 pb-5 text-align-right"> + <Button type="primary" className="green-btn" onClick={goToPlugins}> + Add plugin app + </Button> + </div> + + <Table + className="blue-admin-table" + dataSource={apps} + columns={generateHeaders(configure, setApp)} + pagination={false} + /> + </Fragment> + )} + </div> + ); +}; + +const mapStateToProps = (state) => { + return { + apps: appsSelector(state), + }; +}; + +export default connect(mapStateToProps)(Index); diff --git a/web/src/containers/Admin/Apps/utils.js b/web/src/containers/Admin/Apps/utils.js new file mode 100644 index 0000000000..77dc4cf936 --- /dev/null +++ b/web/src/containers/Admin/Apps/utils.js @@ -0,0 +1,7 @@ +import { createSelector } from 'reselect'; + +const getPlugins = (state) => state.app.plugins; + +export const appsSelector = createSelector([getPlugins], (plugins) => + plugins.filter(({ type }) => type === 'app') +); diff --git a/web/src/containers/Admin/Plugins/index.js b/web/src/containers/Admin/Plugins/index.js index e0711e0b7e..187da49255 100644 --- a/web/src/containers/Admin/Plugins/index.js +++ b/web/src/containers/Admin/Plugins/index.js @@ -18,6 +18,11 @@ const { Item } = Breadcrumb; class Plugins extends Component { constructor(props) { super(props); + const { + router: { + location: { query: { plugin } = {} }, + }, + } = this.props; this.state = { activeTab: '', loading: false, @@ -32,7 +37,7 @@ class Plugins extends Component { isVisible: false, isRemovePlugin: false, removePluginName: '', - tabKey: 'explore', + tabKey: plugin ? 'my_plugin' : 'explore', pluginCards: [], processing: false, }; @@ -75,7 +80,20 @@ class Plugins extends Component { return requestMyPlugins(params) .then((res) => { if (res && res.data) { - this.setState({ myPlugins: res.data }); + const { + router, + router: { + location: { pathname, query: { plugin } = {} }, + }, + } = this.props; + this.setState({ myPlugins: res.data }, () => { + const pluginData = res.data.find(({ name }) => name === plugin); + if (pluginData) { + this.handleOpenPlugin(pluginData); + this.handleBreadcrumb(); + router.push(pathname); + } + }); } }) .catch((err) => { @@ -183,7 +201,7 @@ class Plugins extends Component { if (plugin_type === 'add_plugin' && !isConfigure) { this.setState({ type: 'configure', - isConfigure: true + isConfigure: true, }); } } else { @@ -200,7 +218,7 @@ class Plugins extends Component { selectedPlugin: {}, type: '', isConfigure: false, - tabKey: 'explore' + tabKey: 'explore', }); }; diff --git a/web/src/containers/AppDetails/index.js b/web/src/containers/AppDetails/index.js index 2d167d759f..19c89ec537 100644 --- a/web/src/containers/AppDetails/index.js +++ b/web/src/containers/AppDetails/index.js @@ -23,7 +23,7 @@ const Index = ({ openContactForm, icons: ICONS, router, userApps }) => { } } - const id = generateDynamicTarget(app, 'app'); + const id = generateDynamicTarget(app, 'app', 'kit'); useEffect(() => { setMounted(true); diff --git a/web/src/containers/index.js b/web/src/containers/index.js index 33cd4d5027..202c8176ec 100644 --- a/web/src/containers/index.js +++ b/web/src/containers/index.js @@ -66,3 +66,4 @@ export { default as Roles } from './Admin/Roles'; export { default as Resources } from './Admin/Resources'; export { default as Pairs } from './Admin/Trades'; export { default as Fiatmarkets } from './Admin/Fiat'; +export { default as AdminApps } from './Admin/Apps'; diff --git a/web/src/reducers/appReducer.js b/web/src/reducers/appReducer.js index 8241506288..28d7302d53 100644 --- a/web/src/reducers/appReducer.js +++ b/web/src/reducers/appReducer.js @@ -489,9 +489,7 @@ const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { allWebViews.forEach((plugin) => { const { target: staticTarget, meta, name, plugin_type } = plugin; let target; - if (plugin_type === 'app') { - target = generateDynamicTarget(name, 'app'); - } else if (staticTarget) { + if (staticTarget) { target = staticTarget; } else if (meta) { const { @@ -502,7 +500,10 @@ const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { type, currency, } = meta; - if (is_page) { + + if (plugin_type === 'app') { + target = generateDynamicTarget(name, 'app', type); + } else if (is_page) { target = generateDynamicTarget(name, 'page'); } else if (is_verification_tab && type) { target = generateDynamicTarget(name, 'verification', type); diff --git a/web/src/routes.js b/web/src/routes.js index 9b7adbfd1e..509c4c1f74 100644 --- a/web/src/routes.js +++ b/web/src/routes.js @@ -58,6 +58,7 @@ import { Resources, Pairs, Fiatmarkets, + AdminApps, } from './containers'; import chat from './containers/Admin/Chat'; @@ -544,6 +545,11 @@ export const generateRoutes = (routes = []) => { name="Admin plugins" component={withAdminProps(Plugins, 'plugins')} /> + <Route + path="/admin/apps" + name="Admin apps" + component={withAdminProps(AdminApps, 'apps')} + /> {/* <Route path="/admin/plugins/:services" name="Admin plugins" diff --git a/web/src/utils/id.js b/web/src/utils/id.js index bc8a4e86df..29f0bbba7a 100644 --- a/web/src/utils/id.js +++ b/web/src/utils/id.js @@ -11,7 +11,7 @@ export const generateDynamicTarget = ( const sub = subType.toUpperCase(); switch (type) { case 'app': - return `APPLICATION__${name}`; + return `APPLICATION__${name}__${sub}`; case 'verification': return `REMOTE_VERIFICATION_TAB__${name}__${sub}`; case 'page': From a8277c7f3ee3037c5730c0f38d5f7d6c758e8fca Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Tue, 4 Oct 2022 23:19:36 +0900 Subject: [PATCH 085/132] chart edge case for decimal point scaling --- web/src/actions/chartAction.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/actions/chartAction.js b/web/src/actions/chartAction.js index 204412996b..a87ccb56e7 100644 --- a/web/src/actions/chartAction.js +++ b/web/src/actions/chartAction.js @@ -28,6 +28,7 @@ export const getChartSymbol = (symbol, tickSize, api_name = '') => { let count = getDecimals(tickSize); pricescale = math.pow(10, count); } + if (pricescale === 0) pricescale = 1; // return axios({ // url: `/udf/symbols?symbol=${symbol}`, // method: 'GET' From f5a6e4e8a14e501ebd606e969e01d99e45de80e8 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 5 Oct 2022 06:42:17 +0330 Subject: [PATCH 086/132] fix for booting image issue --- web/src/helpers/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/helpers/boot.js b/web/src/helpers/boot.js index b6ba92e09b..3f3970f6e1 100644 --- a/web/src/helpers/boot.js +++ b/web/src/helpers/boot.js @@ -16,7 +16,7 @@ const getLoadingImage = () => { export const setLoadingImage = ({ icons }) => { const theme = localStorage.getItem('theme'); getLoadingImage().src = - icons[theme]['EXCHANGE_LOADER'] || + (icons[theme] && icons[theme]['EXCHANGE_LOADER']) || icons[defaultIconsKey]['EXCHANGE_LOADER']; }; From 8d054d5c504ef223b39f64128ad639e32dd1d6cc Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 5 Oct 2022 07:57:54 +0330 Subject: [PATCH 087/132] fix for input group parsing warning --- web/src/components/QuickTrade/InputGroup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/QuickTrade/InputGroup.js b/web/src/components/QuickTrade/InputGroup.js index a1ab564f06..6d985a3a77 100644 --- a/web/src/components/QuickTrade/InputGroup.js +++ b/web/src/components/QuickTrade/InputGroup.js @@ -159,7 +159,7 @@ class InputGroup extends React.PureComponent { placeholder={STRINGS['AMOUNT']} style={isOpen ? { display: 'none' } : { width: '67%' }} className="input-group__input" - value={`${inputValue}`} + value={inputValue || ''} onChange={this.onChangeEvent} bordered={false} step={limits.MIN} From 5b2f64e2a1a962edaea87a10bdaf24b1b934ee91 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 5 Oct 2022 09:55:54 +0330 Subject: [PATCH 088/132] handling edge-cases for missing coin object for pairs --- web/src/utils/reducer.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/src/utils/reducer.js b/web/src/utils/reducer.js index 410bca8055..d7a2391298 100644 --- a/web/src/utils/reducer.js +++ b/web/src/utils/reducer.js @@ -1,4 +1,5 @@ import { generateCoinIconId } from 'utils/icon'; +import { DEFAULT_COIN_DATA } from 'config/constants'; export const modifyCoinsData = (coins) => { Object.entries(coins).forEach(([key, data]) => { @@ -13,8 +14,9 @@ export const modifyCoinsData = (coins) => { export const modifyPairsData = (pairs, coins) => { Object.entries(pairs).forEach(([key, data]) => { const { pair_base, pair_2 } = data; - const { display_name: pair_base_display, icon_id } = coins[pair_base]; - const { display_name: pair_2_display } = coins[pair_2]; + const { display_name: pair_base_display, icon_id } = + coins[pair_base] || DEFAULT_COIN_DATA; + const { display_name: pair_2_display } = coins[pair_2] || DEFAULT_COIN_DATA; const display_name = `${pair_base_display}/${pair_2_display}`; data.pair_base_display = pair_base_display; From 87778e5381dac57d3fe4a65670c28bf5252c6ac8 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 5 Oct 2022 10:20:14 +0330 Subject: [PATCH 089/132] smart target and default children improvements --- web/src/components/SmartTarget/index.js | 47 +++++++++++++++---------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/web/src/components/SmartTarget/index.js b/web/src/components/SmartTarget/index.js index eab4197f83..984b8c16b8 100644 --- a/web/src/components/SmartTarget/index.js +++ b/web/src/components/SmartTarget/index.js @@ -12,25 +12,33 @@ import renderFields from 'components/Form/factoryFields'; import { getErrorLocalized } from 'utils/errors'; import { IconTitle, ErrorBoundary } from 'components'; -const DefaultChildren = ({ strings: STRINGS, icons: ICONS }) => { +const DefaultChildren = ({ + strings: STRINGS, + icons: ICONS, + extra: { top, bottom } = {}, +}) => { return ( - <div - style={{ - height: '28rem', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - }} - > - <IconTitle - stringId="PAGE_UNDER_CONSTRUCTION" - text={STRINGS['PAGE_UNDER_CONSTRUCTION']} - iconId="FIAT_UNDER_CONSTRUCTION" - iconPath={ICONS['FIAT_UNDER_CONSTRUCTION']} - className="flex-direction-column" - /> - </div> + <Fragment> + {top} + <div + style={{ + height: '28rem', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }} + > + <IconTitle + stringId="PAGE_UNDER_CONSTRUCTION" + text={STRINGS['PAGE_UNDER_CONSTRUCTION']} + iconId="FIAT_UNDER_CONSTRUCTION" + iconPath={ICONS['FIAT_UNDER_CONSTRUCTION']} + className="flex-direction-column" + /> + </div> + {bottom} + </Fragment> ); }; @@ -44,6 +52,7 @@ const SmartTarget = (props) => { loaderClassName = 'default-remote-component-loader', errorClassName = 'default-remote-component-error', icons: ICONS, + extra, } = props; return targets.includes(id) ? ( @@ -68,7 +77,7 @@ const SmartTarget = (props) => { ) : children ? ( <Fragment>{children}</Fragment> ) : ( - <DefaultChildren strings={STRINGS} icons={ICONS} /> + <DefaultChildren strings={STRINGS} icons={ICONS} extra={extra} /> ); }; From bc7291fa1a542f48ff2e7f8779a27e2dc863919c Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 5 Oct 2022 10:21:21 +0330 Subject: [PATCH 090/132] change for admin apps breadcrumb --- web/src/containers/Admin/Apps/index.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/web/src/containers/Admin/Apps/index.js b/web/src/containers/Admin/Apps/index.js index 588f4765f5..f77704ebf3 100644 --- a/web/src/containers/Admin/Apps/index.js +++ b/web/src/containers/Admin/Apps/index.js @@ -64,6 +64,17 @@ const Index = ({ router, apps }) => { const configure = (app) => router.push(`/admin/plugins?plugin=${app}`); const goToPlugins = () => router.push('/admin/plugins'); + const renderBreadcrumb = () => { + return ( + <Breadcrumb> + <Item> + <span onClick={back}>Apps</span> + </Item> + <Item>{app}</Item> + </Breadcrumb> + ); + }; + useEffect(() => { setId(generateDynamicTarget(app, 'app', 'admin')); }, [app]); @@ -71,15 +82,11 @@ const Index = ({ router, apps }) => { return ( <div className="app_container-content admin-user-container admin-user-content apps-list-container"> {app ? ( - <Fragment> - <Breadcrumb> - <Item> - <span onClick={back}>Apps</span> - </Item> - <Item>{app}</Item> - </Breadcrumb> - <SmartTarget id={id} onBack={back} /> - </Fragment> + <SmartTarget + id={id} + onBack={back} + extra={{ top: renderBreadcrumb() }} + /> ) : ( <Fragment> <div className="d-flex align-items-center"> From 216ef19cbf47e1dd358c307a9d4d81806135b878 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Wed, 5 Oct 2022 10:53:17 +0330 Subject: [PATCH 091/132] change for the admin sidebar --- web/src/containers/Admin/AppWrapper/index.js | 23 ++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/web/src/containers/Admin/AppWrapper/index.js b/web/src/containers/Admin/AppWrapper/index.js index 5fb57807e5..d2504b1700 100644 --- a/web/src/containers/Admin/AppWrapper/index.js +++ b/web/src/containers/Admin/AppWrapper/index.js @@ -570,6 +570,18 @@ class AppWrapper extends React.Component { isConfigure, } = this.state; let pathNames = PATHS; + + if (features.apps) { + pathNames = [ + ...pathNames, + { + path: '/admin/apps', + label: 'Apps', + routeKey: 'apps', + }, + ]; + } + myPlugins.forEach((data) => { if (data.enabled && data.enabled_admin_view) { pathNames = [ @@ -583,17 +595,6 @@ class AppWrapper extends React.Component { } }); - if (features.apps) { - pathNames = [ - ...pathNames, - { - path: '/admin/apps', - label: 'Apps', - routeKey: 'apps', - }, - ]; - } - if (!isLoaded) return null; if (!isLoggedIn()) { router.replace('/login'); From ffc1ac5c28ae767a4ff6ab599dc8f41fe4e0cbf7 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 5 Oct 2022 17:51:52 +0900 Subject: [PATCH 092/132] new mail templates added --- server/mail/strings/ar.json | 12 ++ server/mail/strings/de.json | 12 ++ server/mail/strings/en.json | 221 ++++++++++++++++++------------------ server/mail/strings/es.json | 12 ++ server/mail/strings/fa.json | 12 ++ server/mail/strings/fr.json | 12 ++ server/mail/strings/id.json | 12 ++ server/mail/strings/ja.json | 12 ++ server/mail/strings/ko.json | 12 ++ server/mail/strings/pt.json | 12 ++ server/mail/strings/tr.json | 14 ++- server/mail/strings/vi.json | 12 ++ server/mail/strings/zh.json | 12 ++ 13 files changed, 255 insertions(+), 112 deletions(-) diff --git a/server/mail/strings/ar.json b/server/mail/strings/ar.json index 4b82bb5e96..8f04464b64 100644 --- a/server/mail/strings/ar.json +++ b/server/mail/strings/ar.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> ${name}العزيز </p><p>تمت معالجة طلب سحبك بمبلغ ${amount} ${currency} و نقله إلى عنوان.${address}. </p><p>المبلغ: ${amount} ${currency}<br />الرسوم: ${fee} ${currency}<br />الحالة: ${status}<br />العنوان: ${address}<br />معرّف المعاملة: ${transaction_id}<br /><span id='network'>شبكة الاتصال: ${network}</span><br /><ul>${explorers}</ul><p> تحية<br> فريق${api_name} </p></div>", "title": "${currency} سحب completed" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/de.json b/server/mail/strings/de.json index 3aaaeacd05..e477371866 100644 --- a/server/mail/strings/de.json +++ b/server/mail/strings/de.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> Sehr geehrte/r ${name} </p><p>Ihr Auszahlungsantrag für ${amount} ${currency} wurde bearbeitet.</p><p>Betrag: ${amount} ${currency}<br />Gebühr: ${fee} ${currency}<br />Status: ${status}<br />Adresse: ${address}<br />Transaktionsnummer: ${transaction_id}<br /><span id='network'>Netzwerk: ${network}</span><br /><ul>${explorers}</ul><p> Mit freundlichen Grüßen<br> ${api_name} team </p></div>", "title": "${currency} Abbuchung completed" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/en.json b/server/mail/strings/en.json index f788612601..42c307b902 100644 --- a/server/mail/strings/en.json +++ b/server/mail/strings/en.json @@ -1,113 +1,112 @@ - - { - "en": { - "DOC_REJECTED": { - "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", - "title": "KYC Documents Rejected" - }, - "DOC_VERIFIED": { - "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", - "title": "KYC Documents Approved" - }, - "CONFIRM_EMAIL": { - "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", - "title": "Security Verification" - }, - "LOGIN": { - "html": "<div> <p> Dear ${name} </p> <p> We have recorded a login to your account with the following details </p> <div> <div>Time: ${time}</div> <div>Country: ${country}</div> <div>IP Address: ${ip}</div> </div> <p> If this was not you, please change your password, set up two-factor authentication, and contact us immediately. </p> <p> Regards<br> ${api_name} team </p> </div>", - "title": "Login" - }, - "SIGNUP": { - "html": "<div> <p> Dear ${name} </p> <p> You need to confirm your email account by clicking the button below.<br> If you have any questions feel free to contact us simply by replying to this email.</p><p>Please click on the button below to proceed with your registration.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Confirm</Button></a></div><p> Regards<br> ${api_name} team </p></div>", - "title": "Sign Up" - }, - "WELCOME": { - "html": "<div><p> Dear ${name} </p><p>Thank you for signing up to ${api_name}.</p><p>To begin trading, you must first deposit cryptocurrency or fund money to your account.Please go to your <a href=\"${link_account}\" target=\"_blank\">account</a> and visit the <a href=\"${link_deposit}\" target=\"_blank\">deposit</a> page.,</p><p>If you have any questions or concerns, please contact us simply by replying to this email.</p><p> Regards<br> ${api_name} team </p></div>", - "title": "Welcome" - }, - "RESET_PASSWORD": { - "html": "<div><p> Dear ${name} </p><p>You have made a request to reset the password for your account.<br />To update your password, click on the link below.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Reset My Password</Button></a></div><p>If this request was made in error, it is safe to ignore it; no changes will be made to your account.</p><p>Request initiated from: ${ip}</p><p> Regards<br> ${api_name} team </p></div>", - "title": "Reset Password Request" - }, - "ACCOUNT_VERIFY": { - "html": "<div><p> Dear ${name} </p><p>Congratulations. Your account is verified successfully.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Trade Now</Button></a></div><p> Regards<br> ${api_name} team </p></div>", - "title": "Account Verified" - }, - "ACCOUNT_UPGRADE": { - "html": "<div><p> Dear ${name} </p><p>Congratulations. Your account access level is upgraded to ${tier} tier. You will benefit from lower fees, higher withdrawal limits and other premium features.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Trade Now</Button></a></div><p> Regards<br> ${api_name} team </p></div>", - "title": "Account Upgraded" - }, - "DEPOSIT_CANCEL": { - "html": "<div><p> Dear ${name} </p><p>We were not able to find or process your ${currency} deposit made on ${date} for ${amount}. Thus, the transaction is rejected by our system.</p><p>If you have any further inquiries, you can reply to this email</p><p>Transaction ID: ${txid}<br />Amount: ${amount}<br />Status: Rejected</p><p> Regards<br> ${api_name} team </p></div>", - "title": "${currency} Deposit rejected" - }, - "DEPOSIT_PENDING": { - "html": "<div><div><p> Dear ${name} </p><p> You have a new deposit for ${amount} ${currency} pending in your ${api_name} wallet. Please wait until the transaction is confirmed and your funds will be available in your wallet.</p><p>Amount: ${amount} ${currency}<br />Status: ${status}<br />Address: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Network: ${network}</span><br />Fee: ${fee} ${fee_coin}<br /><ul>${explorers}</ul></div><p> Regards<br> ${api_name} team </p></div>", - "title": "${currency} Deposit pending" - }, - "DEPOSIT_COMPLETED": { - "html": "<div><div><p> Dear ${name} </p><p> Your ${currency} deposit for ${amount} ${currency} is confirmed and completed and it is available in your ${currency} wallet.</p><p>Amount: ${amount} ${currency}<br />Status: ${status}<br />Address: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Network: ${network}</span><br />Fee: ${fee} ${fee_coin}<br /><ul>${explorers}</ul></div><p> Regards<br> ${api_name} team </p></div>", - "title": "${currency} Deposit completed" - }, - "WITHDRAWAL_PENDING": { - "html": "<div><p> Dear ${name} </p><p>You made a withdrawal request for ${amount} ${currency}. Your withdrawal status is pending and will be processed shortly.</p><p>Amount: ${amount} ${currency}<br />Fee: ${fee} ${fee_coin}<br />Status: ${status}<br />Address: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Network: ${network}</span><br /><ul>${explorers}</ul><p> Regards<br> ${api_name} team </p></div>", - "title": "${currency} withdrawal pending" - }, - "WITHDRAWAL_COMPLETED": { - "html": "<div><p> Dear ${name} </p><p>Your withdrawal request for ${amount} ${currency} is processed.</p><p>Amount: ${amount} ${currency}<br />Fee: ${fee} ${fee_coin}<br />Status: ${status}<br />Address: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Network: ${network}</span><br /><ul>${explorers}</ul><p> Regards<br> ${api_name} team </p></div>", - "title": "${currency} withdrawal completed" - }, - "WITHDRAWAL_CANCEL": { - "html": "<div><p> Dear ${name} </p><p>We were not able to find or process your ${currency} withdrawal made on ${date} for ${amount}. Thus the transaction is rejected by our system and your pending withdrawal amount is credited back to your ${api_name} wallet.</p><p>If you have any further inquiries, you can reply to this email</p><p>Transaction ID: ${txid}<br />Amount: ${amount}<br />Status: Rejected</p><p> Regards<br> ${api_name} team </p></div>", - "title": "${currency} Withdrawal rejected" - }, - "WITHDRAWAL_REQUEST": { - "html": "<div><p> Dear ${name} </p><p>You have made a ${currency} withdrawal request of ${amount} to ${address}<br /><br />Amount: ${amount} ${currency}<br />Fee: ${fee} ${fee_coin}<br />Address: ${address}<br /><span id=\"network\">Network: ${network}</span><br /><br />In order to confirm this withdrawal, please click the button below.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Confirm</Button></a></div><p>If this request was made in error, it is safe to ignore it; no changes will be made to your account.</p><p>Request initiated from: ${ip}</p><p> Regards<br> ${api_name} team </p></div>", - "title": "${currency} Withdrawal Request" - }, - "USER_VERIFICATION": { - "html": "<div><h3>User Verification Required</h3><div>User \"${email}\" uploaded his documents for verification. Please verify his documents.</div></div>", - "title": "User Verification" - }, - "SUSPICIOUS_DEPOSIT": { - "html": "<div><h3>Suspicious Deposit</h3><div>The client with email ${email} has received a ${currency} deposit that is suspicious.<br />Transaction ID: ${txid}<h4>Transaction data:</h4><div>${data}</div></div></div>", - "title": "Suspicious Deposit" - }, - "INVALID_ADDRESS": { - "html": "<div><p> Dear ${name} </p><p>Your ${currency} withdrawal for ${amount} was being sent to an invalid address and is rejected.</p><p>Address: ${address}</p><p> Regards<br> ${api_name} team </p></div>", - "title": "Invalid Withdrawal Address" - }, - "USER_DEACTIVATED": { - "html": "<div><p>Your account ${email} has been deactivated. You will not be able to use your account until it is activated by the exchange admin.</p><p> Regards<br> ${api_name} team </p></div>", - "title": "Account ${type}" - }, - "USER_ACTIVATED": { - "html": "<div><p>Your account ${email} has been activated. You are now able to use your account.</p><p> Regards<br> ${api_name} team </p></div>", - "title": "Account ${type}" - }, - "DISCOUNT_UPDATE": { - "html": "<div><p> Dear ${name} </p><p>Your discount rate has been changed to ${rate}%. This rate will be applied to your order fees.</p><p> Regards<br> ${api_name} team </p></div>", - "title": "Discount Rate Change" - }, - "BANK_VERIFIED": { - "html": "<div><p> Dear ${name} </p><p>A pending bank account has been verified. Your valid account can now be used for exchange operations requiring a bank account.</p><div><strong>Verified Bank Accounts:</strong>${list_detail_bank_account}</div><p><a href=\"${link_verification}\">To view your current bank accounts, please visit the exchange verification Tab</a></p><p> Regards<br> ${api_name} team </p></div>", - "title": "Bank Verified" - }, - "USER_ID_VERIFICATION_REJECT": { - "html": "<div><p> Dear ${name} </p><p>Your recent ID verification is processed and is unfortunately rejected. For further actions read the message from our expert below:</p><p>Message: ${message}</p><p> Regards<br> ${api_name} team </p></div>", - "title": "ID Verification Rejected" - }, - "USER_BANK_VERIFICATION_REJECT": { - "html": "<div><p> Dear ${name} </p><p>Your new bank registration is processed and is unfortunately rejected. For further actions read the message from our expert below:</p><p>Message: ${message}</p><p> Regards<br> ${api_name} team </p></div>", - "title": "New Bank Application Rejected" - }, - "PASSWORD_CHANGED": { - "html": "<div><p> Dear ${name} </p><p>This email confirms that you recently changed the password for your account. No further action is required.<br />If you did not authorize this change please contact us immediately.<br /></p><p> Regards<br> ${api_name} team </p></div>", - "title": "Password Changed" - }, - "CHANGE_PASSWORD": { - "html": "<div><p> Dear ${name} </p><p>You have made a request to change the password for your account.<br />To confirm your password changed, click on the link below.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Confirm Change My Password</Button></a></div><p>If this request was made in error, it is safe to ignore it; no changes will be made to your account.</p><p>Request initiated from: ${ip}</p><p> Regards<br> ${api_name} team </p></div>", - "title": "Change Password Confirmation" - } +{ + "en": { + "LOGIN": { + "html": "<div> <p> Dear ${name} </p> <p> We have recorded a login to your account with the following details </p> <div> <div>Time: ${time}</div> <div>Country: ${country}</div> <div>IP Address: ${ip}</div> </div> <p> If this was not you, please change your password, set up two-factor authentication, and contact us immediately. </p> <p> Regards<br> ${api_name} team </p> </div>", + "title": "Login" + }, + "SIGNUP": { + "html": "<div> <p> Dear ${name} </p> <p> You need to confirm your email account by clicking the button below.<br> If you have any questions feel free to contact us simply by replying to this email.</p><p>Please click on the button below to proceed with your registration.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Confirm</Button></a></div><p> Regards<br> ${api_name} team </p></div>", + "title": "Sign Up" + }, + "WELCOME": { + "html": "<div><p> Dear ${name} </p><p>Thank you for signing up to ${api_name}.</p><p>To begin trading, you must first deposit cryptocurrency or fund money to your account.Please go to your <a href=\"${link_account}\" target=\"_blank\">account</a> and visit the <a href=\"${link_deposit}\" target=\"_blank\">deposit</a> page.,</p><p>If you have any questions or concerns, please contact us simply by replying to this email.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Welcome" + }, + "RESET_PASSWORD": { + "html": "<div><p> Dear ${name} </p><p>You have made a request to reset the password for your account.<br />To update your password, click on the link below.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Reset My Password</Button></a></div><p>If this request was made in error, it is safe to ignore it; no changes will be made to your account.</p><p>Request initiated from: ${ip}</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Reset Password Request" + }, + "ACCOUNT_VERIFY": { + "html": "<div><p> Dear ${name} </p><p>Congratulations. Your account is verified successfully.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Trade Now</Button></a></div><p> Regards<br> ${api_name} team </p></div>", + "title": "Account Verified" + }, + "ACCOUNT_UPGRADE": { + "html": "<div><p> Dear ${name} </p><p>Congratulations. Your account access level is upgraded to ${tier} tier. You will benefit from lower fees, higher withdrawal limits and other premium features.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Trade Now</Button></a></div><p> Regards<br> ${api_name} team </p></div>", + "title": "Account Upgraded" + }, + "DEPOSIT_CANCEL": { + "html": "<div><p> Dear ${name} </p><p>We were not able to find or process your ${currency} deposit made on ${date} for ${amount}. Thus, the transaction is rejected by our system.</p><p>If you have any further inquiries, you can reply to this email</p><p>Transaction ID: ${txid}<br />Amount: ${amount}<br />Status: Rejected</p><p> Regards<br> ${api_name} team </p></div>", + "title": "${currency} Deposit rejected" + }, + "DEPOSIT_PENDING": { + "html": "<div><div><p> Dear ${name} </p><p> You have a new deposit for ${amount} ${currency} pending in your ${api_name} wallet. Please wait until the transaction is confirmed and your funds will be available in your wallet.</p><p>Amount: ${amount} ${currency}<br />Status: ${status}<br />Address: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Network: ${network}</span><br />Fee: ${fee} ${fee_coin}<br /><ul>${explorers}</ul></div><p> Regards<br> ${api_name} team </p></div>", + "title": "${currency} Deposit pending" + }, + "DEPOSIT_COMPLETED": { + "html": "<div><div><p> Dear ${name} </p><p> Your ${currency} deposit for ${amount} ${currency} is confirmed and completed and it is available in your ${currency} wallet.</p><p>Amount: ${amount} ${currency}<br />Status: ${status}<br />Address: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Network: ${network}</span><br />Fee: ${fee} ${fee_coin}<br /><ul>${explorers}</ul></div><p> Regards<br> ${api_name} team </p></div>", + "title": "${currency} Deposit completed" + }, + "WITHDRAWAL_PENDING": { + "html": "<div><p> Dear ${name} </p><p>You made a withdrawal request for ${amount} ${currency}. Your withdrawal status is pending and will be processed shortly.</p><p>Amount: ${amount} ${currency}<br />Fee: ${fee} ${fee_coin}<br />Status: ${status}<br />Address: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Network: ${network}</span><br /><ul>${explorers}</ul><p> Regards<br> ${api_name} team </p></div>", + "title": "${currency} withdrawal pending" + }, + "WITHDRAWAL_COMPLETED": { + "html": "<div><p> Dear ${name} </p><p>Your withdrawal request for ${amount} ${currency} is processed.</p><p>Amount: ${amount} ${currency}<br />Fee: ${fee} ${fee_coin}<br />Status: ${status}<br />Address: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Network: ${network}</span><br /><ul>${explorers}</ul><p> Regards<br> ${api_name} team </p></div>", + "title": "${currency} withdrawal completed" + }, + "WITHDRAWAL_CANCEL": { + "html": "<div><p> Dear ${name} </p><p>We were not able to find or process your ${currency} withdrawal made on ${date} for ${amount}. Thus the transaction is rejected by our system and your pending withdrawal amount is credited back to your ${api_name} wallet.</p><p>If you have any further inquiries, you can reply to this email</p><p>Transaction ID: ${txid}<br />Amount: ${amount}<br />Status: Rejected</p><p> Regards<br> ${api_name} team </p></div>", + "title": "${currency} Withdrawal rejected" + }, + "WITHDRAWAL_REQUEST": { + "html": "<div><p> Dear ${name} </p><p>You have made a ${currency} withdrawal request of ${amount} to ${address}<br /><br />Amount: ${amount} ${currency}<br />Fee: ${fee} ${fee_coin}<br />Address: ${address}<br /><span id=\"network\">Network: ${network}</span><br /><br />In order to confirm this withdrawal, please click the button below.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Confirm</Button></a></div><p>If this request was made in error, it is safe to ignore it; no changes will be made to your account.</p><p>Request initiated from: ${ip}</p><p> Regards<br> ${api_name} team </p></div>", + "title": "${currency} Withdrawal Request" + }, + "USER_VERIFICATION": { + "html": "<div><h3>User Verification Required</h3><div>User \"${email}\" uploaded his documents for verification. Please verify his documents.</div></div>", + "title": "User Verification" + }, + "SUSPICIOUS_DEPOSIT": { + "html": "<div><h3>Suspicious Deposit</h3><div>The client with email ${email} has received a ${currency} deposit that is suspicious.<br />Transaction ID: ${txid}<h4>Transaction data:</h4><div>${data}</div></div></div>", + "title": "Suspicious Deposit" + }, + "INVALID_ADDRESS": { + "html": "<div><p> Dear ${name} </p><p>Your ${currency} withdrawal for ${amount} was being sent to an invalid address and is rejected.</p><p>Address: ${address}</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Invalid Withdrawal Address" + }, + "USER_DEACTIVATED": { + "html": "<div><p>Your account ${email} has been deactivated. You will not be able to use your account until it is activated by the exchange admin.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Account ${type}" + }, + "USER_ACTIVATED": { + "html": "<div><p>Your account ${email} has been activated. You are now able to use your account.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Account ${type}" + }, + "DISCOUNT_UPDATE": { + "html": "<div><p> Dear ${name} </p><p>Your discount rate has been changed to ${rate}%. This rate will be applied to your order fees.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Discount Rate Change" + }, + "BANK_VERIFIED": { + "html": "<div><p> Dear ${name} </p><p>A pending bank account has been verified. Your valid account can now be used for exchange operations requiring a bank account.</p><div><strong>Verified Bank Accounts:</strong>${list_detail_bank_account}</div><p><a href=\"${link_verification}\">To view your current bank accounts, please visit the exchange verification Tab</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "Bank Verified" + }, + "USER_ID_VERIFICATION_REJECT": { + "html": "<div><p> Dear ${name} </p><p>Your recent ID verification is processed and is unfortunately rejected. For further actions read the message from our expert below:</p><p>Message: ${message}</p><p> Regards<br> ${api_name} team </p></div>", + "title": "ID Verification Rejected" + }, + "USER_BANK_VERIFICATION_REJECT": { + "html": "<div><p> Dear ${name} </p><p>Your new bank registration is processed and is unfortunately rejected. For further actions read the message from our expert below:</p><p>Message: ${message}</p><p> Regards<br> ${api_name} team </p></div>", + "title": "New Bank Application Rejected" + }, + "PASSWORD_CHANGED": { + "html": "<div><p> Dear ${name} </p><p>This email confirms that you recently changed the password for your account. No further action is required.<br />If you did not authorize this change please contact us immediately.<br /></p><p> Regards<br> ${api_name} team </p></div>", + "title": "Password Changed" + }, + "CHANGE_PASSWORD": { + "html": "<div><p> Dear ${name} </p><p>You have made a request to change the password for your account.<br />To confirm your password changed, click on the link below.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Confirm Change My Password</Button></a></div><p>If this request was made in error, it is safe to ignore it; no changes will be made to your account.</p><p>Request initiated from: ${ip}</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Change Password Confirmation" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } + } } \ No newline at end of file diff --git a/server/mail/strings/es.json b/server/mail/strings/es.json index d80e13fd79..6dd257b19e 100644 --- a/server/mail/strings/es.json +++ b/server/mail/strings/es.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> Estimado/ Estimada ${name} </p><p>Su solicitud de retirada de ${amount} ${currency} se está procesando.</p><p>Cantidad: ${amount} ${currency}<br />Tarifa: ${fee} ${currency}<br />Estado: ${status}<br />Dirección: ${address}<br />ID de transacciónID: ${transaction_id}<br /><span id='network'>La red: ${network}</span><br /><ul>${explorers}</ul><p> Saludos<br>Equipo de ${api_name} </p></div>", "title": "${currency} Retirada completed" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/fa.json b/server/mail/strings/fa.json index 7ce434530e..94fd088207 100644 --- a/server/mail/strings/fa.json +++ b/server/mail/strings/fa.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> کاربر عزیز${name} </p><p>درخواست برداشت شما به مبلغ ${amount} ${currency} درحال انجام و ارسال به آدرس ${address} می باشد.<br />کارمزد: ${fee} ${currency}<br />Status: ${status}<br />آدرس: ${address}<br />شماره پیگیری تراکنش: ${transaction_id}<br /><span id='network'>شبکه: ${network}</span><br /><ul>${explorers}</ul><p> Regards<br> ${api_name} team </p></div>", "title": "${currency} برداشت completed" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/fr.json b/server/mail/strings/fr.json index 7ed1a05621..8c74e4876a 100644 --- a/server/mail/strings/fr.json +++ b/server/mail/strings/fr.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> Bonjour ${name} </p><p>Votre demande de retrait de ${amount} ${currency} est traité.</p><p>Montant: ${amount} ${currency}<br />Frais: ${fee} ${currency}<br />Status: ${status}<br />Addresse: ${address}<br />Identité de la transaction: ${transaction_id}<br /><span id='network'>Réseau: ${network}</span><br /><ul>${explorers}</ul><p> Bien cordialement<br> ${api_name} team </p></div>", "title": "${currency} withdrawal completed" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/id.json b/server/mail/strings/id.json index e2524f2c6d..de1b791832 100644 --- a/server/mail/strings/id.json +++ b/server/mail/strings/id.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p>Kepada ${name} </p><p>Permintaan penarikan Anda untuk ${amount} ${currency} telah diproses dan ditransfer ke alamat ${address}.</p><p>Jumlah: ${amount} ${currency}<br />Biaya: ${fee} ${currency}<br />Status: ${status}<br />Alamat: ${address}<br />ID Transaksi: ${transaction_id}<br /><span id='network'>Jaringan: ${network}</span><br /><ul>${explorers}</ul><p> Salam<br> ${api_name} tim </p></div>", "title": "${currency} Penarikan completed" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/ja.json b/server/mail/strings/ja.json index bbaf021f02..3b8f16f39d 100644 --- a/server/mail/strings/ja.json +++ b/server/mail/strings/ja.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> ${name}様 </p><p>お客様の${amount} ${currency}出金が完了し、アドレス${address}に振り込みされました。</p><p>金額: ${amount} ${currency}<br />手数料: ${fee} ${currency}<br />取引状態: ${status}<br />アドレス: ${address}<br />取引ID: ${transaction_id}<br /><span id='network'>ネットワーク: ${network}</span><br /><ul>${explorers}</ul><p> 敬具<br> ${api_name} チーム </p></div>", "title": "${currency}の引き出しが完了しました" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/ko.json b/server/mail/strings/ko.json index 19cf001bc7..85dc719087 100644 --- a/server/mail/strings/ko.json +++ b/server/mail/strings/ko.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> ${name}님 </p><p>회원님의 ${amount} ${currency}를 출금이 완료되어 회원님의 계좌로 이체되었습니다.</p><p>금액: ${amount} ${currency}<br />수수료: ${fee} ${currency}<br />입금 상태: ${status}<br />주소: ${address}<br />거래 ID 확인: ${transaction_id}<br /><span id='network'>회로망: ${network}</span>>br /><ul>${explorers}</ul><p> 이용해 주셔서 감사합니다.<br> ${api_name} 팀 </p></div>", "title": "${currency} 출금 완료" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/pt.json b/server/mail/strings/pt.json index dcd7510e5d..cf5975dd6e 100644 --- a/server/mail/strings/pt.json +++ b/server/mail/strings/pt.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> Dear ${name} </p><p>Sua solicitação de saque de ${amount} ${currency} foi processada e transferida para o endereço ${address}.</p><p>Quantia: ${amount} ${currency}<br />Taxa: ${fee} ${currency}<br />Status: ${status}<br />Endereço: ${address}<br />ID da transação: ${transaction_id}<br /><span id='network'>Rede: ${network}</span><br /><ul>${explorers}</ul><p> Saudações<br> ${api_name} equipe </p></div>", "title": "${currency} Saque concluído" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/tr.json b/server/mail/strings/tr.json index f873aa7cd4..86f5af4026 100644 --- a/server/mail/strings/tr.json +++ b/server/mail/strings/tr.json @@ -107,6 +107,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> Sayın ${name} </p><p> ${amount} ${currency} için çekiminiz tamamlanmıştır.</p><p>Miktar: ${amount} ${currency}<br />Ücret: ${fee} ${fee_coin}<br />Durum: ${status}<br />Adres: ${address}<br />İşlem ID: ${transaction_id}<br /><span id=\"network\">Ağ: ${network}</span><br /><ul>${explorers}</ul><p> İyi dileklerimizle<br> ${api_name} takımı </p></div>", "title": "${currency} Çekimi Tamamlandı" - } + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" + } } } diff --git a/server/mail/strings/vi.json b/server/mail/strings/vi.json index 5a35b2f9fa..86746cbe90 100644 --- a/server/mail/strings/vi.json +++ b/server/mail/strings/vi.json @@ -95,6 +95,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> ${name} thân mến </p><p>Yêu cầu rút ${amount} ${currency} của quý khách đã được thực hiện và số tiền tương ứng đã được chuyển thành công tới địa chỉ ${address}.</p><p>Số lượng: ${amount} ${currency}<br />Phí: ${fee} ${currency}<br />Trạng thái: ${status}<br />Địa chỉ: ${address}<br />ID giao dịch: ${transaction_id}<br /><span id='network'>Mạng: ${network}</span><br /><ul>${explorers}</ul><p> Cảm ơn quý khách vì đã sử dụng dịch vụ của chúng tôi. <br> Đội ngũ ${api_name} </p></div>", "title": "${currency} Rút tiền hoàn thành" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file diff --git a/server/mail/strings/zh.json b/server/mail/strings/zh.json index 446c1a3684..beb23d7ce8 100644 --- a/server/mail/strings/zh.json +++ b/server/mail/strings/zh.json @@ -91,6 +91,18 @@ "WITHDRAWAL_COMPLETED": { "html": "<div><p> ${name} </p><p>你的${amount} ${currency}已向该地址${address}提款完毕。</p><p>金额: ${amount} ${currency}<br />手续费: ${fee} ${currency}<br />状态: ${status}<br />地址: ${address}<br />交易 ID: ${transaction_id}<br /><span id='network'>网络: ${network}</span><br /><ul>${explorers}</ul><p> 感谢你的使用<br> ${api_name} 团队 </p></div>", "title": "${currency} 提款完成" + }, + "DOC_REJECTED": { + "html": "<div><p>Dear ${email} </p><p>Unfortunately, your uploaded KYC documents have been rejected.<br>The reasons for your documents being rejected are listed below.<br></p><div><ul>${doc_information}</ul></div><p>If you feel these reasons are invalid, please feel free to reply to this email.<br>Otherwise, please reupload valid documents in order to verify your identity.</p><p> Regards<br> ${api_name} team </p</div>", + "title": "KYC Documents Rejected" + }, + "DOC_VERIFIED": { + "html": "<div><p>Dear ${email} </p><p>Your uploaded KYC documents have been approved.<br>You now have access to all exchange features that require identity verification.</p><ul>${doc_information}</ul><p>To view your approved documents, visit your <a href=\"${link}\" target=\"_blank\">Verification page</a></p><p> Regards<br> ${api_name} team </p></div>", + "title": "KYC Documents Approved" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Dear ${name} </p></p><p>You have made sensitive request related to your accounts security. To verify the operation you would require to use to code below to authorize this operation.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>If you did not make this request, report this immediately and proceed to change your credentials as soon as possible.</p><p> Regards<br> ${api_name} team </p></div>", + "title": "Security Verification" } } } \ No newline at end of file From 4a4fe66d7ce20ec6483cf6ff04faa275c9d629e1 Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Wed, 5 Oct 2022 16:38:56 +0530 Subject: [PATCH 093/132] Updated the Validate/Dismiss popup UI and it's functionality in Fiat module's summary tab. --- .../containers/Admin/Fiat/Summarycontent.js | 112 +++++++++++++++++- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/web/src/containers/Admin/Fiat/Summarycontent.js b/web/src/containers/Admin/Fiat/Summarycontent.js index 9f564b861b..c8420e255a 100644 --- a/web/src/containers/Admin/Fiat/Summarycontent.js +++ b/web/src/containers/Admin/Fiat/Summarycontent.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useCallback } from 'react'; import { STATIC_ICONS } from 'config/icons'; import { Link } from 'react-router'; -import { Button, Select, Spin, Table } from 'antd'; +import { Alert, Button, Modal, Select, Spin, Table } from 'antd'; // import { Icon as LegacyIcon } from '@ant-design/compatible'; // import { RightOutlined } from '@ant-design/icons'; // import moment from 'moment'; @@ -9,7 +9,7 @@ import { Button, Select, Spin, Table } from 'antd'; import { Image } from 'components'; // import IconToolTip from '../IconToolTip'; import Coins from '../Coins'; -import { requestDeposits } from '../Deposits/actions'; +import { requestDeposits, requestBurn, requestMint } from '../Deposits/actions'; import { renderContent, renderRowContent, @@ -18,6 +18,7 @@ import { } from '../Deposits/utils'; import './index.css'; +import ValidateDismiss from '../Deposits/ValidateDismiss'; const Summarycontent = ({ handleTabChange, @@ -39,7 +40,11 @@ const Summarycontent = ({ const [fiatCoins, setFiatCoins] = useState([]); const [selectedDepositAsset, setSelectedDepositAsset] = useState(''); const [selectedWithdrawalAsset, setSelectedWithdrawalAsset] = useState(''); - + const [isOpen, setIsOpen] = useState(false); + const [statusType, setStatusType] = useState(''); + const [validateData, setValidateData] = useState({}); + const [queryType, setQueryType] = useState(''); + const [error, setError] = useState(''); let onRampData = Object.values(onramp).filter((d) => Object.keys(d).length); let offRampData = Object.values(offramp).filter((d) => Object.keys(d).length); @@ -70,7 +75,7 @@ const Summarycontent = ({ if (Object.keys(queryParams).length === 0) { setQueryParams({}); } - + setQueryType(queryParams.type); requestDeposits({ ...values, ...queryParams, @@ -169,6 +174,78 @@ const Summarycontent = ({ // </Button> // ); + const onOpenModal = (validateData, statusType) => { + setIsOpen(true); + setStatusType(statusType); + setValidateData(validateData); + }; + + const onCancelModal = () => { + setIsOpen(false); + setStatusType(''); + }; + + const handleConfirm = (formValues) => { + let body = { + transaction_id: formValues.transaction_id, + updated_transaction_id: formValues.updated_transaction_id, + rejected: false, + processing: false, + waiting: false, + }; + if (formValues.description) { + body = { + ...body, + description: formValues.description, + }; + } + if (statusType === 'validate') { + body = { + ...body, + status: true, + dismissed: false, + }; + } else { + body = { + ...body, + dismissed: true, + status: false, + }; + } + if (queryType === 'deposit') { + requestMint(body) + .then((data) => { + onSuccess(queryParams, queryType); + }) + .catch((error) => { + onFailure(error); + }); + } else { + requestBurn(body) + .then((data) => { + onSuccess(queryParams, queryType); + }) + .catch((error) => { + onFailure(error); + }); + } + }; + + const onFailure = (errObj) => { + const message = errObj.data ? errObj.data.message : errObj.message; + setError(message); + onCancelModal(); + }; + + const onSuccess = (queryParams, queryType) => { + requestDeposits(queryParams, { type: queryType }); + onCancelModal(); + }; + + const onCloseErrorAlert = () => { + setError(''); + }; + const renderSelect = (type) => { return ( <Select @@ -216,7 +293,7 @@ const Summarycontent = ({ { title: 'Amount', dataIndex: 'amount', key: 'amount' }, { title: 'Validate/dismiss', - render: (renderData) => renderContent(renderData, () => {}), + render: (renderData) => renderContent(renderData, onOpenModal), }, ]; @@ -619,6 +696,16 @@ const Summarycontent = ({ : 'fiattable1 mb-5' } > + {error && ( + <Alert + message={error} + type="error" + showIcon + onClose={onCloseErrorAlert} + closable={true} + closeText="Close" + /> + )} <Table columns={columns} dataSource={withdrawal.map((item) => { @@ -670,6 +757,21 @@ const Summarycontent = ({ </div> </div> */} </div> + <Modal + visible={isOpen} + footer={null} + onCancel={onCancelModal} + width="37rem" + > + {isOpen ? ( + <ValidateDismiss + validateData={validateData} + statusType={statusType} + onCancel={onCancelModal} + handleConfirm={handleConfirm} + /> + ) : null} + </Modal> </div> ); }; From d882e45e1c2bd54d93021e02c72cc2e9a4776391 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Thu, 6 Oct 2022 01:00:05 +0900 Subject: [PATCH 094/132] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0e4f1849a..a80af1ebac 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # HollaEx Kit [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-green.svg)](https://github.com/facebook/create-react-app/pulls) -HollaEx Kit is an open source while label crypto software suite with a range of features from exchange and trading to user management and onboarding as well as wallet system. In order to run the HollaEx Kit, you need to run the Server as the back-end and Web as your front-end user interface. HollaEx Kit runs as a stand alone node and for trading and blockchain functionalities require to connect to HollaEx Network. By default the node connects o the public HollaEx Network. +HollaEx Kit is an open source while label crypto software suite with a range of features from exchange and trading to user management and onboarding as well as wallet system. In order to run the HollaEx Kit, you need to run the Server as the back-end and Web as your front-end user interface. HollaEx Kit runs as a stand alone node and for trading and blockchain functionalities require to connect to HollaEx Network. By default the node connects to the public HollaEx Network. ## Get Started From ef06dd132c9dcd41887054af2cfc77628fcab657 Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Thu, 6 Oct 2022 22:13:27 +0300 Subject: [PATCH 095/132] Remove ^ --- server/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/package.json b/server/package.json index 9e2ca08efa..c2ccb7ca09 100644 --- a/server/package.json +++ b/server/package.json @@ -17,7 +17,7 @@ "bcryptjs": "2.4.3", "bluebird": "3.5.3", "body-parser": "1.19.0", - "ccxt": "^1.77.49", + "ccxt": "1.77.49", "chai": "4.2.0", "cors": "2.8.5", "elastic-apm-node": "3.15.0", @@ -33,7 +33,7 @@ "hollaex-network-lib": "file:utils/hollaex-network-lib", "hollaex-tools-lib": "file:utils/hollaex-tools-lib", "http": "0.0.0", - "http-proxy-middleware": "^2.0.6", + "http-proxy-middleware": "2.0.6", "install": "0.10.4", "ip-range-check": "0.2.0", "json2csv": "4.5.4", From 93b55d00de7dd7934d962caf34a0b5b321d5ee03 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Fri, 7 Oct 2022 14:06:03 +0900 Subject: [PATCH 096/132] admin query for specifying pending bank and id for user list --- server/utils/hollaex-tools-lib/tools/user.js | 34 +++++++++++++------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/server/utils/hollaex-tools-lib/tools/user.js b/server/utils/hollaex-tools-lib/tools/user.js index 528e0843db..10ea913467 100644 --- a/server/utils/hollaex-tools-lib/tools/user.js +++ b/server/utils/hollaex-tools-lib/tools/user.js @@ -448,6 +448,7 @@ const getAllUsersAdmin = (opts = { id: null, search: null, pending: null, + pending_type: null, limit: null, page: null, order_by: null, @@ -499,19 +500,30 @@ const getAllUsersAdmin = (opts = { }; } } else if (isBoolean(opts.pending) && opts.pending) { + + let pendingQuery = []; + // users that have a pending id waiting for admin to confirm + const pendingId = { + id_data: { + status: 1 + } + }; + // users that have a pending bank waiting for admin to confirm + const pendingBank = getModel('sequelize').literal('bank_account @> \'[{"status":1}]\''); + + if (opts.pending_type) { + if (opts.pending_type === 'id') { + pendingQuery.push(pendingId); + } else if (opts.pending_type === 'bank') { + pendingQuery.push(pendingBank); + } + } else { + pendingQuery = [pendingId, pendingBank]; + } + query = { where: { - $or: [ - getModel('sequelize').literal('bank_account @> \'[{"status":1}]\''), - { - id_data: { - status: 1 - } - }, - { - activated: false - } - ] + [Op.or]: pendingQuery }, attributes: [ 'id', From c34a4e51dd74929ccd6e857507daa783009e1ce1 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Fri, 7 Oct 2022 14:08:16 +0900 Subject: [PATCH 097/132] version update --- server/api/swagger/swagger.yaml | 2 +- server/package.json | 4 +--- version | 2 +- web/package.json | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/server/api/swagger/swagger.yaml b/server/api/swagger/swagger.yaml index c35eecd4eb..3723667995 100644 --- a/server/api/swagger/swagger.yaml +++ b/server/api/swagger/swagger.yaml @@ -1,6 +1,6 @@ swagger: "2.0" info: - version: "2.4.3" + version: "2.4.4" title: HollaEx Kit host: api.hollaex.com basePath: /v2 diff --git a/server/package.json b/server/package.json index c2ccb7ca09..66ca3f3647 100644 --- a/server/package.json +++ b/server/package.json @@ -1,5 +1,5 @@ { - "version": "2.4.3", + "version": "2.4.4", "private": false, "description": "HollaEx Kit", "keywords": [ @@ -10,8 +10,6 @@ "cryptocurrency", "hollaex" ], - "author": "bitHolla Inc.", - "license": "bitHolla Inc.", "main": "app.js", "dependencies": { "bcryptjs": "2.4.3", diff --git a/version b/version index 6550da6970..ab6d27898c 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.4.3 \ No newline at end of file +2.4.4 \ No newline at end of file diff --git a/web/package.json b/web/package.json index 0aebc5b911..8ff54d7b66 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "hollaex-kit", - "version": "2.4.3", + "version": "2.4.4", "private": true, "dependencies": { "@ant-design/compatible": "1.0.5", From e5a44d46aac27d0f1570e08032198040a7721598 Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Fri, 7 Oct 2022 10:58:52 +0300 Subject: [PATCH 098/132] Add changeOrigin --- server/plugins/index.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/server/plugins/index.js b/server/plugins/index.js index 4754fce675..a54cf43e30 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -109,6 +109,7 @@ checkStatus() const options = { target: defaultURL, router: customRouter, + changeOrigin: true }; app.use('/plugins', routes, createProxyMiddleware(options)); @@ -124,16 +125,7 @@ checkStatus() }); for (const plugin of plugins) { - const pluginData = { PORT: 10011 + plugin.id, plugin } - const childProcess = fork(pluginProcess); - childProcess.send(JSON.stringify(pluginData)); - const subStr = plugin.script.match(/\"\/plugins(.*?)\"/g); - - activePlugins[plugin.name] = { - process: childProcess, - port: pluginData.PORT, - endpoints: subStr || [], - }; + startPlugin(plugin); } loggerPlugin.info( From ad1127e0564c006e385fc635913ee4003727f9ed Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Fri, 7 Oct 2022 15:31:08 +0530 Subject: [PATCH 099/132] Updated admin user verification separation between id and bank tabs --- .../containers/Admin/ListUsers/ListUser.js | 6 +-- .../Admin/ListUsers/PendingUsers.js | 38 +++++++++++++++++++ web/src/containers/Admin/ListUsers/index.js | 1 + web/src/containers/Admin/User/index.js | 12 +++--- web/src/index.css | 24 ++++++------ 5 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 web/src/containers/Admin/ListUsers/PendingUsers.js diff --git a/web/src/containers/Admin/ListUsers/ListUser.js b/web/src/containers/Admin/ListUsers/ListUser.js index 8f7d248ecf..5057784cdf 100644 --- a/web/src/containers/Admin/ListUsers/ListUser.js +++ b/web/src/containers/Admin/ListUsers/ListUser.js @@ -23,16 +23,16 @@ class ListUsers extends Component { } componentWillMount() { - this.requestUsers(this.state.page, this.state.limit); + this.requestUsers(this.state.page, this.state.limit, this.props.type); } - requestUsers = (page = 1, limit = 50) => { + requestUsers = (page = 1, limit = 50, pending_type = 'id') => { this.setState({ loading: true, error: '', }); - requestUsers({ pending: true, page, limit }) + requestUsers({ pending: true, page, limit, pending_type }) .then((response) => { this.setState({ users: diff --git a/web/src/containers/Admin/ListUsers/PendingUsers.js b/web/src/containers/Admin/ListUsers/PendingUsers.js new file mode 100644 index 0000000000..11892d4b8a --- /dev/null +++ b/web/src/containers/Admin/ListUsers/PendingUsers.js @@ -0,0 +1,38 @@ +import React, { Component } from 'react'; +import { Tabs } from 'antd'; +import { ListUsers } from '.'; + +const TabPane = Tabs.TabPane; + +class PendingUsers extends Component { + render() { + return ( + <div className="app_container-content user-container"> + <Tabs> + <TabPane tab="Pending Ids" key="PendingIds"> + <div className="m-top"> + <ListUsers + requestUser={this.props.requestUserData} + handleDownload={this.props.requestUsersDownload} + columns={this.props.columns} + type="id" + /> + </div> + </TabPane> + <TabPane tab="Pending Banks" key="Pendingbanks"> + <div className="m-top"> + <ListUsers + requestUser={this.props.requestUserData} + handleDownload={this.props.handleDownload} + columns={this.props.columns} + type="bank" + /> + </div> + </TabPane> + </Tabs> + </div> + ); + } +} + +export default PendingUsers; diff --git a/web/src/containers/Admin/ListUsers/index.js b/web/src/containers/Admin/ListUsers/index.js index d3684e3c1e..e60700c510 100644 --- a/web/src/containers/Admin/ListUsers/index.js +++ b/web/src/containers/Admin/ListUsers/index.js @@ -1,2 +1,3 @@ export { default as FullListUsers } from './FullList'; export { default as ListUsers } from './ListUser'; +export { default as PendingUsers } from './PendingUsers'; diff --git a/web/src/containers/Admin/User/index.js b/web/src/containers/Admin/User/index.js index a5375078f4..108e6ec893 100644 --- a/web/src/containers/Admin/User/index.js +++ b/web/src/containers/Admin/User/index.js @@ -16,7 +16,7 @@ import { AdminHocForm } from '../../../components'; import { requestUser, requestUsersDownload } from './actions'; import UserContent from './UserContent'; -import { ListUsers, FullListUsers } from '../ListUsers'; +import { FullListUsers, PendingUsers } from '../ListUsers'; import { requestMyPlugins } from '../Plugins/action'; // import { isSupport } from '../../../utils/token'; @@ -324,11 +324,11 @@ class App extends Component { ) : null} </TabPane> - <TabPane tab="User Verification" key="userVerification"> - <div className="list_users"> - <ListUsers - requestUser={this.requestUserData} - handleDownload={this.requestUsersDownload} + <TabPane tab="Pending Users" key="pendingUsers"> + <div className="m-top"> + <PendingUsers + requestUserData={this.requestUserData} + requestUsersDownload={this.requestUsersDownload} columns={COLUMNS} /> </div> diff --git a/web/src/index.css b/web/src/index.css index a4de90f08d..1e60bbb662 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -389,8 +389,8 @@ table th { height: auto; flex: 1; } .app_container.layout-mobile .app_container-content { - min-height: calc( 100vh - 10rem); - max-height: calc( 100vh - 10rem); + min-height: calc( 100vh - 10rem); + max-height: calc( 100vh - 10rem); max-width: 100vw; overflow-y: scroll; } .app_container.layout-mobile .app_container-content.chart-embed { @@ -408,8 +408,8 @@ table th { .app_container.layout-mobile .app_container-content .app_container-main.no_bottom_navigation { height: 100%; } .app_container.layout-mobile .content-with-bar { - min-height: calc( 100vh - 17rem); - max-height: calc( 100vh - 17rem); + min-height: calc( 100vh - 17rem); + max-height: calc( 100vh - 17rem); max-width: 100vw; padding: 1rem; margin: 0; @@ -3298,8 +3298,8 @@ table th { .layout-mobile .quote-container { background-color: transparent; overflow-y: scroll; - min-height: calc( 100vh - 17rem); - max-height: calc( 100vh - 17rem); } + min-height: calc( 100vh - 17rem); + max-height: calc( 100vh - 17rem); } .layout-mobile .quote-container .quick_trade-wrapper { flex: 1 1; min-width: 95vw !important; @@ -3453,7 +3453,7 @@ table th { .layout-mobile .presentation_container.verification_container { padding-top: 0 !important; - max-height: calc( 100vh - 10rem); } + max-height: calc( 100vh - 10rem); } .layout-mobile .presentation_container.verification_container .header-content { font-size: 13px !important; } @@ -5644,7 +5644,7 @@ table th { padding: 0.3rem 0; } .modal-market-menu .app-bar-add-tab-content .scroll-view { overflow-y: auto; - height: calc( 100vh - 19rem - 8rem); } + height: calc( 100vh - 19rem - 8rem); } .modal-market-menu .app-bar-tab-overflow-content { background-color: var(--base_background); color: var(--labels_secondary-inactive-label-text-graphics); @@ -8398,14 +8398,14 @@ table th { right: 0 !important; min-width: 100vw; max-width: 100vw; - min-height: calc( 100vh - 4rem); - max-height: calc( 100vh - 4rem); + min-height: calc( 100vh - 4rem); + max-height: calc( 100vh - 4rem); border-radius: 0 !important; padding: 0 !important; } .layout-mobile .ReactModal__Content .dialog-mobile-content { padding: 2.5rem !important; - min-height: calc( 100vh - 8rem); - max-height: calc( 100vh - 8rem); + min-height: calc( 100vh - 8rem); + max-height: calc( 100vh - 8rem); display: flex; } .layout-mobile.LogoutModal .ReactModal__Content { From 7c624c17014e55569feb728a9e7e28e988d03c2d Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Fri, 7 Oct 2022 22:44:43 +0900 Subject: [PATCH 100/132] pending_type for user list endpoint --- server/api/controllers/admin.js | 3 ++- server/api/swagger/swagger.yaml | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/server/api/controllers/admin.js b/server/api/controllers/admin.js index e4632a7e02..e5fc73512f 100644 --- a/server/api/controllers/admin.js +++ b/server/api/controllers/admin.js @@ -111,7 +111,7 @@ const putAdminKit = (req, res) => { const getUsersAdmin = (req, res) => { loggerAdmin.verbose(req.uuid, 'controllers/admin/getUsers/auth', req.auth); - const { id, search, pending, limit, page, order_by, order, start_date, end_date, format } = req.swagger.params; + const { id, search, pending, pending_type, limit, page, order_by, order, start_date, end_date, format } = req.swagger.params; if (order_by.value && typeof order_by.value !== 'string') { loggerAdmin.error( @@ -126,6 +126,7 @@ const getUsersAdmin = (req, res) => { id: id.value, search: search.value, pending: pending.value, + pending_type: pending_type.value, limit: limit.value, page: page.value, order_by: order_by.value, diff --git a/server/api/swagger/swagger.yaml b/server/api/swagger/swagger.yaml index 3723667995..e95967e5f2 100644 --- a/server/api/swagger/swagger.yaml +++ b/server/api/swagger/swagger.yaml @@ -3061,6 +3061,11 @@ paths: in: query required: false type: boolean + - name: pending_type + in: query + required: false + type: string + enum: ['id', 'bank'] - in: query name: limit description: "Number of elements to return. Default: 50. Maximun: 100" From affa54b1a2c5bcb1986344f98c627e8ea4029ff1 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Sat, 8 Oct 2022 02:47:21 +0900 Subject: [PATCH 101/132] getAllTradesNetwork bug fix for cases where maker or taker is null --- server/utils/hollaex-tools-lib/tools/order.js | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/server/utils/hollaex-tools-lib/tools/order.js b/server/utils/hollaex-tools-lib/tools/order.js index 170bc7020e..c51f9870a2 100644 --- a/server/utils/hollaex-tools-lib/tools/order.js +++ b/server/utils/hollaex-tools-lib/tools/order.js @@ -283,7 +283,7 @@ const cancelAllUserOrdersByKitId = async (userKitId, symbol, opts = { } else if (!idDictionary[userKitId]) { throw new Error(USER_NOT_REGISTERED_ON_NETWORK); } - return getNodeLib().cancelAllOrders(idDictionary[userKitId], {symbol, ...opts}); + return getNodeLib().cancelAllOrders(idDictionary[userKitId], { symbol, ...opts }); }; const cancelAllUserOrdersByEmail = (email, symbol, opts = { @@ -339,22 +339,28 @@ const getAllTradesNetwork = (symbol, limit, page, orderBy, order, startDate, end .then(async (trades) => { if (trades.data.length > 0) { const networkIds = []; - for (const trade of trades.data) { - networkIds.push(trade.maker_id, trade.taker_id); + if (trade.maker_id) { + networkIds.push(trade.maker_id); + } + if (trade.taker_id) { + networkIds.push(trade.taker_id); + } } const idDictionary = await mapNetworkIdToKitId(networkIds); for (let trade of trades.data) { - const maker_kit_id = idDictionary[trade.maker_id] || 0; - const taker_kit_id = idDictionary[trade.taker_id] || 0; - - trade.maker_network_id = trade.maker_id; - trade.maker_id = maker_kit_id; - - trade.taker_network_id = trade.taker_id; - trade.taker_id = taker_kit_id; + if (trade.maker_id) { + const maker_kit_id = idDictionary[trade.maker_id] || 0; + trade.maker_network_id = trade.maker_id; + trade.maker_id = maker_kit_id; + } + if (trade.taker_id) { + const taker_kit_id = idDictionary[trade.taker_id] || 0; + trade.taker_network_id = trade.taker_id; + trade.taker_id = taker_kit_id; + } } } From d3d6c1d23d61219e2d2152a54331b2ef4390a310 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Sun, 9 Oct 2022 12:20:02 +0900 Subject: [PATCH 102/132] revert plugin --- server/plugins/controllers.js | 25 ++-- server/plugins/index.js | 206 +++++++++++++++++++++++++------ server/plugins/plugin-process.js | 191 ---------------------------- 3 files changed, 183 insertions(+), 239 deletions(-) delete mode 100644 server/plugins/plugin-process.js diff --git a/server/plugins/controllers.js b/server/plugins/controllers.js index b56ba5200c..3989d1cf2b 100644 --- a/server/plugins/controllers.js +++ b/server/plugins/controllers.js @@ -124,8 +124,10 @@ const deletePlugin = async (req, res) => { 'restarting plugin process' ); - const { stopPlugin } = require('./index') + const { stopPlugin } = require('./index'); + stopPlugin(plugin); + // process.exit(); } } catch (err) { loggerPlugin.error( @@ -282,8 +284,10 @@ const postPlugin = async (req, res) => { ); if (plugin.enabled && plugin.script) { - const { startPlugin } = require('./index') + const { startPlugin } = require('./index'); + startPlugin(plugin); + // process.exit(); } } catch (err) { loggerPlugin.error( @@ -466,9 +470,7 @@ const putPlugin = async (req, res) => { ); if (updatedPlugin.enabled && updatedPlugin.script) { - const { stopPlugin, startPlugin } = require('./index') - stopPlugin(plugin); - startPlugin(updatedPlugin.dataValues); + process.exit(); } } catch (err) { loggerPlugin.error( @@ -608,9 +610,7 @@ const putPluginConfig = async (req, res) => { ); if (plugin.enabled && plugin.script) { - const { stopPlugin, startPlugin } = require('./index') - stopPlugin(plugin); - startPlugin(updatedPlugin.dataValues); + process.exit(); } } catch (err) { loggerPlugin.error( @@ -715,7 +715,9 @@ const disablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { - const { stopPlugin } = require('./index') + // process.exit(); + const { stopPlugin } = require('./index'); + stopPlugin(plugin); } } catch (err) { @@ -771,7 +773,10 @@ const enablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { - const { startPlugin } = require('./index') + //process.exit(); + + const { startPlugin } = require('./index'); + startPlugin(plugin); } } catch (err) { diff --git a/server/plugins/index.js b/server/plugins/index.js index a54cf43e30..5505e2d191 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -15,23 +15,101 @@ const fs = require('fs'); const latestVersion = require('latest-version'); const npm = require('npm-programmatic'); const sequelize = require('sequelize'); -const pluginProcess = path.join(__dirname, "./plugin-process.js"); -const fork = require('child_process').fork -const { createProxyMiddleware } = require('http-proxy-middleware'); +const _eval = require('eval'); +const toolsLib = require('hollaex-tools-lib'); +const lodash = require('lodash'); +const expressValidator = require('express-validator'); +const multer = require('multer'); +const moment = require('moment'); +const mathjs = require('mathjs'); +const bluebird = require('bluebird'); +const umzug = require('umzug'); +const rp = require('request-promise'); +const uuid = require('uuid/v4'); +const jwt = require('jsonwebtoken'); +const momentTz = require('moment-timezone'); +const json2csv = require('json2csv'); +const flat = require('flat'); +const ws = require('ws'); +const cron = require('node-cron'); +const randomString = require('random-string'); +const bcryptjs = require('bcryptjs'); +const expectCt = require('expect-ct'); +const validator = require('validator'); +const otp = require('otp'); +const geoipLite = require('geoip-lite'); +const nodemailer = require('nodemailer'); +const wsHeartbeatServer = require('ws-heartbeat/server'); +const wsHeartbeatClient = require('ws-heartbeat/client'); +const winston = require('winston'); +const elasticApmNode = require('elastic-apm-node'); +const winstonElasticsearchApm = require('winston-elasticsearch-apm'); +const tripleBeam = require('triple-beam'); +const uglifyEs = require('uglify-es'); +const bodyParser = require('body-parser'); let app; -let activePlugins = {} +let disabledPlugins = {}; +const getInstalledLibrary = async (name, version) => { + const jsonFilePath = path.resolve(__dirname, '../node_modules', name, 'package.json'); + + const fileData = fs.readFileSync(jsonFilePath); + const parsedFileData = JSON.parse(fileData); + + loggerPlugin.verbose( + 'plugins/index/getInstalledLibrary', + `${name} library found` + ); + + const checkVersion = version === 'latest' ? await latestVersion(name) : version; + + if (parsedFileData.version !== checkVersion) { + throw new Error('Version does not match'); + } + + loggerPlugin.verbose( + 'plugins/index/getInstalledLibrary', + `${name} version ${version} found` + ); + + const lib = require(name); + return lib; +}; + +const installLibrary = async (library) => { + const [name, version = 'latest'] = library.split('@'); -const stopPlugin = async (plugin) => { try { + const data = await getInstalledLibrary(name, version); + return data; + } catch (err) { loggerPlugin.verbose( - 'plugins/index/kill_plugin', - `killing plugin ${plugin.name}` + 'plugins/index/installLibrary', + `${name} version ${version} installing` ); - activePlugins[plugin.name].process.kill(); - delete activePlugins[plugin.name]; + await npm.install([`${name}@${version}`], { + cwd: path.resolve(__dirname, '../'), + save: true, + output: true + }); + + loggerPlugin.verbose( + 'plugins/index/installLibrary', + `${name} version ${version} installed` + ); + + const lib = require(name); + return lib; + } +}; + +const stopPlugin = async (plugin) => { + + try { + const subStr = plugin.script.match(/"\/plugins(.*?)"/g); + disabledPlugins[plugin.name] = subStr || []; } catch (err) { loggerPlugin.error( @@ -40,7 +118,7 @@ const stopPlugin = async (plugin) => { err.message ); } -} +}; const startPlugin = async (plugin) => { try { @@ -48,17 +126,79 @@ const startPlugin = async (plugin) => { 'plugins/index/initialization', `starting plugin ${plugin.name}` ); - const pluginData = { PORT: 10011 + plugin.id, plugin } - const childProcess = fork(pluginProcess); - childProcess.send(JSON.stringify(pluginData)); - const subStr = plugin.script.match(/\"\/plugins(.*?)\"/g); - - activePlugins[plugin.name] = { - process: childProcess, - port: pluginData.PORT, - endpoints: subStr || [], + + const context = { + configValues: { + publicMeta: plugin.public_meta, + meta: plugin.meta + }, + pluginLibraries: { + app, + loggerPlugin, + toolsLib + }, + app, + toolsLib, + lodash, + expressValidator, + loggerPlugin, + multer, + moment, + mathjs, + bluebird, + umzug, + rp, + sequelize, + uuid, + jwt, + momentTz, + json2csv, + flat, + ws, + cron, + randomString, + bcryptjs, + expectCt, + validator, + uglifyEs, + otp, + latestVersion, + geoipLite, + nodemailer, + wsHeartbeatServer, + wsHeartbeatClient, + cors, + winston, + elasticApmNode, + winstonElasticsearchApm, + tripleBeam, + bodyParser, + morgan, + meta: plugin.meta, + publicMeta: plugin.public_meta, + installedLibraries: {} }; + if (plugin.prescript && lodash.isArray(plugin.prescript.install) && !lodash.isEmpty(plugin.prescript.install)) { + loggerPlugin.verbose( + 'plugins/index/initialization', + `Installing packages for plugin ${plugin.name}` + ); + + for (const library of plugin.prescript.install) { + context.installedLibraries[library] = await installLibrary(library); + } + + loggerPlugin.verbose( + 'plugins/index/initialization', + `Plugin ${plugin.name} packages installed` + ); + } + + _eval(plugin.script, plugin.name, context, true); + + if (disabledPlugins[plugin.name]) delete disabledPlugins[plugin.name]; + loggerPlugin.verbose( 'plugins/index/initialization', @@ -71,7 +211,7 @@ const startPlugin = async (plugin) => { err.message ); } -} +}; checkStatus() @@ -92,27 +232,17 @@ checkStatus() app.use(domainMiddleware); helmetMiddleware(app); - - const defaultURL = 'http://localhost:10012'; - const customRouter = function (req) { + app.use((req, res, next) => { if (req.path.length > 1 && req.path.includes('/plugins/')) { - for (let plugin of Object.values(activePlugins)) { - if (plugin.endpoints.some(endpoint => endpoint.includes(req.path))) { - return `http://localhost:${plugin.port}`; - } + //Check if plugin is disabled + for (let endpoints of Object.values(disabledPlugins)) { + if (endpoints.some((endpoint) => endpoint.includes(req.path))) { return res.status(404).send(); } } } - return defaultURL; - - }; - - const options = { - target: defaultURL, - router: customRouter, - changeOrigin: true - }; + next(); + }); - app.use('/plugins', routes, createProxyMiddleware(options)); + app.use('/plugins', routes); const plugins = await Plugin.findAll({ where: { @@ -155,4 +285,4 @@ checkStatus() module.exports = { startPlugin, stopPlugin -} \ No newline at end of file +}; \ No newline at end of file diff --git a/server/plugins/plugin-process.js b/server/plugins/plugin-process.js deleted file mode 100644 index 13003ff612..0000000000 --- a/server/plugins/plugin-process.js +++ /dev/null @@ -1,191 +0,0 @@ -const express = require('express'); -const morgan = require('morgan'); -const morganType = process.env.NODE_ENV === 'development' ? 'dev' : 'combined'; -const { logEntryRequest, stream, loggerPlugin } = require('../config/logger'); -const cors = require('cors'); -const { domainMiddleware, helmetMiddleware } = require('../config/middleware'); -const path = require('path'); -const fs = require('fs'); -const latestVersion = require('latest-version'); -const npm = require('npm-programmatic'); -const sequelize = require('sequelize'); -const _eval = require('eval'); -const toolsLib = require('hollaex-tools-lib'); -const lodash = require('lodash'); -const expressValidator = require('express-validator'); -const multer = require('multer'); -const moment = require('moment'); -const mathjs = require('mathjs'); -const bluebird = require('bluebird'); -const umzug = require('umzug'); -const rp = require('request-promise'); -const uuid = require('uuid/v4'); -const jwt = require('jsonwebtoken'); -const momentTz = require('moment-timezone'); -const json2csv = require('json2csv'); -const flat = require('flat'); -const ws = require('ws'); -const cron = require('node-cron'); -const randomString = require('random-string'); -const bcryptjs = require('bcryptjs'); -const expectCt = require('expect-ct'); -const validator = require('validator'); -const otp = require('otp'); -const geoipLite = require('geoip-lite'); -const nodemailer = require('nodemailer'); -const wsHeartbeatServer = require('ws-heartbeat/server'); -const wsHeartbeatClient = require('ws-heartbeat/client'); -const winston = require('winston'); -const elasticApmNode = require('elastic-apm-node'); -const winstonElasticsearchApm = require('winston-elasticsearch-apm'); -const tripleBeam = require('triple-beam'); -const uglifyEs = require('uglify-es'); -const bodyParser = require('body-parser'); - - - -const getInstalledLibrary = async (name, version) => { - const jsonFilePath = path.resolve(__dirname, '../node_modules', name, 'package.json'); - - const fileData = fs.readFileSync(jsonFilePath); - const parsedFileData = JSON.parse(fileData); - - loggerPlugin.verbose( - 'plugins/index/getInstalledLibrary', - `${name} library found` - ); - - const checkVersion = version === 'latest' ? await latestVersion(name) : version; - - if (parsedFileData.version !== checkVersion) { - throw new Error('Version does not match'); - } - - loggerPlugin.verbose( - 'plugins/index/getInstalledLibrary', - `${name} version ${version} found` - ); - - const lib = require(name); - return lib; -}; - -const installLibrary = async (library) => { - const [name, version = 'latest'] = library.split('@'); - - try { - const data = await getInstalledLibrary(name, version); - return data; - } catch (err) { - loggerPlugin.verbose( - 'plugins/index/installLibrary', - `${name} version ${version} installing` - ); - - await npm.install([`${name}@${version}`], { - cwd: path.resolve(__dirname, '../'), - save: true, - output: true - }); - - loggerPlugin.verbose( - 'plugins/index/installLibrary', - `${name} version ${version} installed` - ); - - const lib = require(name); - return lib; - } -}; - - -const initPluginProcess = async ({ PORT, plugin }) => { - - const app = express(); - - app.use(morgan(morganType, { stream })); - app.listen(PORT); - app.use(cors()); - app.use(express.urlencoded({ extended: true })); - app.use(express.json()); - app.use(logEntryRequest); - app.use(domainMiddleware); - helmetMiddleware(app); - - const context = { - configValues: { - publicMeta: plugin.public_meta, - meta: plugin.meta - }, - pluginLibraries: { - app, - loggerPlugin, - toolsLib - }, - app, - toolsLib, - lodash, - expressValidator, - loggerPlugin, - multer, - moment, - mathjs, - bluebird, - umzug, - rp, - sequelize, - uuid, - jwt, - momentTz, - json2csv, - flat, - ws, - cron, - randomString, - bcryptjs, - expectCt, - validator, - uglifyEs, - otp, - latestVersion, - geoipLite, - nodemailer, - wsHeartbeatServer, - wsHeartbeatClient, - cors, - winston, - elasticApmNode, - winstonElasticsearchApm, - tripleBeam, - bodyParser, - morgan, - meta: plugin.meta, - publicMeta: plugin.public_meta, - installedLibraries: {} - }; - - if (plugin.prescript && lodash.isArray(plugin.prescript.install) && !lodash.isEmpty(plugin.prescript.install)) { - loggerPlugin.verbose( - 'plugins/index/initialization', - `Installing packages for plugin ${plugin.name}` - ); - - for (const library of plugin.prescript.install) { - context.installedLibraries[library] = await installLibrary(library); - } - - loggerPlugin.verbose( - 'plugins/index/initialization', - `Plugin ${plugin.name} packages installed` - ); - } - - _eval(plugin.script, plugin.name, context, true); - - -} - -process.on("message", function (message) { - initPluginProcess(JSON.parse(message)); - -}); From 8c7f5f662273e2643bea54d993c38003ccf50e6f Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Mon, 10 Oct 2022 16:28:03 +0300 Subject: [PATCH 103/132] Warning cleanup --- web/src/containers/Trade/components/OrderEntry.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/src/containers/Trade/components/OrderEntry.js b/web/src/containers/Trade/components/OrderEntry.js index b3cdd0d3f0..2c970440a1 100644 --- a/web/src/containers/Trade/components/OrderEntry.js +++ b/web/src/containers/Trade/components/OrderEntry.js @@ -440,14 +440,12 @@ class OrderEntry extends Component { increment_price, min_price, max_price, - coins, pair_base, pair_2, pair_base_display, pair_2_display, balance = {}, marketPrice, - pair = '', side = 'buy', } = props; From 8dbe1ac28a004ede37a05a5f16a8f587bce72569 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Mon, 10 Oct 2022 17:13:32 +0300 Subject: [PATCH 104/132] Meta mask popup fix --- web/public/assets/icons/metamask-fox-stake-not-detected.svg | 3 +++ web/src/config/icons/dark.js | 1 + web/src/config/icons/light.js | 1 + web/src/config/icons/static.js | 1 + web/src/config/lang/en.json | 1 + web/src/containers/App/App.js | 6 ++++-- 6 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 web/public/assets/icons/metamask-fox-stake-not-detected.svg diff --git a/web/public/assets/icons/metamask-fox-stake-not-detected.svg b/web/public/assets/icons/metamask-fox-stake-not-detected.svg new file mode 100644 index 0000000000..c37e3b5f92 --- /dev/null +++ b/web/public/assets/icons/metamask-fox-stake-not-detected.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="65.251" height="64.134" viewBox="0 0 65.251 64.134"> + <path id="Path_11" data-name="Path 11" d="M146.077,121.228,113.45,96.75l8.192-12.283V57.094l17.839,22.3h13.2l17.827-22.3V84.467L178.7,96.75ZM118.6,95.87l27.476,20.616L173.562,95.87l-6.833-10.255V67.906L154.5,83.174H137.657L125.434,67.906v17.71Z" transform="translate(-113.45 -57.094)" fill="gray"/> +</svg> diff --git a/web/src/config/icons/dark.js b/web/src/config/icons/dark.js index 9a1fe04be9..7a7773ea77 100644 --- a/web/src/config/icons/dark.js +++ b/web/src/config/icons/dark.js @@ -260,6 +260,7 @@ const nestedIcons = { STAKING_VARIABLE: '/assets/stake/variable_icon.svg', STAKING_UNLOCK: '/assets/stake/unlock-unstake-icon.svg', STAKING_BACKGROUND: '/assets/stake/stake-background.jpg', + META_MASK_NOT_FOUND: '/assets/icons/metamask-fox-stake-not-detected.svg', STAKING_ACCOUNT: '', METAMASK: '', MOVE_XHT: '/assets/stake/xht-move.svg', diff --git a/web/src/config/icons/light.js b/web/src/config/icons/light.js index 754d950158..b039713a45 100644 --- a/web/src/config/icons/light.js +++ b/web/src/config/icons/light.js @@ -232,6 +232,7 @@ const nestedIcons = { STAKING_SUCCESSFUL_MESSAGE: '/assets/stake/stake-unstake-light.png', STAKING_PANEL_BACKGROUND: '/assets/stake/stake-unstake-light.png', STAKING_BACKGROUND: '/assets/stake/stake-background-light.png', + META_MASK_NOT_FOUND: '/assets/icons/metamask-fox-stake-not-detected.svg', }; const icons = flatten(nestedIcons, options); diff --git a/web/src/config/icons/static.js b/web/src/config/icons/static.js index 18aa3a14af..e43376d6b9 100644 --- a/web/src/config/icons/static.js +++ b/web/src/config/icons/static.js @@ -42,6 +42,7 @@ const icons = { ADMIN_PLUGINS: '/assets/images/plugins.svg', ADMIN_TIERS: '/assets/images/tiers.svg', ADMIN_ROLES: '/assets/images/roles.svg', + META_MASK_NOT_FOUND: '/assets/icons/metamask-fox-stake-not-detected.svg', ADMIN_CUSTOMIZE: '/assets/images/customize.svg', ADMIN_MISSING_GO_BACK: '/assets/images/missing-page-go-back-to-dash.svg', HELP_DESCRIPTION_POPUP: '/assets/images/help-popup-description.png', diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index 0fccda8b04..689f216b68 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -687,6 +687,7 @@ "COMPLETED_TOOLTIP": "Stake is mature. Continue staking to earn more rewards or unstake to claim rewards.", "CONNECT_ERROR": "Please check your wallet", "INSTALL_METAMASK": "You must install Metamask into your browser: https://metamask.io/download.html", + "INSTALL_METAMASK_TITLE": "MetaMask not detected", "REWARDS": { "0": { "CARD": "Earn rewards (no bonuses)", "TEXT": "regular rewards." }, "1": { diff --git a/web/src/containers/App/App.js b/web/src/containers/App/App.js index f087b80b33..a15eb15acc 100644 --- a/web/src/containers/App/App.js +++ b/web/src/containers/App/App.js @@ -506,10 +506,12 @@ class App extends Component { case NOTIFICATIONS.METAMASK_ERROR: return ( <MessageDisplay - iconId="RED_WARNING" - iconPath={ICONS['RED_WARNING']} + iconId="META_MASK_NOT_FOUND" + iconPath={ICONS['META_MASK_NOT_FOUND']} onClick={this.onCloseDialog} text={data} + title={STRINGS['STAKE.INSTALL_METAMASK_TITLE']} + titleId={'STAKE.INSTALL_METAMASK_TITLE'} /> ); case NOTIFICATIONS.CONFIGURE_APPS: From 8846efbc80a29e919b0a068bd6aa5942493f84be Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 11 Oct 2022 17:53:55 +0330 Subject: [PATCH 105/132] change for plugins webpack alias --- plugins/webpack-dev.config.js | 8 ++++---- plugins/webpack-main.config.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/webpack-dev.config.js b/plugins/webpack-dev.config.js index a48cc1e6e6..49b4119e80 100644 --- a/plugins/webpack-dev.config.js +++ b/plugins/webpack-dev.config.js @@ -86,10 +86,10 @@ module.exports = (env) => { }, resolve: { alias: { - "components": path.resolve("./src/components"), - "utils": path.resolve("./src/utils"), - "store": path.resolve("./src/store"), - "constants": path.resolve("./src/constants") + "components": path.resolve(__dirname, "src/components"), + "utils": path.resolve(__dirname, "src/utils"), + "store": path.resolve(__dirname, "src/store"), + "constants": path.resolve(__dirname, "src/constants") } } } diff --git a/plugins/webpack-main.config.js b/plugins/webpack-main.config.js index 2465115523..9448ca9bc3 100644 --- a/plugins/webpack-main.config.js +++ b/plugins/webpack-main.config.js @@ -80,10 +80,10 @@ module.exports = (env) => { }, resolve: { alias: { - "components": path.resolve("./src/components"), - "utils": path.resolve("./src/utils"), - "store": path.resolve("./src/store"), - "constants": path.resolve("./src/constants") + "components": path.resolve(__dirname, "src/components"), + "utils": path.resolve(__dirname, "src/utils"), + "store": path.resolve(__dirname, "src/store"), + "constants": path.resolve(__dirname, "src/constants") } } } From 7f9668e74fede5df5188734bd38605d9dfcb0662 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 11 Oct 2022 17:54:56 +0330 Subject: [PATCH 106/132] change for hello-exchange plugin form and strings --- .../hello-exchange/assets/strings.json | 4 +- .../plugins/hello-exchange/views/view/Form.js | 63 +++++++++++++------ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/plugins/src/plugins/hello-exchange/assets/strings.json b/plugins/src/plugins/hello-exchange/assets/strings.json index ab9c4de4f6..3aec9ffdd3 100644 --- a/plugins/src/plugins/hello-exchange/assets/strings.json +++ b/plugins/src/plugins/hello-exchange/assets/strings.json @@ -1,6 +1,6 @@ { "en": { - "title": "New Page", - "content": "{0} content" + "title": "Hello exchange", + "hello": "Hello {0}" } } \ No newline at end of file diff --git a/plugins/src/plugins/hello-exchange/views/view/Form.js b/plugins/src/plugins/hello-exchange/views/view/Form.js index ab0498fcb5..73bd0b7c97 100644 --- a/plugins/src/plugins/hello-exchange/views/view/Form.js +++ b/plugins/src/plugins/hello-exchange/views/view/Form.js @@ -1,37 +1,62 @@ -import React from 'react'; -import { IconTitle } from 'hollaex-web-lib'; +import React, { useEffect, useState } from 'react'; +import { IconTitle, PanelInformationRow } from 'hollaex-web-lib'; import { withKit } from 'components/KitContext'; +import Title from 'components/Title'; +import axios from 'axios'; const Form = ({ strings: STRINGS, icons: ICONS, - generateId + generateId, + plugin_url: PLUGIN_URL }) => { - return ( - <div className="presentation_container apply_rtl verification_container"> - <IconTitle - stringId={generateId('title')} - text={STRINGS[generateId('title')]} - textType="title" - iconPath={ICONS['SIDEBAR_HELP']} - /> - <form className="d-flex flex-column w-100 verification_content-form-wrapper"> - <div className="verification-form-panel mt-3 mb-5"> - <div className="my-4 py-4"> - {STRINGS.formatString(STRINGS[generateId('content')], STRINGS[generateId('title')])} + const [info, setInfo] = useState(); + + useEffect(() => { + axios.get(`${PLUGIN_URL}/plugins/hello-exchange/info`).then(({ data: { exchange_info = {} }}) => { + setInfo(exchange_info); + }) + }, []) + + return ( + <div className="presentation_container apply_rtl verification_container"> + <IconTitle + stringId={generateId('title')} + text={STRINGS[generateId('title')]} + textType="title" + iconPath={ICONS['SIDEBAR_HELP']} + /> + <form className="d-flex flex-column w-100 verification_content-form-wrapper"> + <div className="verification-form-panel mt-3 mb-5"> + <div className="my-4 py-4"> + <Title /> + <div className="py-4"> + {info ? Object.entries(info).map(([key, value]) => ( + <PanelInformationRow + key={key} + label={key} + information={value} + className="title-font" + disable + /> + )) : ( + <div className="pt-4">Loading ...</div> + )} </div> </div> - </form> - </div> - ) + </div> + </form> + </div> + ) } -const mapContextToProps = ({ strings, activeLanguage, icons, generateId }) => ({ +const mapContextToProps = ({ strings, activeLanguage, icons, generateId, plugin_url }) => ({ strings, activeLanguage, icons, generateId, + plugin_url }); export default withKit(mapContextToProps)(Form); \ No newline at end of file From 6e2db324d36c6fcd0c4cd1c63bd113f223a08d0a Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 11 Oct 2022 17:55:47 +0330 Subject: [PATCH 107/132] add app type plugin template files --- plugins/src/templates/app/assets/icons.json | 3 ++ plugins/src/templates/app/assets/strings.json | 6 ++++ plugins/src/templates/app/views/admin/App.js | 20 +++++++++++ plugins/src/templates/app/views/admin/Form.js | 34 +++++++++++++++++++ .../src/templates/app/views/admin/index.js | 1 + .../src/templates/app/views/admin/view.json | 6 ++++ plugins/src/templates/app/views/kit/App.js | 20 +++++++++++ plugins/src/templates/app/views/kit/Form.js | 28 +++++++++++++++ plugins/src/templates/app/views/kit/index.js | 1 + plugins/src/templates/app/views/kit/view.json | 6 ++++ 10 files changed, 125 insertions(+) create mode 100644 plugins/src/templates/app/assets/icons.json create mode 100644 plugins/src/templates/app/assets/strings.json create mode 100644 plugins/src/templates/app/views/admin/App.js create mode 100644 plugins/src/templates/app/views/admin/Form.js create mode 100644 plugins/src/templates/app/views/admin/index.js create mode 100644 plugins/src/templates/app/views/admin/view.json create mode 100644 plugins/src/templates/app/views/kit/App.js create mode 100644 plugins/src/templates/app/views/kit/Form.js create mode 100644 plugins/src/templates/app/views/kit/index.js create mode 100644 plugins/src/templates/app/views/kit/view.json diff --git a/plugins/src/templates/app/assets/icons.json b/plugins/src/templates/app/assets/icons.json new file mode 100644 index 0000000000..3815a2bd63 --- /dev/null +++ b/plugins/src/templates/app/assets/icons.json @@ -0,0 +1,3 @@ +{ + "dark": {} +} \ No newline at end of file diff --git a/plugins/src/templates/app/assets/strings.json b/plugins/src/templates/app/assets/strings.json new file mode 100644 index 0000000000..a112fb80ad --- /dev/null +++ b/plugins/src/templates/app/assets/strings.json @@ -0,0 +1,6 @@ +{ + "en": { + "title": "App", + "content": "{0} content" + } +} \ No newline at end of file diff --git a/plugins/src/templates/app/views/admin/App.js b/plugins/src/templates/app/views/admin/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/app/views/admin/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + <Provider store={store}> + <KitContextProvider {...rest} defaultView={defaultView}> + <Form /> + </KitContextProvider> + </Provider> + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/app/views/admin/Form.js b/plugins/src/templates/app/views/admin/Form.js new file mode 100644 index 0000000000..f3e639a46c --- /dev/null +++ b/plugins/src/templates/app/views/admin/Form.js @@ -0,0 +1,34 @@ +import React, { Fragment } from 'react'; +import { Breadcrumb } from 'antd'; +import { withKit } from 'components/KitContext'; + +const { Item } = Breadcrumb; + +const Form = ({ onBack: goToApps }) => { + + const renderBreadcrumb = () => { + return ( + <Breadcrumb> + <Item> + <span onClick={goToApps}>Apps</span> + </Item> + <Item>app name</Item> + </Breadcrumb> + ); + }; + + return ( + <Fragment> + {renderBreadcrumb()} + <div className="my-4 py-4"> + Admin Content + </div> + </Fragment> + ) +} + +const mapContextToProps = ({ onBack }) => ({ + onBack, +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/app/views/admin/index.js b/plugins/src/templates/app/views/admin/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/app/views/admin/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/app/views/admin/view.json b/plugins/src/templates/app/views/admin/view.json new file mode 100644 index 0000000000..2f888a02b3 --- /dev/null +++ b/plugins/src/templates/app/views/admin/view.json @@ -0,0 +1,6 @@ +{ + "meta": { + "is_app": "true", + "type": "admin" + } +} \ No newline at end of file diff --git a/plugins/src/templates/app/views/kit/App.js b/plugins/src/templates/app/views/kit/App.js new file mode 100644 index 0000000000..3c337b271f --- /dev/null +++ b/plugins/src/templates/app/views/kit/App.js @@ -0,0 +1,20 @@ +import React, { Component } from "react"; +import { Provider } from "react-redux"; +import store from "store"; +import Form from './Form'; +import { KitContextProvider } from "components/KitContext"; + +class App extends Component { + render() { + const { children: defaultView, ...rest } = this.props; + return ( + <Provider store={store}> + <KitContextProvider {...rest} defaultView={defaultView}> + <Form /> + </KitContextProvider> + </Provider> + ); + } +}; + +export { App }; \ No newline at end of file diff --git a/plugins/src/templates/app/views/kit/Form.js b/plugins/src/templates/app/views/kit/Form.js new file mode 100644 index 0000000000..eddefb22d3 --- /dev/null +++ b/plugins/src/templates/app/views/kit/Form.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { withKit } from 'components/KitContext'; + +const Form = ({ + strings: STRINGS, + generateId +}) => { + + return ( + <div className="presentation_container apply_rtl verification_container"> + <form className="d-flex flex-column w-100 verification_content-form-wrapper"> + <div className="verification-form-panel mt-3 mb-5"> + <div className="my-4 py-4"> + {STRINGS.formatString(STRINGS[generateId('content')], STRINGS[generateId('title')])} + </div> + </div> + </form> + </div> + ) +} + +const mapContextToProps = ({ strings, activeLanguage, generateId }) => ({ + strings, + activeLanguage, + generateId, +}); + +export default withKit(mapContextToProps)(Form); \ No newline at end of file diff --git a/plugins/src/templates/app/views/kit/index.js b/plugins/src/templates/app/views/kit/index.js new file mode 100644 index 0000000000..7ea636958d --- /dev/null +++ b/plugins/src/templates/app/views/kit/index.js @@ -0,0 +1 @@ +export { App as default } from "./App"; diff --git a/plugins/src/templates/app/views/kit/view.json b/plugins/src/templates/app/views/kit/view.json new file mode 100644 index 0000000000..41eb14b374 --- /dev/null +++ b/plugins/src/templates/app/views/kit/view.json @@ -0,0 +1,6 @@ +{ + "meta": { + "is_app": "true", + "type": "kit" + } +} \ No newline at end of file From 7ca7ed5427e65bdaf0d6307bbb6a1a71c08eb58d Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 11 Oct 2022 17:56:46 +0330 Subject: [PATCH 108/132] script and templates object updates to support app type plugin --- plugins/scripts/addPlugin.js | 1 + plugins/scripts/patterns.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/plugins/scripts/addPlugin.js b/plugins/scripts/addPlugin.js index a1efe029df..2644d2bfa5 100644 --- a/plugins/scripts/addPlugin.js +++ b/plugins/scripts/addPlugin.js @@ -42,6 +42,7 @@ if (!plugin) { case TEMPLATES.KYC.type: case TEMPLATES.BANK.type: case TEMPLATES.ONRAMP.type: + case TEMPLATES.APP.type: return ( mkdirp(`${PATHS.ROOT}/${plugin}`) .then(() => { diff --git a/plugins/scripts/patterns.js b/plugins/scripts/patterns.js index 5f4562d424..ae795fb798 100644 --- a/plugins/scripts/patterns.js +++ b/plugins/scripts/patterns.js @@ -56,6 +56,10 @@ const TEMPLATES = { type: 'onramp', template: 'src/templates/onramp', }, + APP: { + type: 'app', + template: 'src/templates/app', + }, SERVER: { type: 'server', } From faf5fcfa3987baa18f18cd10555e2d5f605af0c9 Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 11 Oct 2022 17:57:29 +0330 Subject: [PATCH 109/132] app reducer refinements for app type plugins --- web/src/reducers/appReducer.js | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/web/src/reducers/appReducer.js b/web/src/reducers/appReducer.js index 28d7302d53..42b9614bd8 100644 --- a/web/src/reducers/appReducer.js +++ b/web/src/reducers/appReducer.js @@ -448,18 +448,15 @@ const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { } case SET_WEB_VIEWS: { const allWebViews = []; - payload.enabledPlugins.forEach( - ({ name, web_view = [], type: plugin_type }) => { - if (web_view && web_view.length) { - const named_web_views = web_view.map((viewObj) => ({ - ...viewObj, - name, - plugin_type, - })); - allWebViews.push(...named_web_views); - } + payload.enabledPlugins.forEach(({ name, web_view = [] }) => { + if (web_view && web_view.length) { + const named_web_views = web_view.map((viewObj) => ({ + ...viewObj, + name, + })); + allWebViews.push(...named_web_views); } - ); + }); const remoteRoutes = []; allWebViews.forEach(({ meta, name }) => { @@ -487,7 +484,7 @@ const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { const CLUSTERED_WEB_VIEWS = {}; allWebViews.forEach((plugin) => { - const { target: staticTarget, meta, name, plugin_type } = plugin; + const { target: staticTarget, meta, name } = plugin; let target; if (staticTarget) { target = staticTarget; @@ -497,11 +494,12 @@ const reducer = (state = INITIAL_STATE, { type, payload = {} }) => { is_verification_tab, is_wallet, is_ultimate_fiat, + is_app, type, currency, } = meta; - if (plugin_type === 'app') { + if (is_app) { target = generateDynamicTarget(name, 'app', type); } else if (is_page) { target = generateDynamicTarget(name, 'page'); From f96bc3dfa02f10f45f881e935d43b50f118df86a Mon Sep 17 00:00:00 2001 From: Amir Hossein Salar <ah.salar@gmail.com> Date: Tue, 11 Oct 2022 17:58:01 +0330 Subject: [PATCH 110/132] change for kit and admin apps selector logic --- web/src/containers/Admin/Apps/utils.js | 6 +++++- web/src/containers/Apps/utils.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/web/src/containers/Admin/Apps/utils.js b/web/src/containers/Admin/Apps/utils.js index 77dc4cf936..5038c8ba02 100644 --- a/web/src/containers/Admin/Apps/utils.js +++ b/web/src/containers/Admin/Apps/utils.js @@ -3,5 +3,9 @@ import { createSelector } from 'reselect'; const getPlugins = (state) => state.app.plugins; export const appsSelector = createSelector([getPlugins], (plugins) => - plugins.filter(({ type }) => type === 'app') + plugins.filter( + ({ web_view }) => + web_view && + web_view.some(({ meta: { is_app, type } }) => is_app && type === 'admin') + ) ); diff --git a/web/src/containers/Apps/utils.js b/web/src/containers/Apps/utils.js index 170fb47529..7c92a7c5ae 100644 --- a/web/src/containers/Apps/utils.js +++ b/web/src/containers/Apps/utils.js @@ -3,7 +3,11 @@ import { createSelector } from 'reselect'; const getPlugins = (state) => state.app.plugins; const getUserApps = (state) => state.user.settings.apps || []; export const appsSelector = createSelector([getPlugins], (plugins) => - plugins.filter(({ type }) => type === 'app') + plugins.filter( + ({ web_view }) => + web_view && + web_view.some(({ meta: { is_app, type } }) => is_app && type === 'kit') + ) ); export const userAppsSelector = createSelector( From ce4c5c73c8c2b5f9bea6c157cd554fc44e88e699 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Tue, 11 Oct 2022 17:47:18 +0300 Subject: [PATCH 111/132] pin input for otp --- .../components/Form/FormFields/PinInput.js | 90 +++++++++++++++++++ .../Form/FormFields/_FormFields.scss | 13 +-- .../components/Form/FormFields/_PinInput.scss | 48 ++++++++++ web/src/components/Form/factoryFields.js | 3 + web/src/components/OtpForm/_OtpForm.scss | 19 +++- web/src/components/OtpForm/index.js | 34 +++---- web/src/config/lang/en.json | 3 + 7 files changed, 180 insertions(+), 30 deletions(-) create mode 100644 web/src/components/Form/FormFields/PinInput.js create mode 100644 web/src/components/Form/FormFields/_PinInput.scss diff --git a/web/src/components/Form/FormFields/PinInput.js b/web/src/components/Form/FormFields/PinInput.js new file mode 100644 index 0000000000..4f13d6a046 --- /dev/null +++ b/web/src/components/Form/FormFields/PinInput.js @@ -0,0 +1,90 @@ +import React, { useEffect, useRef, useState } from 'react'; +import classnames from 'classnames'; + +const PinInput = ({ + input: { value = '', onChange }, + callback: { handleSubmit, error, isSubmitting }, +}) => { + const [isFocused, setIsFocused] = useState(true); + const [isError, setIsError] = useState(undefined); + const masterRef = useRef(); + + useEffect(() => { + if (value.length === 6) { + handleSubmit(); + } + }, [value, handleSubmit]); + + useEffect(() => { + if (error && masterRef?.current) { + const inputEvent = new Event('input', { bubbles: true }); + const nativeInputValueSetter = Object.getOwnPropertyDescriptor( + window.HTMLInputElement.prototype, + 'value' + ).set; + nativeInputValueSetter.call(masterRef.current, ''); + masterRef.current.dispatchEvent(inputEvent); + setIsError(error); + } + }, [error, onChange, masterRef]); + + const handleOnMasterFocus = (e) => { + var temp_value = e.target.value; + e.target.value = ''; + e.target.value = temp_value; + setIsError(false); + setIsFocused(true); + }; + + const handleOnMasterBlur = () => { + setIsFocused(false); + }; + + const handleMasterValueChange = (e) => { + if (e.target.value.length > 0) { + setIsError(undefined); + } + if (e.target.value.length <= 6 && onChange && !isSubmitting) { + onChange(e); + } + }; + + const getClassname = (index) => { + if (isError) return classnames('error'); + if (isFocused && index === value.length) return classnames('active'); + return ''; + }; + + return ( + <React.Fragment> + <div className={classnames('mainContainer')}> + <input + autoFocus + inputMode={'numeric'} + ref={masterRef} + className={classnames('masterInput')} + disabled={isSubmitting} + type="text" + value={value} + onClick={handleOnMasterFocus} + onChange={handleMasterValueChange} + onBlur={handleOnMasterBlur} + /> + <div className="group"> + <input value={value[0] || ''} className={getClassname(0)} /> + <input value={value[1] || ''} className={getClassname(1)} /> + <input value={value[2] || ''} className={getClassname(2)} /> + </div> + <div className="seperator"></div> + <div className="group"> + <input value={value[3] || ''} className={getClassname(3)} /> + <input value={value[4] || ''} className={getClassname(4)} /> + <input value={value[5] || ''} className={getClassname(5)} /> + </div> + </div> + {isError && <div className="warning_text">{isError}</div>} + </React.Fragment> + ); +}; + +export default PinInput; diff --git a/web/src/components/Form/FormFields/_FormFields.scss b/web/src/components/Form/FormFields/_FormFields.scss index 7da02f0e1f..5bf7b6fbb7 100644 --- a/web/src/components/Form/FormFields/_FormFields.scss +++ b/web/src/components/Form/FormFields/_FormFields.scss @@ -1,5 +1,6 @@ @import './DropdownField.scss'; @import './FieldWrapper.scss'; +@import './PinInput.scss'; $toggle-size: 14px; $toggle-border: 2px; @@ -107,7 +108,7 @@ $placeholder--font-weight: bold; } } .dropdown-triangle::after { - bottom: 0 !important; + bottom: 0 !important; } } } @@ -150,14 +151,14 @@ $placeholder--font-weight: bold; position: relative; top: 0 !important; } - .paper-clip-icon{ + .paper-clip-icon { .action_notification-svg { background-color: $link; width: 1.2rem; height: 1.2rem; border-radius: 50%; display: flex; - margin-left: 5px; + margin-left: 5px; svg { width: 0.8rem !important; height: 0.8rem !important; @@ -215,7 +216,7 @@ $placeholder--font-weight: bold; cursor: pointer; &.left { - opacity: 0.3; + opacity: 0.3; .option-text { position: relative; left: 23px; @@ -309,9 +310,9 @@ $placeholder--font-weight: bold; .toggle-action_button { width: $toggle-size * 3; &.left .option-text { - left: 20px; + left: 20px; } } } } -} \ No newline at end of file +} diff --git a/web/src/components/Form/FormFields/_PinInput.scss b/web/src/components/Form/FormFields/_PinInput.scss new file mode 100644 index 0000000000..f337c23da3 --- /dev/null +++ b/web/src/components/Form/FormFields/_PinInput.scss @@ -0,0 +1,48 @@ +.mainContainer { + display: flex; + width: fit-content; + margin: auto; + position: relative; + justify-content: center; + align-items: center; + input { + width: 30px; + height: 30px; + border-radius: 0.5rem; + border: 1px solid $colors-border; + background: $app-background-color; + text-align: center; + font-size: large; + } + .masterInput { + position: absolute; + width: 100%; + top: 8px; + opacity: 0 !important; + } + + .seperator { + width: 2rem; + border: 1px solid $colors-black; + background: $colors-black; + } +} + +.active { + border: 1px solid $colors-black !important; +} + +.error { + border: 1px solid red !important; +} + +.warning_text { + text-align: center; +} + +.group { + padding: 1rem; + & input { + margin: 0rem 0.25rem; + } +} diff --git a/web/src/components/Form/factoryFields.js b/web/src/components/Form/factoryFields.js index 396566e7ea..ff935fd017 100644 --- a/web/src/components/Form/factoryFields.js +++ b/web/src/components/Form/factoryFields.js @@ -11,6 +11,7 @@ import EditableInputField from './FormFields/EditableInputField'; import CaptchaField from './FormFields/Captcha'; import ToggleField from './FormFields/ToggleField'; import DumbField from './FormFields/DumbFieldForm'; +import PinInput from './FormFields/PinInput'; const renderFields = (fields = {}, callback) => { return ( @@ -62,6 +63,8 @@ const renderFields = (fields = {}, callback) => { return <Field component={TextAreaField} {...commonProps} />; case 'toggle': return <Field component={ToggleField} {...commonProps} />; + case 'pin': + return <Field component={PinInput} {...commonProps} />; case 'text': case 'password': case 'email': diff --git a/web/src/components/OtpForm/_OtpForm.scss b/web/src/components/OtpForm/_OtpForm.scss index 222612a461..967c5158d4 100644 --- a/web/src/components/OtpForm/_OtpForm.scss +++ b/web/src/components/OtpForm/_OtpForm.scss @@ -6,11 +6,22 @@ border-top: 1px solid $colors-border; margin: 1rem 0; position: relative; +} - .otp_form-title-text { - font-weight: bold; - font-size: $font-size-main; - } +.otp_form-info-text { + font-weight: bold; + font-size: $font-size-main; + width: 100%; + text-align: center !important; +} + +.otp_form-subnote-text { + font-weight: 400; + font-size: $font-size-main; + opacity: 0.7; + width: 100%; + margin-top: 5rem; + text-align: center !important; } .otp_form-fields { diff --git a/web/src/components/OtpForm/index.js b/web/src/components/OtpForm/index.js index 90f00ce742..73977c77ab 100644 --- a/web/src/components/OtpForm/index.js +++ b/web/src/components/OtpForm/index.js @@ -29,12 +29,8 @@ class Form extends Component { setFormValues = () => { const formValues = { otp_code: { - type: 'number', - stringId: - 'OTP_FORM.OTP_LABEL,OTP_FORM.OTP_PLACEHOLDER,OTP_FORM.ERROR_INVALID', + type: 'pin', label: STRINGS['OTP_FORM.OTP_LABEL'], - placeholder: STRINGS['OTP_FORM.OTP_PLACEHOLDER'], - validate: [required, validateOtp(STRINGS['OTP_FORM.ERROR_INVALID'])], fullWidth: true, }, }; @@ -51,11 +47,9 @@ class Form extends Component { render() { const { - handleSubmit, submitting, - pristine, + handleSubmit, error, - valid, onClickHelp, icons: ICONS, } = this.props; @@ -70,11 +64,6 @@ class Form extends Component { iconPath={ICONS['OTP_CODE']} /> <div className="otp_form-title-wrapper"> - <span className="otp_form-title-text"> - <EditWrapper stringId="OTP_FORM.OTP_FORM_TITLE"> - {STRINGS['OTP_FORM.OTP_FORM_TITLE']} - </EditWrapper> - </span> {onClickHelp && ( <ActionNotification stringId="NEED_HELP_TEXT" @@ -88,14 +77,19 @@ class Form extends Component { </div> <form onSubmit={handleSubmit} className="w-100" ref={this.setFormRef}> <div className="w-100 otp_form-fields"> - {renderFields(formValues)} - {error && <div className="warning_text">{error}</div>} + {renderFields(formValues, { + isSubmitting: submitting, + error, + handleSubmit, + })} + </div> + <div className="otp_form-info-text"> + {STRINGS['OTP_FORM.OTP_FORM_INFO']} + </div> + <div className="otp_form-subnote-text"> + {STRINGS['OTP_FORM.OTP_FORM_SUBNOTE_LINE_1']} + {STRINGS['OTP_FORM.OTP_FORM_SUBNOTE_LINE_2']} </div> - <EditWrapper stringId="OTP_FORM.OTP_BUTTON" /> - <Button - label={STRINGS['OTP_FORM.OTP_BUTTON']} - disabled={pristine || submitting || !valid} - /> </form> </div> ); diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index 689f216b68..2a84c369e9 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -159,6 +159,9 @@ "REFERRAL_SUCCESS": { "TITLE": "Request Sent", "BUTTON_TEXT": "Okay" }, "OTP_FORM": { "OTP_FORM_TITLE": "Enter your authentication code to continue", + "OTP_FORM_INFO": "Enter your 6-digit code to continue", + "OTP_FORM_SUBNOTE_LINE_1": "Your code is also known as a two-factor authenticator (2FA) or OTP code.", + "OTP_FORM_SUBNOTE_LINE_2": "If you've lost your code, please reach out to support.", "OTP_LABEL": "OTP Code", "OTP_PLACEHOLDER": "Enter the authentication code", "OTP_TITLE": "Authenticator Code", From 00db4b0076cceee3b7469f6e64c48589633fcaec Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 12 Oct 2022 13:39:03 +0900 Subject: [PATCH 112/132] affiliation code db migrations --- .../20221012043710-add-affiliation-code.js | 14 ++++++++++++++ server/db/models/affiliation.js | 4 ++++ 2 files changed, 18 insertions(+) create mode 100644 server/db/migrations/20221012043710-add-affiliation-code.js diff --git a/server/db/migrations/20221012043710-add-affiliation-code.js b/server/db/migrations/20221012043710-add-affiliation-code.js new file mode 100644 index 0000000000..a6f6b74063 --- /dev/null +++ b/server/db/migrations/20221012043710-add-affiliation-code.js @@ -0,0 +1,14 @@ +'use strict'; + +const TABLE = 'Affiliations'; +const COLUMN = 'code'; + +module.exports = { + up: (queryInterface, Sequelize) => + queryInterface.addColumn(TABLE, COLUMN, { + type: Sequelize.STRING, + allowNull: true + }), + down: (queryInterface) => + queryInterface.removeColumn(TABLE, COLUMN) +}; \ No newline at end of file diff --git a/server/db/models/affiliation.js b/server/db/models/affiliation.js index 40f59d197e..b9c69b0f00 100644 --- a/server/db/models/affiliation.js +++ b/server/db/models/affiliation.js @@ -25,6 +25,10 @@ module.exports = function(sequelize, DataTypes) { model: 'Users', key: 'id' } + }, + code: { + type: DataTypes.STRING, + allowNull: true } }, { From b0f245d7e1dd0e802a6638e3219a0f1b690c6b27 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 12 Oct 2022 08:11:23 +0300 Subject: [PATCH 113/132] warning cleaning --- web/src/components/OtpForm/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/src/components/OtpForm/index.js b/web/src/components/OtpForm/index.js index 73977c77ab..d611536abe 100644 --- a/web/src/components/OtpForm/index.js +++ b/web/src/components/OtpForm/index.js @@ -1,8 +1,7 @@ import React, { Component } from 'react'; import { reduxForm } from 'redux-form'; -import { required, validateOtp } from 'components/Form/validations'; import renderFields from 'components/Form/factoryFields'; -import { Button, IconTitle, ActionNotification, EditWrapper } from 'components'; +import { IconTitle, ActionNotification } from 'components'; import STRINGS from 'config/localizedStrings'; import withConfig from 'components/ConfigProvider/withConfig'; From 8cc0968b24e28fa32029f84831bd80ade4f1eeb1 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 12 Oct 2022 12:11:16 +0300 Subject: [PATCH 114/132] warning cleanup, icon change --- web/src/components/Form/FormFields/_FormFields.scss | 7 ------- web/src/components/OtpForm/index.js | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/web/src/components/Form/FormFields/_FormFields.scss b/web/src/components/Form/FormFields/_FormFields.scss index 5bf7b6fbb7..1580c5c8b1 100644 --- a/web/src/components/Form/FormFields/_FormFields.scss +++ b/web/src/components/Form/FormFields/_FormFields.scss @@ -62,10 +62,6 @@ $placeholder--font-weight: bold; } } - .datefield-input { - // visibility: hidden; - } - .datefield-values-wrapper { display: flex; > * { @@ -128,9 +124,6 @@ $placeholder--font-weight: bold; } } -.input_file { -} - .placeholder { font-size: $placeholder--font-size; font-weight: $placeholder--font-weight; diff --git a/web/src/components/OtpForm/index.js b/web/src/components/OtpForm/index.js index d611536abe..a1b316e327 100644 --- a/web/src/components/OtpForm/index.js +++ b/web/src/components/OtpForm/index.js @@ -59,8 +59,8 @@ class Form extends Component { <IconTitle stringId="OTP_FORM.OTP_TITLE" text={STRINGS['OTP_FORM.OTP_TITLE']} - iconId="OTP_CODE" - iconPath={ICONS['OTP_CODE']} + iconId="SET_NEW_PASSWORD" + iconPath={ICONS['SET_NEW_PASSWORD']} /> <div className="otp_form-title-wrapper"> {onClickHelp && ( From 59968c12b18527653ef67f69261188aedb85b602 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 12 Oct 2022 15:37:05 +0300 Subject: [PATCH 115/132] Language select added into appbar --- web/src/components/AppBar/LanguageSwitcher.js | 48 +++++++++++++++++++ web/src/components/AppBar/index.js | 11 ++++- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 web/src/components/AppBar/LanguageSwitcher.js diff --git a/web/src/components/AppBar/LanguageSwitcher.js b/web/src/components/AppBar/LanguageSwitcher.js new file mode 100644 index 0000000000..ef913c169b --- /dev/null +++ b/web/src/components/AppBar/LanguageSwitcher.js @@ -0,0 +1,48 @@ +import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'; +import { Select } from 'antd'; +import { generateLanguageFormValues } from 'containers/UserSettings/LanguageForm'; +import React, { useEffect, useState } from 'react'; +const { Option } = Select; + +const LanguageSwitcher = ({ selected, valid_languages, toggle }) => { + const [isOpen, setIsOpen] = useState(false); + const [_selected, setSelected] = useState(selected); + const languageFormValue = generateLanguageFormValues(valid_languages).language + .options; + const onSwitch = (val) => { + setSelected(val); + }; + + useEffect(() => { + if (toggle && _selected !== selected) toggle(_selected); + }, [_selected, toggle, selected]); + return ( + <Select + value={_selected} + size="small" + onSelect={onSwitch} + bordered={false} + onClick={() => setIsOpen((prev) => !prev)} + suffixIcon={isOpen ? <CaretUpOutlined /> : <CaretDownOutlined />} + className="custom-select-input-style appbar elevated" + dropdownClassName="custom-select-style select-option-wrapper" + > + {languageFormValue.map(({ value, icon, label }) => ( + <Option value={value} key={value} className="capitalize"> + <div className="language_option"> + <img + width="10px" + height="10px" + src={icon} + alt={label} + className="mr-2" + /> + {label} + </div> + </Option> + ))} + </Select> + ); +}; + +export default LanguageSwitcher; diff --git a/web/src/components/AppBar/index.js b/web/src/components/AppBar/index.js index c837dddb4c..f9d0a0250b 100644 --- a/web/src/components/AppBar/index.js +++ b/web/src/components/AppBar/index.js @@ -8,7 +8,7 @@ import { DEFAULT_URL } from 'config/constants'; import MenuList from './MenuList'; import { MobileBarWrapper } from '../'; import { isLoggedIn } from '../../utils/token'; -import { getTickers, changeTheme } from '../../actions/appActions'; +import { getTickers, changeTheme, setLanguage } from '../../actions/appActions'; import { updateUserSettings, setUserData } from '../../actions/userAction'; import ThemeSwitcher from './ThemeSwitcher'; import { EditWrapper, ButtonLink, Image } from 'components'; @@ -16,6 +16,7 @@ import withEdit from 'components/EditProvider/withEdit'; import withConfig from 'components/ConfigProvider/withConfig'; import AnnouncementList from './AnnouncementList'; import STRINGS from 'config/localizedStrings'; +import LanguageSwitcher from './LanguageSwitcher'; class AppBar extends Component { state = { @@ -304,6 +305,13 @@ class AppBar extends Component { id="trade-nav-container" className="d-flex app-bar-account justify-content-end" > + <div className="d-flex app_bar-quicktrade-container"> + <LanguageSwitcher + selected={this.props.activeLanguage} + valid_languages={this.props.constants.valid_languages} + toggle={this.props.changeLanguage} + /> + </div> <div className="d-flex app_bar-quicktrade-container"> <ThemeSwitcher selected={selected} @@ -344,6 +352,7 @@ const mapDispatchToProps = (dispatch) => ({ getTickers: bindActionCreators(getTickers, dispatch), changeTheme: bindActionCreators(changeTheme, dispatch), setUserData: bindActionCreators(setUserData, dispatch), + changeLanguage: bindActionCreators(setLanguage, dispatch), }); export default connect( From 5deee155308eec6dddd57ad1e243f23aee87d0ce Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Wed, 12 Oct 2022 19:15:50 +0530 Subject: [PATCH 116/132] Resolved the slippage validation issue while enter 0 value. --- web/src/components/QuickTrade/InputGroup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/QuickTrade/InputGroup.js b/web/src/components/QuickTrade/InputGroup.js index 6d985a3a77..b7b27d37a0 100644 --- a/web/src/components/QuickTrade/InputGroup.js +++ b/web/src/components/QuickTrade/InputGroup.js @@ -64,7 +64,7 @@ class InputGroup extends React.PureComponent { } = this.props; const keydata = pair.split('-'); let error = ''; - if (!value) { + if (!value || (value && !parseFloat(value))) { error = ''; } else if ( keydata[0] === selectValue && From 567f7bdc66273ed82b95e1e14f1a9560248b2f81 Mon Sep 17 00:00:00 2001 From: fetok12 <fetok12@gmail.com> Date: Thu, 13 Oct 2022 10:52:25 +0300 Subject: [PATCH 117/132] revert back to old plugin logix --- server/plugins/controllers.js | 23 +--- server/plugins/index.js | 219 ++++++++++++++-------------------- 2 files changed, 94 insertions(+), 148 deletions(-) diff --git a/server/plugins/controllers.js b/server/plugins/controllers.js index 3989d1cf2b..ff671204b9 100644 --- a/server/plugins/controllers.js +++ b/server/plugins/controllers.js @@ -124,10 +124,7 @@ const deletePlugin = async (req, res) => { 'restarting plugin process' ); - const { stopPlugin } = require('./index'); - - stopPlugin(plugin); - // process.exit(); + process.exit(); } } catch (err) { loggerPlugin.error( @@ -284,10 +281,7 @@ const postPlugin = async (req, res) => { ); if (plugin.enabled && plugin.script) { - const { startPlugin } = require('./index'); - - startPlugin(plugin); - // process.exit(); + process.exit(); } } catch (err) { loggerPlugin.error( @@ -715,10 +709,7 @@ const disablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { - // process.exit(); - const { stopPlugin } = require('./index'); - - stopPlugin(plugin); + process.exit(); } } catch (err) { loggerPlugin.error( @@ -773,11 +764,7 @@ const enablePlugin = async (req, res) => { res.json({ message: 'Success' }); if (plugin.script) { - //process.exit(); - - const { startPlugin } = require('./index'); - - startPlugin(plugin); + process.exit(); } } catch (err) { loggerPlugin.error( @@ -800,4 +787,4 @@ module.exports = { getPluginScript, disablePlugin, enablePlugin -}; +}; \ No newline at end of file diff --git a/server/plugins/index.js b/server/plugins/index.js index 5505e2d191..4dc4af3a2e 100644 --- a/server/plugins/index.js +++ b/server/plugins/index.js @@ -48,9 +48,6 @@ const tripleBeam = require('triple-beam'); const uglifyEs = require('uglify-es'); const bodyParser = require('body-parser'); -let app; -let disabledPlugins = {}; - const getInstalledLibrary = async (name, version) => { const jsonFilePath = path.resolve(__dirname, '../node_modules', name, 'package.json'); @@ -105,115 +102,6 @@ const installLibrary = async (library) => { } }; -const stopPlugin = async (plugin) => { - - try { - const subStr = plugin.script.match(/"\/plugins(.*?)"/g); - disabledPlugins[plugin.name] = subStr || []; - - } catch (err) { - loggerPlugin.error( - 'plugins/index/kill_plugin', - `error while stopping plugin ${plugin.name}`, - err.message - ); - } -}; - -const startPlugin = async (plugin) => { - try { - loggerPlugin.verbose( - 'plugins/index/initialization', - `starting plugin ${plugin.name}` - ); - - const context = { - configValues: { - publicMeta: plugin.public_meta, - meta: plugin.meta - }, - pluginLibraries: { - app, - loggerPlugin, - toolsLib - }, - app, - toolsLib, - lodash, - expressValidator, - loggerPlugin, - multer, - moment, - mathjs, - bluebird, - umzug, - rp, - sequelize, - uuid, - jwt, - momentTz, - json2csv, - flat, - ws, - cron, - randomString, - bcryptjs, - expectCt, - validator, - uglifyEs, - otp, - latestVersion, - geoipLite, - nodemailer, - wsHeartbeatServer, - wsHeartbeatClient, - cors, - winston, - elasticApmNode, - winstonElasticsearchApm, - tripleBeam, - bodyParser, - morgan, - meta: plugin.meta, - publicMeta: plugin.public_meta, - installedLibraries: {} - }; - - if (plugin.prescript && lodash.isArray(plugin.prescript.install) && !lodash.isEmpty(plugin.prescript.install)) { - loggerPlugin.verbose( - 'plugins/index/initialization', - `Installing packages for plugin ${plugin.name}` - ); - - for (const library of plugin.prescript.install) { - context.installedLibraries[library] = await installLibrary(library); - } - - loggerPlugin.verbose( - 'plugins/index/initialization', - `Plugin ${plugin.name} packages installed` - ); - } - - _eval(plugin.script, plugin.name, context, true); - - if (disabledPlugins[plugin.name]) delete disabledPlugins[plugin.name]; - - - loggerPlugin.verbose( - 'plugins/index/initialization', - `Plugin ${plugin.name} running` - ); - } catch (err) { - loggerPlugin.error( - 'plugins/index/initialization', - `error while starting plugin ${plugin.name}`, - err.message - ); - } -}; - - checkStatus() .then(async () => { loggerPlugin.info( @@ -221,7 +109,7 @@ checkStatus() 'Initializing Plugin Server...' ); - app = express(); + const app = express(); app.use(morgan(morganType, { stream })); app.listen(PORT); @@ -232,16 +120,6 @@ checkStatus() app.use(domainMiddleware); helmetMiddleware(app); - app.use((req, res, next) => { - if (req.path.length > 1 && req.path.includes('/plugins/')) { - //Check if plugin is disabled - for (let endpoints of Object.values(disabledPlugins)) { - if (endpoints.some((endpoint) => endpoint.includes(req.path))) { return res.status(404).send(); } - } - } - next(); - }); - app.use('/plugins', routes); const plugins = await Plugin.findAll({ @@ -255,7 +133,93 @@ checkStatus() }); for (const plugin of plugins) { - startPlugin(plugin); + try { + loggerPlugin.verbose( + 'plugins/index/initialization', + `starting plugin ${plugin.name}` + ); + + const context = { + configValues: { + publicMeta: plugin.public_meta, + meta: plugin.meta + }, + pluginLibraries: { + app, + loggerPlugin, + toolsLib + }, + app, + toolsLib, + lodash, + expressValidator, + loggerPlugin, + multer, + moment, + mathjs, + bluebird, + umzug, + rp, + sequelize, + uuid, + jwt, + momentTz, + json2csv, + flat, + ws, + cron, + randomString, + bcryptjs, + expectCt, + validator, + uglifyEs, + otp, + latestVersion, + geoipLite, + nodemailer, + wsHeartbeatServer, + wsHeartbeatClient, + cors, + winston, + elasticApmNode, + winstonElasticsearchApm, + tripleBeam, + bodyParser, + morgan, + meta: plugin.meta, + publicMeta: plugin.public_meta, + installedLibraries: {} + }; + + if (plugin.prescript && lodash.isArray(plugin.prescript.install) && !lodash.isEmpty(plugin.prescript.install)) { + loggerPlugin.verbose( + 'plugins/index/initialization', + `Installing packages for plugin ${plugin.name}` + ); + + for (const library of plugin.prescript.install) { + context.installedLibraries[library] = await installLibrary(library); + } + + loggerPlugin.verbose( + 'plugins/index/initialization', + `Plugin ${plugin.name} packages installed` + ); + } + + _eval(plugin.script, plugin.name, context, true); + + loggerPlugin.verbose( + 'plugins/index/initialization', + `Plugin ${plugin.name} running` + ); + } catch (err) { + loggerPlugin.error( + 'plugins/index/initialization', + `error while starting plugin ${plugin.name}`, + err.message + ); + } } loggerPlugin.info( @@ -280,9 +244,4 @@ checkStatus() ); setTimeout(() => { process.exit(1); }, 5000); - }); - -module.exports = { - startPlugin, - stopPlugin -}; \ No newline at end of file + }); \ No newline at end of file From 9726b96289428f38bbc75d9fd006cbe83ac9e06d Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Thu, 13 Oct 2022 16:18:44 +0530 Subject: [PATCH 118/132] Fixed the carousel issue for the safari browser. --- web/src/containers/Home/index.js | 64 +++++++++++++++++++------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/web/src/containers/Home/index.js b/web/src/containers/Home/index.js index 14056b6b78..c2e1d9f0d8 100644 --- a/web/src/containers/Home/index.js +++ b/web/src/containers/Home/index.js @@ -9,7 +9,7 @@ import { isMobile } from 'react-device-detect'; import _floor from 'lodash/floor'; import { setWsHeartbeat } from 'ws-heartbeat/client'; import debounce from 'lodash.debounce'; -import { message } from 'antd'; +import { message, Spin } from 'antd'; import moment from 'moment'; import STRINGS from 'config/localizedStrings'; @@ -161,6 +161,7 @@ class Home extends Component { isAmountChanged: false, isHover: false, hoveredIndex: 0, + carouselLodaing: true, }; this.goToPair(pair); this.props.setPriceEssentials({ side: this.state.side }); @@ -210,6 +211,9 @@ class Home extends Component { this.setState({ isBrokerPaused: true }); } this.handleBrokerQuote(pair, side); + setTimeout(() => { + this.setState({ carouselLodaing: false }); + }, 3000); } componentDidUpdate(prevProps, prevState) { @@ -773,35 +777,43 @@ class Home extends Component { const duration = parseInt((50 / 12) * testMarket.length); const marketsData = [...testMarket, ...testMarket, ...testMarket]; - const items = marketsData.map((market) => ( - <MarketCard - market={market} - onDragStart={this.handleDragStart} - role="presentation" - chartData={chartData} - /> - )); return ( <div className="home_carousel_section "> - <div class="slideshow-wrapper"> - <div - class="parent-slider d-flex" - style={{ animationDuration: `${duration}s` }} - > - {items.map((sec, index) => { - return ( - <div - className="section" - key={index} - onClick={() => this.sectionToNav(sec)} - > - {sec} - </div> - ); - })} + <Spin spinning={this.state.carouselLodaing}> + <div class="slideshow-wrapper"> + <div + class="parent-slider d-flex" + style={{ animationDuration: `${duration}s` }} + > + {marketsData.map((sec, index) => { + return ( + <div + className="section" + style={{ + borderRight: `${ + !this.state.carouselLodaing + ? '1px solid #60605d' + : 'none' + }`, + }} + key={index} + onClick={() => this.sectionToNav(sec)} + > + {!this.state.carouselLodaing ? ( + <MarketCard + market={sec} + onDragStart={this.handleDragStart} + role="presentation" + chartData={chartData} + /> + ) : null} + </div> + ); + })} + </div> </div> - </div> + </Spin> </div> ); } From e54fd2726ff8a7198ddf06c183c26425372eed25 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Thu, 13 Oct 2022 20:10:30 +0300 Subject: [PATCH 119/132] index.css added into PR --- web/src/index.css | 80 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/web/src/index.css b/web/src/index.css index 1e60bbb662..3a4b6c4377 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -389,8 +389,8 @@ table th { height: auto; flex: 1; } .app_container.layout-mobile .app_container-content { - min-height: calc( 100vh - 10rem); - max-height: calc( 100vh - 10rem); + min-height: calc( 100vh - 10rem); + max-height: calc( 100vh - 10rem); max-width: 100vw; overflow-y: scroll; } .app_container.layout-mobile .app_container-content.chart-embed { @@ -408,8 +408,8 @@ table th { .app_container.layout-mobile .app_container-content .app_container-main.no_bottom_navigation { height: 100%; } .app_container.layout-mobile .content-with-bar { - min-height: calc( 100vh - 17rem); - max-height: calc( 100vh - 17rem); + min-height: calc( 100vh - 17rem); + max-height: calc( 100vh - 17rem); max-width: 100vw; padding: 1rem; margin: 0; @@ -3298,8 +3298,8 @@ table th { .layout-mobile .quote-container { background-color: transparent; overflow-y: scroll; - min-height: calc( 100vh - 17rem); - max-height: calc( 100vh - 17rem); } + min-height: calc( 100vh - 17rem); + max-height: calc( 100vh - 17rem); } .layout-mobile .quote-container .quick_trade-wrapper { flex: 1 1; min-width: 95vw !important; @@ -3453,7 +3453,7 @@ table th { .layout-mobile .presentation_container.verification_container { padding-top: 0 !important; - max-height: calc( 100vh - 10rem); } + max-height: calc( 100vh - 10rem); } .layout-mobile .presentation_container.verification_container .header-content { font-size: 13px !important; } @@ -5644,7 +5644,7 @@ table th { padding: 0.3rem 0; } .modal-market-menu .app-bar-add-tab-content .scroll-view { overflow-y: auto; - height: calc( 100vh - 19rem - 8rem); } + height: calc( 100vh - 19rem - 8rem); } .modal-market-menu .app-bar-tab-overflow-content { background-color: var(--base_background); color: var(--labels_secondary-inactive-label-text-graphics); @@ -7958,6 +7958,45 @@ table th { .layout-mobile .field_warning_wrapper { padding-bottom: 1rem; } +.mainContainer { + display: flex; + width: fit-content; + margin: auto; + position: relative; + justify-content: center; + align-items: center; } + .mainContainer input { + width: 30px; + height: 30px; + border-radius: 0.5rem; + border: 1px solid var(--calculated_secondary-border); + background: var(--base_background); + text-align: center; + font-size: large; } + .mainContainer .masterInput { + position: absolute; + width: 100%; + top: 8px; + opacity: 0 !important; } + .mainContainer .seperator { + width: 2rem; + border: 1px solid var(--labels_secondary-inactive-label-text-graphics); + background: var(--labels_secondary-inactive-label-text-graphics); } + +.active { + border: 1px solid var(--labels_secondary-inactive-label-text-graphics) !important; } + +.error { + border: 1px solid red !important; } + +.warning_text { + text-align: center; } + +.group { + padding: 1rem; } + .group input { + margin: 0rem 0.25rem; } + .input_icon { margin-right: 0.25rem; width: 1.2rem; } @@ -8398,14 +8437,14 @@ table th { right: 0 !important; min-width: 100vw; max-width: 100vw; - min-height: calc( 100vh - 4rem); - max-height: calc( 100vh - 4rem); + min-height: calc( 100vh - 4rem); + max-height: calc( 100vh - 4rem); border-radius: 0 !important; padding: 0 !important; } .layout-mobile .ReactModal__Content .dialog-mobile-content { padding: 2.5rem !important; - min-height: calc( 100vh - 8rem); - max-height: calc( 100vh - 8rem); + min-height: calc( 100vh - 8rem); + max-height: calc( 100vh - 8rem); display: flex; } .layout-mobile.LogoutModal .ReactModal__Content { @@ -8813,9 +8852,20 @@ table th { border-top: 1px solid var(--calculated_secondary-border); margin: 1rem 0; position: relative; } - .otp_form-title-wrapper .otp_form-title-text { - font-weight: bold; - font-size: 1.1rem; } + +.otp_form-info-text { + font-weight: bold; + font-size: 1.1rem; + width: 100%; + text-align: center !important; } + +.otp_form-subnote-text { + font-weight: 400; + font-size: 1.1rem; + opacity: 0.7; + width: 100%; + margin-top: 5rem; + text-align: center !important; } .otp_form-fields { margin-bottom: 1rem; } From 1e209c845fc69ba3df37e7560a42d4f496ecf479 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Fri, 14 Oct 2022 17:41:21 +0900 Subject: [PATCH 120/132] long term jwt token --- server/api/controllers/user.js | 39 +++++++++++++++++++++++++++++---- server/api/swagger/swagger.yaml | 2 ++ server/constants.js | 3 +++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/server/api/controllers/user.js b/server/api/controllers/user.js index 2f4294ea29..08be7bc92a 100644 --- a/server/api/controllers/user.js +++ b/server/api/controllers/user.js @@ -27,7 +27,7 @@ const { USER_EMAIL_IS_VERIFIED, INVALID_VERIFICATION_CODE } = require('../../messages'); -const { DEFAULT_ORDER_RISK_PERCENTAGE, EVENTS_CHANNEL, API_HOST, DOMAIN } = require('../../constants'); +const { DEFAULT_ORDER_RISK_PERCENTAGE, EVENTS_CHANNEL, API_HOST, DOMAIN, TOKEN_TIME_NORMAL, TOKEN_TIME_LONG } = require('../../constants'); const { all } = require('bluebird'); const { each } = require('lodash'); const { publisher } = require('../../db/pubsub'); @@ -296,9 +296,13 @@ const loginPost = (req, res) => { password, otp_code, captcha, - service + service, + long_term } = req.swagger.params.authentication.value; - let { email } = req.swagger.params.authentication.value; + let { + email + } = req.swagger.params.authentication.value; + const ip = req.headers['x-real-ip']; const device = req.headers['user-agent']; const domain = req.headers['x-real-origin']; @@ -306,6 +310,32 @@ const loginPost = (req, res) => { const referer = req.headers.referer; const time = new Date(); + loggerUser.verbose( + req.uuid, + 'controllers/user/loginPost', + 'email', + email, + 'otp_code', + otp_code, + 'captcha', + captcha, + 'service', + service, + 'long_term', + long_term, + 'ip', + ip, + 'device', + device, + 'domain', + domain, + 'origin', + origin, + 'referer', + referer + ); + + if (!email || typeof email !== 'string' || !isEmail(email)) { loggerUser.error( req.uuid, @@ -394,7 +424,8 @@ const loginPost = (req, res) => { user.is_support, user.is_supervisor, user.is_kyc, - user.is_communicator + user.is_communicator, + long_term ? TOKEN_TIME_LONG : TOKEN_TIME_NORMAL ) }); }) diff --git a/server/api/swagger/swagger.yaml b/server/api/swagger/swagger.yaml index e95967e5f2..67513dbda9 100644 --- a/server/api/swagger/swagger.yaml +++ b/server/api/swagger/swagger.yaml @@ -4686,6 +4686,8 @@ definitions: type: string service: type: string + long_term: + type: boolean ResetPassword: type: object required: diff --git a/server/constants.js b/server/constants.js index c48499c94e..8a3460fe88 100644 --- a/server/constants.js +++ b/server/constants.js @@ -350,6 +350,9 @@ exports.DEFAULT_ORDER_RISK_PERCENTAGE = 90; // used in settings in percentage to // SECURITY CONSTANTS START -------------------------------------------------- +exports.TOKEN_TIME_NORMAL = '24h'; +exports.TOKEN_TIME_LONG = '30d'; + exports.TOKEN_TYPES = { HMAC: 'hmac' }; From 65f716c4ef0ef97f821ef7e1144d4ddf4fca5df2 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Fri, 14 Oct 2022 13:57:32 +0300 Subject: [PATCH 121/132] Wallet formats update --- web/src/containers/Wallet/AssetsBlock.js | 21 ++++++++++++++++----- web/src/utils/currency.js | 6 +++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/web/src/containers/Wallet/AssetsBlock.js b/web/src/containers/Wallet/AssetsBlock.js index 82992aee10..f34667e8aa 100644 --- a/web/src/containers/Wallet/AssetsBlock.js +++ b/web/src/containers/Wallet/AssetsBlock.js @@ -208,7 +208,15 @@ const AssetsBlock = ({ <tbody> {sortedSearchResults.map( ( - [key, { min, allow_deposit, allow_withdrawal, oraclePrice }], + [ + key, + { + increment_unit, + allow_deposit, + allow_withdrawal, + oraclePrice, + }, + ], index ) => { const balanceValue = balance[`${key}_balance`]; @@ -227,10 +235,10 @@ const AssetsBlock = ({ const baseCoin = coins[BASE_CURRENCY] || DEFAULT_COIN_DATA; const balanceText = key === BASE_CURRENCY - ? formatToCurrency(balanceValue, min) + ? formatToCurrency(balanceValue, increment_unit) : formatToCurrency( calculateOraclePrice(balanceValue, oraclePrice), - baseCoin.min + baseCoin.increment_unit ); return ( <tr className="table-row table-bottom-border" key={key}> @@ -268,12 +276,15 @@ const AssetsBlock = ({ )} </td> <td className="td-amount"> - {sortedSearchResults && baseCoin && loading ? ( + {sortedSearchResults && + baseCoin && + loading && + increment_unit ? ( <div className="d-flex"> <div className="mr-4"> {STRINGS.formatString( CURRENCY_PRICE_FORMAT, - formatToCurrency(balanceValue, min, true), + formatToCurrency(balanceValue, increment_unit), display_name )} </div> diff --git a/web/src/utils/currency.js b/web/src/utils/currency.js index a34d495b35..179a2bd42f 100644 --- a/web/src/utils/currency.js +++ b/web/src/utils/currency.js @@ -69,7 +69,11 @@ export const getFormat = (min = 0, fullFormat) => { export const formatToCurrency = (amount = 0, min = 0, fullFormat = false) => { let formatObj = getFormat(min, fullFormat); - return numbro(roundNumber(amount, formatObj.digit)).format(formatObj.format); + let _amount = amount; + if (min >= 1) { + _amount = math.subtract(amount, math.mod(amount, min)); + } + return numbro(roundNumber(_amount, formatObj.digit)).format(formatObj.format); }; export const formatToSimple = (amount = 0, min = 0, fullFormat = false) => { From 034b6a48df8723e847ab4c0b68db608715e51dd7 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Mon, 17 Oct 2022 22:59:50 +0300 Subject: [PATCH 122/132] LAnguage Bug Fix --- web/package-lock.json | 2 +- web/src/components/AppBar/LanguageSwitcher.js | 10 +++++++--- .../components/Form/FormFields/_DropdownField.scss | 5 +++++ web/src/containers/UserSettings/index.js | 12 ++++++++---- web/src/index.css | 4 ++++ 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 6bc6790c6f..03152894bb 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "hollaex-kit", - "version": "2.4.3", + "version": "2.4.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/web/src/components/AppBar/LanguageSwitcher.js b/web/src/components/AppBar/LanguageSwitcher.js index ef913c169b..d3a5a11977 100644 --- a/web/src/components/AppBar/LanguageSwitcher.js +++ b/web/src/components/AppBar/LanguageSwitcher.js @@ -10,12 +10,16 @@ const LanguageSwitcher = ({ selected, valid_languages, toggle }) => { const languageFormValue = generateLanguageFormValues(valid_languages).language .options; const onSwitch = (val) => { - setSelected(val); + toggle(val); }; useEffect(() => { - if (toggle && _selected !== selected) toggle(_selected); - }, [_selected, toggle, selected]); + if (setSelected && _selected !== selected) setSelected(selected); + }, [setSelected, _selected, selected]); + + useEffect(() => { + if (toggle) toggle(_selected); + }, [_selected, toggle]); return ( <Select value={_selected} diff --git a/web/src/components/Form/FormFields/_DropdownField.scss b/web/src/components/Form/FormFields/_DropdownField.scss index 30b0765c72..511115ff0f 100644 --- a/web/src/components/Form/FormFields/_DropdownField.scss +++ b/web/src/components/Form/FormFields/_DropdownField.scss @@ -17,6 +17,11 @@ $dropdown-option--padding: 0.25rem; .dropdown-option-open { margin: 0px !important; + & img { + height: 1rem; + margin-right: 0.25rem; + margin-bottom: 0.25rem; + } } .disabled { diff --git a/web/src/containers/UserSettings/index.js b/web/src/containers/UserSettings/index.js index 96fb51681f..e79d1ebe2e 100644 --- a/web/src/containers/UserSettings/index.js +++ b/web/src/containers/UserSettings/index.js @@ -95,7 +95,7 @@ class UserSettings extends Component { UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.activeLanguage !== this.props.activeLanguage) { - this.updateTabs(this.props, this.state.activeTab); + this.updateTabs(nextProps, this.state.activeTab); } if ( JSON.stringify(this.props.settings) !== JSON.stringify(nextProps.settings) @@ -161,7 +161,10 @@ class UserSettings extends Component { }); }; - updateTabs = ({ username = '', settings = {}, coins = {} }, activeTab) => { + updateTabs = ( + { activeLanguage = '', username = '', settings = {}, coins = {} }, + activeTab + ) => { const { constants = {}, icons: ICONS, @@ -269,7 +272,7 @@ class UserSettings extends Component { this.onSubmitSettings(formProps, 'language') } formFields={languageFormValue} - initialValues={{ language: settings.language }} + initialValues={{ language: activeLanguage }} ICONS={ICONS} /> ), @@ -400,8 +403,9 @@ class UserSettings extends Component { .then(({ data }) => { this.props.setUserData(data); if (data.settings) { - if (data.settings.language) + if (data.settings.language) { this.props.changeLanguage(data.settings.language); + } if (data.settings.interface && data.settings.interface.theme) { this.props.changeTheme(data.settings.interface.theme); localStorage.setItem('theme', data.settings.interface.theme); diff --git a/web/src/index.css b/web/src/index.css index 3a4b6c4377..560412ced3 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -7718,6 +7718,10 @@ table th { .dropdown-option-open { margin: 0px !important; } + .dropdown-option-open img { + height: 1rem; + margin-right: .25rem; + margin-bottom: .25rem; } .disabled .field-children { color: var(--labels_secondary-inactive-label-text-graphics); } From f8b2a0b42266cac54a83ee2d5ee48dcfffbe849f Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Mon, 17 Oct 2022 23:00:07 +0300 Subject: [PATCH 123/132] index.css --- web/src/index.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/index.css b/web/src/index.css index 560412ced3..e3ac4eaae8 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -7720,8 +7720,8 @@ table th { margin: 0px !important; } .dropdown-option-open img { height: 1rem; - margin-right: .25rem; - margin-bottom: .25rem; } + margin-right: 0.25rem; + margin-bottom: 0.25rem; } .disabled .field-children { color: var(--labels_secondary-inactive-label-text-graphics); } From 67f544b2d13d35d920c3da3dee588e9ff906b348 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Mon, 17 Oct 2022 23:12:37 +0300 Subject: [PATCH 124/132] Language switcher bug fix --- web/src/components/AppBar/LanguageSwitcher.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/web/src/components/AppBar/LanguageSwitcher.js b/web/src/components/AppBar/LanguageSwitcher.js index d3a5a11977..e9c6f18579 100644 --- a/web/src/components/AppBar/LanguageSwitcher.js +++ b/web/src/components/AppBar/LanguageSwitcher.js @@ -1,7 +1,9 @@ import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'; +import { updateUserSettings } from 'actions/userAction'; import { Select } from 'antd'; import { generateLanguageFormValues } from 'containers/UserSettings/LanguageForm'; import React, { useEffect, useState } from 'react'; +import { SubmissionError } from 'redux-form'; const { Option } = Select; const LanguageSwitcher = ({ selected, valid_languages, toggle }) => { @@ -10,7 +12,21 @@ const LanguageSwitcher = ({ selected, valid_languages, toggle }) => { const languageFormValue = generateLanguageFormValues(valid_languages).language .options; const onSwitch = (val) => { - toggle(val); + updateUserSettings({ language: val }) + .then(({ data }) => { + if (data.settings) { + if (data.settings.language) { + toggle(data.settings.language); + } + } + }) + .catch((err) => { + const _error = + err.response && err.response.data + ? err.response.data.message + : err.message; + throw new SubmissionError({ _error }); + }); }; useEffect(() => { From 5706cd03238d48dfb8825443b171919747b4bb92 Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Tue, 18 Oct 2022 11:44:31 +0530 Subject: [PATCH 125/132] Changes for confirm UI restriction on limits exchange popup. --- web/src/containers/Admin/Tiers/CheckAndConfirm.js | 4 +++- web/src/containers/Admin/Tiers/index.css | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/web/src/containers/Admin/Tiers/CheckAndConfirm.js b/web/src/containers/Admin/Tiers/CheckAndConfirm.js index eaf3fa095c..92e13608f0 100644 --- a/web/src/containers/Admin/Tiers/CheckAndConfirm.js +++ b/web/src/containers/Admin/Tiers/CheckAndConfirm.js @@ -63,7 +63,8 @@ const CheckAndConfirm = ({ <span className="bold">{currentNative?.toUpperCase()}</span> to{' '} <span className="bold">{currentCoin.toUpperCase()}</span>? </div> - <div className="button-wrapper mt-4"> + <div className="switch-warn mt-3 mb-1">Asset switcher coming soon!</div> + <div className="button-wrapper"> <Button type="primary" className="green-btn" @@ -74,6 +75,7 @@ const CheckAndConfirm = ({ <Button type="primary" className="green-btn" + disabled onClick={() => handleConfirm('edit-limits')} > Confirm diff --git a/web/src/containers/Admin/Tiers/index.css b/web/src/containers/Admin/Tiers/index.css index 0faca30ce3..f9e7b4797f 100644 --- a/web/src/containers/Admin/Tiers/index.css +++ b/web/src/containers/Admin/Tiers/index.css @@ -198,3 +198,8 @@ .admin-tiers-wrapper .description .underline { color: #ffffff; } +.change-Limit-wrapper .switch-warn { + float: right; + font-size: 13px; + color: #f15a29; +} From 627fd28933bd6f728dd2f9965b95174253e817bb Mon Sep 17 00:00:00 2001 From: ram <ram@bitholla.com> Date: Tue, 18 Oct 2022 18:50:05 +0530 Subject: [PATCH 126/132] Fix for landing page icon section background image opacity. --- web/src/containers/Home/_Home.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/containers/Home/_Home.scss b/web/src/containers/Home/_Home.scss index 3ba046dc36..8621fd53b0 100644 --- a/web/src/containers/Home/_Home.scss +++ b/web/src/containers/Home/_Home.scss @@ -366,7 +366,8 @@ $trade-tab--arrow-size: 0.4rem; display: flex; justify-content: center; align-items: center; - background-color: $app-sidebar-background; + background-color: transparent; + border-bottom: 1px $colors-border solid; opacity: 0.8; .card-section-wrapper { display: flex; From 36282e56960e619f791a2020ad2155eeb34712ec Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Tue, 18 Oct 2022 22:57:36 +0300 Subject: [PATCH 127/132] Total Balance Formatting --- web/src/containers/Wallet/MainWallet.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/containers/Wallet/MainWallet.js b/web/src/containers/Wallet/MainWallet.js index 11ea2b6971..f03a6e752e 100644 --- a/web/src/containers/Wallet/MainWallet.js +++ b/web/src/containers/Wallet/MainWallet.js @@ -139,11 +139,12 @@ class Wallet extends Component { { features: { stake_page = false } = {} } = {}, contracts = {} ) => { - const { min, display_name } = coins[BASE_CURRENCY] || DEFAULT_COIN_DATA; + const { increment_unit, display_name } = + coins[BASE_CURRENCY] || DEFAULT_COIN_DATA; const totalAssets = STRINGS.formatString( CURRENCY_PRICE_FORMAT, display_name, - formatToCurrency(total, min) + formatToCurrency(total, increment_unit) ); const searchResult = this.getSearchResult(coins, balance, oraclePrices); From 64ac5bea053f3d0581871788559bc88620b6e172 Mon Sep 17 00:00:00 2001 From: Tayfun Yaltur <tayfunyaltur@gmail.com> Date: Wed, 19 Oct 2022 00:02:34 +0300 Subject: [PATCH 128/132] reset password fixes --- .../UserSecurity/ChangePasswordForm.js | 12 ++----- web/src/containers/UserSecurity/index.js | 36 +++++++++++++------ 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/web/src/containers/UserSecurity/ChangePasswordForm.js b/web/src/containers/UserSecurity/ChangePasswordForm.js index 139a25657d..6f77974961 100644 --- a/web/src/containers/UserSecurity/ChangePasswordForm.js +++ b/web/src/containers/UserSecurity/ChangePasswordForm.js @@ -4,15 +4,10 @@ import { isMobile } from 'react-device-detect'; import renderFields from 'components/Form/factoryFields'; import { Button, EditWrapper } from 'components'; -import { required, password } from 'components/Form/validations'; import STRINGS from 'config/localizedStrings'; -const validate = (values) => { +const validate = () => { const errors = {}; - if (values.new_password !== values.new_password_confirm) { - errors.new_password_confirm = STRINGS['VALIDATIONS.PASSWORDS_DONT_MATCH']; - } - return errors; }; @@ -21,7 +16,6 @@ export const generateFormValues = () => ({ type: 'password', stringId: 'ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.CURRENT_PASSWORD.label,ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.CURRENT_PASSWORD.placeholder', - validate: [required, password], label: STRINGS['ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.CURRENT_PASSWORD.label'], placeholder: @@ -35,7 +29,6 @@ export const generateFormValues = () => ({ type: 'password', stringId: 'ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.NEW_PASSWORD.label,ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.NEW_PASSWORD.placeholder', - validate: [required, password], label: STRINGS['ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.NEW_PASSWORD.label'], placeholder: STRINGS['ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.NEW_PASSWORD.placeholder'], @@ -46,7 +39,6 @@ export const generateFormValues = () => ({ type: 'password', stringId: 'ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.NEW_PASSWORD_REPEAT.label,ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.NEW_PASSWORD_REPEAT.placeholder', - validate: [required], label: STRINGS[ 'ACCOUNT_SECURITY.CHANGE_PASSWORD.FORM.NEW_PASSWORD_REPEAT.label' @@ -65,6 +57,7 @@ const Form = ({ submitting, pristine, error, + _error, valid, initialValues, formFields, @@ -72,6 +65,7 @@ const Form = ({ <form onSubmit={handleSubmit} className="change-password-form-wrapper"> <div className="change-password-form"> {renderFields(formFields)} + {_error && <div className="warning_text">{_error}</div>} {error && <div className="warning_text">{error}</div>} </div> <div className="d-flex justify-content-center mb-4"> diff --git a/web/src/containers/UserSecurity/index.js b/web/src/containers/UserSecurity/index.js index 17d2ff0d09..f340a1040b 100644 --- a/web/src/containers/UserSecurity/index.js +++ b/web/src/containers/UserSecurity/index.js @@ -52,6 +52,7 @@ class UserVerification extends Component { activeTab: 0, jumpToPage: 0, freeze: false, + error: '', updatedPassword: {}, }; @@ -101,11 +102,17 @@ class UserVerification extends Component { prevProps.user.otp.requesting !== this.props.user.otp.requesting || prevProps.user.otp.activated !== this.props.user.otp.activated || prevProps.user.otp_enabled !== this.props.user.otp_enabled || + prevState.error !== this.state.error || prevProps.activeLanguage !== this.props.activeLanguage || this.state.activeTab !== prevState.activeTab ) { this.calculateTabs(this.props.user, this.state.activeTab); } + if (this.state.activeTab !== prevState.activeTab) { + this.setState({ + error: undefined, + }); + } if ( JSON.stringify(prevState.activeTab) !== JSON.stringify(this.state.activeTab) @@ -236,6 +243,7 @@ class UserVerification extends Component { ), content: activeTab === 1 && ( <ChangePasswordForm + _error={this.state.error} onSubmit={this.onSubmitChangePassword} formFields={formValues} /> @@ -381,19 +389,21 @@ class UserVerification extends Component { }); }; + setOtpModalsState = (values) => { + this.setState({ + dialogIsOpen: true, + modalText: undefined, + updatedPassword: { + old_password: values.old_password, + new_password: values.new_password, + }, + }); + }; + onSubmitChangePassword = (values) => { const { otp_enabled } = this.props.user; if (otp_enabled) { - this.setState({ - dialogIsOpen: true, - modalText: - STRINGS['ACCOUNT_SECURITY.CHANGE_PASSWORD.DIALOG.EMAIL_CONFIRMATION'], - stringId: 'ACCOUNT_SECURITY.CHANGE_PASSWORD.DIALOG.EMAIL_CONFIRMATION', - updatedPassword: { - old_password: values.old_password, - new_password: values.new_password, - }, - }); + this.setOtpModalsState(values); } else { return resetPassword({ old_password: values.old_password, @@ -452,6 +462,12 @@ class UserVerification extends Component { if (!_error) { message.error(STRINGS['CHANGE_PASSWORD_FAILED']); } + if (_error !== 'Invalid OTP Code') { + this.setState({ + dialogIsOpen: false, + error: _error, + }); + } throw new SubmissionError({ _error }); }); } else { From 0d4bf1e93721f01869917d9a1fe2dfb1a226f6ef Mon Sep 17 00:00:00 2001 From: mytilene <m_mytilene@yahoo.com> Date: Wed, 19 Oct 2022 10:48:01 +0800 Subject: [PATCH 129/132] Mongolian translation Mongolian translation --- server/mail/strings/mn.json | 112 +++ web/src/config/lang/mn.json | 1300 +++++++++++++++++++++++++++++++++++ 2 files changed, 1412 insertions(+) create mode 100644 server/mail/strings/mn.json create mode 100644 web/src/config/lang/mn.json diff --git a/server/mail/strings/mn.json b/server/mail/strings/mn.json new file mode 100644 index 0000000000..874a1ef5bf --- /dev/null +++ b/server/mail/strings/mn.json @@ -0,0 +1,112 @@ +{ + "mn": { + "LOGIN": { + "html": "<div> <p> Эрхэм хүндэт ${name} </p> <p> Та дараах хаягнаас нэвтэрч орсон байна </p> <div> <div>Цаг: ${time}</div> <div>Улс: ${country}</div> <div>IP Хаяг: ${ip}</div> </div> <p> Хэрэв энэ нь та биш байсан бол нууц үгээ сольж, давхар баталгаажуулалтыг тохируулаад бидэнтэй шууд холбогдоно уу. </p> <p> Хүндэтгэсэн<br> ${api_name} team </p> </div>", + "title": "Нэвтрэх" + }, + "SIGNUP": { + "html": "<div> <p> Эрхэм хүндэт ${name} </p> <p> Та доорх товчийг дарж имэйл хаягаа баталгаажуулах шаардлагатай.<br> Хэрэв танд асуух зүйл байвал энэ имэйлд хариу бичих замаар бидэнтэй холбоо барина уу.</p><p>Доорх товчийг дарж бүртгэлээ үргэлжлүүлнэ үү.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Баталгаажуулах</Button></a></div><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Бүртгүүлэх" + }, + "WELCOME": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>${api_name} бүртгүүлсэнд баярлалаа.</p><p>Арилжааэд оролцохын тулд та эхлээд криптовалютын орлого хийх эсвэл дансандаа мөнгө байршуулах ёстой. Та өөрийн <a href=${link_account} target=_blank>account</a> руу нэвтэрч<a href=${link_deposit} target=_blank>deposit</a> хуудас руу орно уу.,</p><p>Хэрэв танд асуулт, тусламж хэрэгтэй байвал энэ имэйлд хариу бичих замаар бидэнтэй холбоо барина уу.</p><p> Хүндэтгэсэн<br> ${api_name} </p></div>", + "title": "Welcome" + }, + "RESET_PASSWORD": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Та нууц үгээ өөрчлөх хүсэлт явуулсан байна.<br />Нууц үгээ өөрчлөхийн тулд доорх линк дээр дарна уу.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Нууц үг шинэчлэх</Button></a></div><p>Хэрэв та энэ хүсэлтийг санамсаргүйгээр хийсэн бол ямар ч үйлдэл хийх шаардлагагүй; таны бүртгэлд ямар нэг өөрчлөлт орохгүй болно.</p><p>Request initiated from: ${ip}</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Нууц үг өөрчлөх хүсэлт" + }, + "ACCOUNT_VERIFY": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Баяр хүргэе. Таны бүртгэл ажилттай баталгаажлаа.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Арилжаа хийх</Button></a></div><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Бүртгэл баталгаажлаа" + }, + "ACCOUNT_UPGRADE": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Баяр хүргэе. Таны бүртгэлийн ${tier} түвшин болж дээшиллээ. Таны шимтгэл багасч, зарлага гаргах хэмжээ ихэслээ.</p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Арилжаанд оролцох</Button></a></div><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Бүртгэлийн түвшин дээшиллээ" + }, + "DEPOSIT_CANCEL": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Бид таны ${date} өдөр ${amount} дүнтэй ${currency} -р хийсэн орлогын гүйлгээг батлах боломжгүй байна. Тиймээс гүйлгээс систем автоматаар цуцалсан болно.</p><p>Танд асуух зүйл байвал, энэ и-мэйлд хариу бичнэ үү</p><p>Гүйлгээний №: ${txid}<br />Дүн: ${amount}<br />Төлөв: Татгалзсан</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${currency} орлого цуцлагдлаа" + }, + "DEPOSIT_PENDING": { + "html": "<div><div><p> Эрхэм хүндэт ${name} </p><p> Таны ${amount} ${currency} орлого, таны ${api_name} хэтэвчинд орохоор хүлээгдэж байна. Гүйлгээ баталгаажиж таны хэтэвчинд ортол түр хүлээнэ үү.</p><p>Дүн: ${amount} ${currency}<br />Төлөв: ${status}<br />Хаяг: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Сүлжээ: ${network}</span><br />Шимтгэл: ${fee} ${fee_coin}<br /><ul>${explorers}</ul></div><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${currency} орлого хүлээгдэж байна" + }, + "DEPOSIT_COMPLETED": { + "html": "<div><div><p> Эрхэм хүндэт ${name} </p><p> Таны ${amount} ${currency} орлого амжилттай хийгдэж, таны ${currency} хэтэвчинд орлоо.</p><p>Дүн: ${amount} ${currency}<br />Төлөв: ${status}<br />Хаяг: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Сүлжээ: ${network}</span><br />Шимтгэл: ${fee} ${fee_coin}<br /><ul>${explorers}</ul></div><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${currency} Орлого амжилттай" + }, + "WITHDRAWAL_PENDING": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Та ${amount} ${currency} зарлагын хүсэлт илгээсэн байна. Таны зарлагын хүсэлт тун удахгүй шийдэгдэх болно.</p><p>Дүн: ${amount} ${currency}<br />Шимтгэл: ${fee} ${fee_coin}<br />Төлөв: ${status}<br />Хаяг: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Сүлжээ: ${network}</span><br /><ul>${explorers}</ul><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${currency} зарлага хүлээгдэж байна" + }, + "WITHDRAWAL_COMPLETED": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Таны ${amount} ${currency} зарлагын хүсэлт амжилттай биеллээ.</p><p>Дүн: ${amount} ${currency}<br />Шимтгэл: ${fee} ${fee_coin}<br />Төлөв: ${status}<br />Хаяг: ${address}<br />Transaction ID: ${transaction_id}<br /><span id=\"network\">Сүлжээ: ${network}</span><br /><ul>${explorers}</ul><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${currency} зарлага амжилттай" + }, + "WITHDRAWAL_CANCEL": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Бид таны ${date} өдөр хийсэн ${amount} ${currency} хийсэн зарлагын хүсэлтийг баталгаажуулах боломжгүй байна. Таны зарлага цуцлагдаж, зарлагын дүн ${api_name} хэтэвч рүү буцаж орсон болно.</p><p>Танд тусламж хэрэгтэй байвал бидэнд и-мэйл илгээнэ үү</p><p>Гүйлгээ №: ${txid}<br />Дүн: ${amount}<br />Төлөв: Татгалзсан</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${currency} зарлага татгалзлаа" + }, + "WITHDRAWAL_REQUEST": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Та ${amount} ${currency} -г ${address} хаяг руу зарлагадахаар хүсэлт илгээсэн байна<br /><br />Дүн: ${amount} ${currency}<br />Шимтгэл: ${fee} ${fee_coin}<br />Хаяг: ${address}<br /><span id=\"network\">Сүлжээ: ${network}</span><br /><br />Энэ зарлагыг баталгаажуулахын тулд доорх линк дээр дарна уу.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Баталгаажуулах</Button></a></div><p>Хэрэв та энэ хүсэлтийг санамсаргүйгээр хийсэн бол ямар ч үйлдэл хийх шаардлагагүй; таны бүртгэлд ямар нэг өөрчлөлт орохгүй болно.</p><p>Хүсэлт гаргасан хаяг: ${ip}</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${currency} Зарлагын хүсэлт" + }, + "USER_VERIFICATION": { + "html": "<div><h3>Баталгаажуулалт шаардлагатай байна</h3><div> \"${email}\" хаягтай хэрэглэгч бичиг баримтаа баталгаажуулахаар илгээсэн байна. Бичиг баримтыг нь шалгана уу.</div></div>", + "title": "Хэрэглэгч баталгаажуулалт" + }, + "SUSPICIOUS_DEPOSIT": { + "html": "<div><h3>Сэжигтэй гүйлгээ</h3><div> ${email} и-мэйл хаягтай хэрэглэгч ${currency} сэжигтэй орлого хүлээн авлаа.<br />Transaction ID: ${txid}<h4>Transaction data:</h4><div>${data}</div></div></div>", + "title": "Сэжигтэй гүйлгээ" + }, + "INVALID_ADDRESS": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Таны ${amount} ${currency} зарлага буруу хаяг руу илгээгдсэн байсан тул цуцлагдлаа.</p><p>Хаяг: ${address}</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Зарлагын хаяг буруу байна" + }, + "USER_DEACTIVATED": { + "html": "<div><p>Таны ${email} идэвхигүй боллоо. Таны и-мэйл дахин хүсэлт гаргах хүртэл ашиглах боломжгүй болно.</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${type} аккаунт" + }, + "USER_ACTIVATED": { + "html": "<div><p>Таны ${email} баталгаажлаа. Та нэвтэрч орон бүртгэлээ үргэлжлүүлнэ үү.</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "${type} аккаунт" + }, + "DISCOUNT_UPDATE": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Таны хөнгөлөлтийн дүн ${rate}% боллоо. Энэ хөнгөлөлт нь таны захиалгын хураамжид хамаарна.</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Хөнгөлөлтийн дүн өөрчлөгдлөө" + }, + "BANK_VERIFIED": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Таны банкны дансыг баталгаажууллаа. Та одоо орлого, зарлага гаргах боломжтой боллоо</p><div><strong>Баталгаажсан банкны данс:</strong>${list_detail_bank_account}</div><p><a href=\"${link_verification}\">Баталгаажсан банкны дансаа харахын тулд баталгаажуулалтын хэсэг рүү очно уу</a></p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Банк баталгаажлаа" + }, + "USER_ID_VERIFICATION_REJECT": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Таны бичиг баримтыг шалгаж үзээд баталгаажуулах боломжгүй байна. Та доорх мессежийн дагуу дахин илгээнэ үү:</p><p>Шалтгаан: ${message}</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Үнэмлэх баталгаажуулалт амжилтгүй" + }, + "USER_BANK_VERIFICATION_REJECT": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Таны банкны данс бүртгүүлэх хүсэлтийг шалгаж үзээд баталгаажуулах боломжгүй байна. Шалгааныг дор бичив:</p><p>Шалтгаан: ${message}</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Банкны мэдээлэл баталгаажуулалт амжилтгүй" + }, + "PASSWORD_CHANGED": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Энэ имэйл таныг саяхан өөрийн акаунтын нууц үгээ өөрчилсөн болохыг баталгаажуулж байна. Цаашид арга хэмжээ авах шаардлагагүй.<br />Хэрэв та энэ өөрчлөлтийг хийгээгүй бол бидэнтэй шууд холбогдоно уу.<br /></p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Нууц үг өөрчлөгдлөө" + }, + "CHANGE_PASSWORD": { + "html": "<div><p> Эрхэм хүндэт ${name} </p><p>Та нууц үг солих хүсэлт гаргасан байна.<br />Нууц үгээ өөрчилсныг баталгаажуулахын тулд доор линк дээр дарна уу.<br /></p><div style=\"padding-top: 10px; margin-bottom: 10px;\"><a href=\"${link}\" target=\"_blank\"><Button style=\"cursor: pointer; background-color: #333333; color: white; border: none; padding: 1rem; text-transform: uppercase; cursor: pointer !important; font-size: 14px; min-width: 11rem;\">Нууц үг өөрчилснийг батлах</Button></a></div><p>Хэрэв энэ хүсэлтийг алдаатай хийсэн бол та энэ и-мэйлийг тоохгүй байж болно. Таны бүртгэлд ямар нэг өөрчлөлт орохгүй</p><p>Request initiated from: ${ip}</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Нууц үг өөрчлөх баталгаажуулалт" + }, + "DOC_REJECTED": { + "html": "<div><p>Эрхэм хүндэт ${email} </p><p>Харамсалтай нь таны илгээсэн KYC баримт бичгүүдийг баталгаажуулах боломжгүй байна.<br>Таны баримт бичгүүдээс татгалзсан шалтгааныг доор жагсаав.<br></p><div><ul>${doc_information}</ul></div><p>Хэрэв та эдгээр шалтгааныг буруу гэж үзвэл энэ имэйлд хариу илгээнэ үү.<br>Эсвэл, бичиг баримтаа шаардлагын дагуу дахин илгээнэ үү.</p><p> Хүндэтгэсэн<br> ${api_name} team </p</div>", + "title": "KYC бичиг баримт татаглзлаа" + }, + "DOC_VERIFIED": { + "html": "<div><p>Эрхэм хүндэт ${email} </p><p>Таны бичиг баримт баталгаажсан байна.<br>Та одоо баталгаажуулалт шаардсан бүх хэсэгт хандах боломжтой.</p><ul>${doc_information}</ul><p>Та баталгаажуулсан бичиг баримтаа шалгахыг хүсвэл <a href=\"${link}\" target=\"_blank\">Баталгаажуулах хуудас</a> руу орно уу.</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "KYC бичиг баримт баталгаажлаа" + }, + "CONFIRM_EMAIL": { + "html": "<div><p><p>Эрхэм хүндэт ${name} </p></p><p>Та өөрийн бүртгэлийн аюулгүй байдалтай холбоотой хүсэлт гаргасан байна. Үйлдлийг баталгаажуулахын тулд та доорх кодыг оруулаарай.<br /><p style=\"font-size: 1.2rem; text-align: center;\">${code}</p>Хэрэв та энэ хүсэлтийг гаргаагүй бол энэ тухай нэн даруй мэдэгдэж, нэвтрэх мэдээллээ аль болох хурдан солино уу.</p><p> Хүндэтгэсэн<br> ${api_name} team </p></div>", + "title": "Аюулгүй байдлын баталгаажуулалт" + } + } +} \ No newline at end of file diff --git a/web/src/config/lang/mn.json b/web/src/config/lang/mn.json new file mode 100644 index 0000000000..52fa658bde --- /dev/null +++ b/web/src/config/lang/mn.json @@ -0,0 +1,1300 @@ +{ + "APP_TITLE": "HollaEx", + "APP_SUB_TITLE": "Нээлттэй Крипто Эксченж", + "LOGOUT_CONFIRM_TEXT": "Та гарахдаа итгэлтэй байна уу", + "ADD_TRADING_PAIR": "Макет сонгох", + "CANCEL_BASE_WITHDRAWAL": "{0} зарлага цуцлах", + "CANCEL_WITHDRAWAL": "Зарлага цуцлах", + "CANCEL_WITHDRAWAL_POPUP_CONFIRM": "Та дараах хүлээгдэж буй зарлагыг цуцлахдаа итгэлтэй байна уу:", + "TIMESTAMP_FORMAT": "YYYY/MM/DD HH:mm:ss", + "NO_ACTIVE_ORDERS": "Захиалга байхгүй байна. Pro эсвэл Quick trade хуудсан дээр захиалгаа өгөөрэй", + "NO_ACTIVE_TRADES": "Арилжаа одоогоор хийгдээгүй байна", + "NO_ACTIVE_DEPOSITS": "Орлого одоогоор байхгүй байна.", + "NO_ACTIVE_WITHDRAWALS": "Зарлага одоогоор байхгүй байна", + "HOUR_FORMAT": "HH:mm:ss", + "LOGIN_TEXT": "Нэвтрэх хэсэг", + "SIGN_IN": "Нэвтрэх", + "SIGNUP_TEXT": "Бүртгүүлэх", + "REGISTER_TEXT": "Бүртгүүлэх", + "ACCOUNT_TEXT": "Бүртгэл", + "CLOSE_TEXT": "Хаах", + "COPY_TEXT": "Хуулбарлах", + "COPY_SUCCESS_TEXT": "Амжилттай хуулбарлалаа", + "CANCEL_SUCCESS_TEXT": "Амжилттай цуцаллаа!", + "ADD_FILES": "ФАЙЛ НЭМЭХ", + "OR_TEXT": "Эсвэл", + "CONTACT_US_TEXT": "Холбоо барих", + "HELPFUL_RESOURCES_TEXT": "Хэрэгтэй мэдээллүүд", + "HELP_RESOURCE_GUIDE": { + "CONTACT_US": "холбоо барих", + "TEXT": "Танд тусламж хэрэгтэй болон асуух асуулт байвал {0} хандах, эсвэл и-мэйл илгээнэ үү." + }, + "HELP_TELEGRAM_TEXT": "API зааврыг нээж үзнэ үү:", + "HELP_TELEGRAM_LINK": "https://apidocs.hollaex.com", + "NEED_HELP_TEXT": "Тусламж хэрэгтэй юу?", + "HELP_TEXT": "тусламж", + "SUCCESS_TEXT": "Амжилттай", + "ERROR_TEXT": "Алдаа", + "PROCEED": "ҮРГЭЛЖЛҮҮЛЭХ", + "EDIT_TEXT": "Засварлах", + "BACK_TEXT": "Буцах", + "NO_OPTIONS": "Сонголт байхгүй байна", + "SECONDS": "хором", + "VIEW_MARKET": "маркет харах", + "GO_TRADE": "Арилжаанд оролцох", + "VIEW_INFO": "Мэдээллийн хуудас зочлох", + "APPLY_HERE": "Энд дарна уу", + "CONVERT": "Хөрвүүлэх", + "TO": "Юунд", + "HOME": { + "MAIN_TITLE": "Крипто арилжааны бирж", + "MAIN_TEXT": "Та өөрийн и-мэйл хаягаар бүртгүүлээд 24 цагийн турш крипто хөрөнө хялбархан худалдан авах, арилжаалах боломжтой", + "TRADE_CRYPTO": "Арилжаанд оролцох", + "VIEW_EXCHANGE": "Трэйдинг платформ" + }, + "FOOTER": { + "FOOTER_LANGUAGE_TEXT": "ХЭЛ", + "TERMS_OF_SERVICE": "Үйлчилгээний нөхцөл", + "PRIVACY_POLICY": "Нууцлалын бодлого", + "CLICK_HERE": "энд дарж", + "VISIT_HERE": "энд дарж зочилно уу" + }, + "ACCOUNTS": { + "TITLE": "Бүртгэл", + "TAB_VERIFICATION": "Баталгаажуулалт", + "TAB_SECURITY": "Аюулгүй байдал", + "TAB_SETTINGS": "Тохиргоо", + "TAB_APPS": "Аппликэйшн", + "TAB_WALLET": "Хэтэвч", + "TAB_SUMMARY": "Ерөнхий", + "TAB_HISTORY": "Түүх", + "TAB_SIGNOUT": "Гарах", + "TAB_STAKE": "Стэйкинг", + "TAB_FIAT": "Бэлэн мөнгөний удирдлага" + }, + "CONTACT_FORM": { + "CATEGORY_LABEL": "Ангилал", + "CATEGORY_PLACEHOLDER": "Таны асуудалд тохирох ангиллыг сонгоно уу", + "CATEGORY_OPTIONS": { + "OPTION_VERIFY": "Хэрэглэгч баталгаажуулалт", + "OPTION_LEVEL": "Түвшин ахиулах", + "OPTION_DEPOSIT": "Орлого ба зарлага", + "OPTION_BUG": "Алдааг мэдээлэх", + "OPTION_PERSONAL_INFO": "Хувийн мэдээлэл өөрчлөх", + "OPTION_BANK_TRANSFER": "Банкны шилжүүлэг", + "OPTION_REQUEST": "HollaEx бирж дээр урилга илгээх" + }, + "SUBJECT_LABEL": "Гарчиг", + "SUBJECT_PLACEHOLDER": "Асуудлын тухай гарчиг оруулах", + "DESCRIPTION_LABEL": "Тайлбар", + "DESCRIPTION_PLACEHOLDER": "Асуудлын дэлгэрэнгүй тайлбар", + "ATTACHMENT_LABEL": "Хасвралт оруулах (хамгийн ихдээ 3)", + "ATTACHMENT_PLACEHOLDER": "Асуудалд холбоотой файлууд. PDF, JPG, PNG ба GIF файл оруулах боломжтой", + "SUCCESS_TITLE": "Зурвас илгээлээ", + "SUCCESS_MESSAGE_1": "Таны асуудлыг хэрэглэгчийн тусламжийн төвд илгээлээ.", + "SUCCESS_MESSAGE_2": "Та 1-3 өдрийн дараа хариу авах болно." + }, + "DEPOSIT": { + "CRYPTO_LABELS": { + "ADDRESS": "Таны {0} хүлээн авах хаяг", + "DESTINATION_TAG": "Таны {0} хүлээн авах tag", + "MEMO": "Таны {0} memo" + }, + "QR_CODE": "Энэ QR кодыг танд мөнгө илгээхийг хүссэн хүн сканнердаж болно", + "NO_DATA": "Мэдээлэл байхгүй байна" + }, + "LOGIN": { + "LOGIN_TO": "{0} нэвтрэх", + "CANT_LOGIN": "Нэвтэрч чадахгүй байна уу?", + "NO_ACCOUNT": "Бүртгэлгүй юу?", + "CREATE_ACCOUNT": "Энд дарж бүртгүүлээрэй", + "HELP": "Тусламж" + }, + "FORM_FIELDS": { + "EMAIL_LABEL": "И-мэйл", + "EMAIL_PLACEHOLDER": "И-мэйл хаягаа энд оруулна", + "PASSWORD_LABEL": "Нууц үг", + "PASSWORD_PLACEHOLDER": "Нууц үгээ оруулна уу", + "PASSWORD_REPEAT_LABEL": "Retype your password", + "PASSWORD_REPEAT_PLACEHOLDER": "Retype your password" + }, + "VALIDATIONS": { + "OTP_LOGIN": "OTP код оруулна уу", + "CAPTCHA": "Идэвхитэй байх хугацаа дууссан. Хуудсыг дахин уншуулна уу", + "FROZEN_ACCOUNT": "Таны бүртгэл түгжигдсэн байна", + "INVALID_EMAIL": "И-мэйл хаяг буруу байна", + "TYPE_EMAIL": "И-мэйл хаяг оруулна уу", + "REQUIRED": "Бөглөх шаардлагатай", + "INVALID_DATE": "Огноо буруу байна", + "INVALID_PASSWORD": "Iууц үг буруу байна. Доод тал нь 8 тэмдэгттэй, тоо болон тусгай тэмдэгт орно.", + "INVALID_PASSWORD_2": "Нууц үг буруу байна. Доод тал нь 8 тэмдэгттэй, тоо болон тусгай тэмдэгт орно.", + "INVALID_CURRENCY": "Дараах {0} хаяг буруу байна ({1})", + "INVALID_BALANCE": "Таны баланс ({0}) хүрэлцэхгүй байна: ({1}).", + "MIN_VALUE": "Боломжит доод дүн {0}.", + "MAX_VALUE": "Боломжит дээд дүн {0}.", + "MIN_VALUE_NE": "Боломжит доод дүн {0}.", + "MAX_VALUE_NE": "Боломжит дээд дүн {0}.", + "INSUFFICIENT_BALANCE": "Үлдэгдэл хүрэлцэхгүй байна", + "PASSWORDS_DONT_MATCH": "Нууц үг буруу байна.", + "USER_EXIST": "И-мэйл хаяг бүртгэлтэй байна", + "ACCEPT_TERMS": "Үйлчилгээний нөхцөл, нууцлалын бодлогыг зөвшөөрөөгүй байна", + "STEP": "Буруу утга: {0}", + "ONLY_NUMBERS": "Зөвхөн тоо оруулна уу" + }, + "NOTIFICATIONS": { + "BUTTONS": { + "OKAY": "Okay", + "START_TRADING": "арилжаа хийж эхлэх", + "SEE_HISTORY": "түүх харах" + }, + "DEPOSITS": { + "TITLE_RECEIVED": "{0} орлого хүлээн авлаа", + "TITLE_INCOMING": "{0} орлого ирж байна", + "SUBTITLE_RECEIVED": "Та {0} орлого хүлээн авлаа", + "SUBTITLE_INCOMING": "Танд {0} ирж байна", + "INFORMATION_PENDING_1": "Таны {0} арилжаанд орохын тулд ядаж 1 баталгаажуулалт хэрэгтэй.", + "INFORMATION_PENDING_2": "Энэ гүйлгээ нь 10-30 минут болох бөгөөд, амжилттай болонгуут бид танд мэдэгдэх болно." + } + }, + "REFERRAL_SUCCESS": { "TITLE": "Хүсэлт илгээсэн", "BUTTON_TEXT": "Okay" }, + "OTP_FORM": { + "OTP_FORM_TITLE": "Баталгаажуулах код оруулна уу", + "OTP_FORM_INFO": "Үргэлжлүүлэхийн тулд 6 оронтой кодоо оруулна уу", + "OTP_FORM_SUBNOTE_LINE_1": "Your code is also known as a two-factor authenticator (2FA) or OTP code.", + "OTP_FORM_SUBNOTE_LINE_2": "Хэрэв та кодоо алдсан бол оператортой холбогдоно уу.", + "OTP_LABEL": "OTP Code", + "OTP_PLACEHOLDER": "Баталгаажуулах код оруулна уу", + "OTP_TITLE": "Баталгаажуулах код", + "OTP_BUTTON": "илгээх", + "ERROR_INVALID": "OTP Code буруу байна" + }, + "EMAIL_CODE_FORM": { + "TITLE": "Аюулгүй байдлын кодыг оруулна уу", + "LABEL": "Код оруулах (имэйлээ шалгана уу)", + "PLACEHOLDER": "Имэйлд илгээсэн кодыг оруулна уу", + "FORM_TITLE": "Таны имэйл рүү илгээсэн кодыг OTP кодын хамт оруулна уу.", + "BUTTON": "Илгээх", + "ERROR_INVALID": "Таны оруулсан код буруу байна. Дахин оролдоно уу", + "OTP_LABEL": "2FA код (OTP)", + "OTP_PLACEHOLDER": "2FA кодоо оруулна уу" + }, + "QUICK_TRADE_COMPONENT": { + "TITLE": "Хялбар арилжаа", + "BUTTON": "Захиалга батлах", + "INFO": "Крипто арилжаа хийх хурдан бөгөөд хямд", + "CHANGE_TEXT": "өөрчлөлт", + "HIGH_24H": "24Ц ДЭЭД", + "LOW_24H": "24Ц ДООД", + "BEST_BID": "BEST BID", + "BEST_ASK": "BEST ASK", + "FOOTER_TEXT": "Хялбар арилжаанд taker fee бодогдоно", + "FOOTER_TEXT_1": "Эх сурвалж", + "GO_TO_TEXT": "Очих", + "SOURCE_TEXT": "Брокер OTC гэрээ" + }, + "PREVIOUS_PAGE": "өмнөх хуудас", + "NEXT_PAGE": "дараагийн хуудас", + "WALLET": { + "LOADING_ASSETS": "Уншиж байна...", + "TOTAL_ASSETS": "Нийт хөрөнгө", + "AVAILABLE_WITHDRAWAL": "Арилжаалах боломжтой", + "ORDERS_PLURAL": "захиалга", + "ORDERS_SINGULAR": "захиалга", + "HOLD_ORDERS": "Танд {0} нээлттэй {1} байгаа тул таны {4} үлдэгдэл дээр {2} {3} хадгалагдах болно" + }, + "REQUEST_RESET_PASSWORD": { + "TITLE": "Бүртгэл сэргээх", + "SUBTITLE": "Доорх мэдээллээр сэргээнэ үү", + "SUPPORT": "Холбоо барих", + "BUTTON": "Сэргээх линк илгээх" + }, + "REQUEST_RESET_PASSWORD_SUCCESS": { + "TITLE": "Нууц үг сэргээх линк илгээлээ", + "TEXT": "Таны бүртгэлтэй и-мэйл хаяг руу нууц үг сэргээх заавар явууллаа." + }, + "RESET_PASSWORD": { + "TITLE": "Шинэ нууц үг", + "SUBTITLE": "Шинэ нууц үг", + "BUTTON": "Шинэ нууц үг" + }, + "RESET_PASSWORD_SUCCESS": { + "TEXT_1": "Та нууц үгээ амжилттай шинэчиллээ", + "TEXT_2": "Доорх хэсгийг ашиглан нэвтэрч орно уу." + }, + "SIGN_UP": { + "SIGNUP_TO": "{0} -д бүртгүүлэх", + "NO_EMAIL": "И-мэйл хүлээн аваагүй юу?", + "REQUEST_EMAIL": "Энд дарж и-мэйл дахин авна уу", + "HAVE_ACCOUNT": "Бүртгэлтэй юу?", + "GOTO_LOGIN": "Энд дарж нэвтрэх хэсэгт очно уу", + "AFFILIATION_CODE": "Найзын код (заавал биш)", + "AFFILIATION_CODE_PLACEHOLDER": "Найзын кодоо оруулна уу", + "TERMS": { + "terms": "Үйлчилгээний нөхцөл", + "policy": "Нууцлалын бодлого", + "text": "Би {0} болон {1} -г уншсан бөгөөд, хүлээн зөвшөөрч байна" + } + }, + "VERIFICATION_TEXTS": { + "TITLE": "И-мэйл илгээлээ", + "TEXT_1": "Таны и-мэйл хаяг руу баталгаажуулах линк илгээлээ. Та и-мэйлээ шалгана уу", + "TEXT_2": "Хэрэв танд и-мэйл очоогүй бол доорх линк дээр дарж дахин илгээнэ үү." + }, + "VERIFICATION_EMAIL_REQUEST": { + "TITLE": "И-мэйл дахин илгээх хүсэлт", + "BUTTON": "И-мэйл илгээх", + "SUBTITLE": "Доорх имэйлээр баталгаажуулах өөр хүсэлт гаргана уу", + "SUPPORT": "Холбоо барих" + }, + "VERIFICATION_EMAIL_REQUEST_SUCCESS": { + "TITLE": "И-мэйл дахин илгээлээ", + "TEXT_1": "Танд удахгүй баталгаажуулах и-мэйл очно. Хэрэв ирээгүй бол тусламжын оператортой холбогдоно уу" + }, + "VERIFICATION_EMAIL": { + "INVALID_UUID": "Код буруу байна", + "TEXT_1": "Та и-мэйл хаягаа амжилттай баталгаажууллаа.", + "TEXT_2": "Та одоо системд нэвтрэх боломжтой" + }, + "USER_VERIFICATION": { + "INFO_TXT": "Энэ хэсэгт та баталгаажуулалт болон бүртгэл шинэчлэх хэсгийг харж болно.", + "INFO_TXT_1": "Доорх хэсэг тус бүрд шаардлагатай холбогдох мэдээллийг оруулна уу. Зөвхөн бүх хэсгүүдийг бөглөж дууссаны дараа таны мэдээллийг хянаж, бүртгэл шинэчлэлтийг зөвшөөрөх болно..", + "COMPLETED": "Дууссан", + "PENDING_VERIFICATION": "Баталгаажуулалт хүлээгдэж байна", + "TITLE_EMAIL": "И-мэйл", + "MY_EMAIL": "И-мэйл", + "TITLE_USER_DOCUMENTATION": "Бүртгэл", + "TITLE_ID_DOCUMENTS": "Файл оруулах", + "TITLE_BANK_ACCOUNT": "Банкны мэдээлэл", + "TITLE_MOBILE_PHONE": "Утас", + "TITLE_PERSONAL_INFORMATION": "Хувийн мэдээлэл", + "VERIFY_EMAIL": "И-мэйл баталгаажуулах", + "VERIFY_MOBILE_PHONE": "Утас баталгаажуулах", + "VERIFY_USER_DOCUMENTATION": "Бичиг баримт баталгаажуулах", + "VERIFY_ID_DOCUMENTS": "Иргэний үнэмлэх баталгаажуулах", + "TITLE_IDENTITY": "Бүртгэл", + "TITLE_MOBILE": "Гар утас", + "TITLE_BANK": "Банк", + "CHANGE_VALUE": "Мэдээлэл өөрчлөх", + "PENDING_VERIFICATION_PERSONAL_INFORMATION": "Таны мэдээллийг шалгаж байна", + "PENDING_VERIFICATION_DOCUMENTS": "Таны бичиг баримтыг шалгаж байна", + "GOTO_VERIFICATION": "Баталгаажуулалт руу очих", + "GOTO_WALLET": "Хэтэвч рүү очих", + "INCOMPLETED": "Дутуу", + "BANK_VERIFICATION": "Банк баталгаажуулалт", + "IDENTITY_VERIFICATION": "Бүртгэл баталгаажуулалт", + "PHONE_VERIFICATION": "Утас баталгаажуулалт", + "DOCUMENT_VERIFICATION": "Бичиг баримт баталгаажуулалт", + "START_BANK_VERIFICATION": "Банк баталгаажуулалт эхлэх", + "START_IDENTITY_VERIFICATION": "Бүртгэл баталгаажуулалт эхлэх", + "START_PHONE_VERIFICATION": "Утас баталгаажуулалт эхлэх", + "START_DOCUMENTATION_SUBMISSION": "Бичиг баримт баталгаажуулалт эхлэх", + "GO_BACK": "Буцах", + "BANK_VERIFICATION_TEXT_1": "Та энэ хэсэгт өөрийн дансны дугаараа оруулж баталгаажуулна уу.", + "BANK_VERIFICATION_TEXT_2": "Банкаа баталгаажуулснаар та дараах үйлдлүүдийг хийх боломжтой болно:", + "BASE_WITHDRAWAL": "Мөнгөн зарлага", + "BASE_DEPOSITS": "Мөнгөн орлого", + "ADD_ANOTHER_BANK_ACCOUNT": "Данс нэмэх", + "BANK_NAME": "Банкны нэр", + "ACCOUNT_NUMBER": "Дансны дугаар", + "BANK_VERIFICATION_HELP_TEXT": "Энэ хэсгийг баталгаажуулахын тулд та {0} хэсгийг дуусгасан байх ёстой.", + "DOCUMENT_SUBMISSION": "Бичиг баримт илгээх", + "REVIEW_IDENTITY_VERIFICATION": "Бүртгэл баталгаажуулалт хянах", + "PHONE_DETAILS": "Утасны мэдээлэл", + "PHONE_COUNTRY_ORIGIN": "Улс", + "MOBILE_NUMBER": "Утас", + "DOCUMENT_PROOF_SUBMISSION": "Бичиг баримт илгээх", + "START_DOCUMENTATION_RESUBMISSION": "Бичиг баримт дахин илгээх", + "SUBMISSION_PENDING_TXT": "*Энэ хэсгийг та өмнө нь илгээсэн байна. Өөрчлөлт хийж, дахин илгээх нь таны өмнөх мэдээллийг дарж бичих болно.", + "CUSTOMER_SUPPORT_MESSAGE": "Операторын хариу", + "DOCUMENT_PENDING_NOTE": "Таны бичиг баримтыг илгээсэн бөгөөд шалгахаар хүлээгдэж байна.", + "DOCUMENT_VERIFIED_NOTE": "Таны бичиг баримт баталгаажсан байна.", + "NOTE_FROM_VERIFICATION_DEPARTMENT": "Баталгаажуулалтын хариу", + "CODE_EXPIRES_IN": "Код дуусах хугацаа", + "EMAIL_VERIFICATION": "Баталгаажуулах и-мэйл илгээх", + "VERIFICATION_SENT": "Баталгаажуулалт илгээсэн", + "VERIFICATION_SENT_INFO": "И-мэйл хаягаа шалгаад та баталгаажуулалтаа хийнэ үү.", + "OKAY": "Ok", + "USER_DOCUMENTATION_FORM": { + "FORM_FIELDS": { + "FULL_NAME_LABEL": "Таны нэр", + "FULL_NAME_PLACEHOLDER": "Иргэний үнэмлэх дээрх өөрийн нэрээ бичнэ үү", + "GENDER_LABEL": "Хүйс", + "GENDER_PLACEHOLDER": "Өөрийн хүйсээ оруулна уу", + "GENDER_OPTIONS": { "MAN": "Эрэгтэй", "WOMAN": "Эмэгтэй" }, + "NATIONALITY_LABEL": "Иргэншил", + "NATIONALITY_PLACEHOLDER": "Та өөрийн иргэншилээ бичнэ үү", + "DOB_LABEL": "Төрсөн огноо", + "COUNTRY_LABEL": "Оршин суугаа улс", + "COUNTRY_PLACEHOLDER": "Одоогийн оршин суугаа улсаа оруулна уу", + "CITY_LABEL": "Хот", + "CITY_PLACEHOLDER": "Амьдарч буй хотоо бичнэ үү", + "ADDRESS_LABEL": "Хаяг", + "ADDRESS_PLACEHOLDER": "Хаяг", + "POSTAL_CODE_LABEL": "Зип код", + "POSTAL_CODE_PLACEHOLDER": "Зип кодоо оруулна уу", + "PHONE_CODE_LABEL": "Улс", + "PHONE_CODE_PLACEHOLDER": "Утасны дугаар байршиж буй улсаа бичнэ үү", + "PHONE_CODE_DISPLAY": "({0}) {1}", + "PHONE_NUMBER_LABEL": "Утасны дугаар", + "PHONE_NUMBER_PLACEHOLDER": "Утасны дугаараа оруулна уу", + "CONNECTING_LOADING": "Холбогдож байна", + "SMS_SEND": "SМS илгээх", + "SMS_CODE_LABEL": "SМS код", + "SMS_CODE_PLACEHOLDER": "SМS кодоо оруулна уу" + }, + "INFORMATION": { + "TEXT": "АНХААРУУЛГА: Иргэний үнэмлэх дээрхтэй яг ижлээр нэрээ оруулна уу (овог нэр, өөрийн нэр). Та бизнес эрхэлдэг бол байгууллагын дансны хэрэглэгчийн дэмжлэгтэй холбогдоно уу.", + "TITLE_PERSONAL_INFORMATION": "Хувийн мэдээлэл", + "TITLE_PHONE": "Утас", + "PHONE_VERIFICATION_TXT": "Та утасны мэдээллээ баталгаажуулснаар аливаа эрсдлээс сэргийлэгдэх болно.", + "PHONE_VERIFICATION_TXT_1": "Таны орлого зарлагын мэдээлэл таны гар утсанд очно.", + "PHONE_VERIFICATION_TXT_2": "Цаашид LAN утасны дугаараа оруулах, өөрийн мэдээлэл, хаягаа баталгаажуулна уу (заавал биш)." + } + }, + "ID_DOCUMENTS_FORM": { + "VALIDATIONS": { + "ID_NUMBER": "Иргэний үнэмлэхний төрөл сонгоно уу", + "ISSUED_DATE": "Олгосон огноо бичнэ үү", + "EXPIRATION_DATE": "Дуусах огноо бичнэ үү", + "FRONT": "Иргэний үнэмлэхний урд талын зургийг оруулна уу", + "PROOF_OF_RESIDENCY": "Иргэний үнэмлэхний ард тал эсвэл оршин суугаа хаягийн тодорхойлолт оруулна уу", + "SELFIE_PHOTO_ID": "Иргэний үнэмлэхээ гартаа барьсан сельфи зураг авч оруулна уу" + }, + "FORM_FIELDS": { + "ID_NUMBER_LABEL": "Регистрийн дугаар", + "ID_NUMBER_PLACEHOLDER": "Регистрийн дугаараа оруулна уу", + "ISSUED_DATE_LABEL": "Олгосон огноо", + "EXPIRATION_DATE_LABEL": "Дуусах огноо", + "FRONT_LABEL": "Иргэний үнэмлэх эсвэл пасспорт", + "FRONT_PLACEHOLDER": "Хуулбараа энд оруулна уу", + "POR_LABEL": "Хаягийн баталгаажуулалт", + "POR_PLACEHOLDER": "Иргэний үнэмлэхний ард тал эсвэл Хаягийн тодорхойлолт", + "SELFIE_PHOTO_ID_LABEL": "Иргэний үнэмлэхтэй сельфи зураг", + "SELFIE_PHOTO_ID_PLACEHOLDER": "Иргэний үнэмлэхтэй сельфи зургаа оруулна уу" + }, + "INFORMATION": { + "PROOF_OF_RESIDENCY": "Хаягийн тодорхойлолт", + "ID_SECTION": { + "TITLE": "Дараах шаардлагыг хангасан зураг оруулна уу:", + "LIST_ITEM_0": "Бүх документын нийт хэмжээ {0}mb-аас хэтрэхгүй байх ёстой", + "LIST_ITEM_1": "ӨНДӨР ЧАНАР (өнгөт зураг, 300dpi ба түүнээс дээш нягтаршилтай, Зураг тус бүр 2MB буюу түүнээс бага хэмжээтэй байх ёстой) Хэмжээг өөрчлөхийн тулд Webtool ашиглана уу.", + "LIST_ITEM_2": "ТОД ХАРАГДАХУЙЦ БАЙХ.", + "LIST_ITEM_3": "ХҮЧИНТЭЙ БАЙХ", + "WARNING_1": "Зөвхөн хүчинтэй бичиг баримтыг хүлээн авна; Эдгээр баримт бичгийн өндөр чанартай зураг эсвэл сканнердсан зургийг хүлээн авах боломжтой:", + "WARNING_3": "Энэ хэсэгт та иргэний үнэмлэхнйи ар талыг оруулж болно.", + "VIOLATION_ERROR": "Таны байршуулсан бүх баримт бичгийн нийт хэмжээ {0}mb-ын байршуулах хязгаараас хэтэрсэн байна. Үргэлжлүүлэхийн тулд жижиг файлуудыг байршуулна уу." + }, + "POR": { + "SECTION_1_TEXT_1": "Бүртгэл баталгаажуулалтаа удаашруулахгүйн тулд дараах зүйлсийг анхаарна уу:", + "SECTION_1_TEXT_2": "НЭР, ХАЯГ, ОЛГОСОН ОГНОО, ДУУСАХ ОГНОО харагдахуйц байх.", + "SECTION_1_TEXT_3": "Оршин суугаа хаягийн тодорхойлолт 3 САРЫН ДОТОР авсан байх.", + "SECTION_1_TEXT_4": "ӨНДӨР ЧАНАРТАЙ байх (хамгийн багадаа 300 DPI)", + "SECTION_2_TITLE": "ЗӨВШӨӨРӨГДӨХ ХАЯГИЙН ТОДОРХОЙЛОЛТ:", + "SECTION_2_LIST_ITEM_1": "Банкны тодорхойлолт.", + "SECTION_2_LIST_ITEM_2": "E-Mongolia тодорхойлолт.", + "SECTION_2_LIST_ITEM_3": "Цахилгаан, Орон сууцны төлбөрийн баримт.", + "WARNING": "Иргэний үнэмлэхний ард тал, өөрчлөлтийн хуудсын хамт." + }, + "SELFIE": { + "TITLE": "Сельфи зураг оруулах заавар", + "INFO_TEXT": "Паспортоо барьж байгаа зургаа оруулна уу. Ижил зурган дээр солилцооны url-ийн лавлагаа, өнөөдрийн огноо болон таны гарын үсгийг харуулна. Таны царай тодорхой харагдаж, таны ID-ны дэлгэрэнгүй унших боломжтой эсэхийг шалгаарай.", + "REQUIRED": "Анхаарах зүйлс:", + "INSTRUCTION_1": "Царай бүтэн харагдах", + "INSTRUCTION_2": "Бичиг баримт тод харагдах", + "INSTRUCTION_3": "Ncx.mn гэж бичих", + "INSTRUCTION_4": "Өнөөдрийн огноо бичих", + "INSTRUCTION_5": "Гарын үсэг зурах", + "WARNING": "Өөр паспорттой сельфиг татгалзах болно" + } + } + }, + "BANK_ACCOUNT_FORM": { + "VALIDATIONS": { + "ACCOUNT_NUMBER_MAX_LENGTH": "Дансны дугаар 50 тэмдэгтээс бага байх ёстой" + }, + "FORM_FIELDS": { + "BANK_NAME_LABEL": "Банкны нэр", + "BANK_NAME_PLACEHOLDER": "Банкны нэрээ бичнэ үү", + "ACCOUNT_NUMBER_LABEL": "Дансны дугаар", + "ACCOUNT_NUMBER_PLACEHOLDER": "Дансны дугаараа бичнэ үү", + "ACCOUNT_OWNER_LABEL": "Дансны нэр", + "CARD_NUMBER_PLACEHOLDER": "16 оронтой картын дугаараа оруулна уу" + } + }, + "WARNING": { + "TEXT_1": "Та баталгаажуулалтаа хийснээр дараах эрхтэй болно:", + "LIST_ITEM_1": "Өдрийн зарлагын хэмжээ нэмэгдэнэ", + "LIST_ITEM_2": "Өдрийн орлогын хэмжээ нэмэгдэнэ", + "LIST_ITEM_3": "Шимтгэл буурна" + }, + "TITLE_PAYMENT": "Төлбөрийн хэлбэр", + "PAYMENT_VERIFICATION": "Төлбөрийн баталгаажуулалт", + "START_PAYMENT_VERIFICATION": "Баталгаажуулалтыг эхлүүлэх", + "PAYMENT_VERIFICATION_TEXT_1": "Төлбөрийн дансны дэлгэрэнгүй мэдээллийг доор нэмнэ үү.", + "PAYMENT_VERIFICATION_TEXT_2": "Тэдгээрийг баталгаажуулсны дараа таны дансанд янз бүрийн мөнгөн тэмдэгтээр мөнгө авах, байршуулах илүү олон арга бий болно.", + "ADD_ANOTHER_PAYMENT_METHOD": "ӨӨР ТӨЛБӨРИЙН АРГА НЭМЭХ", + "PAYMENT_VERIFICATION_HELP_TEXT": "Энэ хэсгийг баталгаажуулахын тулд та {0} хэсгийг бөглөх ёстой." + }, + "USER_SETTINGS": { + "TITLE_TEXT_1": "Энэ хэсэгт та ерөнхий тохиргоог өөрчлөх боломжтой.", + "TITLE_TEXT_2": "Өөрчлөлтөө хадгалахын тулд Хадгалах товчлуурыг дарна уу.", + "TITLE_NOTIFICATION": "Мэдэгдэл", + "TITLE_INTERFACE": "Интерфэйс", + "TITLE_LANGUAGE": "Хэл", + "TITLE_CHAT": "Чат", + "TITLE_AUDIO_CUE": "Аудио ", + "TITLE_MANAGE_RISK": "Эрсдлийн удирдлага", + "ORDERBOOK_LEVEL": "Захиалгын түвшин (Ихдээ 20)", + "SET_TXT": "ТОХИРУУЛАХ", + "CREATE_ORDER_WARING": "Захиалгын анхааруулга үүсгэх", + "RISKY_TRADE_DETECTED": "Эрсдэлтэй арилжаа мэдрэгдлээ", + "RISKY_WARNING_TEXT_1": "Энэ захиалга нь таны тохируулсан {0} хэмжээнээс хэтэрч байна.", + "RISKY_WARNING_TEXT_2": "(Нийт хөрөнгийн {0} хувь)", + "RISKY_WARNING_TEXT_3": " Та энэ арилжааг хийхийг үнэхээр хүсч байгаагаа шалгаж, баталгаажуулна уу.", + "GO_TO_RISK_MANAGMENT": "ЭРСДЛИЙН УДИРДЛАГА", + "CREATE_ORDER_WARING_TEXT": "Таны арилжааны захиалга таны багцын {0}-ээс илүү гарах үед сэрэмжлүүлэг гарна", + "ORDER_PORTFOLIO_LABEL": "Хэтэвчний хувь хэмжээ:", + "NOTIFICATION_FORM": { + "POPUP_ORDER_CONFIRMATION": "Захиалга өгөхөөс өмнө баталгаажуулна уу.", + "POPUP_ORDER_COMPLETED": "Захиалга дууссаны дараа гарч ирэх цонхыг харуул", + "POPUP_ORDER_PARTIALLY_FILLED": "Захиалга хэсэгчлэн дүүрсэн үед гарч ирэх цонхыг харуулах" + }, + "AUDIO_CUE_FORM": { + "ALL_AUDIO": "Бүх дуун сануулга", + "PUBLIC_TRADE_AUDIO": "Арилжаа хийгдсэн үед", + "ORDERS_PARTIAL_AUDIO": "Захиалга хагас дүүрсэн үед", + "ORDERS_PLACED_AUDIO": "Захиалга тавигдсан үед", + "ORDERS_CANCELED_AUDIO": "Захиалга цуцлагдсан үед", + "ORDERS_COMPLETED_AUDIO": "'Захиалга бүрэн биелсэн үед", + "CLICK_AMOUNTS_AUDIO": "Захиалгын үнэ, хэмжээ дарагдах үед", + "GET_QUICK_TRADE_AUDIO": "Хялбар арилжаа хийх үнийн санал авах үе", + "SUCCESS_QUICK_TRADE_AUDIO": "Амжилттай хялбар арилжаа хийгдэх үед", + "QUICK_TRADE_TIMEOUT_AUDIO": "Хялбар арилжаа цуцлагдах үед" + }, + "RISK_MANAGEMENT": { + "INFO_TEXT": "Худалдааны захиалгын үнэ таны багцын тогтоосон хувиас хэтэрсэн тохиолдолд анхааруулах цонх гарч ирнэ", + "INFO_TEXT_1": "Нийт хөрөнгө {0}: {1}", + "PORTFOLIO": "Хөрөнгийн хувь", + "VALUE_ASSET": "Багцаа үнэ", + "ADJUST": "(ХУВЬ ӨӨРЧЛӨХ)", + "ACTIVATE_RISK_MANAGEMENT": "Эрсдлийн удирдлага идэвхижүүлэх", + "WARNING_POP_UP": "Анхааруулах цонх" + } + }, + "USER_APPS": { + "TITLE": "Таны биржийн апп", + "SUBTITLE": "Таны биржийн дансны хэрэглээний мэдээлэл болон нэмэлт функцуудыг доороос үзнэ үү.", + "ALL_APPS": { + "TAB_TITLE": "Бүх аппууд", + "TITLE": "Биржийн аппууд", + "SUBTITLE": "Таны биржийн дансны хэрэглээний мэдээлэл болон нэмэлт функцуудыг доороос үзнэ үү..", + "SEARCH_PLACEHOLDER": "Хайлт...", + "ADD": { + "SUCCESSFUL": "Таны апп амжилттай нэмлээ!", + "FAILED": "Something went wrong" + } + }, + "MY_APPS": { + "TAB_TITLE": "Миний аппууд", + "TITLE": "Миний арилжааны аппууд", + "SUBTITLE": "Таны идэвхтэй арилжааны аппуудыг доор харуулав. Та товшиж аппын мэдээлэл, функц тус бүрийг өргөжүүлж, нэмэх/хасах боломжтой. Аппликейшн нь таны солилцооны туршлагад илүү их боломжоор хангах зорилготой юм." + }, + "TABLE": { + "APP_NAME": "Аппын нэр", + "DESCRIPTION": "Тодорхойлолт", + "ACTION": "Үйлдэл", + "CONFIGURE": "Тохируулах", + "VIEW_APP": "Аппыг үзэх", + "ADD": "Нэмэх", + "NOT_FOUND": "Ийм апп байхгүй байна...", + "RETRY": "Өөр нэр томъёогоор хайж үзнэ үү" + }, + "APP_DETAILS": { + "BACK_PLACEHOLDER": "{0} {1}", + "BACK_TO_APPS": "Миний аппууд руу", + "BACK": "Буцах" + }, + "CONFIGURE": { + "TITLE": "Аппыг тохируулах", + "SUBTITLE": "Өөрийн аппыг доороос тохируулна уу:", + "REMOVE": "Аппыг устгана уу", + "TEXT": "Хэрэв танд апп холбоотой асуудал байгаа бол доорх 'тусламж' дээр дарж бидэнтэй холбогдоно уу.", + "BACK": "БУЦАХ", + "HELP": "ТУСЛАМЖ" + }, + "REMOVE": { + "TITLE": "Аппыг устгах", + "SUBTITLE": "Энэ нь таны 'Миний аппууд' жагсаалтаас уг аппыг устгах болно.", + "TEXT": "Энэ аппыг устгахдаа итгэлтэй байна уу?", + "BACK": "БУЦАХ", + "CONFIRM": "БАТАЛГААЖУУЛАХ" + } + }, + "TRANSACTION_HISTORY": { + "TITLE": "Түүх", + "TITLE_TRADES": "Арилжааны түүх", + "TITLE_DEPOSITS": "Хадгаламжийн түүх", + "TITLE_WITHDRAWALS": "Татан авалтын түүх", + "TEXT_DOWNLOAD": "ТҮҮХИЙГ ТАТАЖ АВАХ", + "TRADES": "Арилжаанууд", + "DEPOSITS": "Хадгаламжууд", + "WITHDRAWALS": "Татан авалтууд" + }, + "ACCOUNT_SECURITY": { + "TITLE_TEXT": "2FA баталгаажуулалт, нууц үг, API түлхүүрүүд болон бусад аюулгүй байдалтай холбоотой функцуудаас бүртгэлийнхээ аюулгүй байдлын тохиргоог тохируулна уу.", + "OTP": { + "TITLE": "2FA", + "OTP_ENABLED": "otp идэвхитэй", + "OTP_DISABLED": "2FA ИДЭВХИЖҮҮЛЭХ", + "ENABLED_TEXTS": { + "TEXT_1": "Нэвтэрч орох үед OTP шаардах", + "TEXT_2": "Зарлага гаргах үед OTP шаардах" + }, + "DIALOG": { + "SUCCESS": "Та амжилттай 2FA идэвхижүүллээ", + "REVOKE": "ТА 2FA идэвхигүй болголоо" + }, + "CONTENT": { + "TITLE": "Давхар баталгаажуулалт идэвхижүүлэх", + "MESSAGE_1": "Скан", + "MESSAGE_2": "2FA баталгаажуулалтыг төхөөрөмждөө автоматаар тохируулахын тулд Google Authenticator эсвэл Authy ашиглан доорх qrcode-г уншина уу.", + "MESSAGE_3": "Хэрэв танд үүнийг скан хийхэд асуудал гарвал доорх кодыг гараар оруулж болно", + "MESSAGE_4": "Та ирээдүйд гар утсаа солих эсвэл алдах тохиолдолд 2FA-аа сэргээхийн тулд энэ кодыг найдвартай хадгалах ёстой.", + "MESSAGE_5": "Гарын авлага", + "WARNING": "Бид танд 2FA тохируулахыг зөвлөж байна. Ингэх нь таны хөрөнгийн аюулгүй байдлыг ихээхэн нэмэгдүүлэх болно.", + "ENABLE": "2FA баталгаажуулалтыг идэвхжүүлнэ үү", + "DISABLE": "2FA баталгаажуулалтыг идэвхгүй болгох", + "INPUT": "OTP-ээ оруулна уу" + }, + "FORM": { + "PLACEHOLDER": "Google Authenticator-с өгсөн OTP-ээ оруулна уу.", + "BUTTON": "2FA-г идэвхжүүлнэ үү" + } + }, + "CHANGE_PASSWORD": { + "TITLE": "Нууц үг", + "ACTIVE": "ИДЭВХИТЭЙ", + "DIALOG": { + "EMAIL_CONFIRMATION": "Нууц үг солихыг зөвшөөрөхийн тулд танд имэйл илгээгдэх болно." + }, + "FORM": { + "BUTTON": "Нууц үг", + "CURRENT_PASSWORD": { + "label": "Одоогийн нууц үг", + "placeholder": "Шинэ нууц үгээ оруулна уу" + }, + "NEW_PASSWORD": { + "label": "Шинэ нууц үг", + "placeholder": "Шинэ нууц үгээ оруулна уу" + }, + "NEW_PASSWORD_REPEAT": { + "label": "Нууц үг батлах", + "placeholder": "Шинэ нууц үгээ дахин оруулна уу" + } + } + }, + "LOGIN": { + "TITLE": "Нэвтрэлтийн түүх", + "IP_ADDRESS": "IP хаяг", + "TIME": "Огноо", + "CONTENT": { "TITLE": "Нэвтрэлтийн түүх" } + }, + "FREEZE": { + "TITLE": "Аккаунт түгжих", + "CONTENT": { + "MESSAGE_1": "Та дансаа түгжснээр зарлага болон бүх арилжаа зогсоно.", + "WARNING_1": "Хэрэв та халдлагад өртсөн гэж үзсэн тохиолдолд л энэ хэсгийг ашиглана уу", + "TITLE_1": "Аккаунтаа түгжих", + "TITLE_2": "Бүртгэл царцаах", + "MESSAGE_2": "Бүртгэлээ царцаах нь таны бүртгэлийг кибер халдлагаас хамгаалахад тусална.", + "MESSAGE_3": "Та бүртгэлээ царцааснаар дараах үйлдлүүд явагдана:", + "MESSAGE_4": "1. Хүлээгдэж буй зарлагууд цуцлагдана.", + "MESSAGE_5": "2. Бүх арилжаа зогсож, дутуу биелсэн захиалгууд цуцлагдана.", + "MESSAGE_6": "3. Бүртгэлээ дахин идэвхижүүлэхийн тулд оператортой холбогдох шаардлагатай.", + "WARNING_2": "Та бүртгэлээ царцаахдаа итгэлтэй байна уу?" + } + } + }, + "STAKE": { + "NETWORK_WARNING": "Тохиромжгүй сүлжээ. Сүлжээгээ {0} болгож өөрчилнө үү", + "EARN": "Олж авах", + "TITLE": "Стэйк", + "MODAL_TITLE": "Стэйклэж, {0}-г олоорой", + "REVIEW_MODAL_TITLE": "Стэйкийг шалгаж баталгаажуулна уу", + "AVAILABLE_TOKEN": "{0} ({1}) Стэйклэх боломжтой: {2}", + "DEFI_TITLE": "DeFi хөрөнгийн үнэлгээ", + "DEFI_TEXT": "Стэйкинг DeFi стиль нь биржээс гадуур өөрийн хэтэвчийг ашиглах болно. Эхлэхийн тулд та холболт үүсгэх шаардлагатай бөгөөд холбогдсоны дараа та түрийвчнээсээ шууд бооцоо тавьж, орлого олж эхлэх боломжтой.", + "GET_STAKES": "Стэйк авах", + "CURRENT_ETH_BLOCK": "Одоогийн ETH блок: {0}", + "ON_EXCHANGE_XHT": "Биржийн {0} үлдэгдэл: {1} {2}", + "LOGIN_HERE": "Энд нэвтэрнэ үү", + "MOVE_XHT": "{0}-г зөөх", + "ESTIMATED_STAKED": "Нийт Стэйкийн үнэ цэнэ", + "ESTIMATED_EARNINGS": "Орлогын тооцоолсон үнэ цэнэ", + "CONNECT_WALLET": "Хэтэвчээ холбоно уу", + "BACK": "Буцах", + "NEXT": "Дараачийн", + "REVIEW": "Шүүмж", + "ESTIMATED": "EST.", + "BLOCK": "Блок", + "CANCEL": "Цуцлах", + "PROCEED": "Үргэлжлүүлэх", + "GO_TO_WALLET": "Хэтэвч рүү очих", + "AMOUNT_LABEL": "Стэйкийн хэмжээ", + "PERIOD_SUBTITLE": "Удаан стэйклэх тусам илүү их шагнал авах болно. Стэйклэх хугацааг доороос сонгоно уу.", + "STAKE_AND_EARN_DETAILS": "~{0}-р Стэйклэж, {1}-г олоорой", + "PREDICTED_EARNINGS": "Урьдчилан таамагласан орлого", + "VARIABLE_TITLE": "Хувьсагч*", + "VARIABLE_TEXT": "*{0} хувьсагчийн ханш хэрхэн ажилладаг талаар.", + "READ_MORE": "Цааш унших", + "CURRENT_BLOCK": "Одоогийн блок: {0}", + "END_BLOCK": "Төгсгөлийн блок: {0}", + "DURATION": "Үргэлжлэх хугацаа", + "END_ON_BLOCK": "Блок дээр дуусах: {0}", + "SLASHING_TITLE": "Таслах", + "SLASHING_TEXT_1": "Таны Стэйкийн зарчмын {0}%", + "SLASHING_TEXT_2": "Бүх орлогыг хураана", + "REVIEW_NOTE": "Үргэлжлэх хугацааг Ethereum блокуудын цаг хугацаагаар хэмждэг. Эрт стэйклэх нь таны бооцооны зарчмын тодорхой хувийг хасч, ашиг орлогоо хураах тул бооцоо тавихаасаа өмнө дээрх дэлгэрэнгүй мэдээллийг шалгаж баталгаажуулна уу.", + "WAITING_TITLE": "Баталгаажуулахыг хүлээж байна", + "WAITING_TEXT": "Энэ гүйлгээг түрийвчэндээ баталгаажуулна уу", + "PENDING_TEXT": "Гүйлгээ хүлээгдэж байна...", + "CHECKING_ALLOWANCE": "{0} тэтгэмжийг шалгаж байна...", + "WAITING_PROMPT": "{0} {1} {2}", + "WAITING_STAKE": "Стэйкийн хэмжээг баталгаажуулна уу", + "WAITING_WITHDRAW": "Зарцуулахыг зөвшөөрч байна", + "WAITING_UNSTAKE": "Стэйклэхээ болих", + "WAITING_STAKE_ING": "Стэйкинг хүлээгдэж байна", + "WAITING_WITHDRAW_ING": "Зарцуулалтын зөвшөөрлийг боловсруулах", + "WAITING_UNSTAKE_ING": "Стэйклэхээ болих", + "SUCCESSFUL_STAKE_TITLE": "Та {0}-г амжилттай стэйклэлээ", + "SUCCESSFUL_STAKE_AMOUNT": "Стэйклэсэн дүн", + "SUCCESSFUL_STAKE_DURATION_KEY": "Үргэлжлэх хугацаа", + "SUCCESSFUL_STAKE_DURATION_DEF": "{0} ({1}) блок дээр дуусна", + "SUCCESSFUL_STAKE_DESTINATION": "Очих газар", + "SUCCESSFUL_UNSTAKE_ADDRESS": "Миний хаяг", + "OKAY": "Ok", + "ERROR_TITLE": "Алдаа: {0} татгалзсан", + "ERROR_SUBTITLE": "Хэрэв энэ алдаа байсан бол та буцаж очоод дахин оролдож болно", + "SUCCESSFUL_UNSTAKE_TITLE": "Та {0}-г амжилттай арилгалаа", + "SUCCESSFUL_UNSTAKE_AMOUNT": "Нийт хүлээн авах нь", + "EARNINGS": "Орлого", + "ORIGINAL_AMOUNT": "Стэйклэсэн анхны дүн", + "CONNECT_A_WALLET": "Хэтэвчтэй холбогдоно уу", + "CONNECT_WALLET_TABLE": "Стэйкийн үйл явдлуудын түүхийг харахын тулд {0}", + "ZERO_STAKES": "0 стэйк", + "PENDING_TRANSACTIONS": "Хүлээгдэж буй {0} {1}", + "VIEW_ON": "{0} дээрээс харах", + "BLOCKCHAIN": "блокчейн", + "VIEW_POT": "Түгээлтийн POT-г харах", + "COMPLETED": "Ханасан", + "COMPLETED_TOOLTIP": "Стейк нь гүйцсэн. Илүү их шагнал авахын тулд үргэлжлүүлэх эсвэл орлогоо авахын тулд зогсооно уу.", + "CONNECT_ERROR": "Хэтэвчээ шалгана уу", + "INSTALL_METAMASK": "Та өөрийн хөтөч дээрээ Metamask суулгах ёстой: https://metamask.io/download.html", + "INSTALL_METAMASK_TITLE": "MetaMask илэрсэнгүй", + "REWARDS": { + "0": { "CARD": "Шагнал авах (бонус байхгүй)", "TEXT": "тогтмол шагналууд." }, + "1": { + "CARD": "Шагнал + урамшуулал аваарай", + "TEXT": "орлогоосоо урамшуулах урамшуулал" + }, + "2": { + "CARD": "Хамгийн өндөр шагнал, хамгийн өндөр урамшуулал", + "TEXT": "таны орлогын хамгийн өндөр урамшуулал" + }, + "3": { "CARD": "", "TEXT": "" }, + "4": { "CARD": "", "TEXT": "" } + } + }, + "UNSTAKE": { + "TITLE": "Unstake", + "EARLY_TITLE": "Unstake early", + "EARLY_WARNING_TITLE": "Looks like you are trying to unstake early", + "EARLY_WARNING_TEXT_1": "This could lead to a percentage of your initial principle stake being deducted and all earnings being forfeited.", + "EARLY_WARNING_TEXT_2": "Are you sure you want to proceed?", + "BACK": "GO BACK", + "REVIEW": "REMOVE STAKE", + "DURATION": "EST. maturation duration", + "CANCEL": "Cancel", + "PROCEED": "Proceed", + "EARNINGS_FORFEITED": "Earnings forfeited", + "PRICE_FORMAT": "{0} {1}", + "EST_PENDING": "EST. pending: {0}", + "AMOUNT_SLASHED": "Amount slashed*", + "AMOUNT_TO_RECEIVE": "Amount to receive", + "SLASH_FOOTNOTE": "*All amounts slashed are distributed to remaining stakers. Please consider the slashed amount from initial principle, the earnings forfeited and duration remaining and determine if the value lost in unstaking early is worth the cost.", + "AMOUNT_NOTE": "Amounts will be distributed to your wallet address", + "TOTAL_EARNT": "Total earnt", + "PENDING_EARNINGS": "Pending earnings*", + "PENDING_EARNINGS_FOOTNOTE": "*Pending earnings are amounts that have not cleared and require a blockchain transaction in order to be added to your total receiving amount." + }, + "STAKE_TABLE": { + "CURRENCY": "Currency", + "AVAILABLE": "Available to stake", + "TOTAL": "Total staked", + "REWARD_RATE": "Reward rate", + "EARNINGS": "Earnings", + "STAKE": "Stake", + "VARIABLE": "Variable" + }, + "STAKE_LIST": { + "AMOUNT": "Стэйкинг хэмжээ", + "DURATION": "EST. MATURATION DURATION", + "START": "Стейкинг эхэлсэн", + "END": "Стэйк дуусах", + "EARNINGS": "ОРЛОГО", + "STAKE": "Стэйк" + }, + "STAKE_DETAILS": { + "BACK_SUBTITLE": "stake хуудас руу {0}", + "GO_BACK": "Буцах", + "CONTRACT_SUBTITLE": "Token гэрээ: {0}", + "VIEW_MORE": "ИЛҮҮ ИХИЙГ ҮЗЭХ", + "VIEW": "Дэлгэрэнгүй", + "TOKEN": "{0} Token", + "TABS": { + "PUBLIC_INFO": "Олон нийтийн мэдээлэл", + "DISTRIBUTIONS": "Хуваарилалт", + "MY_STAKING": "Миний стэйкинг" + }, + "PUBLIC_INFO": { + "TITLE": "Стэйкингийн мэдээлэл", + "SUBTITLE": "Below is a staking tokenomics for {0} ({1}).", + "TOTAL_DISTRIBUTED_REWARDS": "Total distributed rewards ({0})", + "POT_BALANCE": "POT balance", + "UNCLAIMED_REWARDS": "Unclaimed rewards", + "TOTAL_STAKED": "Нийт стэйк", + "REWARD_RATE": "Хувь хэмжээ", + "MY_STAKE": "Миний стэйк ({0}%)", + "MY_STAKE_PERCENTLESS": "Миний стэйк", + "OTHER_STAKE": "Бусад стэйк ({0}%)", + "EVENTS_TITLE": "Саяхан тараасан урамшуулал" + }, + "DISTRIBUTIONS": { + "TITLE": "{0} шагналыг тараасан", + "SUBTITLE": "Доорх нь {0}-н стэйкинг эзэмшигчдэд хийсэн түгээлтийн түүхэн жагсаалт юм..", + "TIME": "Хуваарилсан огноо", + "TRANSACTION_ID": "Гүйлгээний ID", + "AMOUNT": "Хуваарилсан дүн" + }, + "MY_STAKING": { + "TITLE": "Миний стэйкинг", + "SUBTITLE": "Below displays information and some historical events related to your {0} staking.", + "EVENTS_TITLE": "Historical stake events", + "TIME": "Цаг", + "EVENT": "Event", + "TRANSACTION_ID": "Гүйлгээний ID", + "AMOUNT": "Дүн" + } + }, + "MOVE_XHT": { + "TITLE": "XHT-г зөөх", + "TEXT_1": "XHT-г бооцоо болгохын тулд та эхлээд өөрийн XHT-ээ өөрийн хэтэвч рүү шилжүүлэх ёстой.", + "TEXT_2": "Таны одоо холбогдсон хэтэвчний хаяг байна:", + "LABEL": "Хэтэвчний хаяг", + "TEXT_3": "Хэтэвчний хаяг найдвартай эсэхийг шалгах нь чухал. XHT нь дээрх хэтэвчний хаяг руу шилжих болно." + }, + "MOVE_AMOUNT": { + "TITLE": "Оруулсан хэмжээ", + "PROMPT": "Зөөхийг хүссэн дүнгээ оруулна уу.", + "BALANCE": "{0} баланс: {1}", + "LABEL": "Хөдөлгөөн хийх хэмжээ", + "FEE": "Гүйлгээний хураамж: {0} {1}" + }, + "CURRENCY": "Крипто", + "TYPE": "Төрөл", + "TYPES_VALUES": { "market": "market", "limit": "limit" }, + "TYPES": { "MARKET": "market", "LIMIT": "limit" }, + "SIDE": "Side", + "SIDES_VALUES": { "buy": "авах", "sell": "зарах" }, + "SIDES_VERBS": { "buy": "авсан", "sell": "зарсан" }, + "SIDES": { "BUY": "авах", "SELL": "зарах" }, + "DEFAULT_TOGGLE_OPTIONS": { "ON": "асаалттай", "OFF": "унтарсан" }, + "SIZE": "Хэмжээ", + "PRICE": "Үнэ", + "FEE": "Шимтгэл", + "FEES": "Шимтгэл", + "TIME": "Цаг", + "MORE": "Илүүг", + "VIEW": "Харах", + "STATUS": "Төлөв", + "AMOUNT": "Хэмжээ", + "COMPLETE": "Дууссан", + "PENDING": "Хүлээгдэж буй", + "REJECTED": "Татгалзсан", + "ORDERBOOK": "Захиалгын дэвтэр", + "CANCEL": "Цуцлах", + "CANCEL_ALL": "Бүгдийг цуцлах", + "ORDER_ENTRY": "Захиалга оруулах", + "TRADE_HISTORY": "Түүх", + "CHART": "Үнийн график", + "ORDERS": "Миний идэвхитэй захиалгууд", + "RECENT_TRADES": "Миний гүйлгээнүүд", + "ORDER_HISTORY": "Захиалгын түүх", + "PUBLIC_SALES": "Олон нийтийн борлуулалт", + "REMAINING": "үлдэгдэл", + "FULLFILLED": "{0} % Дүүрсэн", + "FILLED": "Дүүрсэн", + "LOWEST_PRICE": "Доод үнэ ({0})", + "PHASE": "Үе шат", + "INCOMING": "Орлого", + "PRICE_CURRENCY": "Үнэ", + "AMOUNT_SYMBOL": "Хэмжээ", + "ESTIMATED_PRICE": "Багцаа үнэ", + "ORDER_PRICE": "Захиалгын үнэ", + "NO_DATA": "Мэдээлэл байхгүй", + "CHART_TEXTS": { + "d": "Огноо", + "o": "Нээлттэй", + "h": "Дээд", + "l": "Доод", + "c": "Хаах", + "v": "Хэмжээ" + }, + "QUICK_TRADE": "Хялбар ажиллагаа", + "PRO_TRADE": "Арилжаа", + "WALLET_TITLE": "Хэтэвч", + "LOGOUT": "Гарах", + "WITHDRAWALS_MIN_VALUE_ERROR": "Гүйлгээний дүн хэт бага байна. Дүнгээ өөрчилнө үү.", + "WITHDRAWALS_MAX_VALUE_ERROR": "Гүйлгээний дүн хэт өндөр байна. Дүнгээ өөрчилнө үү.", + "WITHDRAWALS_LOWER_BALANCE": "Үлдэгдэл хүрэлцэхгүй байна. Хамгийн багадаа {0} байх шаардлагатай.", + "WITHDRAWALS_BTC_INVALID_ADDRESS": "Bitcoin хаяг буруу байна. Хаягаа дахин шалгана уу", + "WITHDRAWALS_ETH_INVALID_ADDRESS": "Ethereum хаяг буруу байна. Хаягаа дахин шалгана уу", + "WITHDRAWALS_BUTTON_TEXT": "Зарлага хянах", + "WITHDRAWALS_FORM_NETWORK_LABEL": "Сүлжээ", + "DEPOSIT_FORM_NETWORK_WARNING": "Сонгосон сүлжээ нь илгээгчийн сүлжээтэй тохирч байгаа эсэхийг нягтлана уу", + "DEPOSIT_FORM_TITLE_WARNING_DESTINATION_TAG": "Хаяг болон таг заавал оруулна уу.", + "WITHDRAW_PAGE_DESTINATION_TAG_NONE": "Байхгүй", + "WITHDRAW_PAGE_DESTINATION_TAG_MESSAGE": "Хүргэх tag: {0}", + "WITHDRAW_PAGE_NETWORK_TYPE_MESSAGE": "{0} хаягийн сүлжээний төрөл: {1}", + "WITHDRAWALS_FORM_NETWORK_WARNING": "Сонгосон сүлжээ нь очих газрын хэтэвчтэй нийцэж байгаа эсэхийг шалгаарай", + "WITHDRAWALS_FORM_FEE_WARNING": "{0} ({1}) нь энэ хөрөнгийг эргүүлэн татахад тавигдах шаардлага юм", + "WITHDRAWALS_FORM_DESTINATION_TAG_WARNING": "Хүлээн авах хаяг нь шошго шаардлагатай эсэхийг шалгана уу. Мөн Memo, дижитал ID, шошго, тэмдэглэл гэж нэрлэдэг.", + "WITHDRAWALS_FORM_NETWORK_PLACEHOLDER": "Сүлжээ сонгох", + "WITHDRAWALS_FORM_ADDRESS_LABEL": "Хүлээн авах хаяг", + "WITHDRAWALS_FORM_ADDRESS_PLACEHOLDER": "Хаягаа бичнэ үү", + "WITHDRAWALS_FORM_DESTINATION_TAG_LABEL": "Хүлээн авах tag оруулна уу", + "WITHDRAWALS_FORM_MEMO_LABEL": "Memo (заавал биш)", + "WITHDRAWALS_FORM_DESTINATION_TAG_PLACEHOLDER": "Хүлээн авах tag (заавал биш)", + "WITHDRAWALS_FORM_AMOUNT_LABEL": "{0} зарлага хийх дүн", + "WITHDRAWALS_FORM_AMOUNT_PLACEHOLDER": "{0} зарлага хийх дүнгээ оруулна уу", + "WITHDRAWALS_FORM_FEE_COMMON_LABEL": "Гүйлгээний шимтгэл", + "WITHDRAWALS_FORM_FEE_COMMON_LABEL_COIN": "Гүйлгээний шимтгэл ({0})", + "WITHDRAWALS_FORM_FEE_PLACEHOLDER": "Гүйлгээний шимтгэлд ашиглах {0} сонгоно уу", + "DEPOSIT_BANK_REFERENCE": "Энэ '{0}' кодыг гүйлгээний утган дээрээ заавал бичээрэй", + "QUOTE_SUCCESS_REVIEW_MESSAGE": "Та {0} {1} {2} - амжилттай худалдан авлаа", + "COUNTDOWN_ERROR_MESSAGE": "Каунтдаун дууслаа", + "WITHDRAW_PAGE": { + "BANK_TO_WITHDRAW": "Банкны зарлага гаргах", + "MESSAGE_ABOUT_SEND": "Та илгээх гэж байна", + "MESSAGE_BTC_WARNING": "{0} шилжүүлгийг буцаах боломжгүй тул энэ хаягийн үнэн зөвийг баталгаажуулна уу", + "MESSAGE_FEE": "Гүйлгээний шимтлэг {0} ({1}) багтсан", + "MESSAGE_FEE_COIN": "{0} гүйлгээний шимтгэл", + "BASE_MESSAGE_1": "Та өөрийн бүртгүүлсэн нэртэй ижил данс руу гүйлгээ хийх боломжтой.", + "BASE_MESSAGE_2": "Зарлагын доод дүн", + "BASE_MESSAGE_3": "Өдрийн зарлагын дээд хэмжээ", + "CONFIRM_VIA_EMAIL": "И-мэйлээр баталгаажуулах", + "CONFIRM_VIA_EMAIL_1": "Бид тань руу зарлага баталгаажуулах и-мэйл илгээнэ.", + "CONFIRM_VIA_EMAIL_2": "Зарлагын гүйлгээг баталгаажуулахын тулд та", + "CONFIRM_VIA_EMAIL_3": "И-мэйлийг 5 минутын дотор баталгаажуулах шаардлагатай.", + "WITHDRAW_CONFIRM_SUCCESS_1": "Таны зарлагын хүсэлтийг хүлээн авлаа. Зарлага удахгүй баталгаажих болно", + "WITHDRAW_CONFIRM_SUCCESS_2": "Зарлагын төлвөө Зарлагын түүх хэсгээс харна уу.", + "GO_WITHDRAWAL_HISTORY": "Зарлагын түүх", + "WITHDRAWALS_FORM_ERROR_TITLE": "Шилжүүлгийн мэдээлэл буруу байна", + "WITHDRAWALS_FORM_ERROR": "Таны сангийн шилжүүлэг амжилтгүй боллоо. Имэйл рүү мөнгө илгээхдээ хэрэглэгч энэ бирж дотор данстай байхыг шаарддаг. Имэйл зөв эсэхийг шалгаад дахин оролдоно уу." + }, + "WALLET_BUTTON_BASE_DEPOSIT": "орлого", + "WALLET_BUTTON_BASE_WITHDRAW": "зарлага", + "WALLET_BUTTON_CRYPTOCURRENCY_DEPOSIT": "хүлээн авах", + "WALLET_BUTTON_CRYPTOCURRENCY_WITHDRAW": "илгээх", + "AVAILABLE_BALANCE_TEXT": "Боломжит {0} Баланс: {1} {2}", + "BALANCE_TEXT": "Баланс", + "CURRENCY_BALANCE_TEXT": "{0} Баланс", + "WALLET_ALL_ASSETS": "Бүх хөрөнгө", + "WALLET_HIDE_ZERO_BALANCE": "0 дүнтэйг нуух", + "WALLET_ESTIMATED_TOTAL_BALANCE": "Нийт баланс (багцаа)", + "WALLET_ASSETS_SEARCH_TXT": "Хайх", + "PAGINATOR_FORMAT": "{0} / {1}", + "ORDERBOOK_SELLERS": "Худалдагч", + "ORDERBOOK_BUYERS": "Худалдан авагч", + "ORDERBOOK_SPREAD": "зөрүү {0}", + "CALCULATE_MAX": "Дээд хэмжээ", + "VERIFICATION_WARNING_TITLE": "Банкны баталгаажуулалт", + "VERIFICATION_WARNING_MESSAGE": "Та зарлага гаргахын өмнө банкаа баталгаажуулах шаардлагатай.", + "ORDER_SPENT": "Зарцуулсан", + "ORDER_RECEIVED": "Хүлээн авсан", + "ORDER_SOLD": "Зарсан", + "ORDER_BOUGHT": "Худалдаж авсан", + "ORDER_AVERAGE_PRICE": "Дундаж үнэ", + "ORDER_TITLE_CREATED": "Лимит {0} захиалга үүсгэлээ", + "ORDER_TITLE_FULLY_FILLED": "{0} захиалга дүүрлээ", + "ORDER_TITLE_PARTIALLY_FILLED": "{0} захиалга хэсэгчлэн дүүрлээ", + "ORDER_TITLE_TRADE_COMPLETE": "{0} {1} захиалга дууслаа", + "LOGOUT_TITLE": "Та системээс гарсан байна", + "LOGOUT_ERROR_TOKEN_EXPIRED": "Таны системийн хугацаа дууслаа. Дахин нэвтэрч орно уу", + "LOGOUT_ERROR_LOGIN_AGAIN": "Дахин нэвтэрч орно уу", + "LOGOUT_ERROR_INVALID_TOKEN": "Tокен буруу байна", + "LOGOUT_ERROR_INACTIVE": "Та идэвхигүй байсан тул системээс гарсан байна", + "ORDER_ENTRY_BUTTON": "{0} {1}", + "ORDER_ENTRY_ADVANCED": "Нарийвчилсан", + "QUICK_TRADE_OUT_OF_LIMITS": "Захиалгын хэмжээ лимитээс хэтэрсэн байна", + "QUICK_TRADE_TOKEN_USED": "Token хэрэглэгдсэн байна", + "QUICK_TRADE_QUOTE_EXPIRED": "Саналын хугацаа дууссан байна", + "QUICK_TRADE_QUOTE_INVALID": "Санал буруу байна", + "QUICK_TRADE_QUOTE_CALCULATING_ERROR": "Саналыг тооцоолоход алдаа гарлаа", + "QUICK_TRADE_ORDER_CAN_NOT_BE_FILLED": "Ийм хэмжээтэй захиалга дүүрэх боломжгүй", + "QUICK_TRADE_ORDER_NOT_FILLED": "Захиалга дүүрсэнгүй", + "QUICK_TRADE_NO_BALANCE": "Баланс хүрэлцэхгүй байна", + "QUICK_TRADE_SUCCESS": "Амжилттай!", + "QUICK_TRADE_INSUFFICIENT_FUND": "Үлдэгдэл хүрэлцэхгүй байна", + "QUICK_TRADE_INSUFFICIENT_FUND_MESSAGE": "Энэ гүйлгээг хийхэд таны үлдэгдэл хүрэлцэхгүй байна.", + "QUICK_TRADE_BROKER_NOT_AVAILABLE_MESSAGE": "Брокерын OTC хэлэлцээр одоогоор боломжгүй байна.", + "SUBMIT": "илгээх", + "RESUBMIT": "Дахин илгээх", + "VERIFICATION_NOTIFICATION_SKIP_TITLE": "Бичиг баримт дутуу байна!", + "VERIFICATION_NOTIFICATION_SKIP_TEXT": "Мөнгөн орлого зарлагыг идэвхжүүлэхийн тулд та KYC баталгаажуулалтаа хийх ёстой.", + "VERIFICATION_NOTIFICATION_SUCCESS_TITLE": "Амжилттай!", + "VERIFICATION_NOTIFICATION_SUCCESS_TEXT": "Таны мэдээлэл баталгаажсаны дараа та и-мэйл хүлээн авна. Баталгаажуулалт 1-3 хоног үргэлжилнэ", + "VERIFICATION_NOTIFICATION_BUTTON": "ЭКСЧЭЙНЖ РҮҮ ОЧИХ", + "ERROR_USER_ALREADY_VERIFIED": "Хэрэглэгч баталгаажсан байна", + "ERROR_INVALID_CARD_USER": "Банкны мэдээлэл буруу байна", + "ERROR_INVALID_CARD_NUMBER": "Картын дугаар буруу байна", + "ERROR_LOGIN_USER_NOT_VERIFIED": "Хэрэглэгч баталгаажаагүй байна", + "ERROR_LOGIN_USER_NOT_ACTIVATED": "Хэрэглэгч идэвхигүй байна", + "ERROR_LOGIN_INVALID_CREDENTIALS": "Нэвтрэх мэдээлэл буруу", + "SMS_SENT_TO": "{0} SMS илгээх", + "SMS_ERROR_SENT_TO": "{0} руу SMS илгээхэд алдаа гарлаа. Хуудсыг дахин сэргээгээд дахин оролдоно уу.", + "WITHDRAW_NOTIFICATION_TRANSACTION_ID": "Гүйлгээний №:", + "CHECK_ORDER": "Захиалгаа шалгаад баталгаажуулна уу", + "CHECK_ORDER_TYPE": "{0} {1}", + "CONFIRM_TEXT": "Баталгаажуулах", + "INVALID_CAPTCHA": "Captcha буруу байна", + "NO_FEE": "N/A", + "SETTINGS_LANGUAGE_LABEL": "Хэлний сонголт (И-мэйл орно)", + "SETTINGS_THEME_LABEL": "Хэрэглэгчийн интерфэйс", + "SETTING_BUTTON": "хадгалах", + "VERIFICATION_NO_WITHDRAW_TITLE": "Зарлага гаргах боломжгүй", + "VERIFICATION_NO_WITHDRAW_MESSAGE": "Таны бүртгэл зарлага гаргах боломжгүй", + "UP_TO_MARKET": "Up to market", + "VIEW_MY_FEES": "Миний Шимтгэл", + "DEVELOPER_SECTION": { + "TITLE": "API Key", + "INFORMATION_TEXT": "API нь хэтэвчний үлдэгдэл авах, худалдан авах/худалдах захиалгыг удирдах, мөнгө авах хүсэлт гаргах, түүнчлэн сүүлийн арилжаа, захиалгын дэвтэр, ticker зэрэг зах зээлийн мэдээллийг авах боломж олгодог.", + "ERROR_INACTIVE_OTP": "API түлхүүр үүсгэхийн тулд та 2FA баталгаажуулалтыг идэвхжүүлэх хэрэгтэй.", + "ENABLE_2FA": "2FA Идэвхижүүлэх", + "WARNING_TEXT": "DAPI түлхүүрийг бусадтай битгий хуваалцаарай.", + "GENERATE_KEY": "API Түлхүүр үүсгэх", + "ACTIVE": "Идэвхитэй", + "INACTIVE": "Идэвхигүй", + "INVALID_LEVEL": "Та энэ хэсэгт хандахын тулд өөрийн баталгаажуулалтын түвшингээ ахиулах хэрэгтэй" + }, + "DEVELOPERS_TOKEN": { + "API_KEY": "API түлхүүр", + "SECRET_KEY": "Нууц түлхүүр", + "ACCESS": "Хандалт", + "BASIC_ACCESS": "үндсэн хандалт", + "BASIC_ACCESS_PROMPT": "үндсэн хандалт", + "READING_ACCESS": "Унших (Хэтэвчний үлдэгдэл гэх мэт)", + "TRADING_ACCESS": "Арилжаа", + "IP_ACCESS": "IP хандалт", + "IP_ACCESS_PROMPT": "Энэ API түлхүүрээр ямар IP хаяг ажиллахыг тохируулна уу.", + "ANY_IP_ADDRESS": "Аливаа IP хаяг", + "ONLY_TRUSTED_IPS": "Зөвхөн итгэмжлэгдсэн IP", + "ADD_IP_PH": "IP хаягаа оруулна уу. Та олон IP нэмж болно", + "ADD_IP": "Нэмэх", + "ADVANCED_ACCESS": "Нарийвчилсан хандалт", + "ADVANCED_ACCESS_PROMPT": "Итгэмжлэгдсэн IP-г идэвхжүүлэх шаардлагатай.", + "WITHDRAWAL_ACCESS": "Татан авалт", + "SAVE": "Хадгалах", + "BEWARE": "Татан авалтыг зөвшөөрөх нь тодорхой эрсдэлтэй тул болгоомжтой байгаарай!" + }, + "DEVELOPERS_TOKENS_POPUP": { + "GENERATE_TITLE": "API Түлхүүр үүсгэх", + "GENERATE_TEXT": "API түлхүүрээ нэрлээд үүсгэсний дараа нууцална уу. Та дараа нь дахин сэргээх боломжгүй.", + "GENERATE": "Үүсгэх", + "DELETE_TITLE": "API Түлхүүр устгах", + "DELETE_TEXT": "API түлхүүрээ устгах нь буцаах боломжгүй бөгөөд та хүссэн үедээ шинэ API түлхүүр үүсгэж болно. Та API түлхүүрээ устгахыг хүсэж байна уу?", + "DELETE": "УСТГАХ", + "FORM_NAME_LABEL": "Нэр", + "FORM_LABLE_PLACEHOLDER": "Api түлхүүрийн нэр", + "API_KEY_LABEL": "API түлхүүр", + "SECRET_KEY_LABEL": "Нууц үг", + "CREATED_TITLE": "API түлхүүрийг хуулбарлах", + "CREATED_TEXT_1": "API түлхүүрийг яг одоо хуулбарлаж авна уу.", + "CREATED_TEXT_2": "Та түлхүүрийн нууцлалыг өөрөө хариуцах болно." + }, + "DEVELOPERS_TOKENS_TABLE": { + "NAME": "Нэр", + "API_KEY": "API түлхүүр", + "SECRET": "Нууцлал", + "CREATED": "Үүсгэсэн огноо", + "REVOKE": "Хүчингүй болгох", + "REVOKED": "Хүчингүй болгосон", + "REVOKE_TOOLTIP": "Та идэвхигүй болгохын тулд 2FA идэвхижүүлэх шаардлагатай" + }, + "CHAT": { + "READ_MORE": "Дэлгэрэнгүй", + "SHOW_IMAGE": "Зураг харуулах", + "HIDE_IMAGE": "Зураг нуух", + "CHAT_MESSAGE_BOX_PLACEHOLDER": "Мессеж", + "TROLLBOX": "Trollbox ({0})", + "SET_USERNAME": "ХЭРЭГЛЭГЧИЙН НЭРИЙГ ЧАТАНД ТОХИРУУЛАХ" + }, + "INVALID_USERNAME": "Хэрэглэгчийн нэр 3-15 тэмдэгтийн урттай байх ёстой. Зөвхөн жижиг үсэг, тоо, доогуур зураасыг агуулна", + "USERNAME_TAKEN": "Энэ нэр бүртгэлтэй байна.", + "USERNAME_LABEL": "Ник нэр (чатанд ашиглах)", + "USERNAME_PLACEHOLDER": "Ник нэр", + "TAB_USERNAME": "Ник нэр", + "USERNAME_WARNING": "Таны хэрэглэгчийн нэрийг зөвхөн нэг удаа өөрчлөх боломжтой. Хэрэглэгчийн нэрээ авах нь зүйтэй гэдэгт итгэлтэй байна уу.", + "USERNAME_CANNOT_BE_CHANGED": "Хэрэглэгчийн нэрийг өөрчлөх боломжгүй", + "UPGRADE_LEVEL": "Түвшин ахиулах", + "LEVELS": { + "LABEL_LEVEL": "Түвшин", + "LABEL_LEVEL_1": "Нэг", + "LABEL_LEVEL_2": "Хоёр", + "LABEL_LEVEL_3": "Гурав", + "LABEL_BASE_DEPOSIT": "Maker шимтгэл", + "LABEL_BASE_WITHDRAWAL": "Өдрийн зарлагын лимит", + "LABEL_BTC_DEPOSIT": "Өдрийн биткойн орлогын лимит", + "LABEL_BTC_WITHDRAWAL": "Өдрийн биткойн зарлагын лимит", + "LABEL_ETH_DEPOSIT": "Өдрийн Этериум орлого", + "LABEL_ETH_WITHDRAWAL": "Өдрийн Этериум зарлага", + "LABEL_PAIR_MAKER_FEE": "{0} Maker шимтгэл", + "LABEL_PAIR_TAKER_FEE": "{0} Taker шимтгэл", + "UNLIMITED": "Хязгааргүй", + "BLOCKED": "Идэвхигүй" + }, + "WALLET_ADDRESS_TITLE": "{0} Хэтэвч үүсгэх", + "WALLET_ADDRESS_GENERATE": "Үүсгэх", + "WALLET_ADDRESS_MESSAGE": "Та хэтэвчээ үүсгэснээр орлого хийх боломжтой болно.", + "WALLET_ADDRESS_ERROR": "Хаяг үүсгэхэд алдаа гарлаа. Дахин оролдоно уу", + "DEPOSIT_WITHDRAW": "Орлого/Зарлага", + "GENERATE_WALLET": "Хэтэвч үүсгэх", + "TRADE_TAB_CHART": "График", + "TRADE_TAB_TRADE": "Арилжаа", + "TRADE_TAB_ORDERS": "Захиалга", + "TRADE_TAB_POSTS": "Зарлал", + "WALLET_TAB_WALLET": "Хэтэвч", + "WALLET_TAB_TRANSACTIONS": "Гүйлгээ", + "RECEIVE_CURRENCY": "{0} Хүлээн авах", + "SEND_CURRENCY": "{0} Илгээх", + "COPY_ADDRESS": "Хаяг хуулах", + "SUCCESFUL_COPY": "Амжилттай хууллаа!", + "QUICK_TRADE_MODE": "Хялбар арилжаа", + "JUST_NOW": "яг одоо", + "PAIR": "Хослол", + "ZERO_ASSET": "Танд хөрөнгө байхгүй байна", + "DEPOSIT_ASSETS": "Хөрөнгө орлогодох", + "SEARCH_TXT": "Хайх", + "SEARCH_ASSETS": "Хөрөнгө хайх", + "TOTAL_ASSETS_VALUE": "Таны хөрөнгийн багцаа {0}: {1}", + "SUMMARY": { + "TITLE": "Хураангуй", + "URGENT_REQUIREMENTS": "Яаралтай шаардлага", + "TRADING_VOLUME": "Арилжааны хэмжээ", + "ACCOUNT_ASSETS": "Хэрэглэгчийн хөрөнгүүд", + "ACCOUNT_DETAILS": "Хэрэглэгчийн мэдээлэл", + "VIEW_FEE_STRUCTURE": "Шимтгэлийн шатлал, хэмжээ", + "UPGRADE_ACCOUNT": "Түвшин ахиулах", + "ACTIVE_2FA_SECURITY": "2FA Хамгаалалт идэвхижүүлэх", + "ACCOUNT_ASSETS_TXT_1": "Энэ хэсэгт таны бүх хөрөнгийн хураангуй байна.", + "ACCOUNT_ASSETS_TXT_2": "Их хэмжээний хөрөнгөтэй байх нь танд өвөрмөц тэмдэг, арилжааны хураамж бага зэрэг дансны шинэчлэлт хийх эрх олгоно.", + "ACCOUNT_DETAILS_TXT_1": "Таны дансны төрөл нь таны дансны тэмдэг, арилжааны хураамж, хадгаламж, мөнгө авах хязгаарыг тодорхойлдог.", + "ACCOUNT_DETAILS_TXT_2": "Таны арилжааны дансны нас, үйл ажиллагааны түвшин болон дансны нийт хөрөнгийн хэмжээ таны дансыг шинэчлэх боломжтой эсэхийг тодорхойлно.", + "ACCOUNT_DETAILS_TXT_3": "Дансны түвшингээ хадгалахын тулд тогтмол арилжаа хийх, тодорхой хэмжээний хадгалуулсан хөрөнгийг хадгалах шаардлагатай.", + "ACCOUNT_DETAILS_TXT_4": "Үйл ажиллагаа болон хөрөнгийг хадгалахгүй бол дансны зэрэглэлийг үе үе бууруулна.", + "REQUIREMENTS": "Шаардлага", + "ONE_REQUIREMENT": "Аль нэгийг хангасан:", + "REQUEST_ACCOUNT_UPGRADE": "Түвшин ахиулах хүсэлт илгээх", + "FEES_AND_LIMIT": "{0} Шимтгэл & Лимит", + "FEES_AND_LIMIT_TXT_1": "Крипто арилжаачин болох нь шинэ эхлэлийг илтгэнэ. Оюун ухаан, хүсэл зориг, хурдаараа зэвсэглэсэн, эрсдэлд орж, арилжаа хийснээр та дансаа шинэчлэх боломжтой болно.", + "FEES_AND_LIMIT_TXT_2": "Данс бүр өөрийн гэсэн шимтгэлтэй, мөнгө байршуулах, авах хязгаартай.", + "TRADING_FEE_STRUCTURE": "Орлого зарлагын хэмжээ", + "DEPOSIT_AND_WITHDRAWAL_FEES": "Орлого, зарлагын шимтгэлүүд", + "WITHDRAWAL": "Зарлага", + "DEPOSIT": "Орлого", + "TAKER": "Taker", + "MAKER": "Maker", + "DEPOSITS": "Орлогууд", + "WITHDRAWALS": "Зарлагууд", + "NOMINAL_TRADING_WITH_MONTH": "{0} сарын арилжааны хэмжээ", + "LEVEL_OF_ACCOUNT": "{0} дүгээр түвшний бүртгэл", + "TITLE_OF_ACCOUNT": "{0} Бүртгэл", + "LEVEL_TXT_DEFAULT": "Өөрийн түвшний тайлбарыг энд нэмнэ үү", + "CURRENT_TXT": "Одоогийн", + "EMAIL_VERIFICATION": "Имэйл баталгаажуулалт", + "DOCUMENTS": "Баримт бичиг", + "HAP_TEXT": "NCX түншлэлийн хөтөлбөр (HAP) {0}", + "LOCK_AN_EXCHANGE": "Exchange-г түгжих {0}", + "WALLET_SUBSCRIPTION_USERS": "Vault захиалгын хэрэглэгчид {0}", + "TRADE_OVER_XHT": "{0} USDT-с дээш үнээр худалдаа хийнэ", + "XHT_IN_WALLET": "Хэтэвчинд {0} XHT", + "TASKS": "Даалгаврууд", + "INVITE_USER": "Хэрэглэгчдийг урьж, тэдний арилжаанаас шимтгэл аваарай", + "DISCOUNT": "({0}% хөнгөлөлт)", + "MY_FEES_LIMITS": "Миний хураамж ба хязгаарлалт", + "MARKETS": "Арилжаа", + "CHANGE_24H": "24 цагийн өөрчлөлт", + "VOLUME_24H": "24 цагийн хэмжээ", + "VIEW_MORE_MARKETS": "Бүх хослол" + }, + "REFERRAL_LINK": { + "TITLE": "Найзаа урих", + "INFO_TEXT": "Та найзаа урьснаар танд олон төрлийн урамшуулал очих болно.", + "COPY_FIELD_LABEL": "Доорх холбоосыг найзуудтайгаа хуваалцаж, урамшуулал аваарай:", + "REFERRED_USER_COUT": "Та {0} найз урьсан байна", + "COPY_LINK_BUTTON": "ЛИНК ХУУЛАХ" + }, + "NOT_LOGGEDIN": { + "TXT_1": "Арилжаа эхлүүлэхийн тулд та нэвтрэх ёстой", + "TXT_2": "{0} эсвэл {1}", + "LOGIN_HERE": "энд нэвтэрнэ үү" + }, + "USER_LEVEL": "Хэрэглэгчийн түвшин", + "LIMIT_AMOUNT": "Лимит", + "FEE_AMOUNT": "Шимтгэл", + "COINS": "Койн", + "PAIRS": "Хослол", + "NOTE_FOR_EDIT_COIN": "Жич: {0}-г нэмэх, хасахын тулд {1}-г үзнэ үү.", + "REFER_DOCS_LINK": "баримт бичиг", + "EXPIRED_INFO_1": "Таны trial дууслаа. ", + "EXPIRED_INFO_2": "Биржийг дахин идэвхжүүлэхийн тулд барьцаа хөрөнгөө тавина уу.", + "EXPIRED_BUTTON_TXT": "EXCHANGE-г ИДЭВХЖҮҮЛЭХ", + "TRADE_POSTS": { "LEARN_MORE": "Илүү их судла" }, + "OPEN_WALLET": "Хэтэвч", + "CUMULATIVE_AMOUNT_SYMBOL": "Нийт", + "POST_ONLY": "Post only", + "CLEAR": "Арилгах", + "ORDER_TYPE": "Төрөл", + "ORDER_MODE": "Хэлбэр", + "TRIGGER_CONDITIONS": "Хэрэгжих нөхцөл", + "TRANSACTION_STATUS": { + "PENDING": "Хүлээгдэж буй", + "REJECTED": "Татгалзсан", + "COMPLETED": "Дууссан" + }, + "DEPOSIT_STATUS": { + "NEW": "Шинэ", + "SEARCH_FIELD_LABEL": "Гүйлгээний ID хуулж тавина уу", + "CHECK_DEPOSIT_STATUS": "Орлого шалгах", + "SEARCH_BLOCKCHAIN_FOR_DEPOSIT": "Хадгаламжиндаа блокчейн хайх", + "STATUS_DESCRIPTION": "Та гүйлгээний ID (hash) доорх нүдэнд тавьж орлогын явцыг шалгах боломжтой.", + "TRANSACTION_ID": "Гүйлгээний ID (hash)", + "SEARCH_SUCCESS": "Хайлт дууслаа", + "ADDRESS_FIELD_LABEL": "Хаяг хуулж тавина уу", + "CURRENCY_FIELD_LABEL": "Валют сонгоно уy" + }, + "CANCEL_ORDERS": { + "HEADING": "Захиалга цуцлах", + "SUB_HEADING": "Бүх захиалгыг цуцлах", + "INFO_1": "Энэ үйлдэл хийгдсэн бүх хослол дээрх бүх захиалга цуцлагдана.", + "INFO_2": "Та бүх захиалгаа цуцлахдаа итгэлтэй байна уу?" + }, + "AMOUNT_IN": "Оруулсан дүн", + "LIMITS_BLOCK": { + "HEADER_ROW_DESCRIPTION": "24ц дотор хийх орлого зарлагын хэмжээ ({0})", + "HEADER_ROW_TYPE": "Төрөл (Бүх хөрөнгө)", + "HEADER_ROW_AMOUNT": "24ц Хэмжээ ({0})" + }, + "MARKETS_TABLE": { + "TITLE": "Арилжааны хослолууд", + "MARKETS": "Арилжаа", + "LAST_PRICE": "Сүүлийн үнэ", + "CHANGE_24H": "Өөрчлөлт (24 цаг)", + "VOLUME_24h": "Хэмжээ (24 цаг)", + "CHART_24H": "График (24 фаг)", + "VIEW_MARKETS": "Арилжаанд оролцох" + }, + "PAGE_UNDER_CONSTRUCTION": "Энэ хуудас засвартай байгаа тул та дараа зочилж үзнэ үү.", + "UNDEFINED_ERROR_TITLE": "Тодорхойгүй алдаа гарлаа", + "UNDEFINED_ERROR": "Wow! Тодорхойгүй алдаа гарлаа. Та дараа дахин оролдох эсвэл хуудсыг дахин ачааллуулж үзнэ үү.", + "POST_ONLY_TOOLTIP": "Post only захиалга нь лимит захиалгаар явагдана.", + "REFRESH": "Шинэчлэх", + "FEE_REDUCTION": "Шимтгэл бууралт", + "FEE_REDUCTION_DESCRIPTION": "*таны дансанд шимтгэлийн хөнгөлөлт үзүүлсэн байна. Хөнгөлөлт нь таны хийж буй арилжааны Шимтгэлид хамаарна.", + "CHANGE_PASSWORD_FAILED": "Нууц үг солиход алдаа гарлаа", + "MARKET_OPTIONS": { "LIST": "Жагсаалт", "CARD": "Card" }, + "ALL": "Бүгд", + "ASSET_TXT": "Хөрөнгө", + "ONE_DAY": "1 өдөр", + "ONE_WEEK": "7 хоног", + "MONTH": "{0} сар", + "START_DATE": "Эхлэх огноо", + "END_DATE": "Дуусах огноо", + "REGULAR": "Энгийн", + "STOPS": "Зогсох хязгаар", + "VIEW_ALL": "бүгд", + "TRIGGER_PRICE": "Триггер үнэ", + "SPEND_AMOUNT": "Зарцуулсан хэмжээ", + "ESTIMATE_RECEIVE_AMOUNT": "Хүлээн авах дүнгийн хэмжээ", + "TOOLS": { + "ORDERBOOK": "Захиалгын дэлгэрэнгүй", + "CHART": "Үнийн график", + "PUBLIC_SALES": "Олон нийтийн борлуулалт", + "ORDER_ENTRY": "захиалга оруулах", + "RECENT_TRADES": "Миний сүүлд хийсэн арилжаа", + "OPEN_ORDERS": "Нээлттэй захиалга", + "WALLET": "Хэтэвч", + "DEPTH_CHART": "График", + "COMING_SOON": "тун удахгүй" + }, + "WALLET_BALANCE_LOADING": "Үлдэгдэл ачаалж байна...", + "LOADING": "Ачаалж байна...", + "CONNECT_VIA_DESKTOP": { + "TITLE": "Суурин компьютерээр холбогдоно уу", + "SUBTITLE": "Таны мобайл төхөөрөмжөөр дамжуулан DeFi stake хийхийг одоогоор дэмжихгүй байна.", + "TEXT": "Хэтэвчээ холбохын тулд сууран/зөөврийн компьютер ашиглана уу." + }, + "ORDER_HISTORY_CLOSED": "Хаалттай", + "FIAT": { + "UNVERIFIED": { + "TITLE": "Бүрэн баталгаажуулалт", + "TEXT": "{0}-г хийхийн тулд та өөрийн банкны дэлгэрэнгүй мэдээллийг баталгаажуулалтаа дуусгах шаардлагатай. Доорх үргэлжлүүлэх товчийг дарна уу.", + "DEPOSIT": "хадгаламж", + "WITHDRAWAL": "татах" + }, + "REVIEW_DEPOSIT": { + "TITLE": "Орлогын дэлгэрэнгүй мэдээллийг шалгаж баталгаажуулна уу", + "SUBTITLE": "Бүх зүйл зөв эсэхийг шалгахын тулд доорх орлогын дэлгэрэнгүй мэдээллийг шалгана уу.", + "FORMAT": "{0} {1}", + "AMOUNT": "Орлогын хэмжээ", + "FEE": "Орлогын хураамж", + "TRANSACTION_ID": "Гүйлгээний ID", + "NOTE": "Саатал гарахаас зайлсхийхийн тулд орлогын дүн, тэмдэглэл болон гүйлгээний ID нь дээрх дэлгэрэнгүй мэдээлэлтэй тохирч байгаа эсэхийг шалгаарай. Хэрэв асуудал гарвал холбоо барина уу.", + "BACK": "Буцах", + "PROCEED": "Үргэлжлүүлэх" + } + }, + "DEPOSIT_FEE_NOTE": "Таны дансанд байршуулсан нийт дүн нь орлогын хураамжийг хассан дүн байх болно гэдгийг анхаарна уу", + "AMOUNT_LABEL": "Орлогын дүнг оруулна уу (бодит дүнтэй ижил байх ёстой)", + "TRANSACTION_ID_LABEL": "Орлогын гүйлгээний ID-г оруулна уу", + "FEE_LABEL": "Орлогын хураамж", + "AMOUNT_FORMAT": "{0} {1}", + "PENDING_DEPOSIT_TITLE": "Орлого хүлээгдэж байна", + "PENDING_DEPOSIT_TEXT_1": "Таны орлого хүсэлт баталгаажуулах хийхийг хүлээж байна.", + "PENDING_DEPOSIT_TEXT_2": "Орлого 24 цагийн дотор арилдаг боловч 48 цаг хүртэл хугацаа шаардагдах бөгөөд таны хадгаламжийн дүнг хассаны дараа шимтгэл таны үлдэгдэлд орно.", + "DEPOSIT_HOME_NOTE": "Орлогоо хийхдээ өөрийн данснаас мөнгө байршуулах мөнгөө оруулна уу. Саатал гарахаас зайлсхийхийн тулд доор оруулсан дүн таны данснаас илгээх эсвэл илгээсэн бодит дүнтэй тохирч байгаа эсэхийг шалгаарай.", + "DEPOSIT_TXID_NOTE": "Орлогоо хойшлуулахаас зайлсхийхийн тулд гүйлгээний тэмдэглэл эсвэл мессежийн дотор банкны мөнгө хийхдээ дээр харуулсан өвөрмөц тэмдэглэлээ оруулна уу. Доорх дүн нь таны банкнаас байршуулсантай тохирч байгаа эсэх, мөн гүйлгээ хийсний дараа банкнаас өгсөн гүйлгээний ID-г оруулсан эсэхээ шалгаарай.", + "DEPOSIT_BANK_TEXT": "Доорх банкны дэлгэрэнгүй мэдээллийг ашиглан мөнгө байршуулна уу.", + "MIN_DEPOSIT": "Орлогын доод хэмжээ", + "MAX_DEPOSIT": "Орлогын дээд хэмжээ", + "BACK": "буцах", + "DONE": "Дууслаа", + "PENDING_WITHDRAWAL_TITLE": "Зарлагын хүсэлт хүлээгдэж байна", + "PENDING_WITHDRAWAL_TEXT_1": "Таны зарлагын хүсэлтийг баталгаажуулах шаардлагатай тул та түр хүлээнэ үү.", + "DEPOSIT_AMOUNT_MIN_VALIDATION": "Гүйлгээ илгээх хэмжээ бага байна. Илүү их хэмжээгээр туршиж үзээрэй.", + "DEPOSIT_AMOUNT_MAX_VALIDATION": "Гүйлгээ илгээх хэмжээ их байна. Бага хэмжээгээр туршиж үзээрэй.", + "ACCOUNT_NAME": "дансны нэр", + "ACCOUNT_NUMBER": "дансны дугаар", + "BANK_NAME": "банкны нэр", + "VERIFY_BANK_WITHDRAW": "Зардага гаргахын тулд та өөрийн банкны дэлгэрэнгүй мэдээллийн баталгаажуулалтаа дуусгах шаардлагатай. Доорх үргэлжлүүлэх товчийг дарна уу.", + "VERIFICATION_TITLE": "Баталгаажуулалт", + "WITHDRAW_NOTE": "Анхаарна уу: Та зөвхөн өөрийн нэр дээрх дансруу мөнгө татах боломжтой.", + "USER_PAYMENT": { "TITLE": "Төлбөр" }, + "QUOTE_CONFIRMATION_MSG_TEXT_1": "Доорх захиалгаа шалгаад баталгаажуулна уу.", + "QUOTE_CONFIRMATION_MSG_TEXT_2": "Хүлээн авах дүн нь урьдчилсан тооцоо бөгөөд арилжааны хураамжийг оруулаагүй болно", + "ORDER_EXPIRED_MSG": "Захиалгын хугацаа дууссан. Дахин ачааллуулна уу?", + "WITHDRAWALS_FORM_METHOD": "Арга", + "WITHDRAWALS_FORM_ADDRESS_EXCHANGE": "Имэйл солилцох", + "WITHDRAWALS_FORM_EXCHANGE_PLACEHOLDER": "Хэрэглэгчийн имэйлийг оруулна уу", + "WITHDRAWALS_FORM_MAIL_INFO": "Бирж дээрх хэрэглэгчийн имэйлийг оруулаад үнэгүй шилжүүлээрэй." +} \ No newline at end of file From 7d30fa46fbaab5b7a70c40a50ab636101f0a04d1 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 19 Oct 2022 12:56:06 +0900 Subject: [PATCH 130/132] mongolian language web support and migration --- .../20221019035508-add-mongolian-mail.js | 20 +++++++++++++++++++ web/src/config/lang/index.js | 4 +++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 server/db/migrations/20221019035508-add-mongolian-mail.js diff --git a/server/db/migrations/20221019035508-add-mongolian-mail.js b/server/db/migrations/20221019035508-add-mongolian-mail.js new file mode 100644 index 0000000000..f4f817be6e --- /dev/null +++ b/server/db/migrations/20221019035508-add-mongolian-mail.js @@ -0,0 +1,20 @@ +'use strict'; +const TABLE = 'Statuses'; +const COLUMN = 'email'; + +const mongolian = JSON.stringify(require('../../mail/strings/mn.json')); + +module.exports = { + async up(queryInterface) { + await queryInterface.sequelize.query( + `UPDATE public."${TABLE}" + SET ${COLUMN} = ${COLUMN} || '${mongolian}' + `); + }, + + down: () => { + return new Promise((resolve) => { + resolve(); + }); + } +}; diff --git a/web/src/config/lang/index.js b/web/src/config/lang/index.js index 25ed0aaba3..9130c9d54f 100644 --- a/web/src/config/lang/index.js +++ b/web/src/config/lang/index.js @@ -11,6 +11,7 @@ import id from './id.json'; import zh from './zh.json'; import pt from './pt.json'; import tr from './tr.json'; +import tr from './mn.json'; export default { en, @@ -25,5 +26,6 @@ export default { id, zh, pt, - tr + tr, + mn }; From 90e164877731c45b9367e6fd46cfc5a7ee013042 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 19 Oct 2022 14:03:59 +0900 Subject: [PATCH 131/132] lang index fix --- web/src/config/lang/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/config/lang/index.js b/web/src/config/lang/index.js index 9130c9d54f..3a5a2999da 100644 --- a/web/src/config/lang/index.js +++ b/web/src/config/lang/index.js @@ -11,7 +11,7 @@ import id from './id.json'; import zh from './zh.json'; import pt from './pt.json'; import tr from './tr.json'; -import tr from './mn.json'; +import mn from './mn.json'; export default { en, From 24d6bcbf7e4c3aaf78ba6e2dba1e0fb6a34687e0 Mon Sep 17 00:00:00 2001 From: Ali Beikverdi <ali@bitholla.com> Date: Wed, 19 Oct 2022 17:30:09 +0900 Subject: [PATCH 132/132] updated index.css --- web/src/index.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/index.css b/web/src/index.css index e3ac4eaae8..0d66afa8f7 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -1444,7 +1444,8 @@ table th { display: flex; justify-content: center; align-items: center; - background-color: var(--base_wallet-sidebar-and-popup); + background-color: transparent; + border-bottom: 1px var(--calculated_secondary-border) solid; opacity: 0.8; } .html_card_section .card-section-wrapper { display: flex;