mirror of
https://github.com/laurent22/joplin.git
synced 2025-02-01 19:15:01 +02:00
Desktop, Mobile: Resolves #9263: Do not allow switching the sync target if not all resources are downloaded
This commit is contained in:
parent
8abd9b401b
commit
07b4117aa1
@ -738,6 +738,7 @@ packages/lib/models/Tag.test.js
|
||||
packages/lib/models/Tag.js
|
||||
packages/lib/models/dateTimeFormats.test.js
|
||||
packages/lib/models/settings/FileHandler.js
|
||||
packages/lib/models/settings/settingValidations.js
|
||||
packages/lib/models/utils/isItemId.js
|
||||
packages/lib/models/utils/itemCanBeEncrypted.js
|
||||
packages/lib/models/utils/paginatedFeed.js
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -718,6 +718,7 @@ packages/lib/models/Tag.test.js
|
||||
packages/lib/models/Tag.js
|
||||
packages/lib/models/dateTimeFormats.test.js
|
||||
packages/lib/models/settings/FileHandler.js
|
||||
packages/lib/models/settings/settingValidations.js
|
||||
packages/lib/models/utils/isItemId.js
|
||||
packages/lib/models/utils/itemCanBeEncrypted.js
|
||||
packages/lib/models/utils/paginatedFeed.js
|
||||
|
@ -68,7 +68,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
public componentDidMount() {
|
||||
if (this.props.defaultSection) {
|
||||
this.setState({ selectedSectionName: this.props.defaultSection }, () => {
|
||||
this.switchSection(this.props.defaultSection);
|
||||
void this.switchSection(this.props.defaultSection);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -112,7 +112,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
throw new Error(`Invalid screen name: ${screenName}`);
|
||||
}
|
||||
|
||||
public switchSection(name: string) {
|
||||
public async switchSection(name: string) {
|
||||
const section = this.sectionByName(name);
|
||||
let screenName = '';
|
||||
if (section.isScreen) {
|
||||
@ -120,7 +120,9 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
|
||||
if (this.hasChanges()) {
|
||||
const ok = confirm(_('This will open a new screen. Save your current changes?'));
|
||||
if (ok) shared.saveSettings(this);
|
||||
if (ok) {
|
||||
await shared.saveSettings(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +130,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
}
|
||||
|
||||
private sidebar_selectionChange(event: any) {
|
||||
this.switchSection(event.section.name);
|
||||
void this.switchSection(event.section.name);
|
||||
}
|
||||
|
||||
public renderSectionDescription(section: any) {
|
||||
@ -655,12 +657,15 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
}
|
||||
|
||||
public async onApplyClick() {
|
||||
shared.saveSettings(this);
|
||||
const done = await shared.saveSettings(this);
|
||||
if (!done) return;
|
||||
|
||||
await this.checkNeedRestart();
|
||||
}
|
||||
|
||||
public async onSaveClick() {
|
||||
shared.saveSettings(this);
|
||||
const done = await shared.saveSettings(this);
|
||||
if (!done) return;
|
||||
await this.checkNeedRestart();
|
||||
this.props.dispatch({ type: 'NAV_BACK' });
|
||||
}
|
||||
|
@ -109,7 +109,8 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
||||
// changedSettingKeys is cleared in shared.saveSettings so reading it now
|
||||
const shouldSetIgnoreTlsErrors = this.state.changedSettingKeys.includes('net.ignoreTlsErrors');
|
||||
|
||||
await shared.saveSettings(this);
|
||||
const done = await shared.saveSettings(this);
|
||||
if (!done) return;
|
||||
|
||||
if (shouldSetIgnoreTlsErrors) {
|
||||
await setIgnoreTlsErrors(Setting.value('net.ignoreTlsErrors'));
|
||||
|
@ -7,6 +7,7 @@ import Logger from '@joplin/utils/Logger';
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
import { type Registry } from '../../../registry';
|
||||
import settingValidations from '../../../models/settings/settingValidations';
|
||||
|
||||
const logger = Logger.create('config-shared');
|
||||
|
||||
@ -78,7 +79,7 @@ export const checkSyncConfig = async (comp: ConfigScreenComponent, settings: any
|
||||
|
||||
if (result.ok) {
|
||||
// Users often expect config to be auto-saved at this point, if the config check was successful
|
||||
saveSettings(comp);
|
||||
await saveSettings(comp);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@ -132,15 +133,21 @@ let scheduleSaveSettingsIID: ReturnType<typeof setTimeout>|null = null;
|
||||
export const scheduleSaveSettings = (comp: ConfigScreenComponent) => {
|
||||
if (scheduleSaveSettingsIID) clearTimeout(scheduleSaveSettingsIID);
|
||||
|
||||
scheduleSaveSettingsIID = setTimeout(() => {
|
||||
scheduleSaveSettingsIID = setTimeout(async () => {
|
||||
scheduleSaveSettingsIID = null;
|
||||
saveSettings(comp);
|
||||
await saveSettings(comp);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
export const saveSettings = (comp: ConfigScreenComponent) => {
|
||||
export const saveSettings = async (comp: ConfigScreenComponent) => {
|
||||
const savedSettingKeys = comp.state.changedSettingKeys.slice();
|
||||
|
||||
const validationMessage = await settingValidations(savedSettingKeys, comp.state.settings);
|
||||
if (validationMessage) {
|
||||
alert(validationMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const key in comp.state.settings) {
|
||||
if (!comp.state.settings.hasOwnProperty(key)) continue;
|
||||
if (comp.state.changedSettingKeys.indexOf(key) < 0) continue;
|
||||
@ -150,6 +157,8 @@ export const saveSettings = (comp: ConfigScreenComponent) => {
|
||||
comp.setState({ changedSettingKeys: [] });
|
||||
|
||||
onSettingsSaved({ savedSettingKeys });
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export const settingsToComponents = (comp: ConfigScreenComponent, device: AppType, settings: any) => {
|
||||
|
57
packages/lib/models/settings/settingValidations.ts
Normal file
57
packages/lib/models/settings/settingValidations.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { _ } from '../../locale';
|
||||
import shim from '../../shim';
|
||||
import BaseItem from '../BaseItem';
|
||||
import Resource from '../Resource';
|
||||
import Setting from '../Setting';
|
||||
|
||||
// Should return an error message if there's a problem, and an empty string if not.
|
||||
type ValidationHandler = (oldValue: any, newValue: any)=> Promise<string>;
|
||||
|
||||
const validations: Record<string, ValidationHandler> = {
|
||||
|
||||
'sync.target': async (oldValue: number, newValue: number) => {
|
||||
if (oldValue === 0 || newValue === 0) return '';
|
||||
|
||||
const preventChangeMessage = async () => {
|
||||
const needToBeFetched = await Resource.needToBeFetched('always', 1);
|
||||
if (needToBeFetched.length) return _('Some attachments need to be downloaded. Set the attachment download mode to "always" and try again.');
|
||||
|
||||
const downloadErrorCount = await Resource.downloadStatusCounts(Resource.FETCH_STATUS_ERROR);
|
||||
if (downloadErrorCount > 0) return _('Some attachments could not be downloaded. Please try to download them again.');
|
||||
|
||||
const disabledCount = await BaseItem.syncDisabledItemsCount(Setting.value('sync.target'));
|
||||
if (disabledCount > 0) return _('Some items could not be synchronised. Please try to synchronise them first.');
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const message = await preventChangeMessage();
|
||||
if (message) {
|
||||
const resolutionMessage = shim.isReactNative() ?
|
||||
_('Uninstall and reinstall the application. Make sure you create a backup first by exporting all your notes as JEX from the desktop application.') :
|
||||
_('Close the application, then delete your profile in "%s", and start the application again. Make sure you create a backup first by exporting all your notes as JEX.', Setting.value('profileDir'));
|
||||
|
||||
return _('The sync target cannot be changed for the following reason: %s\n\nIf the issue cannot be resolved, you may need to clear your data first by following these instructions:\n\n%s', message, resolutionMessage);
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
const validateSetting = async (settingName: string, oldValue: any, newValue: any) => {
|
||||
if (oldValue === newValue) return '';
|
||||
if (!validations[settingName]) return '';
|
||||
|
||||
return await validations[settingName](oldValue, newValue);
|
||||
};
|
||||
|
||||
export default async (settingKeys: string[], newValues: Record<string, any>) => {
|
||||
for (const key of settingKeys) {
|
||||
const oldValue = Setting.value(key);
|
||||
const newValue = newValues[key];
|
||||
const message = await validateSetting(key, oldValue, newValue);
|
||||
return message;
|
||||
}
|
||||
return '';
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user