Showing
21 changed files
with
475 additions
and
0 deletions
.gitignore
0 → 100644
1 | +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
2 | + | ||
3 | +# dependencies | ||
4 | +/frontend/node_modules | ||
5 | +/frontend/.pnp | ||
6 | +.pnp.js | ||
7 | + | ||
8 | +# testing | ||
9 | +/frontend/coverage | ||
10 | + | ||
11 | +# production | ||
12 | +/frontend/build | ||
13 | + | ||
14 | +# misc | ||
15 | +.DS_Store | ||
16 | +.env.local | ||
17 | +.env.development.local | ||
18 | +.env.test.local | ||
19 | +.env.production.local | ||
20 | + | ||
21 | +npm-debug.log* | ||
22 | +yarn-debug.log* | ||
23 | +yarn-error.log* |
frontend/.env.development
0 → 100644
1 | +PORT=38000 | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
frontend/.env.production
0 → 100644
frontend/.eslintrc.json
0 → 100644
1 | +{ | ||
2 | + "parser": "@typescript-eslint/parser", | ||
3 | + "parserOptions": { | ||
4 | + "project": "tsconfig.json", | ||
5 | + "sourceType": "module" | ||
6 | + }, | ||
7 | + "plugins": ["@typescript-eslint/eslint-plugin"], | ||
8 | + "extends": [ | ||
9 | + "react-app", | ||
10 | + "eslint:recommended", | ||
11 | + "plugin:react/recommended", | ||
12 | + "plugin:jest/recommended", | ||
13 | + "plugin:@typescript-eslint/eslint-recommended", | ||
14 | + "plugin:@typescript-eslint/recommended", | ||
15 | + "prettier", | ||
16 | + "prettier/@typescript-eslint" | ||
17 | + ], | ||
18 | + "rules": { | ||
19 | + "no-console": "warn", | ||
20 | + "id-length": "warn", | ||
21 | + "react/display-name": "off", | ||
22 | + "react/prop-types": "off", | ||
23 | + "no-empty": ["warn", { "allowEmptyCatch": true }], | ||
24 | + "@typescript-eslint/explicit-function-return-type": "off", | ||
25 | + "@typescript-eslint/explicit-member-accessibility": "off", | ||
26 | + "@typescript-eslint/interface-name-prefix": "off", | ||
27 | + "@typescript-eslint/no-explicit-any": "off", | ||
28 | + "@typescript-eslint/no-inferrable-types": "off", | ||
29 | + "@typescript-eslint/no-non-null-assertion": "warn", | ||
30 | + "@typescript-eslint/no-empty-function": "off" | ||
31 | + }, | ||
32 | + "overrides": [ | ||
33 | + { | ||
34 | + "files": "config-overrides.js", | ||
35 | + "parser": "espree", | ||
36 | + "rules": { | ||
37 | + "@typescript-eslint/no-var-requires": "off" | ||
38 | + } | ||
39 | + } | ||
40 | + ] | ||
41 | +} |
frontend/.prettierrc.json
0 → 100644
frontend/config-overrides.js
0 → 100644
1 | +const { | ||
2 | + override, | ||
3 | + addBabelPlugin, | ||
4 | + addBundleVisualizer, | ||
5 | +} = require("customize-cra"); | ||
6 | + | ||
7 | +const findSassModuleRule = (config) => { | ||
8 | + let sassModuleRuleIndex; | ||
9 | + const loaders = config.module.rules.find((ruleItem) => { | ||
10 | + if (!ruleItem.oneOf || !ruleItem.oneOf.length) { | ||
11 | + return false; | ||
12 | + } | ||
13 | + sassModuleRuleIndex = ruleItem.oneOf.findIndex( | ||
14 | + (loaderItem) => | ||
15 | + loaderItem.test && | ||
16 | + loaderItem.test.toString() === "/\\.module\\.(scss|sass)$/" | ||
17 | + ); | ||
18 | + return sassModuleRuleIndex !== -1; | ||
19 | + }); | ||
20 | + | ||
21 | + const sassModuleRule = loaders.oneOf[sassModuleRuleIndex]; | ||
22 | + return sassModuleRule; | ||
23 | +}; | ||
24 | + | ||
25 | +const setCssModuleLocalIdentName = (localIdentName) => (config) => { | ||
26 | + const sassModuleRule = findSassModuleRule(config); | ||
27 | + const cssLoaderOption = (sassModuleRule.use || sassModuleRule.loader).find( | ||
28 | + (loaderItem) => | ||
29 | + loaderItem.loader && loaderItem.loader.includes("/css-loader/") | ||
30 | + ); | ||
31 | + | ||
32 | + cssLoaderOption.options.modules = { localIdentName }; | ||
33 | + return config; | ||
34 | +}; | ||
35 | + | ||
36 | +const setupReactHotLoader = () => (config) => { | ||
37 | + config = addBabelPlugin("react-hot-loader/babel")(config); | ||
38 | + config.entry = ["react-hot-loader/patch", ...config.entry]; | ||
39 | + | ||
40 | + if (!config.resolve.alias) { | ||
41 | + config.resolve.alias = {}; | ||
42 | + } | ||
43 | + config.resolve.alias["react-dom"] = "@hot-loader/react-dom"; | ||
44 | + | ||
45 | + return config; | ||
46 | +}; | ||
47 | + | ||
48 | +if (process.env.NODE_ENV === "development") { | ||
49 | + module.exports = override(setupReactHotLoader()); | ||
50 | +} else { | ||
51 | + module.exports = override( | ||
52 | + setCssModuleLocalIdentName("[hash:base64:5]"), | ||
53 | + addBundleVisualizer({}, true) | ||
54 | + ); | ||
55 | +} |
frontend/package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
frontend/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "khudrive", | ||
3 | + "version": "0.1.0", | ||
4 | + "description": "Dropbox alternative cloud file service", | ||
5 | + "private": true, | ||
6 | + "dependencies": { | ||
7 | + "classnames": "^2.2.6", | ||
8 | + "ky": "^0.19.1", | ||
9 | + "react": "^16.13.1", | ||
10 | + "react-dom": "^16.13.1", | ||
11 | + "react-router-dom": "^5.1.2" | ||
12 | + }, | ||
13 | + "devDependencies": { | ||
14 | + "@hot-loader/react-dom": "^16.13.0", | ||
15 | + "@testing-library/jest-dom": "^5.7.0", | ||
16 | + "@testing-library/react": "^10.0.4", | ||
17 | + "@testing-library/user-event": "^10.1.2", | ||
18 | + "@types/classnames": "^2.2.10", | ||
19 | + "@types/jest": "^25.2.1", | ||
20 | + "@types/node": "12", | ||
21 | + "@types/react": "^16.9.35", | ||
22 | + "@types/react-dom": "^16.9.8", | ||
23 | + "@types/react-router-dom": "^5.1.5", | ||
24 | + "@typescript-eslint/eslint-plugin": "^2.31.0", | ||
25 | + "@typescript-eslint/parser": "^2.31.0", | ||
26 | + "customize-cra": "0.9.1", | ||
27 | + "eslint-config-prettier": "^6.11.0", | ||
28 | + "eslint-plugin-jest": "^23.10.0", | ||
29 | + "husky": "^4.2.5", | ||
30 | + "lint-staged": "^10.2.2", | ||
31 | + "node-sass": "^4.14.1", | ||
32 | + "prettier": "^2.0.5", | ||
33 | + "react-app-rewired": "^2.1.6", | ||
34 | + "react-hot-loader": "^4.12.21", | ||
35 | + "react-scripts": "3.4.1", | ||
36 | + "typescript": "^3.8.3", | ||
37 | + "webpack-bundle-analyzer": "^3.7.0" | ||
38 | + }, | ||
39 | + "scripts": { | ||
40 | + "start": "react-app-rewired start", | ||
41 | + "build": "react-app-rewired build", | ||
42 | + "analyze": "npm run build -- --analyze", | ||
43 | + "lint": "eslint --ext js,jsx,ts,tsx src", | ||
44 | + "prettier": "prettier --write \"src/**/*.{js,ts,tsx,json,css,scss}\"", | ||
45 | + "test": "react-app-rewired test" | ||
46 | + }, | ||
47 | + "browserslist": { | ||
48 | + "production": [ | ||
49 | + ">0.2%", | ||
50 | + "not dead", | ||
51 | + "not op_mini all" | ||
52 | + ], | ||
53 | + "development": [ | ||
54 | + "last 1 chrome version", | ||
55 | + "last 1 firefox version", | ||
56 | + "last 1 safari version" | ||
57 | + ] | ||
58 | + }, | ||
59 | + "husky": { | ||
60 | + "hooks": { | ||
61 | + "pre-commit": "lint-staged" | ||
62 | + } | ||
63 | + }, | ||
64 | + "lint-staged": { | ||
65 | + "*.{js,ts,tsx,json,css,scss}": "prettier --write" | ||
66 | + } | ||
67 | +} |
frontend/public/favicon.ico
0 → 100644
No preview for this file type
frontend/public/index.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="en"> | ||
3 | + <head> | ||
4 | + <meta charset="utf-8" /> | ||
5 | + <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
7 | + <meta name="theme-color" content="#000000" /> | ||
8 | + <meta | ||
9 | + name="description" | ||
10 | + content="Web site created using create-react-app" | ||
11 | + /> | ||
12 | + <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> | ||
13 | + <!-- | ||
14 | + manifest.json provides metadata used when your web app is installed on a | ||
15 | + user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ | ||
16 | + --> | ||
17 | + <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> | ||
18 | + <!-- | ||
19 | + Notice the use of %PUBLIC_URL% in the tags above. | ||
20 | + It will be replaced with the URL of the `public` folder during the build. | ||
21 | + Only files inside the `public` folder can be referenced from the HTML. | ||
22 | + | ||
23 | + Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will | ||
24 | + work correctly both with client-side routing and a non-root public URL. | ||
25 | + Learn how to configure a non-root public URL by running `npm run build`. | ||
26 | + --> | ||
27 | + <title>React App</title> | ||
28 | + </head> | ||
29 | + <body> | ||
30 | + <noscript>You need to enable JavaScript to run this app.</noscript> | ||
31 | + <div id="root"></div> | ||
32 | + <!-- | ||
33 | + This HTML file is a template. | ||
34 | + If you open it directly in the browser, you will see an empty page. | ||
35 | + | ||
36 | + You can add webfonts, meta tags, or analytics to this file. | ||
37 | + The build step will place the bundled scripts into the <body> tag. | ||
38 | + | ||
39 | + To begin the development, run `npm start` or `yarn start`. | ||
40 | + To create a production bundle, use `npm run build` or `yarn build`. | ||
41 | + --> | ||
42 | + </body> | ||
43 | +</html> |
frontend/public/logo192.png
0 → 100644
5.22 KB
frontend/public/logo512.png
0 → 100644
9.44 KB
frontend/public/manifest.json
0 → 100644
1 | +{ | ||
2 | + "short_name": "React App", | ||
3 | + "name": "Create React App Sample", | ||
4 | + "icons": [ | ||
5 | + { | ||
6 | + "src": "favicon.ico", | ||
7 | + "sizes": "64x64 32x32 24x24 16x16", | ||
8 | + "type": "image/x-icon" | ||
9 | + }, | ||
10 | + { | ||
11 | + "src": "logo192.png", | ||
12 | + "type": "image/png", | ||
13 | + "sizes": "192x192" | ||
14 | + }, | ||
15 | + { | ||
16 | + "src": "logo512.png", | ||
17 | + "type": "image/png", | ||
18 | + "sizes": "512x512" | ||
19 | + } | ||
20 | + ], | ||
21 | + "start_url": ".", | ||
22 | + "display": "standalone", | ||
23 | + "theme_color": "#000000", | ||
24 | + "background_color": "#ffffff" | ||
25 | +} |
frontend/public/robots.txt
0 → 100644
frontend/src/App.tsx
0 → 100644
frontend/src/index.css
0 → 100644
1 | +body { | ||
2 | + margin: 0; | ||
3 | + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", | ||
4 | + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", | ||
5 | + sans-serif; | ||
6 | + -webkit-font-smoothing: antialiased; | ||
7 | + -moz-osx-font-smoothing: grayscale; | ||
8 | +} | ||
9 | + | ||
10 | +code { | ||
11 | + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", | ||
12 | + monospace; | ||
13 | +} |
frontend/src/index.tsx
0 → 100644
1 | +import React from "react"; | ||
2 | +import ReactDOM from "react-dom"; | ||
3 | + | ||
4 | +import "./index.css"; | ||
5 | + | ||
6 | +import { App } from "./App"; | ||
7 | + | ||
8 | +import * as serviceWorker from "./serviceWorker"; | ||
9 | + | ||
10 | +ReactDOM.render( | ||
11 | + <React.StrictMode> | ||
12 | + <App /> | ||
13 | + </React.StrictMode>, | ||
14 | + document.getElementById("root") | ||
15 | +); | ||
16 | + | ||
17 | +// If you want your app to work offline and load faster, you can change | ||
18 | +// unregister() to register() below. Note this comes with some pitfalls. | ||
19 | +// Learn more about service workers: https://bit.ly/CRA-PWA | ||
20 | +serviceWorker.unregister(); |
frontend/src/react-app-env.d.ts
0 → 100644
1 | +/// <reference types="react-scripts" /> |
frontend/src/serviceWorker.ts
0 → 100644
1 | +// This optional code is used to register a service worker. | ||
2 | +// register() is not called by default. | ||
3 | + | ||
4 | +// This lets the app load faster on subsequent visits in production, and gives | ||
5 | +// it offline capabilities. However, it also means that developers (and users) | ||
6 | +// will only see deployed updates on subsequent visits to a page, after all the | ||
7 | +// existing tabs open on the page have been closed, since previously cached | ||
8 | +// resources are updated in the background. | ||
9 | + | ||
10 | +// To learn more about the benefits of this model and instructions on how to | ||
11 | +// opt-in, read https://bit.ly/CRA-PWA | ||
12 | + | ||
13 | +const isLocalhost = Boolean( | ||
14 | + window.location.hostname === "localhost" || | ||
15 | + // [::1] is the IPv6 localhost address. | ||
16 | + window.location.hostname === "[::1]" || | ||
17 | + // 127.0.0.0/8 are considered localhost for IPv4. | ||
18 | + window.location.hostname.match( | ||
19 | + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ | ||
20 | + ) | ||
21 | +); | ||
22 | + | ||
23 | +type Config = { | ||
24 | + onSuccess?: (registration: ServiceWorkerRegistration) => void; | ||
25 | + onUpdate?: (registration: ServiceWorkerRegistration) => void; | ||
26 | +}; | ||
27 | + | ||
28 | +export function register(config?: Config) { | ||
29 | + if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { | ||
30 | + // The URL constructor is available in all browsers that support SW. | ||
31 | + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); | ||
32 | + if (publicUrl.origin !== window.location.origin) { | ||
33 | + // Our service worker won't work if PUBLIC_URL is on a different origin | ||
34 | + // from what our page is served on. This might happen if a CDN is used to | ||
35 | + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 | ||
36 | + return; | ||
37 | + } | ||
38 | + | ||
39 | + window.addEventListener("load", () => { | ||
40 | + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; | ||
41 | + | ||
42 | + if (isLocalhost) { | ||
43 | + // This is running on localhost. Let's check if a service worker still exists or not. | ||
44 | + checkValidServiceWorker(swUrl, config); | ||
45 | + | ||
46 | + // Add some additional logging to localhost, pointing developers to the | ||
47 | + // service worker/PWA documentation. | ||
48 | + navigator.serviceWorker.ready.then(() => { | ||
49 | + console.log( | ||
50 | + "This web app is being served cache-first by a service " + | ||
51 | + "worker. To learn more, visit https://bit.ly/CRA-PWA" | ||
52 | + ); | ||
53 | + }); | ||
54 | + } else { | ||
55 | + // Is not localhost. Just register service worker | ||
56 | + registerValidSW(swUrl, config); | ||
57 | + } | ||
58 | + }); | ||
59 | + } | ||
60 | +} | ||
61 | + | ||
62 | +function registerValidSW(swUrl: string, config?: Config) { | ||
63 | + navigator.serviceWorker | ||
64 | + .register(swUrl) | ||
65 | + .then((registration) => { | ||
66 | + registration.onupdatefound = () => { | ||
67 | + const installingWorker = registration.installing; | ||
68 | + if (installingWorker == null) { | ||
69 | + return; | ||
70 | + } | ||
71 | + installingWorker.onstatechange = () => { | ||
72 | + if (installingWorker.state === "installed") { | ||
73 | + if (navigator.serviceWorker.controller) { | ||
74 | + // At this point, the updated precached content has been fetched, | ||
75 | + // but the previous service worker will still serve the older | ||
76 | + // content until all client tabs are closed. | ||
77 | + console.log( | ||
78 | + "New content is available and will be used when all " + | ||
79 | + "tabs for this page are closed. See https://bit.ly/CRA-PWA." | ||
80 | + ); | ||
81 | + | ||
82 | + // Execute callback | ||
83 | + if (config && config.onUpdate) { | ||
84 | + config.onUpdate(registration); | ||
85 | + } | ||
86 | + } else { | ||
87 | + // At this point, everything has been precached. | ||
88 | + // It's the perfect time to display a | ||
89 | + // "Content is cached for offline use." message. | ||
90 | + console.log("Content is cached for offline use."); | ||
91 | + | ||
92 | + // Execute callback | ||
93 | + if (config && config.onSuccess) { | ||
94 | + config.onSuccess(registration); | ||
95 | + } | ||
96 | + } | ||
97 | + } | ||
98 | + }; | ||
99 | + }; | ||
100 | + }) | ||
101 | + .catch((error) => { | ||
102 | + console.error("Error during service worker registration:", error); | ||
103 | + }); | ||
104 | +} | ||
105 | + | ||
106 | +function checkValidServiceWorker(swUrl: string, config?: Config) { | ||
107 | + // Check if the service worker can be found. If it can't reload the page. | ||
108 | + fetch(swUrl, { | ||
109 | + headers: { "Service-Worker": "script" }, | ||
110 | + }) | ||
111 | + .then((response) => { | ||
112 | + // Ensure service worker exists, and that we really are getting a JS file. | ||
113 | + const contentType = response.headers.get("content-type"); | ||
114 | + if ( | ||
115 | + response.status === 404 || | ||
116 | + (contentType != null && contentType.indexOf("javascript") === -1) | ||
117 | + ) { | ||
118 | + // No service worker found. Probably a different app. Reload the page. | ||
119 | + navigator.serviceWorker.ready.then((registration) => { | ||
120 | + registration.unregister().then(() => { | ||
121 | + window.location.reload(); | ||
122 | + }); | ||
123 | + }); | ||
124 | + } else { | ||
125 | + // Service worker found. Proceed as normal. | ||
126 | + registerValidSW(swUrl, config); | ||
127 | + } | ||
128 | + }) | ||
129 | + .catch(() => { | ||
130 | + console.log( | ||
131 | + "No internet connection found. App is running in offline mode." | ||
132 | + ); | ||
133 | + }); | ||
134 | +} | ||
135 | + | ||
136 | +export function unregister() { | ||
137 | + if ("serviceWorker" in navigator) { | ||
138 | + navigator.serviceWorker.ready | ||
139 | + .then((registration) => { | ||
140 | + registration.unregister(); | ||
141 | + }) | ||
142 | + .catch((error) => { | ||
143 | + console.error(error.message); | ||
144 | + }); | ||
145 | + } | ||
146 | +} |
frontend/src/setupTests.ts
0 → 100644
frontend/tsconfig.json
0 → 100644
1 | +{ | ||
2 | + "compilerOptions": { | ||
3 | + "target": "es5", | ||
4 | + "module": "esnext", | ||
5 | + "lib": ["dom", "dom.iterable", "esnext"], | ||
6 | + "baseUrl": "src", | ||
7 | + "allowJs": false, | ||
8 | + "jsx": "preserve", | ||
9 | + "noEmit": true, | ||
10 | + "isolatedModules": true, | ||
11 | + "forceConsistentCasingInFileNames": true, | ||
12 | + "strict": true, | ||
13 | + "skipLibCheck": false, | ||
14 | + "moduleResolution": "node", | ||
15 | + "allowSyntheticDefaultImports": true, | ||
16 | + "esModuleInterop": true, | ||
17 | + "downlevelIteration": true, | ||
18 | + "importHelpers": true, | ||
19 | + "resolveJsonModule": true | ||
20 | + }, | ||
21 | + "include": ["src"] | ||
22 | +} |
-
Please register or login to post a comment