From 37cce9d295ed8e342fe6382164e09c0758928148 Mon Sep 17 00:00:00 2001 From: JuliaSvinareva Date: Sun, 28 Feb 2021 20:47:47 +0300 Subject: [PATCH 1/3] [mobile] Comments (add actions: resolve/reopen, delete comment; check canViewComments to display list of comments; change styles) --- .../lib/controller/collaboration/Comments.jsx | 199 +++++++++++++++++- .../lib/view/collaboration/Collaboration.jsx | 13 +- .../lib/view/collaboration/Comments.jsx | 146 +++++++++++-- .../mobile/resources/less/comments.less | 43 ++-- .../mobile/resources/less/common-ios.less | 3 + apps/common/mobile/resources/less/common.less | 1 - .../mobile/resources/less/ios/comments.less | 22 ++ apps/documenteditor/mobile/locale/en.json | 11 +- 8 files changed, 391 insertions(+), 47 deletions(-) diff --git a/apps/common/mobile/lib/controller/collaboration/Comments.jsx b/apps/common/mobile/lib/controller/collaboration/Comments.jsx index 41a8d57b5..286a8c3bc 100644 --- a/apps/common/mobile/lib/controller/collaboration/Comments.jsx +++ b/apps/common/mobile/lib/controller/collaboration/Comments.jsx @@ -1,10 +1,10 @@ -import React, {Component} from 'react'; +import React, {Component, Fragment} from 'react'; import { inject, observer } from "mobx-react"; import { f7 } from 'framework7-react'; import {Device} from '../../../../../common/mobile/utils/device'; import { withTranslation} from 'react-i18next'; -import {AddComment, ViewComments} from '../../view/collaboration/Comments'; +import {AddComment, EditComment, ViewComments} from '../../view/collaboration/Comments'; // utils const timeZoneOffsetInMs = (new Date()).getTimezoneOffset() * 60000; @@ -255,21 +255,210 @@ class AddCommentController extends Component { } class ViewCommentsController extends Component { - constructor(props) { + constructor (props) { super(props); + this.onCommentMenuClick = this.onCommentMenuClick.bind(this); + this.onResolveComment = this.onResolveComment.bind(this); + this.onEditComment = this.onEditComment.bind(this); + this.closeEditComment = this.closeEditComment.bind(this); + + this.currentUser = this.props.users.currentUser; + + this.state = { + showEditComment: false, + showEditReply: false + }; + } + onChangeComment (comment) { + const ascComment = !!Asc.asc_CCommentDataWord ? new Asc.asc_CCommentDataWord(null) : new Asc.asc_CCommentData(null); + if (ascComment && comment) { + ascComment.asc_putText(comment.comment); + ascComment.asc_putQuoteText(comment.quote); + ascComment.asc_putTime(utcDateToString(new Date(comment.time))); + ascComment.asc_putOnlyOfficeTime(ooDateToString(new Date(comment.time))); + ascComment.asc_putUserId(comment.userId); + ascComment.asc_putUserName(comment.userName); + ascComment.asc_putSolved(comment.resolved); + ascComment.asc_putGuid(comment.guid); + + if (!!ascComment.asc_putDocumentFlag) { + ascComment.asc_putDocumentFlag(comment.unattached); + } + + var reply = comment.replies; + if (reply && reply.length > 0) { + reply.forEach((reply) => { + var addReply = (!!Asc.asc_CCommentDataWord ? new Asc.asc_CCommentDataWord(null) : new Asc.asc_CCommentData(null)); + if (addReply) { + addReply.asc_putText(reply.reply); + addReply.asc_putTime(utcDateToString(new Date(reply.time))); + addReply.asc_putOnlyOfficeTime(ooDateToString(new Date(reply.time))); + addReply.asc_putUserId(reply.userId); + addReply.asc_putUserName(reply.userName); + + ascComment.asc_addReply(addReply); + } + }); + } + const api = Common.EditorApi.get(); + api.asc_changeComment(comment.uid, ascComment); + } + } + onResolveComment (comment) { + let reply = null, + addReply = null, + ascComment = (!!Asc.asc_CCommentDataWord ? new Asc.asc_CCommentDataWord(null) : new Asc.asc_CCommentData(null)); + + if (ascComment && comment) { + ascComment.asc_putText(comment.comment); + ascComment.asc_putQuoteText(comment.quote); + ascComment.asc_putTime(utcDateToString(new Date(comment.time))); + ascComment.asc_putOnlyOfficeTime(ooDateToString(new Date(comment.time))); + ascComment.asc_putUserId(comment.userId); + ascComment.asc_putUserName(comment.userName); + ascComment.asc_putSolved(!comment.resolved); + ascComment.asc_putGuid(comment.guid); + + if (!!ascComment.asc_putDocumentFlag) { + ascComment.asc_putDocumentFlag(comment.unattached); + } + + reply = comment.replies; + if (reply && reply.length > 0) { + reply.forEach((reply) => { + addReply = (!!Asc.asc_CCommentDataWord ? new Asc.asc_CCommentDataWord(null) : new Asc.asc_CCommentData(null)); + if (addReply) { + addReply.asc_putText(reply.reply); + addReply.asc_putTime(utcDateToString(new Date(reply.time))); + addReply.asc_putOnlyOfficeTime(ooDateToString(new Date(reply.time))); + addReply.asc_putUserId(reply.userId); + addReply.asc_putUserName(reply.userName); + + ascComment.asc_addReply(addReply); + } + }); + } + const api = Common.EditorApi.get(); + api.asc_changeComment(comment.uid, ascComment); + } + } + deleteComment (comment) { + const api = Common.EditorApi.get(); + comment && api.asc_removeComment(comment.uid); + } + onEditComment (comment, text) { + comment.comment = text.trim(); + comment.userid = this.currentUser.asc_getIdOriginal(); + comment.username = this.currentUser.asc_getUserName(); + this.onChangeComment(comment); + } + deleteReply (comment, indReply) { + let replies = null, + addReply = null, + ascComment = (!!Asc.asc_CCommentDataWord ? new Asc.asc_CCommentDataWord(null) : new Asc.asc_CCommentData(null)); + + if (ascComment && comment) { + ascComment.asc_putText(comment.comment); + ascComment.asc_putQuoteText(comment.quote); + ascComment.asc_putTime(utcDateToString(new Date(comment.time))); + ascComment.asc_putOnlyOfficeTime(ooDateToString(new Date(comment.time))); + ascComment.asc_putUserId(comment.userId); + ascComment.asc_putUserName(comment.userName); + ascComment.asc_putSolved(comment.resolved); + ascComment.asc_putGuid(comment.guid); + + if (!!ascComment.asc_putDocumentFlag) { + ascComment.asc_putDocumentFlag(comment.unattached); + } + + replies = comment.replies; + if (replies && replies.length) { + replies.forEach((reply) => { + if (reply.ind !== indReply) { + addReply = (!!Asc.asc_CCommentDataWord ? new Asc.asc_CCommentDataWord(null) : new Asc.asc_CCommentData(null)); + if (addReply) { + addReply.asc_putText(reply.reply); + addReply.asc_putTime(utcDateToString(new Date(reply.time))); + addReply.asc_putOnlyOfficeTime(ooDateToString(new Date(reply.time))); + addReply.asc_putUserId(reply.userId); + addReply.asc_putUserName(reply.userName); + + ascComment.asc_addReply(addReply); + } + } + }); + } + const api = Common.EditorApi.get(); + api.asc_changeComment(comment.uid, ascComment); + } + } + onCommentMenuClick (action, comment) { + const { t } = this.props; + const _t = t("Common.Collaboration", { returnObjects: true }); + switch (action) { + case 'editComment': + this.setState({ + showEditComment: true, + editProps: { + comment: comment, + onEditComment: this.onEditComment + } + }); + console.log('editComment'); + break; + case 'resolve': + this.onResolveComment(comment); + break; + case 'deleteComment': + f7.dialog.confirm( + _t.textMessageDeleteComment, + _t.textDeleteComment, + () => { + this.deleteComment(comment); + } + ); + break; + case 'editReply': + this.setState({showEditReply: true}); + console.log('editReply'); + break; + case 'deleteReply': + f7.dialog.confirm( + _t.textMessageDeleteReply, + _t.textDeleteReply, + () => { + this.deleteReply(comment, indReply); + } + ); + break; + case 'addReply': + console.log('addReply'); + break; + } + } + + closeEditComment () { + this.setState({showEditComment: false}); } render() { return( - + + + {this.state.showEditComment && } + ) } } const _CommentsController = inject('storeAppOptions', 'storeComments', 'users')(observer(CommentsController)); const _AddCommentController = inject('storeAppOptions', 'storeComments', 'users')(observer(AddCommentController)); +const _ViewCommentsController = inject('storeComments', 'users')(observer(withTranslation()(ViewCommentsController))); export { _CommentsController as CommentsController, _AddCommentController as AddCommentController, - ViewCommentsController + _ViewCommentsController as ViewCommentsController }; \ No newline at end of file diff --git a/apps/common/mobile/lib/view/collaboration/Collaboration.jsx b/apps/common/mobile/lib/view/collaboration/Collaboration.jsx index eb6496a92..76f48102c 100644 --- a/apps/common/mobile/lib/view/collaboration/Collaboration.jsx +++ b/apps/common/mobile/lib/view/collaboration/Collaboration.jsx @@ -53,7 +53,7 @@ const routes = [ } ]; -const PageCollaboration = props => { +const PageCollaboration = inject('storeAppOptions')(observer(props => { const { t } = useTranslation(); const _t = t('Common.Collaboration', {returnObjects: true}); return ( @@ -72,9 +72,11 @@ const PageCollaboration = props => { - - - + {props.storeAppOptions.canViewComments && + + + + } {window.editorType === 'de' && @@ -85,7 +87,7 @@ const PageCollaboration = props => { ) -}; +})); class CollaborationView extends Component { constructor(props) { @@ -132,6 +134,5 @@ const Collaboration = props => { ) }; -// export withTranslation()(CollaborationPopover); export {PageCollaboration} export default Collaboration; diff --git a/apps/common/mobile/lib/view/collaboration/Comments.jsx b/apps/common/mobile/lib/view/collaboration/Comments.jsx index 17b77316f..10bb4a25f 100644 --- a/apps/common/mobile/lib/view/collaboration/Comments.jsx +++ b/apps/common/mobile/lib/view/collaboration/Comments.jsx @@ -1,6 +1,6 @@ -import React, {useState, useEffect} from 'react'; +import React, {useState, useEffect, Fragment} from 'react'; import {observer, inject} from "mobx-react"; -import { f7, Popup, Page, Navbar, NavLeft, NavRight, NavTitle, Link, Input, Icon, List, ListItem } from 'framework7-react'; +import { f7, Popup, Page, Navbar, NavLeft, NavRight, NavTitle, Link, Input, Icon, List, ListItem, Actions, ActionsGroup, ActionsButton } from 'framework7-react'; import { useTranslation } from 'react-i18next'; import {Device} from '../../../utils/device'; @@ -39,7 +39,7 @@ const AddCommentPopup = inject("storeComments")(observer(props => {
{Device.android && -
{userInfo.initials}
+
{userInfo.initials}
}
{userInfo.name}
@@ -123,12 +123,107 @@ const AddComment = props => { ) }; -// View comments -const ViewComments = ({storeComments}) => { +// Actions +const CommentActions = ({comment, onCommentMenuClick, opened, openActionComment}) => { const { t } = useTranslation(); const _t = t('Common.Collaboration', {returnObjects: true}); + return ( + openActionComment(false)}> + + {comment && + {comment.editable && {onCommentMenuClick('editComment', comment);}}>{_t.textEdit}} + {!comment.resolved ? + {onCommentMenuClick('resolve', comment);}}>{_t.textResolve} : + {onCommentMenuClick('resolve', comment);}}>{_t.textReopen} + } + {onCommentMenuClick('addReply', comment);}}>{_t.textAddReply} + {comment.removable && {onCommentMenuClick('deleteComment', comment);}}>{_t.textDeleteComment}} + + } + + + {_t.textCancel} + + + ) +}; +// Edit comment +const EditCommentPopup = ({comment, onEditComment, opened, close}) => { + const { t } = useTranslation(); + const _t = t('Common.Collaboration', {returnObjects: true}); + const [stateText, setText] = useState(comment.comment); + console.log(comment); + return ( + + + + { + close(); + //f7.popup.close('.edit-comment-popup'); + }}>{_t.textCancel} + + {_t.textEditComment} + + { + onEditComment(comment, stateText); + close(); + //f7.popup.close('.edit-comment-popup'); + }} + > + {Device.android ? : _t.textDone} + + + +
+
+ {Device.android && +
{comment.userInitials}
+ } +
+
{comment.userName}
+
{comment.date}
+
+
+
+ {setText(event.target.value);}}> +
+
+
+ ) +}; + +const EditComment = ({editProps, opened, close}) => { + return ( + Device.phone ? + : + + ) +}; + +// View comments +const ViewComments = ({storeComments, storeAppOptions, onCommentMenuClick, onResolveComment}) => { + const { t } = useTranslation(); + const _t = t('Common.Collaboration', {returnObjects: true}); + const isAndroid = Device.android; + + const viewMode = !storeAppOptions.canComments; const comments = storeComments.sortComments; + const sliceQuote = (text) => { + if (text) { + let sliced = text.slice(0, 100); + if (sliced.length < text.length) { + sliced += '...'; + return sliced; + } + return text; + } + }; + + const [clickComment, setClickComment] = useState(); + const [commentActionsOpened, openActionComment] = useState(false); + return ( @@ -138,12 +233,25 @@ const ViewComments = ({storeComments}) => { {comments.map((comment, indexComment) => { return ( -
-
{comment.userName}
-
{comment.date}
+
+
+ {isAndroid &&
{comment.userInitials}
} +
+
{comment.userName}
+
{comment.date}
+
+
+ {!viewMode && +
+
{onResolveComment(comment);}}>
+
{setClickComment(comment); openActionComment(true);}} + >
+
+ }
-
{comment.quote}
+ {comment.quote &&
{sliceQuote(comment.quote)}
}
{comment.comment}
{comment.replies.length > 0 &&
    @@ -155,9 +263,19 @@ const ViewComments = ({storeComments}) => {
    -
    -
    {reply.userName}
    -
    {reply.date}
    +
    +
    + {isAndroid &&
    {reply.userInitials}
    } +
    +
    {reply.userName}
    +
    {reply.date}
    +
    +
    + {!viewMode && +
    +
    +
    + }
    {reply.reply}
    @@ -177,13 +295,15 @@ const ViewComments = ({storeComments}) => { } + ) }; -const _ViewComments = inject('storeComments')(observer(ViewComments)); +const _ViewComments = inject('storeComments', 'storeAppOptions')(observer(ViewComments)); export { AddComment, + EditComment, _ViewComments as ViewComments } diff --git a/apps/common/mobile/resources/less/comments.less b/apps/common/mobile/resources/less/comments.less index 1dd0092d0..abc2140ad 100644 --- a/apps/common/mobile/resources/less/comments.less +++ b/apps/common/mobile/resources/less/comments.less @@ -39,9 +39,25 @@ } .comment-list { - .item-inner { + .item-content .item-inner { padding-right: 0; padding-bottom: 0; + padding-top: 16px; + .comment-header { + display: flex; + justify-content: space-between; + padding-right: 16px; + .right { + display: flex; + justify-content: space-between; + width: 70px; + } + } + .reply-header { + display: flex; + justify-content: space-between; + padding-right: 16px; + } } .item-title { width: 100%; @@ -77,29 +93,14 @@ padding-right: 15px; pre { white-space: pre-wrap; + overflow-wrap: break-word; } } .list-reply { padding-left: 26px; } - .reply-item { - &:after { - content: none; - } - &:before { - content: ''; - position: absolute; - left: auto; - bottom: 0; - right: auto; - top: 0; - height: 1px; - width: 100%; - background-color: @separator-color; - display: block; - z-index: 15; - -webkit-transform-origin: 50% 100%; - transform-origin: 50% 100%; - } - } +} + +.edit-comment-popup { + z-index: 20000; } \ No newline at end of file diff --git a/apps/common/mobile/resources/less/common-ios.less b/apps/common/mobile/resources/less/common-ios.less index d82f183f3..ea41f1284 100644 --- a/apps/common/mobile/resources/less/common-ios.less +++ b/apps/common/mobile/resources/less/common-ios.less @@ -1,6 +1,9 @@ .device-ios { @blockTitleColor: #6d6d72; + @item-border-color: #c8c7cc; + + --f7-list-item-border-color: @item-border-color; --f7-navbar-link-color: @themeColor; --f7-navbar-text-color: @black; diff --git a/apps/common/mobile/resources/less/common.less b/apps/common/mobile/resources/less/common.less index 77a1b4a64..690e48226 100644 --- a/apps/common/mobile/resources/less/common.less +++ b/apps/common/mobile/resources/less/common.less @@ -9,7 +9,6 @@ @autoColor: @black; @comment-date: #6d6d72; -@separator-color: #c8c7cc; .popup, .popover, .sheet-modal { .list { diff --git a/apps/common/mobile/resources/less/ios/comments.less b/apps/common/mobile/resources/less/ios/comments.less index ab3627426..0f25bb353 100644 --- a/apps/common/mobile/resources/less/ios/comments.less +++ b/apps/common/mobile/resources/less/ios/comments.less @@ -1,2 +1,24 @@ .device-ios { + .comment-list { + .reply-item { + .item-inner:after { + content: none !important; + } + &:before { + content: ''; + position: absolute; + left: auto; + bottom: 0; + right: auto; + top: 0; + height: 1px; + width: 100%; + background-color: var(--f7-list-item-border-color); + display: block; + z-index: 15; + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + } + } + } } \ No newline at end of file diff --git a/apps/documenteditor/mobile/locale/en.json b/apps/documenteditor/mobile/locale/en.json index 3c63789cd..00f9f0ea6 100644 --- a/apps/documenteditor/mobile/locale/en.json +++ b/apps/documenteditor/mobile/locale/en.json @@ -116,7 +116,16 @@ "textAddComment": "Add Comment", "textCancel": "Cancel", "textDone": "Done", - "textNoComments": "This document doesn't contain comments" + "textNoComments": "This document doesn't contain comments", + "textEdit": "Edit", + "textResolve": "Resolve", + "textReopen": "Reopen", + "textAddReply": "Add Reply", + "textDeleteComment": "Delete Comment", + "textMessageDeleteComment": "Do you really want to delete this comment?", + "textMessageDeleteReply": "Do you really want to delete this reply?", + "textDeleteReply": "Delete Reply", + "textEditComment": "Edit Comment" } }, "Settings": { From dd78ba939543718a91361786d2f4ecc28cc7c7d8 Mon Sep 17 00:00:00 2001 From: Maxim Kadushkin Date: Mon, 1 Mar 2021 21:55:58 +0300 Subject: [PATCH 2/3] [mobile] fixed context menu --- apps/common/mobile/lib/controller/ContextMenu.jsx | 2 +- apps/common/mobile/lib/view/ContextMenu.jsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/common/mobile/lib/controller/ContextMenu.jsx b/apps/common/mobile/lib/controller/ContextMenu.jsx index 137a16ee9..ef0f50118 100644 --- a/apps/common/mobile/lib/controller/ContextMenu.jsx +++ b/apps/common/mobile/lib/controller/ContextMenu.jsx @@ -24,7 +24,7 @@ class ContextMenuController extends Component { this.$targetEl = $$(idCntextMenuTargetElement); if ( !this.$targetEl.length ) { // this.$targetEl = $$('
    '); - this.$targetEl = $$(`
    `); + this.$targetEl = $$(`
    `); this.$targetEl.css({left: '-10000px', top: '-10000px'}); $$('#editor_sdk').append(this.$targetEl); diff --git a/apps/common/mobile/lib/view/ContextMenu.jsx b/apps/common/mobile/lib/view/ContextMenu.jsx index efce826a2..954b15e5b 100644 --- a/apps/common/mobile/lib/view/ContextMenu.jsx +++ b/apps/common/mobile/lib/view/ContextMenu.jsx @@ -38,4 +38,5 @@ class ContextMenuView extends Component { } } -export {ContextMenuView as default, idContextMenuElement}; \ No newline at end of file +const exportedIdMenuElemen = `#${idContextMenuElement}`; +export {ContextMenuView as default, exportedIdMenuElemen as idContextMenuElement}; \ No newline at end of file From 2a90efa7b0cb0d611c03de701e9b0518bcb0a2f1 Mon Sep 17 00:00:00 2001 From: JuliaSvinareva Date: Mon, 1 Mar 2021 23:09:39 +0300 Subject: [PATCH 3/3] [mobile] Comments (applying comment display) --- .../lib/controller/collaboration/Comments.jsx | 12 ++++++++++++ .../controller/settings/ApplicationSettings.jsx | 14 +++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/apps/common/mobile/lib/controller/collaboration/Comments.jsx b/apps/common/mobile/lib/controller/collaboration/Comments.jsx index 286a8c3bc..a55dff87d 100644 --- a/apps/common/mobile/lib/controller/collaboration/Comments.jsx +++ b/apps/common/mobile/lib/controller/collaboration/Comments.jsx @@ -3,6 +3,7 @@ import { inject, observer } from "mobx-react"; import { f7 } from 'framework7-react'; import {Device} from '../../../../../common/mobile/utils/device'; import { withTranslation} from 'react-i18next'; +import { LocalStorage } from '../../../utils/LocalStorage'; import {AddComment, EditComment, ViewComments} from '../../view/collaboration/Comments'; @@ -64,6 +65,17 @@ class CommentsController extends Component { Common.Notifications.on('configOptionsFill', () => { this.curUserId = this.appOptions.user.id; }); + + Common.Notifications.on('document:ready', () => { + if (window.editorType === 'de' || window.editorType === 'sse') { + const api = Common.EditorApi.get(); + /** coauthoring begin **/ + const isLiveCommenting = LocalStorage.getBool(`${window.editorType}-mobile-settings-livecomment`, true); + const resolved = LocalStorage.getBool(`${window.editorType}-settings-resolvedcomment`, true); + isLiveCommenting ? api.asc_showComments(resolved) : api.asc_hideComments(); + /** coauthoring end **/ + } + }); } addComment (id, data) { const comment = this.readSDKComment(id, data); diff --git a/apps/documenteditor/mobile/src/controller/settings/ApplicationSettings.jsx b/apps/documenteditor/mobile/src/controller/settings/ApplicationSettings.jsx index b3914a9c6..39b4fd77e 100644 --- a/apps/documenteditor/mobile/src/controller/settings/ApplicationSettings.jsx +++ b/apps/documenteditor/mobile/src/controller/settings/ApplicationSettings.jsx @@ -42,21 +42,21 @@ class ApplicationSettingsController extends Component { if (!value) { api.asc_hideComments(); this.switchDisplayResolved(value); - // Common.localStorage.setBool("de-settings-resolvedcomment", false); + LocalStorage.setBool("de-settings-resolvedcomment", false); } else { - // let resolved = Common.localStorage.getBool("de-settings-resolvedcomment"); - api.asc_showComments(value); + const resolved = LocalStorage.getBool("de-settings-resolvedcomment"); + api.asc_showComments(resolved); } - // Common.localStorage.setBool("de-mobile-settings-livecomment", value); + LocalStorage.setBool("de-mobile-settings-livecomment", value); } switchDisplayResolved(value) { const api = Common.EditorApi.get(); - // let displayComments = Common.localStorage.getBool("de-mobile-settings-livecomment"); - if (value) { + const displayComments = LocalStorage.getBool("de-mobile-settings-livecomment"); + if (displayComments) { api.asc_showComments(value); + LocalStorage.setBool("de-settings-resolvedcomment", value); } - // Common.localStorage.setBool("de-settings-resolvedcomment", value); } setMacrosSettings(value) {