mirror of
https://github.com/laurent22/joplin.git
synced 2025-02-10 19:41:43 +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/Tag.js
|
||||||
packages/lib/models/dateTimeFormats.test.js
|
packages/lib/models/dateTimeFormats.test.js
|
||||||
packages/lib/models/settings/FileHandler.js
|
packages/lib/models/settings/FileHandler.js
|
||||||
|
packages/lib/models/settings/settingValidations.js
|
||||||
packages/lib/models/utils/isItemId.js
|
packages/lib/models/utils/isItemId.js
|
||||||
packages/lib/models/utils/itemCanBeEncrypted.js
|
packages/lib/models/utils/itemCanBeEncrypted.js
|
||||||
packages/lib/models/utils/paginatedFeed.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/Tag.js
|
||||||
packages/lib/models/dateTimeFormats.test.js
|
packages/lib/models/dateTimeFormats.test.js
|
||||||
packages/lib/models/settings/FileHandler.js
|
packages/lib/models/settings/FileHandler.js
|
||||||
|
packages/lib/models/settings/settingValidations.js
|
||||||
packages/lib/models/utils/isItemId.js
|
packages/lib/models/utils/isItemId.js
|
||||||
packages/lib/models/utils/itemCanBeEncrypted.js
|
packages/lib/models/utils/itemCanBeEncrypted.js
|
||||||
packages/lib/models/utils/paginatedFeed.js
|
packages/lib/models/utils/paginatedFeed.js
|
||||||
|
@ -68,7 +68,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
if (this.props.defaultSection) {
|
if (this.props.defaultSection) {
|
||||||
this.setState({ selectedSectionName: 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}`);
|
throw new Error(`Invalid screen name: ${screenName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public switchSection(name: string) {
|
public async switchSection(name: string) {
|
||||||
const section = this.sectionByName(name);
|
const section = this.sectionByName(name);
|
||||||
let screenName = '';
|
let screenName = '';
|
||||||
if (section.isScreen) {
|
if (section.isScreen) {
|
||||||
@ -120,7 +120,9 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||||||
|
|
||||||
if (this.hasChanges()) {
|
if (this.hasChanges()) {
|
||||||
const ok = confirm(_('This will open a new screen. Save your current changes?'));
|
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) {
|
private sidebar_selectionChange(event: any) {
|
||||||
this.switchSection(event.section.name);
|
void this.switchSection(event.section.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderSectionDescription(section: any) {
|
public renderSectionDescription(section: any) {
|
||||||
@ -655,12 +657,15 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onApplyClick() {
|
public async onApplyClick() {
|
||||||
shared.saveSettings(this);
|
const done = await shared.saveSettings(this);
|
||||||
|
if (!done) return;
|
||||||
|
|
||||||
await this.checkNeedRestart();
|
await this.checkNeedRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onSaveClick() {
|
public async onSaveClick() {
|
||||||
shared.saveSettings(this);
|
const done = await shared.saveSettings(this);
|
||||||
|
if (!done) return;
|
||||||
await this.checkNeedRestart();
|
await this.checkNeedRestart();
|
||||||
this.props.dispatch({ type: 'NAV_BACK' });
|
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
|
// changedSettingKeys is cleared in shared.saveSettings so reading it now
|
||||||
const shouldSetIgnoreTlsErrors = this.state.changedSettingKeys.includes('net.ignoreTlsErrors');
|
const shouldSetIgnoreTlsErrors = this.state.changedSettingKeys.includes('net.ignoreTlsErrors');
|
||||||
|
|
||||||
await shared.saveSettings(this);
|
const done = await shared.saveSettings(this);
|
||||||
|
if (!done) return;
|
||||||
|
|
||||||
if (shouldSetIgnoreTlsErrors) {
|
if (shouldSetIgnoreTlsErrors) {
|
||||||
await setIgnoreTlsErrors(Setting.value('net.ignoreTlsErrors'));
|
await setIgnoreTlsErrors(Setting.value('net.ignoreTlsErrors'));
|
||||||
|
@ -7,6 +7,7 @@ import Logger from '@joplin/utils/Logger';
|
|||||||
|
|
||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
import { type Registry } from '../../../registry';
|
import { type Registry } from '../../../registry';
|
||||||
|
import settingValidations from '../../../models/settings/settingValidations';
|
||||||
|
|
||||||
const logger = Logger.create('config-shared');
|
const logger = Logger.create('config-shared');
|
||||||
|
|
||||||
@ -78,7 +79,7 @@ export const checkSyncConfig = async (comp: ConfigScreenComponent, settings: any
|
|||||||
|
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
// Users often expect config to be auto-saved at this point, if the config check was successful
|
// Users often expect config to be auto-saved at this point, if the config check was successful
|
||||||
saveSettings(comp);
|
await saveSettings(comp);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@ -132,15 +133,21 @@ let scheduleSaveSettingsIID: ReturnType<typeof setTimeout>|null = null;
|
|||||||
export const scheduleSaveSettings = (comp: ConfigScreenComponent) => {
|
export const scheduleSaveSettings = (comp: ConfigScreenComponent) => {
|
||||||
if (scheduleSaveSettingsIID) clearTimeout(scheduleSaveSettingsIID);
|
if (scheduleSaveSettingsIID) clearTimeout(scheduleSaveSettingsIID);
|
||||||
|
|
||||||
scheduleSaveSettingsIID = setTimeout(() => {
|
scheduleSaveSettingsIID = setTimeout(async () => {
|
||||||
scheduleSaveSettingsIID = null;
|
scheduleSaveSettingsIID = null;
|
||||||
saveSettings(comp);
|
await saveSettings(comp);
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const saveSettings = (comp: ConfigScreenComponent) => {
|
export const saveSettings = async (comp: ConfigScreenComponent) => {
|
||||||
const savedSettingKeys = comp.state.changedSettingKeys.slice();
|
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) {
|
for (const key in comp.state.settings) {
|
||||||
if (!comp.state.settings.hasOwnProperty(key)) continue;
|
if (!comp.state.settings.hasOwnProperty(key)) continue;
|
||||||
if (comp.state.changedSettingKeys.indexOf(key) < 0) continue;
|
if (comp.state.changedSettingKeys.indexOf(key) < 0) continue;
|
||||||
@ -150,6 +157,8 @@ export const saveSettings = (comp: ConfigScreenComponent) => {
|
|||||||
comp.setState({ changedSettingKeys: [] });
|
comp.setState({ changedSettingKeys: [] });
|
||||||
|
|
||||||
onSettingsSaved({ savedSettingKeys });
|
onSettingsSaved({ savedSettingKeys });
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const settingsToComponents = (comp: ConfigScreenComponent, device: AppType, settings: any) => {
|
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