From a06c4aad49e17fa4f55355663e5c6161d49eba73 Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Mon, 22 Mar 2021 11:42:24 -0500 Subject: [PATCH] Manage numbers. Closes #8, Closes #7, Closes #6, Closes #5, Closes #4 (#14) * Adding numerous number features * Renaming cancel number labels * Updating changelog & version * Removing preview tag * Correcting format of copied number --- .github/workflows/CD.yml | 1 - .gitignore | 1 + .vscodeignore | 8 +- CHANGELOG.md | 26 +- README.md | 12 +- package-lock.json | 832 +++++++++++++++++++- package.json | 132 +++- package.nls.json | 2 +- resources/icon_activitybar.svg | 18 +- resources/icons/dark/active-call-line.svg | 16 + resources/icons/dark/add-call-line.svg | 14 + resources/icons/dark/app-line.svg | 14 + resources/icons/dark/app-plus-line.svg | 12 + resources/icons/dark/books-line.svg | 12 + resources/icons/dark/chain-line.svg | 12 + resources/icons/dark/delete-line.svg | 14 + resources/icons/dark/emoji-line.svg | 12 + resources/icons/dark/error-line.svg | 14 + resources/icons/dark/key-line.svg | 12 + resources/icons/dark/leave-line.svg | 16 + resources/icons/dark/refresh-line.svg | 11 + resources/icons/dark/wallet-line.svg | 12 + resources/icons/light/active-call-line.svg | 16 + resources/icons/light/add-call-line.svg | 14 + resources/icons/light/app-line.svg | 14 + resources/icons/light/app-plus-line.svg | 12 + resources/icons/light/books-line.svg | 12 + resources/icons/light/chain-line.svg | 12 + resources/icons/light/delete-line.svg | 14 + resources/icons/light/emoji-line.svg | 12 + resources/icons/light/error-line.svg | 14 + resources/icons/light/key-line.svg | 12 + resources/icons/light/leave-line.svg | 16 + resources/icons/light/refresh-line.svg | 11 + resources/icons/light/wallet-line.svg | 12 + src/client/api/numbers.ts | 145 +++- src/commands/numbersCommands.ts | 40 +- src/enums/storageKeys.ts | 10 +- src/extension.ts | 12 +- src/steps/assignNumberFlow.ts | 49 ++ src/steps/buyNumberFlow.ts | 150 ++++ src/steps/index.ts | 1 + src/telemetry.ts | 4 +- src/utils/getCountries.ts | 124 +++ src/utils/getIconPath.ts | 8 + src/utils/getNumberTypes.ts | 10 + src/utils/getSearchPatterns.ts | 9 + src/utils/index.ts | 4 + src/views/account.ts | 2 +- src/views/application.ts | 46 +- src/views/help.ts | 10 +- src/views/number.ts | 112 ++- src/views/trees/applicationTreeItem.ts | 2 +- src/views/trees/baseTreeItem.ts | 3 +- src/views/trees/baseTreeViewDataProvider.ts | 2 +- src/views/trees/numberTreeItem.ts | 9 +- webpack.config.js | 11 - 57 files changed, 2026 insertions(+), 111 deletions(-) create mode 100644 resources/icons/dark/active-call-line.svg create mode 100644 resources/icons/dark/add-call-line.svg create mode 100644 resources/icons/dark/app-line.svg create mode 100644 resources/icons/dark/app-plus-line.svg create mode 100644 resources/icons/dark/books-line.svg create mode 100644 resources/icons/dark/chain-line.svg create mode 100644 resources/icons/dark/delete-line.svg create mode 100644 resources/icons/dark/emoji-line.svg create mode 100644 resources/icons/dark/error-line.svg create mode 100644 resources/icons/dark/key-line.svg create mode 100644 resources/icons/dark/leave-line.svg create mode 100644 resources/icons/dark/refresh-line.svg create mode 100644 resources/icons/dark/wallet-line.svg create mode 100644 resources/icons/light/active-call-line.svg create mode 100644 resources/icons/light/add-call-line.svg create mode 100644 resources/icons/light/app-line.svg create mode 100644 resources/icons/light/app-plus-line.svg create mode 100644 resources/icons/light/books-line.svg create mode 100644 resources/icons/light/chain-line.svg create mode 100644 resources/icons/light/delete-line.svg create mode 100644 resources/icons/light/emoji-line.svg create mode 100644 resources/icons/light/error-line.svg create mode 100644 resources/icons/light/key-line.svg create mode 100644 resources/icons/light/leave-line.svg create mode 100644 resources/icons/light/refresh-line.svg create mode 100644 resources/icons/light/wallet-line.svg create mode 100644 src/steps/assignNumberFlow.ts create mode 100644 src/steps/buyNumberFlow.ts create mode 100644 src/utils/getCountries.ts create mode 100644 src/utils/getIconPath.ts create mode 100644 src/utils/getNumberTypes.ts create mode 100644 src/utils/getSearchPatterns.ts diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index 8bcf19c..41f137c 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -25,4 +25,3 @@ jobs: - name: Publish VSIX run: | npx vsce publish --packagePath ./release.vsix --pat ${{ secrets.VSCE_PAT }} - diff --git a/.gitignore b/.gitignore index fdc8d0c..8214b25 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ out +dist node_modules .vscode-test/ *.vsix diff --git a/.vscodeignore b/.vscodeignore index ed3f9d3..5145777 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,10 +1,14 @@ .vscode/** .vscode-test/** -out/test/** +out/** src/** +test/** .gitignore vsc-extension-quickstart.md **/tsconfig.json **/tslint.json **/*.map -**/*.ts \ No newline at end of file +**/*.ts +.github +node_modules +webpack.config.js \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b3a0cd7..e1d0fac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +--- + +## [1.0.0] - 2021-03-13 + ### Added -- Adding unit tests for existing extension capabilities -- CI process now confirms all tests pass on Linux, macOS, and Windows +- Applications now display as tree views that can be expanded to see assigned numbers +- Numbers can be assigned to an application using the context menu on the Numbers view +- Numbers can be removed from an application using the context menu on the Applications view +- Numbers can be purchased using the context button on the Numbers view +- Numbers can be canceled using the context menu on a number in any tree view +- Numbers can be copied to the clipboard using the context menu on a number in any tree view ---- +### Updated + +- Extension now uses approved Vonage icons +- Numbers view renamed Unassigned Numbers to reflect that it only displays numbers unassigned from an application + +### Infrastructure + +- CI process now confirms all tests pass on Linux, macOS, and Windows +- CD process now generates versions on creation of releases in GitHub +- Adding unit tests for existing extension capabilities ## [0.0.10] - 2021-03-08 @@ -40,6 +57,7 @@ from using them if they weren't authenticated - Quick access to Vonage API dashboard - Initial README, CONTRIBUTING, etc. -[unreleased]: https://github.com/vonage/vscode/compare/0.0.10...HEAD +[unreleased]: https://github.com/vonage/vscode/compare/1.0.0...HEAD +[1.0.0]: https://github.com/vonage/vscode/compare/0.0.10...1.0.0 [0.0.10]: https://github.com/vonage/vscode/compare/0.0.8...0.0.10 [0.0.8]: https://github.com/vonage/vscode/compare/edc07b4...0.0.8 diff --git a/README.md b/README.md index 876ea34..649ff2f 100644 --- a/README.md +++ b/README.md @@ -29,15 +29,19 @@ key & secret. The Applications view provides capabilities to create, rename, and delete applications. Additionally, capabilities (voice, messages, RTC, VBC) and be added, edited, and removed from applications via the right-click context menu. You can also open the application -within the Vonage API dashboard. +within the Vonage API dashboard. Expanding an application will display all numbers +associated with the application and provide the ability to remove that number from the +application. -![Example of adding, editing and deleting an application with the extension](https://user-images.githubusercontent.com/1228996/110222548-af883480-7e98-11eb-9d3b-04ae9d3fda5e.gif) +![Example of adding, editing and deleting an application with the extension](https://user-images.githubusercontent.com/1228996/111038005-4e0e1b80-83ec-11eb-9f18-cba8619e7d04.gif) ### Numbers -The Numbers view provides the ability to view all purchased numbers in your account. +The Numbers view provides the ability to view all purchased numbers in your account +that aren't assigned to an application, purchase new numbers, cancel a number, or copy +a number to your clipboard. -![Viewing numbers and their capabilities from within the extension](https://user-images.githubusercontent.com/1228996/110222715-e4e15200-7e99-11eb-8ee3-172f30dc524c.gif) +![Viewing numbers and their capabilities from within the extension](https://user-images.githubusercontent.com/1228996/111038150-0c31a500-83ed-11eb-8412-62811001a2a1.gif) ### Account diff --git a/package-lock.json b/package-lock.json index 693cec0..551c045 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode", - "version": "0.0.9", + "version": "0.0.10", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -43,6 +43,12 @@ } } }, + "@discoveryjs/json-ext": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz", + "integrity": "sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==", + "dev": true + }, "@eslint/eslintrc": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", @@ -160,6 +166,32 @@ "integrity": "sha512-rYff6FI+ZTKAPkJUoyz7Udq3GaoDZnxYDEvdEdFZASiA7PoErltHezDishqQiSDWrGxvxmplH304jyzQmjp0AQ==", "dev": true }, + "@types/eslint": { + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz", + "integrity": "sha512-EHXbc1z2GoQRqHaAT7+grxlTJ3WE2YNeD6jlpPoRc83cCoThRY+NUWjCUZaYmk51OICkPXn2hhphcWcWXgNW0Q==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", + "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.46", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz", + "integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==", + "dev": true + }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -371,6 +403,185 @@ "uuid": "^8.3.0" } }, + "@webassemblyjs/ast": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", + "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz", + "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz", + "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz", + "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz", + "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.0", + "@webassemblyjs/helper-api-error": "1.11.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz", + "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz", + "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz", + "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", + "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz", + "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz", + "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/helper-wasm-section": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0", + "@webassemblyjs/wasm-opt": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0", + "@webassemblyjs/wast-printer": "1.11.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz", + "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/ieee754": "1.11.0", + "@webassemblyjs/leb128": "1.11.0", + "@webassemblyjs/utf8": "1.11.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz", + "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz", + "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-api-error": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/ieee754": "1.11.0", + "@webassemblyjs/leb128": "1.11.0", + "@webassemblyjs/utf8": "1.11.0" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz", + "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.0", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.1.tgz", + "integrity": "sha512-B+4uBUYhpzDXmwuo3V9yBH6cISwxEI4J+NO5ggDaGEEHb0osY/R7MzeKc0bHURXQuZjMM4qD+bSJCKIuI3eNBQ==", + "dev": true + }, + "@webpack-cli/info": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.2.tgz", + "integrity": "sha512-5U9kUJHnwU+FhKH4PWGZuBC1hTEPYyxGSL5jjoBI96Gx8qcYJGOikpiIpFoTq8mmgX3im2zAo2wanv/alD74KQ==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.3.0.tgz", + "integrity": "sha512-k2p2VrONcYVX1wRRrf0f3X2VGltLWcv+JzXRBDmvCxGlCeESx4OXw91TsWeKOkp784uNoVQo313vxJFHXPPwfw==", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -403,6 +614,12 @@ "uri-js": "^4.2.2" } }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -542,6 +759,12 @@ "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", "dev": true }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, "binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", @@ -611,6 +834,19 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "browserslist": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001181", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.649", + "escalade": "^3.1.1", + "node-releases": "^1.1.70" + } + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -676,6 +912,12 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "caniuse-lite": { + "version": "1.0.30001199", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001199.tgz", + "integrity": "sha512-ifbK2eChUCFUwGhlEzIoVwzFt1+iriSjyKKFYNfv6hN34483wyWpLLavYQXhnR036LhkdUYaSDpHg1El++VgHQ==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -782,6 +1024,15 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -845,6 +1096,17 @@ "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", "dev": true }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -903,6 +1165,12 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -911,6 +1179,12 @@ "delayed-stream": "~1.0.0" } }, + "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 + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -922,6 +1196,22 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, + "copy-webpack-plugin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-7.0.0.tgz", + "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", + "dev": true, + "requires": { + "fast-glob": "^3.2.4", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1" + } + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1091,12 +1381,24 @@ "safe-buffer": "^5.0.1" } }, + "electron-to-chromium": { + "version": "1.3.687", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.687.tgz", + "integrity": "sha512-IpzksdQNl3wdgkzf7dnA7/v10w0Utf1dF2L+B4+gKrloBrxCut+au+kky3PYvle3RMdSxZP+UiCZtLbcYRxSNQ==", + "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 }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1105,6 +1407,16 @@ "once": "^1.4.0" } }, + "enhanced-resolve": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", + "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -1114,6 +1426,27 @@ "ansi-colors": "^4.1.1" } }, + "envinfo": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.4.tgz", + "integrity": "sha512-TQXTYFVVwwluWSFis6K2XKxgrD22jEv0FTuLCQI+OjH7rn93+iY0fSSFM5lrSxFY+H1+B0/cvvlamr3UsBivdQ==", + "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" + } + }, + "es-module-lexer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz", + "integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==", + "dev": true + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1328,6 +1661,12 @@ "through": "^2.3.8" } }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, "execa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", @@ -1401,6 +1740,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, "fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -1521,6 +1866,12 @@ } } }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "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", @@ -1598,6 +1949,12 @@ "is-glob": "^4.0.1" } }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, "globals": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", @@ -1700,6 +2057,15 @@ "har-schema": "^2.0.0" } }, + "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-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1788,6 +2154,16 @@ "resolve-from": "^4.0.0" } }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1814,6 +2190,12 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -1839,6 +2221,15 @@ "binary-extensions": "^2.0.0" } }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1882,6 +2273,15 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, + "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==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -1902,11 +2302,45 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "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 + }, + "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" + } + } + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1933,6 +2367,12 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" }, + "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": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -1954,6 +2394,15 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -2024,6 +2473,12 @@ "json-buffer": "3.0.0" } }, + "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", @@ -2040,6 +2495,23 @@ "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", "dev": true }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -2131,6 +2603,16 @@ "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", "dev": true }, + "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" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2488,6 +2970,12 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, + "node-releases": { + "version": "1.1.71", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", + "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "dev": true + }, "noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", @@ -2649,6 +3137,12 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, "path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -2698,6 +3192,60 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "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" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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 + } + } + }, "prebuild-install": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.0.1.tgz", @@ -2742,6 +3290,12 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -2832,6 +3386,15 @@ "picomatch": "^2.2.1" } }, + "rechoir": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", + "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, "regexpp": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", @@ -2902,6 +3465,33 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2956,6 +3546,17 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -2981,6 +3582,15 @@ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "dev": true }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3094,6 +3704,12 @@ } } }, + "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.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3266,6 +3882,12 @@ } } }, + "tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "dev": true + }, "tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -3301,6 +3923,39 @@ } } }, + "terser": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.0.tgz", + "integrity": "sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz", + "integrity": "sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q==", + "dev": true, + "requires": { + "jest-worker": "^26.6.2", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.5.1" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3348,6 +4003,47 @@ "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", "dev": true }, + "ts-loader": { + "version": "8.0.18", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.18.tgz", + "integrity": "sha512-hRZzkydPX30XkLaQwJTDcWDoxZHK6IrEMDQpNd7tgcakFruFkeUp/aY+9hBb7BUGb+ZWKI0jiOGMo0MckwzdDQ==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "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" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.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 + } + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -3557,6 +4253,134 @@ "unzipper": "^0.10.11" } }, + "watchpack": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", + "integrity": "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "webpack": { + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.25.0.tgz", + "integrity": "sha512-jqQZopNCzt9c4K6Qa7j6kIhzHfR9wgF84go58VoNp4JbZrBr2D2l5lcv72CW80yc6NJl8CR6OY8xctnIs0r2uw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.46", + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/wasm-edit": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0", + "acorn": "^8.0.4", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.7.0", + "es-module-lexer": "^0.4.0", + "eslint-scope": "^5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.1", + "watchpack": "^2.0.0", + "webpack-sources": "^2.1.1" + }, + "dependencies": { + "acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", + "dev": true + } + } + }, + "webpack-cli": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.5.0.tgz", + "integrity": "sha512-wXg/ef6Ibstl2f50mnkcHblRPN/P9J4Nlod5Hg9HGFgSeF8rsqDGHJeVe4aR26q9l62TUJi6vmvC2Qz96YJw1Q==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.0.1", + "@webpack-cli/info": "^1.2.2", + "@webpack-cli/serve": "^1.3.0", + "colorette": "^1.2.1", + "commander": "^7.0.0", + "enquirer": "^2.3.6", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "v8-compile-cache": "^2.2.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.1.0.tgz", + "integrity": "sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==", + "dev": true + }, + "execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", + "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", + "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", + "dev": true, + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3584,6 +4408,12 @@ "string-width": "^1.0.2 || 2" } }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, "windows-release": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", diff --git a/package.json b/package.json index 60d6cff..ca54a71 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "vscode", "displayName": "Vonage", - "version": "0.0.10", + "version": "1.0.0", "description": "A Visual Studio Code extension for accessing the Vonage communication APIs.", - "main": "./out/src/extension", + "main": "./dist/extension", "repository": { "type": "git", "url": "https://github.com/vonage/vscode" @@ -23,7 +23,7 @@ }, "publisher": "vonage", "icon": "resources/logo.png", - "preview": true, + "preview": false, "engines": { "vscode": "^1.53.0" }, @@ -61,8 +61,9 @@ "onCommand:vonage.app.vbc.add", "onCommand:vonage.app.vbc.delete", "onCommand:vonage.numbers.refreshNumbersList", - "onCommand:vonage.numbers.filterNumbers", + "onCommand:vonage.numbers.toggleFilter", "onCommand:vonage.numbers.buyNumber", + "onCommand:vonage.numbers.assignNumber", "onCommand:vonage.account.refresh", "onCommand:vonage.account.toggleBalanceView", "onCommand:vonage:login", @@ -89,34 +90,49 @@ "category": "Vonage", "command": "vonage.login", "title": "Sign in", - "icon": "$(lock)" + "icon": { + "light": "resources/icons/light/key-line.svg", + "dark": "resources/icons/dark/key-line.svg" + } }, { "category": "Vonage", "command": "vonage.logout", "title": "Sign out", - "icon": "$(lock)", + "icon": { + "light": "resources/icons/light/leave-line.svg", + "dark": "resources/icons/dark/leave-line.svg" + }, "enablement": "vonage:authenticated" }, { "category": "Vonage", "command": "vonage.help.openDocs", "title": "Open extension documentation", - "icon": "$(book)", + "icon": { + "light": "resources/icons/light/books-line.svg", + "dark": "resources/icons/dark/books-line.svg" + }, "enablement": "view == vonageHelpView" }, { "category": "Vonage", "command": "vonage.help.openReportIssue", "title": "Report issue", - "icon": "$(report)", + "icon": { + "light": "resources/icons/light/error-line.svg", + "dark": "resources/icons/dark/error-line.svg" + }, "enablement": "view == vonageHelpView" }, { "category": "Vonage", "command": "vonage.help.openSurvey", "title": "Rate and provide feedback", - "icon": "$(feedback)", + "icon": { + "light": "resources/icons/light/emoji-line.svg", + "dark": "resources/icons/dark/emoji-line.svg" + }, "enablement": "view == vonageHelpView" }, { @@ -129,7 +145,10 @@ "category": "Vonage", "command": "vonage.account.refresh", "title": "Refresh account", - "icon": "$(refresh)", + "icon": { + "light": "resources/icons/light/refresh-line.svg", + "dark": "resources/icons/dark/refresh-line.svg" + }, "enablement": "vonage:authenticated && view == vonageAccountView" }, { @@ -142,35 +161,46 @@ "category": "Vonage", "command": "vonage.app.refreshAppsList", "title": "Refresh applications list", - "icon": "$(refresh)", + "icon": { + "light": "resources/icons/light/refresh-line.svg", + "dark": "resources/icons/dark/refresh-line.svg" + }, "enablement": "vonage:authenticated && view == vonageAppView" }, { "category": "Vonage", "command": "vonage.app.addApp", "title": "Create application", - "icon": "$(plus)", + "icon": { + "light": "resources/icons/light/app-plus-line.svg", + "dark": "resources/icons/dark/app-plus-line.svg" + }, "enablement": "vonage:authenticated && view == vonageAppView" }, { "category": "Vonage", "command": "vonage.app.updateApp", "title": "Rename", - "icon": "$(save)", "enablement": "vonage:authenticated && view == vonageAppView" }, { "category": "Vonage", "command": "vonage.app.deleteApp", "title": "Delete application", - "icon": "$(trash)", + "icon": { + "light": "resources/icons/light/delete-line.svg", + "dark": "resources/icons/dark/delete-line.svg" + }, "enablement": "vonage:authenticated && view == vonageAppView" }, { "category": "Vonage", "command": "vonage.app.link", "title": "View in dashboard", - "icon": "$(globe)", + "icon": { + "light": "resources/icons/light/chain-line.svg", + "dark": "resources/icons/dark/chain-line.svg" + }, "enablement": "vonage:authenticated && view == vonageAppView" }, { @@ -243,22 +273,45 @@ "category": "Vonage", "command": "vonage.numbers.refreshNumbersList", "title": "Refresh numbers", - "icon": "$(refresh)", + "icon": { + "light": "resources/icons/light/refresh-line.svg", + "dark": "resources/icons/dark/refresh-line.svg" + }, "enablement": "vonage:authenticated && view == vonageNumbersView" }, { "category": "Vonage", "command": "vonage.numbers.buyNumber", "title": "Buy number", - "icon": "$(plus)", + "icon": { + "light": "resources/icons/light/add-call-line.svg", + "dark": "resources/icons/dark/add-call-line.svg" + }, "enablement": "vonage:authenticated && view == vonageNumbersView" }, { "category": "Vonage", - "command": "vonage.numbers.filterNumbers", - "title": "Filter numbers", - "icon": "$(filter)", + "command": "vonage.numbers.cancelNumber", + "title": "Cancel number", "enablement": "vonage:authenticated && view == vonageNumbersView" + }, + { + "category": "Vonage", + "command": "vonage.numbers.assign", + "title": "Assign number to application", + "enablement": "vonage:authenticated && view == vonageNumbersView" + }, + { + "category": "Vonage", + "command": "vonage.numbers.unassign", + "title": "Remove number from application", + "enablement": "vonage:authenticated && view == vonageAppView" + }, + { + "category": "Vonage", + "command": "vonage.numbers.copy", + "title": "Copy to clipboard", + "enablement": "vonage:authenticated && viewItem =~ /^number[a-z-]*/i" } ], "menus": { @@ -278,11 +331,6 @@ "when": "vonage:authenticated && view == vonageNumbersView", "group": "navigation" }, - { - "command": "vonage.numbers.filterNumbers", - "when": "vonage:authenticated && view == vonageNumbersView", - "group": "navigation" - }, { "command": "vonage.numbers.refreshNumbersList", "when": "vonage:authenticated && view == vonageNumbersView", @@ -364,6 +412,26 @@ "command": "vonage.app.updateApp", "when": "viewItem =~ /^application[a-z-]*/i", "group": "applications_9@1" + }, + { + "command": "vonage.numbers.copy", + "when": "viewItem =~ /^number[a-z-]*/i", + "group": "numbers_1@1" + }, + { + "command": "vonage.numbers.assign", + "when": "view == vonageNumbersView && viewItem =~ /(?!.*-app)^number[a-z-]*/g", + "group": "numbers_1@2" + }, + { + "command": "vonage.numbers.unassign", + "when": "view == vonageAppView && viewItem =~ /^number[a-z-]*/i", + "group": "numbers_1@2" + }, + { + "command": "vonage.numbers.cancelNumber", + "when": "viewItem =~ /^number[a-z-]*/i", + "group": "numbers_2@1" } ] }, @@ -419,11 +487,13 @@ ] }, "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "tsc -p ./", + "package": "npx vsce package -o release.vsix", + "vscode:prepublish": "webpack --mode production", + "compile": "tsc -p ./ && webpack", "lint": "eslint --ext .ts .", "watch": "tsc -watch -p ./", "pretest": "npm run compile", + "webpack": "webpack --mode production", "test": "node ./out/test/runTest.js" }, "dependencies": { @@ -451,14 +521,18 @@ "@typescript-eslint/eslint-plugin": "^4.16.0", "@typescript-eslint/parser": "^4.16.0", "chai": "^4.3.3", + "copy-webpack-plugin": "^7.0.0", "eslint": "^7.21.0", "glob": "^7.1.4", "mocha": "^8.2.1", "sinon": "^9.2.1", "source-map-support": "^0.5.12", + "ts-loader": "^8.0.14", "typescript": "^4.2.2", "vscode-codicons": "0.0.14", "vscode-nls-dev": "^3.3.2", - "vscode-test": "^1.4.1" + "vscode-test": "^1.4.1", + "webpack": "^5.19.0", + "webpack-cli": "^4.4.0" } -} +} \ No newline at end of file diff --git a/package.nls.json b/package.nls.json index 926a64d..0b03ef9 100644 --- a/package.nls.json +++ b/package.nls.json @@ -1,6 +1,6 @@ { "vonage-vscode.applications": "Applications", - "vonage-vscode.numbers": "Numbers", + "vonage-vscode.numbers": "Unassigned Numbers", "vonage-vscode.account": "Account", "vonage-vscode.help": "Help and Feedback", "vonage-vscode.login": "Sign in", diff --git a/resources/icon_activitybar.svg b/resources/icon_activitybar.svg index 2087b89..4f863f1 100644 --- a/resources/icon_activitybar.svg +++ b/resources/icon_activitybar.svg @@ -1 +1,17 @@ -Asset 4 \ No newline at end of file + + + + + Asset 4 + + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/active-call-line.svg b/resources/icons/dark/active-call-line.svg new file mode 100644 index 0000000..dceabee --- /dev/null +++ b/resources/icons/dark/active-call-line.svg @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/add-call-line.svg b/resources/icons/dark/add-call-line.svg new file mode 100644 index 0000000..179a91f --- /dev/null +++ b/resources/icons/dark/add-call-line.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/app-line.svg b/resources/icons/dark/app-line.svg new file mode 100644 index 0000000..77e0c98 --- /dev/null +++ b/resources/icons/dark/app-line.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/app-plus-line.svg b/resources/icons/dark/app-plus-line.svg new file mode 100644 index 0000000..435d76c --- /dev/null +++ b/resources/icons/dark/app-plus-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/books-line.svg b/resources/icons/dark/books-line.svg new file mode 100644 index 0000000..c9c227b --- /dev/null +++ b/resources/icons/dark/books-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/chain-line.svg b/resources/icons/dark/chain-line.svg new file mode 100644 index 0000000..1207c1e --- /dev/null +++ b/resources/icons/dark/chain-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/delete-line.svg b/resources/icons/dark/delete-line.svg new file mode 100644 index 0000000..e60a08a --- /dev/null +++ b/resources/icons/dark/delete-line.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/emoji-line.svg b/resources/icons/dark/emoji-line.svg new file mode 100644 index 0000000..ec06a5c --- /dev/null +++ b/resources/icons/dark/emoji-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/error-line.svg b/resources/icons/dark/error-line.svg new file mode 100644 index 0000000..a5c07aa --- /dev/null +++ b/resources/icons/dark/error-line.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/key-line.svg b/resources/icons/dark/key-line.svg new file mode 100644 index 0000000..9c2a4a3 --- /dev/null +++ b/resources/icons/dark/key-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/leave-line.svg b/resources/icons/dark/leave-line.svg new file mode 100644 index 0000000..5139141 --- /dev/null +++ b/resources/icons/dark/leave-line.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/refresh-line.svg b/resources/icons/dark/refresh-line.svg new file mode 100644 index 0000000..8654ab4 --- /dev/null +++ b/resources/icons/dark/refresh-line.svg @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/dark/wallet-line.svg b/resources/icons/dark/wallet-line.svg new file mode 100644 index 0000000..b47c7e1 --- /dev/null +++ b/resources/icons/dark/wallet-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/light/active-call-line.svg b/resources/icons/light/active-call-line.svg new file mode 100644 index 0000000..565a068 --- /dev/null +++ b/resources/icons/light/active-call-line.svg @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/icons/light/add-call-line.svg b/resources/icons/light/add-call-line.svg new file mode 100644 index 0000000..0809156 --- /dev/null +++ b/resources/icons/light/add-call-line.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/resources/icons/light/app-line.svg b/resources/icons/light/app-line.svg new file mode 100644 index 0000000..fef0377 --- /dev/null +++ b/resources/icons/light/app-line.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/resources/icons/light/app-plus-line.svg b/resources/icons/light/app-plus-line.svg new file mode 100644 index 0000000..a398602 --- /dev/null +++ b/resources/icons/light/app-plus-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/light/books-line.svg b/resources/icons/light/books-line.svg new file mode 100644 index 0000000..cfe797d --- /dev/null +++ b/resources/icons/light/books-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/light/chain-line.svg b/resources/icons/light/chain-line.svg new file mode 100644 index 0000000..8eaf2b8 --- /dev/null +++ b/resources/icons/light/chain-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/light/delete-line.svg b/resources/icons/light/delete-line.svg new file mode 100644 index 0000000..8250c83 --- /dev/null +++ b/resources/icons/light/delete-line.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/resources/icons/light/emoji-line.svg b/resources/icons/light/emoji-line.svg new file mode 100644 index 0000000..33ed7c5 --- /dev/null +++ b/resources/icons/light/emoji-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/light/error-line.svg b/resources/icons/light/error-line.svg new file mode 100644 index 0000000..0998767 --- /dev/null +++ b/resources/icons/light/error-line.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/resources/icons/light/key-line.svg b/resources/icons/light/key-line.svg new file mode 100644 index 0000000..d231d9f --- /dev/null +++ b/resources/icons/light/key-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/light/leave-line.svg b/resources/icons/light/leave-line.svg new file mode 100644 index 0000000..ad31642 --- /dev/null +++ b/resources/icons/light/leave-line.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/resources/icons/light/refresh-line.svg b/resources/icons/light/refresh-line.svg new file mode 100644 index 0000000..145e60f --- /dev/null +++ b/resources/icons/light/refresh-line.svg @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/light/wallet-line.svg b/resources/icons/light/wallet-line.svg new file mode 100644 index 0000000..d758a5e --- /dev/null +++ b/resources/icons/light/wallet-line.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/src/client/api/numbers.ts b/src/client/api/numbers.ts index f78a353..0ba388d 100644 --- a/src/client/api/numbers.ts +++ b/src/client/api/numbers.ts @@ -1,12 +1,14 @@ +import { INumber } from '@vonage/server-sdk'; import fetch from 'node-fetch'; import querystring from 'querystring'; import { Auth } from '../../auth'; export class NumbersAPI { - private appUrl = 'https://rest.nexmo.com/account/numbers'; + private accountUrl = 'https://rest.nexmo.com/account/numbers'; + private numbersUrl = 'https://rest.nexmo.com/number'; - async getNumbers(hasApplication?: boolean): Promise { + async getNumbers(applicationId?: string): Promise { const isAuthenticated = await Auth.isAuthenticated(); if (!isAuthenticated) { return []; @@ -18,16 +20,143 @@ export class NumbersAPI { const query = { api_key, api_secret, - has_application: hasApplication + has_application: applicationId !== undefined, + application_id: applicationId }; - const response = await fetch(`${this.appUrl}?${querystring.stringify(query)}`, { method: 'GET', headers }); - const data = await response.json(); - return data.numbers; + const response = await fetch(`${this.accountUrl}?${querystring.stringify(query)}`, { method: 'GET', headers }); + try { + const data = await response.json(); + return data.numbers || []; + } catch (err) { + console.dir(response); + return []; + } } - async buyNumber(): Promise { - return undefined; + async buyNumber(state: any): Promise { + const { api_key, api_secret } = await Auth.getCredentials(); + + const query: any = { + api_key, + api_secret + }; + + const params = new URLSearchParams(); + params.append('country', state.country); + params.append('msisdn', state.msisdn); + + const response = await fetch(`${this.numbersUrl}/buy?${querystring.stringify(query)}`, { method: 'POST', body: params }); + if (response.ok) { + return true; + } else { + const err = await response.json(); + console.dir(err); + return false; + } + } + + async assignToApplication(number: any, applicationId: string): Promise { + // const headers = await Auth.getHeaders(); + const { api_key, api_secret } = await Auth.getCredentials(); + + number.app_id = applicationId; + + const query = { + api_key, + api_secret + }; + + const params = new URLSearchParams(); + + Object.keys(number).forEach((key: string) => { + params.append(key, number[key]); + }); + + const response = await fetch(`${this.numbersUrl}/update?${querystring.stringify(query)}`, { method: 'POST', body: params }); + if (response.ok) { + return true; + } else { + const err = await response.json(); + console.dir(err); + return false; + } + return false; } + async unassignNumber(number: any): Promise { + const { api_key, api_secret } = await Auth.getCredentials(); + + const query: any = { + api_key, + api_secret + }; + + const params = new URLSearchParams(); + params.append('country', number.country); + params.append('msisdn', number.msisdn); + + const response = await fetch(`${this.numbersUrl}/update?${querystring.stringify(query)}`, { method: 'POST', body: params }); + if (response.ok) { + return true; + } else { + const err = await response.json(); + console.dir(err); + return false; + } + } + + async searchNumbers(state: any): Promise { + const { api_key, api_secret } = await Auth.getCredentials(); + + const query: any = { + api_key, + api_secret, + country: state.country + }; + if (state.features) { + query.features = state.features; + } + if (state.pattern) { + query.pattern = state.pattern; + query.search_pattern = state.search_pattern; + } + if (state.type) { + query.type = state.type; + } + + const response = await fetch(`${this.numbersUrl}/search?${querystring.stringify(query)}`, { method: 'GET' }); + if (response.ok) { + const data = await response.json(); + return data.numbers; + } else { + const err = await response.json(); + console.dir(err); + return []; + } + return []; + + } + + async cancelNumber(number: INumber): Promise { + const { api_key, api_secret } = await Auth.getCredentials(); + + const query: any = { + api_key, + api_secret + }; + + const params = new URLSearchParams(); + params.append('country', number.country); + params.append('msisdn', number.msisdn); + + const response = await fetch(`${this.numbersUrl}/cancel?${querystring.stringify(query)}`, { method: 'POST', body: params }); + if (response.ok) { + return true; + } else { + const err = await response.json(); + console.dir(err); + return false; + } + } } \ No newline at end of file diff --git a/src/commands/numbersCommands.ts b/src/commands/numbersCommands.ts index 77bb5ab..58e237d 100644 --- a/src/commands/numbersCommands.ts +++ b/src/commands/numbersCommands.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { Telemetry } from "../telemetry"; -import { NumbersViewDataProvider } from "../views"; +import { NumbersViewDataProvider, NumberTreeItem } from "../views"; export class NumbersCommands { @@ -10,8 +10,11 @@ export class NumbersCommands { private vonageNumbersViewDataProvider: NumbersViewDataProvider) { subscriptions.push(vscode.commands.registerCommand('vonage.numbers.refreshNumbersList', this.refreshNumbersList)); - subscriptions.push(vscode.commands.registerCommand('vonage.numbers.searchNumbers', this.filterNumbers)); subscriptions.push(vscode.commands.registerCommand('vonage.numbers.buyNumber', this.buyNumber)); + subscriptions.push(vscode.commands.registerCommand('vonage.numbers.assign', this.assignNumber)); + subscriptions.push(vscode.commands.registerCommand('vonage.numbers.unassign', this.unassignNumber)); + subscriptions.push(vscode.commands.registerCommand('vonage.numbers.cancelNumber', this.cancelNumber)); + subscriptions.push(vscode.commands.registerCommand('vonage.numbers.copy', this.copyNumber)); } /** Number commands */ @@ -20,13 +23,36 @@ export class NumbersCommands { this.vonageNumbersViewDataProvider.refresh(); }; - filterNumbers = () => { - this.telemetry.sendEvent('number.search'); - this.vonageNumbersViewDataProvider.searchNumbers(); - } - buyNumber = () => { this.telemetry.sendEvent('number.buy'); this.vonageNumbersViewDataProvider.buyNumber(); } + + cancelNumber = (node?: NumberTreeItem) => { + this.telemetry.sendEvent('number.cancel'); + if (node) { + this.vonageNumbersViewDataProvider.cancelNumber(node); + } + } + + assignNumber = (node?: NumberTreeItem) => { + this.telemetry.sendEvent('number.assign'); + if (node) { + this.vonageNumbersViewDataProvider.assignNumber(node); + } + } + + unassignNumber = (node?: NumberTreeItem) => { + this.telemetry.sendEvent('number.unassign'); + if (node) { + this.vonageNumbersViewDataProvider.unassignNumber(node); + } + } + + copyNumber = (node?: NumberTreeItem) => { + this.telemetry.sendEvent('number.copy'); + if (node) { + this.vonageNumbersViewDataProvider.copyNumber(node); + } + } } \ No newline at end of file diff --git a/src/enums/storageKeys.ts b/src/enums/storageKeys.ts index 90abc98..764d627 100644 --- a/src/enums/storageKeys.ts +++ b/src/enums/storageKeys.ts @@ -17,5 +17,13 @@ export enum StorageKeys { /** * Whether to hide the account balance by default */ - hideAccountBalance = 'hideAccountBalance' + hideAccountBalance = 'hideAccountBalance', + /** + * Last country selected + */ + lastCountrySelected = 'lastCountrySelected', + /** + * Display numbers that are assigned to applications + */ + displayAssignedOnly = 'displayAssignedOnly' } \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 687b3e9..bc8d258 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -19,6 +19,7 @@ import { } from './commands'; import { Auth } from './auth'; import { Credentials } from './models'; +import { StorageKeys } from './enums'; let activeExtension: Extension; let _context: vscode.ExtensionContext; @@ -26,7 +27,7 @@ let _context: vscode.ExtensionContext; export class Extension { private onAuthStatusChangedEvent = new vscode.EventEmitter(); - + /** * Activate the extension */ @@ -40,7 +41,7 @@ export class Extension { */ const authenticated = await Auth.isAuthenticated(); vscode.commands.executeCommand('setContext', 'vonage:authenticated', authenticated); - + /** * Notify user of telemetry collection on first use. */ @@ -65,7 +66,7 @@ export class Extension { */ const applicationViewDataProvider = new ApplicationViewDataProvider(); const accountViewDataProvider = new AccountViewDataProvider(_context.globalState); - const numbersViewDataProvider = new NumbersViewDataProvider(); + const numbersViewDataProvider = new NumbersViewDataProvider(_context.globalState); const helpViewDataProvider = new HelpViewDataProvider(); const subscriptions = _context.subscriptions; @@ -88,6 +89,11 @@ export class Extension { Auth.onAuthStatusChanged(authStatusChanged); + const numberAssignmentChanged = async () => { + applicationCommands.refreshAppsList(); + }; + numbersViewDataProvider.onNumberAssignmentChanged(numberAssignmentChanged); + /** * Register tree views within activity bar */ diff --git a/src/steps/assignNumberFlow.ts b/src/steps/assignNumberFlow.ts new file mode 100644 index 0000000..5481e80 --- /dev/null +++ b/src/steps/assignNumberFlow.ts @@ -0,0 +1,49 @@ +import { VonageClient } from "../client/vonageClient"; +import { MultiStepInput } from "../utils"; + +export interface AssignNumberState { + title: string; + step: number; + totalSteps: number; + applicationId: string; + name: string; +} + +const title = 'Assign Number to Application'; + +export abstract class AssignNumberFlow { + + public static async collectInputs(): Promise { + const state = {} as Partial; + await MultiStepInput.run(input => this.selectApplication(input, state)); + return state as AssignNumberState; + } + + private static async selectApplication(input: MultiStepInput, state: Partial) { + + const applications = await VonageClient.application.getApplications(); + + const result = await input.showQuickPick({ + title: title, + step: 1, + totalSteps: 1, + placeholder: 'Choose Application', + items: applications.map((a: any) => { + return { + label: a.name, + description: a.id + }; + }), + shouldResume: this.shouldResume, + }); + state.name = result.label; + state.applicationId = result.description; + } + + private static shouldResume() { + // Could show a notification with the option to resume. + return new Promise((resolve, reject) => { + // noop + }); + } +} diff --git a/src/steps/buyNumberFlow.ts b/src/steps/buyNumberFlow.ts new file mode 100644 index 0000000..75e14c3 --- /dev/null +++ b/src/steps/buyNumberFlow.ts @@ -0,0 +1,150 @@ +import * as vscode from 'vscode'; +import { VonageClient } from "../client/vonageClient"; +import { StorageKeys } from '../enums'; +import { getCountries, getNumberTypes, getSearchPatterns, MultiStepInput } from "../utils"; + +export interface BuyNumberState { + title: string; + step: number; + totalSteps: number; + country: string, + type?: string, + pattern?: any, + search_pattern?: number, + msisdn: string +} + +const title = 'Find Number to Buy'; + +export abstract class BuyNumberFlow { + private static storage: vscode.Memento; + + public static async collectInputs(storage: vscode.Memento): Promise { + this.storage = storage; + const state = {} as Partial; + await MultiStepInput.run(input => this.selectCountry(input, state)); + return state as BuyNumberState; + } + + private static async selectCountry(input: MultiStepInput, state: Partial) { + const countries = getCountries(this.storage).sort((a,b) => { + return (a.picked === b.picked) ? 0 : a.picked ? -1 : 1; + }); + + const result = await input.showQuickPick({ + title: title, + step: 1, + totalSteps: 3, + placeholder: 'Choose Country', + items: countries, + shouldResume: this.shouldResume, + }); + state.country = result.description; + this.storage.update(StorageKeys.lastCountrySelected, result.description); + return (input: MultiStepInput) => this.selectNumberType(input, state); + } + + private static async selectNumberType(input: MultiStepInput, state: Partial) { + const result = await input.showQuickPick({ + title: title, + step: 2, + totalSteps: 3, + placeholder: 'Select Type', + items: getNumberTypes(), + shouldResume: this.shouldResume, + }); + let type = undefined; + switch (result.label) { + case 'Mobile': + type = 'mobile-lvn'; + break; + case 'Landline': + type = 'landline'; + break; + case 'Toll Free': + type = 'landline-toll-free'; + break; + } + state.type = type; + return (input: MultiStepInput) => this.selectNumber(input, state); + } + + // private static async selectPatternType(input: MultiStepInput, state: Partial) { + // const result = await input.showQuickPick({ + // title: title, + // step: 3, + // totalSteps: 5, + // placeholder: 'Search Pattern', + // items: getSearchPatterns(), + // shouldResume: this.shouldResume, + // }); + // let pattern = undefined; + // switch (result.label) { + // case 'Starts with': + // pattern = 0; + // break; + // case 'Contains': + // pattern = 1; + // break; + // case 'Ends with': + // pattern = 2; + // break; + // } + // state.search_pattern = pattern; + // return (input: MultiStepInput) => this.selectPattern(input, state); + // } + + // private static async selectPattern(input: MultiStepInput, state: Partial) { + // const result = await input.showInputBox({ + // title, + // step: 4, + // totalSteps: 5, + // value: '', + // prompt: 'Pattern to match (Note: all numbers are in E.164 format, so the starting pattern includes the country code, such as 1 for USA)', + // validate: this.validateNumber, + // shouldResume: this.shouldResume + // }); + // if (result && result.length > 0) { + // state.pattern = result; + // } + // return (input: MultiStepInput) => this.selectNumber(input, state); + // } + + private static async selectNumber(input: MultiStepInput, state: Partial) { + + const numbers = await VonageClient.numbers.searchNumbers(state); + + const result = await input.showQuickPick({ + title: title, + step: 3, + totalSteps: 3, + placeholder: 'Choose Number', + items: numbers.map((a: any) => { + return { + label: a.msisdn, + detail: a.features.join(', ') + }; + }), + shouldResume: this.shouldResume, + }); + state.msisdn = result.label; + } + + private static shouldResume() { + // Could show a notification with the option to resume. + return new Promise((resolve, reject) => { + // noop + }); + } + + private static async validateNumber(number: any) { + if (number.length === 0) { + return undefined; + } + const parsed = Number.parseInt(number); + if (Number.isNaN(parsed)) { + return 'Can only contain numbers'; + } + return undefined; + } +} diff --git a/src/steps/index.ts b/src/steps/index.ts index 10f7d1e..cac02e7 100644 --- a/src/steps/index.ts +++ b/src/steps/index.ts @@ -1,3 +1,4 @@ +export * from './assignNumberFlow'; export * from './createApplicationFlow'; export * from './loginFlow'; export * from './messageCapabilityFlow'; diff --git a/src/telemetry.ts b/src/telemetry.ts index 3457ef9..d5fd2fb 100644 --- a/src/telemetry.ts +++ b/src/telemetry.ts @@ -171,9 +171,9 @@ export class GoogleAnalyticsTelemetry implements Telemetry { const enableTelemetry = vscode.workspace .getConfiguration('telemetry') .get('enableTelemetry', false); - const stripeEnableTelemetry = vscode.workspace + const vonageEnableTelemetry = vscode.workspace .getConfiguration('vonage.telemetry') .get('enabled', false); - return enableTelemetry && stripeEnableTelemetry; + return enableTelemetry && vonageEnableTelemetry; } } \ No newline at end of file diff --git a/src/utils/getCountries.ts b/src/utils/getCountries.ts new file mode 100644 index 0000000..e2b1449 --- /dev/null +++ b/src/utils/getCountries.ts @@ -0,0 +1,124 @@ +import { Memento, QuickPickItem } from 'vscode'; +import { StorageKeys } from '../enums'; + +const countries = [ + { code: "AL", name: "Albania (+ 355)" }, + { code: "DZ", name: "Algeria (+213)" }, + { code: "AO", name: "Angola (+244)" }, + { code: "AR", name: "Argentina (+54)" }, + { code: "AU", name: "Australia (+61)" }, + { code: "AT", name: "Austria (+43)" }, + { code: "BS", name: "Bahamas (+1 242)" }, + { code: "BH", name: "Bahrain (+973)" }, + { code: "BB", name: "Barbados (+1 246)" }, + { code: "BY", name: "Belarus (+375)" }, + { code: "BE", name: "Belgium (+32)" }, + { code: "BJ", name: "Benin (+229)" }, + { code: "BO", name: "Bolivia (+591)" }, + { code: "BA", name: "Bosnia and Herzegovina (+387)" }, + { code: "BW", name: "Botswana (+267)" }, + { code: "BR", name: "Brazil (+55)" }, + { code: "BG", name: "Bulgaria (+359)" }, + { code: "CA", name: "Canada (+1)" }, + { code: "KH", name: "Cambodia (+855)" }, + { code: "KY", name: "Cayman Islands (+1 345)" }, + { code: "CL", name: "Chile (+56)" }, + { code: "CN", name: "China (+86)" }, + { code: "CO", name: "Colombia (+57)" }, + { code: "CR", name: "Costa Rica (+506)" }, + { code: "HR", name: "Croatia (+385)" }, + { code: "CY", name: "Cyprus (+357)" }, + { code: "CZ", name: "Czech Republic (+420)" }, + { code: "DK", name: "Denmark (+45)" }, + { code: "DO", name: "Dominican Republic (+18)" }, + { code: "EC", name: "Ecuador (+593)" }, + { code: "EG", name: "Egypt (+20)" }, + { code: "SV", name: "El Salvador (+503)" }, + { code: "EE", name: "Estonia (+372)" }, + { code: "FI", name: "Finland (+358)" }, + { code: "FR", name: "France (+33)" }, + { code: "GE", name: "Georgia (+995)" }, + { code: "DE", name: "Germany (+49)" }, + { code: "GH", name: "Ghana (+233)" }, + { code: "GR", name: "Greece (+30)" }, + { code: "GD", name: "Grenada (+1 473)" }, + { code: "GT", name: "Guatemala (+502)" }, + { code: "HK", name: "Hong Kong (+852)" }, + { code: "HN", name: "Honduras (+504)" }, + { code: "HU", name: "Hungary (+36)" }, + { code: "ID", name: "Indonesia (+62)" }, + { code: "IS", name: "Iceland (+354)" }, + { code: "IN", name: "India (+91)" }, + { code: "IE", name: "Ireland (+353)" }, + { code: "IL", name: "Israel (+972)" }, + { code: "IT", name: "Italy (+39)" }, + { code: "JM", name: "Jamaica (+1 876)" }, + { code: "JP", name: "Japan (+81)" }, + { code: "JO", name: "Jordan (+962)" }, + { code: "KZ", name: "Kazakhstan (+7)" }, + { code: "KE", name: "Kenya (+254)" }, + { code: "LV", name: "Latvia (+371)" }, + { code: "LI", name: "Liechtenstein (+423)" }, + { code: "LT", name: "Lithuania (+370)" }, + { code: "LU", name: "Luxembourg (+352)" }, + { code: "MO", name: "Macau (+853)" }, + { code: "MY", name: "Malaysia (+60)" }, + { code: "MT", name: "Malta (+356)" }, + { code: "MU", name: "Mauritius (+230)" }, + { code: "YT", name: "Mayotte (+262)" }, + { code: "MX", name: "Mexico (+52)" }, + { code: "MD", name: "Moldova (+373)" }, + { code: "MC", name: "Monaco (+377)" }, + { code: "NL", name: "Netherlands (+31)" }, + { code: "NZ", name: "New Zealand (+64)" }, + { code: "NI", name: "Nicaragua (+505)" }, + { code: "NG", name: "Nigeria (+234)" }, + { code: "NO", name: "Norway (+47)" }, + { code: "PK", name: "Pakistan (+92)" }, + { code: "PA", name: "Panama (+507)" }, + { code: "PE", name: "Peru (+51)" }, + { code: "PH", name: "Philippines (+63)" }, + { code: "PR", name: "Puerto Rico (+1)" }, + { code: "PL", name: "Poland (+48)" }, + { code: "PT", name: "Portugal (+351)" }, + { code: "RO", name: "Romania (+40)" }, + { code: "RU", name: "Russia (+7)" }, + { code: "RW", name: "Rwanda (+250)" }, + { code: "RE", name: "Reunion (+262)" }, + { code: "SA", name: "Saudi Arabia (+966)" }, + { code: "RS", name: "Serbia (+381)" }, + { code: "SG", name: "Singapore (+65)" }, + { code: "SK", name: "Slovakia (+421)" }, + { code: "SI", name: "Slovenia (+386)" }, + { code: "ZA", name: "South Africa (+27)" }, + { code: "KR", name: "South Korea (+82)" }, + { code: "ES", name: "Spain (+34)" }, + { code: "LK", name: "Sri Lanka (+94)" }, + { code: "SE", name: "Sweden (+46)" }, + { code: "CH", name: "Switzerland (+41)" }, + { code: "TJ", name: "Tajikistan (+992)" }, + { code: "TW", name: "Taiwan (+886)" }, + { code: "TH", name: "Thailand (+66)" }, + { code: "TT", name: "Trinidad and Tobago (+1 868)" }, + { code: "TR", name: "Turkey (+90)" }, + { code: "UA", name: "Ukraine (+380)" }, + { code: "AE", name: "United Arab Emirates (+971)" }, + { code: "GB", name: "United Kingdom (+44)" }, + { code: "US", name: "United States (+1)" }, + { code: "UY", name: "Uruguay (+598)" }, + { code: "UZ", name: "Uzbekistan (+998)" }, + { code: "VE", name: "Venezuela (+58)" }, + { code: "VN", name: "Vietnam (+84)" }, + { code: "ZM", name: "Zambia (+260)" } +]; + +export function getCountries (storage: Memento): QuickPickItem[] { + const previousCountry: string | undefined = storage.get(StorageKeys.lastCountrySelected); + return countries.map ( (i) => { + return { + label: i.name, + description: i.code, + picked: (previousCountry && previousCountry === i.code) as boolean + }; + }); +} \ No newline at end of file diff --git a/src/utils/getIconPath.ts b/src/utils/getIconPath.ts new file mode 100644 index 0000000..d8b1e51 --- /dev/null +++ b/src/utils/getIconPath.ts @@ -0,0 +1,8 @@ +import * as path from 'path'; + +export function getIconPath(iconName: string): any { + return { + light: path.join(__filename, '..', '..', '..', '..', 'resources','icons','light', `${iconName}.svg`), + dark: path.join(__filename, '..', '..', '..', '..', 'resources', 'icons','dark', `${iconName}.svg`) + }; +} diff --git a/src/utils/getNumberTypes.ts b/src/utils/getNumberTypes.ts new file mode 100644 index 0000000..8ef6429 --- /dev/null +++ b/src/utils/getNumberTypes.ts @@ -0,0 +1,10 @@ +import { QuickPickItem } from 'vscode'; + +export function getNumberTypes(): QuickPickItem[] { + return ['Any', 'Mobile', 'Landline', 'Toll Free'].map((i) => { + return { + label: i, + picked: i === 'Any' + }; + }); +} \ No newline at end of file diff --git a/src/utils/getSearchPatterns.ts b/src/utils/getSearchPatterns.ts new file mode 100644 index 0000000..2306b40 --- /dev/null +++ b/src/utils/getSearchPatterns.ts @@ -0,0 +1,9 @@ +import { QuickPickItem } from 'vscode'; + +export function getSearchPatterns(): QuickPickItem[] { + return ['Starts with', 'Contains', 'Ends with'].map((i) => { + return { + label: i, + }; + }); +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 153f3cc..90a09f4 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,7 +1,11 @@ +export * from './getCountries'; export * from './getExtenionInfo'; export * from './getHandlebarsWithHelpers'; export * from './getHTTPMethods'; +export * from './getIconPath'; +export * from './getNumberTypes'; export * from './getOSType'; +export * from './getSearchPatterns'; export * from './multiStepInput'; export * from './openUrl'; export * from './pathNormalize'; diff --git a/src/views/account.ts b/src/views/account.ts index e262147..55e53f5 100644 --- a/src/views/account.ts +++ b/src/views/account.ts @@ -24,7 +24,7 @@ export class AccountViewDataProvider extends BaseTreeViewDataProvider { const accountTreeItem = new BaseTreeItem( `Balance: ${balance}`, TreeItemCollapsibleState.None, - 'law', + 'wallet-line', { command: 'vonage.account.toggleBalanceView', title: '', diff --git a/src/views/application.ts b/src/views/application.ts index 666c65d..9fab62b 100644 --- a/src/views/application.ts +++ b/src/views/application.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { openUrl, showWarningMessage } from '../utils'; -import { BaseTreeViewDataProvider } from './trees'; +import { BaseTreeViewDataProvider, NumberTreeItem } from './trees'; import { ApplicationTreeItem } from './trees'; import { CreateApplicationFlow, @@ -9,14 +9,37 @@ import { MessageCapabilityFlow } from '../steps'; import { VonageClient } from '../client/vonageClient'; +import { INumber } from '@vonage/server-sdk'; +const delayedLoad = () => { + return new Promise((resolve) => { + setTimeout(resolve, 1000); + }); +}; export class ApplicationViewDataProvider extends BaseTreeViewDataProvider { - async buildTree(): Promise { + public async getChildren(element?: any): Promise { + if (element) { + if (element.application) { + await delayedLoad(); + return await this.loadNumbers((element as ApplicationTreeItem).application.id); + } + return []; + } const applications = await VonageClient.application.getApplications(); - return Promise.resolve(applications.map((app: any) => { + return applications.map((app: any) => { return new ApplicationTreeItem(app); - })); + }); + } + + private async loadNumbers(appId: string | undefined): Promise { + if (appId) { + const numbers = await VonageClient.numbers.getNumbers(appId); + return numbers.map((n: INumber) => { + return new NumberTreeItem(n); + }); + } + return []; } /** Application Commands */ @@ -85,7 +108,6 @@ export class ApplicationViewDataProvider extends BaseTreeViewDataProvider { } else { return; } - } /** Voice Commands */ @@ -129,7 +151,6 @@ export class ApplicationViewDataProvider extends BaseTreeViewDataProvider { async updateVoice(node: ApplicationTreeItem): Promise { const state = await VoiceCapabilityFlow.collectInputs(`Update Voice Capability for application ${node.label}`, node); - const application = node.application; application.capabilities.voice = @@ -137,21 +158,15 @@ export class ApplicationViewDataProvider extends BaseTreeViewDataProvider { webhooks: { answer_url: { address: state.answer_url_address, - http_method: state.answer_url_http_method, - // connection_timeout: parseInt(state.answer_url_connection_timeout), - // socket_timeout: parseInt(state.answer_url_socket_timeout) + http_method: state.answer_url_http_method }, fallback_answer_url: { address: state.fallback_answer_url_address, - http_method: state.fallback_answer_url_http_method, - // connection_timeout: parseInt(state.fallback_answer_url_connection_timeout), - // socket_timeout: parseInt(state.fallback_answer_url_socket_timeout) + http_method: state.fallback_answer_url_http_method }, event_url: { address: state.event_url_address, - http_method: state.event_url_http_method, - // connection_timeout: parseInt(state.event_url_connection_timeout), - // socket_timeout: parseInt(state.event_url_socket_timeout) + http_method: state.event_url_http_method } } }; @@ -425,6 +440,5 @@ export class ApplicationViewDataProvider extends BaseTreeViewDataProvider { } else { return; } - } } \ No newline at end of file diff --git a/src/views/help.ts b/src/views/help.ts index 1594768..d0c3915 100644 --- a/src/views/help.ts +++ b/src/views/help.ts @@ -8,25 +8,25 @@ export class HelpViewDataProvider extends BaseTreeViewDataProvider { this.refresh(); const items = [ - new BaseTreeItem('Read documentation', TreeItemCollapsibleState.None, 'book', { + new BaseTreeItem('Read documentation', TreeItemCollapsibleState.None, 'books-line', { command: 'vonage.help.openDocs', title: '', arguments: undefined }), - new BaseTreeItem('Report issue', TreeItemCollapsibleState.None, 'report', { + new BaseTreeItem('Report issue', TreeItemCollapsibleState.None, 'error-line', { command: 'vonage.help.openReportIssue', title: '', arguments: undefined }), - new BaseTreeItem('Rate and provide feedback', TreeItemCollapsibleState.None, 'feedback', { + new BaseTreeItem('Rate and provide feedback', TreeItemCollapsibleState.None, 'emoji-line', { command: 'vonage.help.openSurvey', title: '', arguments: undefined }), - new BaseTreeItem('Sign out', TreeItemCollapsibleState.None, 'lock', { + new BaseTreeItem('Sign out', TreeItemCollapsibleState.None, 'leave-line', { command: 'vonage.logout', title: '', - arguments: undefined, + arguments: undefined }) ]; diff --git a/src/views/number.ts b/src/views/number.ts index 1af4ede..36af334 100644 --- a/src/views/number.ts +++ b/src/views/number.ts @@ -1,23 +1,117 @@ +import * as vscode from 'vscode'; import { VonageClient } from '../client/vonageClient'; +import { AssignNumberFlow } from '../steps'; +import { BuyNumberFlow } from '../steps/buyNumberFlow'; +import { showWarningMessage } from '../utils'; import { NumberTreeItem, BaseTreeViewDataProvider } from './trees'; +const numberAssignmentEventEmitter = new vscode.EventEmitter(); + export class NumbersViewDataProvider extends BaseTreeViewDataProvider { - private hasNumbers = false; + private storage: vscode.Memento; + public onNumberAssignmentChanged = numberAssignmentEventEmitter.event; + + constructor(state: vscode.Memento) { + super(); + this.storage = state; + } async buildTree(): Promise { - const numbers = await VonageClient.numbers.getNumbers(this.hasNumbers); + const numbers = await VonageClient.numbers.getNumbers(); + + if (numbers.length > 0) { + return Promise.resolve(numbers.map((num: any) => { + return new NumberTreeItem(num); + })); + } else { + return []; + } + } + + async buyNumber(): Promise { + const state = await BuyNumberFlow.collectInputs(this.storage); + + const assignResult = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: `Purchasing ${state.msisdn}...` + }, async () => { + return await VonageClient.numbers.buyNumber(state); + }); + + if (assignResult) { + this.refresh(); + } + } + + async cancelNumber(node: NumberTreeItem): Promise { + const confirmDelete = `Are you sure you want to remove "${node.label}"? This cannot be undone.`; + + const result = await showWarningMessage(confirmDelete, undefined, { modal: true }, { title: 'Remove' }); - return Promise.resolve(numbers.map((num: any) => { - return new NumberTreeItem(num); - })); + if (result === true) { + + const deleteResult = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: `Removing number "${node.label}"...` + }, async () => { + return await VonageClient.numbers.cancelNumber(node.number); + }); + + if (deleteResult) { + this.refresh(); + numberAssignmentEventEmitter.fire(''); + vscode.window.showInformationMessage(`Successfully removed number "${node.label}".`); + } + } else { + return; + } } - async searchNumbers(): Promise { - return ''; + async assignNumber(node: NumberTreeItem): Promise { + + const state = await AssignNumberFlow.collectInputs(); + const number = node.number; + + const assignResult = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: `Assigning ${number.msisdn} to ${state.name}...` + }, async () => { + return await VonageClient.numbers.assignToApplication(number, state.applicationId); + }); + + if (assignResult) { + this.refresh(); + numberAssignmentEventEmitter.fire(''); + } + } + + async unassignNumber(node: NumberTreeItem): Promise { + const confirmDelete = `Are you sure you want to unlink "${node.label}"?`; + + const result = await showWarningMessage(confirmDelete, undefined, { modal: true }, { title: 'Unlink' }); + + if (result === true) { + + const deleteResult = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: `Unlinking "${node.label}"...` + }, async () => { + return await VonageClient.numbers.unassignNumber(node.number); + }); + + if (deleteResult) { + this.refresh(); + numberAssignmentEventEmitter.fire(''); + vscode.window.showInformationMessage(`Successfully unlinked number "${node.label}".`); + } + } else { + return; + } } - async buyNumber(): Promise { - return ''; + async copyNumber(node: NumberTreeItem): Promise { + vscode.env.clipboard.writeText(`${node.number.msisdn}`); + vscode.window.showInformationMessage(`'${node.number.msisdn}' copied to clipboard`); } } \ No newline at end of file diff --git a/src/views/trees/applicationTreeItem.ts b/src/views/trees/applicationTreeItem.ts index 643ffd4..487f2d5 100644 --- a/src/views/trees/applicationTreeItem.ts +++ b/src/views/trees/applicationTreeItem.ts @@ -6,7 +6,7 @@ export class ApplicationTreeItem extends BaseTreeItem { constructor( public application: any) { - super(application.name, TreeItemCollapsibleState.None, 'server-process', undefined, 'application'); + super(application.name, TreeItemCollapsibleState.Collapsed, 'app-line', undefined, 'application'); this.setContextValue(); this.setDescription(); this.setToolTip(); diff --git a/src/views/trees/baseTreeItem.ts b/src/views/trees/baseTreeItem.ts index 1f27b2d..f802c3b 100644 --- a/src/views/trees/baseTreeItem.ts +++ b/src/views/trees/baseTreeItem.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { getIconPath } from '../../utils'; export class BaseTreeItem extends vscode.TreeItem { parent: BaseTreeItem | undefined; @@ -13,7 +14,7 @@ export class BaseTreeItem extends vscode.TreeItem { super(label, collapsibleState); this.command = command; this.contextValue = contextValue; - this.iconPath = new vscode.ThemeIcon(icon); + this.iconPath = getIconPath(icon); } makeCollapsible() { diff --git a/src/views/trees/baseTreeViewDataProvider.ts b/src/views/trees/baseTreeViewDataProvider.ts index 2d69f93..3302c9a 100644 --- a/src/views/trees/baseTreeViewDataProvider.ts +++ b/src/views/trees/baseTreeViewDataProvider.ts @@ -1,4 +1,4 @@ -import { Event, EventEmitter, TreeDataProvider, TreeItem } from 'vscode'; +import { Event, EventEmitter, TreeDataProvider, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { BaseTreeItem } from './baseTreeItem'; export class BaseTreeViewDataProvider implements TreeDataProvider { diff --git a/src/views/trees/numberTreeItem.ts b/src/views/trees/numberTreeItem.ts index 7baebf7..b6bf71a 100644 --- a/src/views/trees/numberTreeItem.ts +++ b/src/views/trees/numberTreeItem.ts @@ -10,16 +10,21 @@ export class NumberTreeItem extends BaseTreeItem { constructor( public number: INumber) { - super(number.msisdn, TreeItemCollapsibleState.None, 'symbol-number', undefined, 'number'); + super(`+${number.msisdn}`, TreeItemCollapsibleState.None, 'active-call-line', undefined, 'number'); this.setDescription(); this.setToolTip(); } + public setContextValue(): void { + this.contextValue = `number${Object.keys(this.number.features).length > 0 ? '-' + Object.keys(this.number.features).join('-') : ''}`; + } + + private setDescription(): void { this.description = this.number.features.join(' ').toLowerCase(); } - private setToolTip() { + private setToolTip(): void { this.tooltip = resolveTooltipMarkdown(this.numberTooltipTemplate, { ...this.number }); } diff --git a/webpack.config.js b/webpack.config.js index 397f21a..9c025a8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,7 +2,6 @@ 'use strict'; const path = require('path'); -const CopyPlugin = require('copy-webpack-plugin'); /**@type {import('webpack').Configuration}*/ const config = { @@ -41,16 +40,6 @@ const config = { } ] }, - // plugins: [ - // new CopyPlugin({ - // patterns: [ - // { from: 'src/login', to: 'login' } - // ], - // options: { - // concurrency: 100, - // }, - // }), - // ], node: { __dirname: false }