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 Settings\n \n \n {show && (\n <>\n \n } label=\"Preload buffer\" />\n \n

\n Retracker mode\n \n \n } label=\"Enable IPv6\" />\n
\n } label=\"Force encrypt\" />\n
\n } label=\"Disable TCP\" />\n
\n } label=\"Disable UTP\" />\n
\n } label=\"Disable UPNP\" />\n
\n } label=\"Disable DHT\" />\n
\n } label=\"Disable PEX\" />\n
\n } label=\"Disable upload\" />\n
\n \n \n \n \n \n
\n } label=\"Use disk\" />\n
\n \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 {file.path.split('\\\\').pop().split('/').pop()} | {humanizeSize(file.length)} {viewed && viewed.indexOf(file.id)!=-1 && \"| ✓\"}\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)}}> - - - - - - {setOpen(false)}} aria-labelledby="form-dialog-title" fullWidth={true} maxWidth={'lg'}> - About - - - -

Thanks to everyone who tested and helped.


-

Special thanks:

- Anacrolix Matt Joiner github.com/anacrolix
- tsynik nikk Никита github.com/tsynik
- Tw1cker Руслан Пахнев github.com/Nemiroff
- SpAwN_LMG
-
-
-
- - - -
-
- ) -} \ 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)}> + + + + + + + setOpen(false)} aria-labelledby='form-dialog-title' fullWidth maxWidth='lg'> + About + + + + +
+

Thanks to everyone who tested and helped.

+
+
+

Special thanks:

+ Anacrolix Matt Joiner github.com/anacrolix +
+ tsynik nikk Никита github.com/tsynik +
+ Tw1cker Руслан Пахнев github.com/Nemiroff +
+ SpAwN_LMG +
+
+
+
+ + + + +
+
+ ) +} 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 ( - - Add magnet or link to torrent file - - - - - - - - - - - - - - ) -} \ 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 ( + + Add magnet or link to torrent file + + + + + + + + + + + + + + ) +} 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 ( - - Donate - - - - - - - - - -
- - - - - - - -
- ) -} \ 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 ( + + Donate + + + + + + + + + + {/* eslint-disable-next-line react/no-danger */} +
+ + + + + + + +
+ ) +} 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 ( -
- - - - - - - - Settings - - - {show && ( - <> - - } label="Preload buffer" /> - -

- Retracker mode - - - } label="Enable IPv6" /> -
- } label="Force encrypt" /> -
- } label="Disable TCP" /> -
- } label="Disable UTP" /> -
- } label="Disable UPNP" /> -
- } label="Disable DHT" /> -
- } label="Disable PEX" /> -
- } label="Disable upload" /> -
- - - - - -
- } label="Use disk" /> -
- - - )} - - - - - -

-
- ) -} - 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 ( +
+ + + + + + + + + Settings + + + {show && ( + <> + + + } + label='Preload buffer' + /> + +
+
+ Retracker mode + + + } + label='Enable IPv6' + /> +
+ + } + label='Force encrypt' + /> +
+ } + label='Disable TCP' + /> +
+ } + label='Disable UTP' + /> +
+ + } + label='Disable UPNP' + /> +
+ } + label='Disable DHT' + /> +
+ } + label='Disable PEX' + /> +
+ + } + label='Disable upload' + /> +
+ + + + + +
+ } + label='Use disk' + /> +
+ + + )} +
+ + + + + + +
+
+ ) +} 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 - ? 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) || '---'} - - - - setOpen(false)} - aria-labelledby="form-dialog-title" - fullWidth - maxWidth={'lg'} - > - {!showCache ? : } - - - - - - ) -} - -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 ? 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) || '---'} + + + + + setOpen(false)} aria-labelledby='form-dialog-title' fullWidth maxWidth='lg'> + {!showCache ? ( + + ) : ( + + )} + + + + + + ) +} + +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}` }