comments.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template lang="pug">
  2. div(v-intersect.once='onIntersect')
  3. v-textarea#discussion-new(
  4. outlined
  5. flat
  6. placeholder='Write a new comment...'
  7. auto-grow
  8. dense
  9. rows='3'
  10. hide-details
  11. v-model='newcomment'
  12. color='blue-grey darken-2'
  13. :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
  14. v-if='permissions.write'
  15. )
  16. v-row.mt-2(dense, v-if='!isAuthenticated && permissions.write')
  17. v-col(cols='12', lg='6')
  18. v-text-field(
  19. outlined
  20. color='blue-grey darken-2'
  21. :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
  22. placeholder='Your Name'
  23. hide-details
  24. dense
  25. autocomplete='name'
  26. v-model='guestName'
  27. )
  28. v-col(cols='12', lg='6')
  29. v-text-field(
  30. outlined
  31. color='blue-grey darken-2'
  32. :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
  33. placeholder='Your Email Address'
  34. hide-details
  35. type='email'
  36. dense
  37. autocomplete='email'
  38. v-model='guestEmail'
  39. )
  40. .d-flex.align-center.pt-3(v-if='permissions.write')
  41. v-icon.mr-1(color='blue-grey') mdi-language-markdown-outline
  42. .caption.blue-grey--text Markdown Format
  43. v-spacer
  44. .caption.mr-3(v-if='isAuthenticated') Posting as #[strong {{userDisplayName}}]
  45. v-btn(
  46. dark
  47. color='blue-grey darken-2'
  48. @click='postComment'
  49. depressed
  50. )
  51. v-icon(left) mdi-comment
  52. span.text-none Post Comment
  53. v-divider.mt-3(v-if='permissions.write')
  54. .pa-5.d-flex.align-center.justify-center(v-if='isLoading')
  55. v-progress-circular(
  56. indeterminate
  57. size='20'
  58. width='1'
  59. color='blue-grey'
  60. )
  61. .caption.blue-grey--text.pl-3: em Loading comments...
  62. v-timeline(
  63. dense
  64. v-else-if='comments && comments.length > 0'
  65. )
  66. v-timeline-item(
  67. color='pink darken-4'
  68. large
  69. v-for='cm of comments'
  70. :key='`comment-` + cm.id'
  71. )
  72. template(v-slot:icon)
  73. v-avatar
  74. v-img(src='http://i.pravatar.cc/64')
  75. v-card.elevation-1
  76. v-card-text
  77. .caption: strong {{cm.authorName}}
  78. .overline.grey--text 3 minutes ago
  79. .mt-3 {{cm.render}}
  80. .pt-5.text-center.body-2.blue-grey--text(v-else-if='permissions.write') Be the first to comment.
  81. .text-center.body-2.blue-grey--text(v-else) No comments yet.
  82. </template>
  83. <script>
  84. import gql from 'graphql-tag'
  85. import { get } from 'vuex-pathify'
  86. import validate from 'validate.js'
  87. import _ from 'lodash'
  88. export default {
  89. data () {
  90. return {
  91. newcomment: '',
  92. isLoading: true,
  93. canFetch: false,
  94. comments: [],
  95. guestName: '',
  96. guestEmail: ''
  97. }
  98. },
  99. computed: {
  100. pageId: get('page/id'),
  101. permissions: get('page/commentsPermissions'),
  102. isAuthenticated: get('user/authenticated'),
  103. userDisplayName: get('user/name')
  104. },
  105. methods: {
  106. onIntersect (entries, observer, isIntersecting) {
  107. if (isIntersecting) {
  108. this.isLoading = true
  109. this.canFetch = true
  110. }
  111. },
  112. async postComment () {
  113. let rules = {
  114. comment: {
  115. presence: {
  116. allowEmpty: false
  117. },
  118. length: {
  119. minimum: 2
  120. }
  121. }
  122. }
  123. if (!this.isAuthenticated && this.permissions.write) {
  124. rules.name = {
  125. presence: {
  126. allowEmpty: false
  127. },
  128. length: {
  129. minimum: 2,
  130. maximum: 255
  131. }
  132. }
  133. rules.email = {
  134. presence: {
  135. allowEmpty: false
  136. },
  137. email: true
  138. }
  139. }
  140. const validationResults = validate({
  141. comment: this.newcomment,
  142. name: this.guestName,
  143. email: this.guestEmail
  144. }, rules, { format: 'flat' })
  145. if (validationResults) {
  146. this.$store.commit('showNotification', {
  147. style: 'red',
  148. message: validationResults[0],
  149. icon: 'alert'
  150. })
  151. return
  152. }
  153. const resp = await this.$apollo.mutate({
  154. mutation: gql`
  155. mutation (
  156. $pageId: Int!
  157. $replyTo: Int
  158. $content: String!
  159. $guestName: String
  160. $guestEmail: String
  161. ) {
  162. comments {
  163. create (
  164. pageId: $pageId
  165. replyTo: $replyTo
  166. content: $content
  167. guestName: $guestName
  168. guestEmail: $guestEmail
  169. ) {
  170. responseResult {
  171. succeeded
  172. errorCode
  173. slug
  174. message
  175. }
  176. }
  177. }
  178. }
  179. `,
  180. variables: {
  181. pageId: this.pageId,
  182. replyTo: 0,
  183. content: this.newcomment,
  184. guestName: this.guestName,
  185. guestEmail: this.guestEmail
  186. }
  187. })
  188. if (_.get(resp, 'data.comments.create.responseResult.succeeded', false)) {
  189. this.$store.commit('showNotification', {
  190. style: 'success',
  191. message: 'New comment posted successfully.',
  192. icon: 'check'
  193. })
  194. this.newcomment = ''
  195. } else {
  196. this.$store.commit('showNotification', {
  197. style: 'red',
  198. message: _.get(resp, 'data.comments.create.responseResult.message', 'An unexpected error occured.'),
  199. icon: 'alert'
  200. })
  201. }
  202. }
  203. },
  204. apollo: {
  205. comments: {
  206. query: gql`
  207. query ($pageId: Int!) {
  208. comments {
  209. list(pageId: $pageId) {
  210. id
  211. render
  212. authorName
  213. createdAt
  214. updatedAt
  215. }
  216. }
  217. }
  218. `,
  219. variables() {
  220. return {
  221. pageId: this.pageId
  222. }
  223. },
  224. skip () {
  225. return !this.canFetch
  226. },
  227. fetchPolicy: 'cache-and-network',
  228. update: (data) => data.comments.list,
  229. watchLoading (isLoading) {
  230. this.isLoading = isLoading
  231. }
  232. }
  233. }
  234. }
  235. </script>
  236. <style lang="scss">
  237. </style>