1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-03-23 21:09:30 +02:00

All: Added "None" sync target to allow disabling synchronisation

This commit is contained in:
Laurent Cozic 2021-08-16 16:18:32 +01:00
parent fe4900d254
commit f5f05e6cc5
10 changed files with 165 additions and 20 deletions

View File

@ -870,6 +870,9 @@ packages/lib/SyncTargetJoplinCloud.js.map
packages/lib/SyncTargetJoplinServer.d.ts
packages/lib/SyncTargetJoplinServer.js
packages/lib/SyncTargetJoplinServer.js.map
packages/lib/SyncTargetNone.d.ts
packages/lib/SyncTargetNone.js
packages/lib/SyncTargetNone.js.map
packages/lib/SyncTargetOneDrive.d.ts
packages/lib/SyncTargetOneDrive.js
packages/lib/SyncTargetOneDrive.js.map

3
.gitignore vendored
View File

@ -855,6 +855,9 @@ packages/lib/SyncTargetJoplinCloud.js.map
packages/lib/SyncTargetJoplinServer.d.ts
packages/lib/SyncTargetJoplinServer.js
packages/lib/SyncTargetJoplinServer.js.map
packages/lib/SyncTargetNone.d.ts
packages/lib/SyncTargetNone.js
packages/lib/SyncTargetNone.js.map
packages/lib/SyncTargetOneDrive.d.ts
packages/lib/SyncTargetOneDrive.js
packages/lib/SyncTargetOneDrive.js.map

View File

@ -27,7 +27,9 @@ class ConfigScreenComponent extends BaseScreenComponent {
return { header: null };
}
constructor() {
private componentsY_: Record<string, number> = {};
public constructor() {
super();
this.styles_ = {};
@ -37,6 +39,8 @@ class ConfigScreenComponent extends BaseScreenComponent {
profileExportPath: '',
};
this.scrollViewRef_ = React.createRef();
shared.init(this);
this.checkSyncConfig_ = async () => {
@ -264,10 +268,39 @@ class ConfigScreenComponent extends BaseScreenComponent {
return this.styles_[themeId];
}
private onHeaderLayout(key: string, event: any) {
const layout = event.nativeEvent.layout;
this.componentsY_[`header_${key}`] = layout.y;
}
private onSectionLayout(key: string, event: any) {
const layout = event.nativeEvent.layout;
this.componentsY_[`section_${key}`] = layout.y;
}
private componentY(key: string): number {
if ((`section_${key}`) in this.componentsY_) return this.componentsY_[`section_${key}`];
if ((`header_${key}`) in this.componentsY_) return this.componentsY_[`header_${key}`];
console.error(`ConfigScreen: Could not find key to scroll to: ${key}`);
return 0;
}
public componentDidMount() {
if (this.props.navigation.state.sectionName) {
setTimeout(() => {
this.scrollViewRef_.current.scrollTo({
x: 0,
y: this.componentY(this.props.navigation.state.sectionName),
animated: true,
});
}, 200);
}
}
renderHeader(key: string, title: string) {
const theme = themeStyle(this.props.themeId);
return (
<View key={key} style={this.styles().headerWrapperStyle}>
<View key={key} style={this.styles().headerWrapperStyle} onLayout={(event: any) => this.onHeaderLayout(key, event)}>
<Text style={theme.headerStyle}>{title}</Text>
</View>
);
@ -335,7 +368,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
if (!settingComps.length) return null;
return (
<View key={key}>
<View key={key} onLayout={(event: any) => this.onSectionLayout(key, event)}>
{this.renderHeader(section.name, Setting.sectionNameToLabel(section.name))}
<View>{settingComps}</View>
</View>
@ -602,7 +635,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
return (
<View style={this.rootStyle(this.props.themeId).root}>
<ScreenHeader title={_('Configuration')} showSaveButton={true} showSearchButton={false} showSideMenuButton={false} saveButtonDisabled={!this.state.changedSettingKeys.length} onSaveButtonPress={this.saveButton_press} />
<ScrollView>{settingComps}</ScrollView>
<ScrollView ref={this.scrollViewRef_}>{settingComps}</ScrollView>
</View>
);
}

View File

@ -0,0 +1,30 @@
import { _ } from './locale.js';
import BaseSyncTarget from './BaseSyncTarget';
import { FileApi } from './file-api';
export default class SyncTargetNone extends BaseSyncTarget {
public static id() {
return 0;
}
public static targetName() {
return 'none';
}
public static label() {
return _('(None)');
}
public async fileApi(): Promise<FileApi> {
return null;
}
protected async initFileApi() {
}
protected async initSynchronizer() {
return null as any;
}
}

View File

@ -1,3 +1,5 @@
import SyncTargetNone from './SyncTargetNone';
export interface SyncTargetInfo {
id: number;
name: string;
@ -8,25 +10,47 @@ export interface SyncTargetInfo {
classRef: any;
}
// const syncTargetOrder = [
// 'joplinCloud',
// 'dropbox',
// 'onedrive',
// ];
export default class SyncTargetRegistry {
private static reg_: Record<number, SyncTargetInfo> = {};
private static get reg() {
if (!this.reg_[0]) {
this.reg_[0] = {
id: 0,
name: SyncTargetNone.targetName(),
label: SyncTargetNone.label(),
classRef: SyncTargetNone,
description: SyncTargetNone.description(),
supportsSelfHosted: false,
supportsConfigCheck: false,
};
}
return this.reg_;
}
public static classById(syncTargetId: number) {
const info = SyncTargetRegistry.reg_[syncTargetId];
const info = SyncTargetRegistry.reg[syncTargetId];
if (!info) throw new Error(`Invalid id: ${syncTargetId}`);
return info.classRef;
}
public static infoByName(name: string): SyncTargetInfo {
for (const [, info] of Object.entries(this.reg_)) {
for (const [, info] of Object.entries(this.reg)) {
if (info.name === name) return info;
}
throw new Error(`Unknown name: ${name}`);
}
public static addClass(SyncTargetClass: any) {
this.reg_[SyncTargetClass.id()] = {
this.reg[SyncTargetClass.id()] = {
id: SyncTargetClass.id(),
name: SyncTargetClass.targetName(),
label: SyncTargetClass.label(),
@ -38,21 +62,21 @@ export default class SyncTargetRegistry {
}
public static allIds() {
return Object.keys(this.reg_);
return Object.keys(this.reg);
}
public static nameToId(name: string) {
for (const n in this.reg_) {
if (!this.reg_.hasOwnProperty(n)) continue;
if (this.reg_[n].name === name) return this.reg_[n].id;
for (const n in this.reg) {
if (!this.reg.hasOwnProperty(n)) continue;
if (this.reg[n].name === name) return this.reg[n].id;
}
throw new Error(`Name not found: ${name}. Was the sync target registered?`);
}
public static idToMetadata(id: number) {
for (const n in this.reg_) {
if (!this.reg_.hasOwnProperty(n)) continue;
if (this.reg_[n].id === id) return this.reg_[n];
for (const n in this.reg) {
if (!this.reg.hasOwnProperty(n)) continue;
if (this.reg[n].id === id) return this.reg[n];
}
throw new Error(`ID not found: ${id}`);
}
@ -63,14 +87,26 @@ export default class SyncTargetRegistry {
public static idAndLabelPlainObject(os: string) {
const output: Record<string, string> = {};
for (const n in this.reg_) {
if (!this.reg_.hasOwnProperty(n)) continue;
const info = this.reg_[n];
for (const n in this.reg) {
if (!this.reg.hasOwnProperty(n)) continue;
const info = this.reg[n];
if (info.classRef.unsupportedPlatforms().indexOf(os) >= 0) {
continue;
}
output[n] = info.label;
}
return output;
// const sorted: Record<string, string> = {};
// for (const o of syncTargetOrder) {
// sorted[o] = output[o];
// }
// for (const [name, value] of Object.entries(output)) {
// if (!sorted[name]) sorted[name] = value;
// }
// return sorted;
}
}

View File

@ -1,6 +1,7 @@
import { utils, CommandRuntime, CommandDeclaration, CommandContext } from '../services/CommandService';
import { _ } from '../locale';
import { reg } from '../registry';
import Setting from '../models/Setting';
export const declaration: CommandDeclaration = {
name: 'synchronize',
@ -17,6 +18,14 @@ export const runtime = (): CommandRuntime => {
const action = syncStarted ? 'cancel' : 'start';
if (!Setting.value('sync.target')) {
context.dispatch({
type: 'DIALOG_OPEN',
name: 'syncWizard',
});
return 'init';
}
if (!(await reg.syncTarget().isAuthenticated())) {
if (reg.syncTarget().authRouteName()) {
utils.store.dispatch({

View File

@ -1,4 +1,5 @@
const Folder = require('../../models/Folder').default;
const Setting = require('../../models/Setting').default;
const BaseModel = require('../../BaseModel').default;
const shared = {};
@ -81,6 +82,20 @@ shared.synchronize_press = async function(comp) {
const action = comp.props.syncStarted ? 'cancel' : 'start';
if (!Setting.value('sync.target')) {
comp.props.dispatch({
type: 'SIDE_MENU_CLOSE',
});
comp.props.dispatch({
type: 'NAV_GO',
routeName: 'Config',
sectionName: 'sync',
});
return 'init';
}
if (!(await reg.syncTarget().isAuthenticated())) {
if (reg.syncTarget().authRouteName()) {
comp.props.dispatch({

View File

@ -319,7 +319,7 @@ class Setting extends BaseModel {
},
'sync.target': {
value: SyncTargetRegistry.nameToId('dropbox'),
value: 0,
type: SettingItemType.Int,
isEnum: true,
public: true,

View File

@ -71,6 +71,11 @@ class Registry {
// sure it gets synced. So we wait for the current sync operation to
// finish (if one is running), then we trigger a sync just after.
waitForSyncFinishedThenSync = async () => {
if (!Setting.value('sync.target')) {
this.logger().info('waitForSyncFinishedThenSync - cancelling because no sync target is selected.');
return;
}
this.waitForReSyncCalls_.push(true);
try {
const synchronizer = await this.syncTarget().synchronizer();
@ -119,6 +124,12 @@ class Registry {
const syncTargetId = Setting.value('sync.target');
if (!syncTargetId) {
this.logger().info('Sync cancelled - no sync target is selected.');
promiseResolve();
return;
}
if (!(await this.syncTarget(syncTargetId).isAuthenticated())) {
this.logger().info('Synchroniser is missing credentials - manual sync required to authenticate.');
promiseResolve();

View File

@ -161,6 +161,13 @@ export default class ResourceFetcher extends BaseService {
return;
}
const fileApi = await this.fileApi();
if (!fileApi) {
this.logger().debug('ResourceFetcher: Disabled because fileApi is not set');
return;
}
this.fetchingItems_[resourceId] = resource;
const localResourceContentPath = Resource.fullPath(resource, !!resource.encryption_blob_encrypted);
@ -168,8 +175,6 @@ export default class ResourceFetcher extends BaseService {
await Resource.setLocalState(resource, { fetch_status: Resource.FETCH_STATUS_STARTED });
const fileApi = await this.fileApi();
this.logger().debug(`ResourceFetcher: Downloading resource: ${resource.id}`);
this.eventEmitter_.emit('downloadStarted', { id: resource.id });