diff --git a/apps/spreadsheeteditor/mobile/locale/en.json b/apps/spreadsheeteditor/mobile/locale/en.json index 6fdb89f96..1ddc9b4b7 100644 --- a/apps/spreadsheeteditor/mobile/locale/en.json +++ b/apps/spreadsheeteditor/mobile/locale/en.json @@ -6,6 +6,29 @@ "textAnonymous": "Anonymous" } }, + "ContextMenu": { + "menuViewComment": "View Comment", + "menuAddComment": "Add Comment", + "menuMore": "More", + "menuCancel": "Cancel", + "textCopyCutPasteActions": "Copy, Cut and Paste Actions", + "errorCopyCutPaste": "Copy, cut and paste actions using the context menu will be performed within the current file only.", + "textDoNotShowAgain": "Don't show again", + "warnMergeLostData": "Operation can destroy data in the selected cells. Continue?", + "notcriticalErrorTitle": "Warning", + "menuAddLink": "Add Link", + "menuOpenLink": "Open Link", + "menuUnfreezePanes": "Unfreeze Panes", + "menuFreezePanes": "Freeze Panes", + "menuUnwrap": "Unwrap", + "menuWrap": "Wrap", + "menuUnmerge": "Unmerge", + "menuCell": "Cell", + "menuShow": "Show", + "menuHide": "Hide", + "menuEdit": "Edit", + "menuDelete": "Delete" + }, "View" : { "Add" : { "textChart": "Chart", diff --git a/apps/spreadsheeteditor/mobile/src/controller/ContextMenu.jsx b/apps/spreadsheeteditor/mobile/src/controller/ContextMenu.jsx new file mode 100644 index 000000000..bad773d42 --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/controller/ContextMenu.jsx @@ -0,0 +1,375 @@ +import React, { useContext } from 'react'; +import { f7 } from 'framework7-react'; +import { inject, observer } from "mobx-react"; +import { withTranslation} from 'react-i18next'; +import { LocalStorage } from '../../../../common/mobile/utils/LocalStorage'; + +import ContextMenuController from '../../../../common/mobile/lib/controller/ContextMenu'; +import { idContextMenuElement } from '../../../../common/mobile/lib/view/ContextMenu'; +import { Device } from '../../../../common/mobile/utils/device'; + +@inject ( stores => ({ + isEdit: stores.storeAppOptions.isEdit, + canViewComments: stores.storeAppOptions.canViewComments, + users: stores.users, + isDisconnected: stores.users.isDisconnected +})) +class ContextMenu extends ContextMenuController { + constructor(props) { + super(props); + + // console.log('context menu controller created'); + this.onApiShowComment = this.onApiShowComment.bind(this); + this.onApiHideComment = this.onApiHideComment.bind(this); + this.getUserName = this.getUserName.bind(this); + } + + static closeContextMenu() { + f7.popover.close(idContextMenuElement, false); + } + + getUserName(id) { + const user = this.props.users.searchUserByCurrentId(id); + return Common.Utils.UserInfoParser.getParsedName(user.asc_getUserName()); + } + + componentWillUnmount() { + super.componentWillUnmount(); + + const api = Common.EditorApi.get(); + api.asc_unregisterCallback('asc_onShowComment', this.onApiShowComment); + api.asc_unregisterCallback('asc_onHideComment', this.onApiHideComment); + } + + + onApiShowComment(comments) { + this.isComments = comments && comments.length > 0; + } + + onApiHideComment() { + this.isComments = false; + } + + // onMenuClosed() { + // super.onMenuClosed(); + // } + + onMenuItemClick(action) { + const { t } = this.props; + const _t = t("ContextMenu", { returnObjects: true }); + + super.onMenuItemClick(action); + + const api = Common.EditorApi.get(); + const info = api.asc_getCellInfo(); + switch (action) { + case 'cut': + if (!api.asc_Cut() && !LocalStorage.getBool("sse-hide-copy-cut-paste-warning")) { + this.showCopyCutPasteModal(); + } + break; + case 'copy': + if (!api.asc_Copy() && !LocalStorage.getBool("sse-hide-copy-cut-paste-warning")) { + this.showCopyCutPasteModal(); + } + break; + case 'paste': + if (!api.asc_Paste() && !LocalStorage.getBool("sse-hide-copy-cut-paste-warning")) { + this.showCopyCutPasteModal(); + } + break; + case 'addcomment': + Common.Notifications.trigger('addcomment'); + break; + case 'viewcomment': + Common.Notifications.trigger('viewcomment'); + break; + case 'del': + api.asc_emptyCells(Asc.c_oAscCleanOptions.All); + break; + case 'wrap': + api.asc_setCellTextWrap(true); + break; + case 'unwrap': + api.asc_setCellTextWrap(false); + break; + case 'edit': + setTimeout(() => { + this.props.openOptions('edit'); + }, 0); + break; + case 'merge': + if (api.asc_mergeCellsDataLost(Asc.c_oAscMergeOptions.Merge)) { + setTimeout(() => { + f7.dialog.confirm(_t.warnMergeLostData, _t.notcriticalErrorTitle, () => { + api.asc_mergeCells(Asc.c_oAscMergeOptions.Merge); + }); + }, 0); + } else { + api.asc_mergeCells(Asc.c_oAscMergeOptions.Merge); + } + break; + case 'unmerge': + api.asc_mergeCells(Asc.c_oAscMergeOptions.None); + break; + case 'hide': + api[info.asc_getSelectionType() == Asc.c_oAscSelectionType.RangeRow ? 'asc_hideRows' : 'asc_hideColumns'](); + break; + case 'show': + api[info.asc_getSelectionType() == Asc.c_oAscSelectionType.RangeRow ? 'asc_showRows' : 'asc_showColumns'](); + break; + case 'addlink': + setTimeout(() => { + this.props.openOptions('add', 'link'); + }, 400) + break; + case 'openlink': + const linkinfo = info.asc_getHyperlink(); + if ( linkinfo.asc_getType() == Asc.c_oAscHyperlinkType.RangeLink ) { + const nameSheet = linkinfo.asc_getSheet(); + const curActiveSheet = api.asc_getActiveWorksheetIndex(); + api.asc_setWorksheetRange(linkinfo); + //SSE.getController('Statusbar').onLinkWorksheetRange(nameSheet, curActiveSheet); + } else { + const url = linkinfo.asc_getHyperlinkUrl().replace(/\s/g, "%20"); + api.asc_getUrlType(url) > 0 && this.openLink(url); + } + break; + case 'freezePanes': + api.asc_freezePane(); + break; + } + + console.log("click context menu item: " + action); + } + + showCopyCutPasteModal() { + const { t } = this.props; + const _t = t("ContextMenu", { returnObjects: true }); + f7.dialog.create({ + title: _t.textCopyCutPasteActions, + text: _t.errorCopyCutPaste, + content: `
+ + ${_t.textDoNotShowAgain} +
`, + buttons: [{ + text: 'OK', + onClick: () => { + const dontShow = $$('input[name="checkbox-show"]').prop('checked'); + if (dontShow) LocalStorage.setItem("de-hide-copy-cut-paste-warning", 1); + } + }] + }).open(); + } + + openLink(url) { + const newDocumentPage = window.open(url, '_blank'); + + if (newDocumentPage) { + newDocumentPage.focus(); + } + } + + onDocumentReady() { + super.onDocumentReady(); + + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onShowComment', this.onApiShowComment); + api.asc_registerCallback('asc_onHideComment', this.onApiHideComment); + } + + initMenuItems() { + if ( !Common.EditorApi ) return []; + + const { t } = this.props; + const _t = t("ContextMenu", { returnObjects: true }); + + const { isEdit, canViewComments, isDisconnected } = this.props; + + const api = Common.EditorApi.get(); + const cellinfo = api.asc_getCellInfo(); + + const itemsIcon = []; + const itemsText = []; + + let iscellmenu, isrowmenu, iscolmenu, isallmenu, ischartmenu, isimagemenu, istextshapemenu, isshapemenu, istextchartmenu; + let iscelllocked = cellinfo.asc_getLocked(); + const seltype = cellinfo.asc_getSelectionType(); + const xfs = cellinfo.asc_getXfs(); + const isComments = cellinfo.asc_getComments().length > 0; //prohibit adding multiple comments in one cell; + + switch (seltype) { + case Asc.c_oAscSelectionType.RangeCells: iscellmenu = true; break; + case Asc.c_oAscSelectionType.RangeRow: isrowmenu = true; break; + case Asc.c_oAscSelectionType.RangeCol: iscolmenu = true; break; + case Asc.c_oAscSelectionType.RangeMax: isallmenu = true; break; + case Asc.c_oAscSelectionType.RangeImage: isimagemenu = true; break; + case Asc.c_oAscSelectionType.RangeShape: isshapemenu = true; break; + case Asc.c_oAscSelectionType.RangeChart: ischartmenu = true; break; + case Asc.c_oAscSelectionType.RangeChartText: istextchartmenu = true; break; + case Asc.c_oAscSelectionType.RangeShapeText: istextshapemenu = true; break; + } + + if (!isEdit) { + if (iscellmenu || istextchartmenu || istextshapemenu) { + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + } + if (iscellmenu && cellinfo.asc_getHyperlink()) { + itemsText.push({ + caption: _t.menuOpenLink, + event: 'openlink' + }); + } + if (canViewComments && isComments) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } + } else { + + if (!iscelllocked && (isimagemenu || isshapemenu || ischartmenu || istextshapemenu || istextchartmenu)) { + api.asc_getGraphicObjectProps().every((object) => { + if (object.asc_getObjectType() == Asc.c_oAscTypeSelectElement.Image) { + iscelllocked = object.asc_getObjectValue().asc_getLocked(); + } + return !iscelllocked; + }); + } + + if (iscelllocked || api.isCellEdited) { + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + + } else { + itemsIcon.push({ + event: 'cut', + icon: 'icon-cut' + }); + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + itemsIcon.push({ + event: 'paste', + icon: 'icon-paste' + }); + itemsText.push({ + caption: _t.menuDelete, + event: 'del' + }); + + if (isimagemenu || isshapemenu || ischartmenu || + istextshapemenu || istextchartmenu) { + itemsText.push({ + caption: _t.menuEdit, + event: 'edit' + }); + } else { + if (iscolmenu || isrowmenu) { + itemsText.push({ + caption: _t.menuHide, + event: 'hide' + }); + itemsText.push({ + caption: _t.menuShow, + event: 'show' + }); + } else if (iscellmenu) { + if (!iscelllocked) { + itemsText.push({ + caption: _t.menuCell, + event: 'edit' + }); + } + + if (cellinfo.asc_getMerge() == Asc.c_oAscMergeOptions.None) { + itemsText.push({ + caption: _t.menuMerge, + event: 'merge' + }); + } + + if (cellinfo.asc_getMerge() == Asc.c_oAscMergeOptions.Merge) { + itemsText.push({ + caption: _t.menuUnmerge, + event: 'unmerge' + }); + } + + itemsText.push( + xfs.asc_getWrapText() ? + { + caption: _t.menuUnwrap, + event: 'unwrap' + } : + { + caption: _t.menuWrap, + event: 'wrap' + }); + + if (cellinfo.asc_getHyperlink() && !cellinfo.asc_getMultiselect()) { + itemsText.push({ + caption: _t.menuOpenLink, + event: 'openlink' + }); + } else if (!cellinfo.asc_getHyperlink() && !cellinfo.asc_getMultiselect() && + !cellinfo.asc_getLockText() && !!cellinfo.asc_getText()) { + itemsText.push({ + caption: _t.menuAddLink, + event: 'addlink' + }); + } + } + + itemsText.push({ + caption: api.asc_getSheetViewSettings().asc_getIsFreezePane() ? _t.menuUnfreezePanes : _t.menuFreezePanes, + event: 'freezePanes' + }); + + } + + if (canViewComments) { + if (isComments) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } else if (iscellmenu) { + itemsText.push({ + caption: _t.menuAddComment, + event: 'addcomment' + }); + } + } + } + } + + + if ( Device.phone && itemsText.length > 2 ) { + this.extraItems = itemsText.splice(2,itemsText.length, { + caption: _t.menuMore, + event: 'showActionSheet' + }); + } + + return itemsIcon.concat(itemsText); + } + + initExtraItems () { + return (this.extraItems && this.extraItems.length > 0 ? this.extraItems : []); + } +} + +const _ContextMenu = withTranslation()(ContextMenu); +_ContextMenu.closeContextMenu = ContextMenu.closeContextMenu; +export { _ContextMenu as default }; \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/page/main.jsx b/apps/spreadsheeteditor/mobile/src/page/main.jsx index ae75b14d4..4b2331fb3 100644 --- a/apps/spreadsheeteditor/mobile/src/page/main.jsx +++ b/apps/spreadsheeteditor/mobile/src/page/main.jsx @@ -12,6 +12,7 @@ import { Device } from '../../../../common/mobile/utils/device'; import { Search, SearchSettings } from '../controller/Search'; import {FunctionGroups} from "../controller/add/AddFunction"; +import ContextMenu from '../controller/ContextMenu'; export default class MainPage extends Component { constructor(props) { @@ -98,6 +99,7 @@ export default class MainPage extends Component { {/* hidden component*/} + ) } diff --git a/apps/spreadsheeteditor/mobile/src/store/appOptions.js b/apps/spreadsheeteditor/mobile/src/store/appOptions.js index 70d85c02d..d76c9e53f 100644 --- a/apps/spreadsheeteditor/mobile/src/store/appOptions.js +++ b/apps/spreadsheeteditor/mobile/src/store/appOptions.js @@ -3,6 +3,8 @@ import {action, observable, makeObservable} from 'mobx'; export class storeAppOptions { constructor() { makeObservable(this, { + isEdit: observable, + canViewComments: observable, setConfigOptions: action, setPermissionOptions: action }); @@ -10,6 +12,9 @@ export class storeAppOptions { config = {}; + isEdit = false; + canViewComments = false; + setConfigOptions (config) { this.config = config; this.user = Common.Utils.fillUserInfo(config.user, config.lang, "Local.User"/*me.textAnonymous*/);