fix: incremental loading for actions (#449)

* fix: incremental loading for actions

* fix: simplify loadingExtensions initialization

* fix: loading spinner per extension

* refactor: minor changes

* fix: loading and hidden state should not be persisted

* chore(deps): update snjs

* fix: keep === comparison operator

Co-authored-by: Johnny Almonte <johnny243@users.noreply.github.com>
This commit is contained in:
Johnny A
2020-08-26 09:35:54 -04:00
committed by GitHub
parent a63bfd8c0a
commit c4506a5407
3 changed files with 65 additions and 44 deletions

View File

@@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application';
import { WebDirective } from './../../types';
import template from '%/directives/actions-menu.pug';
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
import { SNItem, Action, SNActionsExtension } from 'snjs/dist/@types';
import { SNItem, Action, SNActionsExtension, UuidString } from 'snjs/dist/@types';
import { ActionResponse } from 'snjs/dist/@types/services/actions_service';
import { ActionsExtensionMutator } from 'snjs/dist/@types/models/app/extension';
@@ -15,7 +15,7 @@ type ActionSubRow = {
onClick: () => void
label: string
subtitle: string
spinnerClass: string | undefined
spinnerClass?: string
}
type UpdateActionParams = {
@@ -24,24 +24,21 @@ type UpdateActionParams = {
subrows?: ActionSubRow[]
}
type UpdateExtensionParams = {
hidden?: boolean
type ActionsMenuState = {
extensions: SNActionsExtension[],
hiddenState: Record<UuidString, Boolean>
loadingState: Record<UuidString, Boolean>
}
class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements ActionsMenuScope {
application!: WebApplication
item!: SNItem
public loadingExtensions: boolean = true
/* @ngInject */
constructor(
$timeout: ng.ITimeoutService
) {
super($timeout);
this.state = {
extensions: []
};
}
$onInit() {
@@ -52,20 +49,28 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
this.loadExtensions();
};
async loadExtensions() {
const actionExtensions = this.application.actionsManager!.getExtensions().sort((a, b) => {
/** @override */
getInitialState() {
const extensions = this.application.actionsManager!.getExtensions().sort((a, b) => {
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
});
const extensionsForItem = await Promise.all(actionExtensions.map((extension) => {
return this.application.actionsManager!.loadExtensionInContextOfItem(
return {
extensions,
loadingState: {},
hiddenState: {}
};
}
async loadExtensions() {
await Promise.all(this.state.extensions.map(async (extension: SNActionsExtension) => {
await this.setLoadingExtension(extension.uuid, true);
const updatedExtension = await this.application.actionsManager!.loadExtensionInContextOfItem(
extension,
this.props.item
this.item
);
await this.updateExtension(updatedExtension!);
await this.setLoadingExtension(extension.uuid, false);
}));
this.loadingExtensions = false;
await this.setState({
extensions: extensionsForItem
});
}
async executeAction(action: Action, extension: SNActionsExtension) {
@@ -79,7 +84,7 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
await this.updateAction(action, extension, { running: true });
const response = await this.application.actionsManager!.runAction(
action,
this.props.item,
this.item,
async () => {
/** @todo */
return '';
@@ -144,40 +149,59 @@ class ActionsMenuCtrl extends PureViewCtrl implements ActionsMenuScope {
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 = Boolean(params?.hidden);
}) as SNActionsExtension;
private async updateExtension(extension: SNActionsExtension) {
const extensions = this.state.extensions.map((ext: SNActionsExtension) => {
if (extension.uuid === ext.uuid) {
return updatedExtension;
return extension;
}
return ext;
});
await this.setState({
extensions: extensions
extensions
});
}
private async reloadExtension(extension: SNActionsExtension) {
const extensionInContext = await this.application.actionsManager!.loadExtensionInContextOfItem(
extension,
this.props.item
this.item
);
const extensions = this.state.extensions.map((ext: SNActionsExtension) => {
if (extension.uuid === ext.uuid) {
return extensionInContext;
return extensionInContext!;
}
return ext;
});
this.setState({
extensions: extensions
extensions
});
}
private async toggleExtensionVisibility(extensionUuid: UuidString) {
const { hiddenState } = this.state;
hiddenState[extensionUuid] = !hiddenState[extensionUuid] ?? false;
await this.setState({
hiddenState
});
}
private isExtensionVisible(extensionUuid: UuidString) {
const { hiddenState } = this.state;
return hiddenState[extensionUuid] ?? false;
}
private async setLoadingExtension(extensionUuid: UuidString, value = false) {
const { loadingState } = this.state;
loadingState[extensionUuid] = value;
await this.setState({
loadingState
});
}
private isExtensionLoading(extensionUuid: UuidString) {
const { loadingState } = this.state;
return loadingState[extensionUuid] ?? false;
}
}
export class ActionsMenu extends WebDirective {

View File

@@ -7,23 +7,20 @@
target='blank'
)
menu-row(label="'Download Actions'")
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')
.sk-menu-panel-header(
ng-click='self.updateExtension(extension, { hidden: !extension.hidden }); $event.stopPropagation();'
ng-click='self.toggleExtensionVisibility(extension.uuid); $event.stopPropagation();'
)
.sk-menu-panel-column
.sk-menu-panel-header-title {{extension.name}}
div(ng-if='extension.hidden') …
div(ng-if='self.isExtensionVisible(extension.uuid)') …
div(ng-if='self.isExtensionLoading(extension.uuid)')
.sk-spinner.small.loading
menu-row(
action='self.executeAction(action, extension)',
label='action.label',
ng-if='!extension.hidden',
ng-repeat='action in extension.actionsWithContextForItem(self.props.item) track by $index',
ng-if='!self.isExtensionVisible(extension.uuid) && !self.isExtensionLoading(extension.uuid)',
ng-repeat='action in extension.actionsWithContextForItem(self.item) track by $index',
disabled='action.running'
spinner-class="action.running ? 'info' : null",
sub-rows='action.subrows',
@@ -36,5 +33,5 @@
menu-row(
faded='true',
label="'No Actions Available'",
ng-if='extension.actionsWithContextForItem(self.props.item).length == 0'
ng-if='extension.actionsWithContextForItem(self.item).length == 0'
)

View File

@@ -64,6 +64,6 @@
},
"dependencies": {
"sncrypto": "github:standardnotes/sncrypto#4a080efeb646dbf9ca3dffdfcfa9d081b4dc6de0",
"snjs": "github:standardnotes/snjs#75ac6ff8e620f3b7e796b117b9d0afce303962a0"
"snjs": "github:standardnotes/snjs#2ef57eba1fc0e4f81e667d80bc3b99e0f650b6d1"
}
}