Typed reducers (#1136)

* Added typed actions/reducers
* Added commands to check types / eslint issues
* Added redux dev tools
main
Nikita Glazov 6 years ago committed by GitHub
parent 7e8fc2366a
commit 06529b984d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,6 +12,7 @@ module.exports = {
'parserOptions': { 'parserOptions': {
'parser': '@typescript-eslint/parser', 'parser': '@typescript-eslint/parser',
'ecmaVersion': 6, 'ecmaVersion': 6,
'project': './tsconfig.json',
}, },
'plugins': [ 'plugins': [
'@typescript-eslint', '@typescript-eslint',

@ -1132,12 +1132,12 @@
} }
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "2.10.0", "version": "2.19.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.10.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.19.2.tgz",
"integrity": "sha512-rT51fNLW0u3fnDGnAHVC5nu+Das+y2CpW10yqvf6/j5xbuUV3FxA3mBaIbM24CXODXjbgUznNb4Kg9XZOUxKAw==", "integrity": "sha512-HX2qOq2GOV04HNrmKnTpSIpHjfl7iwdXe3u/Nvt+/cpmdvzYvY0NHSiTkYN257jHnq4OM/yo+OsFgati+7LqJA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "2.10.0", "@typescript-eslint/experimental-utils": "2.19.2",
"eslint-utils": "^1.4.3", "eslint-utils": "^1.4.3",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0", "regexpp": "^3.0.0",
@ -1145,13 +1145,13 @@
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "2.10.0", "version": "2.19.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.10.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.19.2.tgz",
"integrity": "sha512-FZhWq6hWWZBP76aZ7bkrfzTMP31CCefVIImrwP3giPLcoXocmLTmr92NLZxuIcTL4GTEOE33jQMWy9PwelL+yQ==", "integrity": "sha512-B88QuwT1wMJR750YvTJBNjMZwmiPpbmKYLm1yI7PCc3x0NariqPwqaPsoJRwU9DmUi0cd9dkhz1IqEnwfD+P1A==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "2.10.0", "@typescript-eslint/typescript-estree": "2.19.2",
"eslint-scope": "^5.0.0" "eslint-scope": "^5.0.0"
}, },
"dependencies": { "dependencies": {
@ -1168,57 +1168,28 @@
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "1.13.0", "version": "2.19.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.19.2.tgz",
"integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", "integrity": "sha512-8uwnYGKqX9wWHGPGdLB9sk9+12sjcdqEEYKGgbS8A0IvYX59h01o8os5qXUHMq2na8vpDRaV0suTLM7S8wraTA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/eslint-visitor-keys": "^1.0.0", "@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "1.13.0", "@typescript-eslint/experimental-utils": "2.19.2",
"@typescript-eslint/typescript-estree": "1.13.0", "@typescript-eslint/typescript-estree": "2.19.2",
"eslint-visitor-keys": "^1.0.0" "eslint-visitor-keys": "^1.1.0"
},
"dependencies": {
"@typescript-eslint/experimental-utils": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz",
"integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "1.13.0",
"eslint-scope": "^4.0.0"
}
},
"@typescript-eslint/typescript-estree": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz",
"integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==",
"dev": true,
"requires": {
"lodash.unescape": "4.0.1",
"semver": "5.5.0"
}
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true
}
} }
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "2.10.0", "version": "2.19.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.10.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.19.2.tgz",
"integrity": "sha512-oOYnplddQNm/LGVkqbkAwx4TIBuuZ36cAQq9v3nFIU9FmhemHuVzAesMSXNQDdAzCa5bFgCrfD3JWhYVKlRN2g==", "integrity": "sha512-Xu/qa0MDk6upQWqE4Qy2X16Xg8Vi32tQS2PR0AvnT/ZYS4YGDvtn2MStOh5y8Zy2mg4NuL06KUHlvCh95j9C6Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^4.1.1", "debug": "^4.1.1",
"eslint-visitor-keys": "^1.1.0", "eslint-visitor-keys": "^1.1.0",
"glob": "^7.1.6", "glob": "^7.1.6",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
"lodash.unescape": "4.0.1", "lodash": "^4.17.15",
"semver": "^6.3.0", "semver": "^6.3.0",
"tsutils": "^3.17.1" "tsutils": "^3.17.1"
}, },
@ -1455,6 +1426,12 @@
"integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
"dev": true "dev": true
}, },
"acorn-jsx": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz",
"integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==",
"dev": true
},
"add-dom-event-listener": { "add-dom-event-listener": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz", "resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz",
@ -1499,6 +1476,15 @@
"integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
"dev": true "dev": true
}, },
"ansi-escapes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
"integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
"dev": true,
"requires": {
"type-fest": "^0.8.1"
}
},
"ansi-html": { "ansi-html": {
"version": "0.0.7", "version": "0.0.7",
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
@ -1804,6 +1790,12 @@
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
"dev": true "dev": true
}, },
"astral-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
"dev": true
},
"async": { "async": {
"version": "2.6.3", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
@ -1968,12 +1960,6 @@
} }
} }
}, },
"babel": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
"integrity": "sha1-0NHn2APpdHZb7qMjLU4VPA77kPQ=",
"dev": true
},
"babel-loader": { "babel-loader": {
"version": "8.0.6", "version": "8.0.6",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz",
@ -2510,6 +2496,12 @@
"supports-color": "^5.3.0" "supports-color": "^5.3.0"
} }
}, },
"chardet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"chokidar": { "chokidar": {
"version": "2.1.8", "version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
@ -2600,6 +2592,21 @@
} }
} }
}, },
"cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"dev": true,
"requires": {
"restore-cursor": "^3.1.0"
}
},
"cli-width": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
"dev": true
},
"cliui": { "cliui": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
@ -3278,6 +3285,12 @@
"regexp.prototype.flags": "^1.2.0" "regexp.prototype.flags": "^1.2.0"
} }
}, },
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
"default-gateway": { "default-gateway": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
@ -3843,37 +3856,185 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true "dev": true
}, },
"eslint": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
"integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"ajv": "^6.10.0",
"chalk": "^2.1.0",
"cross-spawn": "^6.0.5",
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^1.4.3",
"eslint-visitor-keys": "^1.1.0",
"espree": "^6.1.2",
"esquery": "^1.0.1",
"esutils": "^2.0.2",
"file-entry-cache": "^5.0.1",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^5.0.0",
"globals": "^12.1.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"inquirer": "^7.0.0",
"is-glob": "^4.0.0",
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.3.0",
"lodash": "^4.17.14",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"natural-compare": "^1.4.0",
"optionator": "^0.8.3",
"progress": "^2.0.0",
"regexpp": "^2.0.1",
"semver": "^6.1.2",
"strip-ansi": "^5.2.0",
"strip-json-comments": "^3.0.1",
"table": "^5.2.3",
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true,
"requires": {
"esutils": "^2.0.2"
}
},
"eslint-scope": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
"integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
}
},
"glob-parent": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
"integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"globals": {
"version": "12.3.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz",
"integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==",
"dev": true,
"requires": {
"type-fest": "^0.8.1"
}
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
}
},
"regexpp": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
"dev": true
},
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"eslint-config-airbnb": { "eslint-config-airbnb": {
"version": "17.1.1", "version": "18.0.1",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-17.1.1.tgz", "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.0.1.tgz",
"integrity": "sha512-xCu//8a/aWqagKljt+1/qAM62BYZeNq04HmdevG5yUGWpja0I/xhqd6GdLRch5oetEGFiJAnvtGuTEAese53Qg==", "integrity": "sha512-hLb/ccvW4grVhvd6CT83bECacc+s4Z3/AEyWQdIT2KeTsG9dR7nx1gs7Iw4tDmGKozCNHFn4yZmRm3Tgy+XxyQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"eslint-config-airbnb-base": "^13.2.0", "eslint-config-airbnb-base": "^14.0.0",
"object.assign": "^4.1.0", "object.assign": "^4.1.0",
"object.entries": "^1.1.0" "object.entries": "^1.1.0"
} }
}, },
"eslint-config-airbnb-base": { "eslint-config-airbnb-base": {
"version": "13.2.0", "version": "14.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.2.0.tgz", "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz",
"integrity": "sha512-1mg/7eoB4AUeB0X1c/ho4vb2gYkNH8Trr/EgCT/aGmKhhG+F6vF5s8+iRBlWAzFIAphxIdp3YfEKgEl0f9Xg+w==", "integrity": "sha512-2IDHobw97upExLmsebhtfoD3NAKhV4H0CJWP3Uprd/uk+cHuWYOczPVxQ8PxLFUAw7o3Th1RAU8u1DoUpr+cMA==",
"dev": true, "dev": true,
"requires": { "requires": {
"confusing-browser-globals": "^1.0.5", "confusing-browser-globals": "^1.0.7",
"object.assign": "^4.1.0", "object.assign": "^4.1.0",
"object.entries": "^1.1.0" "object.entries": "^1.1.0"
} }
}, },
"eslint-config-airbnb-typescript": { "eslint-config-airbnb-typescript": {
"version": "4.0.1", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-4.0.1.tgz", "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-7.0.0.tgz",
"integrity": "sha512-4LHD0O0X1e08k+e8AngAsKPYNc7nL+5PzK7OEl9qZ6d7C+wo8BN2fMxBhhiUmRggJxArrldp7Dgb1s2f1/Robg==", "integrity": "sha512-ki0JvJEdz2E0QWMeDfSgyr7tLwSmTYhMwaZP0XNnBhQfsjAAlLXwpQZHZBIpaoPrc2Fs6pFUTUU39xD3XPXKZQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/parser": "^1.11.0", "@typescript-eslint/parser": "^2.19.0",
"eslint-config-airbnb": "^17.1.0", "eslint-config-airbnb": "^18.0.1",
"eslint-config-airbnb-base": "^13.1.0" "eslint-config-airbnb-base": "^14.0.0"
} }
}, },
"eslint-import-resolver-node": { "eslint-import-resolver-node": {
@ -4123,12 +4284,40 @@
"integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
"dev": true "dev": true
}, },
"espree": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz",
"integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==",
"dev": true,
"requires": {
"acorn": "^7.1.0",
"acorn-jsx": "^5.1.0",
"eslint-visitor-keys": "^1.1.0"
},
"dependencies": {
"acorn": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
"integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
"dev": true
}
}
},
"esprima": { "esprima": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true "dev": true
}, },
"esquery": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz",
"integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==",
"dev": true,
"requires": {
"estraverse": "^4.0.0"
}
},
"esrecurse": { "esrecurse": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
@ -4345,6 +4534,17 @@
} }
} }
}, },
"external-editor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
"integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
"dev": true,
"requires": {
"chardet": "^0.7.0",
"iconv-lite": "^0.4.24",
"tmp": "^0.0.33"
}
},
"extglob": { "extglob": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
@ -4428,6 +4628,12 @@
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true "dev": true
}, },
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
"faye-websocket": { "faye-websocket": {
"version": "0.10.0", "version": "0.10.0",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
@ -4464,6 +4670,24 @@
"integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
"dev": true "dev": true
}, },
"figures": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
"integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==",
"dev": true,
"requires": {
"escape-string-regexp": "^1.0.5"
}
},
"file-entry-cache": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
"integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
"dev": true,
"requires": {
"flat-cache": "^2.0.1"
}
},
"fill-range": { "fill-range": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@ -4551,6 +4775,34 @@
"resolve-dir": "^1.0.1" "resolve-dir": "^1.0.1"
} }
}, },
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
"integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
"dev": true,
"requires": {
"flatted": "^2.0.0",
"rimraf": "2.6.3",
"write": "1.0.3"
},
"dependencies": {
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
}
}
},
"flatted": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
"integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
"dev": true
},
"flatten": { "flatten": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
@ -5938,6 +6190,12 @@
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
"dev": true "dev": true
}, },
"ignore": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
"image-size": { "image-size": {
"version": "0.5.5", "version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
@ -6043,6 +6301,86 @@
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true "dev": true
}, },
"inquirer": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz",
"integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==",
"dev": true,
"requires": {
"ansi-escapes": "^4.2.1",
"chalk": "^2.4.2",
"cli-cursor": "^3.1.0",
"cli-width": "^2.0.0",
"external-editor": "^3.0.3",
"figures": "^3.0.0",
"lodash": "^4.17.15",
"mute-stream": "0.0.8",
"run-async": "^2.2.0",
"rxjs": "^6.5.3",
"string-width": "^4.1.0",
"strip-ansi": "^5.1.0",
"through": "^2.3.6"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"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
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
}
}
}
}
},
"internal-ip": { "internal-ip": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
@ -6292,6 +6630,12 @@
"isobject": "^3.0.1" "isobject": "^3.0.1"
} }
}, },
"is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
"dev": true
},
"is-regex": { "is-regex": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
@ -6428,6 +6772,12 @@
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true "dev": true
}, },
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
"dev": true
},
"json-stringify-safe": { "json-stringify-safe": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@ -6560,6 +6910,16 @@
"pify": "^4.0.1" "pify": "^4.0.1"
} }
}, },
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
"dev": true,
"requires": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
}
},
"load-json-file": { "load-json-file": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
@ -6675,12 +7035,6 @@
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
}, },
"lodash.unescape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
"integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
"dev": true
},
"loglevel": { "loglevel": {
"version": "1.6.4", "version": "1.6.4",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.4.tgz", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.4.tgz",
@ -7169,6 +7523,12 @@
"resolved": "https://registry.npmjs.org/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", "resolved": "https://registry.npmjs.org/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
"integrity": "sha512-gciOLNN8Vsf7YzcqRjKzlAJ6y7e+B86u7i3KXes0xfxx/nfLmozlW1Vn+Sc9x3tPIePFgc1AeIFhtRgkqTjzDQ==" "integrity": "sha512-gciOLNN8Vsf7YzcqRjKzlAJ6y7e+B86u7i3KXes0xfxx/nfLmozlW1Vn+Sc9x3tPIePFgc1AeIFhtRgkqTjzDQ=="
}, },
"mute-stream": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true
},
"nan": { "nan": {
"version": "2.14.0", "version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
@ -7194,6 +7554,12 @@
"to-regex": "^3.0.1" "to-regex": "^3.0.1"
} }
}, },
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
"negotiator": { "negotiator": {
"version": "0.6.2", "version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@ -7653,6 +8019,15 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"onetime": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
"integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
"dev": true,
"requires": {
"mimic-fn": "^2.1.0"
}
},
"opn": { "opn": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
@ -7662,6 +8037,20 @@
"is-wsl": "^1.1.0" "is-wsl": "^1.1.0"
} }
}, },
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
"dev": true,
"requires": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.6",
"levn": "~0.3.0",
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2",
"word-wrap": "~1.2.3"
}
},
"original": { "original": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
@ -7864,6 +8253,23 @@
"no-case": "^2.2.0" "no-case": "^2.2.0"
} }
}, },
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"requires": {
"callsites": "^3.0.0"
},
"dependencies": {
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
}
}
},
"parse-asn1": { "parse-asn1": {
"version": "5.1.5", "version": "5.1.5",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
@ -8583,6 +8989,12 @@
"uniq": "^1.0.1" "uniq": "^1.0.1"
} }
}, },
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
"dev": true
},
"pretty-error": { "pretty-error": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz",
@ -8611,6 +9023,12 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true "dev": true
}, },
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"promise": { "promise": {
"version": "7.3.1", "version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
@ -9568,6 +9986,11 @@
"symbol-observable": "^1.2.0" "symbol-observable": "^1.2.0"
} }
}, },
"redux-devtools-extension": {
"version": "2.13.8",
"resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz",
"integrity": "sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg=="
},
"redux-logger": { "redux-logger": {
"version": "3.0.6", "version": "3.0.6",
"resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz",
@ -9835,6 +10258,16 @@
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
"dev": true "dev": true
}, },
"restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"dev": true,
"requires": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
}
},
"ret": { "ret": {
"version": "0.1.15", "version": "0.1.15",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@ -9875,6 +10308,15 @@
"classnames": "^2.2.5" "classnames": "^2.2.5"
} }
}, },
"run-async": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
"integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
"dev": true,
"requires": {
"is-promise": "^2.1.0"
}
},
"run-queue": { "run-queue": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
@ -9884,6 +10326,15 @@
"aproba": "^1.1.1" "aproba": "^1.1.1"
} }
}, },
"rxjs": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
"integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@ -10420,6 +10871,17 @@
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true "dev": true
}, },
"slice-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
"integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"astral-regex": "^1.0.0",
"is-fullwidth-code-point": "^2.0.0"
}
},
"snapdragon": { "snapdragon": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@ -11011,6 +11473,12 @@
"get-stdin": "^4.0.1" "get-stdin": "^4.0.1"
} }
}, },
"strip-json-comments": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
"integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
"dev": true
},
"style-loader": { "style-loader": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.0.0.tgz", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.0.0.tgz",
@ -11086,6 +11554,46 @@
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
}, },
"table": {
"version": "5.4.6",
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
"integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
"dev": true,
"requires": {
"ajv": "^6.10.2",
"lodash": "^4.17.14",
"slice-ansi": "^2.1.0",
"string-width": "^3.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"tapable": { "tapable": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
@ -11158,6 +11666,18 @@
} }
} }
}, },
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
"through2": { "through2": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
@ -11240,6 +11760,15 @@
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
}, },
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"dev": true,
"requires": {
"os-tmpdir": "~1.0.2"
}
},
"to-arraybuffer": { "to-arraybuffer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@ -11414,6 +11943,21 @@
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true "dev": true
}, },
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
"dev": true,
"requires": {
"prelude-ls": "~1.1.2"
}
},
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true
},
"type-is": { "type-is": {
"version": "1.6.18", "version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@ -12135,6 +12679,12 @@
"string-width": "^1.0.2 || 2" "string-width": "^1.0.2 || 2"
} }
}, },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true
},
"worker-farm": { "worker-farm": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
@ -12189,6 +12739,15 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true "dev": true
}, },
"write": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
"integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
"dev": true,
"requires": {
"mkdirp": "^0.5.1"
}
},
"ws": { "ws": {
"version": "6.2.1", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",

@ -5,22 +5,27 @@
"main": "src/index.tsx", "main": "src/index.tsx",
"scripts": { "scripts": {
"build": "webpack --config ./webpack.config.js", "build": "webpack --config ./webpack.config.js",
"start": "webpack-dev-server --config ./webpack.config.js --mode=development" "start": "webpack-dev-server --config ./webpack.config.js --mode=development",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"lint": "eslint './src/**/*.{ts,tsx}'",
"lint:fix": "eslint './src/**/*.{ts,tsx}' --fix"
}, },
"author": "Intel", "author": "Intel",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.6.0", "@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/preset-env": "^7.6.0", "@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.6.0", "@babel/preset-typescript": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5", "@typescript-eslint/eslint-plugin": "^2.19.2",
"@typescript-eslint/eslint-plugin": "^2.10.0", "@typescript-eslint/parser": "^2.19.2",
"babel": "^6.23.0",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",
"babel-plugin-import": "^1.12.2", "babel-plugin-import": "^1.12.2",
"css-loader": "^3.2.0", "css-loader": "^3.2.0",
"eslint-config-airbnb-typescript": "^4.0.1", "eslint": "^6.8.0",
"eslint-config-airbnb-typescript": "^7.0.0",
"eslint-import-resolver-typescript": "^2.0.0", "eslint-import-resolver-typescript": "^2.0.0",
"eslint-plugin-import": "^2.18.2", "eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-jsx-a11y": "^6.2.3",
@ -61,6 +66,7 @@
"react-router-dom": "^5.1.0", "react-router-dom": "^5.1.0",
"react-share": "^3.0.1", "react-share": "^3.0.1",
"redux": "^4.0.4", "redux": "^4.0.4",
"redux-devtools-extension": "^2.13.8",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0" "redux-thunk": "^2.3.0"
} }

@ -1,7 +1,5 @@
import { AnyAction, Dispatch, ActionCreator } from 'redux';
import { ThunkAction } from 'redux-thunk';
import getCore from 'cvat-core'; import getCore from 'cvat-core';
import { ActionUnion, createAction, ThunkAction } from '../utils/redux';
const core = getCore(); const core = getCore();
@ -11,45 +9,23 @@ export enum AboutActionTypes {
GET_ABOUT_FAILED = 'GET_ABOUT_FAILED', GET_ABOUT_FAILED = 'GET_ABOUT_FAILED',
} }
function getAbout(): AnyAction { const aboutActions = {
const action = { getAbout: () => createAction(AboutActionTypes.GET_ABOUT),
type: AboutActionTypes.GET_ABOUT, getAboutSuccess: (server: any) => createAction(AboutActionTypes.GET_ABOUT_SUCCESS, { server }),
payload: {}, getAboutFailed: (error: any) => createAction(AboutActionTypes.GET_ABOUT_FAILED, { error }),
}; };
return action; export type AboutActions = ActionUnion<typeof aboutActions>;
}
export const getAboutAsync = (): ThunkAction => async (dispatch) => {
function getAboutSuccess(server: any): AnyAction { dispatch(aboutActions.getAbout());
const action = {
type: AboutActionTypes.GET_ABOUT_SUCCESS, try {
payload: { server }, const about = await core.server.about();
}; dispatch(
aboutActions.getAboutSuccess(about),
return action; );
} } catch (error) {
dispatch(aboutActions.getAboutFailed(error));
function getAboutFailed(error: any): AnyAction { }
const action = { };
type: AboutActionTypes.GET_ABOUT_FAILED,
payload: { error },
};
return action;
}
export function getAboutAsync():
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
dispatch(getAbout());
try {
const about = await core.server.about();
dispatch(
getAboutSuccess(about),
);
} catch (error) {
dispatch(getAboutFailed(error));
}
};
}

@ -1,7 +1,5 @@
import { AnyAction, Dispatch, ActionCreator } from 'redux';
import { ThunkAction } from 'redux-thunk';
import getCore from 'cvat-core'; import getCore from 'cvat-core';
import { ActionUnion, createAction, ThunkAction } from '../utils/redux';
const cvat = getCore(); const cvat = getCore();
@ -19,158 +17,79 @@ export enum AuthActionTypes {
LOGOUT_FAILED = 'LOGOUT_FAILED', LOGOUT_FAILED = 'LOGOUT_FAILED',
} }
export function registerSuccess(user: any): AnyAction { const authActions = {
return { authorizeSuccess: (user: any) => createAction(AuthActionTypes.AUTHORIZED_SUCCESS, { user }),
type: AuthActionTypes.REGISTER_SUCCESS, authorizeFailed: (error: any) => createAction(AuthActionTypes.AUTHORIZED_FAILED, { error }),
payload: { login: () => createAction(AuthActionTypes.LOGIN),
user, loginSuccess: (user: any) => createAction(AuthActionTypes.LOGIN_SUCCESS, { user }),
}, loginFailed: (error: any) => createAction(AuthActionTypes.LOGIN_FAILED, { error }),
}; register: () => createAction(AuthActionTypes.REGISTER),
} registerSuccess: (user: any) => createAction(AuthActionTypes.REGISTER_SUCCESS, { user }),
registerFailed: (error: any) => createAction(AuthActionTypes.REGISTER_FAILED, { error }),
export function registerFailed(error: any): AnyAction { logout: () => createAction(AuthActionTypes.LOGOUT),
return { logoutSuccess: () => createAction(AuthActionTypes.LOGOUT_SUCCESS),
type: AuthActionTypes.REGISTER_FAILED, logoutFailed: (error: any) => createAction(AuthActionTypes.LOGOUT_FAILED, { error }),
payload: { };
error,
}, export type AuthActions = ActionUnion<typeof authActions>;
};
} export const registerAsync = (
export function loginSuccess(user: any): AnyAction {
return {
type: AuthActionTypes.LOGIN_SUCCESS,
payload: {
user,
},
};
}
export function loginFailed(error: any): AnyAction {
return {
type: AuthActionTypes.LOGIN_FAILED,
payload: {
error,
},
};
}
export function logoutSuccess(): AnyAction {
return {
type: AuthActionTypes.LOGOUT_SUCCESS,
payload: {},
};
}
export function logoutFailed(error: any): AnyAction {
return {
type: AuthActionTypes.LOGOUT_FAILED,
payload: {
error,
},
};
}
export function authorizedSuccess(user: any): AnyAction {
return {
type: AuthActionTypes.AUTHORIZED_SUCCESS,
payload: {
user,
},
};
}
export function authorizedFailed(error: any): AnyAction {
return {
type: AuthActionTypes.AUTHORIZED_FAILED,
payload: {
error,
},
};
}
export function registerAsync(
username: string, username: string,
firstName: string, firstName: string,
lastName: string, lastName: string,
email: string, email: string,
password1: string, password1: string,
password2: string, password2: string,
): ThunkAction<Promise<void>, {}, {}, AnyAction> { ): ThunkAction => async (
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => { dispatch,
dispatch({ ) => {
type: AuthActionTypes.REGISTER, dispatch(authActions.register());
payload: {},
}); try {
await cvat.server.register(username, firstName, lastName, email, password1, password2);
let users = null; const users = await cvat.users.get({ self: true });
try {
await cvat.server.register(username, firstName, lastName, dispatch(authActions.registerSuccess(users[0]));
email, password1, password2); } catch (error) {
users = await cvat.users.get({ self: true }); dispatch(authActions.registerFailed(error));
} catch (error) { }
dispatch(registerFailed(error)); };
return;
} export const loginAsync = (username: string, password: string): ThunkAction => async (dispatch) => {
dispatch(authActions.login());
dispatch(registerSuccess(users[0]));
}; try {
} await cvat.server.login(username, password);
const users = await cvat.users.get({ self: true });
export function loginAsync(username: string, password: string):
ThunkAction<Promise<void>, {}, {}, AnyAction> { dispatch(authActions.loginSuccess(users[0]));
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => { } catch (error) {
dispatch({ dispatch(authActions.loginFailed(error));
type: AuthActionTypes.LOGIN, }
payload: {}, };
});
export const logoutAsync = (): ThunkAction => async (dispatch) => {
let users = null; dispatch(authActions.logout());
try {
await cvat.server.login(username, password); try {
users = await cvat.users.get({ self: true }); await cvat.server.logout();
} catch (error) { dispatch(authActions.logoutSuccess());
dispatch(loginFailed(error)); } catch (error) {
return; dispatch(authActions.logoutFailed(error));
} }
};
dispatch(loginSuccess(users[0]));
}; export const authorizedAsync = (): ThunkAction => async (dispatch) => {
} try {
const result = await cvat.server.authorized();
export function logoutAsync(): ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
dispatch({
type: AuthActionTypes.LOGOUT,
payload: {},
});
try {
await cvat.server.logout();
} catch (error) {
dispatch(logoutFailed(error));
return;
}
dispatch(logoutSuccess());
};
}
export function authorizedAsync(): ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
let result = null;
try {
result = await cvat.server.authorized();
} catch (error) {
dispatch(authorizedFailed(error));
return;
}
if (result) { if (result) {
const userInstance = (await cvat.users.get({ self: true }))[0]; const userInstance = (await cvat.users.get({ self: true }))[0];
dispatch(authorizedSuccess(userInstance)); dispatch(authActions.authorizeSuccess(userInstance));
} else { } else {
dispatch(authorizedSuccess(null)); dispatch(authActions.authorizeSuccess(null));
} }
}; } catch (error) {
} dispatch(authActions.authorizeFailed(error));
}
};

@ -33,8 +33,7 @@ function CursorControl(props: Props): JSX.Element {
<Icon <Icon
component={CursorIcon} component={CursorIcon}
className={activeControl === ActiveControl.CURSOR className={activeControl === ActiveControl.CURSOR
? 'cvat-active-canvas-control' : '' ? 'cvat-active-canvas-control' : ''}
}
onClick={ onClick={
activeControl !== ActiveControl.CURSOR activeControl !== ActiveControl.CURSOR
? (): void => canvasInstance.cancel() ? (): void => canvasInstance.cancel()

@ -33,8 +33,7 @@ function MoveControl(props: Props): JSX.Element {
<Icon <Icon
component={MoveIcon} component={MoveIcon}
className={activeControl === ActiveControl.DRAG_CANVAS className={activeControl === ActiveControl.DRAG_CANVAS
? 'cvat-active-canvas-control' : '' ? 'cvat-active-canvas-control' : ''}
}
onClick={(): void => { onClick={(): void => {
if (activeControl === ActiveControl.DRAG_CANVAS) { if (activeControl === ActiveControl.DRAG_CANVAS) {
canvasInstance.dragCanvas(false); canvasInstance.dragCanvas(false);

@ -33,8 +33,7 @@ function ResizeControl(props: Props): JSX.Element {
<Icon <Icon
component={ZoomIcon} component={ZoomIcon}
className={activeControl === ActiveControl.ZOOM_CANVAS className={activeControl === ActiveControl.ZOOM_CANVAS
? 'cvat-active-canvas-control' : '' ? 'cvat-active-canvas-control' : ''}
}
onClick={(): void => { onClick={(): void => {
if (activeControl === ActiveControl.ZOOM_CANVAS) { if (activeControl === ActiveControl.ZOOM_CANVAS) {
canvasInstance.zoomCanvas(false); canvasInstance.zoomCanvas(false);

@ -114,14 +114,12 @@ function LabelItemComponent(props: Props): JSX.Element {
<Col span={3}> <Col span={3}>
{ statesLocked { statesLocked
? <Icon type='lock' onClick={unlockStates} /> ? <Icon type='lock' onClick={unlockStates} />
: <Icon type='unlock' onClick={lockStates} /> : <Icon type='unlock' onClick={lockStates} />}
}
</Col> </Col>
<Col span={3}> <Col span={3}>
{ statesHidden { statesHidden
? <Icon type='eye-invisible' onClick={showStates} /> ? <Icon type='eye-invisible' onClick={showStates} />
: <Icon type='eye' onClick={hideStates} /> : <Icon type='eye' onClick={hideStates} />}
}
</Col> </Col>
</Row> </Row>
); );

@ -189,58 +189,49 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col span={6}> <Col span={6}>
{ navigateFirstKeyframe { navigateFirstKeyframe
? <Icon component={FirstIcon} onClick={navigateFirstKeyframe} /> ? <Icon component={FirstIcon} onClick={navigateFirstKeyframe} />
: <Icon component={FirstIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} /> : <Icon component={FirstIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} />}
}
</Col> </Col>
<Col span={6}> <Col span={6}>
{ navigatePrevKeyframe { navigatePrevKeyframe
? <Icon component={PreviousIcon} onClick={navigatePrevKeyframe} /> ? <Icon component={PreviousIcon} onClick={navigatePrevKeyframe} />
: <Icon component={PreviousIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} /> : <Icon component={PreviousIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} />}
}
</Col> </Col>
<Col span={6}> <Col span={6}>
{ navigateNextKeyframe { navigateNextKeyframe
? <Icon component={NextIcon} onClick={navigateNextKeyframe} /> ? <Icon component={NextIcon} onClick={navigateNextKeyframe} />
: <Icon component={NextIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} /> : <Icon component={NextIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} />}
}
</Col> </Col>
<Col span={6}> <Col span={6}>
{ navigateLastKeyframe { navigateLastKeyframe
? <Icon component={LastIcon} onClick={navigateLastKeyframe} /> ? <Icon component={LastIcon} onClick={navigateLastKeyframe} />
: <Icon component={LastIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} /> : <Icon component={LastIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} />}
}
</Col> </Col>
</Row> </Row>
<Row type='flex' justify='space-around'> <Row type='flex' justify='space-around'>
<Col span={4}> <Col span={4}>
{ outside { outside
? <Icon component={ObjectOutsideIcon} onClick={unsetOutside} /> ? <Icon component={ObjectOutsideIcon} onClick={unsetOutside} />
: <Icon type='select' onClick={setOutside} /> : <Icon type='select' onClick={setOutside} />}
}
</Col> </Col>
<Col span={4}> <Col span={4}>
{ locked { locked
? <Icon type='lock' onClick={unlock} /> ? <Icon type='lock' onClick={unlock} />
: <Icon type='unlock' onClick={lock} /> : <Icon type='unlock' onClick={lock} />}
}
</Col> </Col>
<Col span={4}> <Col span={4}>
{ occluded { occluded
? <Icon type='team' onClick={unsetOccluded} /> ? <Icon type='team' onClick={unsetOccluded} />
: <Icon type='user' onClick={setOccluded} /> : <Icon type='user' onClick={setOccluded} />}
}
</Col> </Col>
<Col span={4}> <Col span={4}>
{ hidden { hidden
? <Icon type='eye-invisible' onClick={show} /> ? <Icon type='eye-invisible' onClick={show} />
: <Icon type='eye' onClick={hide} /> : <Icon type='eye' onClick={hide} />}
}
</Col> </Col>
<Col span={4}> <Col span={4}>
{ keyframe { keyframe
? <Icon type='star' theme='filled' onClick={unsetKeyframe} /> ? <Icon type='star' theme='filled' onClick={unsetKeyframe} />
: <Icon type='star' onClick={setKeyframe} /> : <Icon type='star' onClick={setKeyframe} />}
}
</Col> </Col>
</Row> </Row>
</Col> </Col>
@ -255,20 +246,17 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col span={8}> <Col span={8}>
{ locked { locked
? <Icon type='lock' onClick={unlock} /> ? <Icon type='lock' onClick={unlock} />
: <Icon type='unlock' onClick={lock} /> : <Icon type='unlock' onClick={lock} />}
}
</Col> </Col>
<Col span={8}> <Col span={8}>
{ occluded { occluded
? <Icon type='team' onClick={unsetOccluded} /> ? <Icon type='team' onClick={unsetOccluded} />
: <Icon type='user' onClick={setOccluded} /> : <Icon type='user' onClick={setOccluded} />}
}
</Col> </Col>
<Col span={8}> <Col span={8}>
{ hidden { hidden
? <Icon type='eye-invisible' onClick={show} /> ? <Icon type='eye-invisible' onClick={show} />
: <Icon type='eye' onClick={hide} /> : <Icon type='eye' onClick={hide} />}
}
</Col> </Col>
</Row> </Row>
</Col> </Col>
@ -662,8 +650,7 @@ function ObjectItemComponent(props: Props): JSX.Element {
collapse={collapse} collapse={collapse}
changeAttribute={changeAttribute} changeAttribute={changeAttribute}
/> />
) )}
}
</div> </div>
); );
} }

@ -96,20 +96,17 @@ function ObjectListHeader(props: Props): JSX.Element {
<Col span={2}> <Col span={2}>
{ statesLocked { statesLocked
? <Icon type='lock' onClick={unlockAllStates} /> ? <Icon type='lock' onClick={unlockAllStates} />
: <Icon type='unlock' onClick={lockAllStates} /> : <Icon type='unlock' onClick={lockAllStates} />}
}
</Col> </Col>
<Col span={2}> <Col span={2}>
{ statesHidden { statesHidden
? <Icon type='eye-invisible' onClick={showAllStates} /> ? <Icon type='eye-invisible' onClick={showAllStates} />
: <Icon type='eye' onClick={hideAllStates} /> : <Icon type='eye' onClick={hideAllStates} />}
}
</Col> </Col>
<Col span={2}> <Col span={2}>
{ statesCollapsed { statesCollapsed
? <Icon type='caret-down' onClick={expandAllStates} /> ? <Icon type='caret-down' onClick={expandAllStates} />
: <Icon type='caret-up' onClick={collapseAllStates} /> : <Icon type='caret-up' onClick={collapseAllStates} />}
}
</Col> </Col>
<StatesOrderingSelector <StatesOrderingSelector
statesOrdering={statesOrdering} statesOrdering={statesOrdering}

@ -52,8 +52,7 @@ function LeftGroup(props: Props): JSX.Element {
type='link' type='link'
className={saving className={saving
? 'cvat-annotation-disabled-header-button' ? 'cvat-annotation-disabled-header-button'
: 'cvat-annotation-header-button' : 'cvat-annotation-header-button'}
}
> >
<Icon component={SaveIcon} /> <Icon component={SaveIcon} />
{ saving ? 'Saving...' : 'Save' } { saving ? 'Saving...' : 'Save' }

@ -68,8 +68,7 @@ function PlayerButtons(props: Props): JSX.Element {
onClick={onSwitchPlay} onClick={onSwitchPlay}
/> />
</Tooltip> </Tooltip>
) )}
}
<Tooltip title='Go next'> <Tooltip title='Go next'>
<Icon component={NextIcon} onClick={onNextFrame} /> <Icon component={NextIcon} onClick={onNextFrame} />

@ -119,8 +119,7 @@ export default class FileManager extends React.PureComponent<Props, State> {
{`${files.local.length} files selected`} {`${files.local.length} files selected`}
</Text> </Text>
</> </>
) )}
}
</Tabs.TabPane> </Tabs.TabPane>
); );
} }
@ -182,12 +181,12 @@ export default class FileManager extends React.PureComponent<Props, State> {
share: keys, share: keys,
}, },
}); });
}} }
}
> >
{ renderTreeNodes(treeData) } { renderTreeNodes(treeData) }
</Tree> </Tree>
) : <Text className='cvat-text-color'>No data found</Text> ) : <Text className='cvat-text-color'>No data found</Text>}
}
</Tabs.TabPane> </Tabs.TabPane>
); );
} }

@ -152,8 +152,7 @@ function HeaderContainer(props: Props): JSX.Element {
> >
Models Models
</Button> </Button>
) )}
}
{ installedAnalytics { installedAnalytics
&& ( && (
<Button <Button
@ -169,8 +168,7 @@ function HeaderContainer(props: Props): JSX.Element {
> >
Analytics Analytics
</Button> </Button>
) )}
}
</div> </div>
<div className='cvat-right-header'> <div className='cvat-right-header'>
<Button <Button

@ -49,8 +49,7 @@ export default function ConstructorViewerItem(props: ConstructorViewerItemProps)
<Icon type='close' /> <Icon type='close' />
</span> </span>
</Tooltip> </Tooltip>
) )}
}
</div> </div>
); );
} }

@ -506,8 +506,7 @@ class LabelForm extends React.PureComponent<Props, {}> {
<Text>Attributes</Text> <Text>Attributes</Text>
</Col> </Col>
</Row> </Row>
) )}
}
{ attributeItems.reverse() } { attributeItems.reverse() }
<Row type='flex' justify='start' align='middle'> <Row type='flex' justify='start' align='middle'>
{ this.renderDoneButton() } { this.renderDoneButton() }

@ -335,8 +335,7 @@ export default class ModelRunnerModalComponent extends React.PureComponent<Props
{ withMapping && tags} { withMapping && tags}
{ withMapping { withMapping
&& mappingISAvailable && mappingISAvailable
&& this.renderMappingInput(availableModelLabels, taskLabels) && this.renderMappingInput(availableModelLabels, taskLabels)}
}
{ withMapping { withMapping
&& ( && (
<div> <div>
@ -349,8 +348,7 @@ export default class ModelRunnerModalComponent extends React.PureComponent<Props
Clean old annotations Clean old annotations
</Checkbox> </Checkbox>
</div> </div>
) )}
}
</div> </div>
); );
} }

@ -53,8 +53,7 @@ export default function ModelsPageComponent(props: Props): JSX.Element {
<div className='cvat-models-page'> <div className='cvat-models-page'>
<TopBarComponent installedAutoAnnotation={installedAutoAnnotation} /> <TopBarComponent installedAutoAnnotation={installedAutoAnnotation} />
{ !!integratedModels.length { !!integratedModels.length
&& <BuiltModelsList models={integratedModels} /> && <BuiltModelsList models={integratedModels} />}
}
{ !!uploadedModels.length && ( { !!uploadedModels.length && (
<UploadedModelsList <UploadedModelsList
registeredUsers={registeredUsers} registeredUsers={registeredUsers}
@ -66,8 +65,7 @@ export default function ModelsPageComponent(props: Props): JSX.Element {
&& !uploadedModels.length && !uploadedModels.length
&& !installedTFAnnotation && !installedTFAnnotation
&& !installedTFSegmentation && !installedTFSegmentation
&& <EmptyListComponent /> && <EmptyListComponent />}
}
<FeedbackComponent /> <FeedbackComponent />
</div> </div>
); );

@ -43,8 +43,7 @@ function TopBarComponent(props: Props): JSX.Element {
> >
Create new model Create new model
</Button> </Button>
) )}
}
</Col> </Col>
</Row> </Row>
); );

@ -79,7 +79,8 @@ export default function UploadedModelItem(props: Props): JSX.Element {
Delete Delete
</Menu.Item> </Menu.Item>
</Menu> </Menu>
)} )
}
> >
<Icon className='cvat-menu-icon' component={MenuIcon} /> <Icon className='cvat-menu-icon' component={MenuIcon} />
</Dropdown> </Dropdown>

@ -245,24 +245,21 @@ export default class DetailsComponent extends React.PureComponent<Props, State>
<Icon type='check-circle' /> <Icon type='check-circle' />
Synchronized Synchronized
</Tag> </Tag>
) )}
}
{repositoryStatus === 'merged' {repositoryStatus === 'merged'
&& ( && (
<Tag color='green'> <Tag color='green'>
<Icon type='check-circle' /> <Icon type='check-circle' />
Merged Merged
</Tag> </Tag>
) )}
}
{repositoryStatus === 'syncing' {repositoryStatus === 'syncing'
&& ( && (
<Tag color='purple'> <Tag color='purple'>
<Icon type='loading' /> <Icon type='loading' />
Syncing Syncing
</Tag> </Tag>
) )}
}
{repositoryStatus === '!sync' {repositoryStatus === '!sync'
&& ( && (
<Tag <Tag
@ -290,8 +287,7 @@ export default class DetailsComponent extends React.PureComponent<Props, State>
<Icon type='warning' /> <Icon type='warning' />
Synchronize Synchronize
</Tag> </Tag>
) )}
}
</Col> </Col>
</Row> </Row>
) )

@ -32,7 +32,8 @@ export default function DetailsComponent(props: DetailsComponentProps): JSX.Elem
<ActionsMenuContainer <ActionsMenuContainer
taskInstance={taskInstance} taskInstance={taskInstance}
/> />
)} )
}
> >
<Button size='large'> <Button size='large'>
<Text className='cvat-text-color'>Actions</Text> <Text className='cvat-text-color'>Actions</Text>

@ -62,8 +62,7 @@ class TaskItemComponent extends React.PureComponent<TaskItemProps & RouteCompone
</Text> </Text>
<br /> <br />
</> </>
) )}
}
<Text type='secondary'>{`Last updated ${updated}`}</Text> <Text type='secondary'>{`Last updated ${updated}`}</Text>
</Col> </Col>
); );
@ -94,6 +93,8 @@ class TaskItemComponent extends React.PureComponent<TaskItemProps & RouteCompone
progressText = <Text strong className={progressColor}>Pending</Text>; progressText = <Text strong className={progressColor}>Pending</Text>;
} }
const jobsProgress = numOfCompleted / numOfJobs;
return ( return (
<Col span={6}> <Col span={6}>
<Row type='flex' justify='space-between' align='top'> <Row type='flex' justify='space-between' align='top'>
@ -111,7 +112,7 @@ class TaskItemComponent extends React.PureComponent<TaskItemProps & RouteCompone
<Col> <Col>
<Progress <Progress
className={`${progressColor} cvat-task-progress`} className={`${progressColor} cvat-task-progress`}
percent={numOfCompleted * 100 / numOfJobs} percent={jobsProgress * 100}
strokeColor='#1890FF' strokeColor='#1890FF'
showInfo={false} showInfo={false}
strokeWidth={5} strokeWidth={5}
@ -142,8 +143,7 @@ class TaskItemComponent extends React.PureComponent<TaskItemProps & RouteCompone
</Col> </Col>
</Row> </Row>
</> </>
) )}
}
</Col> </Col>
); );
} }

@ -223,8 +223,7 @@ class TasksPageComponent extends React.PureComponent<TasksPageProps & RouteCompo
<TaskListContainer <TaskListContainer
onSwitchPage={this.handlePagination} onSwitchPage={this.handlePagination}
/> />
) : <EmptyListComponent /> ) : <EmptyListComponent />}
}
<FeedbackComponent /> <FeedbackComponent />
</div> </div>
); );

@ -1,13 +1,12 @@
import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { logoutAsync } from 'actions/auth-actions';
import { import {
SupportedPlugins, SupportedPlugins,
CombinedState, CombinedState,
} from 'reducers/interfaces'; } from 'reducers/interfaces';
import HeaderComponent from 'components/header/header'; import HeaderComponent from 'components/header/header';
import { logoutAsync } from 'actions/auth-actions';
interface StateToProps { interface StateToProps {
logoutFetching: boolean; logoutFetching: boolean;
@ -20,7 +19,7 @@ interface StateToProps {
} }
interface DispatchToProps { interface DispatchToProps {
onLogout(): void; onLogout: typeof logoutAsync;
} }
function mapStateToProps(state: CombinedState): StateToProps { function mapStateToProps(state: CombinedState): StateToProps {
@ -39,19 +38,11 @@ function mapStateToProps(state: CombinedState): StateToProps {
}; };
} }
function mapDispatchToProps(dispatch: any): DispatchToProps { const mapDispatchToProps: DispatchToProps = {
return { onLogout: logoutAsync,
onLogout: (): void => dispatch(logoutAsync()), };
};
}
function HeaderContainer(props: StateToProps & DispatchToProps): JSX.Element {
return (
<HeaderComponent {...props} />
);
}
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps, mapDispatchToProps,
)(HeaderContainer); )(HeaderComponent);

@ -1,15 +1,14 @@
import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { loginAsync } from 'actions/auth-actions';
import LoginPageComponent from 'components/login-page/login-page'; import LoginPageComponent from 'components/login-page/login-page';
import { CombinedState } from 'reducers/interfaces'; import { CombinedState } from 'reducers/interfaces';
import { loginAsync } from 'actions/auth-actions';
interface StateToProps { interface StateToProps {
fetching: boolean; fetching: boolean;
} }
interface DispatchToProps { interface DispatchToProps {
onLogin(username: string, password: string): void; onLogin: typeof loginAsync;
} }
function mapStateToProps(state: CombinedState): StateToProps { function mapStateToProps(state: CombinedState): StateToProps {
@ -18,19 +17,11 @@ function mapStateToProps(state: CombinedState): StateToProps {
}; };
} }
function mapDispatchToProps(dispatch: any): DispatchToProps { const mapDispatchToProps: DispatchToProps = {
return { onLogin: loginAsync,
onLogin: (...args): void => dispatch(loginAsync(...args)), };
};
}
function LoginPageContainer(props: DispatchToProps & StateToProps): JSX.Element {
return (
<LoginPageComponent {...props} />
);
}
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps, mapDispatchToProps,
)(LoginPageContainer); )(LoginPageComponent);

@ -6,10 +6,11 @@ import {
Reducer, Reducer,
} from 'redux'; } from 'redux';
import { createLogger } from 'redux-logger'; import { createLogger } from 'redux-logger';
import isDev from 'utils/enviroment';
const logger = createLogger({ const logger = createLogger({
predicate: () => process.env.NODE_ENV === 'development', predicate: isDev,
collapsed: true, collapsed: true,
}); });
@ -21,9 +22,18 @@ const middlewares = [
let store: Store | null = null; let store: Store | null = null;
export default function createCVATStore(createRootReducer: () => Reducer): void { export default function createCVATStore(createRootReducer: () => Reducer): void {
let appliedMiddlewares = applyMiddleware(...middlewares);
if (isDev()) {
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
const { composeWithDevTools } = require('redux-devtools-extension');
appliedMiddlewares = composeWithDevTools(appliedMiddlewares);
}
store = createStore( store = createStore(
createRootReducer(), createRootReducer(),
applyMiddleware(...middlewares), appliedMiddlewares,
); );
} }

@ -1,8 +1,7 @@
import { AnyAction } from 'redux'; import { AboutActions, AboutActionTypes } from 'actions/about-actions';
import { AuthActions, AuthActionTypes } from 'actions/auth-actions';
import { AboutState } from './interfaces'; import { AboutState } from './interfaces';
import { AuthActionTypes } from '../actions/auth-actions';
import { AboutActionTypes } from '../actions/about-actions';
const defaultState: AboutState = { const defaultState: AboutState = {
server: {}, server: {},
@ -10,7 +9,10 @@ const defaultState: AboutState = {
initialized: false, initialized: false,
}; };
export default function (state: AboutState = defaultState, action: AnyAction): AboutState { export default function (
state: AboutState = defaultState,
action: AboutActions | AuthActions,
): AboutState {
switch (action.type) { switch (action.type) {
case AboutActionTypes.GET_ABOUT: { case AboutActionTypes.GET_ABOUT: {
return { return {

@ -1,6 +1,4 @@
import { AnyAction } from 'redux'; import { AuthActions, AuthActionTypes } from 'actions/auth-actions';
import { AuthActionTypes } from 'actions/auth-actions';
import { AuthState } from './interfaces'; import { AuthState } from './interfaces';
const defaultState: AuthState = { const defaultState: AuthState = {
@ -9,7 +7,7 @@ const defaultState: AuthState = {
user: null, user: null,
}; };
export default (state = defaultState, action: AnyAction): AuthState => { export default (state = defaultState, action: AuthActions): AuthState => {
switch (action.type) { switch (action.type) {
case AuthActionTypes.AUTHORIZED_SUCCESS: case AuthActionTypes.AUTHORIZED_SUCCESS:
return { return {
@ -53,7 +51,7 @@ export default (state = defaultState, action: AnyAction): AuthState => {
return { return {
...state, ...state,
fetching: true, fetching: true,
user: action.payload.user, user: null,
}; };
case AuthActionTypes.REGISTER_SUCCESS: case AuthActionTypes.REGISTER_SUCCESS:
return { return {

@ -0,0 +1,3 @@
export default function isDev() {
return process.env.NODE_ENV === 'development';
}

@ -0,0 +1,18 @@
import { Action, ActionCreatorsMapObject, AnyAction } from 'redux';
import { ThunkAction as _ThunkAction } from 'redux-thunk';
import { CombinedState } from '../reducers/interfaces';
export interface ActionWithPayload<T, P> extends Action<T> {
payload: P;
}
export function createAction<T extends string>(type: T): Action<T>;
export function createAction<T extends string, P>(type: T, payload: P): ActionWithPayload<T, P>;
export function createAction<T extends string, P>(type: T, payload?: P) {
return typeof payload === 'undefined' ? { type } : { type, payload };
}
export type ActionUnion<A extends ActionCreatorsMapObject> = ReturnType<A[keyof A]>;
export type ThunkAction<R = void, A extends Action = AnyAction>
= _ThunkAction<R, CombinedState, {}, A>;

@ -64,4 +64,4 @@ const validationPatterns = {
}, },
}; };
export default { ...validationPatterns }; export default validationPatterns;

Loading…
Cancel
Save