Showing
24 changed files
with
365 additions
and
118 deletions
assets/js/videoPlayer.js
0 → 100644
File mode changed
assets/scss/config/utils.scss
0 → 100644
assets/scss/pages/userProfile.scss
0 → 100644
1 | +.user-profile { | ||
2 | + width: 100%; | ||
3 | + display: flex; | ||
4 | + align-items: center; | ||
5 | + flex-direction: column; | ||
6 | + .user-profile__header { | ||
7 | + display: flex; | ||
8 | + flex-direction: column; | ||
9 | + align-items: center; | ||
10 | + margin-bottom: 18px; | ||
11 | + .profile__username { | ||
12 | + font-size: 18px; | ||
13 | + margin-top: 15px; | ||
14 | + } | ||
15 | + } | ||
16 | + .user-profile__btns { | ||
17 | + display: flex; | ||
18 | + flex-direction: column; | ||
19 | + align-items: center; | ||
20 | + a:not(:last-child) { | ||
21 | + margin-bottom: 20px; | ||
22 | + } | ||
23 | + } | ||
24 | +} |
... | @@ -10,6 +10,11 @@ | ... | @@ -10,6 +10,11 @@ |
10 | width: 100px; | 10 | width: 100px; |
11 | margin-bottom: 25px; | 11 | margin-bottom: 25px; |
12 | } | 12 | } |
13 | + .video__author { | ||
14 | + a { | ||
15 | + color: #3498db; | ||
16 | + } | ||
17 | + } | ||
13 | .video__title, | 18 | .video__title, |
14 | .video__views, | 19 | .video__views, |
15 | .video__description { | 20 | .video__description { | ... | ... |
assets/scss/partials/videoPlayer.scss
0 → 100644
1 | +.videoPlayer { | ||
2 | + position: relative; | ||
3 | + &:hover { | ||
4 | + .videoPlayer__controls { | ||
5 | + opacity: 1; | ||
6 | + } | ||
7 | + } | ||
8 | + video { | ||
9 | + max-width: 100%; | ||
10 | + } | ||
11 | + .videoPlayer__controls { | ||
12 | + opacity: 0; | ||
13 | + transition: opacity 0.4s linear; | ||
14 | + color: white; | ||
15 | + position: absolute; | ||
16 | + z-index: 9; | ||
17 | + bottom: 5px; | ||
18 | + width: 100%; | ||
19 | + background-color: rgba(0, 0, 0, 0.5); | ||
20 | + padding: 10px; | ||
21 | + display: grid; | ||
22 | + grid-template-columns: repeat(3, 1fr); | ||
23 | + font-size: 16px; | ||
24 | + .videoPlayer__column:first-child { | ||
25 | + display: flex; | ||
26 | + align-items: center; | ||
27 | + span:first-child { | ||
28 | + margin-right: 10px; | ||
29 | + } | ||
30 | + } | ||
31 | + .videoPlayer__column:last-child { | ||
32 | + justify-self: end; | ||
33 | + } | ||
34 | + .videoPlayer__column:nth-child(2) { | ||
35 | + justify-self: center; | ||
36 | + } | ||
37 | + i { | ||
38 | + font-size: 25px; | ||
39 | + cursor: pointer; | ||
40 | + } | ||
41 | + } | ||
42 | +} |
1 | @import "config/_variables.scss"; | 1 | @import "config/_variables.scss"; |
2 | @import "config/reset.scss"; | 2 | @import "config/reset.scss"; |
3 | +@import "config/utils.scss"; | ||
3 | @import "main.scss"; | 4 | @import "main.scss"; |
4 | 5 | ||
5 | @import "partials/header.scss"; | 6 | @import "partials/header.scss"; |
... | @@ -7,6 +8,8 @@ | ... | @@ -7,6 +8,8 @@ |
7 | @import "partials/form.scss"; | 8 | @import "partials/form.scss"; |
8 | @import "partials/socialLogin.scss"; | 9 | @import "partials/socialLogin.scss"; |
9 | @import "partials/videoBlock.scss"; | 10 | @import "partials/videoBlock.scss"; |
11 | +@import "partials/videoPlayer.scss"; | ||
10 | 12 | ||
11 | @import "pages/home.scss"; | 13 | @import "pages/home.scss"; |
12 | @import "pages/videoDetail.scss"; | 14 | @import "pages/videoDetail.scss"; |
15 | +@import "pages/userProfile.scss"; | ... | ... |
... | @@ -120,7 +120,7 @@ export const logout = (req, res) => { | ... | @@ -120,7 +120,7 @@ export const logout = (req, res) => { |
120 | 120 | ||
121 | export const getMe = (req, res) => | 121 | export const getMe = (req, res) => |
122 | res.render("userDetail", { pageTitle: "User Detail", user: req.user }); | 122 | res.render("userDetail", { pageTitle: "User Detail", user: req.user }); |
123 | - | 123 | +// req.user -> 로그인된 유저 |
124 | // export const users = (req, res) => res.render("users", { pageTitle: "Users" }); | 124 | // export const users = (req, res) => res.render("users", { pageTitle: "Users" }); |
125 | 125 | ||
126 | export const userDetail = async (req, res) => { | 126 | export const userDetail = async (req, res) => { |
... | @@ -128,13 +128,50 @@ export const userDetail = async (req, res) => { | ... | @@ -128,13 +128,50 @@ export const userDetail = async (req, res) => { |
128 | params: { id }, | 128 | params: { id }, |
129 | } = req; // req로 부터 params의 id가져오기 | 129 | } = req; // req로 부터 params의 id가져오기 |
130 | try { | 130 | try { |
131 | - const user = await User.findById(id); | 131 | + const user = await User.findById(id).populate("videos"); |
132 | res.render("userDetail", { pageTitle: "User Detail", user }); | 132 | res.render("userDetail", { pageTitle: "User Detail", user }); |
133 | } catch (error) { | 133 | } catch (error) { |
134 | res.redirect(routes.home); | 134 | res.redirect(routes.home); |
135 | } | 135 | } |
136 | }; | 136 | }; |
137 | -export const editProfile = (req, res) => | 137 | +export const getEditProfile = (req, res) => |
138 | res.render("editProfile", { pageTitle: "Edit Profile" }); | 138 | res.render("editProfile", { pageTitle: "Edit Profile" }); |
139 | -export const changePassword = (req, res) => | 139 | + |
140 | +export const postEditProfile = async (req, res) => { | ||
141 | + const { | ||
142 | + body: { name, email }, | ||
143 | + file, | ||
144 | + } = req; | ||
145 | + try { | ||
146 | + // 로그인된 user의 id 가져오면 됨 | ||
147 | + await User.findByIdAndUpdate(req.user.id, { | ||
148 | + name, | ||
149 | + email, | ||
150 | + avatarUrl: file ? file.path : req.user.avatarUrl, | ||
151 | + }); | ||
152 | + res.redirect(routes.me); | ||
153 | + } catch (error) { | ||
154 | + res.redirect(`/users${routes.editProfile}`); | ||
155 | + } | ||
156 | +}; | ||
157 | + | ||
158 | +export const getChangePassword = (req, res) => | ||
140 | res.render("changePassword", { pageTitle: "Change Password" }); | 159 | res.render("changePassword", { pageTitle: "Change Password" }); |
160 | + | ||
161 | +export const postChangePassword = async (req, res) => { | ||
162 | + const { | ||
163 | + body: { oldPassword, newPassword, newPassword1 }, | ||
164 | + } = req; | ||
165 | + try { | ||
166 | + if (newPassword !== newPassword1) { | ||
167 | + res.status(400); | ||
168 | + res.redirect(`/users${routes.changePassword}`); | ||
169 | + return; | ||
170 | + } | ||
171 | + await req.user.changePassword(oldPassword, newPassword); | ||
172 | + res.redirect(routes.me); | ||
173 | + } catch (error) { | ||
174 | + res.status(400); | ||
175 | + res.redirect(`/users${routes.changePassword}`); | ||
176 | + } | ||
177 | +}; | ... | ... |
... | @@ -46,9 +46,12 @@ export const postUpload = async (req, res) => { | ... | @@ -46,9 +46,12 @@ export const postUpload = async (req, res) => { |
46 | fileUrl: path, | 46 | fileUrl: path, |
47 | title, | 47 | title, |
48 | description, | 48 | description, |
49 | + creator: req.user.id, | ||
49 | // 여기있는 fileUrl, title, description은 videoDB의 속성이다. | 50 | // 여기있는 fileUrl, title, description은 videoDB의 속성이다. |
50 | }); | 51 | }); |
51 | - console.log(newVideo); | 52 | + // console.log(newVideo); |
53 | + req.user.videos.push(newVideo.id); // user DB의 video atribute에 추가 | ||
54 | + req.user.save(); | ||
52 | res.redirect(routes.videoDetail(newVideo.id)); // id는 DB의 id | 55 | res.redirect(routes.videoDetail(newVideo.id)); // id는 DB의 id |
53 | // id는 mongoDB가 랜덤하게 만들어준다. | 56 | // id는 mongoDB가 랜덤하게 만들어준다. |
54 | }; | 57 | }; |
... | @@ -59,7 +62,7 @@ export const videoDetail = async (req, res) => { | ... | @@ -59,7 +62,7 @@ export const videoDetail = async (req, res) => { |
59 | params: { id }, | 62 | params: { id }, |
60 | } = req; | 63 | } = req; |
61 | try { | 64 | try { |
62 | - const video = await Video.findById(id); | 65 | + const video = await Video.findById(id).populate("creator"); |
63 | res.render("videoDetail", { pageTitle: video.title, video }); | 66 | res.render("videoDetail", { pageTitle: video.title, video }); |
64 | } catch (error) { | 67 | } catch (error) { |
65 | res.redirect(routes.home); | 68 | res.redirect(routes.home); |
... | @@ -72,7 +75,11 @@ export const getEditVideo = async (req, res) => { | ... | @@ -72,7 +75,11 @@ export const getEditVideo = async (req, res) => { |
72 | try { | 75 | try { |
73 | const video = await Video.findById(id); | 76 | const video = await Video.findById(id); |
74 | // video를 받아서 render로 통해 템플릿으로 던져준다, | 77 | // video를 받아서 render로 통해 템플릿으로 던져준다, |
75 | - res.render("editVideo", { pageTitle: `Edit ${video.title}`, video }); | 78 | + if (String(video.creator) !== req.user.id) { |
79 | + throw Error(); | ||
80 | + } else { | ||
81 | + res.render("editVideo", { pageTitle: `Edit ${video.title}`, video }); | ||
82 | + } | ||
76 | // rendering하는 순간 템플릿에선 video의 title과 description을 던져준다. | 83 | // rendering하는 순간 템플릿에선 video의 title과 description을 던져준다. |
77 | } catch (error) { | 84 | } catch (error) { |
78 | res.redirect(routes.home); | 85 | res.redirect(routes.home); |
... | @@ -99,7 +106,13 @@ export const deleteVideo = async (req, res) => { | ... | @@ -99,7 +106,13 @@ export const deleteVideo = async (req, res) => { |
99 | params: { id }, | 106 | params: { id }, |
100 | } = req; | 107 | } = req; |
101 | try { | 108 | try { |
102 | - await Video.findOneAndRemove({ _id: id }); | 109 | + const video = await Video.findById(id); |
110 | + // video를 받아서 render로 통해 템플릿으로 던져준다, | ||
111 | + if (String(video.creator) !== req.user.id) { | ||
112 | + throw Error(); | ||
113 | + } else { | ||
114 | + await Video.findOneAndRemove({ _id: id }); | ||
115 | + } | ||
103 | } catch (error) { | 116 | } catch (error) { |
104 | console.log(error); | 117 | console.log(error); |
105 | } | 118 | } | ... | ... |
... | @@ -2,6 +2,7 @@ import multer from "multer"; | ... | @@ -2,6 +2,7 @@ import multer from "multer"; |
2 | import routes from "./routes"; | 2 | import routes from "./routes"; |
3 | 3 | ||
4 | const multerVideo = multer({ dest: "uploads/videos/" }); | 4 | const multerVideo = multer({ dest: "uploads/videos/" }); |
5 | +const multerAvatar = multer({ dest: "uploads/avatars/" }); | ||
5 | 6 | ||
6 | export const localsMiddleware = (req, res, next) => { | 7 | export const localsMiddleware = (req, res, next) => { |
7 | res.locals.siteName = "my Youtube"; | 8 | res.locals.siteName = "my Youtube"; |
... | @@ -26,3 +27,4 @@ export const onlyPrivate = (req, res, next) => { | ... | @@ -26,3 +27,4 @@ export const onlyPrivate = (req, res, next) => { |
26 | }; | 27 | }; |
27 | export const uploadVideo = multerVideo.single("videoFile"); | 28 | export const uploadVideo = multerVideo.single("videoFile"); |
28 | // single에 들어간 videoFile은 upload.pug의 file 부분 input name | 29 | // single에 들어간 videoFile은 upload.pug의 file 부분 input name |
30 | +export const uploadAvatar = multerAvatar.single("avatar"); | ... | ... |
... | @@ -10,6 +10,10 @@ const CommentSchema = new mongoose.Schema({ | ... | @@ -10,6 +10,10 @@ const CommentSchema = new mongoose.Schema({ |
10 | type: Date, | 10 | type: Date, |
11 | default: Date.now, | 11 | default: Date.now, |
12 | }, | 12 | }, |
13 | + creator: { | ||
14 | + type: mongoose.Schema.Types.ObjectId, | ||
15 | + ref: "User", | ||
16 | + }, | ||
13 | /* | 17 | /* |
14 | 18 | ||
15 | video: { //video와 comment를 연결하는 방법 #2 | 19 | video: { //video와 comment를 연결하는 방법 #2 | ... | ... |
... | @@ -7,6 +7,18 @@ const UserSchema = new mongoose.Schema({ | ... | @@ -7,6 +7,18 @@ const UserSchema = new mongoose.Schema({ |
7 | avatarUrl: String, | 7 | avatarUrl: String, |
8 | facebookId: Number, | 8 | facebookId: Number, |
9 | githubId: Number, | 9 | githubId: Number, |
10 | + comments: [ | ||
11 | + { | ||
12 | + type: mongoose.Schema.Types.ObjectId, // 그 다음 어느 model에서 온 id인지 알려줘야 한다. | ||
13 | + ref: "Comment", | ||
14 | + }, | ||
15 | + ], | ||
16 | + videos: [ | ||
17 | + { | ||
18 | + type: mongoose.Schema.Types.ObjectId, // 그 다음 어느 model에서 온 id인지 알려줘야 한다. | ||
19 | + ref: "Video", | ||
20 | + }, | ||
21 | + ], | ||
10 | }); | 22 | }); |
11 | // 이 상태에서 새로운 스키마를 추가한다. | 23 | // 이 상태에서 새로운 스키마를 추가한다. |
12 | // passportLocalMongoose는 configuration object가 필요하다. | 24 | // passportLocalMongoose는 configuration object가 필요하다. | ... | ... |
... | @@ -27,6 +27,10 @@ const VideoSchema = new mongoose.Schema({ | ... | @@ -27,6 +27,10 @@ const VideoSchema = new mongoose.Schema({ |
27 | ref: "Comment", | 27 | ref: "Comment", |
28 | }, | 28 | }, |
29 | ], | 29 | ], |
30 | + creator: { | ||
31 | + type: mongoose.Schema.Types.ObjectId, | ||
32 | + ref: "User", | ||
33 | + }, | ||
30 | }); | 34 | }); |
31 | // 이제 이 스키마를 이용하여 model을 만들어준다. | 35 | // 이제 이 스키마를 이용하여 model을 만들어준다. |
32 | // 모델의 이름은 "Video" | 36 | // 모델의 이름은 "Video" | ... | ... |
... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
4 | "description": "make Youtube Website", | 4 | "description": "make Youtube Website", |
5 | "main": "app.js", | 5 | "main": "app.js", |
6 | "scripts": { | 6 | "scripts": { |
7 | - "dev:server": "nodemon --exec babel-node init.js --delay 2 --ignore 'scss'", | 7 | + "dev:server": "nodemon --exec babel-node init.js --delay 2 --ignore '.scss' --ignore 'static'", |
8 | "dev:assets": "WEBPACK_ENV=development webpack -w", | 8 | "dev:assets": "WEBPACK_ENV=development webpack -w", |
9 | "build:assets": "WEBPACK_ENV=production webpack", | 9 | "build:assets": "WEBPACK_ENV=production webpack", |
10 | "tunnel": "ngrok http 80" | 10 | "tunnel": "ngrok http 80" | ... | ... |
... | @@ -2,15 +2,20 @@ import express from "express"; | ... | @@ -2,15 +2,20 @@ import express from "express"; |
2 | import routes from "../routes"; | 2 | import routes from "../routes"; |
3 | import { | 3 | import { |
4 | userDetail, | 4 | userDetail, |
5 | - editProfile, | 5 | + getEditProfile, |
6 | - changePassword, | 6 | + postEditProfile, |
7 | + getChangePassword, | ||
8 | + postChangePassword, | ||
7 | } from "../controllers/userController"; | 9 | } from "../controllers/userController"; |
8 | -import { onlyPrivate } from "../middlewares"; | 10 | +import { onlyPrivate, uploadAvatar } from "../middlewares"; |
9 | 11 | ||
10 | const userRouter = express.Router(); | 12 | const userRouter = express.Router(); |
11 | 13 | ||
12 | -userRouter.get(routes.editProfile, onlyPrivate, editProfile); | 14 | +userRouter.get(routes.editProfile, onlyPrivate, getEditProfile); |
13 | -userRouter.get(routes.changePassword, onlyPrivate, changePassword); | 15 | +userRouter.post(routes.editProfile, onlyPrivate, uploadAvatar, postEditProfile); |
16 | + | ||
17 | +userRouter.get(routes.changePassword, onlyPrivate, getChangePassword); | ||
18 | +userRouter.post(routes.changePassword, onlyPrivate, postChangePassword); | ||
14 | userRouter.get(routes.userDetail(), userDetail); | 19 | userRouter.get(routes.userDetail(), userDetail); |
15 | 20 | ||
16 | export default userRouter; | 21 | export default userRouter; | ... | ... |
... | @@ -4,9 +4,9 @@ import { | ... | @@ -4,9 +4,9 @@ import { |
4 | getUpload, | 4 | getUpload, |
5 | postUpload, | 5 | postUpload, |
6 | videoDetail, | 6 | videoDetail, |
7 | + deleteVideo, | ||
7 | getEditVideo, | 8 | getEditVideo, |
8 | postEditVideo, | 9 | postEditVideo, |
9 | - deleteVideo, | ||
10 | } from "../controllers/videoController"; | 10 | } from "../controllers/videoController"; |
11 | import { uploadVideo, onlyPrivate } from "../middlewares"; | 11 | import { uploadVideo, onlyPrivate } from "../middlewares"; |
12 | // export const videoRouter = express.Router(); 이렇게하면 이 변수만 export하게 된다. | 12 | // export const videoRouter = express.Router(); 이렇게하면 이 변수만 export하게 된다. | ... | ... |
... | @@ -94,18 +94,29 @@ | ... | @@ -94,18 +94,29 @@ |
94 | /***/ (function(module, __webpack_exports__, __webpack_require__) { | 94 | /***/ (function(module, __webpack_exports__, __webpack_require__) { |
95 | 95 | ||
96 | "use strict"; | 96 | "use strict"; |
97 | -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _scss_style_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../scss/style.scss */ \"./assets/scss/style.scss\");\n/* harmony import */ var _scss_style_scss__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_scss_style_scss__WEBPACK_IMPORTED_MODULE_0__);\n\n\nconst something = async () => {\n console.log(\"something\");\n};\n\n\n//# sourceURL=webpack:///./assets/js/main.js?"); | 97 | +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _scss_styles_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../scss/styles.scss */ \"./assets/scss/styles.scss\");\n/* harmony import */ var _scss_styles_scss__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_scss_styles_scss__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _videoPlayer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./videoPlayer */ \"./assets/js/videoPlayer.js\");\n/* harmony import */ var _videoPlayer__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_videoPlayer__WEBPACK_IMPORTED_MODULE_1__);\n\n\n\n\n//# sourceURL=webpack:///./assets/js/main.js?"); |
98 | 98 | ||
99 | /***/ }), | 99 | /***/ }), |
100 | 100 | ||
101 | -/***/ "./assets/scss/style.scss": | 101 | +/***/ "./assets/js/videoPlayer.js": |
102 | -/*!********************************!*\ | 102 | +/*!**********************************!*\ |
103 | - !*** ./assets/scss/style.scss ***! | 103 | + !*** ./assets/js/videoPlayer.js ***! |
104 | - \********************************/ | 104 | + \**********************************/ |
105 | /*! no static exports found */ | 105 | /*! no static exports found */ |
106 | /***/ (function(module, exports) { | 106 | /***/ (function(module, exports) { |
107 | 107 | ||
108 | -eval("// removed by extract-text-webpack-plugin\n\n//# sourceURL=webpack:///./assets/scss/style.scss?"); | 108 | +eval("\n\n//# sourceURL=webpack:///./assets/js/videoPlayer.js?"); |
109 | + | ||
110 | +/***/ }), | ||
111 | + | ||
112 | +/***/ "./assets/scss/styles.scss": | ||
113 | +/*!*********************************!*\ | ||
114 | + !*** ./assets/scss/styles.scss ***! | ||
115 | + \*********************************/ | ||
116 | +/*! no static exports found */ | ||
117 | +/***/ (function(module, exports) { | ||
118 | + | ||
119 | +eval("// removed by extract-text-webpack-plugin\n\n//# sourceURL=webpack:///./assets/scss/styles.scss?"); | ||
109 | 120 | ||
110 | /***/ }), | 121 | /***/ }), |
111 | 122 | ... | ... |
... | @@ -140,6 +140,13 @@ input { | ... | @@ -140,6 +140,13 @@ input { |
140 | input:focus, input:active { | 140 | input:focus, input:active { |
141 | outline: none; } | 141 | outline: none; } |
142 | 142 | ||
143 | +.u-avatar { | ||
144 | + height: 80px; | ||
145 | + background-color: #ea232c; | ||
146 | + -webkit-border-radius: 50%; | ||
147 | + -moz-border-radius: 50%; | ||
148 | + border-radius: 50%; } | ||
149 | + | ||
143 | html, | 150 | html, |
144 | body { | 151 | body { |
145 | height: 100%; } | 152 | height: 100%; } |
... | @@ -413,6 +420,49 @@ input[type="submit"] { | ... | @@ -413,6 +420,49 @@ input[type="submit"] { |
413 | font-weight: 300; | 420 | font-weight: 300; |
414 | margin-bottom: 10px; } | 421 | margin-bottom: 10px; } |
415 | 422 | ||
423 | +.videoPlayer { | ||
424 | + position: relative; } | ||
425 | + .videoPlayer:hover .videoPlayer__controls { | ||
426 | + opacity: 1; } | ||
427 | + .videoPlayer video { | ||
428 | + max-width: 100%; } | ||
429 | + .videoPlayer .videoPlayer__controls { | ||
430 | + opacity: 0; | ||
431 | + -webkit-transition: opacity 0.4s linear; | ||
432 | + -o-transition: opacity 0.4s linear; | ||
433 | + -moz-transition: opacity 0.4s linear; | ||
434 | + transition: opacity 0.4s linear; | ||
435 | + color: white; | ||
436 | + position: absolute; | ||
437 | + z-index: 9; | ||
438 | + bottom: 5px; | ||
439 | + width: 100%; | ||
440 | + background-color: rgba(0, 0, 0, 0.5); | ||
441 | + padding: 10px; | ||
442 | + display: grid; | ||
443 | + grid-template-columns: repeat(3, 1fr); | ||
444 | + font-size: 16px; } | ||
445 | + .videoPlayer .videoPlayer__controls .videoPlayer__column:first-child { | ||
446 | + display: -webkit-box; | ||
447 | + display: -webkit-flex; | ||
448 | + display: -moz-box; | ||
449 | + display: -ms-flexbox; | ||
450 | + display: flex; | ||
451 | + -webkit-box-align: center; | ||
452 | + -webkit-align-items: center; | ||
453 | + -moz-box-align: center; | ||
454 | + -ms-flex-align: center; | ||
455 | + align-items: center; } | ||
456 | + .videoPlayer .videoPlayer__controls .videoPlayer__column:first-child span:first-child { | ||
457 | + margin-right: 10px; } | ||
458 | + .videoPlayer .videoPlayer__controls .videoPlayer__column:last-child { | ||
459 | + justify-self: end; } | ||
460 | + .videoPlayer .videoPlayer__controls .videoPlayer__column:nth-child(2) { | ||
461 | + justify-self: center; } | ||
462 | + .videoPlayer .videoPlayer__controls i { | ||
463 | + font-size: 25px; | ||
464 | + cursor: pointer; } | ||
465 | + | ||
416 | .home-videos { | 466 | .home-videos { |
417 | display: grid; | 467 | display: grid; |
418 | grid-template-columns: repeat(6, minmax(150px, 1fr)); | 468 | grid-template-columns: repeat(6, minmax(150px, 1fr)); |
... | @@ -450,6 +500,8 @@ input[type="submit"] { | ... | @@ -450,6 +500,8 @@ input[type="submit"] { |
450 | .video-detail__container .video__info button { | 500 | .video-detail__container .video__info button { |
451 | width: 100px; | 501 | width: 100px; |
452 | margin-bottom: 25px; } | 502 | margin-bottom: 25px; } |
503 | + .video-detail__container .video__info .video__author a { | ||
504 | + color: #3498db; } | ||
453 | .video-detail__container .video__info .video__title, | 505 | .video-detail__container .video__info .video__title, |
454 | .video-detail__container .video__info .video__views, | 506 | .video-detail__container .video__info .video__views, |
455 | .video-detail__container .video__info .video__description { | 507 | .video-detail__container .video__info .video__description { |
... | @@ -466,3 +518,65 @@ input[type="submit"] { | ... | @@ -466,3 +518,65 @@ input[type="submit"] { |
466 | margin-top: 25px; } | 518 | margin-top: 25px; } |
467 | .video-detail__container .video__comments .video__comment-number { | 519 | .video-detail__container .video__comments .video__comment-number { |
468 | font-size: 18px; } | 520 | font-size: 18px; } |
521 | + | ||
522 | +.user-profile { | ||
523 | + width: 100%; | ||
524 | + display: -webkit-box; | ||
525 | + display: -webkit-flex; | ||
526 | + display: -moz-box; | ||
527 | + display: -ms-flexbox; | ||
528 | + display: flex; | ||
529 | + -webkit-box-align: center; | ||
530 | + -webkit-align-items: center; | ||
531 | + -moz-box-align: center; | ||
532 | + -ms-flex-align: center; | ||
533 | + align-items: center; | ||
534 | + -webkit-box-orient: vertical; | ||
535 | + -webkit-box-direction: normal; | ||
536 | + -webkit-flex-direction: column; | ||
537 | + -moz-box-orient: vertical; | ||
538 | + -moz-box-direction: normal; | ||
539 | + -ms-flex-direction: column; | ||
540 | + flex-direction: column; } | ||
541 | + .user-profile .user-profile__header { | ||
542 | + display: -webkit-box; | ||
543 | + display: -webkit-flex; | ||
544 | + display: -moz-box; | ||
545 | + display: -ms-flexbox; | ||
546 | + display: flex; | ||
547 | + -webkit-box-orient: vertical; | ||
548 | + -webkit-box-direction: normal; | ||
549 | + -webkit-flex-direction: column; | ||
550 | + -moz-box-orient: vertical; | ||
551 | + -moz-box-direction: normal; | ||
552 | + -ms-flex-direction: column; | ||
553 | + flex-direction: column; | ||
554 | + -webkit-box-align: center; | ||
555 | + -webkit-align-items: center; | ||
556 | + -moz-box-align: center; | ||
557 | + -ms-flex-align: center; | ||
558 | + align-items: center; | ||
559 | + margin-bottom: 18px; } | ||
560 | + .user-profile .user-profile__header .profile__username { | ||
561 | + font-size: 18px; | ||
562 | + margin-top: 15px; } | ||
563 | + .user-profile .user-profile__btns { | ||
564 | + display: -webkit-box; | ||
565 | + display: -webkit-flex; | ||
566 | + display: -moz-box; | ||
567 | + display: -ms-flexbox; | ||
568 | + display: flex; | ||
569 | + -webkit-box-orient: vertical; | ||
570 | + -webkit-box-direction: normal; | ||
571 | + -webkit-flex-direction: column; | ||
572 | + -moz-box-orient: vertical; | ||
573 | + -moz-box-direction: normal; | ||
574 | + -ms-flex-direction: column; | ||
575 | + flex-direction: column; | ||
576 | + -webkit-box-align: center; | ||
577 | + -webkit-align-items: center; | ||
578 | + -moz-box-align: center; | ||
579 | + -ms-flex-align: center; | ||
580 | + align-items: center; } | ||
581 | + .user-profile .user-profile__btns a:not(:last-child) { | ||
582 | + margin-bottom: 20px; } | ... | ... |
... | @@ -3,7 +3,7 @@ extends layouts/main | ... | @@ -3,7 +3,7 @@ extends layouts/main |
3 | block content | 3 | block content |
4 | .form-container | 4 | .form-container |
5 | form(action=`/users${routes.changePassword}`, method="post") | 5 | form(action=`/users${routes.changePassword}`, method="post") |
6 | - input(type="password", name="oldPasswod", placeholder="Current Password") | 6 | + input(type="password", name="oldPassword", placeholder="Current Password") |
7 | input(type="password", name="newPassword", placeholder="New Password") | 7 | input(type="password", name="newPassword", placeholder="New Password") |
8 | input(type="password", name="newPassword1", placeholder="Verify New Password") | 8 | input(type="password", name="newPassword1", placeholder="Verify New Password") |
9 | input(type="submit", value="Change Password") | 9 | input(type="submit", value="Change Password") |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -2,12 +2,11 @@ extends layouts/main | ... | @@ -2,12 +2,11 @@ extends layouts/main |
2 | 2 | ||
3 | block content | 3 | block content |
4 | .form-container | 4 | .form-container |
5 | - form(action=routes.editProfile, method="post") | 5 | + form(action=`/users${routes.editProfile}`, method="post", enctype="multipart/form-data") |
6 | .fileUpload | 6 | .fileUpload |
7 | label(for="avatar") Avatar | 7 | label(for="avatar") Avatar |
8 | - input(type="file", id="avatar", name="avatar") | 8 | + input(type="file", id="avatar", name="avatar", accept="image/*") |
9 | - input(type="text", placeholder="Name", name="name") | 9 | + input(type="text", placeholder="Name", name="name", value=loggedUser.name) |
10 | - input(type="email", placeholder="Email", name="email") | 10 | + input(type="email", placeholder="Email", name="email", value=loggedUser.email) |
11 | input(type="submit", value="Update Profile") | 11 | input(type="submit", value="Update Profile") |
12 | - a.form-container__link(href=`/users${routes.changePassword}`) | ||
13 | - button Change Password | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
12 | + | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -9,70 +9,4 @@ block content | ... | @@ -9,70 +9,4 @@ block content |
9 | title : item.title, | 9 | title : item.title, |
10 | views: item.views, | 10 | views: item.views, |
11 | videoFile:item.fileUrl | 11 | videoFile:item.fileUrl |
12 | - }) | 12 | + }) |
13 | - +videoBlock({ | ||
14 | - id:item.id, | ||
15 | - title:item.title, | ||
16 | - views:item.views, | ||
17 | - videoFile:item.fileUrl | ||
18 | - }) | ||
19 | - +videoBlock({ | ||
20 | - id:item.id, | ||
21 | - title:item.title, | ||
22 | - views:item.views, | ||
23 | - videoFile:item.fileUrl | ||
24 | - }) | ||
25 | - +videoBlock({ | ||
26 | - id:item.id, | ||
27 | - title:item.title, | ||
28 | - views:item.views, | ||
29 | - videoFile:item.fileUrl | ||
30 | - }) | ||
31 | - +videoBlock({ | ||
32 | - id:item.id, | ||
33 | - title:item.title, | ||
34 | - views:item.views, | ||
35 | - videoFile:item.fileUrl | ||
36 | - }) | ||
37 | - +videoBlock({ | ||
38 | - id:item.id, | ||
39 | - title:item.title, | ||
40 | - views:item.views, | ||
41 | - videoFile:item.fileUrl | ||
42 | - }) | ||
43 | - +videoBlock({ | ||
44 | - id:item.id, | ||
45 | - title:item.title, | ||
46 | - views:item.views, | ||
47 | - videoFile:item.fileUrl | ||
48 | - }) | ||
49 | - +videoBlock({ | ||
50 | - id:item.id, | ||
51 | - title:item.title, | ||
52 | - views:item.views, | ||
53 | - videoFile:item.fileUrl | ||
54 | - }) | ||
55 | - +videoBlock({ | ||
56 | - id:item.id, | ||
57 | - title:item.title, | ||
58 | - views:item.views, | ||
59 | - videoFile:item.fileUrl | ||
60 | - }) | ||
61 | - +videoBlock({ | ||
62 | - id:item.id, | ||
63 | - title:item.title, | ||
64 | - views:item.views, | ||
65 | - videoFile:item.fileUrl | ||
66 | - }) | ||
67 | - +videoBlock({ | ||
68 | - id:item.id, | ||
69 | - title:item.title, | ||
70 | - views:item.views, | ||
71 | - videoFile:item.fileUrl | ||
72 | - }) | ||
73 | - +videoBlock({ | ||
74 | - id:item.id, | ||
75 | - title:item.title, | ||
76 | - views:item.views, | ||
77 | - videoFile:item.fileUrl | ||
78 | - }) | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
views/mixins/videoPlayer.pug
0 → 100644
1 | +mixin videoPlayer(video={}) | ||
2 | + .videoPlayer | ||
3 | + video(src=`/${video.src}`) | ||
4 | + .videoPlayer__controls | ||
5 | + .videoPlayer__column | ||
6 | + span | ||
7 | + i.fas.fa-volume-up | ||
8 | + span | ||
9 | + |00:00 / 10:00 | ||
10 | + .videoPlayer__column | ||
11 | + span | ||
12 | + i.fas.fa-play | ||
13 | + .videoPlayer__column | ||
14 | + span | ||
15 | + i.fas.fa-expand | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | extends layouts/main | 1 | extends layouts/main |
2 | +include mixins/videoBlock | ||
2 | 3 | ||
3 | block content | 4 | block content |
4 | .user-profile | 5 | .user-profile |
5 | .user-profile__header | 6 | .user-profile__header |
6 | - img.avatar(src=user.avatarUrl) | ||
7 | - h4.profile__username=user.name | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
7 | + img.u-avatar(src=user.avatarUrl) | ||
8 | + h4.profile__username=user.name | ||
9 | + if user.id === loggedUser.id | ||
10 | + .user-profile__btns | ||
11 | + a(href=`/users${routes.editProfile}`) | ||
12 | + button 🖌 Edit Profile | ||
13 | + a(href=`/users${routes.changePassword}`) | ||
14 | + button 🔒 Change Password | ||
15 | + .home-videos | ||
16 | + each item in user.videos | ||
17 | + +videoBlock({ | ||
18 | + id: item.id, | ||
19 | + title : item.title, | ||
20 | + views: item.views, | ||
21 | + videoFile:item.fileUrl | ||
22 | + }) | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | extends layouts/main | 1 | extends layouts/main |
2 | +include mixins/videoPlayer | ||
2 | 3 | ||
3 | block content | 4 | block content |
4 | - .video__player | 5 | + .video-detail__container |
5 | - video(src=`/${video.fileUrl}`) | 6 | + +videoPlayer({ |
6 | - .video__info | 7 | + src:video.fileUrl |
7 | - a(href=routes.editVideo(video.id)) | 8 | + }) |
8 | - button Edit video | 9 | + .video__info |
9 | - h5.video__title=video.title | 10 | + if loggedUser && video.creator.id === loggedUser.id |
10 | - span.video__views=video.views | 11 | + a(href=routes.editVideo(video.id)) |
11 | - p.video__description=video.description | 12 | + button Edit video |
12 | - if video.views === 1 | 13 | + h5.video__title=video.title |
14 | + p.video__description=video.description | ||
15 | + if video.views === 1 | ||
13 | span.video__views 1 view | 16 | span.video__views 1 view |
14 | - else | ||
15 | - span.video__views #{video.views} views | ||
16 | - .video__comment | ||
17 | - if video.comments.length === 1 | ||
18 | - span.video__comment-number 1 comment | ||
19 | - else | ||
20 | - span.video__comment-number #{video.comments.length} comments | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
17 | + else | ||
18 | + span.video__views #{video.views} views | ||
19 | + .video__author | ||
20 | + |Uploaded by | ||
21 | + a(href=routes.userDetail(video.creator.id))=video.creator.name | ||
22 | + .video__comment | ||
23 | + if video.comments.length === 1 | ||
24 | + span.video__comment-number 1 comment | ||
25 | + else | ||
26 | + span.video__comment-number #{video.comments.length} comments | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
-
Please register or login to post a comment