Skip to content

Commit

Permalink
Mark comments as read
Browse files Browse the repository at this point in the history
Signed-off-by: Christopher Ng <chrng8@gmail.com>
  • Loading branch information
Pytal committed May 2, 2023
1 parent 193e096 commit ccfb772
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 187 deletions.
28 changes: 25 additions & 3 deletions apps/comments/src/filesplugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*
*/

import { subscribe, unsubscribe } from '@nextcloud/event-bus'

(function() {

_.extend(OC.Files.Client, {
Expand All @@ -42,11 +44,27 @@
* @namespace
*/
OCA.Comments.FilesPlugin = {
_actionSpec: {
name: 'Comment',
mime: 'all',
className: '.action-comment',
},

ignoreLists: [
'trashbin',
'files.public',
],

_commentsReadHandler: () => {},

_handleCommentsRead(context, ressourceId) {
if (context.$file.data().id === ressourceId) {
const { mime, name, className } = this._actionSpec
context.fileList.fileActions.unregisterAction({ context, mime, name, className })
unsubscribe('comments:comments:read', this._commentsReadHandler)
}
},

_formatCommentCount(count) {
return OCA.Comments.Templates.filesplugin({
count,
Expand Down Expand Up @@ -90,7 +108,7 @@

// register "comment" action for reading comments
fileList.fileActions.registerAction({
name: 'Comment',
name: self._actionSpec.name,
displayName(context) {
if (context && context.$file) {
const unread = parseInt(context.$file.data('comments-unread'), 10)
Expand All @@ -100,7 +118,7 @@
}
return t('comments', 'Comment')
},
mime: 'all',
mime: self._actionSpec.mime,
order: -140,
iconClass: 'icon-comment',
permissions: OC.PERMISSION_READ,
Expand All @@ -109,14 +127,18 @@
const $file = context.$file
const unreadComments = $file.data('comments-unread')
if (unreadComments) {
self._commentsReadHandler = ({ ressourceId }) => self._handleCommentsRead(context, ressourceId)
// Remove unread comments icon on read
subscribe('comments:comments:read', self._commentsReadHandler)

const $actionLink = $(self._formatCommentCount(unreadComments))
context.$file.find('a.name>span.fileactions').append($actionLink)
return $actionLink
}
return ''
},
actionHandler(fileName, context) {
context.$file.find('.action-comment').tooltip('hide')
context.$file.find(self._actionSpec.className).tooltip('hide')
// open sidebar in comments section
OCA.Files.Sidebar.setActiveTab('comments')
OCA.Files.Sidebar.open(context.dir + '/' + fileName)
Expand Down
55 changes: 55 additions & 0 deletions apps/comments/src/services/ReadComments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @copyright 2023 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @license AGPL-3.0-or-later
*
* 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/>.
*
*/

import client from './DavClient.js'

import type { Response } from 'webdav'

/**
* Mark comments older than the date timestamp as read
*
* @param commentsType the ressource type
* @param ressourceId the ressource ID
* @param date the date object
*/
export const markCommentsAsRead = (
commentsType: string,
ressourceId: number,
date: Date,
): Promise<Response> => {
const ressourcePath = ['', commentsType, ressourceId].join('/')
const readMarker = date.toUTCString()

return client.customRequest(ressourcePath, {
method: 'PROPPATCH',
data: `<?xml version="1.0"?>
<d:propertyupdate
xmlns:d="DAV:"
xmlns:oc="http://owncloud.org/ns">
<d:set>
<d:prop>
<oc:readMarker>${readMarker}</oc:readMarker>
</d:prop>
</d:set>
</d:propertyupdate>`,
})
}
20 changes: 19 additions & 1 deletion apps/comments/src/views/Comments.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
-->

<template>
<div class="comments" :class="{ 'icon-loading': isFirstLoading }">
<div class="comments"
:class="{ 'icon-loading': isFirstLoading }"
v-observe-visibility="onVisibilityChange">
<!-- Editor -->
<Comment v-bind="editorData"
:auto-complete="autoComplete"
Expand Down Expand Up @@ -84,8 +86,11 @@ import { generateOcsUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import { loadState } from '@nextcloud/initial-state'
import axios from '@nextcloud/axios'
import { emit } from '@nextcloud/event-bus'
import { showError } from '@nextcloud/dialogs'
import VTooltip from 'v-tooltip'
import Vue from 'vue'
import VueObserveVisibility from 'vue-observe-visibility'

import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
Expand All @@ -96,8 +101,10 @@ import AlertCircleOutlineIcon from 'vue-material-design-icons/AlertCircleOutline
import Comment from '../components/Comment.vue'
import { getComments, DEFAULT_LIMIT } from '../services/GetComments.ts'
import cancelableRequest from '../utils/cancelableRequest.js'
import { markCommentsAsRead } from '../services/ReadComments.js'

Vue.use(VTooltip)
Vue.use(VueObserveVisibility)

export default {
name: 'Comments',
Expand Down Expand Up @@ -145,6 +152,17 @@ export default {
},

methods: {
async onVisibilityChange(isVisible) {
if (isVisible) {
try {
await markCommentsAsRead(this.commentsType, this.ressourceId, new Date())
emit('comments:comments:read', { ressourceId: this.ressourceId })
} catch (e) {
showError(e.message || t('comments', 'Failed to mark comments as read'))
}
}
},

/**
* Update current ressourceId and fetch new data
*
Expand Down
18 changes: 18 additions & 0 deletions apps/files/js/fileactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,24 @@
this.icons[name] = action.icon;
this._notifyUpdateListeners('registerAction', {action: action});
},

/**
* Unregister action
*
* @param {object} options options object
* @param {OCA.Files.FileActionContext} options.context action context
* @param {string} options.mime mime type
* @param {string} options.name action name
* @param {string} options.className action element class
*/
unregisterAction: function ({ context, mime, name, className }) {
const unregisteredAction = this.actions[mime][name]
delete this.actions[mime][name]
delete this.icons[name]
context.$file.find(className).remove()
this._notifyUpdateListeners('unregisterAction', { action: unregisteredAction })
},

/**
* Clears all registered file actions.
*/
Expand Down
1 change: 0 additions & 1 deletion apps/files/src/components/SidebarTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export default {
type: String,
required: false,
},

/**
* Lifecycle methods.
* They are prefixed with `on` to avoid conflict with Vue
Expand Down
Loading

0 comments on commit ccfb772

Please sign in to comment.