| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 | 
							- <template lang="pug">
 
-   div(v-intersect.once='onIntersect')
 
-     v-textarea#discussion-new(
 
-       outlined
 
-       flat
 
-       placeholder='Write a new comment...'
 
-       auto-grow
 
-       dense
 
-       rows='3'
 
-       hide-details
 
-       v-model='newcomment'
 
-       color='blue-grey darken-2'
 
-       :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
 
-       v-if='permissions.write'
 
-     )
 
-     v-row.mt-2(dense, v-if='!isAuthenticated && permissions.write')
 
-       v-col(cols='12', lg='6')
 
-         v-text-field(
 
-           outlined
 
-           color='blue-grey darken-2'
 
-           :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
 
-           placeholder='Your Name'
 
-           hide-details
 
-           dense
 
-           autocomplete='name'
 
-           v-model='guestName'
 
-         )
 
-       v-col(cols='12', lg='6')
 
-         v-text-field(
 
-           outlined
 
-           color='blue-grey darken-2'
 
-           :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
 
-           placeholder='Your Email Address'
 
-           hide-details
 
-           type='email'
 
-           dense
 
-           autocomplete='email'
 
-           v-model='guestEmail'
 
-         )
 
-     .d-flex.align-center.pt-3(v-if='permissions.write')
 
-       v-icon.mr-1(color='blue-grey') mdi-language-markdown-outline
 
-       .caption.blue-grey--text Markdown Format
 
-       v-spacer
 
-       .caption.mr-3(v-if='isAuthenticated') Posting as #[strong {{userDisplayName}}]
 
-       v-btn(
 
-         dark
 
-         color='blue-grey darken-2'
 
-         @click='postComment'
 
-         depressed
 
-         )
 
-         v-icon(left) mdi-comment
 
-         span.text-none Post Comment
 
-     v-divider.mt-3(v-if='permissions.write')
 
-     .pa-5.d-flex.align-center.justify-center(v-if='isLoading && !hasLoadedOnce')
 
-       v-progress-circular(
 
-         indeterminate
 
-         size='20'
 
-         width='1'
 
-         color='blue-grey'
 
-       )
 
-       .caption.blue-grey--text.pl-3: em Loading comments...
 
-     v-timeline(
 
-       dense
 
-       v-else-if='comments && comments.length > 0'
 
-       )
 
-       v-timeline-item.comments-post(
 
-         color='pink darken-4'
 
-         large
 
-         v-for='cm of comments'
 
-         :key='`comment-` + cm.id'
 
-         :id='`comment-post-id-` + cm.id'
 
-         )
 
-         template(v-slot:icon)
 
-           v-avatar(color='blue-grey')
 
-             //- v-img(src='http://i.pravatar.cc/64')
 
-             span.white--text.title {{cm.initials}}
 
-         v-card.elevation-1
 
-           v-card-text
 
-             .comments-post-actions(v-if='permissions.manage && !isBusy && commentEditId === 0')
 
-               v-icon.mr-3(small, @click='editComment(cm)') mdi-pencil
 
-               v-icon(small, @click='deleteCommentConfirm(cm)') mdi-delete
 
-             .comments-post-name.caption: strong {{cm.authorName}}
 
-             .comments-post-date.overline.grey--text {{cm.createdAt | moment('from') }} #[em(v-if='cm.createdAt !== cm.updatedAt') - modified {{cm.updatedAt | moment('from') }}]
 
-             .comments-post-content.mt-3(v-if='commentEditId !== cm.id', v-html='cm.render')
 
-             .comments-post-editcontent.mt-3(v-else)
 
-               v-textarea(
 
-                 outlined
 
-                 flat
 
-                 auto-grow
 
-                 dense
 
-                 rows='3'
 
-                 hide-details
 
-                 v-model='commentEditContent'
 
-                 color='blue-grey darken-2'
 
-                 :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
 
-               )
 
-               .d-flex.align-center.pt-3
 
-                 v-spacer
 
-                 v-btn.mr-3(
 
-                   dark
 
-                   color='blue-grey darken-2'
 
-                   @click='editCommentCancel'
 
-                   outlined
 
-                   )
 
-                   v-icon(left) mdi-close
 
-                   span.text-none Cancel
 
-                 v-btn(
 
-                   dark
 
-                   color='blue-grey darken-2'
 
-                   @click='updateComment'
 
-                   depressed
 
-                   )
 
-                   v-icon(left) mdi-comment
 
-                   span.text-none Update Comment
 
-     .pt-5.text-center.body-2.blue-grey--text(v-else-if='permissions.write') Be the first to comment.
 
-     .text-center.body-2.blue-grey--text(v-else) No comments yet.
 
-     v-dialog(v-model='deleteCommentDialogShown', max-width='500')
 
-       v-card
 
-         .dialog-header.is-red Confirm Delete
 
-         v-card-text.pt-5
 
-           span Are you sure you want to permanently delete this comment?
 
-           .caption: strong This action cannot be undone!
 
-         v-card-chin
 
-           v-spacer
 
-           v-btn(text, @click='deleteCommentDialogShown = false') {{$t('common:actions.cancel')}}
 
-           v-btn(color='red', dark, @click='deleteComment') {{$t('common:actions.delete')}}
 
- </template>
 
- <script>
 
- import gql from 'graphql-tag'
 
- import { get } from 'vuex-pathify'
 
- import validate from 'validate.js'
 
- import _ from 'lodash'
 
- export default {
 
-   data () {
 
-     return {
 
-       newcomment: '',
 
-       isLoading: true,
 
-       hasLoadedOnce: false,
 
-       comments: [],
 
-       guestName: '',
 
-       guestEmail: '',
 
-       commentToDelete: {},
 
-       commentEditId: 0,
 
-       commentEditContent: null,
 
-       deleteCommentDialogShown: false,
 
-       isBusy: false,
 
-       scrollOpts: {
 
-         duration: 1500,
 
-         offset: 0,
 
-         easing: 'easeInOutCubic'
 
-       }
 
-     }
 
-   },
 
-   computed: {
 
-     pageId: get('page/id'),
 
-     permissions: get('page/commentsPermissions'),
 
-     isAuthenticated: get('user/authenticated'),
 
-     userDisplayName: get('user/name')
 
-   },
 
-   methods: {
 
-     onIntersect (entries, observer, isIntersecting) {
 
-       if (isIntersecting) {
 
-         this.fetch(true)
 
-       }
 
-     },
 
-     async fetch (silent = false) {
 
-       this.isLoading = true
 
-       try {
 
-         const results = await this.$apollo.query({
 
-           query: gql`
 
-             query ($locale: String!, $path: String!) {
 
-               comments {
 
-                 list(locale: $locale, path: $path) {
 
-                   id
 
-                   render
 
-                   authorName
 
-                   createdAt
 
-                   updatedAt
 
-                 }
 
-               }
 
-             }
 
-           `,
 
-           variables: {
 
-             locale: this.$store.get('page/locale'),
 
-             path: this.$store.get('page/path')
 
-           },
 
-           fetchPolicy: 'network-only'
 
-         })
 
-         this.comments = _.get(results, 'data.comments.list', []).map(c => {
 
-           const nameParts = c.authorName.toUpperCase().split(' ')
 
-           let initials = _.head(nameParts).charAt(0)
 
-           if (nameParts.length > 1) {
 
-             initials += _.last(nameParts).charAt(0)
 
-           }
 
-           c.initials = initials
 
-           return c
 
-         })
 
-       } catch (err) {
 
-         console.warn(err)
 
-         if (!silent) {
 
-           this.$store.commit('showNotification', {
 
-             style: 'red',
 
-             message: err.message,
 
-             icon: 'alert'
 
-           })
 
-         }
 
-       }
 
-       this.isLoading = false
 
-       this.hasLoadedOnce = true
 
-     },
 
-     /**
 
-      * Post New Comment
 
-      */
 
-     async postComment () {
 
-       let rules = {
 
-         comment: {
 
-           presence: {
 
-             allowEmpty: false
 
-           },
 
-           length: {
 
-             minimum: 2
 
-           }
 
-         }
 
-       }
 
-       if (!this.isAuthenticated && this.permissions.write) {
 
-         rules.name = {
 
-           presence: {
 
-             allowEmpty: false
 
-           },
 
-           length: {
 
-             minimum: 2,
 
-             maximum: 255
 
-           }
 
-         }
 
-         rules.email = {
 
-           presence: {
 
-             allowEmpty: false
 
-           },
 
-           email: true
 
-         }
 
-       }
 
-       const validationResults = validate({
 
-         comment: this.newcomment,
 
-         name: this.guestName,
 
-         email: this.guestEmail
 
-       }, rules, { format: 'flat' })
 
-       if (validationResults) {
 
-         this.$store.commit('showNotification', {
 
-           style: 'red',
 
-           message: validationResults[0],
 
-           icon: 'alert'
 
-         })
 
-         return
 
-       }
 
-       try {
 
-         const resp = await this.$apollo.mutate({
 
-           mutation: gql`
 
-             mutation (
 
-               $pageId: Int!
 
-               $replyTo: Int
 
-               $content: String!
 
-               $guestName: String
 
-               $guestEmail: String
 
-             ) {
 
-               comments {
 
-                 create (
 
-                   pageId: $pageId
 
-                   replyTo: $replyTo
 
-                   content: $content
 
-                   guestName: $guestName
 
-                   guestEmail: $guestEmail
 
-                 ) {
 
-                   responseResult {
 
-                     succeeded
 
-                     errorCode
 
-                     slug
 
-                     message
 
-                   }
 
-                   id
 
-                 }
 
-               }
 
-             }
 
-           `,
 
-           variables: {
 
-             pageId: this.pageId,
 
-             replyTo: 0,
 
-             content: this.newcomment,
 
-             guestName: this.guestName,
 
-             guestEmail: this.guestEmail
 
-           }
 
-         })
 
-         if (_.get(resp, 'data.comments.create.responseResult.succeeded', false)) {
 
-           this.$store.commit('showNotification', {
 
-             style: 'success',
 
-             message: 'New comment posted successfully.',
 
-             icon: 'check'
 
-           })
 
-           this.newcomment = ''
 
-           await this.fetch()
 
-           this.$nextTick(() => {
 
-             this.$vuetify.goTo(`#comment-post-id-${_.get(resp, 'data.comments.create.id', 0)}`, this.scrollOpts)
 
-           })
 
-         } else {
 
-           throw new Error(_.get(resp, 'data.comments.create.responseResult.message', 'An unexpected error occured.'))
 
-         }
 
-       } catch (err) {
 
-         this.$store.commit('showNotification', {
 
-           style: 'red',
 
-           message: err.message,
 
-           icon: 'alert'
 
-         })
 
-       }
 
-     },
 
-     /**
 
-      * Show Comment Editing Form
 
-      */
 
-     async editComment (cm) {
 
-       this.$store.commit(`loadingStart`, 'comments-edit')
 
-       this.isBusy = true
 
-       try {
 
-         const results = await this.$apollo.query({
 
-           query: gql`
 
-             query ($id: Int!) {
 
-               comments {
 
-                 single(id: $id) {
 
-                   content
 
-                 }
 
-               }
 
-             }
 
-           `,
 
-           variables: {
 
-             id: cm.id
 
-           },
 
-           fetchPolicy: 'network-only'
 
-         })
 
-         this.commentEditContent = _.get(results, 'data.comments.single.content', null)
 
-         if (this.commentEditContent === null) {
 
-           throw new Error('Failed to load comment content.')
 
-         }
 
-       } catch (err) {
 
-         console.warn(err)
 
-         this.$store.commit('showNotification', {
 
-           style: 'red',
 
-           message: err.message,
 
-           icon: 'alert'
 
-         })
 
-       }
 
-       this.commentEditId = cm.id
 
-       this.isBusy = false
 
-       this.$store.commit(`loadingStop`, 'comments-edit')
 
-     },
 
-     /**
 
-      * Cancel Comment Edit
 
-      */
 
-     editCommentCancel () {
 
-       this.commentEditId = 0
 
-       this.commentEditContent = null
 
-     },
 
-     /**
 
-      * Update Comment with new content
 
-      */
 
-     async updateComment () {
 
-       this.$store.commit(`loadingStart`, 'comments-edit')
 
-       this.isBusy = true
 
-       try {
 
-         if (this.commentEditContent.length < 2) {
 
-           throw new Error('Comment is empty or too short!')
 
-         }
 
-         const resp = await this.$apollo.mutate({
 
-           mutation: gql`
 
-             mutation (
 
-               $id: Int!
 
-               $content: String!
 
-             ) {
 
-               comments {
 
-                 update (
 
-                   id: $id,
 
-                   content: $content
 
-                 ) {
 
-                   responseResult {
 
-                     succeeded
 
-                     errorCode
 
-                     slug
 
-                     message
 
-                   }
 
-                   render
 
-                 }
 
-               }
 
-             }
 
-           `,
 
-           variables: {
 
-             id: this.commentEditId,
 
-             content: this.commentEditContent
 
-           }
 
-         })
 
-         if (_.get(resp, 'data.comments.update.responseResult.succeeded', false)) {
 
-           this.$store.commit('showNotification', {
 
-             style: 'success',
 
-             message: 'Comment was updated successfully.',
 
-             icon: 'check'
 
-           })
 
-           const cm = _.find(this.comments, ['id', this.commentEditId])
 
-           cm.render = _.get(resp, 'data.comments.update.render', '-- Failed to load updated comment --')
 
-           cm.updatedAt = (new Date()).toISOString()
 
-           this.editCommentCancel()
 
-         } else {
 
-           throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occured.'))
 
-         }
 
-       } catch (err) {
 
-         console.warn(err)
 
-         this.$store.commit('showNotification', {
 
-           style: 'red',
 
-           message: err.message,
 
-           icon: 'alert'
 
-         })
 
-       }
 
-       this.isBusy = false
 
-       this.$store.commit(`loadingStop`, 'comments-edit')
 
-     },
 
-     /**
 
-      * Show Delete Comment Confirmation Dialog
 
-      */
 
-     deleteCommentConfirm (cm) {
 
-       this.commentToDelete = cm
 
-       this.deleteCommentDialogShown = true
 
-     },
 
-     /**
 
-      * Delete Comment
 
-      */
 
-     async deleteComment () {
 
-       this.$store.commit(`loadingStart`, 'comments-delete')
 
-       this.isBusy = true
 
-       this.deleteCommentDialogShown = false
 
-       try {
 
-         const resp = await this.$apollo.mutate({
 
-           mutation: gql`
 
-             mutation (
 
-               $id: Int!
 
-             ) {
 
-               comments {
 
-                 delete (
 
-                   id: $id
 
-                 ) {
 
-                   responseResult {
 
-                     succeeded
 
-                     errorCode
 
-                     slug
 
-                     message
 
-                   }
 
-                 }
 
-               }
 
-             }
 
-           `,
 
-           variables: {
 
-             id: this.commentToDelete.id
 
-           }
 
-         })
 
-         if (_.get(resp, 'data.comments.delete.responseResult.succeeded', false)) {
 
-           this.$store.commit('showNotification', {
 
-             style: 'success',
 
-             message: 'Comment was deleted successfully.',
 
-             icon: 'check'
 
-           })
 
-           this.comments = _.reject(this.comments, ['id', this.commentToDelete.id])
 
-         } else {
 
-           throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occured.'))
 
-         }
 
-       } catch (err) {
 
-         this.$store.commit('showNotification', {
 
-           style: 'red',
 
-           message: err.message,
 
-           icon: 'alert'
 
-         })
 
-       }
 
-       this.isBusy = false
 
-       this.$store.commit(`loadingStop`, 'comments-delete')
 
-     }
 
-   }
 
- }
 
- </script>
 
- <style lang="scss">
 
- .comments-post {
 
-   position: relative;
 
-   &:hover {
 
-     .comments-post-actions {
 
-       opacity: 1;
 
-     }
 
-   }
 
-   &-actions {
 
-     position: absolute;
 
-     top: 16px;
 
-     right: 16px;
 
-     opacity: 0;
 
-     transition: opacity .4s ease;
 
-   }
 
-   &-content {
 
-     > p:first-child {
 
-       padding-top: 0;
 
-     }
 
-     p {
 
-       padding-top: 1rem;
 
-       margin-bottom: 0;
 
-     }
 
-     img {
 
-       max-width: 100%;
 
-       border-radius: 5px;
 
-     }
 
-     code {
 
-       background-color: rgba(mc('pink', '500'), .1);
 
-       box-shadow: none;
 
-     }
 
-     pre > code {
 
-       margin-top: 1rem;
 
-       padding: 12px;
 
-       background-color: #111;
 
-       box-shadow: none;
 
-       border-radius: 5px;
 
-       width: 100%;
 
-       color: #FFF;
 
-       font-weight: 400;
 
-       font-size: .85rem;
 
-       font-family: Roboto Mono, monospace;
 
-     }
 
-   }
 
- }
 
- </style>
 
 
  |