Merge pull request 'fix/bug50809' (#1075) from fix/bug50809 into develop

Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/web-apps/pulls/1075
This commit is contained in:
Oleg Korshul
2026-03-26 19:52:40 +00:00
32 changed files with 639 additions and 305 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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 **/
}

View File

@ -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%);

View File

@ -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);

View File

@ -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;

View File

@ -440,6 +440,14 @@
}
}
.title {
.subtitle {
color: inherit;
margin-bottom: 5px;
margin-top: 3px;
}
}
.subnavbar .title {
font-size: 14px;
font-weight: normal;

View File

@ -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;

View File

@ -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",

View File

@ -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) => {

View File

@ -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;
}

View File

@ -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}
/>
)

View File

@ -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
}}>

View File

@ -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;

View File

@ -135,7 +135,7 @@ const ToolbarView = props => {
<SvgIcon slot="media" symbolId={IconVersionHistory.id} className={'icon icon-svg'} />
</Link>
: null),
<Link iconOnly key='btn-settings' className={(props.disabledSettings || props.disabledControls || isDisconnected || isOpenModal) && 'disabled'} id='btn-settings' href={false} onClick={() => props.openOptions('settings')}>
<Link iconOnly key='btn-settings' className={(props.disabledSettings || props.disabledControls || isDisconnected || isOpenModal) ? 'disabled' : (props.isSaveBadgeShown ? ' notify' : '')} id='btn-settings' href={false} onClick={() => props.openOptions('settings')}>
{Device.ios ?
<SvgIcon symbolId={IconSettingsIos.id} className={'icon icon-svg'} /> :
<SvgIcon symbolId={IconSettingsAndroid.id} className={'icon icon-svg'} />

View File

@ -46,7 +46,10 @@ const SettingsPage = inject("storeAppOptions", "storeReview", "storeDocumentInfo
const isHistoryDisabled = docExt && (docExt === 'xps' || docExt === 'djvu' || docExt === 'pdf');
const navbar =
<Navbar>
<div className="title" onClick={settingsContext.changeTitleHandler}>{docTitle}</div>
<div className="title" onClick={settingsContext.changeTitleHandler}>
{docTitle}
<span className="subtitle">{appOptions.savingDocStatusText}</span>
</div>
{Device.phone && <NavRight><Link popupClose=".settings-popup">{_t.textDone}</Link></NavRight>}
</Navbar>;
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 (
<Page>
{navbar}
<List>
{isShowBack &&
<ListItem title={gobackTitle} link="#" className='no-indicator' onClick={() => Common.Notifications.trigger('goback')}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{isEditableForms ? [
(isFavorite !== undefined && isFavorite !== null ?
<ListItem key='add-to-favorites-link' title={isFavorite ? t('Settings.textRemoveFromFavorites') : t('Settings.textAddToFavorites')} link='#' className='no-indicator' onClick={settingsContext.toggleFavorite}>
{isFavorite ?
<SvgIcon slot="media" symbolId={IconRemoveFavorites.id} className={'icon icon-svg'} />
:
<SvgIcon slot="media" symbolId={IconAddFavorites.id} className={'icon icon-svg'} />
<>
<List>
{isShowBack &&
<ListItem title={gobackTitle} link="#" className='no-indicator' onClick={() => Common.Notifications.trigger('goback')}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
: ''),
<ListItem key='clear-all-fields-link' title={t('Settings.textClearAllFields')} link='#' className='no-indicator' onClick={settingsContext.clearAllFields} disabled={isSignatureForm}>
<SvgIcon slot="media" symbolId={IconClearFields.id} className={'icon icon-svg'} />
}
{isEditableForms ? [
(isFavorite !== undefined && isFavorite !== null ?
<ListItem key='add-to-favorites-link' title={isFavorite ? t('Settings.textRemoveFromFavorites') : t('Settings.textAddToFavorites')} link='#' className='no-indicator' onClick={settingsContext.toggleFavorite}>
{isFavorite ?
<SvgIcon slot="media" symbolId={IconRemoveFavorites.id} className={'icon icon-svg'} />
:
<SvgIcon slot="media" symbolId={IconAddFavorites.id} className={'icon icon-svg'} />
}
</ListItem>
: ''),
<ListItem key='clear-all-fields-link' title={t('Settings.textClearAllFields')} link='#' className='no-indicator' onClick={settingsContext.clearAllFields} disabled={isSignatureForm}>
<SvgIcon slot="media" symbolId={IconClearFields.id} className={'icon icon-svg'} />
</ListItem>
] : null}
{!appOptions.canLiveView &&
<ListItem title={_t.textAutoSaveDocument}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
<Toggle checked={isAutosave}
onToggleChange={() => {
appOptions.changeAutosave(!isAutosave);
settingsContext.switchAutosave(!isAutosave);
}}
/>
</ListItem>
}
{!appOptions.canLiveView &&
<ListItem title={_t.textSaveDocument} className={`no-indicator${appOptions.isSaveBadgeShown ? ' notify' : ''}`} onClick={settingsContext.tryToSave}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
</List>
<List>
{(Device.phone || isEditableForms) &&
<ListItem title={!_isEdit || isViewer ? _t.textFind : _t.textFindAndReplace} link='#' searchbarEnable='.searchbar' onClick={settingsContext.closeModal} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconSearch.id} className={'icon icon-svg'} />
</ListItem>
}
{(_isEdit && canProtect) &&
<ListItem title={t('Settings.textProtection')} link="/protection">
<SvgIcon slot="media" symbolId={IconProtection.id} className={'icon icon-svg'} />
</ListItem>
}
{_isEdit && !isHistoryDisabled && canUseHistory &&
<ListItem title={t('Settings.textVersionHistory')} link={!Device.phone ? "/version-history" : ""} onClick={() => {
if(Device.phone) {
onOpenOptions('history');
}
}}>
<SvgIcon slot="media" symbolId={IconVersionHistory.id} className={'icon icon-svg'} />
</ListItem>
}
{!isEditableForms ?
<ListItem title={t('Settings.textNavigation')} link={!Device.phone ? '/navigation' : '#'} onClick={() => {
if(Device.phone) {
onOpenOptions('navigation');
}
}}>
<SvgIcon slot="media" symbolId={IconNavigation.id} className={'icon icon-svg'} />
</ListItem>
: null}
{window.matchMedia("(max-width: 359px)").matches ?
<ListItem title={_t.textCollaboration} link="#" onClick={() => {
onOpenOptions('coauth');
}} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconCollaboration.id} className={'icon icon-svg '} />
</ListItem>
: null}
{Device.sailfish && _isEdit &&
<ListItem title={_t.textSpellcheck} onClick={() => settingsContext.onOrthographyCheck()} className='no-indicator' link="#">
<SvgIcon slot="media" symbolId={IconSpellcheck.id} className={'icon icon-svg'} />
</ListItem>
}
{(appOptions.isMobileViewAvailable && ((Device.phone && !isViewer) || isEditableForms)) &&
<ListItem title={t('Settings.textMobileView')}>
<SvgIcon slot="media" symbolId={IconMobileView.id} className={'icon icon-svg'} />
<Toggle checked={isMobileView} onToggleChange={() => {
onOpenOptions('snackbar');
settingsContext.onChangeMobileView();
}} />
</ListItem>
}
{(_isEdit && !isViewer) &&
<ListItem title={_t.textDocumentSettings} disabled={displayMode !== 'markup'} link='/document-settings/'>
<SvgIcon slot="media" symbolId={IconDocSetup.id} className={'icon icon-svg'} />
</ListItem>
}
<ListItem title={_t.textApplicationSettings} link="/application-settings/">
<SvgIcon slot="media" symbolId={IconAppSettings.id} className={'icon icon-svg'} />
</ListItem>
] : null}
{(Device.phone || isEditableForms) &&
<ListItem title={!_isEdit || isViewer ? _t.textFind : _t.textFindAndReplace} link='#' searchbarEnable='.searchbar' onClick={settingsContext.closeModal} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconSearch.id} className={'icon icon-svg'} />
</ListItem>
}
{(_isEdit && canProtect) &&
<ListItem title={t('Settings.textProtection')} link="/protection">
<SvgIcon slot="media" symbolId={IconProtection.id} className={'icon icon-svg'} />
</ListItem>
}
{_isEdit && !isHistoryDisabled && canUseHistory &&
<ListItem title={t('Settings.textVersionHistory')} link={!Device.phone ? "/version-history" : ""} onClick={() => {
if(Device.phone) {
onOpenOptions('history');
}
}}>
<SvgIcon slot="media" symbolId={IconVersionHistory.id} className={'icon icon-svg'} />
</ListItem>
}
{!isEditableForms ?
<ListItem title={t('Settings.textNavigation')} link={!Device.phone ? '/navigation' : '#'} onClick={() => {
if(Device.phone) {
onOpenOptions('navigation');
}
}}>
<SvgIcon slot="media" symbolId={IconNavigation.id} className={'icon icon-svg'} />
</ListItem>
: null}
{window.matchMedia("(max-width: 359px)").matches ?
<ListItem title={_t.textCollaboration} link="#" onClick={() => {
onOpenOptions('coauth');
}} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconCollaboration.id} className={'icon icon-svg '} />
</ListItem>
: null}
{Device.sailfish && _isEdit &&
<ListItem title={_t.textSpellcheck} onClick={() => settingsContext.onOrthographyCheck()} className='no-indicator' link="#">
<SvgIcon slot="media" symbolId={IconSpellcheck.id} className={'icon icon-svg'} />
</ListItem>
}
{(appOptions.isMobileViewAvailable && ((Device.phone && !isViewer) || isEditableForms)) &&
<ListItem title={t('Settings.textMobileView')}>
<SvgIcon slot="media" symbolId={IconMobileView.id} className={'icon icon-svg'} />
<Toggle checked={isMobileView} onToggleChange={() => {
onOpenOptions('snackbar');
settingsContext.onChangeMobileView();
}} />
</ListItem>
}
{(_isEdit && !isViewer) &&
<ListItem title={_t.textDocumentSettings} disabled={displayMode !== 'markup'} link='/document-settings/'>
<SvgIcon slot="media" symbolId={IconDocSetup.id} className={'icon icon-svg'} />
</ListItem>
}
<ListItem title={_t.textApplicationSettings} link="/application-settings/">
<SvgIcon slot="media" symbolId={IconAppSettings.id} className={'icon icon-svg'} />
</ListItem>
{_canDownload &&
<ListItem title={isEditableForms ? t('Settings.textExport') : _t.textDownload} link="/download/">
{isEditableForms ?
<SvgIcon slot="media" symbolId={IconExport.id} className={'icon icon-svg'} /> :
{_canDownload &&
<ListItem title={isEditableForms ? t('Settings.textExport') : _t.textDownload} link="/download/">
{isEditableForms ?
<SvgIcon slot="media" symbolId={IconExport.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
}
</ListItem>
}
{_canDownloadOrigin &&
<ListItem title={_t.textDownload} link="#" onClick={settingsContext.onDownloadOrigin} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
}
</ListItem>
}
{_canDownloadOrigin &&
<ListItem title={_t.textDownload} link="#" onClick={settingsContext.onDownloadOrigin} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canPrint &&
<ListItem title={_t.textPrint} onClick={settingsContext.onPrint} link='#' className='no-indicator'>
<SvgIcon slot="media" symbolId={IconPrint.id} className={'icon icon-svg'} />
</ListItem>
}
{!(!_canDisplayInfo && isBranding) &&
<ListItem title={_t.textDocumentInfo} link="/document-info/">
<SvgIcon slot="media" symbolId={IconInfo.id} className={'icon icon-svg'} />
</ListItem>
}
{_canHelp &&
<ListItem title={_t.textHelp} link="#" className='no-indicator' onClick={settingsContext.showHelp}>
<SvgIcon slot="media" symbolId={IconHelp.id} className={'icon icon-svg'} />
</ListItem>
}
{_canAbout &&
<ListItem title={_t.textAbout} link="/about/">
<SvgIcon slot="media" symbolId={IconAbout.id} className={'icon icon-svg'} />
</ListItem>
}
{_canFeedback &&
<ListItem title={t('Settings.textFeedback')} link="#" className='no-indicator' onClick={settingsContext.showFeedback}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconFeedbackForIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconFeedbackForAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{canCloseEditor &&
<ListItem title={closeButtonText ?? t('Settings.textClose')} link="#" className='close-editor-btn no-indicator' onClick={() => Common.Notifications.trigger('close')}></ListItem>
}
</List>
</ListItem>
}
{_canPrint &&
<ListItem title={_t.textPrint} onClick={settingsContext.onPrint} link='#' className='no-indicator'>
<SvgIcon slot="media" symbolId={IconPrint.id} className={'icon icon-svg'} />
</ListItem>
}
{!(!_canDisplayInfo && isBranding) &&
<ListItem title={_t.textDocumentInfo} link="/document-info/">
<SvgIcon slot="media" symbolId={IconInfo.id} className={'icon icon-svg'} />
</ListItem>
}
{_canHelp &&
<ListItem title={_t.textHelp} link="#" className='no-indicator' onClick={settingsContext.showHelp}>
<SvgIcon slot="media" symbolId={IconHelp.id} className={'icon icon-svg'} />
</ListItem>
}
{_canAbout &&
<ListItem title={_t.textAbout} link="/about/">
<SvgIcon slot="media" symbolId={IconAbout.id} className={'icon icon-svg'} />
</ListItem>
}
{_canFeedback &&
<ListItem title={t('Settings.textFeedback')} link="#" className='no-indicator' onClick={settingsContext.showFeedback}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconFeedbackForIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconFeedbackForAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{canCloseEditor &&
<ListItem title={closeButtonText ?? t('Settings.textClose')} link="#" className='close-editor-btn no-indicator' onClick={() => Common.Notifications.trigger('close')}></ListItem>
}
</List>
</>
</Page>
)
}));

View File

@ -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",

View File

@ -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']:

View File

@ -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;
}

View File

@ -296,6 +296,7 @@ const ToolbarController = inject('storeAppOptions', 'users', 'storeFocusObjects'
changeTitleHandler={changeTitleHandler}
forceDesktopMode={forceDesktopMode}
isHiddenFileName={appOptions.config?.customization?.toolbarHideFileName ?? false}
isSaveBadgeShown={appOptions.isSaveBadgeShown}
/>
)
}));

View File

@ -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 (
<SettingsContext.Provider value={{
onPrint,
@ -167,7 +179,9 @@ const SettingsController = props => {
showFeedback,
onDownloadOrigin,
closeModal,
changeTitleHandler
changeTitleHandler,
switchAutosave,
tryToSave,
}}>
<SettingsView />
</SettingsContext.Provider>

View File

@ -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;

View File

@ -93,7 +93,7 @@ const ToolbarView = props => {
{Device.phone ? null : <Link iconOnly className={(props.disabledControls || props.disabledPreview || isOpenModal) && 'disabled'}searchbarEnable='.searchbar' href={false}> <SvgIcon symbolId={IconSearch.id} className={'icon icon-svg'} /></Link>}
{props.displayCollaboration && window.matchMedia("(min-width: 375px)").matches && !isVersionHistoryMode ? <Link iconOnly className={(props.disabledControls || isOpenModal) && 'disabled'} id='btn-coauth' href={false} onClick={() => props.openOptions('coauth')}><SvgIcon symbolId={IconCollaboration.id} className={'icon icon-svg'} /></Link> : null}
{isVersionHistoryMode ? <Link iconOnly id='btn-open-history' href={false} className={isOpenModal && 'disabled'} onClick={() => props.openOptions('history')}><SvgIcon symbolId={IconHistory.id} className={'icon icon-svg'} /> </Link> : null}
<Link iconOnly className={(props.disabledSettings || props.disabledControls || isDisconnected || isOpenModal) && 'disabled'} id='btn-settings' href={false} onClick={() => props.openOptions('settings')}>
<Link iconOnly className={(props.disabledSettings || props.disabledControls || isDisconnected || isOpenModal) ? 'disabled' : (props.isSaveBadgeShown ? ' notify' : '')} id='btn-settings' href={false} onClick={() => props.openOptions('settings')}>
{Device.ios ?
<SvgIcon symbolId={IconSettingsIos.id} className={'icon icon-svg'} /> :
<SvgIcon symbolId={IconSettingsAndroid.id} className={'icon icon-svg'} />

View File

@ -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 =
<Navbar>
<div className="title" onClick={settingsContext.changeTitleHandler}>{docTitle}</div>
<div className="title" onClick={settingsContext.changeTitleHandler}>
{docTitle}
<span className="subtitle">{appOptions.savingDocStatusText}</span>
</div>
{Device.phone && <NavRight><Link popupClose=".settings-popup">{_t.textDone}</Link></NavRight>}
</Navbar>;
@ -83,84 +87,110 @@ const SettingsPage = inject('storeAppOptions', 'storeToolbarSettings', 'storePre
return (
<Page>
{navbar}
<List>
{isShowBack &&
<ListItem title={gobackTitle} link="#" className='no-indicator' onClick={() => Common.Notifications.trigger('goback')}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
<>
<List>
{isShowBack &&
<ListItem title={gobackTitle} link="#" className='no-indicator' onClick={() => Common.Notifications.trigger('goback')}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{!appOptions.canLiveView &&
<ListItem title={_t.textAutoSaveDocument}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
<Toggle checked={isAutosave}
onToggleChange={() => {
appOptions.changeAutosave(!isAutosave);
settingsContext.switchAutosave(!isAutosave);
}}
/>
</ListItem>
}
{!appOptions.canLiveView &&
<ListItem title={_t.textSaveDocument} className={`no-indicator${appOptions.isSaveBadgeShown ? ' notify' : ''}`} onClick={settingsContext.tryToSave}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
</List>
<List>
{!props.inPopover &&
<ListItem disabled={appOptions.readerMode || disabledPreview ? true : false} title={!_isEdit ? _t.textFind : _t.textFindAndReplace} link="#" searchbarEnable='.searchbar' onClick={settingsContext.closeModal} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconSearch.id} className={'icon icon-svg'} />
</ListItem>
}
{window.matchMedia("(max-width: 374px)").matches ?
<ListItem title={_t.textCollaboration} link="#" onClick={() => onOpenOptions('coauth')} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconCollaboration.id} className={'icon icon-svg'} />
</ListItem>
: null}
{_isEdit &&
<ListItem link="/presentation-settings/" title={_t.textPresentationSettings}>
<SvgIcon slot="media" symbolId={IconSetup.id} className={'icon icon-svg'} />
</ListItem>
}
<ListItem title={_t.textApplicationSettings} link="/application-settings/">
<SvgIcon slot="media" symbolId={IconAppSettings.id} className={'icon icon-svg'} />
</ListItem>
}
{!props.inPopover &&
<ListItem disabled={appOptions.readerMode || disabledPreview ? true : false} title={!_isEdit ? _t.textFind : _t.textFindAndReplace} link="#" searchbarEnable='.searchbar' onClick={settingsContext.closeModal} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconSearch.id} className={'icon icon-svg'} />
</ListItem>
}
{window.matchMedia("(max-width: 374px)").matches ?
<ListItem title={_t.textCollaboration} link="#" onClick={() => onOpenOptions('coauth')} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconCollaboration.id} className={'icon icon-svg'} />
</ListItem>
: null}
{_isEdit &&
<ListItem link="/presentation-settings/" title={_t.textPresentationSettings}>
<SvgIcon slot="media" symbolId={IconSetup.id} className={'icon icon-svg'} />
</ListItem>
}
<ListItem title={_t.textApplicationSettings} link="/application-settings/">
<SvgIcon slot="media" symbolId={IconAppSettings.id} className={'icon icon-svg'} />
</ListItem>
{_isEdit && canUseHistory &&
<ListItem title={t('View.Settings.textVersionHistory')} link={!Device.phone ? "/version-history" : ""} onClick={() => {
if(Device.phone) {
onOpenOptions('history');
}
}}>
<SvgIcon slot="media" symbolId={IconVersionHistory.id} className={'icon icon-svg'} />
</ListItem>
}
{_canDownload &&
<ListItem title={_t.textDownload} link="/download/">
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canDownloadOrigin &&
<ListItem title={_t.textDownload} link="#" onClick={settingsContext.onDownloadOrigin} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canPrint &&
<ListItem className={disabledPreview && 'disabled'} title={_t.textPrint} onClick={settingsContext.onPrint}>
<SvgIcon slot="media" symbolId={IconPrint.id} className={'icon icon-svg'} />
</ListItem>
}
{!(!_canDisplayInfo && isBranding) &&
<ListItem title={_t.textPresentationInfo} link="/presentation-info/">
<SvgIcon slot="media" symbolId={IconInfo .id} className={'icon icon-svg'} />
</ListItem>
}
{_canHelp &&
<ListItem title={_t.textHelp} link="#" className='no-indicator' onClick={settingsContext.showHelp}>
<SvgIcon slot="media" symbolId={IconHelp.id} className={'icon icon-svg'} />
</ListItem>
}
{_canAbout &&
<ListItem title={_t.textAbout} link="/about/">
<SvgIcon slot="media" symbolId={IconAbout.id} className={'icon icon-svg'} />
</ListItem>
}
{_canFeedback &&
<ListItem title={t('View.Settings.textFeedback')} link="#" className='no-indicator' onClick={settingsContext.showFeedback}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconFeedbackIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconFeedbackAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{canCloseEditor &&
<ListItem title={closeButtonText ?? t('View.Settings.textClose')} link="#" className='close-editor-btn no-indicator' onClick={() => Common.Notifications.trigger('close')}></ListItem>
}
</List>
{_isEdit && canUseHistory &&
<ListItem title={t('View.Settings.textVersionHistory')} link={!Device.phone ? "/version-history" : ""} onClick={() => {
if(Device.phone) {
onOpenOptions('history');
}
}}>
<SvgIcon slot="media" symbolId={IconVersionHistory.id} className={'icon icon-svg'} />
</ListItem>
}
{_canDownload &&
<ListItem title={_t.textDownload} link="/download/">
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canDownloadOrigin &&
<ListItem title={_t.textDownload} link="#" onClick={settingsContext.onDownloadOrigin} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canPrint &&
<ListItem className={disabledPreview && 'disabled'} title={_t.textPrint} onClick={settingsContext.onPrint}>
<SvgIcon slot="media" symbolId={IconPrint.id} className={'icon icon-svg'} />
</ListItem>
}
{!(!_canDisplayInfo && isBranding) &&
<ListItem title={_t.textPresentationInfo} link="/presentation-info/">
<SvgIcon slot="media" symbolId={IconInfo .id} className={'icon icon-svg'} />
</ListItem>
}
{_canHelp &&
<ListItem title={_t.textHelp} link="#" className='no-indicator' onClick={settingsContext.showHelp}>
<SvgIcon slot="media" symbolId={IconHelp.id} className={'icon icon-svg'} />
</ListItem>
}
{_canAbout &&
<ListItem title={_t.textAbout} link="/about/">
<SvgIcon slot="media" symbolId={IconAbout.id} className={'icon icon-svg'} />
</ListItem>
}
{_canFeedback &&
<ListItem title={t('View.Settings.textFeedback')} link="#" className='no-indicator' onClick={settingsContext.showFeedback}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconFeedbackIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconFeedbackAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{canCloseEditor &&
<ListItem title={closeButtonText ?? t('View.Settings.textClose')} link="#" className='close-editor-btn no-indicator' onClick={() => Common.Notifications.trigger('close')}></ListItem>
}
</List>
</>
</Page>
)
}));

View File

@ -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",

View File

@ -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) => {

View File

@ -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 () {

View File

@ -317,6 +317,7 @@ const ToolbarController = inject('storeAppOptions', 'users', 'storeSpreadsheetIn
changeTitleHandler={changeTitleHandler}
forceDesktopMode={forceDesktopMode}
isHiddenFileName={appOptions.config?.customization?.toolbarHideFileName ?? false}
isSaveBadgeShown={appOptions.isSaveBadgeShown}
/>
)
}));

View File

@ -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 (
<SettingsContext.Provider value={{
onPrint,
@ -184,7 +196,9 @@ const SettingsController = inject('storeAppOptions', 'storeSpreadsheetInfo')(obs
showFeedback,
onDownloadOrigin,
closeModal,
changeTitleHandler
changeTitleHandler,
switchAutosave,
tryToSave,
}}>
<SettingsView />
</SettingsContext.Provider>

View File

@ -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;

View File

@ -84,7 +84,7 @@ const ToolbarView = props => {
{Device.phone ? null : <Link iconOnly className={(props.disabledControls || props.disabledSearch || isOpenModal) && 'disabled'} searchbarEnable='.searchbar' href={false}><SvgIcon symbolId={IconSearch.id} className={'icon icon-svg'} /></Link>}
{!props.isDrawMode && props.displayCollaboration && window.matchMedia("(min-width: 360px)").matches && !isVersionHistoryMode ? <Link iconOnly className={(props.disabledControls || props.disabledCollaboration || isOpenModal) && 'disabled'} id='btn-coauth' href={false} onClick={() => props.openOptions('coauth')}><SvgIcon symbolId={IconCollaboration.id} className={'icon icon-svg'} /></Link> : null}
{isVersionHistoryMode ? <Link iconOnly id='btn-open-history' href={false} className={isOpenModal && 'disabled'} onClick={() => props.openOptions('history')}><SvgIcon symbolId={IconHistory.id} className={'icon icon-svg'} /></Link> : null}
<Link iconOnly className={(props.disabledSettings || props.disabledControls || isDisconnected || isOpenModal) && 'disabled'} id='btn-settings' href={false} onClick={() => props.openOptions('settings')}> {Device.ios ?
<Link iconOnly className={(props.disabledSettings || props.disabledControls || isDisconnected || isOpenModal) ? 'disabled' : (props.isSaveBadgeShown ? ' notify' : '')} id='btn-settings' href={false} onClick={() => props.openOptions('settings')}> {Device.ios ?
<SvgIcon symbolId={IconSettingsIos.id} className={'icon icon-svg'} /> :
<SvgIcon symbolId={IconSettingsAndroid.id} className={'icon icon-svg'} />
}</Link>

View File

@ -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 =
<Navbar>
<div className="title" onClick={settingsContext.changeTitleHandler}>{docTitle}</div>
<div className="title" onClick={settingsContext.changeTitleHandler}>
{docTitle}
<span className="subtitle">{appOptions.savingDocStatusText}</span>
</div>
{Device.phone &&
<NavRight><Link popupClose=".settings-popup">{_t.textDone}</Link></NavRight>
}
@ -83,84 +87,110 @@ const SettingsPage = inject('storeAppOptions', 'storeSpreadsheetInfo', 'storeToo
return (
<Page>
{navbar}
<List>
{isShowBack &&
<ListItem title={gobackTitle} link="#" className='no-indicator' onClick={() => Common.Notifications.trigger('goback')}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
<>
<List>
{isShowBack &&
<ListItem title={gobackTitle} link="#" className='no-indicator' onClick={() => Common.Notifications.trigger('goback')}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{!appOptions.canLiveView &&
<ListItem title={_t.textAutoSaveDocument}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
<Toggle checked={isAutosave}
onToggleChange={() => {
appOptions.changeAutosave(!isAutosave);
settingsContext.switchAutosave(!isAutosave);
}}
/>
</ListItem>
}
{!appOptions.canLiveView &&
<ListItem title={_t.textSaveDocument} className={`no-indicator${appOptions.isSaveBadgeShown ? ' notify' : ''}`} onClick={settingsContext.tryToSave}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconReturnIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconReturnAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
</List>
<List>
{!props.inPopover &&
<ListItem disabled={appOptions.readerMode ? true : false} title={!_isEdit ? _t.textFind : _t.textFindAndReplace} link="#" searchbarEnable='.searchbar' onClick={settingsContext.closeModal} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconSearch.id} className={'icon icon-svg'} />
</ListItem>
}
{window.matchMedia("(max-width: 359px)").matches ?
<ListItem title={_t.textCollaboration} link="#" onClick={() => onOpenOptions('coauth')} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconCollaboration.id} className={'icon icon-svg'} />
</ListItem>
: null}
{_isEdit &&
<ListItem link="/spreadsheet-settings/" title={_t.textSpreadsheetSettings}>
<SvgIcon slot="media" symbolId={IconTableSettings.id} className={'icon icon-svg'} />
</ListItem>
}
<ListItem title={_t.textApplicationSettings} link="/application-settings/">
<SvgIcon slot="media" symbolId={IconAppSettings.id} className={'icon icon-svg'} />
</ListItem>
}
{!props.inPopover &&
<ListItem disabled={appOptions.readerMode ? true : false} title={!_isEdit ? _t.textFind : _t.textFindAndReplace} link="#" searchbarEnable='.searchbar' onClick={settingsContext.closeModal} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconSearch.id} className={'icon icon-svg'} />
</ListItem>
}
{window.matchMedia("(max-width: 359px)").matches ?
<ListItem title={_t.textCollaboration} link="#" onClick={() => onOpenOptions('coauth')} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconCollaboration.id} className={'icon icon-svg'} />
</ListItem>
: null}
{_isEdit &&
<ListItem link="/spreadsheet-settings/" title={_t.textSpreadsheetSettings}>
<SvgIcon slot="media" symbolId={IconTableSettings.id} className={'icon icon-svg'} />
</ListItem>
}
<ListItem title={_t.textApplicationSettings} link="/application-settings/">
<SvgIcon slot="media" symbolId={IconAppSettings.id} className={'icon icon-svg'} />
</ListItem>
{_isEdit && canUseHistory &&
<ListItem title={t('View.Settings.textVersionHistory')} link={!Device.phone ? "/version-history" : ""} onClick={() => {
if(Device.phone) {
onOpenOptions('history');
}
}}>
<SvgIcon slot="media" symbolId={IconVersionHistory.id} className={'icon icon-svg'} />
</ListItem>
}
{_canDownload &&
<ListItem title={_t.textDownload} link="/download/">
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canDownloadOrigin &&
<ListItem title={_t.textDownload} link="#" onClick={settingsContext.onDownloadOrigin} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canPrint &&
<ListItem title={_t.textPrint} onClick={settingsContext.onPrint}>
<SvgIcon slot="media" symbolId={IconPrint.id} className={'icon icon-svg'} />
</ListItem>
}
{!(!_canDisplayInfo && isBranding) &&
<ListItem title={_t.textSpreadsheetInfo} link="/spreadsheet-info/">
<SvgIcon slot="media" symbolId={IconInfo .id} className={'icon icon-svg'} />
</ListItem>
}
{_canHelp &&
<ListItem title={_t.textHelp} link="#" className='no-indicator' onClick={settingsContext.showHelp}>
<SvgIcon slot="media" symbolId={IconHelp.id} className={'icon icon-svg'} />
</ListItem>
}
{_canAbout &&
<ListItem title={_t.textAbout} link="/about/">
<SvgIcon slot="media" symbolId={IconAbout.id} className={'icon icon-svg'} />
</ListItem>
}
{_canFeedback &&
<ListItem title={t('View.Settings.textFeedback')} link="#" className='no-indicator' onClick={settingsContext.showFeedback}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconFeedbackIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconFeedbackAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{canCloseEditor &&
<ListItem title={closeButtonText ?? t('View.Settings.textClose')} link="#" className='close-editor-btn no-indicator' onClick={() => Common.Notifications.trigger('close')}></ListItem>
}
</List>
{_isEdit && canUseHistory &&
<ListItem title={t('View.Settings.textVersionHistory')} link={!Device.phone ? "/version-history" : ""} onClick={() => {
if(Device.phone) {
onOpenOptions('history');
}
}}>
<SvgIcon slot="media" symbolId={IconVersionHistory.id} className={'icon icon-svg'} />
</ListItem>
}
{_canDownload &&
<ListItem title={_t.textDownload} link="/download/">
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canDownloadOrigin &&
<ListItem title={_t.textDownload} link="#" onClick={settingsContext.onDownloadOrigin} className='no-indicator'>
<SvgIcon slot="media" symbolId={IconDownload.id} className={'icon icon-svg'} />
</ListItem>
}
{_canPrint &&
<ListItem title={_t.textPrint} onClick={settingsContext.onPrint}>
<SvgIcon slot="media" symbolId={IconPrint.id} className={'icon icon-svg'} />
</ListItem>
}
{!(!_canDisplayInfo && isBranding) &&
<ListItem title={_t.textSpreadsheetInfo} link="/spreadsheet-info/">
<SvgIcon slot="media" symbolId={IconInfo .id} className={'icon icon-svg'} />
</ListItem>
}
{_canHelp &&
<ListItem title={_t.textHelp} link="#" className='no-indicator' onClick={settingsContext.showHelp}>
<SvgIcon slot="media" symbolId={IconHelp.id} className={'icon icon-svg'} />
</ListItem>
}
{_canAbout &&
<ListItem title={_t.textAbout} link="/about/">
<SvgIcon slot="media" symbolId={IconAbout.id} className={'icon icon-svg'} />
</ListItem>
}
{_canFeedback &&
<ListItem title={t('View.Settings.textFeedback')} link="#" className='no-indicator' onClick={settingsContext.showFeedback}>
{Device.ios ?
<SvgIcon slot="media" symbolId={IconFeedbackIos.id} className={'icon icon-svg'} /> :
<SvgIcon slot="media" symbolId={IconFeedbackAndroid.id} className={'icon icon-svg'} />
}
</ListItem>
}
{canCloseEditor &&
<ListItem title={closeButtonText ?? t('View.Settings.textClose')} link="#" className='close-editor-btn no-indicator' onClick={() => Common.Notifications.trigger('close')}></ListItem>
}
</List>
</>
</Page>
)
}));