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:
parent
fe4900d254
commit
f5f05e6cc5
@ -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
3
.gitignore
vendored
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
30
packages/lib/SyncTargetNone.ts
Normal file
30
packages/lib/SyncTargetNone.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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({
|
||||
|
@ -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({
|
||||
|
@ -319,7 +319,7 @@ class Setting extends BaseModel {
|
||||
},
|
||||
|
||||
'sync.target': {
|
||||
value: SyncTargetRegistry.nameToId('dropbox'),
|
||||
value: 0,
|
||||
type: SettingItemType.Int,
|
||||
isEnum: true,
|
||||
public: true,
|
||||
|
@ -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();
|
||||
|
@ -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 });
|
||||
|
Loading…
x
Reference in New Issue
Block a user