From 368eb4c637044e68a40892f38a7a7f25ddc5e2e1 Mon Sep 17 00:00:00 2001 From: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com> Date: Thu, 24 Sep 2020 16:58:33 +0200 Subject: [PATCH] fix: display note history action items without throwing --- .../directives/views/actionsMenu.ts | 105 +++++++++++------- .../templates/directives/actions-menu.pug | 40 +++---- app/assets/templates/directives/menu-row.pug | 14 +-- 3 files changed, 93 insertions(+), 66 deletions(-) diff --git a/app/assets/javascripts/directives/views/actionsMenu.ts b/app/assets/javascripts/directives/views/actionsMenu.ts index 42a7cb5fe..d98a6c049 100644 --- a/app/assets/javascripts/directives/views/actionsMenu.ts +++ b/app/assets/javascripts/directives/views/actionsMenu.ts @@ -18,12 +18,6 @@ type ActionSubRow = { spinnerClass?: string } -type UpdateActionParams = { - running?: boolean - error?: boolean - subrows?: ActionSubRow[] -} - type ExtensionState = { hidden: boolean loading: boolean @@ -33,6 +27,17 @@ type ExtensionState = { type ActionsMenuState = { extensions: SNActionsExtension[] extensionsState: Record + selectedActionId?: number + menu: { + uuid: UuidString, + name: string, + loading: boolean, + error: boolean, + hidden: boolean, + actions: (Action & { + subrows?: ActionSubRow[] + })[] + }[] } class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements ActionsMenuScope { @@ -52,6 +57,7 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti item: this.item }); this.loadExtensions(); + this.rebuildMenu(); }; /** @override */ @@ -69,10 +75,43 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti }); return { extensions, - extensionsState + extensionsState, + menu: [], }; } + rebuildMenu({ + extensions = this.state.extensions, + extensionsState = this.state.extensionsState, + selectedActionId = this.state.selectedActionId, + } = {}) { + return this.setState({ + extensions, + extensionsState, + selectedActionId, + menu: extensions.map(extension => { + const state = extensionsState[extension.uuid]; + return { + uuid: extension.uuid, + name: extension.name, + loading: state?.loading ?? false, + error: state?.error ?? false, + hidden: state?.hidden ?? false, + actions: extension.actionsWithContextForItem(this.item).map(action => { + if (action.id === selectedActionId) { + return { + ...action, + subrows: this.subRowsForAction(action, extension) + } + } else { + return action; + } + }) + }; + }) + }); + } + async loadExtensions() { await Promise.all(this.state.extensions.map(async (extension: SNActionsExtension) => { await this.setLoadingExtension(extension.uuid, true); @@ -89,14 +128,14 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti })); } - async executeAction(action: Action, extension: SNActionsExtension) { + async executeAction(action: Action, extensionUuid: UuidString) { if (action.verb === 'nested') { - if (!action.subrows) { - const subrows = this.subRowsForAction(action, extension); - await this.updateAction(action, extension, { subrows }); - } + this.rebuildMenu({ + selectedActionId: action.id + }); return; } + const extension = this.application.findItem(extensionUuid) as SNActionsExtension; await this.updateAction(action, extension, { running: true }); const response = await this.application.actionsManager!.runAction( action, @@ -127,14 +166,15 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti } } - private subRowsForAction(parentAction: Action, extension: SNActionsExtension): ActionSubRow[] | undefined { + private subRowsForAction(parentAction: Action, extension: Pick): ActionSubRow[] | undefined { if (!parentAction.subactions) { return undefined; } return parentAction.subactions.map((subaction) => { return { + id: subaction.id, onClick: () => { - this.executeAction(subaction, extension); + this.executeAction(subaction, extension.uuid); }, label: subaction.label, subtitle: subaction.desc, @@ -146,7 +186,10 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti private async updateAction( action: Action, extension: SNActionsExtension, - params: UpdateActionParams + params: { + running?: boolean + error?: boolean + } ) { const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => { const extensionMutator = mutator as ActionsExtensionMutator; @@ -156,7 +199,6 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti ...action, running: params?.running, error: params?.error, - subrows: params?.subrows || act?.subrows, } as Action; } return act; @@ -172,7 +214,7 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti } return ext; }); - await this.setState({ + await this.rebuildMenu({ extensions }); } @@ -188,49 +230,34 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti } return ext; }); - this.setState({ + this.rebuildMenu({ extensions }); } - private async toggleExtensionVisibility(extensionUuid: UuidString) { + public toggleExtensionVisibility(extensionUuid: UuidString) { const { extensionsState } = this.state; extensionsState[extensionUuid].hidden = !extensionsState[extensionUuid].hidden; - await this.setState({ + this.rebuildMenu({ extensionsState }); } - private isExtensionVisible(extensionUuid: UuidString) { - const { extensionsState } = this.state; - return extensionsState[extensionUuid].hidden; - } - - private async setLoadingExtension(extensionUuid: UuidString, value = false) { + private setLoadingExtension(extensionUuid: UuidString, value = false) { const { extensionsState } = this.state; extensionsState[extensionUuid].loading = value; - await this.setState({ + this.rebuildMenu({ extensionsState }); } - private isExtensionLoading(extensionUuid: UuidString) { - const { extensionsState } = this.state; - return extensionsState[extensionUuid].loading; - } - - private async setErrorExtension(extensionUuid: UuidString, value = false) { + private setErrorExtension(extensionUuid: UuidString, value = false) { const { extensionsState } = this.state; extensionsState[extensionUuid].error = value; - await this.setState({ + this.rebuildMenu({ extensionsState }); } - - private extensionHasError(extensionUuid: UuidString) { - const { extensionsState } = this.state; - return extensionsState[extensionUuid].error; - } } export class ActionsMenu extends WebDirective { diff --git a/app/assets/templates/directives/actions-menu.pug b/app/assets/templates/directives/actions-menu.pug index aaf285820..99a50788d 100644 --- a/app/assets/templates/directives/actions-menu.pug +++ b/app/assets/templates/directives/actions-menu.pug @@ -1,43 +1,43 @@ .sn-component .sk-menu-panel.dropdown-menu a.no-decoration( - href='https://standardnotes.org/extensions', - ng-if='self.state.extensions.length == 0', - rel='noopener', + href='https://standardnotes.org/extensions', + ng-if='self.state.extensions.length == 0', + rel='noopener', target='blank' ) menu-row(label="'Download Actions'") - div(ng-repeat='extension in self.state.extensions track by extension.uuid') + div(ng-repeat='extension in self.state.menu track by extension.uuid') .sk-menu-panel-header( ng-click='self.toggleExtensionVisibility(extension.uuid); $event.stopPropagation();' - ) + ) .sk-menu-panel-column .sk-menu-panel-header-title {{extension.name}} - div(ng-if='self.isExtensionVisible(extension.uuid)') … - div(ng-if='self.isExtensionLoading(extension.uuid)') + div(ng-if='extension.visible') … + div(ng-if='extension.loading') .sk-spinner.small.loading menu-row( - action='self.executeAction(action, extension)', - label='action.label', - ng-if='!self.isExtensionVisible(extension.uuid) && !self.isExtensionLoading(extension.uuid) && !self.extensionHasError(extension.uuid)', - ng-repeat='action in extension.actionsWithContextForItem(self.item) track by $index', + action='self.executeAction(action, extension.uuid)', + label='action.label', + ng-if='!extension.visible && !extension.loading && !extension.error', + ng-repeat='action in extension.actions track by $index', disabled='action.running' - spinner-class="action.running ? 'info' : null", - sub-rows='action.subrows', + spinner-class="action.running ? 'info' : null", + sub-rows='action.subrows', subtitle='action.desc' ) .sk-sublabel(ng-if="action.access_type") - | Uses + | Uses strong {{action.access_type}} | access to this note. menu-row( - faded='true', - label="'No Actions Available'", - ng-if='extension.actionsWithContextForItem(self.item).length == 0' + faded='true', + label="'No Actions Available'", + ng-if='!extension.actions.length' ) menu-row( - faded='true', - label="'Error loading actions'", + faded='true', + label="'Error loading actions'", subtitle="'Please try again later.'" - ng-if='self.extensionHasError(extension.uuid)' + ng-if='extension.error' ) diff --git a/app/assets/templates/directives/menu-row.pug b/app/assets/templates/directives/menu-row.pug index 20b33ffad..25b7a18ea 100644 --- a/app/assets/templates/directives/menu-row.pug +++ b/app/assets/templates/directives/menu-row.pug @@ -1,12 +1,12 @@ .sk-menu-panel-row.row( - ng-attr-title='{{ctrl.desc}}', + ng-attr-title='{{ctrl.desc}}', ng-click='ctrl.onClick($event)' ) .sk-menu-panel-column .left .sk-menu-panel-column( ng-if=` - ctrl.circle && + ctrl.circle && (!ctrl.circleAlign || ctrl.circleAlign == 'left') ` ) @@ -21,17 +21,17 @@ ng-transclude .sk-menu-panel-subrows(ng-if='ctrl.subRows && ctrl.subRows.length > 0') menu-row( - ng-repeat='row in ctrl.subRows', - action='row.onClick()', - label='row.label', - spinner-class='row.spinnerClass', + ng-repeat='row in ctrl.subRows', + action='row.onClick()', + label='row.label', + spinner-class='row.spinnerClass', subtitle='row.subtitle' ) .sk-menu-panel-column(ng-if="ctrl.circle && ctrl.circleAlign == 'right'") .sk-circle.small(ng-class='ctrl.circle') .sk-menu-panel-column(ng-if='ctrl.hasButton') .sk-button( - ng-class='ctrl.buttonClass', + ng-class='ctrl.buttonClass', ng-click='ctrl.clickAccessoryButton($event)' ) .sk-label {{ctrl.buttonText}}