Merge pull request #417 from standardnotes/004-actions
refactor: actions menu
This commit is contained in:
@@ -2,19 +2,37 @@ import { WebApplication } from '@/ui_models/application';
|
|||||||
import { WebDirective } from './../../types';
|
import { WebDirective } from './../../types';
|
||||||
import template from '%/directives/actions-menu.pug';
|
import template from '%/directives/actions-menu.pug';
|
||||||
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
||||||
import { SNItem, Action, SNActionsExtension } from '@node_modules/snjs/dist/@types';
|
import { SNItem, Action, SNActionsExtension } from 'snjs/dist/@types';
|
||||||
import { ActionResponse } from '@node_modules/snjs/dist/@types/services/actions_service';
|
import { ActionResponse } from 'snjs/dist/@types/services/actions_service';
|
||||||
|
import { ActionsExtensionMutator } from 'snjs/dist/@types/models/app/extension';
|
||||||
|
|
||||||
type ActionsMenuScope = {
|
type ActionsMenuScope = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
item: SNItem
|
item: SNItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ActionSubRow = {
|
||||||
|
onClick: () => void
|
||||||
|
label: string
|
||||||
|
subtitle: string
|
||||||
|
spinnerClass: string | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateActionParams = {
|
||||||
|
running?: boolean
|
||||||
|
error?: boolean
|
||||||
|
subrows?: ActionSubRow[]
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateExtensionParams = {
|
||||||
|
hidden?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
|
class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
|
||||||
|
|
||||||
application!: WebApplication
|
application!: WebApplication
|
||||||
item!: SNItem
|
item!: SNItem
|
||||||
public loadingState: Partial<Record<string, boolean>> = {}
|
public loadingExtensions: boolean = true
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
@@ -35,32 +53,32 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async loadExtensions() {
|
async loadExtensions() {
|
||||||
const extensions = this.application.actionsManager!.getExtensions().sort((a, b) => {
|
const actionExtensions = this.application.actionsManager!.getExtensions().sort((a, b) => {
|
||||||
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||||
});
|
});
|
||||||
for (const extension of extensions) {
|
const extensionsForItem = await Promise.all(actionExtensions.map((extension) => {
|
||||||
this.loadingState[extension.uuid] = true;
|
return this.application.actionsManager!.loadExtensionInContextOfItem(
|
||||||
await this.application.actionsManager!.loadExtensionInContextOfItem(
|
|
||||||
extension,
|
extension,
|
||||||
this.props.item
|
this.props.item
|
||||||
);
|
);
|
||||||
this.loadingState[extension.uuid] = false;
|
}));
|
||||||
|
if (extensionsForItem.length == 0) {
|
||||||
|
this.loadingExtensions = false;
|
||||||
}
|
}
|
||||||
this.setState({
|
await this.setState({
|
||||||
extensions: extensions
|
extensions: extensionsForItem
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeAction(action: Action, extension: SNActionsExtension) {
|
async executeAction(action: Action, extension: SNActionsExtension) {
|
||||||
if (action.verb === 'nested') {
|
if (action.verb === 'nested') {
|
||||||
if (!action.subrows) {
|
if (!action.subrows) {
|
||||||
action.subrows = this.subRowsForAction(action, extension);
|
const subrows = this.subRowsForAction(action, extension);
|
||||||
} else {
|
await this.updateAction(action, extension, { subrows });
|
||||||
action.subrows = undefined;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
action.running = true;
|
await this.updateAction(action, extension, { running: true });
|
||||||
const response = await this.application.actionsManager!.runAction(
|
const response = await this.application.actionsManager!.runAction(
|
||||||
action,
|
action,
|
||||||
this.props.item,
|
this.props.item,
|
||||||
@@ -69,18 +87,13 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (action.error) {
|
if (response.error) {
|
||||||
|
await this.updateAction(action, extension, { error: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
action.running = false;
|
await this.updateAction(action, extension, { running: false });
|
||||||
this.handleActionResponse(action, response);
|
this.handleActionResponse(action, response);
|
||||||
await this.application.actionsManager!.loadExtensionInContextOfItem(
|
await this.reloadExtension(extension);
|
||||||
extension,
|
|
||||||
this.props.item
|
|
||||||
);
|
|
||||||
this.setState({
|
|
||||||
extensions: this.state.extensions
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleActionResponse(action: Action, result: ActionResponse) {
|
handleActionResponse(action: Action, result: ActionResponse) {
|
||||||
@@ -95,7 +108,7 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subRowsForAction(parentAction: Action, extension: SNActionsExtension) {
|
private subRowsForAction(parentAction: Action, extension: SNActionsExtension): ActionSubRow[] | undefined {
|
||||||
if (!parentAction.subactions) {
|
if (!parentAction.subactions) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -110,6 +123,63 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async updateAction(
|
||||||
|
action: Action,
|
||||||
|
extension: SNActionsExtension,
|
||||||
|
params: UpdateActionParams
|
||||||
|
) {
|
||||||
|
const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => {
|
||||||
|
const extensionMutator = mutator as ActionsExtensionMutator;
|
||||||
|
extensionMutator.actions = extension!.actions.map((act) => {
|
||||||
|
if (act && params && act.verb === action.verb && act.url === action.url) {
|
||||||
|
return {
|
||||||
|
...action,
|
||||||
|
running: params?.running,
|
||||||
|
error: params?.error,
|
||||||
|
subrows: params?.subrows || act?.subrows,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return act;
|
||||||
|
});
|
||||||
|
}) as SNActionsExtension;
|
||||||
|
await this.updateExtension(updatedExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateExtension(
|
||||||
|
extension: SNActionsExtension,
|
||||||
|
params?: UpdateExtensionParams
|
||||||
|
) {
|
||||||
|
const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => {
|
||||||
|
const extensionMutator = mutator as ActionsExtensionMutator;
|
||||||
|
extensionMutator.hidden = params && params.hidden;
|
||||||
|
}) as SNActionsExtension;
|
||||||
|
const extensions = this.state.extensions.map((ext: SNActionsExtension) => {
|
||||||
|
if (extension.uuid === ext.uuid) {
|
||||||
|
return updatedExtension;
|
||||||
|
}
|
||||||
|
return ext;
|
||||||
|
});
|
||||||
|
await this.setState({
|
||||||
|
extensions: extensions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async reloadExtension(extension: SNActionsExtension) {
|
||||||
|
const extensionInContext = await this.application.actionsManager!.loadExtensionInContextOfItem(
|
||||||
|
extension,
|
||||||
|
this.props.item
|
||||||
|
);
|
||||||
|
const extensions = this.state.extensions.map((ext: SNActionsExtension) => {
|
||||||
|
if (extension.uuid === ext.uuid) {
|
||||||
|
return extensionInContext;
|
||||||
|
}
|
||||||
|
return ext;
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
extensions: extensions
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ActionsMenu extends WebDirective {
|
export class ActionsMenu extends WebDirective {
|
||||||
|
|||||||
@@ -7,19 +7,24 @@
|
|||||||
target='blank'
|
target='blank'
|
||||||
)
|
)
|
||||||
menu-row(label="'Download Actions'")
|
menu-row(label="'Download Actions'")
|
||||||
div(ng-repeat='extension in self.state.extensions track by extension.uuid')
|
div(ng-if='self.loadingExtensions')
|
||||||
|
.sk-menu-panel-header
|
||||||
|
.sk-menu-panel-column
|
||||||
|
.sk-menu-panel-header-title Loading...
|
||||||
|
.sk-spinner.small.loading
|
||||||
|
div(ng-repeat='extension in self.state.extensions track by extension.uuid; self.loadingExtensions = false')
|
||||||
.sk-menu-panel-header(
|
.sk-menu-panel-header(
|
||||||
ng-click='extension.hide = !extension.hide; $event.stopPropagation();'
|
ng-click='self.updateExtension(extension, { hidden: !extension.hidden }); $event.stopPropagation();'
|
||||||
)
|
)
|
||||||
.sk-menu-panel-column
|
.sk-menu-panel-column
|
||||||
.sk-menu-panel-header-title {{extension.name}}
|
.sk-menu-panel-header-title {{extension.name}}
|
||||||
.sk-spinner.small.loading(ng-if='self.loadingState[extension.uuid]')
|
div(ng-if='extension.hidden') …
|
||||||
div(ng-if='extension.hide') …
|
|
||||||
menu-row(
|
menu-row(
|
||||||
action='self.executeAction(action, extension);',
|
action='self.executeAction(action, extension)',
|
||||||
label='action.label',
|
label='action.label',
|
||||||
ng-if='!extension.hide',
|
ng-if='!extension.hidden',
|
||||||
ng-repeat='action in extension.actionsWithContextForItem(self.props.item)',
|
ng-repeat='action in extension.actionsWithContextForItem(self.props.item) track by $index',
|
||||||
|
disabled='action.running'
|
||||||
spinner-class="action.running ? 'info' : null",
|
spinner-class="action.running ? 'info' : null",
|
||||||
sub-rows='action.subrows',
|
sub-rows='action.subrows',
|
||||||
subtitle='action.desc'
|
subtitle='action.desc'
|
||||||
|
|||||||
Reference in New Issue
Block a user