mirror of
https://github.com/laurent22/joplin.git
synced 2025-05-13 21:46:37 +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.d.ts
|
||||||
packages/lib/SyncTargetJoplinServer.js
|
packages/lib/SyncTargetJoplinServer.js
|
||||||
packages/lib/SyncTargetJoplinServer.js.map
|
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.d.ts
|
||||||
packages/lib/SyncTargetOneDrive.js
|
packages/lib/SyncTargetOneDrive.js
|
||||||
packages/lib/SyncTargetOneDrive.js.map
|
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.d.ts
|
||||||
packages/lib/SyncTargetJoplinServer.js
|
packages/lib/SyncTargetJoplinServer.js
|
||||||
packages/lib/SyncTargetJoplinServer.js.map
|
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.d.ts
|
||||||
packages/lib/SyncTargetOneDrive.js
|
packages/lib/SyncTargetOneDrive.js
|
||||||
packages/lib/SyncTargetOneDrive.js.map
|
packages/lib/SyncTargetOneDrive.js.map
|
||||||
|
@ -27,7 +27,9 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
return { header: null };
|
return { header: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
private componentsY_: Record<string, number> = {};
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
this.styles_ = {};
|
this.styles_ = {};
|
||||||
|
|
||||||
@ -37,6 +39,8 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
profileExportPath: '',
|
profileExportPath: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.scrollViewRef_ = React.createRef();
|
||||||
|
|
||||||
shared.init(this);
|
shared.init(this);
|
||||||
|
|
||||||
this.checkSyncConfig_ = async () => {
|
this.checkSyncConfig_ = async () => {
|
||||||
@ -264,10 +268,39 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
return this.styles_[themeId];
|
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) {
|
renderHeader(key: string, title: string) {
|
||||||
const theme = themeStyle(this.props.themeId);
|
const theme = themeStyle(this.props.themeId);
|
||||||
return (
|
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>
|
<Text style={theme.headerStyle}>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@ -335,7 +368,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
if (!settingComps.length) return null;
|
if (!settingComps.length) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View key={key}>
|
<View key={key} onLayout={(event: any) => this.onSectionLayout(key, event)}>
|
||||||
{this.renderHeader(section.name, Setting.sectionNameToLabel(section.name))}
|
{this.renderHeader(section.name, Setting.sectionNameToLabel(section.name))}
|
||||||
<View>{settingComps}</View>
|
<View>{settingComps}</View>
|
||||||
</View>
|
</View>
|
||||||
@ -602,7 +635,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
return (
|
return (
|
||||||
<View style={this.rootStyle(this.props.themeId).root}>
|
<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} />
|
<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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
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 {
|
export interface SyncTargetInfo {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@ -8,25 +10,47 @@ export interface SyncTargetInfo {
|
|||||||
classRef: any;
|
classRef: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const syncTargetOrder = [
|
||||||
|
// 'joplinCloud',
|
||||||
|
// 'dropbox',
|
||||||
|
// 'onedrive',
|
||||||
|
// ];
|
||||||
|
|
||||||
export default class SyncTargetRegistry {
|
export default class SyncTargetRegistry {
|
||||||
|
|
||||||
private static reg_: Record<number, SyncTargetInfo> = {};
|
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) {
|
public static classById(syncTargetId: number) {
|
||||||
const info = SyncTargetRegistry.reg_[syncTargetId];
|
const info = SyncTargetRegistry.reg[syncTargetId];
|
||||||
if (!info) throw new Error(`Invalid id: ${syncTargetId}`);
|
if (!info) throw new Error(`Invalid id: ${syncTargetId}`);
|
||||||
return info.classRef;
|
return info.classRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static infoByName(name: string): SyncTargetInfo {
|
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;
|
if (info.name === name) return info;
|
||||||
}
|
}
|
||||||
throw new Error(`Unknown name: ${name}`);
|
throw new Error(`Unknown name: ${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static addClass(SyncTargetClass: any) {
|
public static addClass(SyncTargetClass: any) {
|
||||||
this.reg_[SyncTargetClass.id()] = {
|
this.reg[SyncTargetClass.id()] = {
|
||||||
id: SyncTargetClass.id(),
|
id: SyncTargetClass.id(),
|
||||||
name: SyncTargetClass.targetName(),
|
name: SyncTargetClass.targetName(),
|
||||||
label: SyncTargetClass.label(),
|
label: SyncTargetClass.label(),
|
||||||
@ -38,21 +62,21 @@ export default class SyncTargetRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static allIds() {
|
public static allIds() {
|
||||||
return Object.keys(this.reg_);
|
return Object.keys(this.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static nameToId(name: string) {
|
public static nameToId(name: string) {
|
||||||
for (const n in this.reg_) {
|
for (const n in this.reg) {
|
||||||
if (!this.reg_.hasOwnProperty(n)) continue;
|
if (!this.reg.hasOwnProperty(n)) continue;
|
||||||
if (this.reg_[n].name === name) return this.reg_[n].id;
|
if (this.reg[n].name === name) return this.reg[n].id;
|
||||||
}
|
}
|
||||||
throw new Error(`Name not found: ${name}. Was the sync target registered?`);
|
throw new Error(`Name not found: ${name}. Was the sync target registered?`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static idToMetadata(id: number) {
|
public static idToMetadata(id: number) {
|
||||||
for (const n in this.reg_) {
|
for (const n in this.reg) {
|
||||||
if (!this.reg_.hasOwnProperty(n)) continue;
|
if (!this.reg.hasOwnProperty(n)) continue;
|
||||||
if (this.reg_[n].id === id) return this.reg_[n];
|
if (this.reg[n].id === id) return this.reg[n];
|
||||||
}
|
}
|
||||||
throw new Error(`ID not found: ${id}`);
|
throw new Error(`ID not found: ${id}`);
|
||||||
}
|
}
|
||||||
@ -63,14 +87,26 @@ export default class SyncTargetRegistry {
|
|||||||
|
|
||||||
public static idAndLabelPlainObject(os: string) {
|
public static idAndLabelPlainObject(os: string) {
|
||||||
const output: Record<string, string> = {};
|
const output: Record<string, string> = {};
|
||||||
for (const n in this.reg_) {
|
for (const n in this.reg) {
|
||||||
if (!this.reg_.hasOwnProperty(n)) continue;
|
if (!this.reg.hasOwnProperty(n)) continue;
|
||||||
const info = this.reg_[n];
|
const info = this.reg[n];
|
||||||
if (info.classRef.unsupportedPlatforms().indexOf(os) >= 0) {
|
if (info.classRef.unsupportedPlatforms().indexOf(os) >= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
output[n] = info.label;
|
output[n] = info.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
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 { utils, CommandRuntime, CommandDeclaration, CommandContext } from '../services/CommandService';
|
||||||
import { _ } from '../locale';
|
import { _ } from '../locale';
|
||||||
import { reg } from '../registry';
|
import { reg } from '../registry';
|
||||||
|
import Setting from '../models/Setting';
|
||||||
|
|
||||||
export const declaration: CommandDeclaration = {
|
export const declaration: CommandDeclaration = {
|
||||||
name: 'synchronize',
|
name: 'synchronize',
|
||||||
@ -17,6 +18,14 @@ export const runtime = (): CommandRuntime => {
|
|||||||
|
|
||||||
const action = syncStarted ? 'cancel' : 'start';
|
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 (!(await reg.syncTarget().isAuthenticated())) {
|
||||||
if (reg.syncTarget().authRouteName()) {
|
if (reg.syncTarget().authRouteName()) {
|
||||||
utils.store.dispatch({
|
utils.store.dispatch({
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const Folder = require('../../models/Folder').default;
|
const Folder = require('../../models/Folder').default;
|
||||||
|
const Setting = require('../../models/Setting').default;
|
||||||
const BaseModel = require('../../BaseModel').default;
|
const BaseModel = require('../../BaseModel').default;
|
||||||
|
|
||||||
const shared = {};
|
const shared = {};
|
||||||
@ -81,6 +82,20 @@ shared.synchronize_press = async function(comp) {
|
|||||||
|
|
||||||
const action = comp.props.syncStarted ? 'cancel' : 'start';
|
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 (!(await reg.syncTarget().isAuthenticated())) {
|
||||||
if (reg.syncTarget().authRouteName()) {
|
if (reg.syncTarget().authRouteName()) {
|
||||||
comp.props.dispatch({
|
comp.props.dispatch({
|
||||||
|
@ -319,7 +319,7 @@ class Setting extends BaseModel {
|
|||||||
},
|
},
|
||||||
|
|
||||||
'sync.target': {
|
'sync.target': {
|
||||||
value: SyncTargetRegistry.nameToId('dropbox'),
|
value: 0,
|
||||||
type: SettingItemType.Int,
|
type: SettingItemType.Int,
|
||||||
isEnum: true,
|
isEnum: true,
|
||||||
public: true,
|
public: true,
|
||||||
|
@ -71,6 +71,11 @@ class Registry {
|
|||||||
// sure it gets synced. So we wait for the current sync operation to
|
// 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.
|
// finish (if one is running), then we trigger a sync just after.
|
||||||
waitForSyncFinishedThenSync = async () => {
|
waitForSyncFinishedThenSync = async () => {
|
||||||
|
if (!Setting.value('sync.target')) {
|
||||||
|
this.logger().info('waitForSyncFinishedThenSync - cancelling because no sync target is selected.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.waitForReSyncCalls_.push(true);
|
this.waitForReSyncCalls_.push(true);
|
||||||
try {
|
try {
|
||||||
const synchronizer = await this.syncTarget().synchronizer();
|
const synchronizer = await this.syncTarget().synchronizer();
|
||||||
@ -119,6 +124,12 @@ class Registry {
|
|||||||
|
|
||||||
const syncTargetId = Setting.value('sync.target');
|
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())) {
|
if (!(await this.syncTarget(syncTargetId).isAuthenticated())) {
|
||||||
this.logger().info('Synchroniser is missing credentials - manual sync required to authenticate.');
|
this.logger().info('Synchroniser is missing credentials - manual sync required to authenticate.');
|
||||||
promiseResolve();
|
promiseResolve();
|
||||||
|
@ -161,6 +161,13 @@ export default class ResourceFetcher extends BaseService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileApi = await this.fileApi();
|
||||||
|
|
||||||
|
if (!fileApi) {
|
||||||
|
this.logger().debug('ResourceFetcher: Disabled because fileApi is not set');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.fetchingItems_[resourceId] = resource;
|
this.fetchingItems_[resourceId] = resource;
|
||||||
|
|
||||||
const localResourceContentPath = Resource.fullPath(resource, !!resource.encryption_blob_encrypted);
|
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 });
|
await Resource.setLocalState(resource, { fetch_status: Resource.FETCH_STATUS_STARTED });
|
||||||
|
|
||||||
const fileApi = await this.fileApi();
|
|
||||||
|
|
||||||
this.logger().debug(`ResourceFetcher: Downloading resource: ${resource.id}`);
|
this.logger().debug(`ResourceFetcher: Downloading resource: ${resource.id}`);
|
||||||
|
|
||||||
this.eventEmitter_.emit('downloadStarted', { id: resource.id });
|
this.eventEmitter_.emit('downloadStarted', { id: resource.id });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user