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.js
|
||||
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.js
|
||||
packages/lib/services/commands/MenuUtils.js.map
|
||||
|
@ -76,7 +76,7 @@ module.exports = {
|
||||
|
||||
// Warn only for now because fixing everything would take too much
|
||||
// refactoring, but new code should try to stick to it.
|
||||
'complexity': ['warn', { max: 10 }],
|
||||
// 'complexity': ['warn', { max: 10 }],
|
||||
|
||||
// Checks rules of Hooks
|
||||
'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.js
|
||||
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.js
|
||||
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: {
|
||||
label: _('&Note'),
|
||||
submenu: [
|
||||
menuItemDic.toggleExternalEditing,
|
||||
menuItemDic.setTags,
|
||||
menuItemDic.showShareNoteDialog,
|
||||
separator(),
|
||||
menuItemDic.showNoteContentProperties,
|
||||
],
|
||||
@ -818,6 +825,7 @@ function useMenu(props: Props) {
|
||||
rootMenus.edit,
|
||||
rootMenus.view,
|
||||
rootMenus.go,
|
||||
rootMenus.folder,
|
||||
rootMenus.note,
|
||||
rootMenus.tools,
|
||||
rootMenus.help,
|
||||
|
@ -45,5 +45,7 @@ export default function() {
|
||||
'editor.swapLineUp',
|
||||
'editor.swapLineDown',
|
||||
'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 {
|
||||
|
||||
private expression_: string;
|
||||
private expression_: AdvancedExpression;
|
||||
private validate_: boolean;
|
||||
private rules_: ContextKeyExpression = null;
|
||||
private ruleCache_: Record<string, ContextKeyExpression> = {};
|
||||
|
||||
constructor(expression: string, validate: boolean) {
|
||||
this.expression_ = expression;
|
||||
public constructor(expression: string, validate: boolean = true) {
|
||||
this.expression_ = parseAdvancedExpression(expression);
|
||||
this.validate_ = validate;
|
||||
}
|
||||
|
||||
private createContext(ctx: any) {
|
||||
private createContext(ctx: any): IContext {
|
||||
return {
|
||||
getValue: (key: string) => {
|
||||
return ctx[key];
|
||||
@ -19,21 +73,28 @@ export default class WhenClause {
|
||||
};
|
||||
}
|
||||
|
||||
private get rules(): ContextKeyExpression {
|
||||
if (!this.rules_) {
|
||||
this.rules_ = ContextKeyExpr.deserialize(this.expression_);
|
||||
}
|
||||
|
||||
return this.rules_;
|
||||
private rules(exp: string): ContextKeyExpression {
|
||||
if (this.ruleCache_[exp]) return this.ruleCache_[exp];
|
||||
this.ruleCache_[exp] = ContextKeyExpr.deserialize(exp);
|
||||
return this.ruleCache_[exp];
|
||||
}
|
||||
|
||||
public evaluate(context: any): boolean {
|
||||
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) {
|
||||
const keys = this.rules.keys();
|
||||
const keys = this.rules(this.expression_.original.replace(/[()]/g, ' ')).keys();
|
||||
for (const key of keys) {
|
||||
if (!(key in context)) throw new Error(`No such key: ${key}`);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ export interface WhenClauseContext {
|
||||
noteIsHtml: boolean;
|
||||
folderIsShareRootAndOwnedByUser: boolean;
|
||||
folderIsShared: boolean;
|
||||
joplinServerConnected: boolean;
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
return {
|
||||
@ -75,5 +76,7 @@ export default function stateToWhenClauseContext(state: State, options: WhenClau
|
||||
// Current context folder
|
||||
folderIsShareRootAndOwnedByUser: commandFolder ? isRootSharedFolder(commandFolder) && isSharedFolderOwner(state, commandFolder.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>;
|
||||
|
||||
/**
|
||||
* Defines whether the command should be enabled or disabled, which in turns affects
|
||||
* the enabled state of any associated button or menu item.
|
||||
* Defines whether the command should be enabled or disabled, which in turns
|
||||
* 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
|
||||
* `true` or `false`. It supports the following operators:
|
||||
* The condition should be expressed as a "when-clause" (as in Visual Studio
|
||||
* Code). It's a simple boolean expression that evaluates to `true` or
|
||||
* `false`. It supports the following operators:
|
||||
*
|
||||
* Operator | Symbol | Example
|
||||
* -- | -- | --
|
||||
@ -40,7 +41,17 @@ export interface Command {
|
||||
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
|
||||
* 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.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user