import React, {Fragment} from 'react'; import {Link} from 'framework7-react'; import { Device } from '../../web-apps/apps/common/mobile/utils/device'; import { AddCommentController, EditCommentController, } from "../../web-apps/apps/common/mobile/lib/controller/collaboration/Comments"; const EditorUIController = () => { return null }; EditorUIController.isSupportEditFeature = () => { return true; }; /** This is the file where the magic happens. Try to figure out a working version from a both the old commits and my hunches from hacking around in the old webapp editor https://github.com/ONLYOFFICE/web-apps/commit/45a9058da461a9da67ccb389faad08bce8cbda55 */ EditorUIController.getToolbarOptions = (options) => { // console.log('EditorUIController.getToolbarOptions' ,options); return ( options.onEditClick()}> options.onAddClick()}> ); }; EditorUIController.getUndoRedo = (options) => { // console.log('EditorUIController.getUndoRedo', options); return ( options.onUndoClick()}> options.onRedoClick()}> ); } EditorUIController.getEditCommentControllers = () => { // console.log('EditorUIController.getEditCommentControllers'); return ( ); } // https://github.com/ONLYOFFICE/web-apps/commit/ee15cf025dd64fe0f48eba015c7bdcea0446a38b EditorUIController.initFonts = (storeTextSettings) => { // console.log('EditorUIController.initFonts', storeTextSettings); const api = Common.EditorApi.get(); api.asc_registerCallback('asc_onInitEditorFonts', (fonts, select) => { // console.log('asc_onInitEditorFonts', fonts, select); storeTextSettings.initEditorFonts(fonts, select); }); api.asc_registerCallback('asc_onFontFamily', (font) => { // console.log('asc_onFontFamily', font); storeTextSettings.resetFontName(font); }); api.asc_registerCallback('asc_onFontSize', (size) => { // console.log('asc_onFontSize', size); storeTextSettings.resetFontSize(size); }); api.asc_registerCallback('asc_onBold', (isBold) => { // console.log('asc_onBold', isBold); storeTextSettings.resetIsBold(isBold); }); api.asc_registerCallback('asc_onItalic', (isItalic) => { // console.log('asc_onItalic', isItalic); storeTextSettings.resetIsItalic(isItalic); }); api.asc_registerCallback('asc_onUnderline', (isUnderline) => { // console.log('asc_onUnderline', isUnderline); storeTextSettings.resetIsUnderline(isUnderline); }); api.asc_registerCallback('asc_onStrikeout', (isStrikeout) => { // console.log('asc_onStrikeout', isStrikeout); storeTextSettings.resetIsStrikeout(isStrikeout); }); }; EditorUIController.initEditorStyles = (storeParagraphSettings) => { // console.log('EditorUIController.initEditorStyles', storeParagraphSettings); const api = Common.EditorApi.get(); api.asc_registerCallback('asc_onInitEditorStyles', (styles) => { // console.log('asc_onInitEditorStyles', styles); storeParagraphSettings.initEditorStyles(styles); }); api.asc_registerCallback('asc_onParaStyleName', (name) => { // console.log('asc_onParaStyleName', name); storeParagraphSettings.changeParaStyleName(name); }); } EditorUIController.initFocusObjects = (storeFocusObjects) => { storeFocusObjects.intf = EditorUIController.Intf(storeFocusObjects); // console.log('EditorUIController.initFocusObjects', storeFocusObjects); const api = Common.EditorApi.get(); api.asc_registerCallback('asc_onFocusObject', objects => { // console.log('asc_onFocusObject', objects); storeFocusObjects.resetFocusObjects(objects); }); } EditorUIController.initTableTemplates = (storeTableSettings) => { // console.log('EditorUIController.initTableTemplates', storeTableSettings); const api = Common.EditorApi.get(); api.asc_registerCallback('asc_onInitTableTemplates', (templates) => { // console.log('asc_onInitTableTemplates', templates); storeTableSettings.initTableTemplates(templates); }); } EditorUIController.initThemeColors = () => { // console.log('EditorUIController.initThemeColors'); const api = Common.EditorApi.get(); api.asc_registerCallback('asc_onSendThemeColors', (colors, standart_colors) => { // console.log('asc_onSendThemeColors', colors, standart_colors); Common.Utils.ThemeColor.setColors(colors, standart_colors); }); } EditorUIController.updateChartStyles = (storeChartSettings, storeFocusObjects) => { // console.log('EditorUIController.updateChartStyles', storeChartSettings, storeFocusObjects); const api = Common.EditorApi.get(); api.asc_registerCallback('asc_onUpdateChartStyles', () => { // console.log('asc_onUpdateChartStyles', storeFocusObjects.chartObject); if (storeFocusObjects.chartObject && storeFocusObjects.chartObject.get_ChartProperties()) { storeChartSettings.updateChartStyles(api.asc_getChartPreviews(storeFocusObjects.chartObject.get_ChartProperties().getType())); } }); } EditorUIController.ContextMenu = { // Both those methods are taken from this commit: // https://github.com/ONLYOFFICE/web-apps/commit/b0c9a1c5d28371d78ae54854f74f9bdaa8ee9efa // 'edit' in handleMenuItemClick is from // https://github.com/ONLYOFFICE/web-apps/commit/cb62ade641dbdc2c7a2779d408b6d82bbf738500 mapMenuItems: (contextMenu) => { // console.log('EditorUIController.ContextMenu.mapMenuItems', contextMenu); if ( !Common.EditorApi ) return []; const { t } = contextMenu.props; const _t = t("ContextMenu", { returnObjects: true }); const { isEdit, canViewComments, canReview, isDisconnected } = contextMenu.props; const { isViewer, isProtected } = contextMenu.props; // This probably needs to be refired if(!isEdit || isViewer || isProtected) return EditorUIController.ContextMenu.mapMenuItemsReadOnly(contextMenu); const api = Common.EditorApi.get(); const stack = api.getSelectedElements(); const canCopy = api.can_CopyCut(); let itemsIcon = [], itemsText = []; if ( canCopy ) { itemsIcon.push({ event: 'copy', icon: 'icon-copy' }); } if ( canViewComments && contextMenu.isComments && !isEdit ) { itemsText.push({ caption: _t.menuViewComment, event: 'viewcomment' }); } let isText = false, isTable = false, isImage = false, isChart = false, isShape = false, isLink = false, lockedText = false, lockedTable = false, lockedImage = false, lockedHeader = false; stack.forEach(item => { const objectType = item.get_ObjectType(), objectValue = item.get_ObjectValue(); if ( objectType == Asc.c_oAscTypeSelectElement.Header ) { lockedHeader = objectValue.get_Locked(); } else if ( objectType == Asc.c_oAscTypeSelectElement.Paragraph ) { lockedText = objectValue.get_Locked(); isText = true; } else if ( objectType == Asc.c_oAscTypeSelectElement.Image ) { lockedImage = objectValue.get_Locked(); if ( objectValue && objectValue.get_ChartProperties() ) { isChart = true; } else if ( objectValue && objectValue.get_ShapeProperties() ) { isShape = true; } else { isImage = true; } } else if ( objectType == Asc.c_oAscTypeSelectElement.Table ) { lockedTable = objectValue.get_Locked(); isTable = true; } else if ( objectType == Asc.c_oAscTypeSelectElement.Hyperlink ) { isLink = true; } }); if ( stack.length > 0 ) { const swapItems = function(items, indexBefore, indexAfter) { items[indexAfter] = items.splice(indexBefore, 1, items[indexAfter])[0]; }; if ( isEdit && !isDisconnected ) { if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader && canCopy ) { itemsIcon.push({ event: 'cut', icon: 'icon-cut' }); // Swap 'Copy' and 'Cut' swapItems(itemsIcon, 0, 1); } if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ) { itemsIcon.push({ event: 'paste', icon: 'icon-paste' }); } if ( isTable && api.CheckBeforeMergeCells() && !lockedTable && !lockedHeader) { itemsText.push({ caption: _t.menuMerge, event: 'merge' }); } if ( isTable && api.CheckBeforeSplitCells() && !lockedTable && !lockedHeader ) { itemsText.push({ caption: _t.menuSplit, event: 'split' }); } if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ) { itemsText.push({ caption: _t.menuDelete, event: 'delete' }); } if ( isTable && !lockedTable && !lockedText && !lockedHeader ) { itemsText.push({ caption: _t.menuDeleteTable, event: 'deletetable' }); } if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ){ itemsText.push({ caption: _t.menuEdit, event: 'edit' }); } if ( !!api.can_AddHyperlink() && !lockedHeader) { itemsText.push({ caption: _t.menuAddLink, event: 'addlink' }); } if ( canReview ) { if (contextMenu.inRevisionChange) { itemsText.push({ caption: _t.menuReviewChange, event: 'reviewchange' }); } else { itemsText.push({ caption: _t.menuReview, event: 'review' }); } } if ( contextMenu.isComments && canViewComments ) { itemsText.push({ caption: _t.menuViewComment, event: 'viewcomment' }); } const isObject = isShape || isChart || isImage || isTable; const hideAddComment = !canViewComments || api.can_AddQuotedComment() === false || lockedText || lockedTable || lockedImage || lockedHeader || (!isText && isObject); if ( !hideAddComment ) { itemsText.push({ caption: _t.menuAddComment, event: 'addcomment' }); } } } if ( isLink ) { itemsText.push({ caption: _t.menuOpenLink, event: 'openlink' }); itemsText.push({ caption: _t.menuEditLink, event: 'editlink' }); } if ( Device.phone && itemsText.length > 2 ) { contextMenu.extraItems = itemsText.splice(2,itemsText.length, { caption: _t.menuMore, event: 'showActionSheet' }); } const menuItems = itemsIcon.concat(itemsText); // console.log('EditorUIController.ContextMenu.mapMenuItems', menuItems); return menuItems; } , handleMenuItemClick: (contextMenu, action) => { // console.log('EditorUIController.ContextMenu.handleMenuItemClick', contextMenu, action); const api = Common.EditorApi.get(); switch (action) { case 'edit': setTimeout(() => { contextMenu.props.openOptions('edit'); }, 400); return true; case 'addcomment': Common.Notifications.trigger('addcomment'); return true; case 'merge': api.MergeCells(); return true; case 'delete': api.asc_Remove(); return true; case 'deletetable': api.remTable(); return true; case 'editlink': setTimeout(() => { contextMenu.props.openOptions('edit', 'link'); }, 400) return true; } return false; }, // This method handles the menu items shown when the user is in edit mode but // can't edit the form i.e: in viewer mode or when the document is protected // This is mostly a copy of the else statement of ContextMenu.initMenuItems // with the bare minimum modifications so it can execute. // It is done that way on purpose even if some code ends up duplicated // This should make noticing and following Onlyoffice changes easier mapMenuItemsReadOnly: (contextMenu) => { const { t } = contextMenu.props; const _t = t("ContextMenu", {returnObjects: true}); const { isEdit, canFillForms, isDisconnected, isViewer, canEditComments, isProtected, typeProtection } = contextMenu.props; const { isComments, canViewComments, canCoAuthoring, canComments, dataDoc } = contextMenu.props; const api = Common.EditorApi.get(); const inToc = api.asc_GetTableOfContentsPr(true); const stack = api.getSelectedElements(); const canCopy = api.can_CopyCut(); const docExt = dataDoc ? dataDoc.fileType : ''; const isAllowedEditing = !isProtected || typeProtection === Asc.c_oAscEDocProtect.TrackedChanges; const isAllowedCommenting = typeProtection === Asc.c_oAscEDocProtect.Comments; let isText = false, isObject = false, isLink = false, locked = false; stack.forEach(item => { const objectType = item.get_ObjectType(), objectValue = item.get_ObjectValue(); if ( objectType == Asc.c_oAscTypeSelectElement.Header ) { locked = objectValue.get_Locked(); } else if ( objectType == Asc.c_oAscTypeSelectElement.Paragraph ) { locked = objectValue.get_Locked(); isText = true; } else if ( objectType == Asc.c_oAscTypeSelectElement.Image || objectType == Asc.c_oAscTypeSelectElement.Table) { locked = objectValue.get_Locked(); isObject = true; } else if ( objectType == Asc.c_oAscTypeSelectElement.Hyperlink ) { isLink = true; } }); let itemsIcon = [], itemsText = []; if (canCopy) { itemsIcon.push({ event: 'copy', icon: 'icon-copy' }); } if (!isDisconnected) { if (canFillForms && canCopy && !locked && (!isViewer || docExt === 'oform') && isAllowedEditing) { itemsIcon.push({ event: 'cut', icon: 'icon-cut' }); } if (canFillForms && canCopy && !locked && (!isViewer || docExt === 'oform') && isAllowedEditing) { itemsIcon.push({ event: 'paste', icon: 'icon-paste' }); } if (canViewComments && isComments) { itemsText.push({ caption: _t.menuViewComment, event: 'viewcomment' }); } if (api.can_AddQuotedComment() !== false && canCoAuthoring && canComments && !locked && !(!isText && isObject) && (!isViewer || canEditComments) && (isAllowedEditing || isAllowedCommenting)) { itemsText.push({ caption: _t.menuAddComment, event: 'addcomment' }); } } if (isLink) { itemsText.push({ caption: _t.menuOpenLink, event: 'openlink' }); if(isAllowedEditing && !isViewer) { itemsText.push({ caption: t('ContextMenu.menuEditLink'), event: 'editlink' }); } } if(inToc && isEdit && !isViewer && isAllowedEditing) { itemsText.push({ caption: t('ContextMenu.textRefreshEntireTable'), event: 'refreshEntireTable' }); itemsText.push({ caption: t('ContextMenu.textRefreshPageNumbersOnly'), event: 'refreshPageNumbers' }); } return itemsIcon.concat(itemsText); } }; // This objects holds the mysterious functions in focusObjects.js inside the intf object // These for now are based on the latest valid implementation from the mobile editor // The easiest way to fix it was to restores those in focusObjects.js: A way to improve would be to do that here. // https://github.com/ONLYOFFICE/web-apps/commit/b0c9a1c5d28371d78ae54854f74f9bdaa8ee9efa // https://github.com/ONLYOFFICE/web-apps/blame/bc7713368a787e01a0ce93a2d7956c77c762e1c5/apps/documenteditor/mobile/src/store/focusObjects.js (for settings or filterFocusObjects) EditorUIController.Intf = function (storeFocusObjects) { var obj = {}; // console.log(window.Asc.c_oAscTypeSelectElement); obj.filterFocusObjects = () => { const _settings = []; for (let object of storeFocusObjects._focusObjects) { let type = object.get_ObjectType(); if (Asc.c_oAscTypeSelectElement.Paragraph === type) { _settings.push('text', 'paragraph'); } else if (Asc.c_oAscTypeSelectElement.Table === type) { _settings.push('table'); } else if (Asc.c_oAscTypeSelectElement.Image === type) { if (object.get_ObjectValue().get_ChartProperties()) { // Exclude shapes if chart exist let si = _settings.indexOf('shape'); si < 0 ? _settings.push('chart') : _settings.splice(si,1,'chart'); } else if (object.get_ObjectValue().get_ShapeProperties() && !_settings.includes('chart')) { _settings.push('shape'); } else { _settings.push('image'); } } else if (Asc.c_oAscTypeSelectElement.Hyperlink === type) { _settings.push('hyperlink'); } else if (Asc.c_oAscTypeSelectElement.Header === type) { _settings.push('header'); } } return _settings.filter((value, index, self) => self.indexOf(value) === index); } obj.getHeaderObject = () => { const headers = []; for (let object of storeFocusObjects._focusObjects) { if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Header) { headers.push(object); } } if (headers.length > 0) { const object = headers[headers.length - 1]; // get top return object.get_ObjectValue(); } else { return undefined; } } obj.getParagraphObject = () => { const paragraphs = []; for (let object of storeFocusObjects._focusObjects) { if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Paragraph) { paragraphs.push(object); } } if (paragraphs.length > 0) { const object = paragraphs[paragraphs.length - 1]; // get top return object.get_ObjectValue(); } else { return undefined; } } obj.getShapeObject = () => { const shapes = []; for (let object of storeFocusObjects._focusObjects) { if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Image) { if (object.get_ObjectValue() && typeof object.get_ObjectValue().get_ShapeProperties === "function" && object.get_ObjectValue().get_ShapeProperties()) { shapes.push(object); } } } if (shapes.length > 0) { const object = shapes[shapes.length - 1]; // get top return object.get_ObjectValue(); } else { return undefined; } } obj.getImageObject = () => { const images = []; for (let object of storeFocusObjects._focusObjects) { if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Image) { const imageObject = object.get_ObjectValue(); if (imageObject && imageObject.get_ShapeProperties() === null && imageObject.get_ChartProperties() === null) { images.push(object); } } } if (images.length > 0) { const object = images[images.length - 1]; // get top return object.get_ObjectValue(); } else { return undefined; } } obj.getTableObject = () => { const tables = []; for (let object of storeFocusObjects._focusObjects) { if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Table) { tables.push(object); } } if (tables.length > 0) { const object = tables[tables.length - 1]; // get top table return object.get_ObjectValue(); } else { return undefined; } } obj.getChartObject = () => { const charts = []; for (let object of storeFocusObjects._focusObjects) { if (object.get_ObjectValue() && typeof object.get_ObjectValue().get_ChartProperties === 'function' && object.get_ObjectValue().get_ChartProperties() ) { charts.push(object); } } if (charts.length > 0) { const object = charts[charts.length - 1]; // get top table return object.get_ObjectValue(); } else { return undefined; } } obj.getLinkObject = () => { const links = []; for (let object of storeFocusObjects._focusObjects) { if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink) { links.push(object); } } if (links.length > 0) { const object = links[links.length - 1]; // get top return object.get_ObjectValue(); } else { return undefined; } } return obj; } export default EditorUIController;