Hub - initial commit (#393)

This commit is contained in:
Piotr Rogowski 2022-01-24 22:59:21 +01:00 committed by GitHub
parent 1b1352df47
commit 5e81493569
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 329 additions and 218 deletions

220
package-lock.json generated
View File

@ -10,11 +10,11 @@
"license": "MIT",
"dependencies": {
"@reduxjs/toolkit": "^1.7.1",
"@sentry/react": "^6.16.1",
"@sentry/tracing": "^6.16.1",
"@sentry/react": "^6.17.0",
"@sentry/tracing": "^6.17.0",
"@speedy-tuner/ini": "^0.3.0",
"@speedy-tuner/types": "^0.3.0",
"antd": "^4.18.4",
"antd": "^4.18.5",
"firebase": "^9.6.4",
"kbar": "^0.1.0-beta.27",
"mlg-converter": "^0.5.1",
@ -3828,13 +3828,13 @@
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
},
"node_modules/@sentry/browser": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.16.1.tgz",
"integrity": "sha512-F2I5RL7RTLQF9CccMrqt73GRdK3FdqaChED3RulGQX5lH6U3exHGFxwyZxSrY4x6FedfBFYlfXWWCJXpLnFkow==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.17.1.tgz",
"integrity": "sha512-Tj4uXNRA6dAwKzOwLGvTOsD8WNk6tiPaIYXPsIIOdBjDsFBfiu4JY8ygCzQpj7Fwy8qL6UULygU4kBuAx2ieyQ==",
"dependencies": {
"@sentry/core": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/core": "6.17.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"tslib": "^1.9.3"
},
"engines": {
@ -3842,14 +3842,14 @@
}
},
"node_modules/@sentry/core": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.16.1.tgz",
"integrity": "sha512-UFI0264CPUc5cR1zJH+S2UPOANpm6dLJOnsvnIGTjsrwzR0h8Hdl6rC2R/GPq+WNbnipo9hkiIwDlqbqvIU5vw==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.17.1.tgz",
"integrity": "sha512-dRcKs3+IKx2w7+xwg3mWRc4ghjnY+36W+qCOJXRbWyRjlzb67Es+TDYY3BxWwN4beNr2dvQkRt5HoPxJnrzGVQ==",
"dependencies": {
"@sentry/hub": "6.16.1",
"@sentry/minimal": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/hub": "6.17.1",
"@sentry/minimal": "6.17.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"tslib": "^1.9.3"
},
"engines": {
@ -3857,12 +3857,12 @@
}
},
"node_modules/@sentry/hub": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.16.1.tgz",
"integrity": "sha512-4PGtg6AfpqMkreTpL7ymDeQ/U1uXv03bKUuFdtsSTn/FRf9TLS4JB0KuTZCxfp1IRgAA+iFg6B784dDkT8R9eg==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.17.1.tgz",
"integrity": "sha512-14lNcM4kt2sKhsfZ6WG2gbsPMydxBAY1OSk+WodO/x0Esdr7VbaVngSJSFzCI0iRBPLSQuFVtUWaAZzSF+WdiA==",
"dependencies": {
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"tslib": "^1.9.3"
},
"engines": {
@ -3870,12 +3870,12 @@
}
},
"node_modules/@sentry/minimal": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.16.1.tgz",
"integrity": "sha512-dq+mI1EQIvUM+zJtGCVgH3/B3Sbx4hKlGf2Usovm9KoqWYA+QpfVBholYDe/H2RXgO7LFEefDLvOdHDkqeJoyA==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.17.1.tgz",
"integrity": "sha512-0V5YqVCylMjjDhD98Xod1ZdxzVRUjDSq7ssCJ2PRjfRAEtk7mN5oeznyZUhyx4pZB8hNh2G9PdasIR2WIjYN9w==",
"dependencies": {
"@sentry/hub": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/hub": "6.17.1",
"@sentry/types": "6.17.1",
"tslib": "^1.9.3"
},
"engines": {
@ -3883,14 +3883,14 @@
}
},
"node_modules/@sentry/react": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.16.1.tgz",
"integrity": "sha512-n8fOEKbym4kBi946q3AWXBNy1UKTmABj/hE2nAJbTWhi5IwdM7WBG6QCT2yq7oTHLuTxQrAwgKQc+A6zFTyVHg==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.17.1.tgz",
"integrity": "sha512-Woq1QCtpXv4yXetOv68/V61vaUIVrcs7K0SZ3GZxdMJMmu3hpECFZIClFYE4z2fFT/ygueGJoGAL177R7/U3Vw==",
"dependencies": {
"@sentry/browser": "6.16.1",
"@sentry/minimal": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/browser": "6.17.1",
"@sentry/minimal": "6.17.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"hoist-non-react-statics": "^3.3.2",
"tslib": "^1.9.3"
},
@ -3902,14 +3902,14 @@
}
},
"node_modules/@sentry/tracing": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.16.1.tgz",
"integrity": "sha512-MPSbqXX59P+OEeST+U2V/8Hu/8QjpTUxTNeNyTHWIbbchdcMMjDbXTS3etCgajZR6Ro+DHElOz5cdSxH6IBGlA==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.17.1.tgz",
"integrity": "sha512-2to3Y3I+kdoJWdbPbK4llALXc+765W0SAAghFWEJ5L3mups59CGf03HtRHPE8p2Hw2Tr6YO6gjtZhvm+I49LPg==",
"dependencies": {
"@sentry/hub": "6.16.1",
"@sentry/minimal": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/hub": "6.17.1",
"@sentry/minimal": "6.17.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"tslib": "^1.9.3"
},
"engines": {
@ -3917,19 +3917,19 @@
}
},
"node_modules/@sentry/types": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.16.1.tgz",
"integrity": "sha512-Wh354g30UsJ5kYJbercektGX4ZMc9MHU++1NjeN2bTMnbofEcpUDWIiKeulZEY65IC1iU+1zRQQgtYO+/hgCUQ==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.17.1.tgz",
"integrity": "sha512-EBFiN3utd1xoxowy+WFSDQGgS4J+VEZbc7j3uYVZNjn03/w5pe9FfeLsszJ2s1/hCf/K7GAGH4NA7i0r7SWF3g==",
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/utils": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.16.1.tgz",
"integrity": "sha512-7ngq/i4R8JZitJo9Sl8PDnjSbDehOxgr1vsoMmerIsyRZ651C/8B+jVkMhaAPgSdyJ0AlE3O7DKKTP1FXFw9qw==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.17.1.tgz",
"integrity": "sha512-FOjxMZ4yBflYvJYhNDacRfSa0NTKLsjCXQ3u1phxODRUwhQ7wwqmtwy86AL5uzfpiqZKwXAPlWoWihBluw7vqw==",
"dependencies": {
"@sentry/types": "6.16.1",
"@sentry/types": "6.17.1",
"tslib": "^1.9.3"
},
"engines": {
@ -5356,22 +5356,21 @@
}
},
"node_modules/antd": {
"version": "4.18.4",
"resolved": "https://registry.npmjs.org/antd/-/antd-4.18.4.tgz",
"integrity": "sha512-7KCEhIyPeQJF/OenkfOTcx+5sHpiI5U6OzYmTUJn9wVPjcl07eFXu2w9teM9pJV9X7mSUWyPeM5aMFmBQo2TNQ==",
"version": "4.18.5",
"resolved": "https://registry.npmjs.org/antd/-/antd-4.18.5.tgz",
"integrity": "sha512-5fN3C2lWAzonhOYYlNpzIw2OHl7vxFZ+4cJ7DK/XZrV+75OY61Y+OkanqMJwrFtDDamIez35OM7cAezGko9tew==",
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons": "^4.7.0",
"@ant-design/react-slick": "~0.28.1",
"@babel/runtime": "^7.12.5",
"@ctrl/tinycolor": "^3.4.0",
"array-tree-filter": "^2.1.0",
"classnames": "^2.2.6",
"copy-to-clipboard": "^3.2.0",
"lodash": "^4.17.21",
"memoize-one": "^6.0.0",
"moment": "^2.25.3",
"rc-cascader": "~3.0.0-alpha.8",
"rc-cascader": "~3.2.1",
"rc-checkbox": "~2.3.0",
"rc-collapse": "~3.1.0",
"rc-dialog": "~8.6.0",
@ -5398,7 +5397,7 @@
"rc-textarea": "~0.3.0",
"rc-tooltip": "~5.1.1",
"rc-tree": "~5.4.3",
"rc-tree-select": "~5.1.0",
"rc-tree-select": "~5.1.1",
"rc-trigger": "^5.2.10",
"rc-upload": "~4.3.0",
"rc-util": "^5.14.0",
@ -18329,14 +18328,14 @@
}
},
"node_modules/rc-cascader": {
"version": "3.0.0-alpha.8",
"resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.0.0-alpha.8.tgz",
"integrity": "sha512-zZ6tczHacUy622E7m5aruCcB3ii+J5bhusCPpyb64LP9KbcKcquchdgWeeyVY/7K8BrJXOTOJW1MDZ9nxsWBWw==",
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.2.1.tgz",
"integrity": "sha512-Raxam9tFzBL4TCgHoyVcf7+Q2KSFneUk3FZXi9w1tfxEihLlezSH0oCNMjHJN8hxWwwx9ZbI9UzWTfFImjXc0Q==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"array-tree-filter": "^2.1.0",
"classnames": "^2.3.1",
"rc-select": "~14.0.0-alpha.8",
"rc-select": "~14.0.0-alpha.23",
"rc-tree": "~5.4.3",
"rc-util": "^5.6.1"
},
@ -28086,84 +28085,84 @@
}
},
"@sentry/browser": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.16.1.tgz",
"integrity": "sha512-F2I5RL7RTLQF9CccMrqt73GRdK3FdqaChED3RulGQX5lH6U3exHGFxwyZxSrY4x6FedfBFYlfXWWCJXpLnFkow==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.17.1.tgz",
"integrity": "sha512-Tj4uXNRA6dAwKzOwLGvTOsD8WNk6tiPaIYXPsIIOdBjDsFBfiu4JY8ygCzQpj7Fwy8qL6UULygU4kBuAx2ieyQ==",
"requires": {
"@sentry/core": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/core": "6.17.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"tslib": "^1.9.3"
}
},
"@sentry/core": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.16.1.tgz",
"integrity": "sha512-UFI0264CPUc5cR1zJH+S2UPOANpm6dLJOnsvnIGTjsrwzR0h8Hdl6rC2R/GPq+WNbnipo9hkiIwDlqbqvIU5vw==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.17.1.tgz",
"integrity": "sha512-dRcKs3+IKx2w7+xwg3mWRc4ghjnY+36W+qCOJXRbWyRjlzb67Es+TDYY3BxWwN4beNr2dvQkRt5HoPxJnrzGVQ==",
"requires": {
"@sentry/hub": "6.16.1",
"@sentry/minimal": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/hub": "6.17.1",
"@sentry/minimal": "6.17.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"tslib": "^1.9.3"
}
},
"@sentry/hub": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.16.1.tgz",
"integrity": "sha512-4PGtg6AfpqMkreTpL7ymDeQ/U1uXv03bKUuFdtsSTn/FRf9TLS4JB0KuTZCxfp1IRgAA+iFg6B784dDkT8R9eg==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.17.1.tgz",
"integrity": "sha512-14lNcM4kt2sKhsfZ6WG2gbsPMydxBAY1OSk+WodO/x0Esdr7VbaVngSJSFzCI0iRBPLSQuFVtUWaAZzSF+WdiA==",
"requires": {
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"tslib": "^1.9.3"
}
},
"@sentry/minimal": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.16.1.tgz",
"integrity": "sha512-dq+mI1EQIvUM+zJtGCVgH3/B3Sbx4hKlGf2Usovm9KoqWYA+QpfVBholYDe/H2RXgO7LFEefDLvOdHDkqeJoyA==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.17.1.tgz",
"integrity": "sha512-0V5YqVCylMjjDhD98Xod1ZdxzVRUjDSq7ssCJ2PRjfRAEtk7mN5oeznyZUhyx4pZB8hNh2G9PdasIR2WIjYN9w==",
"requires": {
"@sentry/hub": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/hub": "6.17.1",
"@sentry/types": "6.17.1",
"tslib": "^1.9.3"
}
},
"@sentry/react": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.16.1.tgz",
"integrity": "sha512-n8fOEKbym4kBi946q3AWXBNy1UKTmABj/hE2nAJbTWhi5IwdM7WBG6QCT2yq7oTHLuTxQrAwgKQc+A6zFTyVHg==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.17.1.tgz",
"integrity": "sha512-Woq1QCtpXv4yXetOv68/V61vaUIVrcs7K0SZ3GZxdMJMmu3hpECFZIClFYE4z2fFT/ygueGJoGAL177R7/U3Vw==",
"requires": {
"@sentry/browser": "6.16.1",
"@sentry/minimal": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/browser": "6.17.1",
"@sentry/minimal": "6.17.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"hoist-non-react-statics": "^3.3.2",
"tslib": "^1.9.3"
}
},
"@sentry/tracing": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.16.1.tgz",
"integrity": "sha512-MPSbqXX59P+OEeST+U2V/8Hu/8QjpTUxTNeNyTHWIbbchdcMMjDbXTS3etCgajZR6Ro+DHElOz5cdSxH6IBGlA==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.17.1.tgz",
"integrity": "sha512-2to3Y3I+kdoJWdbPbK4llALXc+765W0SAAghFWEJ5L3mups59CGf03HtRHPE8p2Hw2Tr6YO6gjtZhvm+I49LPg==",
"requires": {
"@sentry/hub": "6.16.1",
"@sentry/minimal": "6.16.1",
"@sentry/types": "6.16.1",
"@sentry/utils": "6.16.1",
"@sentry/hub": "6.17.1",
"@sentry/minimal": "6.17.1",
"@sentry/types": "6.17.1",
"@sentry/utils": "6.17.1",
"tslib": "^1.9.3"
}
},
"@sentry/types": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.16.1.tgz",
"integrity": "sha512-Wh354g30UsJ5kYJbercektGX4ZMc9MHU++1NjeN2bTMnbofEcpUDWIiKeulZEY65IC1iU+1zRQQgtYO+/hgCUQ=="
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.17.1.tgz",
"integrity": "sha512-EBFiN3utd1xoxowy+WFSDQGgS4J+VEZbc7j3uYVZNjn03/w5pe9FfeLsszJ2s1/hCf/K7GAGH4NA7i0r7SWF3g=="
},
"@sentry/utils": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.16.1.tgz",
"integrity": "sha512-7ngq/i4R8JZitJo9Sl8PDnjSbDehOxgr1vsoMmerIsyRZ651C/8B+jVkMhaAPgSdyJ0AlE3O7DKKTP1FXFw9qw==",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.17.1.tgz",
"integrity": "sha512-FOjxMZ4yBflYvJYhNDacRfSa0NTKLsjCXQ3u1phxODRUwhQ7wwqmtwy86AL5uzfpiqZKwXAPlWoWihBluw7vqw==",
"requires": {
"@sentry/types": "6.16.1",
"@sentry/types": "6.17.1",
"tslib": "^1.9.3"
}
},
@ -29224,22 +29223,21 @@
}
},
"antd": {
"version": "4.18.4",
"resolved": "https://registry.npmjs.org/antd/-/antd-4.18.4.tgz",
"integrity": "sha512-7KCEhIyPeQJF/OenkfOTcx+5sHpiI5U6OzYmTUJn9wVPjcl07eFXu2w9teM9pJV9X7mSUWyPeM5aMFmBQo2TNQ==",
"version": "4.18.5",
"resolved": "https://registry.npmjs.org/antd/-/antd-4.18.5.tgz",
"integrity": "sha512-5fN3C2lWAzonhOYYlNpzIw2OHl7vxFZ+4cJ7DK/XZrV+75OY61Y+OkanqMJwrFtDDamIez35OM7cAezGko9tew==",
"requires": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons": "^4.7.0",
"@ant-design/react-slick": "~0.28.1",
"@babel/runtime": "^7.12.5",
"@ctrl/tinycolor": "^3.4.0",
"array-tree-filter": "^2.1.0",
"classnames": "^2.2.6",
"copy-to-clipboard": "^3.2.0",
"lodash": "^4.17.21",
"memoize-one": "^6.0.0",
"moment": "^2.25.3",
"rc-cascader": "~3.0.0-alpha.8",
"rc-cascader": "~3.2.1",
"rc-checkbox": "~2.3.0",
"rc-collapse": "~3.1.0",
"rc-dialog": "~8.6.0",
@ -29266,7 +29264,7 @@
"rc-textarea": "~0.3.0",
"rc-tooltip": "~5.1.1",
"rc-tree": "~5.4.3",
"rc-tree-select": "~5.1.0",
"rc-tree-select": "~5.1.1",
"rc-trigger": "^5.2.10",
"rc-upload": "~4.3.0",
"rc-util": "^5.14.0",
@ -39159,14 +39157,14 @@
}
},
"rc-cascader": {
"version": "3.0.0-alpha.8",
"resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.0.0-alpha.8.tgz",
"integrity": "sha512-zZ6tczHacUy622E7m5aruCcB3ii+J5bhusCPpyb64LP9KbcKcquchdgWeeyVY/7K8BrJXOTOJW1MDZ9nxsWBWw==",
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.2.1.tgz",
"integrity": "sha512-Raxam9tFzBL4TCgHoyVcf7+Q2KSFneUk3FZXi9w1tfxEihLlezSH0oCNMjHJN8hxWwwx9ZbI9UzWTfFImjXc0Q==",
"requires": {
"@babel/runtime": "^7.12.5",
"array-tree-filter": "^2.1.0",
"classnames": "^2.3.1",
"rc-select": "~14.0.0-alpha.8",
"rc-select": "~14.0.0-alpha.23",
"rc-tree": "~5.4.3",
"rc-util": "^5.6.1"
}

View File

@ -32,11 +32,11 @@
},
"dependencies": {
"@reduxjs/toolkit": "^1.7.1",
"@sentry/react": "^6.16.1",
"@sentry/tracing": "^6.16.1",
"@sentry/react": "^6.17.0",
"@sentry/tracing": "^6.17.0",
"@speedy-tuner/ini": "^0.3.0",
"@speedy-tuner/types": "^0.3.0",
"antd": "^4.18.4",
"antd": "^4.18.5",
"firebase": "^9.6.4",
"kbar": "^0.1.0-beta.27",
"mlg-converter": "^0.5.1",

View File

@ -33,6 +33,7 @@ import {
import useDb from './hooks/useDb';
import useServerStorage from './hooks/useServerStorage';
import Info from './pages/Info';
import Hub from './pages/Hub';
import 'react-perfect-scrollbar/dist/css/styles.css';
@ -127,11 +128,7 @@ const App = ({ ui, navigation }: { ui: UIState, navigation: NavigationState }) =
<Switch>
<Route path={Routes.ROOT} exact>
<ContentFor>
<Result
status="info"
title="This page is under construction"
style={{ marginTop: 50 }}
/>
<Hub />
</ContentFor>
</Route>
<Route path={Routes.TUNE_ROOT} exact>

View File

@ -30,6 +30,7 @@ import {
InfoCircleOutlined,
FundOutlined,
SettingOutlined,
CarOutlined,
} from '@ant-design/icons';
import {
useHistory,
@ -313,6 +314,14 @@ const CommandPalette = (props: CommandPaletteProps) => {
}, [logout]);
const initialActions = [
{
id: 'HubAction',
section: Sections.NAVIGATION,
name: 'Hub',
subtitle: 'Public tunes and logs.',
icon: <CarOutlined />,
perform: () => history.push(Routes.ROOT),
},
{
id: 'ToggleSidebar',
name: 'Toggle Sidebar',

View File

@ -45,6 +45,7 @@ import {
UserAddOutlined,
LogoutOutlined,
InfoCircleOutlined,
CarOutlined,
} from '@ant-design/icons';
import { useKBar } from 'kbar';
import store from '../store';
@ -74,6 +75,9 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
const history = useHistory();
const { query } = useKBar();
const buildTuneUrl = (route: string) => tuneId ? generatePath(route, { tuneId }) : null;
const matchedRootPath = useMemo(() => matchPath(pathname, {
path: Routes.ROOT,
}), [pathname]);
const matchedTuneRootPath = useMemo(() => matchPath(pathname, {
path: Routes.TUNE_ROOT,
}), [pathname]);
@ -114,33 +118,39 @@ const TopBar = ({ tuneId }: { tuneId: string | null }) => {
<Col span={14} md={12} sm={16} style={{ textAlign: 'center' }}>
<Radio.Group
key={pathname}
defaultValue={matchedTabPath?.url || matchedTuneRootPath?.url}
defaultValue={matchedTabPath?.url || matchedTuneRootPath?.url || matchedRootPath?.url || ''}
optionType="button"
buttonStyle="solid"
onChange={(e) => history.push(e.target.value)}
>
<Radio.Button value={buildTuneUrl(Routes.ROOT)}>
<Space>
<CarOutlined />
{lg && 'Hub'}
</Space>
</Radio.Button>
<Radio.Button value={buildTuneUrl(Routes.TUNE_ROOT)}>
<Space>
<InfoCircleOutlined />
{sm && 'Info'}
{lg && 'Info'}
</Space>
</Radio.Button>
<Radio.Button value={buildTuneUrl(Routes.TUNE_TUNE)}>
<Space>
<ToolOutlined />
{sm && 'Tune'}
{lg && 'Tune'}
</Space>
</Radio.Button>
<Radio.Button value={buildTuneUrl(Routes.TUNE_LOGS)}>
<Space>
<FundOutlined />
{sm && 'Logs'}
{lg && 'Logs'}
</Space>
</Radio.Button>
<Radio.Button value={buildTuneUrl(Routes.TUNE_DIAGNOSE)}>
<Space>
<SettingOutlined />
{sm && 'Diagnose'}
{lg && 'Diagnose'}
</Space>
</Radio.Button>
</Radio.Group>

View File

@ -1,6 +1,14 @@
import {
User,
UserCredential,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
sendEmailVerification,
signOut,
sendPasswordResetEmail,
GoogleAuthProvider,
GithubAuthProvider,
signInWithPopup,
} from 'firebase/auth';
import {
createContext,
@ -10,17 +18,7 @@ import {
useMemo,
useState,
} from 'react';
import {
auth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
sendEmailVerification,
signOut,
sendPasswordResetEmail,
GoogleAuthProvider,
GithubAuthProvider,
signInWithPopup,
} from '../firebase';
import { auth } from '../firebase';
interface AuthValue {
currentUser: User | null,

View File

@ -1,33 +1,9 @@
import { initializeApp } from 'firebase/app';
import { getPerformance } from 'firebase/performance';
import {
getAuth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
sendEmailVerification,
sendPasswordResetEmail,
GoogleAuthProvider,
GithubAuthProvider,
signInWithPopup,
} from 'firebase/auth';
import { getAuth } from 'firebase/auth';
import { getAnalytics } from 'firebase/analytics';
import {
getStorage,
ref,
uploadBytes,
uploadBytesResumable,
deleteObject,
getBytes,
} from 'firebase/storage';
import {
getFirestore,
doc,
setDoc,
collection,
addDoc,
getDoc,
} from 'firebase/firestore';
import { getStorage } from 'firebase/storage';
import { getFirestore } from 'firebase/firestore';
const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
@ -47,27 +23,10 @@ const db = getFirestore();
const storage = getStorage();
export {
auth,
app,
analytics,
performance,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
sendEmailVerification,
signOut,
sendPasswordResetEmail,
GoogleAuthProvider,
GithubAuthProvider,
signInWithPopup,
ref as storageRef,
storage,
uploadBytes,
uploadBytesResumable,
deleteObject,
getBytes,
doc as fireStoreDoc,
collection as fireStoreCollection,
setDoc,
addDoc,
getDoc,
auth,
db,
storage,
};

View File

@ -1,12 +1,18 @@
import { notification } from 'antd';
import * as Sentry from '@sentry/browser';
import { Timestamp } from 'firebase/firestore';
import {
fireStoreDoc,
Timestamp,
doc,
getDoc,
setDoc,
db,
} from '../firebase';
collection,
where,
query,
getDocs,
QuerySnapshot,
orderBy,
} from 'firebase/firestore';
import { db } from '../firebase';
import { TuneDbData } from '../types/dbData';
const TUNES_PATH = 'publicTunes';
@ -14,9 +20,9 @@ const TUNES_PATH = 'publicTunes';
const genericError = (error: Error) => notification.error({ message: 'Database Error', description: error.message });
const useDb = () => {
const getData = async (tuneId: string) => {
const getTuneData = async (tuneId: string) => {
try {
const tune = (await getDoc(fireStoreDoc(db, TUNES_PATH, tuneId))).data() as TuneDbData;
const tune = (await getDoc(doc(db, TUNES_PATH, tuneId))).data() as TuneDbData;
const processed = {
...tune,
createdAt: (tune?.createdAt as Timestamp)?.toDate().toISOString(),
@ -33,9 +39,29 @@ const useDb = () => {
}
};
const listTunesData = async () => {
try {
const tunesRef = collection(db, TUNES_PATH);
const q = query(
tunesRef,
where('isPublished', '==', true),
where('isListed', '==', true),
orderBy('createdAt', 'desc'),
);
return Promise.resolve(await getDocs(q));
} catch (error) {
Sentry.captureException(error);
console.error(error);
genericError(error as Error);
return Promise.reject(error);
}
};
const updateData = async (tuneId: string, data: TuneDbData) => {
try {
await setDoc(fireStoreDoc(db, TUNES_PATH, tuneId), data, { merge: true });
await setDoc(doc(db, TUNES_PATH, tuneId), data, { merge: true });
return Promise.resolve();
} catch (error) {
@ -48,8 +74,9 @@ const useDb = () => {
};
return {
getTune: (tuneId: string): Promise<TuneDbData> => getData(tuneId),
updateData: (tuneId: string, data: TuneDbData): Promise<void> => updateData(tuneId, data),
getTune: (tuneId: string): Promise<TuneDbData> => getTuneData(tuneId),
listTunes: (): Promise<QuerySnapshot<TuneDbData>> => listTunesData(),
};
};

View File

@ -1,13 +1,13 @@
import { notification } from 'antd';
import * as Sentry from '@sentry/browser';
import { UploadTask } from 'firebase/storage';
import {
storage,
storageRef,
UploadTask,
ref,
getBytes,
deleteObject,
uploadBytesResumable,
} from '../firebase';
} from 'firebase/storage';
import { storage } from '../firebase';
const BASE_PATH = 'public/users';
@ -16,7 +16,7 @@ const genericError = (error: Error) => notification.error({ message: 'Database E
const useServerStorage = () => {
const getFile = async (path: string) => {
try {
const buffer = await getBytes(storageRef(storage, path));
const buffer = await getBytes(ref(storage, path));
return Promise.resolve(buffer);
} catch (error) {
@ -30,7 +30,7 @@ const useServerStorage = () => {
const removeFile = async (path: string) => {
try {
await deleteObject(storageRef(storage, path));
await deleteObject(ref(storage, path));
return Promise.resolve();
} catch (error) {
@ -43,7 +43,7 @@ const useServerStorage = () => {
};
const uploadFile = (path: string, file: File, data: Uint8Array) =>
uploadBytesResumable(storageRef(storage, path), data, {
uploadBytesResumable(ref(storage, path), data, {
customMetadata: {
name: file.name,
size: `${file.size}`,

111
src/pages/Hub.tsx Normal file
View File

@ -0,0 +1,111 @@
import {
Badge,
Card,
Col,
Row,
Tooltip,
Typography,
} from 'antd';
import {
CopyOutlined,
StarOutlined,
ArrowRightOutlined,
} from '@ant-design/icons';
import {
useCallback,
useEffect,
useState,
} from 'react';
import {
generatePath,
useHistory,
} from 'react-router';
import useDb from '../hooks/useDb';
import { TuneDbData } from '../types/dbData';
import { Routes } from '../routes';
import { generateShareUrl } from '../utils/url';
const containerStyle = {
padding: 20,
maxWidth: 800,
margin: '0 auto',
};
const loadingCards = (
<>
<Col span={8}>
<Card loading />
</Col>
<Col span={8}>
<Card loading />
</Col>
<Col span={8}>
<Card loading />
</Col>
</>
);
const Hub = () => {
const [tunes, setTunes] = useState<TuneDbData[]>([]);
const { listTunes } = useDb();
const history = useHistory();
const [copied, setCopied] = useState(false);
const goToTune = (tuneId: string) => history.push(generatePath(Routes.TUNE_ROOT, { tuneId }));
const copyToClipboard = async (shareUrl: string) => {
if (navigator.clipboard) {
await navigator.clipboard.writeText(shareUrl);
setCopied(true);
setTimeout(() => setCopied(false), 1000);
}
};
const loadData = useCallback(() => {
listTunes().then((data) => {
const temp: TuneDbData[] = [];
data.forEach((tuneSnapshot) => {
temp.push(tuneSnapshot.data());
});
setTunes(temp);
});
}, [listTunes]);
useEffect(() => {
loadData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // TODO: fix this
return (
<div style={containerStyle}>
<Typography.Title>Hub</Typography.Title>
<Row gutter={[16, 16]}>
{tunes.length === 0 ? loadingCards : (
tunes.map((tune) => (
<Col span={16} sm={8} key={tune.tuneFile}>
<Card
title={tune.details!.model}
actions={[
<Badge count={0} showZero size="small" color="gold">
<StarOutlined />
</Badge>,
<Tooltip title={copied ? 'Copied!' : 'Copy URL'}>
<CopyOutlined onClick={() => copyToClipboard(generateShareUrl(tune.id!))} />
</Tooltip>,
<ArrowRightOutlined onClick={() => goToTune(tune.id!)} />,
]}
>
<Typography.Text ellipsis>
{tune.details!.make} {tune.details!.model} {tune.details!.year}
</Typography.Text>
</Card>
</Col>
)))}
</Row>
</div>
);
};
export default Hub;

View File

@ -53,9 +53,9 @@ import { Routes } from '../routes';
import TuneParser from '../utils/tune/TuneParser';
import TriggerLogsParser from '../utils/logs/TriggerLogsParser';
import LogParser from '../utils/logs/LogParser';
import { TuneDbData } from '../types/dbData';
import useDb from '../hooks/useDb';
import useServerStorage from '../hooks/useServerStorage';
import { generateShareUrl } from '../utils/url';
const { Item } = Form;
@ -116,7 +116,7 @@ const UploadPage = () => {
const { currentUser, refreshToken } = useAuth();
const history = useHistory();
const { removeFile, uploadFile, basePathForFile } = useServerStorage();
const { updateData, getTune } = useDb();
const { updateData } = useDb();
const requiredRules = [{ required: true, message: 'This field is required!' }];
const [readme, setReadme] = useState('# My Tune\n\ndescription');
@ -139,7 +139,8 @@ const UploadPage = () => {
const publish = async (values: any) => {
setIsLoading(true);
await updateData(newTuneId!, {
createdAt: new Date(),
id: newTuneId!,
userUid: currentUser!.uid,
updatedAt: new Date(),
isPublished: true,
isListed: values.isListed,
@ -225,29 +226,25 @@ const UploadPage = () => {
});
const uploadTune = async (options: UploadRequestOption) => {
const found = await getTune(newTuneId!);
if (!found) {
const tuneData: TuneDbData = {
setShareUrl(generateShareUrl(newTuneId!));
const { path } = (options.data as unknown as UploadFileData);
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
upload(path, options, () => {
// this is `create` for firebase
// initialize data
updateData(newTuneId!, {
id: newTuneId!,
userUid: currentUser!.uid,
createdAt: new Date(),
updatedAt: new Date(),
isPublished: false,
isListed: true,
tuneFile: null,
logFiles: [],
toothLogFiles: [],
customIniFile: null,
details: {},
};
await updateData(newTuneId!, tuneData);
}
setShareUrl(`${process.env.REACT_APP_WEB_URL}/#/t/${newTuneId}`);
const { path } = (options.data as unknown as UploadFileData);
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
upload(path, options, () => {
updateData(newTuneId!, { tuneFile: path });
tuneFile: path,
});
}, async (file) => {
const { result, message } = await validateSize(file);
if (!result) {
@ -432,7 +429,9 @@ const UploadPage = () => {
genericError(error as Error);
}
setNewTuneId(nanoidCustom());
const tuneId = nanoidCustom();
setNewTuneId(tuneId);
console.log('New tuneId:', tuneId);
}, [currentUser, history, refreshToken]);
useEffect(() => {

View File

@ -17,6 +17,7 @@ export interface TuneDataDetails {
}
export interface TuneDbData {
id?: string,
userUid?: string;
createdAt?: Date | Timestamp | string;
updatedAt?: Date | Timestamp | string;

2
src/utils/url.ts Normal file
View File

@ -0,0 +1,2 @@
// eslint-disable-next-line import/prefer-default-export
export const generateShareUrl = (tuneId: string) => `${process.env.REACT_APP_WEB_URL}/#/t/${tuneId}`;