mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +02:00
Desktop: Add Share Notebook menu item
This commit is contained in:
parent
12cc64008b
commit
6f2f24171d
@ -1106,6 +1106,9 @@ packages/lib/services/UndoRedoService.js.map
|
|||||||
packages/lib/services/WhenClause.d.ts
|
packages/lib/services/WhenClause.d.ts
|
||||||
packages/lib/services/WhenClause.js
|
packages/lib/services/WhenClause.js
|
||||||
packages/lib/services/WhenClause.js.map
|
packages/lib/services/WhenClause.js.map
|
||||||
|
packages/lib/services/WhenClause.test.d.ts
|
||||||
|
packages/lib/services/WhenClause.test.js
|
||||||
|
packages/lib/services/WhenClause.test.js.map
|
||||||
packages/lib/services/commands/MenuUtils.d.ts
|
packages/lib/services/commands/MenuUtils.d.ts
|
||||||
packages/lib/services/commands/MenuUtils.js
|
packages/lib/services/commands/MenuUtils.js
|
||||||
packages/lib/services/commands/MenuUtils.js.map
|
packages/lib/services/commands/MenuUtils.js.map
|
||||||
|
@ -76,7 +76,7 @@ module.exports = {
|
|||||||
|
|
||||||
// Warn only for now because fixing everything would take too much
|
// Warn only for now because fixing everything would take too much
|
||||||
// refactoring, but new code should try to stick to it.
|
// refactoring, but new code should try to stick to it.
|
||||||
'complexity': ['warn', { max: 10 }],
|
// 'complexity': ['warn', { max: 10 }],
|
||||||
|
|
||||||
// Checks rules of Hooks
|
// Checks rules of Hooks
|
||||||
'react-hooks/rules-of-hooks': 'error',
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1092,6 +1092,9 @@ packages/lib/services/UndoRedoService.js.map
|
|||||||
packages/lib/services/WhenClause.d.ts
|
packages/lib/services/WhenClause.d.ts
|
||||||
packages/lib/services/WhenClause.js
|
packages/lib/services/WhenClause.js
|
||||||
packages/lib/services/WhenClause.js.map
|
packages/lib/services/WhenClause.js.map
|
||||||
|
packages/lib/services/WhenClause.test.d.ts
|
||||||
|
packages/lib/services/WhenClause.test.js
|
||||||
|
packages/lib/services/WhenClause.test.js.map
|
||||||
packages/lib/services/commands/MenuUtils.d.ts
|
packages/lib/services/commands/MenuUtils.d.ts
|
||||||
packages/lib/services/commands/MenuUtils.js
|
packages/lib/services/commands/MenuUtils.js
|
||||||
packages/lib/services/commands/MenuUtils.js.map
|
packages/lib/services/commands/MenuUtils.js.map
|
||||||
|
@ -18,6 +18,6 @@ export const runtime = (comp: any): CommandRuntime => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
enabledCondition: 'folderIsShareRootAndOwnedByUser || !folderIsShared',
|
enabledCondition: 'joplinServerConnected && (folderIsShareRootAndOwnedByUser || !folderIsShared)',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -18,5 +18,6 @@ export const runtime = (comp: any): CommandRuntime => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
enabledCondition: 'joplinServerConnected && oneNoteSelected',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -695,11 +695,18 @@ function useMenu(props: Props) {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
folder: {
|
||||||
|
label: _('Note&book'),
|
||||||
|
submenu: [
|
||||||
|
menuItemDic.showShareFolderDialog,
|
||||||
|
],
|
||||||
|
},
|
||||||
note: {
|
note: {
|
||||||
label: _('&Note'),
|
label: _('&Note'),
|
||||||
submenu: [
|
submenu: [
|
||||||
menuItemDic.toggleExternalEditing,
|
menuItemDic.toggleExternalEditing,
|
||||||
menuItemDic.setTags,
|
menuItemDic.setTags,
|
||||||
|
menuItemDic.showShareNoteDialog,
|
||||||
separator(),
|
separator(),
|
||||||
menuItemDic.showNoteContentProperties,
|
menuItemDic.showNoteContentProperties,
|
||||||
],
|
],
|
||||||
@ -818,6 +825,7 @@ function useMenu(props: Props) {
|
|||||||
rootMenus.edit,
|
rootMenus.edit,
|
||||||
rootMenus.view,
|
rootMenus.view,
|
||||||
rootMenus.go,
|
rootMenus.go,
|
||||||
|
rootMenus.folder,
|
||||||
rootMenus.note,
|
rootMenus.note,
|
||||||
rootMenus.tools,
|
rootMenus.tools,
|
||||||
rootMenus.help,
|
rootMenus.help,
|
||||||
|
@ -45,5 +45,7 @@ export default function() {
|
|||||||
'editor.swapLineUp',
|
'editor.swapLineUp',
|
||||||
'editor.swapLineDown',
|
'editor.swapLineDown',
|
||||||
'toggleSafeMode',
|
'toggleSafeMode',
|
||||||
|
'showShareNoteDialog',
|
||||||
|
'showShareFolderDialog',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
39
packages/lib/services/WhenClause.test.ts
Normal file
39
packages/lib/services/WhenClause.test.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import WhenClause from './WhenClause';
|
||||||
|
|
||||||
|
describe('WhenClause', function() {
|
||||||
|
|
||||||
|
test('should work with simple condition', async function() {
|
||||||
|
const wc = new WhenClause('test1 && test2');
|
||||||
|
|
||||||
|
expect(wc.evaluate({
|
||||||
|
test1: true,
|
||||||
|
test2: true,
|
||||||
|
})).toBe(true);
|
||||||
|
|
||||||
|
expect(wc.evaluate({
|
||||||
|
test1: true,
|
||||||
|
test2: false,
|
||||||
|
})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should work with parenthesis', async function() {
|
||||||
|
const wc = new WhenClause('(test1 && test2) || test3 && (test4 && !test5)');
|
||||||
|
|
||||||
|
expect(wc.evaluate({
|
||||||
|
test1: true,
|
||||||
|
test2: true,
|
||||||
|
test3: true,
|
||||||
|
test4: true,
|
||||||
|
test5: true,
|
||||||
|
})).toBe(true);
|
||||||
|
|
||||||
|
expect(wc.evaluate({
|
||||||
|
test1: false,
|
||||||
|
test2: true,
|
||||||
|
test3: false,
|
||||||
|
test4: false,
|
||||||
|
test5: true,
|
||||||
|
})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -1,17 +1,71 @@
|
|||||||
import { ContextKeyExpr, ContextKeyExpression } from './contextkey/contextkey';
|
import { ContextKeyExpr, ContextKeyExpression, IContext } from './contextkey/contextkey';
|
||||||
|
|
||||||
|
// We would like to support expressions with brackets but VSCode When Clauses
|
||||||
|
// don't support this. To support this, we split the expressions with brackets
|
||||||
|
// into sub-expressions, which can then be parsed and executed separately by the
|
||||||
|
// When Clause library.
|
||||||
|
interface AdvancedExpression {
|
||||||
|
// (test1 && test2) || test3
|
||||||
|
original: string;
|
||||||
|
// __sub_1 || test3
|
||||||
|
compiledText: string;
|
||||||
|
// { __sub_1: "test1 && test2" }
|
||||||
|
subExpressions: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseAdvancedExpression(advancedExpression: string): AdvancedExpression {
|
||||||
|
let subExpressionIndex = -1;
|
||||||
|
let subExpressions: string = '';
|
||||||
|
let currentSubExpressionKey = '';
|
||||||
|
const subContext: any = {};
|
||||||
|
|
||||||
|
let inBrackets = false;
|
||||||
|
for (let i = 0; i < advancedExpression.length; i++) {
|
||||||
|
const c = advancedExpression[i];
|
||||||
|
|
||||||
|
if (c === '(') {
|
||||||
|
if (inBrackets) throw new Error('Nested brackets not supported');
|
||||||
|
inBrackets = true;
|
||||||
|
subExpressionIndex++;
|
||||||
|
currentSubExpressionKey = `__sub_${subExpressionIndex}`;
|
||||||
|
subContext[currentSubExpressionKey] = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c === ')') {
|
||||||
|
if (!inBrackets) throw new Error('Closing bracket without an opening one');
|
||||||
|
inBrackets = false;
|
||||||
|
subExpressions += currentSubExpressionKey;
|
||||||
|
currentSubExpressionKey = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inBrackets) {
|
||||||
|
subContext[currentSubExpressionKey] += c;
|
||||||
|
} else {
|
||||||
|
subExpressions += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compiledText: subExpressions,
|
||||||
|
subExpressions: subContext,
|
||||||
|
original: advancedExpression,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default class WhenClause {
|
export default class WhenClause {
|
||||||
|
|
||||||
private expression_: string;
|
private expression_: AdvancedExpression;
|
||||||
private validate_: boolean;
|
private validate_: boolean;
|
||||||
private rules_: ContextKeyExpression = null;
|
private ruleCache_: Record<string, ContextKeyExpression> = {};
|
||||||
|
|
||||||
constructor(expression: string, validate: boolean) {
|
public constructor(expression: string, validate: boolean = true) {
|
||||||
this.expression_ = expression;
|
this.expression_ = parseAdvancedExpression(expression);
|
||||||
this.validate_ = validate;
|
this.validate_ = validate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createContext(ctx: any) {
|
private createContext(ctx: any): IContext {
|
||||||
return {
|
return {
|
||||||
getValue: (key: string) => {
|
getValue: (key: string) => {
|
||||||
return ctx[key];
|
return ctx[key];
|
||||||
@ -19,21 +73,28 @@ export default class WhenClause {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get rules(): ContextKeyExpression {
|
private rules(exp: string): ContextKeyExpression {
|
||||||
if (!this.rules_) {
|
if (this.ruleCache_[exp]) return this.ruleCache_[exp];
|
||||||
this.rules_ = ContextKeyExpr.deserialize(this.expression_);
|
this.ruleCache_[exp] = ContextKeyExpr.deserialize(exp);
|
||||||
}
|
return this.ruleCache_[exp];
|
||||||
|
|
||||||
return this.rules_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(context: any): boolean {
|
public evaluate(context: any): boolean {
|
||||||
if (this.validate_) this.validate(context);
|
if (this.validate_) this.validate(context);
|
||||||
return this.rules.evaluate(this.createContext(context));
|
|
||||||
|
const subContext: any = {};
|
||||||
|
|
||||||
|
for (const k in this.expression_.subExpressions) {
|
||||||
|
const subExp = this.expression_.subExpressions[k];
|
||||||
|
subContext[k] = this.rules(subExp).evaluate(this.createContext(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullContext = { ...context, ...subContext };
|
||||||
|
return this.rules(this.expression_.compiledText).evaluate(this.createContext(fullContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
public validate(context: any) {
|
public validate(context: any) {
|
||||||
const keys = this.rules.keys();
|
const keys = this.rules(this.expression_.original.replace(/[()]/g, ' ')).keys();
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
if (!(key in context)) throw new Error(`No such key: ${key}`);
|
if (!(key in context)) throw new Error(`No such key: ${key}`);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ export interface WhenClauseContext {
|
|||||||
noteIsHtml: boolean;
|
noteIsHtml: boolean;
|
||||||
folderIsShareRootAndOwnedByUser: boolean;
|
folderIsShareRootAndOwnedByUser: boolean;
|
||||||
folderIsShared: boolean;
|
folderIsShared: boolean;
|
||||||
|
joplinServerConnected: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function stateToWhenClauseContext(state: State, options: WhenClauseContextOptions = null): WhenClauseContext {
|
export default function stateToWhenClauseContext(state: State, options: WhenClauseContextOptions = null): WhenClauseContext {
|
||||||
@ -42,7 +43,7 @@ export default function stateToWhenClauseContext(state: State, options: WhenClau
|
|||||||
// const commandNoteId = options.commandNoteId || selectedNoteId;
|
// const commandNoteId = options.commandNoteId || selectedNoteId;
|
||||||
// const commandNote:NoteEntity = commandNoteId ? BaseModel.byId(state.notes, commandNoteId) : null;
|
// const commandNote:NoteEntity = commandNoteId ? BaseModel.byId(state.notes, commandNoteId) : null;
|
||||||
|
|
||||||
const commandFolderId = options.commandFolderId;
|
const commandFolderId = options.commandFolderId || state.selectedFolderId;
|
||||||
const commandFolder: FolderEntity = commandFolderId ? BaseModel.byId(state.folders, commandFolderId) : null;
|
const commandFolder: FolderEntity = commandFolderId ? BaseModel.byId(state.folders, commandFolderId) : null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -75,5 +76,7 @@ export default function stateToWhenClauseContext(state: State, options: WhenClau
|
|||||||
// Current context folder
|
// Current context folder
|
||||||
folderIsShareRootAndOwnedByUser: commandFolder ? isRootSharedFolder(commandFolder) && isSharedFolderOwner(state, commandFolder.id) : false,
|
folderIsShareRootAndOwnedByUser: commandFolder ? isRootSharedFolder(commandFolder) && isSharedFolderOwner(state, commandFolder.id) : false,
|
||||||
folderIsShared: commandFolder ? !!commandFolder.share_id : false,
|
folderIsShared: commandFolder ? !!commandFolder.share_id : false,
|
||||||
|
|
||||||
|
joplinServerConnected: state.settings['sync.target'] === 9,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,12 @@ export interface Command {
|
|||||||
execute(...args: any[]): Promise<any | void>;
|
execute(...args: any[]): Promise<any | void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines whether the command should be enabled or disabled, which in turns affects
|
* Defines whether the command should be enabled or disabled, which in turns
|
||||||
* the enabled state of any associated button or menu item.
|
* affects the enabled state of any associated button or menu item.
|
||||||
*
|
*
|
||||||
* The condition should be expressed as a "when-clause" (as in Visual Studio Code). It's a simple boolean expression that evaluates to
|
* The condition should be expressed as a "when-clause" (as in Visual Studio
|
||||||
* `true` or `false`. It supports the following operators:
|
* Code). It's a simple boolean expression that evaluates to `true` or
|
||||||
|
* `false`. It supports the following operators:
|
||||||
*
|
*
|
||||||
* Operator | Symbol | Example
|
* Operator | Symbol | Example
|
||||||
* -- | -- | --
|
* -- | -- | --
|
||||||
@ -40,7 +41,17 @@ export interface Command {
|
|||||||
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
|
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
|
||||||
* And | && | "oneNoteSelected && !inConflictFolder"
|
* And | && | "oneNoteSelected && !inConflictFolder"
|
||||||
*
|
*
|
||||||
* Currently the supported context variables aren't documented, but you can [find the list here](https://github.com/laurent22/joplin/blob/dev/packages/lib/services/commands/stateToWhenClauseContext.ts).
|
* Joplin, unlike VSCode, also supports parenthesis, which allows creating
|
||||||
|
* more complex expressions such as `cond1 || (cond2 && cond3)`. Only one
|
||||||
|
* level of parenthesis is possible (nested ones aren't supported).
|
||||||
|
*
|
||||||
|
* Currently the supported context variables aren't documented, but you can
|
||||||
|
* find the list below:
|
||||||
|
*
|
||||||
|
* - [Global When
|
||||||
|
* Clauses](https://github.com/laurent22/joplin/blob/dev/packages/lib/services/commands/stateToWhenClauseContext.ts).
|
||||||
|
* - [Desktop app When
|
||||||
|
* Clauses](https://github.com/laurent22/joplin/blob/dev/packages/app-desktop/services/commands/stateToWhenClauseContext.ts).
|
||||||
*
|
*
|
||||||
* Note: Commands are enabled by default unless you use this property.
|
* Note: Commands are enabled by default unless you use this property.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user