diff --git a/apps/common/main/resources/less/colors-table-night.less b/apps/common/main/resources/less/colors-table-night.less index 959a2b590a..a265b6d2b6 100644 --- a/apps/common/main/resources/less/colors-table-night.less +++ b/apps/common/main/resources/less/colors-table-night.less @@ -233,8 +233,8 @@ --background-fill-input-disabled: var(--background-normal); --background-fill-input-readonly: var(--background-normal); - --highlight-category-button-hover: rgba(18, 132, 238, 0.05); - --highlight-category-button-pressed: rgba(18, 132, 238, 0.12); + --highlight-category-button-hover: #585858; + --highlight-category-button-pressed: #2d2d2d; --highlight-header-input-hover: var(--highlight-header-button-hover); --highlight-header-input-pressed: var(--background-fill-input); diff --git a/apps/common/main/resources/less/colors-table-white.less b/apps/common/main/resources/less/colors-table-white.less index 566d620d5a..facfecd5d2 100644 --- a/apps/common/main/resources/less/colors-table-white.less +++ b/apps/common/main/resources/less/colors-table-white.less @@ -235,8 +235,8 @@ --background-fill-input-disabled: var(--background-normal); --background-fill-input-readonly: var(--background-normal); - --highlight-category-button-hover: rgba(18, 132, 238, 0.05); - --highlight-category-button-pressed: rgba(18, 132, 238, 0.12); + --highlight-category-button-hover: #EAEAEA; + --highlight-category-button-pressed: #d8d8d8; --highlight-header-input-hover: var(--highlight-header-button-hover); --highlight-header-input-pressed: var(--background-fill-input); diff --git a/apps/common/mobile/lib/controller/collaboration/Collaboration.jsx b/apps/common/mobile/lib/controller/collaboration/Collaboration.jsx index f7eee5b16d..1be49c4e50 100644 --- a/apps/common/mobile/lib/controller/collaboration/Collaboration.jsx +++ b/apps/common/mobile/lib/controller/collaboration/Collaboration.jsx @@ -43,9 +43,11 @@ class CollaborationController extends Component { } } else if (!appOptions.isEdit && appOptions.isRestrictedEdit) { isFastCoauth = true; - api.asc_SetFastCollaborative(isFastCoauth); + const lsvalue = LocalStorage.getItem(`${window.editorType}-mobile-autosave`), + intvalue = lsvalue != null ? parseInt(lsvalue) : 1; + api.asc_setAutoSaveGap(intvalue); + api.asc_SetFastCollaborative(intvalue == 1); window.editorType === 'de' && api.SetCollaborativeMarksShowType(Asc.c_oAscCollaborativeMarksShowType.None); - api.asc_setAutoSaveGap(1); } else if (appOptions.canLiveView) { // viewer isFastCoauth = !(appOptions.config.coEditing && appOptions.config.coEditing.mode==='strict'); api.asc_SetFastCollaborative(isFastCoauth); @@ -62,10 +64,18 @@ class CollaborationController extends Component { if (window.editorType === 'sse') { value = appOptions.canAutosave ? 1 : 0; // FORCE AUTOSAVE } else { - value = isFastCoauth; // Common.localStorage.getItem("de-settings-autosave"); - value = (!isFastCoauth && value !== null) ? parseInt(value) : (appOptions.canCoAuthoring ? 1 : 0); + value = appOptions.canCoAuthoring && isFastCoauth ? 1 : 0; + } + + if ( !value ) { + api.asc_SetFastCollaborative(false); + api.asc_setAutoSaveGap(0); + } else { + const lsvalue = LocalStorage.getItem(`${window.editorType}-mobile-autosave`), + intvalue = lsvalue != null ? parseInt(lsvalue) : 1; + api.asc_setAutoSaveGap(intvalue); + api.asc_SetFastCollaborative(intvalue == 1); } - api.asc_setAutoSaveGap(value); } /** coauthoring end **/ } diff --git a/apps/common/mobile/resources/less/colors-table-dark.less b/apps/common/mobile/resources/less/colors-table-dark.less index c1e3e859d3..e6a71f4b2f 100644 --- a/apps/common/mobile/resources/less/colors-table-dark.less +++ b/apps/common/mobile/resources/less/colors-table-dark.less @@ -6,6 +6,7 @@ --background-tertiary: #131313; --background-menu-divider: fade(#545458, 50%); --background-button: #333333; + --background-notification-badge: #F4D804; --text-normal: fade(#FFF, 87%); --text-secondary: fade(#FFF, 60%); diff --git a/apps/common/mobile/resources/less/colors-table.less b/apps/common/mobile/resources/less/colors-table.less index da8490f437..6552359df0 100644 --- a/apps/common/mobile/resources/less/colors-table.less +++ b/apps/common/mobile/resources/less/colors-table.less @@ -14,6 +14,7 @@ --background-tertiary: #EFF0F5; --background-menu-divider: fade(#3C3C43, 15%); --background-button: #EFF0F5; + --background-notification-badge: #F4D804; --text-normal: #000000; --text-secondary: fade(#000, 60%); @@ -97,6 +98,7 @@ @background-tertiary: var(--background-tertiary); @background-menu-divider: var(--background-menu-divider); @background-button: var(--background-button); +@background-notification-badge: var(--background-notification-badge); @text-normal: var(--text-normal); @text-secondary: var(--text-secondary); diff --git a/apps/common/mobile/resources/less/common-ios.less b/apps/common/mobile/resources/less/common-ios.less index b6db7dd911..a308fa9a63 100644 --- a/apps/common/mobile/resources/less/common-ios.less +++ b/apps/common/mobile/resources/less/common-ios.less @@ -92,6 +92,16 @@ } } + .title { + .subtitle { + color: inherit; + margin-bottom: 16px; + margin-top: -10px; + font-size: 12px; + color: @text-secondary; + } + } + .subnavbar { .icon-back { color: @brandColor; diff --git a/apps/common/mobile/resources/less/common-material.less b/apps/common/mobile/resources/less/common-material.less index 5bd1b63f51..cb7008c9f5 100644 --- a/apps/common/mobile/resources/less/common-material.less +++ b/apps/common/mobile/resources/less/common-material.less @@ -440,6 +440,14 @@ } } + .title { + .subtitle { + color: inherit; + margin-bottom: 5px; + margin-top: 3px; + } + } + .subnavbar .title { font-size: 14px; font-weight: normal; diff --git a/apps/common/mobile/resources/less/common.less b/apps/common/mobile/resources/less/common.less index 0415edf2bc..286239cdcb 100644 --- a/apps/common/mobile/resources/less/common.less +++ b/apps/common/mobile/resources/less/common.less @@ -83,6 +83,20 @@ .icon-back { color: @toolbar-icons; } + .link { + &.notify { + &::before { + content: ' '; + position: absolute; + width: 10px; + height: 10px; + bottom: 8px; + right: 10px; + background-color: @background-notification-badge; + border-radius: 6px; + } + } + } } .page.page-with-subnavbar { @@ -153,6 +167,21 @@ } } } + + &.notify { + .item-media { + &::before { + content: ' '; + position: absolute; + width: 10px; + height: 10px; + bottom: 8px; + right: 1px; + background-color: @background-notification-badge; + border-radius: 6px; + } + } + } } .item-text { text-overflow: initial; diff --git a/apps/documenteditor/mobile/locale/en.json b/apps/documenteditor/mobile/locale/en.json index d40522f7dd..3e34528504 100644 --- a/apps/documenteditor/mobile/locale/en.json +++ b/apps/documenteditor/mobile/locale/en.json @@ -533,7 +533,8 @@ "txtEditingMode": "Set editing mode...", "uploadImageTextText": "Uploading image...", "uploadImageTitleText": "Uploading Image", - "waitText": "Please, wait..." + "waitText": "Please, wait...", + "changesSaved": "All changes saved" }, "Main": { "criticalErrorTitle": "Error", @@ -744,6 +745,8 @@ "textFind": "Find", "textFindAndReplace": "Find and Replace", "textFindAndReplaceAll": "Find and Replace All", + "textSaveDocument": "Save", + "textAutoSaveDocument": "Autosave", "textFormat": "Format", "textHelp": "Help", "textHiddenTableBorders": "Hidden Table Borders", diff --git a/apps/documenteditor/mobile/src/controller/LongActions.jsx b/apps/documenteditor/mobile/src/controller/LongActions.jsx index 0842b121e1..10a66d171e 100644 --- a/apps/documenteditor/mobile/src/controller/LongActions.jsx +++ b/apps/documenteditor/mobile/src/controller/LongActions.jsx @@ -74,7 +74,7 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { let action = {id: id, type: type}; stackLongActions.pop(action); - //this.updateWindowTitle(true); + Common.Notifications.trigger('update:windowtitle', true); action = stackLongActions.get({type: Asc.c_oAscAsyncActionType.Information}) || stackLongActions.get({type: Asc.c_oAscAsyncActionType.BlockInteraction}); @@ -87,6 +87,9 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { } if (id===Asc.c_oAscAsyncAction['Submit'] && !submitFail) { Common.Gateway.submitForm(); + } else if ((id==Asc.c_oAscAsyncAction['Save'] || id==Asc.c_oAscAsyncAction['ForceSaveButton'])) { + storeAppOptions.changeSavingDocStatusText(_t.changesSaved); + storeAppOptions.isSaveBadgeShown && storeAppOptions.changeIsSaveBadgeShown(false); } }; @@ -104,6 +107,7 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { case Asc.c_oAscAsyncAction['Save']: title = _t.saveTitleText; // text = _t.saveTextText; + storeAppOptions.changeSavingDocStatusText(_t.saveTextText); break; case Asc.c_oAscAsyncAction['LoadDocumentFonts']: @@ -206,7 +210,6 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { loadMask = f7.dialog.preloader(title); } } - }; const onConfirmAction = (id, apiCallback, data) => { diff --git a/apps/documenteditor/mobile/src/controller/Main.jsx b/apps/documenteditor/mobile/src/controller/Main.jsx index 9a2d6e9a1e..7257fa0e88 100644 --- a/apps/documenteditor/mobile/src/controller/Main.jsx +++ b/apps/documenteditor/mobile/src/controller/Main.jsx @@ -382,6 +382,8 @@ class MainController extends Component { this.updateWindowTitle(true); + appOptions.changeAutosave(LocalStorage.itemExists('de-mobile-autosave') ? LocalStorage.getBool("de-mobile-autosave") : true); + value = LocalStorage.getBool("de-mobile-no-characters"); appSettings.changeNoCharacters(value); this.api.put_ShowParaMarks(value); @@ -638,6 +640,7 @@ class MainController extends Component { this.api.asc_registerCallback('asc_onDocumentModifiedChanged', this.onDocumentModifiedChanged.bind(this)); this.api.asc_registerCallback('asc_onDocumentCanSaveChanged', this.onDocumentCanSaveChanged.bind(this)); + this.api.asc_registerCallback('asc_onCollaborativeChanges', this.onCollaborativeChanges.bind(this)); Common.Notifications.trigger('preloader:close'); Common.Notifications.trigger('preloader:endAction', Asc.c_oAscAsyncActionType['BlockInteraction'], this.ApplyEditRights); @@ -661,7 +664,14 @@ class MainController extends Component { } onDocumentCanSaveChanged (isCanSave) { - // + const storeAppOptions = this.props.storeAppOptions; + storeAppOptions.changeIsSaveBadgeShown(isCanSave); + } + + onCollaborativeChanges () { + const storeAppOptions = this.props.storeAppOptions; + if (!storeAppOptions.isSaveBadgeShown) storeAppOptions.changeIsSaveBadgeShown(true); + storeAppOptions.changeSavingDocStatusText(''); } onBeforeUnload () { @@ -1037,6 +1047,7 @@ class MainController extends Component { }); Common.Notifications.on('markfavorite', this.markFavorite.bind(this)); + Common.Notifications.on('update:windowtitle', force => this.updateWindowTitle(force)); } insertImageFromStorage(data) { @@ -1449,6 +1460,9 @@ class MainController extends Component { window.document.title = title; } + if (isModified) + this.props.storeAppOptions.changeSavingDocStatusText(''); + this._isDocReady && (this._state.isDocModified !== isModified) && Common.Gateway.setDocumentModified(isModified); this._state.isDocModified = isModified; } diff --git a/apps/documenteditor/mobile/src/controller/Toolbar.jsx b/apps/documenteditor/mobile/src/controller/Toolbar.jsx index 8b37e48672..5f48b62e6b 100644 --- a/apps/documenteditor/mobile/src/controller/Toolbar.jsx +++ b/apps/documenteditor/mobile/src/controller/Toolbar.jsx @@ -465,6 +465,7 @@ const ToolbarController = inject('storeAppOptions', 'users', 'storeReview', 'sto canSubmitForms={appOptions.canSubmitForms} forceDesktopMode={forceDesktopMode} isHiddenFileName={appOptions.config?.customization?.toolbarHideFileName ?? false} + isSaveBadgeShown={appOptions.isSaveBadgeShown} disableForceDesktop={appOptions.disableForceDesktop !== false} /> ) diff --git a/apps/documenteditor/mobile/src/controller/settings/Settings.jsx b/apps/documenteditor/mobile/src/controller/settings/Settings.jsx index 46c9b564d4..043a7737e5 100644 --- a/apps/documenteditor/mobile/src/controller/settings/Settings.jsx +++ b/apps/documenteditor/mobile/src/controller/settings/Settings.jsx @@ -191,6 +191,17 @@ const SettingsController = props => { Common.Notifications.trigger('markfavorite', !isFavorite); }; + const switchAutosave = (value) => { + const api = Common.EditorApi.get(); + LocalStorage.setBool("de-mobile-autosave", value); + api.asc_SetFastCollaborative(value); + api.asc_setAutoSaveGap(value ? 1 : 0); + }; + + const tryToSave = () => { + Common.EditorApi.get().asc_Save(); + }; + const saveAsPdf = () => { const api = Common.EditorApi.get(); @@ -221,6 +232,8 @@ const SettingsController = props => { closeModal, clearAllFields, toggleFavorite, + switchAutosave, + tryToSave, saveAsPdf, submitForm }}> diff --git a/apps/documenteditor/mobile/src/store/appOptions.js b/apps/documenteditor/mobile/src/store/appOptions.js index d86b8c2ab7..3f60d2d324 100644 --- a/apps/documenteditor/mobile/src/store/appOptions.js +++ b/apps/documenteditor/mobile/src/store/appOptions.js @@ -46,6 +46,15 @@ export class storeAppOptions { isFavorite: observable, setFavorite: action, + isAutosave: observable, + changeAutosave: action, + + isSaveBadgeShown: observable, + changeIsSaveBadgeShown: action, + + savingDocStatusText: observable, + changeSavingDocStatusText: action, + customization: observable, }); } @@ -110,6 +119,21 @@ export class storeAppOptions { this.isDocReady = value; } + isAutosave = true; + changeAutosave (value) { + this.isAutosave = value; + } + + isSaveBadgeShown = false; + changeIsSaveBadgeShown (value) { + this.isSaveBadgeShown = value; + } + + savingDocStatusText = ''; + changeSavingDocStatusText (value) { + this.savingDocStatusText = value; + } + config = {}; customization; diff --git a/apps/documenteditor/mobile/src/view/Toolbar.jsx b/apps/documenteditor/mobile/src/view/Toolbar.jsx index 69837a4e37..ad94703445 100644 --- a/apps/documenteditor/mobile/src/view/Toolbar.jsx +++ b/apps/documenteditor/mobile/src/view/Toolbar.jsx @@ -135,7 +135,7 @@ const ToolbarView = props => { : null), - props.openOptions('settings')}> + props.openOptions('settings')}> {Device.ios ? : diff --git a/apps/documenteditor/mobile/src/view/settings/SettingsPage.jsx b/apps/documenteditor/mobile/src/view/settings/SettingsPage.jsx index a246bf64ae..132020fafe 100644 --- a/apps/documenteditor/mobile/src/view/settings/SettingsPage.jsx +++ b/apps/documenteditor/mobile/src/view/settings/SettingsPage.jsx @@ -46,7 +46,10 @@ const SettingsPage = inject("storeAppOptions", "storeReview", "storeDocumentInfo const isHistoryDisabled = docExt && (docExt === 'xps' || docExt === 'djvu' || docExt === 'pdf'); const navbar = -
{docTitle}
+
+ {docTitle} + {appOptions.savingDocStatusText} +
{Device.phone && {_t.textDone}}
; const isSignatureForm = props.storeToolbarSettings.isSignatureForm; @@ -67,6 +70,7 @@ const SettingsPage = inject("storeAppOptions", "storeReview", "storeDocumentInfo const canUseHistory = appOptions.canUseHistory; const gobackTitle = appOptions.customization?.goback?.text || _t.textOpenLocation; const isShowBack = props.storeToolbarSettings.isShowBack; + const isAutosave = appOptions.isAutosave; let _isEdit = false, _canDownload = false, @@ -104,131 +108,158 @@ const SettingsPage = inject("storeAppOptions", "storeReview", "storeDocumentInfo return ( {navbar} - - {isShowBack && - Common.Notifications.trigger('goback')}> - {Device.ios ? - : - - } - - } - {isEditableForms ? [ - (isFavorite !== undefined && isFavorite !== null ? - - {isFavorite ? - - : - + <> + + {isShowBack && + Common.Notifications.trigger('goback')}> + {Device.ios ? + : + } - : ''), - - + } + {isEditableForms ? [ + (isFavorite !== undefined && isFavorite !== null ? + + {isFavorite ? + + : + + } + + : ''), + + + + ] : null} + {!appOptions.canLiveView && + + {Device.ios ? + : + + } + { + appOptions.changeAutosave(!isAutosave); + settingsContext.switchAutosave(!isAutosave); + }} + /> + + } + {!appOptions.canLiveView && + + {Device.ios ? + : + + } + + } + + + {(Device.phone || isEditableForms) && + + + + } + {(_isEdit && canProtect) && + + + + } + {_isEdit && !isHistoryDisabled && canUseHistory && + { + if(Device.phone) { + onOpenOptions('history'); + } + }}> + + + } + {!isEditableForms ? + { + if(Device.phone) { + onOpenOptions('navigation'); + } + }}> + + + : null} + {window.matchMedia("(max-width: 359px)").matches ? + { + onOpenOptions('coauth'); + }} className='no-indicator'> + + + : null} + {Device.sailfish && _isEdit && + settingsContext.onOrthographyCheck()} className='no-indicator' link="#"> + + + } + {(appOptions.isMobileViewAvailable && ((Device.phone && !isViewer) || isEditableForms)) && + + + { + onOpenOptions('snackbar'); + settingsContext.onChangeMobileView(); + }} /> + + } + {(_isEdit && !isViewer) && + + + + } + + - ] : null} - {(Device.phone || isEditableForms) && - - - - } - {(_isEdit && canProtect) && - - - - } - {_isEdit && !isHistoryDisabled && canUseHistory && - { - if(Device.phone) { - onOpenOptions('history'); - } - }}> - - - } - {!isEditableForms ? - { - if(Device.phone) { - onOpenOptions('navigation'); - } - }}> - - - : null} - {window.matchMedia("(max-width: 359px)").matches ? - { - onOpenOptions('coauth'); - }} className='no-indicator'> - - - : null} - {Device.sailfish && _isEdit && - settingsContext.onOrthographyCheck()} className='no-indicator' link="#"> - - - } - {(appOptions.isMobileViewAvailable && ((Device.phone && !isViewer) || isEditableForms)) && - - - { - onOpenOptions('snackbar'); - settingsContext.onChangeMobileView(); - }} /> - - } - {(_isEdit && !isViewer) && - - - - } - - - - {_canDownload && - - {isEditableForms ? - : + {_canDownload && + + {isEditableForms ? + : + + } + + } + {_canDownloadOrigin && + - } - - } - {_canDownloadOrigin && - - - - } - {_canPrint && - - - - } - {!(!_canDisplayInfo && isBranding) && - - - - } - {_canHelp && - - - - } - {_canAbout && - - - - } - {_canFeedback && - - {Device.ios ? - : - - } - - } - {canCloseEditor && - Common.Notifications.trigger('close')}> - } - + + } + {_canPrint && + + + + } + {!(!_canDisplayInfo && isBranding) && + + + + } + {_canHelp && + + + + } + {_canAbout && + + + + } + {_canFeedback && + + {Device.ios ? + : + + } + + } + {canCloseEditor && + Common.Notifications.trigger('close')}> + } + + + ) })); diff --git a/apps/presentationeditor/mobile/locale/en.json b/apps/presentationeditor/mobile/locale/en.json index 96ee375d05..43aed553f8 100644 --- a/apps/presentationeditor/mobile/locale/en.json +++ b/apps/presentationeditor/mobile/locale/en.json @@ -278,7 +278,8 @@ "txtEditingMode": "Set editing mode...", "uploadImageTextText": "Uploading image...", "uploadImageTitleText": "Uploading Image", - "waitText": "Please, wait..." + "waitText": "Please, wait...", + "changesSaved": "All changes saved" }, "Toolbar": { "btnRestartNow": "Restart now", @@ -580,6 +581,8 @@ "textFind": "Find", "textFindAndReplace": "Find and Replace", "textFindAndReplaceAll": "Find and Replace All", + "textSaveDocument": "Save", + "textAutoSaveDocument": "Autosave", "textHelp": "Help", "textHighlight": "Highlight Results", "textInch": "Inch", diff --git a/apps/presentationeditor/mobile/src/controller/LongActions.jsx b/apps/presentationeditor/mobile/src/controller/LongActions.jsx index 346764ff5b..2d7ddf24d6 100644 --- a/apps/presentationeditor/mobile/src/controller/LongActions.jsx +++ b/apps/presentationeditor/mobile/src/controller/LongActions.jsx @@ -68,7 +68,7 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { let action = {id: id, type: type}; stackLongActions.pop(action); - //this.updateWindowTitle(true); + Common.Notifications.trigger('update:windowtitle', true); action = stackLongActions.get({type: Asc.c_oAscAsyncActionType.Information}) || stackLongActions.get({type: Asc.c_oAscAsyncActionType.BlockInteraction}); @@ -79,6 +79,11 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { f7.dialog.close(loadMask.el) : f7.dialog.close($$('.dialog-preloader')); } + + if ((id==Asc.c_oAscAsyncAction['Save'] || id==Asc.c_oAscAsyncAction['ForceSaveButton'])) { + storeAppOptions.changeSavingDocStatusText(_t.changesSaved); + storeAppOptions.isSaveBadgeShown && storeAppOptions.changeIsSaveBadgeShown(false); + } }; const setLongActionView = (action) => { @@ -94,6 +99,7 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { case Asc.c_oAscAsyncAction['Save']: title = _t.saveTitleText; // text = _t.saveTextText; + storeAppOptions.changeSavingDocStatusText(_t.saveTextText); break; case Asc.c_oAscAsyncAction['LoadDocumentFonts']: diff --git a/apps/presentationeditor/mobile/src/controller/Main.jsx b/apps/presentationeditor/mobile/src/controller/Main.jsx index 17929691ca..c71fb1f82f 100644 --- a/apps/presentationeditor/mobile/src/controller/Main.jsx +++ b/apps/presentationeditor/mobile/src/controller/Main.jsx @@ -326,6 +326,7 @@ class MainController extends Component { this.api.asc_registerCallback('asc_onDocumentModifiedChanged', this.onDocumentModifiedChanged.bind(this)); this.api.asc_registerCallback('asc_onDocumentCanSaveChanged', this.onDocumentCanSaveChanged.bind(this)); + this.api.asc_registerCallback('asc_onCollaborativeChanges', this.onCollaborativeChanges.bind(this)); Common.Notifications.trigger('preloader:close'); Common.Notifications.trigger('preloader:endAction', Asc.c_oAscAsyncActionType['BlockInteraction'], this.ApplyEditRights); @@ -379,7 +380,14 @@ class MainController extends Component { } onDocumentCanSaveChanged (isCanSave) { - // + const storeAppOptions = this.props.storeAppOptions; + storeAppOptions.changeIsSaveBadgeShown(isCanSave); + } + + onCollaborativeChanges () { + const storeAppOptions = this.props.storeAppOptions; + if (!storeAppOptions.isSaveBadgeShown) storeAppOptions.changeIsSaveBadgeShown(true); + storeAppOptions.changeSavingDocStatusText(''); } onBeforeUnload () { @@ -542,6 +550,8 @@ class MainController extends Component { storePresentationInfo.changeTitle(meta.title); } }); + + Common.Notifications.on('update:windowtitle', force => this.updateWindowTitle(force)); } insertImageFromStorage(data) { @@ -582,6 +592,8 @@ class MainController extends Component { appSettings.changeSpellCheck(value); this.api.asc_setSpellCheck(value); + appOptions.changeAutosave(LocalStorage.itemExists('pe-mobile-autosave') ? LocalStorage.getBool("pe-mobile-autosave") : true); + this.updateWindowTitle(true); if (appOptions.isEdit && this.needToUpdateVersion) { @@ -883,6 +895,9 @@ class MainController extends Component { window.document.title = title; } + if (isModified) + this.props.storeAppOptions.changeSavingDocStatusText(''); + this._isDocReady && (this._state.isDocModified !== isModified) && Common.Gateway.setDocumentModified(isModified); this._state.isDocModified = isModified; } diff --git a/apps/presentationeditor/mobile/src/controller/Toolbar.jsx b/apps/presentationeditor/mobile/src/controller/Toolbar.jsx index aab03127f2..cd149160fc 100644 --- a/apps/presentationeditor/mobile/src/controller/Toolbar.jsx +++ b/apps/presentationeditor/mobile/src/controller/Toolbar.jsx @@ -296,6 +296,7 @@ const ToolbarController = inject('storeAppOptions', 'users', 'storeFocusObjects' changeTitleHandler={changeTitleHandler} forceDesktopMode={forceDesktopMode} isHiddenFileName={appOptions.config?.customization?.toolbarHideFileName ?? false} + isSaveBadgeShown={appOptions.isSaveBadgeShown} /> ) })); diff --git a/apps/presentationeditor/mobile/src/controller/settings/Settings.jsx b/apps/presentationeditor/mobile/src/controller/settings/Settings.jsx index f9b69b9f23..9ee7cb479f 100644 --- a/apps/presentationeditor/mobile/src/controller/settings/Settings.jsx +++ b/apps/presentationeditor/mobile/src/controller/settings/Settings.jsx @@ -4,6 +4,7 @@ import SettingsView from '../../view/settings/Settings'; import { f7 } from 'framework7-react'; import { observer, inject } from "mobx-react"; import { useTranslation } from 'react-i18next'; +import { LocalStorage } from "../../../../../common/mobile/utils/LocalStorage.mjs"; export const SettingsContext = createContext(); @@ -160,6 +161,17 @@ const SettingsController = props => { } } + const switchAutosave = (value) => { + const api = Common.EditorApi.get(); + LocalStorage.setBool("pe-mobile-autosave", value); + api.asc_SetFastCollaborative(value); + api.asc_setAutoSaveGap(value ? 1 : 0); + }; + + const tryToSave = () => { + Common.EditorApi.get().asc_Save(); + }; + return ( { showFeedback, onDownloadOrigin, closeModal, - changeTitleHandler + changeTitleHandler, + switchAutosave, + tryToSave, }}> diff --git a/apps/presentationeditor/mobile/src/store/appOptions.js b/apps/presentationeditor/mobile/src/store/appOptions.js index e77564c534..a64353fdff 100644 --- a/apps/presentationeditor/mobile/src/store/appOptions.js +++ b/apps/presentationeditor/mobile/src/store/appOptions.js @@ -20,6 +20,15 @@ export class storeAppOptions { isDocReady: observable, changeDocReady: action, + isAutosave: observable, + changeAutosave: action, + + isSaveBadgeShown: observable, + changeIsSaveBadgeShown: action, + + savingDocStatusText: observable, + changeSavingDocStatusText: action, + customization: observable, }); } @@ -46,6 +55,21 @@ export class storeAppOptions { this.isDocReady = value; } + isAutosave = true; + changeAutosave (value) { + this.isAutosave = value; + } + + isSaveBadgeShown = false; + changeIsSaveBadgeShown (value) { + this.isSaveBadgeShown = value; + } + + savingDocStatusText = ''; + changeSavingDocStatusText (value) { + this.savingDocStatusText = value; + } + setConfigOptions (config, _t) { this.config = config; this.customization = config.customization; diff --git a/apps/presentationeditor/mobile/src/view/Toolbar.jsx b/apps/presentationeditor/mobile/src/view/Toolbar.jsx index aa259b3c6e..57e7563f4d 100644 --- a/apps/presentationeditor/mobile/src/view/Toolbar.jsx +++ b/apps/presentationeditor/mobile/src/view/Toolbar.jsx @@ -93,7 +93,7 @@ const ToolbarView = props => { {Device.phone ? null : } {props.displayCollaboration && window.matchMedia("(min-width: 375px)").matches && !isVersionHistoryMode ? props.openOptions('coauth')}> : null} {isVersionHistoryMode ? props.openOptions('history')}> : null} - props.openOptions('settings')}> + props.openOptions('settings')}> {Device.ios ? : diff --git a/apps/presentationeditor/mobile/src/view/settings/SettingsPage.jsx b/apps/presentationeditor/mobile/src/view/settings/SettingsPage.jsx index 0f44d173d3..baee931230 100644 --- a/apps/presentationeditor/mobile/src/view/settings/SettingsPage.jsx +++ b/apps/presentationeditor/mobile/src/view/settings/SettingsPage.jsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { Page, Navbar, NavRight, Link, Icon, ListItem, List, f7 } from 'framework7-react'; +import { Page, Navbar, NavRight, Link, Icon, ListItem, List, f7, Toggle } from 'framework7-react'; import { useTranslation } from 'react-i18next'; import { Device } from '../../../../../common/mobile/utils/device'; import { observer, inject } from "mobx-react"; @@ -36,9 +36,13 @@ const SettingsPage = inject('storeAppOptions', 'storeToolbarSettings', 'storePre const closeButtonText = canCloseEditor && appOptions.customization.close.text; const gobackTitle = appOptions.customization?.goback?.text || _t.textOpenLocation; const isShowBack = props.storeToolbarSettings.isShowBack; + const isAutosave = appOptions.isAutosave; const navbar = -
{docTitle}
+
+ {docTitle} + {appOptions.savingDocStatusText} +
{Device.phone && {_t.textDone}}
; @@ -83,84 +87,110 @@ const SettingsPage = inject('storeAppOptions', 'storeToolbarSettings', 'storePre return ( {navbar} - - {isShowBack && - Common.Notifications.trigger('goback')}> - {Device.ios ? - : - - } + <> + + {isShowBack && + Common.Notifications.trigger('goback')}> + {Device.ios ? + : + + } + + } + {!appOptions.canLiveView && + + {Device.ios ? + : + + } + { + appOptions.changeAutosave(!isAutosave); + settingsContext.switchAutosave(!isAutosave); + }} + /> + + } + {!appOptions.canLiveView && + + {Device.ios ? + : + + } + + } + + + {!props.inPopover && + + + + } + {window.matchMedia("(max-width: 374px)").matches ? + onOpenOptions('coauth')} className='no-indicator'> + + + : null} + {_isEdit && + + + + } + + - } - {!props.inPopover && - - - - } - {window.matchMedia("(max-width: 374px)").matches ? - onOpenOptions('coauth')} className='no-indicator'> - - - : null} - {_isEdit && - - - - } - - - - {_isEdit && canUseHistory && - { - if(Device.phone) { - onOpenOptions('history'); - } - }}> - - - } - {_canDownload && - - - - } - {_canDownloadOrigin && - - - - } - {_canPrint && - - - - } - {!(!_canDisplayInfo && isBranding) && - - - - } - {_canHelp && - - - - } - {_canAbout && - - - - } - {_canFeedback && - - {Device.ios ? - : - - } - - } - {canCloseEditor && - Common.Notifications.trigger('close')}> - } - + {_isEdit && canUseHistory && + { + if(Device.phone) { + onOpenOptions('history'); + } + }}> + + + } + {_canDownload && + + + + } + {_canDownloadOrigin && + + + + } + {_canPrint && + + + + } + {!(!_canDisplayInfo && isBranding) && + + + + } + {_canHelp && + + + + } + {_canAbout && + + + + } + {_canFeedback && + + {Device.ios ? + : + + } + + } + {canCloseEditor && + Common.Notifications.trigger('close')}> + } + + ) })); diff --git a/apps/spreadsheeteditor/mobile/locale/en.json b/apps/spreadsheeteditor/mobile/locale/en.json index a7ae15495e..5a5a543f44 100644 --- a/apps/spreadsheeteditor/mobile/locale/en.json +++ b/apps/spreadsheeteditor/mobile/locale/en.json @@ -426,7 +426,8 @@ "txtEditingMode": "Set editing mode...", "uploadImageTextText": "Uploading image...", "uploadImageTitleText": "Uploading Image", - "waitText": "Please, wait..." + "waitText": "Please, wait...", + "changesSaved": "All changes saved" }, "Statusbar": { "notcriticalErrorTitle": "Warning", @@ -792,6 +793,8 @@ "textFind": "Find", "textFindAndReplace": "Find and Replace", "textFindAndReplaceAll": "Find and Replace All", + "textSaveDocument": "Save", + "textAutoSaveDocument": "Autosave", "textFormat": "Format", "textFormulaLanguage": "Formula Language", "textFormulas": "Formulas", diff --git a/apps/spreadsheeteditor/mobile/src/controller/LongActions.jsx b/apps/spreadsheeteditor/mobile/src/controller/LongActions.jsx index 9a0b19282a..883eba280d 100644 --- a/apps/spreadsheeteditor/mobile/src/controller/LongActions.jsx +++ b/apps/spreadsheeteditor/mobile/src/controller/LongActions.jsx @@ -93,7 +93,7 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { let action = {id: id, type: type}; stackLongActions.pop(action); - //this.updateWindowTitle(true); + Common.Notifications.trigger('update:windowtitle', true); action = stackLongActions.get({type: Asc.c_oAscAsyncActionType.Information}) || stackLongActions.get({type: Asc.c_oAscAsyncActionType.BlockInteraction}); @@ -104,6 +104,11 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { f7.dialog.close(loadMask.el) : f7.dialog.close($$('.dialog-preloader')); } + + if ((id==Asc.c_oAscAsyncAction['Save'] || id==Asc.c_oAscAsyncAction['ForceSaveButton'])) { + storeAppOptions.changeSavingDocStatusText(_t.changesSaved); + storeAppOptions.isSaveBadgeShown && storeAppOptions.changeIsSaveBadgeShown(false); + } }; const setLongActionView = (action) => { @@ -119,6 +124,7 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { case Asc.c_oAscAsyncAction['Save']: title = _t.saveTitleText; // text = _t.saveTextText; + storeAppOptions.changeSavingDocStatusText(_t.saveTextText); break; case Asc.c_oAscAsyncAction['LoadDocumentFonts']: @@ -214,7 +220,6 @@ const LongActionsController = inject('storeAppOptions')(({storeAppOptions}) => { loadMask = showLoadMask(title, action.id === Asc.c_oAscAsyncAction['Open']); } } - }; const onConfirmAction = (id, apiCallback, data) => { diff --git a/apps/spreadsheeteditor/mobile/src/controller/Main.jsx b/apps/spreadsheeteditor/mobile/src/controller/Main.jsx index 33617ab30e..78d19a066f 100644 --- a/apps/spreadsheeteditor/mobile/src/controller/Main.jsx +++ b/apps/spreadsheeteditor/mobile/src/controller/Main.jsx @@ -602,6 +602,8 @@ class MainController extends Component { }); this.api.asc_registerCallback('asc_onNeedUpdateExternalReferenceOnOpen', this.onNeedUpdateExternalReference.bind(this)); + + Common.Notifications.on('update:windowtitle', force => this.updateWindowTitle(force)); } insertImageFromStorage (data) { @@ -764,6 +766,8 @@ class MainController extends Component { appSettings.changeRefStyle(value); this.api.asc_setR1C1Mode(value); + appOptions.changeAutosave(LocalStorage.itemExists('sse-mobile-autosave') ? LocalStorage.getBool("sse-mobile-autosave") : true); + Common.Gateway.documentReady(); f7.emit('resize'); @@ -813,6 +817,7 @@ class MainController extends Component { this.api.asc_registerCallback('asc_onDocumentModifiedChanged', this.onDocumentModifiedChanged.bind(this)); this.api.asc_registerCallback('asc_onDocumentCanSaveChanged', this.onDocumentCanSaveChanged.bind(this)); + this.api.asc_registerCallback('asc_onCollaborativeChanges', this.onCollaborativeChanges.bind(this)); Common.Notifications.trigger('preloader:close'); Common.Notifications.trigger('preloader:endAction', Asc.c_oAscAsyncActionType['BlockInteraction'], this.ApplyEditRights); @@ -1189,6 +1194,9 @@ class MainController extends Component { window.document.title = title; } + if (isModified) + this.props.storeAppOptions.changeSavingDocStatusText(''); + this._isDocReady && (this._state.isDocModified !== isModified) && Common.Gateway.setDocumentModified(isModified); this._state.isDocModified = isModified; } @@ -1204,7 +1212,14 @@ class MainController extends Component { } onDocumentCanSaveChanged (isCanSave) { - // + const storeAppOptions = this.props.storeAppOptions; + storeAppOptions.changeIsSaveBadgeShown(isCanSave); + } + + onCollaborativeChanges () { + const storeAppOptions = this.props.storeAppOptions; + if (!storeAppOptions.isSaveBadgeShown) storeAppOptions.changeIsSaveBadgeShown(true); + storeAppOptions.changeSavingDocStatusText(''); } onPrint () { diff --git a/apps/spreadsheeteditor/mobile/src/controller/Toolbar.jsx b/apps/spreadsheeteditor/mobile/src/controller/Toolbar.jsx index d41c59c8d8..0b5bf9f818 100644 --- a/apps/spreadsheeteditor/mobile/src/controller/Toolbar.jsx +++ b/apps/spreadsheeteditor/mobile/src/controller/Toolbar.jsx @@ -317,6 +317,7 @@ const ToolbarController = inject('storeAppOptions', 'users', 'storeSpreadsheetIn changeTitleHandler={changeTitleHandler} forceDesktopMode={forceDesktopMode} isHiddenFileName={appOptions.config?.customization?.toolbarHideFileName ?? false} + isSaveBadgeShown={appOptions.isSaveBadgeShown} /> ) })); diff --git a/apps/spreadsheeteditor/mobile/src/controller/settings/Settings.jsx b/apps/spreadsheeteditor/mobile/src/controller/settings/Settings.jsx index dc28c8c03b..d5fb8d7748 100644 --- a/apps/spreadsheeteditor/mobile/src/controller/settings/Settings.jsx +++ b/apps/spreadsheeteditor/mobile/src/controller/settings/Settings.jsx @@ -4,6 +4,7 @@ import { Device } from '../../../../../common/mobile/utils/device'; import SettingsView from "../../view/settings/Settings"; import { f7 } from 'framework7-react'; import { useTranslation } from 'react-i18next'; +import { LocalStorage } from "../../../../../common/mobile/utils/LocalStorage.mjs"; export const SettingsContext = createContext(); @@ -177,6 +178,17 @@ const SettingsController = inject('storeAppOptions', 'storeSpreadsheetInfo')(obs } } + const switchAutosave = (value) => { + const api = Common.EditorApi.get(); + LocalStorage.setBool("sse-mobile-autosave", value); + api.asc_SetFastCollaborative(value); + api.asc_setAutoSaveGap(value ? 1 : 0); + }; + + const tryToSave = () => { + Common.EditorApi.get().asc_Save(); + }; + return ( diff --git a/apps/spreadsheeteditor/mobile/src/store/appOptions.js b/apps/spreadsheeteditor/mobile/src/store/appOptions.js index 497d8ff286..6adee182c7 100644 --- a/apps/spreadsheeteditor/mobile/src/store/appOptions.js +++ b/apps/spreadsheeteditor/mobile/src/store/appOptions.js @@ -21,6 +21,15 @@ export class storeAppOptions { isDocReady: observable, changeDocReady: action, + isAutosave: observable, + changeAutosave: action, + + isSaveBadgeShown: observable, + changeIsSaveBadgeShown: action, + + savingDocStatusText: observable, + changeSavingDocStatusText: action, + customization: observable, }); } @@ -52,6 +61,21 @@ export class storeAppOptions { this.isDocReady = value; } + isAutosave = true; + changeAutosave (value) { + this.isAutosave = value; + } + + isSaveBadgeShown = false; + changeIsSaveBadgeShown (value) { + this.isSaveBadgeShown = value; + } + + savingDocStatusText = ''; + changeSavingDocStatusText (value) { + this.savingDocStatusText = value; + } + setConfigOptions (config, _t) { this.config = config; this.customization = config.customization; diff --git a/apps/spreadsheeteditor/mobile/src/view/Toolbar.jsx b/apps/spreadsheeteditor/mobile/src/view/Toolbar.jsx index f1b063ea9e..4e7b6df372 100644 --- a/apps/spreadsheeteditor/mobile/src/view/Toolbar.jsx +++ b/apps/spreadsheeteditor/mobile/src/view/Toolbar.jsx @@ -84,7 +84,7 @@ const ToolbarView = props => { {Device.phone ? null : } {!props.isDrawMode && props.displayCollaboration && window.matchMedia("(min-width: 360px)").matches && !isVersionHistoryMode ? props.openOptions('coauth')}> : null} {isVersionHistoryMode ? props.openOptions('history')}> : null} - props.openOptions('settings')}> {Device.ios ? + props.openOptions('settings')}> {Device.ios ? : } diff --git a/apps/spreadsheeteditor/mobile/src/view/settings/SettingsPage.jsx b/apps/spreadsheeteditor/mobile/src/view/settings/SettingsPage.jsx index 893243219a..a34b3c46f1 100644 --- a/apps/spreadsheeteditor/mobile/src/view/settings/SettingsPage.jsx +++ b/apps/spreadsheeteditor/mobile/src/view/settings/SettingsPage.jsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { Page, Navbar, NavRight, Link, Icon, ListItem, List, f7 } from 'framework7-react'; +import { Page, Navbar, NavRight, Link, Icon, ListItem, List, f7, Toggle } from 'framework7-react'; import { useTranslation } from 'react-i18next'; import { Device } from '../../../../../common/mobile/utils/device'; import { observer, inject } from "mobx-react"; @@ -34,9 +34,13 @@ const SettingsPage = inject('storeAppOptions', 'storeSpreadsheetInfo', 'storeToo const closeButtonText = canCloseEditor && appOptions.customization.close.text; const gobackTitle = appOptions.customization?.goback?.text || _t.textOpenLocation; const isShowBack = props.storeToolbarSettings.isShowBack; + const isAutosave = appOptions.isAutosave; const navbar = -
{docTitle}
+
+ {docTitle} + {appOptions.savingDocStatusText} +
{Device.phone && {_t.textDone} } @@ -83,84 +87,110 @@ const SettingsPage = inject('storeAppOptions', 'storeSpreadsheetInfo', 'storeToo return ( {navbar} - - {isShowBack && - Common.Notifications.trigger('goback')}> - {Device.ios ? - : - - } + <> + + {isShowBack && + Common.Notifications.trigger('goback')}> + {Device.ios ? + : + + } + + } + {!appOptions.canLiveView && + + {Device.ios ? + : + + } + { + appOptions.changeAutosave(!isAutosave); + settingsContext.switchAutosave(!isAutosave); + }} + /> + + } + {!appOptions.canLiveView && + + {Device.ios ? + : + + } + + } + + + {!props.inPopover && + + + + } + {window.matchMedia("(max-width: 359px)").matches ? + onOpenOptions('coauth')} className='no-indicator'> + + + : null} + {_isEdit && + + + + } + + - } - {!props.inPopover && - - - - } - {window.matchMedia("(max-width: 359px)").matches ? - onOpenOptions('coauth')} className='no-indicator'> - - - : null} - {_isEdit && - - - - } - - - - {_isEdit && canUseHistory && - { - if(Device.phone) { - onOpenOptions('history'); - } - }}> - - - } - {_canDownload && - - - - } - {_canDownloadOrigin && - - - - } - {_canPrint && - - - - } - {!(!_canDisplayInfo && isBranding) && - - - - } - {_canHelp && - - - - } - {_canAbout && - - - - } - {_canFeedback && - - {Device.ios ? - : - - } - - } - {canCloseEditor && - Common.Notifications.trigger('close')}> - } - + {_isEdit && canUseHistory && + { + if(Device.phone) { + onOpenOptions('history'); + } + }}> + + + } + {_canDownload && + + + + } + {_canDownloadOrigin && + + + + } + {_canPrint && + + + + } + {!(!_canDisplayInfo && isBranding) && + + + + } + {_canHelp && + + + + } + {_canAbout && + + + + } + {_canFeedback && + + {Device.ios ? + : + + } + + } + {canCloseEditor && + Common.Notifications.trigger('close')}> + } + + ) }));