Merge branch 'react' into 'master'
React Add some files and dir for react-app See merge request !1
Showing
22 changed files
with
515 additions
and
2 deletions
1 | -.idea | 1 | +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. |
2 | -node_modules | 2 | + |
3 | +# dependencies | ||
4 | +/frontend/node_modules | ||
5 | +/.pnp | ||
6 | +.pnp.js | ||
7 | + | ||
8 | +# testing | ||
9 | +/coverage | ||
10 | + | ||
11 | +# production | ||
12 | +/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/package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
frontend/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "khuwitch", | ||
3 | + "version": "0.1.0", | ||
4 | + "private": true, | ||
5 | + "dependencies": { | ||
6 | + "@material-ui/core": "^4.11.1", | ||
7 | + "@material-ui/icons": "^4.9.1", | ||
8 | + "@testing-library/jest-dom": "^5.11.4", | ||
9 | + "@testing-library/react": "^11.1.0", | ||
10 | + "@testing-library/user-event": "^12.1.10", | ||
11 | + "react": "^17.0.1", | ||
12 | + "react-dom": "^17.0.1", | ||
13 | + "react-scripts": "4.0.1", | ||
14 | + "web-vitals": "^0.2.4" | ||
15 | + }, | ||
16 | + "scripts": { | ||
17 | + "start": "react-scripts start", | ||
18 | + "build": "react-scripts build", | ||
19 | + "test": "react-scripts test", | ||
20 | + "eject": "react-scripts eject" | ||
21 | + }, | ||
22 | + "eslintConfig": { | ||
23 | + "extends": [ | ||
24 | + "react-app", | ||
25 | + "react-app/jest" | ||
26 | + ] | ||
27 | + }, | ||
28 | + "browserslist": { | ||
29 | + "production": [ | ||
30 | + ">0.2%", | ||
31 | + "not dead", | ||
32 | + "not op_mini all" | ||
33 | + ], | ||
34 | + "development": [ | ||
35 | + "last 1 chrome version", | ||
36 | + "last 1 firefox version", | ||
37 | + "last 1 safari version" | ||
38 | + ] | ||
39 | + } | ||
40 | +} |
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>Khuwitch</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.css
0 → 100644
1 | +.App { | ||
2 | + text-align: center; | ||
3 | +} | ||
4 | + | ||
5 | +.App-logo { | ||
6 | + height: 40vmin; | ||
7 | + pointer-events: none; | ||
8 | +} | ||
9 | + | ||
10 | +@media (prefers-reduced-motion: no-preference) { | ||
11 | + .App-logo { | ||
12 | + animation: App-logo-spin infinite 20s linear; | ||
13 | + } | ||
14 | +} | ||
15 | + | ||
16 | +.App-header { | ||
17 | + background-color: #282c34; | ||
18 | + min-height: 100vh; | ||
19 | + display: flex; | ||
20 | + flex-direction: column; | ||
21 | + align-items: center; | ||
22 | + justify-content: center; | ||
23 | + font-size: calc(10px + 2vmin); | ||
24 | + color: white; | ||
25 | +} | ||
26 | + | ||
27 | +.App-link { | ||
28 | + color: #61dafb; | ||
29 | +} | ||
30 | + | ||
31 | +@keyframes App-logo-spin { | ||
32 | + from { | ||
33 | + transform: rotate(0deg); | ||
34 | + } | ||
35 | + to { | ||
36 | + transform: rotate(360deg); | ||
37 | + } | ||
38 | +} |
frontend/src/App.js
0 → 100644
frontend/src/App.test.js
0 → 100644
frontend/src/Body.css
0 → 100644
frontend/src/Body.js
0 → 100644
frontend/src/Channel.css
0 → 100644
1 | +.channel__list { | ||
2 | + display: flex; | ||
3 | + flex-grow: 1; | ||
4 | + flex-direction: column; | ||
5 | + flex: 0.3; | ||
6 | + min-width: 500px; | ||
7 | + height: 100vh; | ||
8 | + color: white; | ||
9 | + background-color: #0e0e0e; | ||
10 | + box-sizing: border-box; | ||
11 | +} | ||
12 | + | ||
13 | +.channel__list__title { | ||
14 | + display: flex; | ||
15 | + padding: 25px; | ||
16 | + width: 30vw; | ||
17 | + min-width: 500px; | ||
18 | + background-color: #0e0e0e; | ||
19 | + font-size: 20px; | ||
20 | + font-weight: bold; | ||
21 | + position: fixed; | ||
22 | + box-sizing: border-box; | ||
23 | +} | ||
24 | + | ||
25 | +.channel__list > ul { | ||
26 | + list-style: none; | ||
27 | + overflow: auto; | ||
28 | + padding-top: 50px; | ||
29 | +} | ||
30 | + | ||
31 | +.channel__list > ul > li > hr { | ||
32 | + border: 2px solid #282828; | ||
33 | +} | ||
34 | + | ||
35 | +.channel { | ||
36 | + display: flex; | ||
37 | + align-items: center; | ||
38 | + flex-direction: row; | ||
39 | + height: 100px; | ||
40 | + background-color: #0e0e0e; | ||
41 | +} | ||
42 | + | ||
43 | +.channel__url { | ||
44 | + display: flex; | ||
45 | + align-items: center; | ||
46 | + flex-grow: 1; | ||
47 | + text-decoration: none; | ||
48 | + font-size: 14px; | ||
49 | + color:white; | ||
50 | + box-sizing: border-box; | ||
51 | + padding-top: 20px; | ||
52 | + padding-bottom: 20px; | ||
53 | +} | ||
54 | + | ||
55 | +.channel__box { | ||
56 | + display: flex; | ||
57 | + flex-basis: 80%; | ||
58 | + flex-direction: row; | ||
59 | + justify-content: space-between; | ||
60 | +} | ||
61 | + | ||
62 | +.channel__thumbnail { | ||
63 | + height: 25px; | ||
64 | + width: 25px; | ||
65 | + border-radius: 70%; | ||
66 | + overflow: hidden; | ||
67 | + margin-right: 30px; | ||
68 | + background-color: white; | ||
69 | +} | ||
70 | + | ||
71 | +.channel__icon { | ||
72 | + height: 25px; | ||
73 | + width: 25px; | ||
74 | + border-radius: 70%; | ||
75 | + overflow: hidden; | ||
76 | + margin-right: 30px; | ||
77 | +} | ||
78 | + | ||
79 | +.channel__info { | ||
80 | + flex-basis: 70%; | ||
81 | + margin-right: 20px; | ||
82 | +} | ||
83 | + | ||
84 | +.channel__name { | ||
85 | + align-content: center; | ||
86 | + font-size: 16px; | ||
87 | +} | ||
88 | + | ||
89 | +.channel__game { | ||
90 | + font-size: 12px; | ||
91 | + color: gray; | ||
92 | +} | ||
93 | + | ||
94 | +.channel__view { | ||
95 | + text-align: right; | ||
96 | +} | ||
97 | + | ||
98 | +.play__icon { | ||
99 | + margin-left: 10px; | ||
100 | + margin-right: 10px; | ||
101 | +} | ||
102 | + | ||
103 | +.pause__icon { | ||
104 | + margin-left: 10px; | ||
105 | + margin-right: 10px; | ||
106 | +} | ||
107 | + | ||
108 | + | ||
109 | + | ||
110 | + |
frontend/src/Channel.js
0 → 100644
1 | +import React from "react"; | ||
2 | +import "./Channel.css"; | ||
3 | +import "@material-ui/icons" | ||
4 | +import PersonIcon from "@material-ui/icons/Person"; | ||
5 | +import PlayArrowIcon from '@material-ui/icons/PlayArrow'; | ||
6 | +import PauseIcon from '@material-ui/icons/Pause'; | ||
7 | +import { WatchOutlined } from "@material-ui/icons"; | ||
8 | + | ||
9 | +export function Channel(props) { | ||
10 | + const channel = props.channel; | ||
11 | + | ||
12 | + return ( | ||
13 | + <div className="channel"> | ||
14 | + <a href={channel.url} className="channel__url"> | ||
15 | + {channel.thumbnail && <img className="channel__thumbnail" src={channel.thumbnail} alt=""></img>} | ||
16 | + {!channel.thumbnail && <PersonIcon className="channel__icon"/>} | ||
17 | + <div className="channel__box"> | ||
18 | + <div className="channel__info"> | ||
19 | + <div className="channel__name">{channel.name}</div> | ||
20 | + <div className="channel__game">{channel.game}</div> | ||
21 | + </div> | ||
22 | + <div className="channel__view">{channel.view}</div> | ||
23 | + </div> | ||
24 | + </a> | ||
25 | + {channel.isPlay && <PauseIcon className="pause__icon" onClick={props.onClick} />} | ||
26 | + {!channel.isPlay && <PlayArrowIcon className="play__icon" onClick={props.onClick} />} | ||
27 | + </div> | ||
28 | + ); | ||
29 | +} | ||
30 | + | ||
31 | +export class ChannelList extends React.Component { | ||
32 | + constructor(props) { | ||
33 | + super(props); | ||
34 | + | ||
35 | + // data for test | ||
36 | + const test = [ | ||
37 | + {"name": "name1", "view": 999, "game": "game1" ,"url": "https://www.twitch.tv", "thumbnail": "https://upload.wikimedia.org/wikipedia/commons/2/26/Twitch_logo.svg"}, | ||
38 | + {"name": "name2", "view": 123124124, "game": "game2" , "url": "https://www.twitch.tv"}, | ||
39 | + {"name": "name1", "view": 999, "game": "game1" ,"url": "https://www.twitch.tv", "thumbnail": "https://upload.wikimedia.org/wikipedia/commons/2/26/Twitch_logo.svg"}, | ||
40 | + {"name": "name2", "view": 123124124, "game": "game2" , "url": "https://www.twitch.tv"}, | ||
41 | + {"name": "name1", "view": 999, "game": "game1" ,"url": "https://www.twitch.tv", "thumbnail": "https://upload.wikimedia.org/wikipedia/commons/2/26/Twitch_logo.svg"}, | ||
42 | + {"name": "name2", "view": 123124124, "game": "game2" , "url": "https://www.twitch.tv"}, | ||
43 | + {"name": "name1", "view": 999, "game": "game1" ,"url": "https://www.twitch.tv", "thumbnail": "https://upload.wikimedia.org/wikipedia/commons/2/26/Twitch_logo.svg"}, | ||
44 | + {"name": "name2", "view": 123124124, "game": "game2" , "url": "https://www.twitch.tv"}, | ||
45 | + {"name": "name1", "view": 999, "game": "game1" ,"url": "https://www.twitch.tv", "thumbnail": "https://upload.wikimedia.org/wikipedia/commons/2/26/Twitch_logo.svg"}, | ||
46 | + {"name": "name2", "view": 123124124, "game": "game2" , "url": "https://www.twitch.tv"}, | ||
47 | + ] | ||
48 | + | ||
49 | + this.state = { | ||
50 | + channels: test, | ||
51 | + xisPlaying: null, | ||
52 | + } | ||
53 | + } | ||
54 | + | ||
55 | + changeState(channel, index){ | ||
56 | + const channels = this.state.channels; | ||
57 | + // 버튼을 누른 채널이 재생중일때 | ||
58 | + if (channel.isPlay === true) { | ||
59 | + channels[index].isPlay = false; | ||
60 | + this.setState({ | ||
61 | + channels: channels, | ||
62 | + xisPlaying: null, | ||
63 | + }); | ||
64 | + } | ||
65 | + | ||
66 | + // 버튼을 누른 채널이 재생중이 아닐 때 | ||
67 | + else { | ||
68 | + // xisPlaying이 null이 아닐 때 | ||
69 | + if (this.state.xisPlaying !== null) { | ||
70 | + // 기존에 재생되던 채널의 isPlay를 false로, 클릭 이벤트가 발생한 채널의 isPlay를 true로 변경 | ||
71 | + channels[this.state.xisPlaying].isPlay = false; | ||
72 | + channels[index].isPlay = true; | ||
73 | + } | ||
74 | + | ||
75 | + // xisPlaying이 null일 때 | ||
76 | + else { | ||
77 | + channels[index].isPlay = true; | ||
78 | + } | ||
79 | + | ||
80 | + this.setState({ | ||
81 | + channels: channels, | ||
82 | + xisPlaying: index, | ||
83 | + }); | ||
84 | + } | ||
85 | + } | ||
86 | + | ||
87 | + render() { | ||
88 | + return ( | ||
89 | + <div className="channel__list"> | ||
90 | + <div className="channel__list__title"> CHANNEL LIST</div> | ||
91 | + <ul> | ||
92 | + {this.state.channels.map((channel, index) => ( | ||
93 | + <li key={index}> | ||
94 | + <Channel | ||
95 | + channel={channel} | ||
96 | + onClick={ () => this.changeState(channel, index)} | ||
97 | + /> | ||
98 | + <hr /> | ||
99 | + </li> | ||
100 | + ))} | ||
101 | + </ul> | ||
102 | + </div> | ||
103 | + | ||
104 | + ); | ||
105 | + } | ||
106 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
frontend/src/Login.css
0 → 100644
1 | +.login { | ||
2 | + display: grid; | ||
3 | + background-color: #0e0e10; | ||
4 | + height: 100vh; | ||
5 | + place-items: center; | ||
6 | +} | ||
7 | + | ||
8 | +.login > img { | ||
9 | + height: 300px; | ||
10 | +} | ||
11 | + | ||
12 | +.login > a { | ||
13 | + color: white; | ||
14 | + padding: 30px; | ||
15 | + border-radius: 100px; | ||
16 | + text-decoration: none; | ||
17 | + font-size: 30px; | ||
18 | + font-weight: bold; | ||
19 | + background-color: #9147ff; | ||
20 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
frontend/src/Login.js
0 → 100644
1 | +import React from "react"; | ||
2 | +import "./Login.css"; | ||
3 | + | ||
4 | +function Login(){ | ||
5 | + const OAuthUrl = ""; // oauth 인증용 url | ||
6 | + return ( | ||
7 | + <div className="login"> | ||
8 | + <img | ||
9 | + src="https://upload.wikimedia.org/wikipedia/commons/2/26/Twitch_logo.svg" | ||
10 | + alt="" | ||
11 | + /> | ||
12 | + <a href={OAuthUrl}>Login to Twitch</a> | ||
13 | + </div> | ||
14 | + ); | ||
15 | +} | ||
16 | + | ||
17 | +export default Login; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
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.js
0 → 100644
1 | +import React from 'react'; | ||
2 | +import ReactDOM from 'react-dom'; | ||
3 | +import './index.css'; | ||
4 | +import App from './App'; | ||
5 | +import reportWebVitals from './reportWebVitals'; | ||
6 | + | ||
7 | +ReactDOM.render( | ||
8 | + <React.StrictMode> | ||
9 | + <App /> | ||
10 | + </React.StrictMode>, | ||
11 | + document.getElementById('root') | ||
12 | +); | ||
13 | + | ||
14 | +// If you want to start measuring performance in your app, pass a function | ||
15 | +// to log results (for example: reportWebVitals(console.log)) | ||
16 | +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals | ||
17 | +reportWebVitals(); |
frontend/src/reportWebVitals.js
0 → 100644
1 | +const reportWebVitals = onPerfEntry => { | ||
2 | + if (onPerfEntry && onPerfEntry instanceof Function) { | ||
3 | + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { | ||
4 | + getCLS(onPerfEntry); | ||
5 | + getFID(onPerfEntry); | ||
6 | + getFCP(onPerfEntry); | ||
7 | + getLCP(onPerfEntry); | ||
8 | + getTTFB(onPerfEntry); | ||
9 | + }); | ||
10 | + } | ||
11 | +}; | ||
12 | + | ||
13 | +export default reportWebVitals; |
-
Please register or login to post a comment