Skip to content

Commit b004046

Browse files
committed
Create comment service and comment CRUD
1 parent 96a73f0 commit b004046

File tree

9 files changed

+560
-209
lines changed

9 files changed

+560
-209
lines changed

src/components/NavBar.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,10 @@ export default {
347347
},
348348
mounted() {
349349
this.drawer = this.$vuetify.breakpoint.mdAndDown ? false : true
350+
// console.log(this.$route.name)
351+
this.drawer = this.$route.name === 'Watch' ? false : this.drawer
352+
},
353+
created() {
350354
this.drawer = this.$route.name === 'Watch' ? false : this.drawer
351355
}
352356
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<template>
2+
<v-card class="transparent" flat>
3+
<v-list-item three-line class="pl-0">
4+
<v-list-item-avatar size="50"
5+
><v-img :src="avatar"></v-img
6+
></v-list-item-avatar>
7+
<v-list-item-content class="align-self-auto">
8+
<v-text-field
9+
v-model="comment"
10+
placeholder="Add a public comment..."
11+
@click="showCommentBtns = true"
12+
>
13+
</v-text-field>
14+
<div v-if="showCommentBtns" class="d-inline-block text-right">
15+
<v-btn text @click="showCommentBtns = !showCommentBtns">Cancel</v-btn>
16+
<v-btn
17+
class="blue darken-3 white--text"
18+
depressed
19+
tile
20+
:loading="loading"
21+
:disabled="comment === ''"
22+
@click="createComment"
23+
>Comment</v-btn
24+
>
25+
</div>
26+
</v-list-item-content>
27+
</v-list-item>
28+
</v-card>
29+
</template>
30+
31+
<script>
32+
import CommentService from '@/services/CommentService'
33+
export default {
34+
props: {
35+
videoId: {
36+
type: String,
37+
required: true
38+
}
39+
},
40+
data: function() {
41+
return {
42+
showCommentBtns: false,
43+
loading: false,
44+
comment: '',
45+
avatar: `${process.env.VUE_APP_URL}/uploads/avatars/${this.$store.getters.currentUser.photoUrl}`
46+
}
47+
},
48+
methods: {
49+
async createComment() {
50+
if (this.comment === '') return
51+
52+
this.loading = true
53+
const comment = await CommentService.createComment({
54+
text: this.comment,
55+
videoId: this.videoId
56+
})
57+
.catch((err) => {
58+
console.log(err)
59+
})
60+
.finally(() => {
61+
this.loading = false
62+
})
63+
64+
if (!comment) return
65+
this.comment = ''
66+
comment.data.data.userId = this.$store.getters.currentUser
67+
this.$store.dispatch('addComment', comment.data.data)
68+
}
69+
}
70+
}
71+
</script>
72+
73+
<style></style>
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
<template>
2+
<div>
3+
<div v-for="(comment, i) in loading ? 4 : comments" :key="comment._id">
4+
<v-skeleton-loader type="list-item-avatar-two-line" :loading="loading">
5+
<v-card class="transparent" flat>
6+
<v-list-item three-line class="pl-0 mt-2">
7+
<v-list-item-avatar
8+
v-if="typeof comment.userId !== 'undefined'"
9+
size="50"
10+
><v-img
11+
:src="`${url}/uploads/avatars/${comment.userId.photoUrl}`"
12+
></v-img
13+
></v-list-item-avatar>
14+
<v-list-item-content>
15+
<div class="d-flex mb-0">
16+
<v-list-item-title
17+
v-if="comment.userId"
18+
class="font-weight-medium caption mb-0 d-flex"
19+
>{{ comment.userId.channelName }}
20+
<span class="pl-2 font-weight-light grey--text">
21+
{{ dateFormatter(comment.createdAt) }}</span
22+
>
23+
</v-list-item-title>
24+
<v-menu bottom left>
25+
<template v-slot:activator="{ on }">
26+
<v-btn icon v-on="on">
27+
<v-icon>mdi-dots-vertical</v-icon>
28+
</v-btn>
29+
</template>
30+
31+
<v-list>
32+
<v-list-item @click="deleteComment(comment._id)">
33+
<v-list-item-title
34+
><v-icon>mdi-trash</v-icon>Delete</v-list-item-title
35+
>
36+
</v-list-item>
37+
</v-list>
38+
</v-menu>
39+
</div>
40+
<v-list-item-subtitle
41+
class="mt-n2 black--text text--darken-4 caption"
42+
>{{ comment.text }}</v-list-item-subtitle
43+
>
44+
45+
<div>
46+
<v-btn
47+
text
48+
small
49+
:ripple="false"
50+
@click.stop="showReply(`${'reply' + comment._id}`)"
51+
>Reply</v-btn
52+
>
53+
</div>
54+
<div class="d-none" :ref="`${'reply' + comment._id}`">
55+
<v-list-item three-line class="pl-0">
56+
<v-list-item-avatar
57+
v-if="typeof comment.userId !== 'undefined'"
58+
class="mt-0"
59+
size="40"
60+
><v-img
61+
:src="`${url}/uploads/avatars/${comment.userId.photoUrl}`"
62+
></v-img
63+
></v-list-item-avatar>
64+
<v-list-item-content class="align-self-auto mt-0 pt-0">
65+
<v-form :ref="`form${comment._id}`">
66+
<v-text-field
67+
:ref="`${'input' + comment._id}`"
68+
class="pt-0 mt-0 body-2"
69+
placeholder="Add a public comment..."
70+
:value="repliesInput[`input${comment._id}`]"
71+
>
72+
</v-text-field>
73+
</v-form>
74+
<div
75+
:ref="comment._id + 'btns'"
76+
class="d-inline-block text-right"
77+
>
78+
<v-btn text @click="hideReply(comment._id)" small
79+
>Cancel</v-btn
80+
>
81+
<v-btn
82+
class="blue darken-3 white--text"
83+
depressed
84+
tile
85+
small
86+
:loading="btnLoading && i == index"
87+
@click="
88+
index = i
89+
addReply($event, comment._id)
90+
"
91+
>Reply</v-btn
92+
>
93+
</div>
94+
</v-list-item-content>
95+
</v-list-item>
96+
<!-- </v-list-item-action> -->
97+
</div>
98+
<v-expansion-panels>
99+
<v-expansion-panel class="transparent elevation-0">
100+
<v-expansion-panel-header
101+
v-if="comment.replies && comment.replies.length > 0"
102+
class="blue--text text--darken-3"
103+
>{{
104+
comment.replies.length
105+
}}
106+
replies</v-expansion-panel-header
107+
>
108+
<v-expansion-panel-content>
109+
<v-list-item
110+
three-line
111+
class="pl-0 mt-2"
112+
v-for="reply in comment.replies"
113+
:key="reply._id"
114+
>
115+
<v-list-item-avatar
116+
v-if="typeof reply !== 'undefined'"
117+
size="50"
118+
><v-img
119+
:src="
120+
`${url}/uploads/avatars/${reply.userId.photoUrl}`
121+
"
122+
></v-img
123+
></v-list-item-avatar>
124+
<v-list-item-content>
125+
<div class="d-flex mb-0">
126+
<v-list-item-title
127+
v-if="reply.userId"
128+
class="font-weight-medium caption mb-0 d-flex"
129+
>{{ reply.userId.channelName }}
130+
<span class="pl-2 font-weight-light grey--text">
131+
{{ dateFormatter(reply.createdAt) }}</span
132+
>
133+
</v-list-item-title>
134+
<v-menu bottom left>
135+
<template v-slot:activator="{ on }">
136+
<v-btn icon v-on="on">
137+
<v-icon>mdi-dots-vertical</v-icon>
138+
</v-btn>
139+
</template>
140+
141+
<v-list>
142+
<v-list-item
143+
@click="deleteReply(comment._id, reply._id)"
144+
>
145+
<v-list-item-title
146+
><v-icon>mdi-trash</v-icon
147+
>Delete</v-list-item-title
148+
>
149+
</v-list-item>
150+
</v-list>
151+
</v-menu>
152+
</div>
153+
<v-list-item-subtitle
154+
class="mt-n2 black--text text--darken-4 caption"
155+
>{{ reply.text }}</v-list-item-subtitle
156+
>
157+
</v-list-item-content>
158+
</v-list-item>
159+
</v-expansion-panel-content>
160+
</v-expansion-panel>
161+
</v-expansion-panels>
162+
</v-list-item-content>
163+
</v-list-item>
164+
</v-card>
165+
</v-skeleton-loader>
166+
</div>
167+
<v-snackbar v-model="snackbar">
168+
Comment deleted successfully
169+
<v-btn color="white" text @click="snackbar = false" icon>
170+
<v-icon>mdi-close-circle</v-icon>
171+
</v-btn>
172+
</v-snackbar>
173+
</div>
174+
</template>
175+
176+
<script>
177+
import moment from 'moment'
178+
import CommentService from '@/services/CommentService'
179+
import ReplyService from '@/services/ReplyService'
180+
export default {
181+
props: {
182+
videoId: {
183+
type: String,
184+
required: true
185+
}
186+
},
187+
data: function() {
188+
return {
189+
repliesInput: {},
190+
comments: [],
191+
index: -1,
192+
btnLoading: false,
193+
url: process.env.VUE_APP_URL,
194+
snackbar: false,
195+
loading: false
196+
}
197+
},
198+
methods: {
199+
async getComments() {
200+
this.loading = true
201+
const comments = await this.$store
202+
.dispatch('setComments', this.videoId)
203+
.catch((err) => console.log(err))
204+
.finally(() => (this.loading = false))
205+
// console.log(this.loading)
206+
if (!comments) return
207+
208+
this.comments = this.$store.getters.getComments.data
209+
// this.loading = false
210+
console.log(this.$store.getters.getComments.data)
211+
},
212+
async deleteComment(id) {
213+
this.comments = this.comments.filter(
214+
(comment) => comment._id.toString() !== id.toString()
215+
)
216+
this.snackbar = true
217+
await CommentService.deleteById(id).catch((err) => {
218+
console.log(err)
219+
})
220+
},
221+
async addReply(event, id) {
222+
this.btnLoading = true
223+
// console.log((event.target.loading = true))
224+
this.$refs[`form${id}`][0].reset()
225+
// console.log(this.$refs[`input${id}`][0].$refs.input.value)
226+
227+
const reply = await ReplyService.createReply({
228+
commentId: id,
229+
text: this.$refs[`input${id}`][0].$refs.input.value
230+
})
231+
.catch((err) => {
232+
console.log(err)
233+
})
234+
.finally(() => (this.btnLoading = false))
235+
reply.data.data.userId = this.$store.getters.currentUser
236+
this.comments
237+
.find((comment) => comment._id === id)
238+
.replies.unshift(reply.data.data)
239+
},
240+
showReply(id) {
241+
this.$refs[id][0].classList.toggle('d-none')
242+
},
243+
hideReply(id) {
244+
this.$refs[`form${id}`][0].reset()
245+
this.$refs['reply' + id][0].classList.toggle('d-none')
246+
},
247+
dateFormatter(date) {
248+
return moment(date).fromNow()
249+
}
250+
},
251+
mounted() {
252+
this.getComments()
253+
}
254+
}
255+
</script>
256+
257+
<style lang="scss">
258+
.v-application--is-ltr .v-expansion-panel-header__icon {
259+
margin-left: initial;
260+
}
261+
262+
.v-expansion-panel::before {
263+
box-shadow: initial;
264+
}
265+
</style>

src/services/CommentService.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Api from '@/services/Api'
2+
3+
export default {
4+
getCommentByVideoId(filters) {
5+
return Api().get(`comments/${filters}/videos`)
6+
},
7+
createComment(data) {
8+
return Api().post('comments', data)
9+
},
10+
deleteById(id) {
11+
return Api().delete(`comments/${id}`)
12+
}
13+
}

src/services/ReplyService.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Api from '@/services/Api'
2+
3+
export default {
4+
createReply(data) {
5+
return Api().post('replies', data)
6+
},
7+
deleteById(id) {
8+
return Api().delete(`replies/${id}`)
9+
}
10+
}

src/store/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Vue from 'vue'
22
import Vuex from 'vuex'
33

4+
import comments from './modules/comment'
5+
46
Vue.use(Vuex)
57

68
export default new Vuex.Store({
@@ -53,5 +55,5 @@ export default new Vuex.Store({
5355
localStorage.removeItem('user')
5456
}
5557
},
56-
modules: {}
58+
modules: { comments }
5759
})

0 commit comments

Comments
 (0)