123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- <!--
- - @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
- -
- - @author John Molakvoæ <skjnldsv@protonmail.com>
- -
- - @license GNU AGPL version 3 or any later version
- -
- - This program is free software: you can redistribute it and/or modify
- - it under the terms of the GNU Affero General Public License as
- - published by the Free Software Foundation, either version 3 of the
- - License, or (at your option) any later version.
- -
- - This program is distributed in the hope that it will be useful,
- - but WITHOUT ANY WARRANTY; without even the implied warranty of
- - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- - GNU Affero General Public License for more details.
- -
- - You should have received a copy of the GNU Affero General Public License
- - along with this program. If not, see <http://www.gnu.org/licenses/>.
- -
- -->
-
- <template>
- <div class="comments" :class="{ 'icon-loading': isFirstLoading }">
- <!-- Editor -->
- <Comment v-bind="editorData"
- :auto-complete="autoComplete"
- :editor="true"
- :ressource-id="ressourceId"
- class="comments__writer"
- @new="onNewComment" />
-
- <template v-if="!isFirstLoading">
- <EmptyContent v-if="!hasComments && done" icon="icon-comment">
- {{ t('comments', 'No comments yet, start the conversation!') }}
- </EmptyContent>
-
- <!-- Comments -->
- <Comment v-for="comment in comments"
- v-else
- :key="comment.id"
- v-bind="comment"
- :auto-complete="autoComplete"
- :ressource-id="ressourceId"
- :message.sync="comment.message"
- class="comments__list"
- @delete="onDelete" />
-
- <!-- Loading more message -->
- <div v-if="loading && !isFirstLoading" class="comments__info icon-loading" />
-
- <div v-else-if="hasComments && done" class="comments__info">
- {{ t('comments', 'No more messages') }}
- </div>
-
- <!-- Error message -->
- <EmptyContent v-else-if="error" class="comments__error" icon="icon-error">
- {{ error }}
- <template #desc>
- <button icon="icon-history" @click="getComments">
- {{ t('comments', 'Retry') }}
- </button>
- </template>
- </EmptyContent>
- </template>
- </div>
- </template>
-
- <script>
- import { generateOcsUrl } from '@nextcloud/router'
- import { getCurrentUser } from '@nextcloud/auth'
- import axios from '@nextcloud/axios'
- import VTooltip from 'v-tooltip'
- import Vue from 'vue'
-
- import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
-
- import Comment from '../components/Comment'
- import getComments, { DEFAULT_LIMIT } from '../services/GetComments'
- import cancelableRequest from '../utils/cancelableRequest'
-
- Vue.use(VTooltip)
-
- export default {
- name: 'Comments',
-
- components: {
- // Avatar,
- Comment,
- EmptyContent,
- },
-
- data() {
- return {
- error: '',
- loading: false,
- done: false,
-
- ressourceId: null,
- offset: 0,
- comments: [],
-
- cancelRequest: () => {},
-
- editorData: {
- actorDisplayName: getCurrentUser().displayName,
- actorId: getCurrentUser().uid,
- key: 'editor',
- },
-
- Comment,
- }
- },
-
- computed: {
- hasComments() {
- return this.comments.length > 0
- },
- isFirstLoading() {
- return this.loading && this.offset === 0
- },
- },
-
- methods: {
- /**
- * Update current ressourceId and fetch new data
- * @param {Number} ressourceId the current ressourceId (fileId...)
- */
- async update(ressourceId) {
- this.ressourceId = ressourceId
- this.resetState()
- this.getComments()
- },
-
- /**
- * Ran when the bottom of the tab is reached
- */
- onScrollBottomReached() {
- /**
- * Do not fetch more if we:
- * - are showing an error
- * - already fetched everything
- * - are currently loading
- */
- if (this.error || this.done || this.loading) {
- return
- }
- this.getComments()
- },
-
- /**
- * Get the existing shares infos
- */
- async getComments() {
- // Cancel any ongoing request
- this.cancelRequest('cancel')
-
- try {
- this.loading = true
- this.error = ''
-
- // Init cancellable request
- const { request, cancel } = cancelableRequest(getComments)
- this.cancelRequest = cancel
-
- // Fetch comments
- const comments = await request({
- commentsType: this.commentsType,
- ressourceId: this.ressourceId,
- }, { offset: this.offset })
-
- this.logger.debug(`Processed ${comments.length} comments`, { comments })
-
- // We received less than the requested amount,
- // we're done fetching comments
- if (comments.length < DEFAULT_LIMIT) {
- this.done = true
- }
-
- // Insert results
- this.comments.push(...comments)
-
- // Increase offset for next fetch
- this.offset += DEFAULT_LIMIT
- } catch (error) {
- if (error.message === 'cancel') {
- return
- }
- this.error = t('comments', 'Unable to load the comments list')
- console.error('Error loading the comments list', error)
- } finally {
- this.loading = false
- }
- },
-
- /**
- * Autocomplete @mentions
- * @param {string} search the query
- * @param {Function} callback the callback to process the results with
- */
- async autoComplete(search, callback) {
- const results = await axios.get(generateOcsUrl('core', 2) + 'autocomplete/get', {
- params: {
- search,
- itemType: 'files',
- itemId: this.ressourceId,
- sorter: 'commenters|share-recipients',
- limit: OC.appConfig?.comments?.maxAutoCompleteResults || 25,
- },
- })
- return callback(results.data.ocs.data)
- },
-
- /**
- * Add newly created comment to the list
- * @param {Object} comment the new comment
- */
- onNewComment(comment) {
- this.comments.unshift(comment)
- },
-
- /**
- * Remove deleted comment from the list
- * @param {number} id the deleted comment
- */
- onDelete(id) {
- const index = this.comments.findIndex(comment => comment.id === id)
- if (index > -1) {
- this.comments.splice(index, 1)
- } else {
- console.error('Could not find the deleted comment in the list', id)
- }
- },
-
- /**
- * Reset the current view to its default state
- */
- resetState() {
- this.error = ''
- this.loading = false
- this.done = false
- this.offset = 0
- this.comments = []
- },
- },
- }
- </script>
-
- <style lang="scss" scoped>
- .comments {
- // Do not add emptycontent top margin
- &__error{
- margin-top: 0;
- }
-
- &__info {
- height: 60px;
- color: var(--color-text-maxcontrast);
- text-align: center;
- line-height: 60px;
- }
- }
- </style>
|