Merge branch 'develop' of github.com:standardnotes/web into develop

This commit is contained in:
Mo Bitar
2020-09-25 09:56:45 -05:00
3 changed files with 93 additions and 66 deletions

View File

@@ -18,12 +18,6 @@ type ActionSubRow = {
spinnerClass?: string spinnerClass?: string
} }
type UpdateActionParams = {
running?: boolean
error?: boolean
subrows?: ActionSubRow[]
}
type ExtensionState = { type ExtensionState = {
hidden: boolean hidden: boolean
loading: boolean loading: boolean
@@ -33,6 +27,17 @@ type ExtensionState = {
type ActionsMenuState = { type ActionsMenuState = {
extensions: SNActionsExtension[] extensions: SNActionsExtension[]
extensionsState: Record<UuidString, ExtensionState> extensionsState: Record<UuidString, ExtensionState>
selectedActionId?: number
menu: {
uuid: UuidString,
name: string,
loading: boolean,
error: boolean,
hidden: boolean,
actions: (Action & {
subrows?: ActionSubRow[]
})[]
}[]
} }
class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements ActionsMenuScope { class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements ActionsMenuScope {
@@ -52,6 +57,7 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti
item: this.item item: this.item
}); });
this.loadExtensions(); this.loadExtensions();
this.rebuildMenu();
}; };
/** @override */ /** @override */
@@ -69,10 +75,43 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti
}); });
return { return {
extensions, 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() { async loadExtensions() {
await Promise.all(this.state.extensions.map(async (extension: SNActionsExtension) => { await Promise.all(this.state.extensions.map(async (extension: SNActionsExtension) => {
await this.setLoadingExtension(extension.uuid, true); 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.verb === 'nested') {
if (!action.subrows) { this.rebuildMenu({
const subrows = this.subRowsForAction(action, extension); selectedActionId: action.id
await this.updateAction(action, extension, { subrows }); });
}
return; return;
} }
const extension = this.application.findItem(extensionUuid) as SNActionsExtension;
await this.updateAction(action, extension, { 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,
@@ -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<SNActionsExtension, 'uuid'>): ActionSubRow[] | undefined {
if (!parentAction.subactions) { if (!parentAction.subactions) {
return undefined; return undefined;
} }
return parentAction.subactions.map((subaction) => { return parentAction.subactions.map((subaction) => {
return { return {
id: subaction.id,
onClick: () => { onClick: () => {
this.executeAction(subaction, extension); this.executeAction(subaction, extension.uuid);
}, },
label: subaction.label, label: subaction.label,
subtitle: subaction.desc, subtitle: subaction.desc,
@@ -146,7 +186,10 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti
private async updateAction( private async updateAction(
action: Action, action: Action,
extension: SNActionsExtension, extension: SNActionsExtension,
params: UpdateActionParams params: {
running?: boolean
error?: boolean
}
) { ) {
const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => { const updatedExtension = await this.application.changeItem(extension.uuid, (mutator) => {
const extensionMutator = mutator as ActionsExtensionMutator; const extensionMutator = mutator as ActionsExtensionMutator;
@@ -156,7 +199,6 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti
...action, ...action,
running: params?.running, running: params?.running,
error: params?.error, error: params?.error,
subrows: params?.subrows || act?.subrows,
} as Action; } as Action;
} }
return act; return act;
@@ -172,7 +214,7 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti
} }
return ext; return ext;
}); });
await this.setState({ await this.rebuildMenu({
extensions extensions
}); });
} }
@@ -188,49 +230,34 @@ class ActionsMenuCtrl extends PureViewCtrl<{}, ActionsMenuState> implements Acti
} }
return ext; return ext;
}); });
this.setState({ this.rebuildMenu({
extensions extensions
}); });
} }
private async toggleExtensionVisibility(extensionUuid: UuidString) { public toggleExtensionVisibility(extensionUuid: UuidString) {
const { extensionsState } = this.state; const { extensionsState } = this.state;
extensionsState[extensionUuid].hidden = !extensionsState[extensionUuid].hidden; extensionsState[extensionUuid].hidden = !extensionsState[extensionUuid].hidden;
await this.setState({ this.rebuildMenu({
extensionsState extensionsState
}); });
} }
private isExtensionVisible(extensionUuid: UuidString) { private setLoadingExtension(extensionUuid: UuidString, value = false) {
const { extensionsState } = this.state;
return extensionsState[extensionUuid].hidden;
}
private async setLoadingExtension(extensionUuid: UuidString, value = false) {
const { extensionsState } = this.state; const { extensionsState } = this.state;
extensionsState[extensionUuid].loading = value; extensionsState[extensionUuid].loading = value;
await this.setState({ this.rebuildMenu({
extensionsState extensionsState
}); });
} }
private isExtensionLoading(extensionUuid: UuidString) { private setErrorExtension(extensionUuid: UuidString, value = false) {
const { extensionsState } = this.state;
return extensionsState[extensionUuid].loading;
}
private async setErrorExtension(extensionUuid: UuidString, value = false) {
const { extensionsState } = this.state; const { extensionsState } = this.state;
extensionsState[extensionUuid].error = value; extensionsState[extensionUuid].error = value;
await this.setState({ this.rebuildMenu({
extensionsState extensionsState
}); });
} }
private extensionHasError(extensionUuid: UuidString) {
const { extensionsState } = this.state;
return extensionsState[extensionUuid].error;
}
} }
export class ActionsMenu extends WebDirective { export class ActionsMenu extends WebDirective {

View File

@@ -1,43 +1,43 @@
.sn-component .sn-component
.sk-menu-panel.dropdown-menu .sk-menu-panel.dropdown-menu
a.no-decoration( a.no-decoration(
href='https://standardnotes.org/extensions', href='https://standardnotes.org/extensions',
ng-if='self.state.extensions.length == 0', ng-if='self.state.extensions.length == 0',
rel='noopener', rel='noopener',
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-repeat='extension in self.state.menu track by extension.uuid')
.sk-menu-panel-header( .sk-menu-panel-header(
ng-click='self.toggleExtensionVisibility(extension.uuid); $event.stopPropagation();' ng-click='self.toggleExtensionVisibility(extension.uuid); $event.stopPropagation();'
) )
.sk-menu-panel-column .sk-menu-panel-column
.sk-menu-panel-header-title {{extension.name}} .sk-menu-panel-header-title {{extension.name}}
div(ng-if='self.isExtensionVisible(extension.uuid)') … div(ng-if='extension.visible') …
div(ng-if='self.isExtensionLoading(extension.uuid)') div(ng-if='extension.loading')
.sk-spinner.small.loading .sk-spinner.small.loading
menu-row( menu-row(
action='self.executeAction(action, extension)', action='self.executeAction(action, extension.uuid)',
label='action.label', label='action.label',
ng-if='!self.isExtensionVisible(extension.uuid) && !self.isExtensionLoading(extension.uuid) && !self.extensionHasError(extension.uuid)', ng-if='!extension.visible && !extension.loading && !extension.error',
ng-repeat='action in extension.actionsWithContextForItem(self.item) track by $index', ng-repeat='action in extension.actions track by $index',
disabled='action.running' 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'
) )
.sk-sublabel(ng-if="action.access_type") .sk-sublabel(ng-if="action.access_type")
| Uses | Uses
strong {{action.access_type}} strong {{action.access_type}}
| access to this note. | access to this note.
menu-row( menu-row(
faded='true', faded='true',
label="'No Actions Available'", label="'No Actions Available'",
ng-if='extension.actionsWithContextForItem(self.item).length == 0' ng-if='!extension.actions.length'
) )
menu-row( menu-row(
faded='true', faded='true',
label="'Error loading actions'", label="'Error loading actions'",
subtitle="'Please try again later.'" subtitle="'Please try again later.'"
ng-if='self.extensionHasError(extension.uuid)' ng-if='extension.error'
) )

View File

@@ -1,12 +1,12 @@
.sk-menu-panel-row.row( .sk-menu-panel-row.row(
ng-attr-title='{{ctrl.desc}}', ng-attr-title='{{ctrl.desc}}',
ng-click='ctrl.onClick($event)' ng-click='ctrl.onClick($event)'
) )
.sk-menu-panel-column .sk-menu-panel-column
.left .left
.sk-menu-panel-column( .sk-menu-panel-column(
ng-if=` ng-if=`
ctrl.circle && ctrl.circle &&
(!ctrl.circleAlign || ctrl.circleAlign == 'left') (!ctrl.circleAlign || ctrl.circleAlign == 'left')
` `
) )
@@ -21,17 +21,17 @@
ng-transclude ng-transclude
.sk-menu-panel-subrows(ng-if='ctrl.subRows && ctrl.subRows.length > 0') .sk-menu-panel-subrows(ng-if='ctrl.subRows && ctrl.subRows.length > 0')
menu-row( menu-row(
ng-repeat='row in ctrl.subRows', ng-repeat='row in ctrl.subRows',
action='row.onClick()', action='row.onClick()',
label='row.label', label='row.label',
spinner-class='row.spinnerClass', spinner-class='row.spinnerClass',
subtitle='row.subtitle' subtitle='row.subtitle'
) )
.sk-menu-panel-column(ng-if="ctrl.circle && ctrl.circleAlign == 'right'") .sk-menu-panel-column(ng-if="ctrl.circle && ctrl.circleAlign == 'right'")
.sk-circle.small(ng-class='ctrl.circle') .sk-circle.small(ng-class='ctrl.circle')
.sk-menu-panel-column(ng-if='ctrl.hasButton') .sk-menu-panel-column(ng-if='ctrl.hasButton')
.sk-button( .sk-button(
ng-class='ctrl.buttonClass', ng-class='ctrl.buttonClass',
ng-click='ctrl.clickAccessoryButton($event)' ng-click='ctrl.clickAccessoryButton($event)'
) )
.sk-label {{ctrl.buttonText}} .sk-label {{ctrl.buttonText}}