diff --git a/web/.env b/web/.env
deleted file mode 100644
index 20a7ee3..0000000
--- a/web/.env
+++ /dev/null
@@ -1,3 +0,0 @@
-INLINE_RUNTIME_CHUNK=false
-GENERATE_SOURCEMAP=false
-SKIP_PREFLIGHT_CHECK=true
\ No newline at end of file
diff --git a/web/.env_example b/web/.env_example
new file mode 100644
index 0000000..166e2b4
--- /dev/null
+++ b/web/.env_example
@@ -0,0 +1 @@
+REACT_APP_SERVER_HOST=
\ No newline at end of file
diff --git a/web/.eslintcache b/web/.eslintcache
deleted file mode 100644
index c0f4296..0000000
--- a/web/.eslintcache
+++ /dev/null
@@ -1 +0,0 @@
-[{"/home/yourok/MEGAWork/go/TorrServer/web/src/index.js":"1","/home/yourok/MEGAWork/go/TorrServer/web/src/App.js":"2","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Appbar/index.js":"3","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Settings.js":"4","/home/yourok/MEGAWork/go/TorrServer/web/src/components/TorrentList.js":"5","/home/yourok/MEGAWork/go/TorrServer/web/src/components/RemoveAll.js":"6","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Donate/DonateDialog.js":"7","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Appbar/useStyles.js":"8","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Upload.js":"9","/home/yourok/MEGAWork/go/TorrServer/web/src/utils/Hosts.js":"10","/home/yourok/MEGAWork/go/TorrServer/web/src/components/About.js":"11","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Add/index.js":"12","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Donate/index.js":"13","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Torrent/index.js":"14","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Add/AddDialog.js":"15","/home/yourok/MEGAWork/go/TorrServer/web/src/components/Torrent/style.js":"16","/home/yourok/MEGAWork/go/TorrServer/web/src/components/DialogCacheInfo.js":"17","/home/yourok/MEGAWork/go/TorrServer/web/src/components/DialogTorrentInfo.js":"18","/home/yourok/MEGAWork/go/TorrServer/web/src/utils/Utils.js":"19","/home/yourok/MEGAWork/go/TorrServer/web/src/icons/index.js":"20"},{"size":224,"mtime":1607328766846,"results":"21","hashOfConfig":"22"},{"size":753,"mtime":1621921809285,"results":"23","hashOfConfig":"22"},{"size":5537,"mtime":1621922466004,"results":"24","hashOfConfig":"22"},{"size":9039,"mtime":1621236854096,"results":"25","hashOfConfig":"22"},{"size":1618,"mtime":1621921809293,"results":"26","hashOfConfig":"22"},{"size":1325,"mtime":1621921809293,"results":"27","hashOfConfig":"22"},{"size":1919,"mtime":1621922466008,"results":"28","hashOfConfig":"22"},{"size":1728,"mtime":1621922466008,"results":"29","hashOfConfig":"22"},{"size":1348,"mtime":1607335733737,"results":"30","hashOfConfig":"22"},{"size":796,"mtime":1614236974272,"results":"31","hashOfConfig":"22"},{"size":2278,"mtime":1608810011199,"results":"32","hashOfConfig":"22"},{"size":880,"mtime":1621921809285,"results":"33","hashOfConfig":"22"},{"size":1700,"mtime":1621922466016,"results":"34","hashOfConfig":"22"},{"size":6309,"mtime":1621921809293,"results":"35","hashOfConfig":"22"},{"size":2440,"mtime":1621921809285,"results":"36","hashOfConfig":"22"},{"size":2167,"mtime":1621922466020,"results":"37","hashOfConfig":"22"},{"size":4995,"mtime":1621921809289,"results":"38","hashOfConfig":"22"},{"size":7486,"mtime":1617970930041,"results":"39","hashOfConfig":"22"},{"size":419,"mtime":1614236662474,"results":"40","hashOfConfig":"22"},{"size":2468,"mtime":1621921809293,"results":"41","hashOfConfig":"22"},{"filePath":"42","messages":"43","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},"1btbc6o",{"filePath":"45","messages":"46","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"47","messages":"48","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"49","messages":"50","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"51","usedDeprecatedRules":"44"},{"filePath":"52","messages":"53","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"54","messages":"55","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"56","messages":"57","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"58","messages":"59","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"60","messages":"61","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"62","messages":"63","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"64","messages":"65","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"66","messages":"67","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"68","messages":"69","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"70","messages":"71","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"72","messages":"73","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"74","messages":"75","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"76","messages":"77","errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"78","usedDeprecatedRules":"44"},{"filePath":"79","messages":"80","errorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":"81"},{"filePath":"82","messages":"83","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"84","messages":"85","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/home/yourok/MEGAWork/go/TorrServer/web/src/index.js",[],["86","87"],"/home/yourok/MEGAWork/go/TorrServer/web/src/App.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Appbar/index.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Settings.js",["88"],"import ListItem from '@material-ui/core/ListItem'\nimport ListItemIcon from '@material-ui/core/ListItemIcon'\nimport ListItemText from '@material-ui/core/ListItemText'\nimport React, { useEffect } from 'react'\nimport SettingsIcon from '@material-ui/icons/Settings'\nimport Dialog from '@material-ui/core/Dialog'\nimport DialogTitle from '@material-ui/core/DialogTitle'\nimport DialogContent from '@material-ui/core/DialogContent'\nimport TextField from '@material-ui/core/TextField'\nimport DialogActions from '@material-ui/core/DialogActions'\nimport Button from '@material-ui/core/Button'\nimport { FormControlLabel, InputLabel, Select, Switch } from '@material-ui/core'\nimport { settingsHost, setTorrServerHost, torrserverHost } from '../utils/Hosts'\n\nexport default function SettingsDialog() {\n const [open, setOpen] = React.useState(false)\n const [settings, setSets] = React.useState({})\n const [show, setShow] = React.useState(false)\n const [tsHost, setTSHost] = React.useState(torrserverHost ? torrserverHost : window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : ''))\n\n const handleClickOpen = () => {\n setOpen(true)\n }\n const handleClose = () => {\n setOpen(false)\n }\n const handleCloseSave = () => {\n setOpen(false)\n let sets = JSON.parse(JSON.stringify(settings))\n sets.CacheSize *= 1024 * 1024\n sets.PreloadBufferSize *= 1024 * 1024\n fetch(settingsHost(), {\n method: 'post',\n body: JSON.stringify({ action: 'set', sets: sets }),\n headers: {\n Accept: 'application/json, text/plain, */*',\n 'Content-Type': 'application/json',\n },\n })\n }\n\n useEffect(() => {\n fetch(settingsHost(), {\n method: 'post',\n body: JSON.stringify({ action: 'get' }),\n headers: {\n Accept: 'application/json, text/plain, */*',\n 'Content-Type': 'application/json',\n },\n })\n .then((res) => res.json())\n .then(\n (json) => {\n json.CacheSize /= 1024 * 1024\n json.PreloadBufferSize /= 1024 * 1024\n setSets(json)\n setShow(true)\n },\n (error) => {\n setShow(false)\n console.log(error)\n }\n )\n .catch((e) => {\n setShow(false)\n console.log(e)\n })\n }, [tsHost])\n\n const onInputHost = (event) => {\n let host = event.target.value\n setTorrServerHost(host)\n setTSHost(host)\n }\n\n const inputForm = (event) => {\n let sets = JSON.parse(JSON.stringify(settings))\n if (event.target.type === 'number' || event.target.type === 'select-one') {\n sets[event.target.id] = Number(event.target.value)\n } else if (event.target.type === 'checkbox') {\n sets[event.target.id] = Boolean(event.target.checked)\n } else if (event.target.type === 'url') {\n sets[event.target.id] = event.target.value\n }\n setSets(sets)\n }\n\n return (\n
\n \n \n \n \n \n \n \n
\n )\n}\n\n","/home/yourok/MEGAWork/go/TorrServer/web/src/components/TorrentList.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/RemoveAll.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Donate/DonateDialog.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Appbar/useStyles.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Upload.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/utils/Hosts.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/About.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Add/index.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Donate/index.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Torrent/index.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Add/AddDialog.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/Torrent/style.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/components/DialogCacheInfo.js",["89","90"],"import React, { useEffect, useRef } from 'react'\nimport Typography from '@material-ui/core/Typography'\n\nimport { getPeerString, humanizeSize } from '../utils/Utils'\nimport DialogTitle from '@material-ui/core/DialogTitle'\nimport DialogContent from '@material-ui/core/DialogContent'\nimport { cacheHost } from '../utils/Hosts'\n\nexport default function DialogCacheInfo(props) {\n const [hash] = React.useState(props.hash)\n const [cache, setCache] = React.useState({})\n const timerID = useRef(-1)\n const [pMap, setPMap] = React.useState([])\n\n useEffect(() => {\n if (hash)\n timerID.current = setInterval(() => {\n getCache(hash, (cache) => {\n setCache(cache)\n })\n }, 100)\n else clearInterval(timerID.current)\n\n return () => {\n clearInterval(timerID.current)\n }\n }, [hash, props.open])\n\n useEffect(() => {\n if (cache && cache.PiecesCount && cache.Pieces) {\n var map = [];\n for (let i = 0; i < cache.PiecesCount; i++) {\n var reader = 0\n var cls = \"piece\"\n var prc = 0\n if (cache.Pieces[i]) {\n if (cache.Pieces[i].Completed && cache.Pieces[i].Size >= cache.Pieces[i].Length)\n cls += \" piece-complete\"\n else\n cls += \" piece-loading\"\n prc = (cache.Pieces[i].Size / cache.Pieces[i].Length * 100).toFixed(2)\n }\n\n cache.Readers.forEach(r => {\n if (i >= r.Start && i <= r.End && i !== r.Reader)\n cls += \" reader-range\"\n if (i === r.Reader) {\n cls += \" piece-reader\"\n }\n })\n map.push({\n prc: prc,\n class: cls,\n info: i,\n reader: reader,\n })\n }\n setPMap(map)\n }\n }, [cache.Pieces])\n\n return (\n \n
\n \n Hash {cache.Hash}\n
\n Capacity {humanizeSize(cache.Capacity)}\n
\n Filled {humanizeSize(cache.Filled)}\n
\n Torrent size {cache.Torrent && cache.Torrent.torrent_size && humanizeSize(cache.Torrent.torrent_size)}\n
\n Pieces length {humanizeSize(cache.PiecesLength)}\n
\n Pieces count {cache.PiecesCount}\n
\n Peers: {getPeerString(cache.Torrent)}\n
\n Download speed {cache.Torrent && cache.Torrent.download_speed ? humanizeSize(cache.Torrent.download_speed) + '/sec' : ''}\n
\n Upload speed {cache.Torrent && cache.Torrent.upload_speed ? humanizeSize(cache.Torrent.upload_speed) + '/sec' : ''}\n
\n Status {cache.Torrent && cache.Torrent.stat_string && cache.Torrent.stat_string}\n \n \n \n
\n \n {pMap.map(itm => (\n
\n {itm.prc > 0 && itm.prc < 100 && (\n \n )}\n \n ))}\n
\n \n
\n )\n}\n\nfunction getCache(hash, callback) {\n try {\n fetch(cacheHost(), {\n method: 'post',\n body: JSON.stringify({ action: 'get', hash: hash }),\n headers: {\n Accept: 'application/json, text/plain, */*',\n 'Content-Type': 'application/json',\n },\n })\n .then((res) => res.json())\n .then(\n (json) => {\n callback(json)\n },\n (error) => {\n callback({})\n console.error(error)\n }\n )\n } catch (e) {\n console.error(e)\n callback({})\n }\n}\n/*\n{\n \"Hash\": \"41e36c8de915d80db83fc134bee4e7e2d292657e\",\n \"Capacity\": 209715200,\n \"Filled\": 2914808,\n \"PiecesLength\": 4194304,\n \"PiecesCount\": 2065,\n \"DownloadSpeed\": 32770.860273455524,\n \"Pieces\": {\n \"2064\": {\n \"Id\": 2064,\n \"Length\": 2914808,\n \"Size\": 162296,\n \"Completed\": false\n }\n }\n}\n */\n","/home/yourok/MEGAWork/go/TorrServer/web/src/components/DialogTorrentInfo.js",["91","92","93","94","95"],"import React, { useEffect } from 'react'\nimport Typography from '@material-ui/core/Typography'\nimport { Button, ButtonGroup, Grid, List, ListItem } from '@material-ui/core'\nimport CachedIcon from '@material-ui/icons/Cached'\nimport LinearProgress from '@material-ui/core/LinearProgress';\n\nimport { getPeerString, humanizeSize } from '../utils/Utils'\nimport { playlistTorrHost, streamHost, viewedHost } from '../utils/Hosts'\nimport DialogTitle from '@material-ui/core/DialogTitle'\nimport DialogContent from '@material-ui/core/DialogContent'\n\nconst style = {\n width100: {\n width: '100%',\n },\n width80: {\n width: '80%',\n },\n poster: {\n display: 'flex',\n flexDirection: 'row',\n borderRadius: '5px',\n },\n}\n\nexport default function DialogTorrentInfo(props) {\n const [torrent, setTorrent] = React.useState(props.torrent)\n const [viewed, setViewed] = React.useState(null)\n const [progress, setProgress] = React.useState(-1)\n\n useEffect(() => {\n setTorrent(props.torrent)\n if(torrent.stat==2)\n setProgress(torrent.preloaded_bytes * 100 / torrent.preload_size)\n getViewed(props.torrent.hash,(list) => {\n if (list) {\n let lst = list.map((itm) => itm.file_index)\n setViewed(lst)\n }else\n setViewed(null)\n })\n }, [props.torrent, props.open])\n\n return (\n \n
\n \n {torrent.poster &&
}\n \n {torrent.title} {torrent.name && torrent.name !== torrent.title && ' | ' + torrent.name}\n \n Peers: {getPeerString(torrent)}\n
\n Loaded: {getPreload(torrent)}\n
\n Speed: {humanizeSize(torrent.download_speed)}\n
\n Status: {torrent.stat_string}\n
\n \n \n \n {torrent.stat==2 && }\n \n
\n \n \n \n \n \n \n \n \n {getPlayableFile(torrent) &&\n getPlayableFile(torrent).map((file) => (\n \n \n \n \n ))}\n
\n \n
\n )\n}\n\nfunction remViews(hash){\n try {\n if (hash)\n fetch(viewedHost(), {\n method: 'post',\n body: JSON.stringify({ action: 'rem', hash: hash, file_index:-1 }),\n headers: {\n Accept: 'application/json, text/plain, */*',\n 'Content-Type': 'application/json',\n },\n })\n } catch (e) {\n console.error(e)\n }\n}\n\nfunction getViewed(hash, callback) {\n try {\n fetch(viewedHost(), {\n method: 'post',\n body: JSON.stringify({ action: 'list', hash: hash }),\n headers: {\n Accept: 'application/json, text/plain, */*',\n 'Content-Type': 'application/json',\n },\n })\n .then((res) => res.json())\n .then(\n (json) => {\n callback(json)\n },\n (error) => {\n callback(null)\n }\n )\n } catch (e) {\n console.error(e)\n }\n}\n\nfunction getPlayableFile(torrent){\n if (!torrent || !torrent.file_stats)\n return null\n return torrent.file_stats.filter(file => extPlayable.includes(getExt(file.path)))\n}\n\nfunction getExt(filename){\n const ext = filename.split('.').pop()\n if (ext == filename)\n return ''\n return ext.toLowerCase()\n}\n\nfunction getPreload(torrent) {\n if (torrent.preloaded_bytes > 0 && torrent.preload_size > 0 && torrent.preloaded_bytes < torrent.preload_size) {\n let progress = ((torrent.preloaded_bytes * 100) / torrent.preload_size).toFixed(2)\n return humanizeSize(torrent.preloaded_bytes) + ' / ' + humanizeSize(torrent.preload_size) + ' ' + progress + '%'\n }\n\n if (!torrent.preloaded_bytes) return humanizeSize(0)\n\n return humanizeSize(torrent.preloaded_bytes)\n}\n\nconst extPlayable = [\n// video\n \"3g2\",\n \"3gp\",\n \"aaf\",\n \"asf\",\n \"avchd\",\n \"avi\",\n \"drc\",\n \"flv\",\n \"iso\",\n \"m2v\",\n \"m2ts\",\n \"m4p\",\n \"m4v\",\n \"mkv\",\n \"mng\",\n \"mov\",\n \"mp2\",\n \"mp4\",\n \"mpe\",\n \"mpeg\",\n \"mpg\",\n \"mpv\",\n \"mxf\",\n \"nsv\",\n \"ogg\",\n \"ogv\",\n \"ts\",\n \"qt\",\n \"rm\",\n \"rmvb\",\n \"roq\",\n \"svi\",\n \"vob\",\n \"webm\",\n \"wmv\",\n \"yuv\",\n// audio\n \"aac\",\n \"aiff\",\n \"ape\",\n \"au\",\n \"flac\",\n \"gsm\",\n \"it\",\n \"m3u\",\n \"m4a\",\n \"mid\",\n \"mod\",\n \"mp3\",\n \"mpa\",\n \"pls\",\n \"ra\",\n \"s3m\",\n \"sid\",\n \"wav\",\n \"wma\",\n \"xm\"\n]\n","/home/yourok/MEGAWork/go/TorrServer/web/src/utils/Utils.js",[],"/home/yourok/MEGAWork/go/TorrServer/web/src/icons/index.js",[],{"ruleId":"96","replacedBy":"97"},{"ruleId":"98","replacedBy":"99"},{"ruleId":"100","severity":1,"message":"101","line":105,"column":29,"nodeType":"102","endLine":105,"endColumn":35},{"ruleId":"103","severity":1,"message":"104","line":44,"column":39,"nodeType":"105","messageId":"106","endLine":50,"endColumn":18},{"ruleId":"107","severity":1,"message":"108","line":60,"column":8,"nodeType":"109","endLine":60,"endColumn":22,"suggestions":"110"},{"ruleId":"111","severity":1,"message":"112","line":33,"column":24,"nodeType":"113","messageId":"114","endLine":33,"endColumn":26},{"ruleId":"107","severity":1,"message":"115","line":42,"column":8,"nodeType":"109","endLine":42,"endColumn":35,"suggestions":"116"},{"ruleId":"111","severity":1,"message":"112","line":63,"column":30,"nodeType":"113","messageId":"114","endLine":63,"endColumn":32},{"ruleId":"111","severity":1,"message":"117","line":91,"column":152,"nodeType":"113","messageId":"114","endLine":91,"endColumn":154},{"ruleId":"111","severity":1,"message":"112","line":154,"column":13,"nodeType":"113","messageId":"114","endLine":154,"endColumn":15},"no-native-reassign",["118"],"no-negated-in-lhs",["119"],"jsx-a11y/heading-has-content","Headings must have content and the content must be accessible by a screen reader.","JSXOpeningElement","no-loop-func","Function declared in a loop contains unsafe references to variable(s) 'cls', 'cls'.","ArrowFunctionExpression","unsafeRefs","react-hooks/exhaustive-deps","React Hook useEffect has a missing dependency: 'cache'. Either include it or remove the dependency array.","ArrayExpression",["120"],"eqeqeq","Expected '===' and instead saw '=='.","BinaryExpression","unexpected","React Hook useEffect has missing dependencies: 'torrent.preload_size', 'torrent.preloaded_bytes', and 'torrent.stat'. Either include them or remove the dependency array. You can also replace multiple useState variables with useReducer if 'setProgress' needs the current value of 'torrent.preloaded_bytes'.",["121"],"Expected '!==' and instead saw '!='.","no-global-assign","no-unsafe-negation",{"desc":"122","fix":"123"},{"desc":"124","fix":"125"},"Update the dependencies array to be: [cache, cache.Pieces]",{"range":"126","text":"127"},"Update the dependencies array to be: [props.torrent, props.open, torrent.stat, torrent.preloaded_bytes, torrent.preload_size]",{"range":"128","text":"129"},[2056,2070],"[cache, cache.Pieces]",[1372,1399],"[props.torrent, props.open, torrent.stat, torrent.preloaded_bytes, torrent.preload_size]"]
\ No newline at end of file
diff --git a/web/.eslintrc b/web/.eslintrc
new file mode 100644
index 0000000..929c132
--- /dev/null
+++ b/web/.eslintrc
@@ -0,0 +1,41 @@
+{
+ "plugins": [ "prettier" ],
+ "extends": [ "airbnb", "react-app", "react-app/jest", "prettier" ],
+ "rules": {
+ "prettier/prettier": ["warn", {
+ "trailingComma": "all",
+ "singleQuote": true,
+ "jsxSingleQuote": true,
+ "printWidth": 120,
+ "arrowParens": "avoid", // Allow single argument without parentheses in arrow functions
+ "semi": false
+ }],
+ "import/no-anonymous-default-export": 0, // Allow "export default"
+ "import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.test.js", "**/*.spec.js"]}],
+ "react/jsx-one-expression-per-line": 0,
+ "import/order": ["warn", {
+ "groups": [
+ "external", // node_modules
+ "internal", // src folder
+ ["parent", "sibling"]
+ ],
+ "newlines-between": "always" // Separate all groups with new line
+ }],
+ "no-plusplus": 0,
+ "consistent-return": 0, // returning value is not required in arrow functions
+ "no-nested-ternary": 0,
+ "react/require-default-props": 0,
+ "indent": 0,
+ "comma-dangle": 0,
+ "no-shadow": 0, // Allow using same variable name in outer and function scopes
+ "no-unused-vars": ["warn", {
+ "vars": "local",
+ "args": "after-used",
+ "ignoreRestSiblings": true
+ }],
+ "react/prop-types": 0,
+ "react/react-in-jsx-scope": 0,
+ "react/jsx-uses-react": 0,
+ "import/no-unresolved": 0 // used to allow relative paths from "src" folder
+ }
+}
\ No newline at end of file
diff --git a/web/.gitignore b/web/.gitignore
index 0c2df13..687b948 100644
--- a/web/.gitignore
+++ b/web/.gitignore
@@ -24,3 +24,5 @@ yarn-error.log*
# eslint
.eslintcache
+
+.env
diff --git a/web/.prettierrc b/web/.prettierrc
deleted file mode 100644
index e929b0c..0000000
--- a/web/.prettierrc
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "useTabs": false,
- "printWidth": 200,
- "tabWidth": 4,
- "singleQuote": true,
- "trailingComma": "es5",
- "jsxBracketSameLine": false,
- "parser": "flow",
- "semi": false
-}
\ No newline at end of file
diff --git a/web/README.md b/web/README.md
index e4d09b7..faed973 100644
--- a/web/README.md
+++ b/web/README.md
@@ -1 +1,17 @@
-## TorrServer web client
+# TorrServer web client
+
+### How to start project
+
+0. ignore first two steps if the server is on `localhost`
+1. duplicate `.env_example` and rename it to `.env`
+2. in `.env` file add server address to `REACT_APP_SERVER_HOST` (without last "/")
+> `http://192.168.78.4:8090` - correct
+>
+> `http://192.168.78.4:8090/` - wrong
+3. `npm start`
+
+### Eslint
+> Prettier will fix the code every time the code is saved
+
+- `npm run lint` - to find all linting problems
+- `npm run fix` - to fix code
\ No newline at end of file
diff --git a/web/jsconfig.json b/web/jsconfig.json
new file mode 100644
index 0000000..ec9aa3f
--- /dev/null
+++ b/web/jsconfig.json
@@ -0,0 +1,6 @@
+{
+ "compilerOptions": {
+ "baseUrl": "src"
+ },
+ "include": ["src"]
+}
\ No newline at end of file
diff --git a/web/package-lock.json b/web/package-lock.json
index fbe3f90..49e763f 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -11,6 +11,7 @@
"dependencies": {
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.11.2",
+ "clsx": "^1.1.1",
"fontsource-roboto": "^4.0.0",
"material-ui-image": "^3.3.2",
"react": "^17.0.2",
@@ -26,6 +27,9 @@
"babel-minify": "^0.5.1",
"babel-preset-minify": "^0.5.1",
"eslint": "^7.27.0",
+ "eslint-config-airbnb": "^18.2.1",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-plugin-prettier": "^3.4.0",
"gulp": "^4.0.2",
"gulp-inline-source": "^4.0.0",
"gulp-replace": "^1.1.3",
@@ -3668,18 +3672,21 @@
"integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ=="
},
"node_modules/array-includes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz",
- "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
+ "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
"dependencies": {
- "call-bind": "^1.0.0",
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.18.0-next.1",
- "get-intrinsic": "^1.0.1",
+ "es-abstract": "^1.18.0-next.2",
+ "get-intrinsic": "^1.1.1",
"is-string": "^1.0.5"
},
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/array-initial": {
@@ -5868,14 +5875,6 @@
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
"integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
},
- "node_modules/contains-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
- "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
@@ -7271,25 +7270,32 @@
}
},
"node_modules/es-abstract": {
- "version": "1.18.0-next.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
- "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz",
+ "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==",
"dependencies": {
+ "call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
+ "get-intrinsic": "^1.1.1",
"has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.2",
- "is-negative-zero": "^2.0.0",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
+ "has-symbols": "^1.0.2",
+ "is-callable": "^1.2.3",
+ "is-negative-zero": "^2.0.1",
+ "is-regex": "^1.1.2",
+ "is-string": "^1.0.5",
+ "object-inspect": "^1.9.0",
"object-keys": "^1.1.1",
- "object.assign": "^4.1.1",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
+ "object.assign": "^4.1.2",
+ "string.prototype.trimend": "^1.0.4",
+ "string.prototype.trimstart": "^1.0.4",
+ "unbox-primitive": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/es-to-primitive": {
@@ -7499,6 +7505,57 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/eslint-config-airbnb": {
+ "version": "18.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz",
+ "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-config-airbnb-base": "^14.2.1",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0",
+ "eslint-plugin-import": "^2.22.1",
+ "eslint-plugin-jsx-a11y": "^6.4.1",
+ "eslint-plugin-react": "^7.21.5",
+ "eslint-plugin-react-hooks": "^4 || ^3 || ^2.3.0 || ^1.7.0"
+ }
+ },
+ "node_modules/eslint-config-airbnb-base": {
+ "version": "14.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz",
+ "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==",
+ "dev": true,
+ "dependencies": {
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0",
+ "eslint-plugin-import": "^2.22.1"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz",
+ "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
"node_modules/eslint-config-react-app": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
@@ -7533,11 +7590,11 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/eslint-module-utils": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
- "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz",
+ "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==",
"dependencies": {
- "debug": "^2.6.9",
+ "debug": "^3.2.7",
"pkg-dir": "^2.0.0"
},
"engines": {
@@ -7545,11 +7602,11 @@
}
},
"node_modules/eslint-module-utils/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dependencies": {
- "ms": "2.0.0"
+ "ms": "^2.1.1"
}
},
"node_modules/eslint-module-utils/node_modules/find-up": {
@@ -7575,11 +7632,6 @@
"node": ">=4"
}
},
- "node_modules/eslint-module-utils/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
- },
"node_modules/eslint-module-utils/node_modules/p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -7642,26 +7694,31 @@
}
},
"node_modules/eslint-plugin-import": {
- "version": "2.22.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
- "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
+ "version": "2.23.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.3.tgz",
+ "integrity": "sha512-wDxdYbSB55F7T5CC7ucDjY641VvKmlRwT0Vxh7PkY1mI4rclVRFWYfsrjDgZvwYYDZ5ee0ZtfFKXowWjqvEoRQ==",
"dependencies": {
- "array-includes": "^3.1.1",
- "array.prototype.flat": "^1.2.3",
- "contains-path": "^0.1.0",
+ "array-includes": "^3.1.3",
+ "array.prototype.flat": "^1.2.4",
"debug": "^2.6.9",
- "doctrine": "1.5.0",
+ "doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.4",
- "eslint-module-utils": "^2.6.0",
+ "eslint-module-utils": "^2.6.1",
+ "find-up": "^2.0.0",
"has": "^1.0.3",
+ "is-core-module": "^2.4.0",
"minimatch": "^3.0.4",
- "object.values": "^1.1.1",
- "read-pkg-up": "^2.0.0",
- "resolve": "^1.17.0",
+ "object.values": "^1.1.3",
+ "pkg-up": "^2.0.0",
+ "read-pkg-up": "^3.0.0",
+ "resolve": "^1.20.0",
"tsconfig-paths": "^3.9.0"
},
"engines": {
"node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0"
}
},
"node_modules/eslint-plugin-import/node_modules/debug": {
@@ -7673,22 +7730,105 @@
}
},
"node_modules/eslint-plugin-import/node_modules/doctrine": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
- "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dependencies": {
- "esutils": "^2.0.2",
- "isarray": "^1.0.0"
+ "esutils": "^2.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/eslint-plugin-import/node_modules/find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dependencies": {
+ "locate-path": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dependencies": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/eslint-plugin-import/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
+ "node_modules/eslint-plugin-import/node_modules/p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dependencies": {
+ "p-try": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dependencies": {
+ "p-limit": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+ "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "dependencies": {
+ "find-up": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dependencies": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/eslint-plugin-jest": {
"version": "24.1.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.1.3.tgz",
@@ -7726,6 +7866,27 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.0.tgz",
"integrity": "sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug=="
},
+ "node_modules/eslint-plugin-prettier": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz",
+ "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=5.0.0",
+ "prettier": ">=1.13.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
"node_modules/eslint-plugin-react": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz",
@@ -8554,6 +8715,12 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
+ "node_modules/fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
"node_modules/fast-glob": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
@@ -10254,6 +10421,14 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/has-bigints": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+ "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -10263,11 +10438,14 @@
}
},
"node_modules/has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-value": {
@@ -11101,27 +11279,6 @@
"node": ">= 0.4"
}
},
- "node_modules/internal-slot/node_modules/es-abstract": {
- "version": "1.17.7",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
- "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
- "dependencies": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.2",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.1",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/interpret": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
@@ -11223,6 +11380,14 @@
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
+ "node_modules/is-bigint": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
+ "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -11235,17 +11400,34 @@
"node": ">=8"
}
},
+ "node_modules/is-boolean-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
+ "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==",
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"node_modules/is-callable": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
- "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
+ "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-ci": {
@@ -11273,11 +11455,14 @@
}
},
"node_modules/is-core-module": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
- "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
+ "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
"dependencies": {
"has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-data-descriptor": {
@@ -11413,11 +11598,14 @@
}
},
"node_modules/is-negative-zero": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
- "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-number": {
@@ -11428,6 +11616,17 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-number-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz",
+ "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
@@ -11491,14 +11690,18 @@
"integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c="
},
"node_modules/is-regex": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
- "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz",
+ "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==",
"dependencies": {
- "has-symbols": "^1.0.1"
+ "call-bind": "^1.0.2",
+ "has-symbols": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-regexp": {
@@ -14045,13 +14248,13 @@
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
},
"node_modules/load-json-file": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
- "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"dependencies": {
"graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
"strip-bom": "^3.0.0"
},
"engines": {
@@ -14059,22 +14262,23 @@
}
},
"node_modules/load-json-file/node_modules/parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dependencies": {
- "error-ex": "^1.2.0"
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
},
"engines": {
- "node": ">=0.10.0"
+ "node": ">=4"
}
},
"node_modules/load-json-file/node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"engines": {
- "node": ">=0.10.0"
+ "node": ">=4"
}
},
"node_modules/loader-runner": {
@@ -14110,9 +14314,9 @@
}
},
"node_modules/lodash": {
- "version": "4.17.20",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
- "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash._reinterpolate": {
"version": "3.0.0",
@@ -15336,17 +15540,20 @@
}
},
"node_modules/object.values": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz",
- "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz",
+ "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==",
"dependencies": {
- "call-bind": "^1.0.0",
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.18.0-next.1",
+ "es-abstract": "^1.18.0-next.2",
"has": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/obuf": {
@@ -17277,6 +17484,18 @@
"node": ">=10.13.0"
}
},
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/pretty-bytes": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz",
@@ -17970,25 +18189,25 @@
}
},
"node_modules/read-pkg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
- "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
"dependencies": {
- "load-json-file": "^2.0.0",
+ "load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
- "path-type": "^2.0.0"
+ "path-type": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/read-pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
- "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
+ "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
"dependencies": {
"find-up": "^2.0.0",
- "read-pkg": "^2.0.0"
+ "read-pkg": "^3.0.0"
},
"engines": {
"node": ">=4"
@@ -18056,22 +18275,22 @@
}
},
"node_modules/read-pkg/node_modules/path-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
- "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dependencies": {
- "pify": "^2.0.0"
+ "pify": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/read-pkg/node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"engines": {
- "node": ">=0.10.0"
+ "node": ">=4"
}
},
"node_modules/readable-stream": {
@@ -18180,27 +18399,6 @@
"node": ">= 0.4"
}
},
- "node_modules/regexp.prototype.flags/node_modules/es-abstract": {
- "version": "1.17.7",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
- "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
- "dependencies": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.2",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.1",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/regexpp": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
@@ -20070,21 +20268,27 @@
}
},
"node_modules/string.prototype.trimend": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz",
- "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+ "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"dependencies": {
- "call-bind": "^1.0.0",
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/string.prototype.trimstart": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz",
- "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+ "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"dependencies": {
- "call-bind": "^1.0.0",
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/stringify-object": {
@@ -20998,6 +21202,20 @@
"is-typedarray": "^1.0.0"
}
},
+ "node_modules/unbox-primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+ "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has-bigints": "^1.0.1",
+ "has-symbols": "^1.0.2",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
@@ -21308,27 +21526,6 @@
"object.getownpropertydescriptors": "^2.1.0"
}
},
- "node_modules/util.promisify/node_modules/es-abstract": {
- "version": "1.17.7",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
- "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
- "dependencies": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.2",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.1",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/util/node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
@@ -22933,6 +23130,21 @@
"which": "bin/which"
}
},
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@@ -26428,14 +26640,14 @@
"integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ=="
},
"array-includes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz",
- "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
+ "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
"requires": {
- "call-bind": "^1.0.0",
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.18.0-next.1",
- "get-intrinsic": "^1.0.1",
+ "es-abstract": "^1.18.0-next.2",
+ "get-intrinsic": "^1.1.1",
"is-string": "^1.0.5"
}
},
@@ -28342,11 +28554,6 @@
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
"integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
},
- "contains-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
- "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo="
- },
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
@@ -29526,22 +29733,26 @@
}
},
"es-abstract": {
- "version": "1.18.0-next.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
- "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz",
+ "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==",
"requires": {
+ "call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
+ "get-intrinsic": "^1.1.1",
"has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.2",
- "is-negative-zero": "^2.0.0",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
+ "has-symbols": "^1.0.2",
+ "is-callable": "^1.2.3",
+ "is-negative-zero": "^2.0.1",
+ "is-regex": "^1.1.2",
+ "is-string": "^1.0.5",
+ "object-inspect": "^1.9.0",
"object-keys": "^1.1.1",
- "object.assign": "^4.1.1",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
+ "object.assign": "^4.1.2",
+ "string.prototype.trimend": "^1.0.4",
+ "string.prototype.trimstart": "^1.0.4",
+ "unbox-primitive": "^1.0.0"
}
},
"es-to-primitive": {
@@ -29815,6 +30026,35 @@
}
}
},
+ "eslint-config-airbnb": {
+ "version": "18.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz",
+ "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==",
+ "dev": true,
+ "requires": {
+ "eslint-config-airbnb-base": "^14.2.1",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.2"
+ }
+ },
+ "eslint-config-airbnb-base": {
+ "version": "14.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz",
+ "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==",
+ "dev": true,
+ "requires": {
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.2"
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz",
+ "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==",
+ "dev": true,
+ "requires": {}
+ },
"eslint-config-react-app": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
@@ -29848,13 +30088,105 @@
}
},
"eslint-module-utils": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
- "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz",
+ "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==",
"requires": {
- "debug": "^2.6.9",
+ "debug": "^3.2.7",
"pkg-dir": "^2.0.0"
},
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+ },
+ "pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ }
+ }
+ },
+ "eslint-plugin-flowtype": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.2.0.tgz",
+ "integrity": "sha512-z7ULdTxuhlRJcEe1MVljePXricuPOrsWfScRXFhNzVD5dmTHWjIF57AxD0e7AbEoLSbjSsaA5S+hCg43WvpXJQ==",
+ "requires": {
+ "lodash": "^4.17.15",
+ "string-natural-compare": "^3.0.1"
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.23.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.3.tgz",
+ "integrity": "sha512-wDxdYbSB55F7T5CC7ucDjY641VvKmlRwT0Vxh7PkY1mI4rclVRFWYfsrjDgZvwYYDZ5ee0ZtfFKXowWjqvEoRQ==",
+ "requires": {
+ "array-includes": "^3.1.3",
+ "array.prototype.flat": "^1.2.4",
+ "debug": "^2.6.9",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.4",
+ "eslint-module-utils": "^2.6.1",
+ "find-up": "^2.0.0",
+ "has": "^1.0.3",
+ "is-core-module": "^2.4.0",
+ "minimatch": "^3.0.4",
+ "object.values": "^1.1.3",
+ "pkg-up": "^2.0.0",
+ "read-pkg-up": "^3.0.0",
+ "resolve": "^1.20.0",
+ "tsconfig-paths": "^3.9.0"
+ },
"dependencies": {
"debug": {
"version": "2.6.9",
@@ -29864,6 +30196,14 @@
"ms": "2.0.0"
}
},
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@@ -29912,66 +30252,22 @@
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
- "pkg-dir": {
+ "pkg-up": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
- "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+ "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
"requires": {
"find-up": "^2.1.0"
}
- }
- }
- },
- "eslint-plugin-flowtype": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.2.0.tgz",
- "integrity": "sha512-z7ULdTxuhlRJcEe1MVljePXricuPOrsWfScRXFhNzVD5dmTHWjIF57AxD0e7AbEoLSbjSsaA5S+hCg43WvpXJQ==",
- "requires": {
- "lodash": "^4.17.15",
- "string-natural-compare": "^3.0.1"
- }
- },
- "eslint-plugin-import": {
- "version": "2.22.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
- "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
- "requires": {
- "array-includes": "^3.1.1",
- "array.prototype.flat": "^1.2.3",
- "contains-path": "^0.1.0",
- "debug": "^2.6.9",
- "doctrine": "1.5.0",
- "eslint-import-resolver-node": "^0.3.4",
- "eslint-module-utils": "^2.6.0",
- "has": "^1.0.3",
- "minimatch": "^3.0.4",
- "object.values": "^1.1.1",
- "read-pkg-up": "^2.0.0",
- "resolve": "^1.17.0",
- "tsconfig-paths": "^3.9.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
},
- "doctrine": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
- "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"requires": {
- "esutils": "^2.0.2",
- "isarray": "^1.0.0"
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
}
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
@@ -30008,6 +30304,15 @@
}
}
},
+ "eslint-plugin-prettier": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz",
+ "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0"
+ }
+ },
"eslint-plugin-react": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz",
@@ -30545,6 +30850,12 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
+ "fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
"fast-glob": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
@@ -31958,15 +32269,20 @@
"function-bind": "^1.1.1"
}
},
+ "has-bigints": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+ "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
+ },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
},
"has-value": {
"version": "1.0.0",
@@ -32665,26 +32981,6 @@
"es-abstract": "^1.17.0-next.1",
"has": "^1.0.3",
"side-channel": "^1.0.2"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.7",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
- "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.2",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.1",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
}
},
"interpret": {
@@ -32760,6 +33056,11 @@
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
+ "is-bigint": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
+ "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA=="
+ },
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -32769,15 +33070,23 @@
"binary-extensions": "^2.0.0"
}
},
+ "is-boolean-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
+ "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==",
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"is-callable": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
- "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
+ "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ=="
},
"is-ci": {
"version": "2.0.0",
@@ -32801,9 +33110,9 @@
}
},
"is-core-module": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
- "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
+ "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
"requires": {
"has": "^1.0.3"
}
@@ -32903,15 +33212,20 @@
"dev": true
},
"is-negative-zero": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
- "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w=="
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
+ "is-number-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz",
+ "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw=="
+ },
"is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
@@ -32957,11 +33271,12 @@
"integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c="
},
"is-regex": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
- "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz",
+ "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==",
"requires": {
- "has-symbols": "^1.0.1"
+ "call-bind": "^1.0.2",
+ "has-symbols": "^1.0.2"
}
},
"is-regexp": {
@@ -34993,28 +35308,29 @@
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
},
"load-json-file": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
- "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"requires": {
"graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
"strip-bom": "^3.0.0"
},
"dependencies": {
"parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"requires": {
- "error-ex": "^1.2.0"
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
}
},
"pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
}
}
},
@@ -35042,9 +35358,9 @@
}
},
"lodash": {
- "version": "4.17.20",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
- "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
@@ -36060,13 +36376,13 @@
}
},
"object.values": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz",
- "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz",
+ "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==",
"requires": {
- "call-bind": "^1.0.0",
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3",
- "es-abstract": "^1.18.0-next.1",
+ "es-abstract": "^1.18.0-next.2",
"has": "^1.0.3"
}
},
@@ -37657,6 +37973,15 @@
"integrity": "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==",
"dev": true
},
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
"pretty-bytes": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz",
@@ -38196,37 +38521,37 @@
}
},
"read-pkg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
- "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
"requires": {
- "load-json-file": "^2.0.0",
+ "load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
- "path-type": "^2.0.0"
+ "path-type": "^3.0.0"
},
"dependencies": {
"path-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
- "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"requires": {
- "pify": "^2.0.0"
+ "pify": "^3.0.0"
}
},
"pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
}
}
},
"read-pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
- "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
+ "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
"requires": {
"find-up": "^2.0.0",
- "read-pkg": "^2.0.0"
+ "read-pkg": "^3.0.0"
},
"dependencies": {
"find-up": {
@@ -38357,26 +38682,6 @@
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.7",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
- "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.2",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.1",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
}
},
"regexpp": {
@@ -39962,20 +40267,20 @@
}
},
"string.prototype.trimend": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz",
- "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+ "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"requires": {
- "call-bind": "^1.0.0",
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"string.prototype.trimstart": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz",
- "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+ "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"requires": {
- "call-bind": "^1.0.0",
+ "call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
@@ -40699,6 +41004,17 @@
"is-typedarray": "^1.0.0"
}
},
+ "unbox-primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+ "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has-bigints": "^1.0.1",
+ "has-symbols": "^1.0.2",
+ "which-boxed-primitive": "^1.0.2"
+ }
+ },
"unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
@@ -40966,26 +41282,6 @@
"es-abstract": "^1.17.2",
"has-symbols": "^1.0.1",
"object.getownpropertydescriptors": "^2.1.0"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.7",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
- "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.2",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.1",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
}
},
"utila": {
@@ -42311,6 +42607,18 @@
"isexe": "^2.0.0"
}
},
+ "which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "requires": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ }
+ },
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
diff --git a/web/package.json b/web/package.json
index d74d02d..0aae4c8 100644
--- a/web/package.json
+++ b/web/package.json
@@ -5,6 +5,7 @@
"dependencies": {
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.11.2",
+ "clsx": "^1.1.1",
"fontsource-roboto": "^4.0.0",
"material-ui-image": "^3.3.2",
"react": "^17.0.2",
@@ -17,12 +18,9 @@
"build": "react-scripts build",
"build-js": "npm run build && npx gulp",
"test": "react-scripts test",
- "eject": "react-scripts eject"
- },
- "eslintConfig": {
- "extends": [
- "react-app"
- ]
+ "eject": "react-scripts eject",
+ "lint": "eslint --ext .js,.jsx src --color",
+ "fix": "eslint --ext .js,.jsx src --color --fix"
},
"browserslist": {
"production": [
@@ -44,6 +42,9 @@
"babel-minify": "^0.5.1",
"babel-preset-minify": "^0.5.1",
"eslint": "^7.27.0",
+ "eslint-config-airbnb": "^18.2.1",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-plugin-prettier": "^3.4.0",
"gulp": "^4.0.2",
"gulp-inline-source": "^4.0.0",
"gulp-replace": "^1.1.3",
diff --git a/web/src/App.js b/web/src/App.js
deleted file mode 100644
index 9609b91..0000000
--- a/web/src/App.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import CssBaseline from '@material-ui/core/CssBaseline'
-import Appbar from './components/Appbar/index.js'
-import { createMuiTheme, MuiThemeProvider } from '@material-ui/core'
-
-const baseTheme = createMuiTheme({
- overrides: {
- MuiCssBaseline: {
- '@global': {
- html: {
- WebkitFontSmoothing: 'auto',
- },
- },
- },
- },
- palette: {
- primary: {
- main: '#3fb57a',
- },
- secondary: {
- main: '#FFA724',
- },
- tonalOffset: 0.2,
- },
-})
-
-export default function App() {
- return (
-
-
-
-
- )
-}
diff --git a/web/src/App.jsx b/web/src/App.jsx
new file mode 100644
index 0000000..7286c36
--- /dev/null
+++ b/web/src/App.jsx
@@ -0,0 +1,34 @@
+import CssBaseline from '@material-ui/core/CssBaseline'
+import { createMuiTheme, MuiThemeProvider } from '@material-ui/core'
+
+import Appbar from './components/Appbar/index'
+
+const baseTheme = createMuiTheme({
+ overrides: {
+ MuiCssBaseline: {
+ '@global': {
+ html: {
+ WebkitFontSmoothing: 'auto',
+ },
+ },
+ },
+ },
+ palette: {
+ primary: {
+ main: '#3fb57a',
+ },
+ secondary: {
+ main: '#FFA724',
+ },
+ tonalOffset: 0.2,
+ },
+})
+
+export default function App() {
+ return (
+
+
+
+
+ )
+}
diff --git a/web/src/components/About.js b/web/src/components/About.js
deleted file mode 100644
index 513b894..0000000
--- a/web/src/components/About.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from 'react';
-import Button from '@material-ui/core/Button';
-import Dialog from '@material-ui/core/Dialog';
-import DialogActions from '@material-ui/core/DialogActions';
-import DialogContent from '@material-ui/core/DialogContent';
-import DialogContentText from '@material-ui/core/DialogContentText';
-import DialogTitle from '@material-ui/core/DialogTitle';
-import InfoIcon from '@material-ui/icons/Info';
-import ListItem from '@material-ui/core/ListItem'
-import ListItemIcon from '@material-ui/core/ListItemIcon'
-import ListItemText from '@material-ui/core/ListItemText'
-
-export default function AboutDialog() {
- const [open, setOpen] = React.useState(false)
-
- return (
-
-
{setOpen(true)}}>
-
-
-
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/web/src/components/About.jsx b/web/src/components/About.jsx
new file mode 100644
index 0000000..4daff3a
--- /dev/null
+++ b/web/src/components/About.jsx
@@ -0,0 +1,56 @@
+import { useState } from 'react'
+import Button from '@material-ui/core/Button'
+import Dialog from '@material-ui/core/Dialog'
+import DialogActions from '@material-ui/core/DialogActions'
+import DialogContent from '@material-ui/core/DialogContent'
+import DialogContentText from '@material-ui/core/DialogContentText'
+import DialogTitle from '@material-ui/core/DialogTitle'
+import InfoIcon from '@material-ui/icons/Info'
+import ListItem from '@material-ui/core/ListItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import ListItemText from '@material-ui/core/ListItemText'
+
+export default function AboutDialog() {
+ const [open, setOpen] = useState(false)
+
+ return (
+
+
setOpen(true)}>
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/web/src/components/Add/AddDialog.js b/web/src/components/Add/AddDialog.js
deleted file mode 100644
index 46b352d..0000000
--- a/web/src/components/Add/AddDialog.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import { useState } from 'react'
-import Button from '@material-ui/core/Button'
-import TextField from '@material-ui/core/TextField'
-import Dialog from '@material-ui/core/Dialog'
-import DialogActions from '@material-ui/core/DialogActions'
-import DialogContent from '@material-ui/core/DialogContent'
-import DialogTitle from '@material-ui/core/DialogTitle'
-import { torrentsHost } from '../../utils/Hosts'
-
-export default function AddDialog({ handleClose }) {
- const [magnet, setMagnet] = useState('')
- const [title, setTitle] = useState('')
- const [poster, setPoster] = useState('')
-
- const inputMagnet = ({ target: { value } }) => setMagnet(value)
- const inputTitle = ({ target: { value } }) => setTitle(value)
- const inputPoster = ({ target: { value } }) => setPoster(value)
-
- const handleCloseSave = () => {
- try {
- if (!magnet) return
-
- fetch(torrentsHost(), {
- method: 'post',
- body: JSON.stringify({
- action: 'add',
- link: magnet,
- title: title,
- poster: poster,
- save_to_db: true,
- }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- handleClose()
- } catch (e) {
- console.log(e)
- }
- }
-
- return (
-
- )
-}
\ No newline at end of file
diff --git a/web/src/components/Add/AddDialog.jsx b/web/src/components/Add/AddDialog.jsx
new file mode 100644
index 0000000..dbbbe72
--- /dev/null
+++ b/web/src/components/Add/AddDialog.jsx
@@ -0,0 +1,72 @@
+import { useState } from 'react'
+import Button from '@material-ui/core/Button'
+import TextField from '@material-ui/core/TextField'
+import Dialog from '@material-ui/core/Dialog'
+import DialogActions from '@material-ui/core/DialogActions'
+import DialogContent from '@material-ui/core/DialogContent'
+import DialogTitle from '@material-ui/core/DialogTitle'
+import { torrentsHost } from 'utils/Hosts'
+
+export default function AddDialog({ handleClose }) {
+ const [magnet, setMagnet] = useState('')
+ const [title, setTitle] = useState('')
+ const [poster, setPoster] = useState('')
+
+ const inputMagnet = ({ target: { value } }) => setMagnet(value)
+ const inputTitle = ({ target: { value } }) => setTitle(value)
+ const inputPoster = ({ target: { value } }) => setPoster(value)
+
+ const handleCloseSave = () => {
+ try {
+ if (!magnet) return
+
+ fetch(torrentsHost(), {
+ method: 'post',
+ body: JSON.stringify({
+ action: 'add',
+ link: magnet,
+ title,
+ poster,
+ save_to_db: true,
+ }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ handleClose()
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ return (
+
+ )
+}
diff --git a/web/src/components/Add/index.js b/web/src/components/Add/index.js
deleted file mode 100644
index 7a0c0a1..0000000
--- a/web/src/components/Add/index.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { useState } from 'react'
-import ListItemIcon from '@material-ui/core/ListItemIcon'
-import LibraryAddIcon from '@material-ui/icons/LibraryAdd'
-import ListItemText from '@material-ui/core/ListItemText'
-import ListItem from '@material-ui/core/ListItem'
-import AddDialog from './AddDialog'
-
-export default function AddDialogButton() {
- const [isDialogOpen, setIsDialogOpen] = useState(false)
-
- const handleClickOpen = () => setIsDialogOpen(true)
- const handleClose = () => setIsDialogOpen(false)
-
- return (
-
-
-
-
-
-
-
-
- {isDialogOpen &&
}
-
- )
-}
diff --git a/web/src/components/Add/index.jsx b/web/src/components/Add/index.jsx
new file mode 100644
index 0000000..b798785
--- /dev/null
+++ b/web/src/components/Add/index.jsx
@@ -0,0 +1,27 @@
+import { useState } from 'react'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import LibraryAddIcon from '@material-ui/icons/LibraryAdd'
+import ListItemText from '@material-ui/core/ListItemText'
+import ListItem from '@material-ui/core/ListItem'
+
+import AddDialog from './AddDialog'
+
+export default function AddDialogButton() {
+ const [isDialogOpen, setIsDialogOpen] = useState(false)
+
+ const handleClickOpen = () => setIsDialogOpen(true)
+ const handleClose = () => setIsDialogOpen(false)
+
+ return (
+
+
+
+
+
+
+
+
+ {isDialogOpen &&
}
+
+ )
+}
diff --git a/web/src/components/Appbar/index.js b/web/src/components/Appbar/index.js
deleted file mode 100644
index d4bd34b..0000000
--- a/web/src/components/Appbar/index.js
+++ /dev/null
@@ -1,151 +0,0 @@
-import { useEffect, useState } from 'react'
-import clsx from 'clsx'
-import { useTheme } from '@material-ui/core/styles'
-import Drawer from '@material-ui/core/Drawer'
-import AppBar from '@material-ui/core/AppBar'
-import Toolbar from '@material-ui/core/Toolbar'
-import List from '@material-ui/core/List'
-import Typography from '@material-ui/core/Typography'
-import Divider from '@material-ui/core/Divider'
-import IconButton from '@material-ui/core/IconButton'
-import MenuIcon from '@material-ui/icons/Menu'
-import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'
-import ChevronRightIcon from '@material-ui/icons/ChevronRight'
-import ListItem from '@material-ui/core/ListItem'
-import ListItemIcon from '@material-ui/core/ListItemIcon'
-import ListItemText from '@material-ui/core/ListItemText'
-import CreditCardIcon from '@material-ui/icons/CreditCard'
-
-import ListIcon from '@material-ui/icons/List'
-import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew'
-
-import TorrentList from '../TorrentList'
-
-import AddDialogButton from '../Add'
-import RemoveAll from '../RemoveAll'
-import SettingsDialog from '../Settings'
-import AboutDialog from '../About'
-import { playlistAllHost, shutdownHost, torrserverHost } from '../../utils/Hosts'
-import DonateSnackbar from '../Donate'
-import DonateDialog from '../Donate/DonateDialog'
-import UploadDialog from '../Upload'
-import useStyles from './useStyles'
-
-export default function MiniDrawer() {
- const classes = useStyles()
- const theme = useTheme()
- const [isDrawerOpen, setIsDrawerOpen] = useState(false)
- const [isDonationDialogOpen, setIsDonationDialogOpen] = useState(false)
- const [tsVersion, setTSVersion] = useState('')
-
- const handleDrawerOpen = () => {
- setIsDrawerOpen(true)
- }
-
- const handleDrawerClose = () => {
- setIsDrawerOpen(false)
- }
-
- useEffect(() => {
- fetch(torrserverHost + '/echo')
- .then((resp) => resp.text())
- .then((txt) => {
- if (!txt.startsWith('')) setTSVersion(txt)
- })
- }, [isDrawerOpen])
-
- return (
-
-
-
-
-
-
-
- TorrServer {tsVersion}
-
-
-
-
-
-
-
- {theme.direction === 'rtl' ? : }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- fetch(shutdownHost())}>
-
-
-
-
-
-
-
-
-
-
- setIsDonationDialogOpen(true)}>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {isDonationDialogOpen &&
setIsDonationDialogOpen(false)} />}
- {!JSON.parse(localStorage.getItem('snackbarIsClosed')) && }
-
- )
-}
diff --git a/web/src/components/Appbar/index.jsx b/web/src/components/Appbar/index.jsx
new file mode 100644
index 0000000..318f2d0
--- /dev/null
+++ b/web/src/components/Appbar/index.jsx
@@ -0,0 +1,149 @@
+import { useEffect, useState } from 'react'
+import clsx from 'clsx'
+import { useTheme } from '@material-ui/core/styles'
+import Drawer from '@material-ui/core/Drawer'
+import AppBar from '@material-ui/core/AppBar'
+import Toolbar from '@material-ui/core/Toolbar'
+import List from '@material-ui/core/List'
+import Typography from '@material-ui/core/Typography'
+import Divider from '@material-ui/core/Divider'
+import IconButton from '@material-ui/core/IconButton'
+import MenuIcon from '@material-ui/icons/Menu'
+import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'
+import ChevronRightIcon from '@material-ui/icons/ChevronRight'
+import ListItem from '@material-ui/core/ListItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import ListItemText from '@material-ui/core/ListItemText'
+import CreditCardIcon from '@material-ui/icons/CreditCard'
+import ListIcon from '@material-ui/icons/List'
+import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew'
+import { playlistAllHost, shutdownHost, getTorrServerHost } from 'utils/Hosts'
+import TorrentList from 'components/TorrentList'
+import AddDialogButton from 'components/Add'
+import RemoveAll from 'components/RemoveAll'
+import SettingsDialog from 'components/Settings'
+import AboutDialog from 'components/About'
+import DonateSnackbar from 'components/Donate'
+import DonateDialog from 'components/Donate/DonateDialog'
+import UploadDialog from 'components/Upload'
+
+import useStyles from './useStyles'
+
+export default function MiniDrawer() {
+ const classes = useStyles()
+ const theme = useTheme()
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false)
+ const [isDonationDialogOpen, setIsDonationDialogOpen] = useState(false)
+ const [tsVersion, setTSVersion] = useState('')
+
+ const handleDrawerOpen = () => {
+ setIsDrawerOpen(true)
+ }
+
+ const handleDrawerClose = () => {
+ setIsDrawerOpen(false)
+ }
+
+ useEffect(() => {
+ fetch(`${getTorrServerHost()}/echo`)
+ .then(resp => resp.text())
+ .then(txt => {
+ if (!txt.startsWith('')) setTSVersion(txt)
+ })
+ }, [isDrawerOpen])
+
+ return (
+
+
+
+
+
+
+
+ TorrServer {tsVersion}
+
+
+
+
+
+
+
+ {theme.direction === 'rtl' ? : }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fetch(shutdownHost())}>
+
+
+
+
+
+
+
+
+
+
+ setIsDonationDialogOpen(true)}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {isDonationDialogOpen &&
setIsDonationDialogOpen(false)} />}
+ {!JSON.parse(localStorage.getItem('snackbarIsClosed')) && }
+
+ )
+}
diff --git a/web/src/components/Appbar/useStyles.js b/web/src/components/Appbar/useStyles.js
index 7c71d42..d46b1d0 100644
--- a/web/src/components/Appbar/useStyles.js
+++ b/web/src/components/Appbar/useStyles.js
@@ -2,64 +2,64 @@ import { makeStyles } from '@material-ui/core/styles'
const drawerWidth = 240
-export default makeStyles((theme) => ({
+export default makeStyles(theme => ({
root: {
- display: 'flex',
+ display: 'flex',
},
appBar: {
- zIndex: theme.zIndex.drawer + 1,
- transition: theme.transitions.create(['width', 'margin'], {
- easing: theme.transitions.easing.sharp,
- duration: theme.transitions.duration.leavingScreen,
- }),
+ zIndex: theme.zIndex.drawer + 1,
+ transition: theme.transitions.create(['width', 'margin'], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
},
appBarShift: {
- marginLeft: drawerWidth,
- width: `calc(100% - ${drawerWidth}px)`,
- transition: theme.transitions.create(['width', 'margin'], {
- easing: theme.transitions.easing.sharp,
- duration: theme.transitions.duration.enteringScreen,
- }),
+ marginLeft: drawerWidth,
+ width: `calc(100% - ${drawerWidth}px)`,
+ transition: theme.transitions.create(['width', 'margin'], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
},
menuButton: {
- marginRight: 36,
+ marginRight: 36,
},
hide: {
- display: 'none',
+ display: 'none',
},
drawer: {
- width: drawerWidth,
- flexShrink: 1,
- whiteSpace: 'nowrap',
+ width: drawerWidth,
+ flexShrink: 1,
+ whiteSpace: 'nowrap',
},
drawerOpen: {
- width: drawerWidth,
- transition: theme.transitions.create('width', {
- easing: theme.transitions.easing.sharp,
- duration: theme.transitions.duration.enteringScreen,
- }),
+ width: drawerWidth,
+ transition: theme.transitions.create('width', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
},
drawerClose: {
- transition: theme.transitions.create('width', {
- easing: theme.transitions.easing.sharp,
- duration: theme.transitions.duration.leavingScreen,
- }),
- overflowX: 'hidden',
- width: theme.spacing(7) + 1,
- [theme.breakpoints.up('sm')]: {
- width: theme.spacing(9) + 1,
- },
+ transition: theme.transitions.create('width', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
+ overflowX: 'hidden',
+ width: theme.spacing(7) + 1,
+ [theme.breakpoints.up('sm')]: {
+ width: theme.spacing(9) + 1,
+ },
},
toolbar: {
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'flex-end',
- padding: theme.spacing(0, 1),
- // necessary for content to be below app bar
- ...theme.mixins.toolbar,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'flex-end',
+ padding: theme.spacing(0, 1),
+ // necessary for content to be below app bar
+ ...theme.mixins.toolbar,
},
content: {
- flexGrow: 1,
- padding: theme.spacing(3),
+ flexGrow: 1,
+ padding: theme.spacing(3),
},
-}))
\ No newline at end of file
+}))
diff --git a/web/src/components/DialogCacheInfo.js b/web/src/components/DialogCacheInfo.js
deleted file mode 100644
index 68d170a..0000000
--- a/web/src/components/DialogCacheInfo.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import React, { useEffect, useRef } from 'react'
-import Typography from '@material-ui/core/Typography'
-
-import { getPeerString, humanizeSize } from '../utils/Utils'
-import DialogTitle from '@material-ui/core/DialogTitle'
-import DialogContent from '@material-ui/core/DialogContent'
-import { cacheHost } from '../utils/Hosts'
-
-export default function DialogCacheInfo(props) {
- const [hash] = React.useState(props.hash)
- const [cache, setCache] = React.useState({})
- const timerID = useRef(-1)
- const [pMap, setPMap] = React.useState([])
-
- useEffect(() => {
- if (hash)
- timerID.current = setInterval(() => {
- getCache(hash, (cache) => {
- setCache(cache)
- })
- }, 100)
- else clearInterval(timerID.current)
-
- return () => {
- clearInterval(timerID.current)
- }
- }, [hash, props.open])
-
- useEffect(() => {
- if (cache && cache.PiecesCount && cache.Pieces) {
- var map = [];
- for (let i = 0; i < cache.PiecesCount; i++) {
- var reader = 0
- var cls = "piece"
- var prc = 0
- if (cache.Pieces[i]) {
- if (cache.Pieces[i].Completed && cache.Pieces[i].Size >= cache.Pieces[i].Length)
- cls += " piece-complete"
- else
- cls += " piece-loading"
- prc = (cache.Pieces[i].Size / cache.Pieces[i].Length * 100).toFixed(2)
- }
-
- cache.Readers.forEach(r => {
- if (i >= r.Start && i <= r.End && i !== r.Reader)
- cls += " reader-range"
- if (i === r.Reader) {
- cls += " piece-reader"
- }
- })
- map.push({
- prc: prc,
- class: cls,
- info: i,
- reader: reader,
- })
- }
- setPMap(map)
- }
- }, [cache.Pieces])
-
- return (
-
-
-
- Hash {cache.Hash}
-
- Capacity {humanizeSize(cache.Capacity)}
-
- Filled {humanizeSize(cache.Filled)}
-
- Torrent size {cache.Torrent && cache.Torrent.torrent_size && humanizeSize(cache.Torrent.torrent_size)}
-
- Pieces length {humanizeSize(cache.PiecesLength)}
-
- Pieces count {cache.PiecesCount}
-
- Peers: {getPeerString(cache.Torrent)}
-
- Download speed {cache.Torrent && cache.Torrent.download_speed ? humanizeSize(cache.Torrent.download_speed) + '/sec' : ''}
-
- Upload speed {cache.Torrent && cache.Torrent.upload_speed ? humanizeSize(cache.Torrent.upload_speed) + '/sec' : ''}
-
- Status {cache.Torrent && cache.Torrent.stat_string && cache.Torrent.stat_string}
-
-
-
-
-
- {pMap.map(itm => (
-
- {itm.prc > 0 && itm.prc < 100 && (
-
- )}
-
- ))}
-
-
-
- )
-}
-
-function getCache(hash, callback) {
- try {
- fetch(cacheHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'get', hash: hash }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- .then((res) => res.json())
- .then(
- (json) => {
- callback(json)
- },
- (error) => {
- callback({})
- console.error(error)
- }
- )
- } catch (e) {
- console.error(e)
- callback({})
- }
-}
-/*
-{
- "Hash": "41e36c8de915d80db83fc134bee4e7e2d292657e",
- "Capacity": 209715200,
- "Filled": 2914808,
- "PiecesLength": 4194304,
- "PiecesCount": 2065,
- "DownloadSpeed": 32770.860273455524,
- "Pieces": {
- "2064": {
- "Id": 2064,
- "Length": 2914808,
- "Size": 162296,
- "Completed": false
- }
- }
-}
- */
diff --git a/web/src/components/DialogCacheInfo.jsx b/web/src/components/DialogCacheInfo.jsx
new file mode 100644
index 0000000..ffdef9f
--- /dev/null
+++ b/web/src/components/DialogCacheInfo.jsx
@@ -0,0 +1,142 @@
+import { useEffect, useRef, useState } from 'react'
+import Typography from '@material-ui/core/Typography'
+import DialogTitle from '@material-ui/core/DialogTitle'
+import DialogContent from '@material-ui/core/DialogContent'
+import { getPeerString, humanizeSize } from 'utils/Utils'
+import { cacheHost } from 'utils/Hosts'
+
+export default function DialogCacheInfo({ hash, open }) {
+ const [cache, setCache] = useState({})
+ const timerID = useRef(-1)
+ const [pMap, setPMap] = useState([])
+
+ useEffect(() => {
+ if (hash)
+ timerID.current = setInterval(() => {
+ getCache(hash, cache => {
+ setCache(cache)
+ })
+ }, 100)
+ else clearInterval(timerID.current)
+
+ return () => {
+ clearInterval(timerID.current)
+ }
+ }, [hash, open])
+
+ useEffect(() => {
+ if (cache && cache.PiecesCount && cache.Pieces) {
+ const map = []
+ for (let i = 0; i < cache.PiecesCount; i++) {
+ const reader = 0
+ let cls = 'piece'
+ let prc = 0
+ if (cache.Pieces[i]) {
+ if (cache.Pieces[i].Completed && cache.Pieces[i].Size >= cache.Pieces[i].Length) cls += ' piece-complete'
+ else cls += ' piece-loading'
+ prc = ((cache.Pieces[i].Size / cache.Pieces[i].Length) * 100).toFixed(2)
+ }
+
+ cache.Readers.forEach(r => {
+ if (i >= r.Start && i <= r.End && i !== r.Reader) cls += ' reader-range'
+ if (i === r.Reader) {
+ cls += ' piece-reader'
+ }
+ })
+ map.push({
+ prc,
+ class: cls,
+ info: i,
+ reader,
+ })
+ }
+ setPMap(map)
+ }
+ }, [cache.Pieces])
+
+ return (
+
+
+
+ Hash {cache.Hash}
+
+ Capacity {humanizeSize(cache.Capacity)}
+
+ Filled {humanizeSize(cache.Filled)}
+
+ Torrent size {cache.Torrent && cache.Torrent.torrent_size && humanizeSize(cache.Torrent.torrent_size)}
+
+ Pieces length {humanizeSize(cache.PiecesLength)}
+
+ Pieces count {cache.PiecesCount}
+
+ Peers: {getPeerString(cache.Torrent)}
+
+ Download speed {' '}
+ {cache.Torrent && cache.Torrent.download_speed ? `${humanizeSize(cache.Torrent.download_speed)}/sec` : ''}
+
+ Upload speed {' '}
+ {cache.Torrent && cache.Torrent.upload_speed ? `${humanizeSize(cache.Torrent.upload_speed)}/sec` : ''}
+
+ Status {cache.Torrent && cache.Torrent.stat_string && cache.Torrent.stat_string}
+
+
+
+
+
+ {pMap.map(itm => (
+
+ {itm.prc > 0 && itm.prc < 100 && (
+
+ )}
+
+ ))}
+
+
+
+ )
+}
+
+function getCache(hash, callback) {
+ try {
+ fetch(cacheHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'get', hash }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(res => res.json())
+ .then(
+ json => {
+ callback(json)
+ },
+ error => {
+ callback({})
+ console.error(error)
+ },
+ )
+ } catch (e) {
+ console.error(e)
+ callback({})
+ }
+}
+/*
+{
+ "Hash": "41e36c8de915d80db83fc134bee4e7e2d292657e",
+ "Capacity": 209715200,
+ "Filled": 2914808,
+ "PiecesLength": 4194304,
+ "PiecesCount": 2065,
+ "DownloadSpeed": 32770.860273455524,
+ "Pieces": {
+ "2064": {
+ "Id": 2064,
+ "Length": 2914808,
+ "Size": 162296,
+ "Completed": false
+ }
+ }
+}
+ */
diff --git a/web/src/components/DialogTorrentInfo.js b/web/src/components/DialogTorrentInfo.js
deleted file mode 100644
index 197fd54..0000000
--- a/web/src/components/DialogTorrentInfo.js
+++ /dev/null
@@ -1,229 +0,0 @@
-import React, { useEffect } from 'react'
-import Typography from '@material-ui/core/Typography'
-import { Button, ButtonGroup, Grid, List, ListItem } from '@material-ui/core'
-import CachedIcon from '@material-ui/icons/Cached'
-import LinearProgress from '@material-ui/core/LinearProgress';
-
-import { getPeerString, humanizeSize } from '../utils/Utils'
-import { playlistTorrHost, streamHost, viewedHost } from '../utils/Hosts'
-import DialogTitle from '@material-ui/core/DialogTitle'
-import DialogContent from '@material-ui/core/DialogContent'
-
-const style = {
- width100: {
- width: '100%',
- },
- width80: {
- width: '80%',
- },
- poster: {
- display: 'flex',
- flexDirection: 'row',
- borderRadius: '5px',
- },
-}
-
-export default function DialogTorrentInfo(props) {
- const [torrent, setTorrent] = React.useState(props.torrent)
- const [viewed, setViewed] = React.useState(null)
- const [progress, setProgress] = React.useState(-1)
-
- useEffect(() => {
- setTorrent(props.torrent)
- if(torrent.stat==2)
- setProgress(torrent.preloaded_bytes * 100 / torrent.preload_size)
- getViewed(props.torrent.hash,(list) => {
- if (list) {
- let lst = list.map((itm) => itm.file_index)
- setViewed(lst)
- }else
- setViewed(null)
- })
- }, [props.torrent, props.open])
-
- return (
-
-
-
- {torrent.poster &&
}
-
- {torrent.title} {torrent.name && torrent.name !== torrent.title && ' | ' + torrent.name}
-
- Peers: {getPeerString(torrent)}
-
- Loaded: {getPreload(torrent)}
-
- Speed: {humanizeSize(torrent.download_speed)}
-
- Status: {torrent.stat_string}
-
-
-
-
- {torrent.stat==2 && }
-
-
-
-
-
-
-
-
-
-
- {getPlayableFile(torrent) &&
- getPlayableFile(torrent).map((file) => (
-
-
-
-
- ))}
-
-
-
- )
-}
-
-function remViews(hash){
- try {
- if (hash)
- fetch(viewedHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'rem', hash: hash, file_index:-1 }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- } catch (e) {
- console.error(e)
- }
-}
-
-function getViewed(hash, callback) {
- try {
- fetch(viewedHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'list', hash: hash }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- .then((res) => res.json())
- .then(
- (json) => {
- callback(json)
- },
- (error) => {
- callback(null)
- }
- )
- } catch (e) {
- console.error(e)
- }
-}
-
-function getPlayableFile(torrent){
- if (!torrent || !torrent.file_stats)
- return null
- return torrent.file_stats.filter(file => extPlayable.includes(getExt(file.path)))
-}
-
-function getExt(filename){
- const ext = filename.split('.').pop()
- if (ext == filename)
- return ''
- return ext.toLowerCase()
-}
-
-function getPreload(torrent) {
- if (torrent.preloaded_bytes > 0 && torrent.preload_size > 0 && torrent.preloaded_bytes < torrent.preload_size) {
- let progress = ((torrent.preloaded_bytes * 100) / torrent.preload_size).toFixed(2)
- return humanizeSize(torrent.preloaded_bytes) + ' / ' + humanizeSize(torrent.preload_size) + ' ' + progress + '%'
- }
-
- if (!torrent.preloaded_bytes) return humanizeSize(0)
-
- return humanizeSize(torrent.preloaded_bytes)
-}
-
-const extPlayable = [
-// video
- "3g2",
- "3gp",
- "aaf",
- "asf",
- "avchd",
- "avi",
- "drc",
- "flv",
- "iso",
- "m2v",
- "m2ts",
- "m4p",
- "m4v",
- "mkv",
- "mng",
- "mov",
- "mp2",
- "mp4",
- "mpe",
- "mpeg",
- "mpg",
- "mpv",
- "mxf",
- "nsv",
- "ogg",
- "ogv",
- "ts",
- "qt",
- "rm",
- "rmvb",
- "roq",
- "svi",
- "vob",
- "webm",
- "wmv",
- "yuv",
-// audio
- "aac",
- "aiff",
- "ape",
- "au",
- "flac",
- "gsm",
- "it",
- "m3u",
- "m4a",
- "mid",
- "mod",
- "mp3",
- "mpa",
- "pls",
- "ra",
- "s3m",
- "sid",
- "wav",
- "wma",
- "xm"
-]
diff --git a/web/src/components/DialogTorrentInfo.jsx b/web/src/components/DialogTorrentInfo.jsx
new file mode 100644
index 0000000..1de8b59
--- /dev/null
+++ b/web/src/components/DialogTorrentInfo.jsx
@@ -0,0 +1,254 @@
+import { useEffect, useState } from 'react'
+import Typography from '@material-ui/core/Typography'
+import { Button, ButtonGroup, Grid, List, ListItem } from '@material-ui/core'
+import CachedIcon from '@material-ui/icons/Cached'
+import LinearProgress from '@material-ui/core/LinearProgress'
+import DialogTitle from '@material-ui/core/DialogTitle'
+import DialogContent from '@material-ui/core/DialogContent'
+import { getPeerString, humanizeSize } from 'utils/Utils'
+import { playlistTorrHost, streamHost, viewedHost } from 'utils/Hosts'
+
+const style = {
+ width100: {
+ width: '100%',
+ },
+ width80: {
+ width: '80%',
+ },
+ poster: {
+ display: 'flex',
+ flexDirection: 'row',
+ borderRadius: '5px',
+ },
+}
+
+export default function DialogTorrentInfo({ torrent, open }) {
+ const [torrentLocalComponentValue, setTorrentLocalComponentValue] = useState(torrent)
+ const [viewed, setViewed] = useState(null)
+ const [progress, setProgress] = useState(-1)
+
+ useEffect(() => {
+ setTorrentLocalComponentValue(torrent)
+ if (torrentLocalComponentValue.stat === 2)
+ setProgress((torrentLocalComponentValue.preloaded_bytes * 100) / torrentLocalComponentValue.preload_size)
+ getViewed(torrent.hash, list => {
+ if (list) {
+ const lst = list.map(itm => itm.file_index)
+ setViewed(lst)
+ } else setViewed(null)
+ })
+ }, [torrent, open])
+
+ return (
+
+
+
+
+ {torrentLocalComponentValue.poster && (
+
+ )}
+
+
+ {torrentLocalComponentValue.title}{' '}
+ {torrentLocalComponentValue.name &&
+ torrentLocalComponentValue.name !== torrentLocalComponentValue.title &&
+ ` | ${torrentLocalComponentValue.name}`}
+
+ Peers: {getPeerString(torrentLocalComponentValue)}
+
+ Loaded: {getPreload(torrentLocalComponentValue)}
+
+ Speed: {humanizeSize(torrentLocalComponentValue.download_speed)}
+
+ Status: {torrentLocalComponentValue.stat_string}
+
+
+
+
+ {torrentLocalComponentValue.stat === 2 && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+ {getPlayableFile(torrentLocalComponentValue) &&
+ getPlayableFile(torrentLocalComponentValue).map(file => (
+
+
+
+
+ ))}
+
+
+
+ )
+}
+
+function remViews(hash) {
+ try {
+ if (hash)
+ fetch(viewedHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'rem', hash, file_index: -1 }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ } catch (e) {
+ console.error(e)
+ }
+}
+
+function getViewed(hash, callback) {
+ try {
+ fetch(viewedHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'list', hash }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(res => res.json())
+ .then(callback)
+ } catch (e) {
+ console.error(e)
+ }
+}
+
+function getPlayableFile(torrent) {
+ if (!torrent || !torrent.file_stats) return null
+ return torrent.file_stats.filter(file => extPlayable.includes(getExt(file.path)))
+}
+
+function getExt(filename) {
+ const ext = filename.split('.').pop()
+ if (ext === filename) return ''
+ return ext.toLowerCase()
+}
+
+function getPreload(torrent) {
+ if (torrent.preloaded_bytes > 0 && torrent.preload_size > 0 && torrent.preloaded_bytes < torrent.preload_size) {
+ const progress = ((torrent.preloaded_bytes * 100) / torrent.preload_size).toFixed(2)
+ return `${humanizeSize(torrent.preloaded_bytes)} / ${humanizeSize(torrent.preload_size)} ${progress}%`
+ }
+
+ if (!torrent.preloaded_bytes) return humanizeSize(0)
+
+ return humanizeSize(torrent.preloaded_bytes)
+}
+
+const extPlayable = [
+ // video
+ '3g2',
+ '3gp',
+ 'aaf',
+ 'asf',
+ 'avchd',
+ 'avi',
+ 'drc',
+ 'flv',
+ 'iso',
+ 'm2v',
+ 'm2ts',
+ 'm4p',
+ 'm4v',
+ 'mkv',
+ 'mng',
+ 'mov',
+ 'mp2',
+ 'mp4',
+ 'mpe',
+ 'mpeg',
+ 'mpg',
+ 'mpv',
+ 'mxf',
+ 'nsv',
+ 'ogg',
+ 'ogv',
+ 'ts',
+ 'qt',
+ 'rm',
+ 'rmvb',
+ 'roq',
+ 'svi',
+ 'vob',
+ 'webm',
+ 'wmv',
+ 'yuv',
+ // audio
+ 'aac',
+ 'aiff',
+ 'ape',
+ 'au',
+ 'flac',
+ 'gsm',
+ 'it',
+ 'm3u',
+ 'm4a',
+ 'mid',
+ 'mod',
+ 'mp3',
+ 'mpa',
+ 'pls',
+ 'ra',
+ 's3m',
+ 'sid',
+ 'wav',
+ 'wma',
+ 'xm',
+]
diff --git a/web/src/components/Donate/DonateDialog.js b/web/src/components/Donate/DonateDialog.js
deleted file mode 100644
index ab5c112..0000000
--- a/web/src/components/Donate/DonateDialog.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import ListItem from '@material-ui/core/ListItem'
-import Dialog from '@material-ui/core/Dialog'
-import DialogTitle from '@material-ui/core/DialogTitle'
-import DialogContent from '@material-ui/core/DialogContent'
-import DialogActions from '@material-ui/core/DialogActions'
-import List from '@material-ui/core/List'
-import ButtonGroup from '@material-ui/core/ButtonGroup'
-import Button from '@material-ui/core/Button'
-
-const donateFrame =
- ''
-
-export default function DonateDialog ({ onClose }) {
- return (
-
- )
-}
\ No newline at end of file
diff --git a/web/src/components/Donate/DonateDialog.jsx b/web/src/components/Donate/DonateDialog.jsx
new file mode 100644
index 0000000..cb17b0e
--- /dev/null
+++ b/web/src/components/Donate/DonateDialog.jsx
@@ -0,0 +1,41 @@
+import ListItem from '@material-ui/core/ListItem'
+import Dialog from '@material-ui/core/Dialog'
+import DialogTitle from '@material-ui/core/DialogTitle'
+import DialogContent from '@material-ui/core/DialogContent'
+import DialogActions from '@material-ui/core/DialogActions'
+import List from '@material-ui/core/List'
+import ButtonGroup from '@material-ui/core/ButtonGroup'
+import Button from '@material-ui/core/Button'
+
+const donateFrame =
+ ''
+
+export default function DonateDialog({ onClose }) {
+ return (
+
+ )
+}
diff --git a/web/src/components/Donate/index.js b/web/src/components/Donate/index.js
deleted file mode 100644
index 8a91e84..0000000
--- a/web/src/components/Donate/index.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { useState } from 'react'
-import Button from '@material-ui/core/Button'
-import Snackbar from '@material-ui/core/Snackbar'
-import IconButton from '@material-ui/core/IconButton'
-import CreditCardIcon from '@material-ui/icons/CreditCard'
-import CloseIcon from '@material-ui/icons/Close'
-import DonateDialog from './DonateDialog'
-
-export default function DonateSnackbar() {
- const [open, setOpen] = useState(false)
- const [snackbarOpen, setSnackbarOpen] = useState(true)
-
- const disableSnackbar = () => {
- setSnackbarOpen(false)
- localStorage.setItem('snackbarIsClosed', true)
- }
-
- return (
- <>
- {open && setOpen(false)} />}
-
-
-
-
-
-
-
- >
- }
- />
- >
- )
-}
diff --git a/web/src/components/Donate/index.jsx b/web/src/components/Donate/index.jsx
new file mode 100644
index 0000000..eff69da
--- /dev/null
+++ b/web/src/components/Donate/index.jsx
@@ -0,0 +1,54 @@
+import { useState } from 'react'
+import Button from '@material-ui/core/Button'
+import Snackbar from '@material-ui/core/Snackbar'
+import IconButton from '@material-ui/core/IconButton'
+import CreditCardIcon from '@material-ui/icons/CreditCard'
+import CloseIcon from '@material-ui/icons/Close'
+
+import DonateDialog from './DonateDialog'
+
+export default function DonateSnackbar() {
+ const [open, setOpen] = useState(false)
+ const [snackbarOpen, setSnackbarOpen] = useState(true)
+
+ const disableSnackbar = () => {
+ setSnackbarOpen(false)
+ localStorage.setItem('snackbarIsClosed', true)
+ }
+
+ return (
+ <>
+ {open && setOpen(false)} />}
+
+
+
+
+
+
+
+ >
+ }
+ />
+ >
+ )
+}
diff --git a/web/src/components/RemoveAll.js b/web/src/components/RemoveAll.js
deleted file mode 100644
index ca51d86..0000000
--- a/web/src/components/RemoveAll.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from 'react'
-import ListItem from '@material-ui/core/ListItem'
-import ListItemIcon from '@material-ui/core/ListItemIcon'
-import ListItemText from '@material-ui/core/ListItemText'
-import DeleteIcon from '@material-ui/icons/Delete'
-import { torrentsHost } from '../utils/Hosts'
-
-const fnRemoveAll = () => {
- fetch(torrentsHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'list' }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- .then((res) => res.json())
- .then((json) => {
- json.forEach((torr) => {
- fetch(torrentsHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'rem', hash: torr.hash }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- })
- })
-}
-
-export default function RemoveAll() {
- return (
-
-
-
-
-
-
- )
-}
diff --git a/web/src/components/RemoveAll.jsx b/web/src/components/RemoveAll.jsx
new file mode 100644
index 0000000..fd81770
--- /dev/null
+++ b/web/src/components/RemoveAll.jsx
@@ -0,0 +1,41 @@
+import ListItem from '@material-ui/core/ListItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import ListItemText from '@material-ui/core/ListItemText'
+import DeleteIcon from '@material-ui/icons/Delete'
+import { torrentsHost } from 'utils/Hosts'
+
+const fnRemoveAll = () => {
+ fetch(torrentsHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'list' }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(res => res.json())
+ .then(json => {
+ json.forEach(torr => {
+ fetch(torrentsHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'rem', hash: torr.hash }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ })
+ })
+}
+
+export default function RemoveAll() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/web/src/components/Settings.js b/web/src/components/Settings.js
deleted file mode 100644
index cb67dc8..0000000
--- a/web/src/components/Settings.js
+++ /dev/null
@@ -1,162 +0,0 @@
-import ListItem from '@material-ui/core/ListItem'
-import ListItemIcon from '@material-ui/core/ListItemIcon'
-import ListItemText from '@material-ui/core/ListItemText'
-import React, { useEffect } from 'react'
-import SettingsIcon from '@material-ui/icons/Settings'
-import Dialog from '@material-ui/core/Dialog'
-import DialogTitle from '@material-ui/core/DialogTitle'
-import DialogContent from '@material-ui/core/DialogContent'
-import TextField from '@material-ui/core/TextField'
-import DialogActions from '@material-ui/core/DialogActions'
-import Button from '@material-ui/core/Button'
-import { FormControlLabel, InputLabel, Select, Switch } from '@material-ui/core'
-import { settingsHost, setTorrServerHost, torrserverHost } from '../utils/Hosts'
-
-export default function SettingsDialog() {
- const [open, setOpen] = React.useState(false)
- const [settings, setSets] = React.useState({})
- const [show, setShow] = React.useState(false)
- const [tsHost, setTSHost] = React.useState(torrserverHost ? torrserverHost : window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : ''))
-
- const handleClickOpen = () => {
- setOpen(true)
- }
- const handleClose = () => {
- setOpen(false)
- }
- const handleCloseSave = () => {
- setOpen(false)
- let sets = JSON.parse(JSON.stringify(settings))
- sets.CacheSize *= 1024 * 1024
- sets.PreloadBufferSize *= 1024 * 1024
- fetch(settingsHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'set', sets: sets }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- }
-
- useEffect(() => {
- fetch(settingsHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'get' }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- .then((res) => res.json())
- .then(
- (json) => {
- json.CacheSize /= 1024 * 1024
- json.PreloadBufferSize /= 1024 * 1024
- setSets(json)
- setShow(true)
- },
- (error) => {
- setShow(false)
- console.log(error)
- }
- )
- .catch((e) => {
- setShow(false)
- console.log(e)
- })
- }, [tsHost])
-
- const onInputHost = (event) => {
- let host = event.target.value
- setTorrServerHost(host)
- setTSHost(host)
- }
-
- const inputForm = (event) => {
- let sets = JSON.parse(JSON.stringify(settings))
- if (event.target.type === 'number' || event.target.type === 'select-one') {
- sets[event.target.id] = Number(event.target.value)
- } else if (event.target.type === 'checkbox') {
- sets[event.target.id] = Boolean(event.target.checked)
- } else if (event.target.type === 'url') {
- sets[event.target.id] = event.target.value
- }
- setSets(sets)
- }
-
- return (
-
-
-
-
-
-
-
-
-
- )
-}
-
diff --git a/web/src/components/Settings.jsx b/web/src/components/Settings.jsx
new file mode 100644
index 0000000..ceb623a
--- /dev/null
+++ b/web/src/components/Settings.jsx
@@ -0,0 +1,281 @@
+import ListItem from '@material-ui/core/ListItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import ListItemText from '@material-ui/core/ListItemText'
+import { useEffect, useState } from 'react'
+import SettingsIcon from '@material-ui/icons/Settings'
+import Dialog from '@material-ui/core/Dialog'
+import DialogTitle from '@material-ui/core/DialogTitle'
+import DialogContent from '@material-ui/core/DialogContent'
+import TextField from '@material-ui/core/TextField'
+import DialogActions from '@material-ui/core/DialogActions'
+import Button from '@material-ui/core/Button'
+import { FormControlLabel, InputLabel, Select, Switch } from '@material-ui/core'
+import { settingsHost, setTorrServerHost, getTorrServerHost } from 'utils/Hosts'
+
+export default function SettingsDialog() {
+ const [open, setOpen] = useState(false)
+ const [settings, setSets] = useState({})
+ const [show, setShow] = useState(false)
+
+ const { protocol, hostname, port } = window.location
+ const [tsHost, setTSHost] = useState(getTorrServerHost() || `${protocol}//${hostname}${port ? `:${port}` : ''}`)
+
+ const handleClickOpen = () => setOpen(true)
+ const handleClose = () => setOpen(false)
+ const handleCloseSave = () => {
+ setOpen(false)
+ const sets = JSON.parse(JSON.stringify(settings))
+ sets.CacheSize *= 1024 * 1024
+ sets.PreloadBufferSize *= 1024 * 1024
+ fetch(settingsHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'set', sets }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ }
+
+ useEffect(() => {
+ fetch(settingsHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'get' }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(res => res.json())
+ .then(
+ json => {
+ // eslint-disable-next-line no-param-reassign
+ json.CacheSize /= 1024 * 1024
+ // eslint-disable-next-line no-param-reassign
+ json.PreloadBufferSize /= 1024 * 1024
+ setSets(json)
+ setShow(true)
+ },
+ error => {
+ setShow(false)
+ console.log(error)
+ },
+ )
+ .catch(e => {
+ setShow(false)
+ console.log(e)
+ })
+ }, [tsHost])
+
+ const onInputHost = event => {
+ const host = event.target.value
+ setTorrServerHost(host)
+ setTSHost(host)
+ }
+
+ const inputForm = event => {
+ const sets = JSON.parse(JSON.stringify(settings))
+ if (event.target.type === 'number' || event.target.type === 'select-one') {
+ sets[event.target.id] = Number(event.target.value)
+ } else if (event.target.type === 'checkbox') {
+ sets[event.target.id] = Boolean(event.target.checked)
+ } else if (event.target.type === 'url') {
+ sets[event.target.id] = event.target.value
+ }
+ setSets(sets)
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/web/src/components/Torrent/index.js b/web/src/components/Torrent/index.js
deleted file mode 100644
index 1d905a3..0000000
--- a/web/src/components/Torrent/index.js
+++ /dev/null
@@ -1,189 +0,0 @@
-import { useEffect, useRef, useState } from 'react'
-import Button from '@material-ui/core/Button'
-
-import 'fontsource-roboto'
-
-import HeightIcon from '@material-ui/icons/Height';
-import CloseIcon from '@material-ui/icons/Close';
-import DeleteIcon from '@material-ui/icons/Delete'
-import DialogActions from '@material-ui/core/DialogActions'
-import Dialog from '@material-ui/core/Dialog'
-
-import { getPeerString, humanizeSize } from '../../utils/Utils'
-
-import DialogTorrentInfo from '../DialogTorrentInfo'
-import { torrentsHost } from '../../utils/Hosts'
-import DialogCacheInfo from '../DialogCacheInfo'
-import DataUsageIcon from '@material-ui/icons/DataUsage'
-import { NoImageIcon } from '../../icons';
-import { StyledButton, TorrentCard, TorrentCardButtons, TorrentCardDescription, TorrentCardDescriptionContent, TorrentCardDescriptionLabel, TorrentCardPoster } from './style';
-
-export default function Torrent(props) {
- const [open, setOpen] = useState(false)
- const [showCache, setShowCache] = useState(false)
- const [torrent, setTorrent] = useState(props.torrent)
- const timerID = useRef(-1)
-
- useEffect(() => {
- setTorrent(props.torrent)
- }, [props.torrent])
-
- useEffect(() => {
- if (open)
- timerID.current = setInterval(() => {
- getTorrent(torrent.hash, (torr, error) => {
- if (error) console.error(error)
- else if (torr) setTorrent(torr)
- })
- }, 1000)
- else clearInterval(timerID.current)
-
- return () => {
- clearInterval(timerID.current)
- }
- }, [torrent.hash, open])
-
- const { title, name, poster, torrent_size, download_speed } = torrent
-
- return (
- <>
-
-
-
- {poster
- ?
- : }
-
-
-
- {
- setShowCache(true)
- setOpen(true)
- }}
- >
-
- Cache
-
-
- dropTorrent(torrent)}
- >
-
- Drop
-
-
- deleteTorrent(torrent)}
- >
-
- Delete
-
-
- {
- setShowCache(false)
- setOpen(true)
- }}
- >
-
- Details
-
-
-
-
- Name
- {title || name}
-
- Size
- {torrent_size > 0 && humanizeSize(torrent_size)}
-
- Download speed
- {download_speed > 0 ? humanizeSize(download_speed) : '---'}
-
- Peers
- {getPeerString(torrent) || '---'}
-
-
-
-
- >
- )
-}
-
-function getTorrent(hash, callback) {
- try {
- fetch(torrentsHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'get', hash: hash }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- .then((res) => res.json())
- .then(
- (json) => {
- callback(json, null)
- },
- (error) => {
- callback(null, error)
- }
- )
- } catch (e) {
- console.error(e)
- }
-}
-
-function deleteTorrent(torrent) {
- try {
- fetch(torrentsHost(), {
- method: 'post',
- body: JSON.stringify({
- action: 'rem',
- hash: torrent.hash,
- }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- } catch (e) {
- console.error(e)
- }
-}
-
-function dropTorrent(torrent) {
- try {
- fetch(torrentsHost(), {
- method: 'post',
- body: JSON.stringify({
- action: 'drop',
- hash: torrent.hash,
- }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- } catch (e) {
- console.error(e)
- }
-}
diff --git a/web/src/components/Torrent/index.jsx b/web/src/components/Torrent/index.jsx
new file mode 100644
index 0000000..f48f314
--- /dev/null
+++ b/web/src/components/Torrent/index.jsx
@@ -0,0 +1,188 @@
+/* eslint-disable camelcase */
+import 'fontsource-roboto'
+import { useEffect, useRef, useState } from 'react'
+import Button from '@material-ui/core/Button'
+import HeightIcon from '@material-ui/icons/Height'
+import CloseIcon from '@material-ui/icons/Close'
+import DeleteIcon from '@material-ui/icons/Delete'
+import DialogActions from '@material-ui/core/DialogActions'
+import Dialog from '@material-ui/core/Dialog'
+import DataUsageIcon from '@material-ui/icons/DataUsage'
+import { getPeerString, humanizeSize } from 'utils/Utils'
+import { torrentsHost } from 'utils/Hosts'
+import { NoImageIcon } from 'icons'
+import DialogTorrentInfo from 'components/DialogTorrentInfo'
+import DialogCacheInfo from 'components/DialogCacheInfo'
+
+import {
+ StyledButton,
+ TorrentCard,
+ TorrentCardButtons,
+ TorrentCardDescription,
+ TorrentCardDescriptionContent,
+ TorrentCardDescriptionLabel,
+ TorrentCardPoster,
+} from './style'
+
+export default function Torrent({ torrent }) {
+ const [open, setOpen] = useState(false)
+ const [showCache, setShowCache] = useState(false)
+ const [torrentLocalComponentValue, setTorrentLocalComponentValue] = useState(torrent)
+ const timerID = useRef(-1)
+
+ useEffect(() => {
+ setTorrentLocalComponentValue(torrent)
+ }, [torrent])
+
+ useEffect(() => {
+ if (open)
+ timerID.current = setInterval(() => {
+ getTorrent(torrentLocalComponentValue.hash, (torr, error) => {
+ if (error) console.error(error)
+ else if (torr) setTorrentLocalComponentValue(torr)
+ })
+ }, 1000)
+ else clearInterval(timerID.current)
+
+ return () => {
+ clearInterval(timerID.current)
+ }
+ }, [torrentLocalComponentValue.hash, open])
+
+ const { title, name, poster, torrent_size, download_speed } = torrentLocalComponentValue
+
+ return (
+ <>
+
+
+ {poster ?
: }
+
+
+
+ {
+ setShowCache(true)
+ setOpen(true)
+ }}
+ >
+
+ Cache
+
+
+ dropTorrent(torrentLocalComponentValue)}>
+
+ Drop
+
+
+ deleteTorrent(torrentLocalComponentValue)}>
+
+ Delete
+
+
+ {
+ setShowCache(false)
+ setOpen(true)
+ }}
+ >
+
+ Details
+
+
+
+
+ Name
+ {title || name}
+
+ Size
+
+ {torrent_size > 0 && humanizeSize(torrent_size)}
+
+
+ Download speed
+
+ {download_speed > 0 ? humanizeSize(download_speed) : '---'}
+
+
+ Peers
+
+ {getPeerString(torrentLocalComponentValue) || '---'}
+
+
+
+
+
+ >
+ )
+}
+
+function getTorrent(hash, callback) {
+ try {
+ fetch(torrentsHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'get', hash }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(res => res.json())
+ .then(
+ json => {
+ callback(json, null)
+ },
+ error => {
+ callback(null, error)
+ },
+ )
+ } catch (e) {
+ console.error(e)
+ }
+}
+
+function deleteTorrent(torrent) {
+ try {
+ fetch(torrentsHost(), {
+ method: 'post',
+ body: JSON.stringify({
+ action: 'rem',
+ hash: torrent.hash,
+ }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ } catch (e) {
+ console.error(e)
+ }
+}
+
+function dropTorrent(torrent) {
+ try {
+ fetch(torrentsHost(), {
+ method: 'post',
+ body: JSON.stringify({
+ action: 'drop',
+ hash: torrent.hash,
+ }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ } catch (e) {
+ console.error(e)
+ }
+}
diff --git a/web/src/components/Torrent/style.js b/web/src/components/Torrent/style.js
index f7cd3b3..927b636 100644
--- a/web/src/components/Torrent/style.js
+++ b/web/src/components/Torrent/style.js
@@ -1,99 +1,98 @@
-import styled, { css } from 'styled-components';
+import styled, { css } from 'styled-components'
export const TorrentCard = styled.div`
- border: 1px solid;
- border-radius: 5px;
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- grid-template-rows: 175px minmax(min-content, 1fr);
- grid-template-areas:
- "poster buttons"
- "description description";
- gap: 10px;
- padding: 10px;
- background: #3fb57a;
- box-shadow:
- 0px 2px 4px -1px rgb(0 0 0 / 20%),
- 0px 4px 5px 0px rgb(0 0 0 / 14%),
- 0px 1px 10px 0px rgb(0 0 0 / 12%);
+ border: 1px solid;
+ border-radius: 5px;
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-template-rows: 175px minmax(min-content, 1fr);
+ grid-template-areas:
+ 'poster buttons'
+ 'description description';
+ gap: 10px;
+ padding: 10px;
+ background: #3fb57a;
+ box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
`
export const TorrentCardPoster = styled.div`
- grid-area: poster;
- border-radius: 5px;
- overflow: hidden;
- text-align: center;
+ grid-area: poster;
+ border-radius: 5px;
+ overflow: hidden;
+ text-align: center;
- ${({ isPoster }) => isPoster ? css`
- img {
+ ${({ isPoster }) =>
+ isPoster
+ ? css`
+ img {
height: 100%;
border-radius: 5px;
- }
- `: css`
- display: grid;
- place-items: center;
- background: #74c39c;
- border: 1px solid;
+ }
+ `
+ : css`
+ display: grid;
+ place-items: center;
+ background: #74c39c;
+ border: 1px solid;
- svg {
+ svg {
transform: translateY(-3px);
- }
- `};
+ }
+ `};
`
export const TorrentCardButtons = styled.div`
- grid-area: buttons;
- display: grid;
- gap: 5px;
+ grid-area: buttons;
+ display: grid;
+ gap: 5px;
`
export const TorrentCardDescription = styled.div`
- grid-area: description;
- background: #74c39c;
- border-radius: 5px;
- padding: 5px;
- word-break: break-word;
+ grid-area: description;
+ background: #74c39c;
+ border-radius: 5px;
+ padding: 5px;
+ word-break: break-word;
`
export const TorrentCardDescriptionLabel = styled.div`
- text-transform: uppercase;
- font-size: 10px;
- font-weight: 500;
- letter-spacing: 0.4px;
- color: #216e47;
+ text-transform: uppercase;
+ font-size: 10px;
+ font-weight: 500;
+ letter-spacing: 0.4px;
+ color: #216e47;
`
export const TorrentCardDescriptionContent = styled.div`
- margin-left: 5px;
- margin-bottom: 10px;
+ margin-left: 5px;
+ margin-bottom: 10px;
`
export const StyledButton = styled.button`
- border-radius: 5px;
- border: none;
- cursor: pointer;
- transition: 0.2s;
- display: flex;
- align-items: center;
- text-transform: uppercase;
- background: #216e47;
- color: #fff;
- font-size: 1rem;
- font-family: "Roboto", "Helvetica", "Arial", sans-serif;
- letter-spacing: 0.009em;
+ border-radius: 5px;
+ border: none;
+ cursor: pointer;
+ transition: 0.2s;
+ display: flex;
+ align-items: center;
+ text-transform: uppercase;
+ background: #216e47;
+ color: #fff;
+ font-size: 1rem;
+ font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
+ letter-spacing: 0.009em;
+
+ > :first-child {
+ margin-right: 10px;
+ }
+
+ @media (max-width: 600px) {
+ font-size: 0.7rem;
> :first-child {
- margin-right: 10px;
+ margin-right: 15px;
}
+ }
- @media (max-width: 600px) {
- font-size: 0.7rem;
-
- > :first-child {
- margin-right: 15px;
- }
- }
-
-
- :hover {
- background: #2a7e54;
- }
-`
\ No newline at end of file
+ :hover {
+ background: #2a7e54;
+ }
+`
diff --git a/web/src/components/TorrentList.js b/web/src/components/TorrentList.js
deleted file mode 100644
index e33fb05..0000000
--- a/web/src/components/TorrentList.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import styled from 'styled-components';
-import { useEffect, useRef, useState } from 'react'
-import Torrent from './Torrent'
-import { Typography } from '@material-ui/core'
-import { torrentsHost } from '../utils/Hosts'
-
-const TorrentListWrapper = styled.div`
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(250px, 350px));
- gap: 30px;
-`
-
-export default function TorrentList() {
- const [torrents, setTorrents] = useState([])
- const [offline, setOffline] = useState(true)
- const timerID = useRef(-1)
-
- useEffect(() => {
- timerID.current = setInterval(() => {
- getTorrentList((torrs) => {
- if (torrs) setOffline(false)
- else setOffline(true)
- setTorrents(torrs)
- })
- }, 1000)
-
- return () => {
- clearInterval(timerID.current)
- }
- }, [])
-
- return (
-
- {offline ? Offline : (
- torrents && torrents.map(torrent => )
- )}
-
- )
-}
-
-function getTorrentList(callback) {
- fetch(torrentsHost(), {
- method: 'post',
- body: JSON.stringify({ action: 'list' }),
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- },
- })
- .then((res) => res.json())
- .then(
- (json) => {
- callback(json)
- },
- (error) => {
- callback(null)
- }
- )
-}
diff --git a/web/src/components/TorrentList.jsx b/web/src/components/TorrentList.jsx
new file mode 100644
index 0000000..3dc434b
--- /dev/null
+++ b/web/src/components/TorrentList.jsx
@@ -0,0 +1,55 @@
+import styled from 'styled-components'
+import { useEffect, useRef, useState } from 'react'
+import { Typography } from '@material-ui/core'
+import { torrentsHost } from 'utils/Hosts'
+
+import Torrent from './Torrent'
+
+const TorrentListWrapper = styled.div`
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 350px));
+ gap: 30px;
+`
+
+export default function TorrentList() {
+ const [torrents, setTorrents] = useState([])
+ const [offline, setOffline] = useState(true)
+ const timerID = useRef(-1)
+
+ useEffect(() => {
+ timerID.current = setInterval(() => {
+ getTorrentList(torrs => {
+ if (torrs) setOffline(false)
+ else setOffline(true)
+ setTorrents(torrs)
+ })
+ }, 1000)
+
+ return () => {
+ clearInterval(timerID.current)
+ }
+ }, [])
+
+ return (
+
+ {offline ? (
+ Offline
+ ) : (
+ torrents && torrents.map(torrent => )
+ )}
+
+ )
+}
+
+function getTorrentList(callback) {
+ fetch(torrentsHost(), {
+ method: 'post',
+ body: JSON.stringify({ action: 'list' }),
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(res => res.json())
+ .then(callback)
+}
diff --git a/web/src/components/Upload.js b/web/src/components/Upload.js
deleted file mode 100644
index 53eba54..0000000
--- a/web/src/components/Upload.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from 'react'
-import ListItemIcon from '@material-ui/core/ListItemIcon'
-import ListItemText from '@material-ui/core/ListItemText'
-import ListItem from '@material-ui/core/ListItem'
-import PublishIcon from '@material-ui/icons/Publish'
-import { torrentUploadHost } from '../utils/Hosts'
-
-const classes = {
- input: {
- display: 'none',
- },
-}
-
-export default function UploadDialog() {
- const handleCapture = ({ target }) => {
- let data = new FormData()
- data.append('save', 'true')
- for (let i = 0; i < target.files.length; i++) {
- data.append('file' + i, target.files[i])
- }
- fetch(torrentUploadHost(), {
- method: 'POST',
- body: data,
- })
- }
-
- return (
-
- )
-}
diff --git a/web/src/components/Upload.jsx b/web/src/components/Upload.jsx
new file mode 100644
index 0000000..e2af75e
--- /dev/null
+++ b/web/src/components/Upload.jsx
@@ -0,0 +1,49 @@
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import ListItemText from '@material-ui/core/ListItemText'
+import ListItem from '@material-ui/core/ListItem'
+import PublishIcon from '@material-ui/icons/Publish'
+import { torrentUploadHost } from 'utils/Hosts'
+
+const classes = {
+ input: {
+ display: 'none',
+ },
+}
+
+export default function UploadDialog() {
+ const handleCapture = ({ target }) => {
+ const data = new FormData()
+ data.append('save', 'true')
+ for (let i = 0; i < target.files.length; i++) {
+ data.append(`file${i}`, target.files[i])
+ }
+ fetch(torrentUploadHost(), {
+ method: 'POST',
+ body: data,
+ })
+ }
+
+ return (
+
+ )
+}
diff --git a/web/src/icons/index.js b/web/src/icons/index.js
deleted file mode 100644
index 7ccab70..0000000
--- a/web/src/icons/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export const NoImageIcon = () => (
-
-)
\ No newline at end of file
diff --git a/web/src/icons/index.jsx b/web/src/icons/index.jsx
new file mode 100644
index 0000000..f454f2b
--- /dev/null
+++ b/web/src/icons/index.jsx
@@ -0,0 +1,22 @@
+// eslint-disable-next-line import/prefer-default-export
+export const NoImageIcon = () => (
+
+)
diff --git a/web/src/index.js b/web/src/index.js
deleted file mode 100644
index b01d9fe..0000000
--- a/web/src/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react'
-import ReactDOM from 'react-dom'
-import './index.css'
-import App from './App'
-
-ReactDOM.render(
-
-
- ,
- document.getElementById('root')
-)
diff --git a/web/src/index.jsx b/web/src/index.jsx
new file mode 100644
index 0000000..52bdd23
--- /dev/null
+++ b/web/src/index.jsx
@@ -0,0 +1,12 @@
+import { StrictMode } from 'react'
+import ReactDOM from 'react-dom'
+
+import './index.css'
+import App from './App'
+
+ReactDOM.render(
+
+
+ ,
+ document.getElementById('root'),
+)
diff --git a/web/src/utils/Hosts.js b/web/src/utils/Hosts.js
index 0522d7d..4970306 100644
--- a/web/src/utils/Hosts.js
+++ b/web/src/utils/Hosts.js
@@ -1,17 +1,17 @@
-export var torrserverHost = ''
-// export var torrserverHost = 'http://127.0.0.1:8090'
+let torrserverHost = process.env.REACT_APP_SERVER_HOST || ''
-export const torrentsHost = () => torrserverHost + '/torrents'
-export const viewedHost = () => torrserverHost + '/viewed'
-export const cacheHost = () => torrserverHost + '/cache'
-export const torrentUploadHost = () => torrserverHost + '/torrent/upload'
-export const settingsHost = () => torrserverHost + '/settings'
-export const streamHost = () => torrserverHost + '/stream'
-export const shutdownHost = () => torrserverHost + '/shutdown'
-export const echoHost = () => torrserverHost + '/echo'
-export const playlistAllHost = () => torrserverHost + '/playlistall/all.m3u'
-export const playlistTorrHost = () => torrserverHost + '/stream'
+export const torrentsHost = () => `${torrserverHost}/torrents`
+export const viewedHost = () => `${torrserverHost}/viewed`
+export const cacheHost = () => `${torrserverHost}/cache`
+export const torrentUploadHost = () => `${torrserverHost}/torrent/upload`
+export const settingsHost = () => `${torrserverHost}/settings`
+export const streamHost = () => `${torrserverHost}/stream`
+export const shutdownHost = () => `${torrserverHost}/shutdown`
+export const echoHost = () => `${torrserverHost}/echo`
+export const playlistAllHost = () => `${torrserverHost}/playlistall/all.m3u`
+export const playlistTorrHost = () => `${torrserverHost}/stream`
-export const setTorrServerHost = (host) => {
- torrserverHost = host
+export const getTorrServerHost = () => torrserverHost
+export const setTorrServerHost = host => {
+ torrserverHost = host
}
diff --git a/web/src/utils/Utils.js b/web/src/utils/Utils.js
index 9e3bd89..766489f 100644
--- a/web/src/utils/Utils.js
+++ b/web/src/utils/Utils.js
@@ -1,10 +1,10 @@
export function humanizeSize(size) {
- if (!size) return ''
- var i = Math.floor(Math.log(size) / Math.log(1024))
- return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]
+ if (!size) return ''
+ const i = Math.floor(Math.log(size) / Math.log(1024))
+ return `${(size / Math.pow(1024, i)).toFixed(2) * 1} ${['B', 'kB', 'MB', 'GB', 'TB'][i]}`
}
export function getPeerString(torrent) {
- if (!torrent || !torrent.connected_seeders) return ''
- return '[' + torrent.connected_seeders + '] ' + torrent.active_peers + ' / ' + torrent.total_peers
+ if (!torrent || !torrent.connected_seeders) return ''
+ return `[${torrent.connected_seeders}] ${torrent.active_peers} / ${torrent.total_peers}`
}