From 418ba70acbe1fed9558467b5664b9b56929bf9dc Mon Sep 17 00:00:00 2001 From: Nikola Stojanovic Date: Mon, 30 Jan 2023 14:12:32 +0100 Subject: [PATCH 01/11] Started woking on hotKeys functionality --- .../authoring-angular-integration.tsx | 10 + .../apps/authoring-react/authoring-react.tsx | 181 ++++++++++-------- .../apps/authoring-react/with-keybindings.tsx | 48 +++++ scripts/core/superdesk-api.d.ts | 3 + scripts/index.ts | 2 +- 5 files changed, 162 insertions(+), 82 deletions(-) create mode 100644 scripts/apps/authoring-react/with-keybindings.tsx diff --git a/scripts/apps/authoring-react/authoring-angular-integration.tsx b/scripts/apps/authoring-react/authoring-angular-integration.tsx index a83b1071a1..7ba15ced31 100644 --- a/scripts/apps/authoring-react/authoring-angular-integration.tsx +++ b/scripts/apps/authoring-react/authoring-angular-integration.tsx @@ -57,6 +57,11 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut /> ), availableOffline: true, + hotkeys: { + 'ctrl+alt+shift+s': () => { + console.log('saving'); + }, + }, }; const closeButton: ITopBarWidget = { @@ -72,6 +77,11 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut /> ), availableOffline: true, + hotkeys: { + 'ctrl+q': () => { + console.log('closing'); + }, + }, }; const minimizeButton: ITopBarWidget = { diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index def32667c3..22fb0f74ca 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -45,6 +45,7 @@ import {getField} from 'apps/fields'; import {preferences} from 'api/preferences'; import {dispatchEditorEvent, addEditorEventListener} from './authoring-react-editor-events'; import {previewAuthoringEntity} from './preview-article-modal'; +import {WithKeybindings} from './with-keybindings'; export function getFieldsData( item: T, @@ -190,6 +191,20 @@ function getInitialState( return initialState; } +function getHotkeysFromActions(actions: Array>) { + return actions + .filter((action) => action.hotkeys != null) + .map((action) => action.hotkeys) + .reduce((acc, obj) => { + acc = { + ...acc, + ...obj, + }; + + return acc; + }, {}); +} + /** * Toggling a field "off" hides it and removes its values. * Toggling to "on", displays field's input and allows setting a value. @@ -1121,67 +1136,87 @@ export class AuthoringReact extends React.PureCo ) } - - {(panelState, panelActions) => { - return ( - - - - )} - main={( - -
- { - this.props.topBar2Widgets - .map((Component, i) => { - return ( - - ); - }) - } -
- - - { - previewAuthoringEntity( - state.profile, - state.fieldsDataWithChanges, - ); + + + {(panelState, panelActions) => { + return ( + + + + )} + main={( + +
+ { + this.props.topBar2Widgets + .map((Component, i) => { + return ( + + ); + }) + } +
+ + + { + previewAuthoringEntity( + state.profile, + state.fieldsDataWithChanges, + ); + }} + /> + + + )} + authoringHeader={( +
+ - - - )} - authoringHeader={( +
+ )} + >
extends React.PureCo validationErrors={state.validationErrors} />
- )} - > -
- -
-
- )} - sideOverlay={!pinned && OpenWidgetComponent != null && OpenWidgetComponent} - sideOverlayOpen={!pinned && OpenWidgetComponent != null} - sidePanel={pinned && OpenWidgetComponent != null && OpenWidgetComponent} - sidePanelOpen={pinned && OpenWidgetComponent != null} - sideBar={this.props.getSidebar?.(exposed)} - /> - ); - }} -
+
+ )} + sideOverlay={!pinned && OpenWidgetComponent != null && OpenWidgetComponent} + sideOverlayOpen={!pinned && OpenWidgetComponent != null} + sidePanel={pinned && OpenWidgetComponent != null && OpenWidgetComponent} + sidePanelOpen={pinned && OpenWidgetComponent != null} + sideBar={this.props.getSidebar?.(exposed)} + /> + ); + }} +
+ ); } diff --git a/scripts/apps/authoring-react/with-keybindings.tsx b/scripts/apps/authoring-react/with-keybindings.tsx new file mode 100644 index 0000000000..d32c41168b --- /dev/null +++ b/scripts/apps/authoring-react/with-keybindings.tsx @@ -0,0 +1,48 @@ + +import React from 'react'; + +interface IProps { + keybindings: { + [key: string]: () => void; + }; +} + +export class WithKeybindings extends React.PureComponent { + constructor(props: IProps) { + super(props); + + this.handleKeyUp = this.handleKeyUp.bind(this); + } + + handleKeyUp(event: KeyboardEvent) { + for (const [hotkey, fn] of Object.entries(this.props.keybindings)) { + const splitted = hotkey.split('+'); + const altRequired = splitted.includes('alt'); + const shiftRequired = splitted.includes('shift'); + const ctrlRequired = splitted.includes('ctrl'); + + if ( + altRequired ? event.altKey : !event.altKey + && (shiftRequired ? event.shiftKey : !event.shiftKey) + && (ctrlRequired ? event.ctrlKey : !event.ctrlKey) + && (splitted[splitted.length - 1] === event.key.toLowerCase()) + ) { + fn(); + event.stopPropagation(); + break; + } + } + } + + componentDidMount() { + window.addEventListener('keyup', this.handleKeyUp); + } + + componentWillUnmount(): void { + window.removeEventListener('keyup', this.handleKeyUp); + } + + render(): React.ReactNode { + return this.props.children; + } +} diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index 39ab1e847a..83d5ad5dbb 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -162,6 +162,9 @@ declare module 'superdesk-api' { availableOffline: boolean; priority: IDisplayPriority; group: 'start' | 'middle' | 'end'; + hotkeys?: { + [key: string]: () => void; + }; } interface IPropsAuthoring { diff --git a/scripts/index.ts b/scripts/index.ts index adc031b930..5039af1689 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -253,4 +253,4 @@ setTimeout(() => { if (started !== true) { startApp([], {}); } -}, 500); +}, 501); From 759f64e194f6e32611b2f2b5bbc4906506ae1f98 Mon Sep 17 00:00:00 2001 From: Tomas Kikutis Date: Mon, 30 Jan 2023 14:54:18 +0100 Subject: [PATCH 02/11] code style improvements --- .../authoring-angular-integration.tsx | 4 +-- .../apps/authoring-react/authoring-react.tsx | 21 +++++------ .../apps/authoring-react/with-keybindings.tsx | 36 +++++++++---------- scripts/core/superdesk-api.d.ts | 2 +- 4 files changed, 29 insertions(+), 34 deletions(-) diff --git a/scripts/apps/authoring-react/authoring-angular-integration.tsx b/scripts/apps/authoring-react/authoring-angular-integration.tsx index 7ba15ced31..324d2d84d4 100644 --- a/scripts/apps/authoring-react/authoring-angular-integration.tsx +++ b/scripts/apps/authoring-react/authoring-angular-integration.tsx @@ -57,7 +57,7 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut /> ), availableOffline: true, - hotkeys: { + keyBindings: { 'ctrl+alt+shift+s': () => { console.log('saving'); }, @@ -77,7 +77,7 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut /> ), availableOffline: true, - hotkeys: { + keyBindings: { 'ctrl+q': () => { console.log('closing'); }, diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index 22fb0f74ca..69639e14b9 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -45,7 +45,7 @@ import {getField} from 'apps/fields'; import {preferences} from 'api/preferences'; import {dispatchEditorEvent, addEditorEventListener} from './authoring-react-editor-events'; import {previewAuthoringEntity} from './preview-article-modal'; -import {WithKeybindings} from './with-keybindings'; +import {WithKeyBindings} from './with-keybindings'; export function getFieldsData( item: T, @@ -191,17 +191,14 @@ function getInitialState( return initialState; } -function getHotkeysFromActions(actions: Array>) { +function getKeyBindingsFromActions(actions: Array>) { return actions - .filter((action) => action.hotkeys != null) - .map((action) => action.hotkeys) - .reduce((acc, obj) => { - acc = { + .filter((action) => action.keyBindings != null) + .reduce((acc, action) => { + return { ...acc, - ...obj, + ...action.keyBindings, }; - - return acc; }, {}); } @@ -1136,9 +1133,7 @@ export class AuthoringReact extends React.PureCo ) } - + {(panelState, panelActions) => { return ( @@ -1236,7 +1231,7 @@ export class AuthoringReact extends React.PureCo ); }} - + ); } diff --git a/scripts/apps/authoring-react/with-keybindings.tsx b/scripts/apps/authoring-react/with-keybindings.tsx index d32c41168b..90df3368e3 100644 --- a/scripts/apps/authoring-react/with-keybindings.tsx +++ b/scripts/apps/authoring-react/with-keybindings.tsx @@ -2,12 +2,12 @@ import React from 'react'; interface IProps { - keybindings: { + keyBindings: { [key: string]: () => void; }; } -export class WithKeybindings extends React.PureComponent { +export class WithKeyBindings extends React.PureComponent { constructor(props: IProps) { super(props); @@ -15,22 +15,22 @@ export class WithKeybindings extends React.PureComponent { } handleKeyUp(event: KeyboardEvent) { - for (const [hotkey, fn] of Object.entries(this.props.keybindings)) { - const splitted = hotkey.split('+'); - const altRequired = splitted.includes('alt'); - const shiftRequired = splitted.includes('shift'); - const ctrlRequired = splitted.includes('ctrl'); - - if ( - altRequired ? event.altKey : !event.altKey - && (shiftRequired ? event.shiftKey : !event.shiftKey) - && (ctrlRequired ? event.ctrlKey : !event.ctrlKey) - && (splitted[splitted.length - 1] === event.key.toLowerCase()) - ) { - fn(); - event.stopPropagation(); - break; - } + const matchingKeyBinding: string | null = Object.keys(this.props.keyBindings).find((hotkey) => { + const split = hotkey.split('+'); + const altRequired = split.includes('alt'); + const shiftRequired = split.includes('shift'); + const ctrlRequired = split.includes('ctrl'); + + return (altRequired && event.altKey === true) + && (shiftRequired && event.shiftKey === true) + && (ctrlRequired && event.ctrlKey === true) + && (split[split.length - 1] === event.key.toLowerCase()); + }); + + if (matchingKeyBinding != null) { + event.stopPropagation(); + + this.props.keyBindings[matchingKeyBinding](); } } diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index 83d5ad5dbb..41c295c63d 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -162,7 +162,7 @@ declare module 'superdesk-api' { availableOffline: boolean; priority: IDisplayPriority; group: 'start' | 'middle' | 'end'; - hotkeys?: { + keyBindings?: { [key: string]: () => void; }; } From a2baf20e3c1e56d2d7f77dfd908e3ac5866b7a14 Mon Sep 17 00:00:00 2001 From: Tomas Kikutis Date: Mon, 30 Jan 2023 15:08:59 +0100 Subject: [PATCH 03/11] apply save and close key bindings --- .../authoring-react/authoring-angular-integration.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/apps/authoring-react/authoring-angular-integration.tsx b/scripts/apps/authoring-react/authoring-angular-integration.tsx index 324d2d84d4..e4201e6796 100644 --- a/scripts/apps/authoring-react/authoring-angular-integration.tsx +++ b/scripts/apps/authoring-react/authoring-angular-integration.tsx @@ -58,8 +58,10 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut ), availableOffline: true, keyBindings: { - 'ctrl+alt+shift+s': () => { - console.log('saving'); + 'ctrl+shift+s': () => { + if (hasUnsavedChanges()) { + save(); + } }, }, }; @@ -78,8 +80,8 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut ), availableOffline: true, keyBindings: { - 'ctrl+q': () => { - console.log('closing'); + 'ctrl+shift+e': () => { + initiateClosing(); }, }, }; From 5700f963c40f5e95f644565cef5bb5a2e1babdf7 Mon Sep 17 00:00:00 2001 From: dzonidoo Date: Wed, 1 Feb 2023 14:47:22 +0100 Subject: [PATCH 04/11] Co-authored-by: Tomas Kikutis --- .../authoring-angular-integration.tsx | 5 + .../apps/authoring-react/authoring-react.tsx | 97 ++++++++++++++++--- .../authoring-react/fields/register-fields.ts | 5 + .../apps/authoring-react/with-keybindings.tsx | 8 +- scripts/apps/highlights/views/menu.html | 3 +- scripts/core/superdesk-api.d.ts | 5 + 6 files changed, 107 insertions(+), 16 deletions(-) diff --git a/scripts/apps/authoring-react/authoring-angular-integration.tsx b/scripts/apps/authoring-react/authoring-angular-integration.tsx index e4201e6796..84ecf81a2a 100644 --- a/scripts/apps/authoring-react/authoring-angular-integration.tsx +++ b/scripts/apps/authoring-react/authoring-angular-integration.tsx @@ -210,6 +210,11 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut }} /> ), + keyBindings: { + 'ctrl+shift+u': () => { + stealLock(); + } + }, availableOffline: false, }); diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index 69639e14b9..4f3deff215 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -1089,6 +1089,24 @@ export class AuthoringReact extends React.PureCo default: true, }); }, + keyBindings: { + 'ctrl+shift+y': () => { + + console.log('trigered'); + this.setState({ + ...state, + spellcheckerEnabled: nextValue, + }); + + dispatchEditorEvent('spellchecker__set_status', nextValue); + + preferences.update(SPELLCHECKER_PREFERENCE, { + type: 'bool', + enabled: nextValue, + default: true, + }); + }, + }, }); } else { coreActions.push({ @@ -1109,10 +1127,33 @@ export class AuthoringReact extends React.PureCo default: true, }); }, + keyBindings: { + 'ctrl+shift+y': () => { + console.log('trigered'); + + const nextValue = true; + + this.setState({ + ...state, + spellcheckerEnabled: true, + }); + + dispatchEditorEvent('spellchecker__set_status', nextValue); + + preferences.update(SPELLCHECKER_PREFERENCE, { + type: 'bool', + enabled: nextValue, + default: true, + }); + }, + }, }); } } + console.log(coreActions); + + return [...coreActions, ...actions]; }); }} @@ -1123,8 +1164,44 @@ export class AuthoringReact extends React.PureCo }, ]; + console.log(toolbar1Widgets); + + // eslint-disable-next-line array-callback-return + toolbar1Widgets.filter((e) => { + if (e.keyBindings) { + console.log(e.keyBindings); + } + }); + + + const pinned = state.openWidget?.pinned === true; + const preview = { + jsxButton: () => { + return ( + { + previewAuthoringEntity( + state.profile, + state.fieldsDataWithChanges, + ); + }} + /> + ); + }, + keybindings: { + 'ctrl+shift+i': () => { + previewAuthoringEntity( + state.profile, + state.fieldsDataWithChanges, + ); + }, + }, + }; + return ( { @@ -1133,7 +1210,14 @@ export class AuthoringReact extends React.PureCo ) } - + {(panelState, panelActions) => { return ( @@ -1173,16 +1257,7 @@ export class AuthoringReact extends React.PureCo - { - previewAuthoringEntity( - state.profile, - state.fieldsDataWithChanges, - ); - }} - /> + {preview.jsxButton()} )} diff --git a/scripts/apps/authoring-react/fields/register-fields.ts b/scripts/apps/authoring-react/fields/register-fields.ts index 48170470be..8a32a61abb 100644 --- a/scripts/apps/authoring-react/fields/register-fields.ts +++ b/scripts/apps/authoring-react/fields/register-fields.ts @@ -25,6 +25,11 @@ export function registerAuthoringReactFields() { onTrigger: () => { runTansa(contentProfile, fieldsData); }, + keyBindings: { + 'ctrl+shift+y': () => { + runTansa(contentProfile, fieldsData); + }, + }, }; return Promise.resolve([checkSpellingAction]); diff --git a/scripts/apps/authoring-react/with-keybindings.tsx b/scripts/apps/authoring-react/with-keybindings.tsx index 90df3368e3..4a4b0500eb 100644 --- a/scripts/apps/authoring-react/with-keybindings.tsx +++ b/scripts/apps/authoring-react/with-keybindings.tsx @@ -21,10 +21,10 @@ export class WithKeyBindings extends React.PureComponent { const shiftRequired = split.includes('shift'); const ctrlRequired = split.includes('ctrl'); - return (altRequired && event.altKey === true) - && (shiftRequired && event.shiftKey === true) - && (ctrlRequired && event.ctrlKey === true) - && (split[split.length - 1] === event.key.toLowerCase()); + return (altRequired ? event.altKey : !event.altKey) + && (shiftRequired ? event.shiftKey : !event.shiftKey) + && (ctrlRequired ? event.ctrlKey : !event.ctrlKey) + && (split[split.length - 1] === event.key.toLowerCase()); }); if (matchingKeyBinding != null) { diff --git a/scripts/apps/highlights/views/menu.html b/scripts/apps/highlights/views/menu.html index 1b1f1ed796..d67ec62139 100644 --- a/scripts/apps/highlights/views/menu.html +++ b/scripts/apps/highlights/views/menu.html @@ -4,4 +4,5 @@ sd-hotkey-options="{global: true}" sd-hotkey-callback="highlightsHotkey" dropdown sd-package-highlights-dropdown> - \ No newline at end of file + + diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index 41c295c63d..6ba8970ade 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -433,12 +433,17 @@ declare module 'superdesk-api' { */ export type IDisplayPriority = number; + export interface IKeyBindings { + [key: string]: () => void; + } + export interface IAuthoringAction { groupId?: string; // action lists can specify which groups they wanna render via an id priority?: IDisplayPriority; icon?: string; label: string; onTrigger(): void; + keyBindings?: IKeyBindings; } export interface IArticleActionBulk { From 3713c6a731a95e1aedc0cabb643a3b0c3644092a Mon Sep 17 00:00:00 2001 From: dzonidoo Date: Wed, 1 Feb 2023 15:45:51 +0100 Subject: [PATCH 05/11] fix lint --- package-lock.json | 18 +++++++++--------- .../authoring-angular-integration.tsx | 3 +-- .../authoring-integration-wrapper.tsx | 2 -- .../apps/authoring-react/authoring-react.tsx | 11 +---------- .../authoring-react/fields/date/editor.tsx | 2 +- .../mark-for-desks/mark-for-desks-modal.tsx | 2 +- 6 files changed, 13 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index f58d07e913..cee8643da8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -597,9 +597,9 @@ } }, "@superdesk/primereact": { - "version": "5.0.2-7", - "resolved": "https://registry.npmjs.org/@superdesk/primereact/-/primereact-5.0.2-7.tgz", - "integrity": "sha512-pgA1Fu+3kifz33nf8zUu40WVJ/aKaIKVfyZwFbS5WqGAZlyut2Ro2sA5iaS0mYHezR/bfQ7GvCiZP4Y8zLx6sw==", + "version": "5.0.2-8", + "resolved": "https://registry.npmjs.org/@superdesk/primereact/-/primereact-5.0.2-8.tgz", + "integrity": "sha512-KsqnypKVtAcEWsk0pQS5uZRNN5m7hDVH5RIkLeTO4YQU492pVRs3AhNkfzLKJENVU1D8VUrsJKG/T1wXnFafTQ==", "requires": { "react-transition-group": "^4.4.1" }, @@ -15955,9 +15955,9 @@ } }, "superdesk-ui-framework": { - "version": "3.0.1-beta.28", - "resolved": "https://registry.npmjs.org/superdesk-ui-framework/-/superdesk-ui-framework-3.0.1-beta.28.tgz", - "integrity": "sha512-dUi6GK6OQTN8RCs/ejxegfHW8e/3KVw7h53OMukXQ44AqKBiz0+duCHeYHjh6ZUo/9Pd91uU2rji8naqsyYOvQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/superdesk-ui-framework/-/superdesk-ui-framework-3.0.2.tgz", + "integrity": "sha512-0aQZ+7oTs4O5SNckQSevhAKNl5wL5Fb35cR1JwtsSZLj+x8l6ry7gAyCTt9Z9MqVl5IqfzuDd4f3OYAeZ/Vc4g==", "requires": { "@material-ui/lab": "^4.0.0-alpha.56", "@popperjs/core": "^2.4.0", @@ -15975,9 +15975,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", - "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==" + "version": "14.18.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz", + "integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==" }, "moment": { "version": "2.29.4", diff --git a/scripts/apps/authoring-react/authoring-angular-integration.tsx b/scripts/apps/authoring-react/authoring-angular-integration.tsx index 84ecf81a2a..7a40005d82 100644 --- a/scripts/apps/authoring-react/authoring-angular-integration.tsx +++ b/scripts/apps/authoring-react/authoring-angular-integration.tsx @@ -158,7 +158,6 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut actions.push(getManageHighlights()); } - // eslint-disable-next-line no-case-declarations const manageDesksButton: ITopBarWidget = ({ group: 'start', @@ -213,7 +212,7 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut keyBindings: { 'ctrl+shift+u': () => { stealLock(); - } + }, }, availableOffline: false, }); diff --git a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx index 2b4a8d1f8c..0caa422714 100644 --- a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx +++ b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx @@ -47,7 +47,6 @@ import {CompareArticleVersionsModal} from './toolbar/compare-article-versions'; import {httpRequestJsonLocal} from 'core/helpers/network'; import {getArticleAdapter} from './article-adapter'; import {ui} from 'core/ui-utils'; -import TranslateModal from './toolbar/translate-modal'; import {MarkForDesksModal} from './toolbar/mark-for-desks/mark-for-desks-modal'; function getAuthoringActionsFromExtensions( @@ -212,7 +211,6 @@ const getSaveAsTemplate = (getItem: () => IArticle): IAuthoringAction => ({ ), }); - const getTranslateModal = (getItem: () => IArticle): IAuthoringAction => ({ label: gettext('Translate'), onTrigger: () => { diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index 4f3deff215..19c09ca971 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -1091,8 +1091,6 @@ export class AuthoringReact extends React.PureCo }, keyBindings: { 'ctrl+shift+y': () => { - - console.log('trigered'); this.setState({ ...state, spellcheckerEnabled: nextValue, @@ -1129,8 +1127,6 @@ export class AuthoringReact extends React.PureCo }, keyBindings: { 'ctrl+shift+y': () => { - console.log('trigered'); - const nextValue = true; this.setState({ @@ -1151,9 +1147,6 @@ export class AuthoringReact extends React.PureCo } } - console.log(coreActions); - - return [...coreActions, ...actions]; }); }} @@ -1173,8 +1166,6 @@ export class AuthoringReact extends React.PureCo } }); - - const pinned = state.openWidget?.pinned === true; const preview = { @@ -1214,7 +1205,7 @@ export class AuthoringReact extends React.PureCo keyBindings={ { ...preview.keybindings, - ...getKeyBindingsFromActions(authoringOptions.actions) + ...getKeyBindingsFromActions(authoringOptions.actions), } } > diff --git a/scripts/apps/authoring-react/fields/date/editor.tsx b/scripts/apps/authoring-react/fields/date/editor.tsx index d2a441a15a..8929a86703 100644 --- a/scripts/apps/authoring-react/fields/date/editor.tsx +++ b/scripts/apps/authoring-react/fields/date/editor.tsx @@ -30,7 +30,7 @@ export class Editor extends React.PureComponent { }} dateFormat={appConfig.view.dateformat} locale={getLocaleForDatePicker(this.props.language)} - shortcuts={this.props.config?.shortcuts?.map(({label, value, term}) => { + headerButtonBar={this.props.config?.shortcuts?.map(({label, value, term}) => { return { label, days: differenceInCalendarDays( diff --git a/scripts/apps/authoring-react/toolbar/mark-for-desks/mark-for-desks-modal.tsx b/scripts/apps/authoring-react/toolbar/mark-for-desks/mark-for-desks-modal.tsx index 7cdd3f7c18..7250908064 100644 --- a/scripts/apps/authoring-react/toolbar/mark-for-desks/mark-for-desks-modal.tsx +++ b/scripts/apps/authoring-react/toolbar/mark-for-desks/mark-for-desks-modal.tsx @@ -39,7 +39,7 @@ export class MarkForDesksModal extends React.PureComponent { > { + onChange={(value) => { this.setState({ ...this.state, selectedDesks: value.map((desk) => desk._id), From 3fba203d9013870726d43b88f559a016c0cc7ac7 Mon Sep 17 00:00:00 2001 From: dzonidoo Date: Wed, 1 Feb 2023 17:07:34 +0100 Subject: [PATCH 06/11] Changed getAction interface to be synchronous and added keybindings for spell checker --- .../authoring-integration-wrapper.tsx | 53 +++-- .../apps/authoring-react/authoring-react.tsx | 194 +++++++++--------- .../authoring-react/fields/register-fields.ts | 4 +- .../subcomponents/authoring-actions-menu.tsx | 2 +- .../directives/AuthoringTopbarDirective.ts | 6 +- .../monitoring/directives/ItemActionsMenu.ts | 172 ++++++++-------- .../components/actions-menu/MenuItems.tsx | 6 +- scripts/core/superdesk-api-helpers.tsx | 9 +- scripts/core/superdesk-api.d.ts | 9 +- 9 files changed, 223 insertions(+), 232 deletions(-) diff --git a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx index 0caa422714..00e3283a29 100644 --- a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx +++ b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx @@ -53,7 +53,7 @@ function getAuthoringActionsFromExtensions( item: IArticle, contentProfile: IContentProfileV2, fieldsData: Map, -): Promise> { +): Array { const actionGetters : Array = flatMap( @@ -61,10 +61,9 @@ function getAuthoringActionsFromExtensions( (extension) => extension.activationResult.contributions?.getAuthoringActions ?? [], ); - return Promise.all(actionGetters.map((getPromise) => getPromise(item, contentProfile, fieldsData))) - .then((res) => { - return flatMap(res); - }); + return flatMap( + actionGetters.map((getPromise) => getPromise(item, contentProfile, fieldsData)), + ); } const defaultToolbarItems: Array> = [CreatedModifiedInfo]; @@ -373,28 +372,28 @@ export class AuthoringIntegrationWrapper extends React.PureComponent { - return Promise.all([ - getAuthoringActionsFromExtensions(item, contentProfile, fieldsData), - getArticleActionsFromExtensions(item), - ]).then((res) => { - const [authoringActionsFromExtensions, articleActionsFromExtensions] = res; - - return [ - getSaveAsTemplate(getLatestItem), - getCompareVersionsModal( - getLatestItem, - authoringStorage, - fieldsAdapter, - storageAdapter, - ), - getHighlightsAction(getLatestItem), - getMarkedForDesksModal(getLatestItem), - getExportModal(getLatestItem, handleUnsavedChanges, hasUnsavedChanges), - getTranslateModal(getLatestItem), - ...authoringActionsFromExtensions, - ...articleActionsFromExtensions, - ]; - }); + const authoringActionsFromExtensions = getAuthoringActionsFromExtensions( + item, + contentProfile, + fieldsData, + ); + const articleActionsFromExtensions = getArticleActionsFromExtensions(item); + + return [ + getSaveAsTemplate(getLatestItem), + getCompareVersionsModal( + getLatestItem, + authoringStorage, + fieldsAdapter, + storageAdapter, + ), + getHighlightsAction(getLatestItem), + getMarkedForDesksModal(getLatestItem), + getExportModal(getLatestItem, handleUnsavedChanges, hasUnsavedChanges), + getTranslateModal(getLatestItem), + ...authoringActionsFromExtensions, + ...articleActionsFromExtensions, + ]; }} getInlineToolbarActions={this.props.getInlineToolbarActions} getAuthoringTopBarWidgets={ diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index 19c09ca971..a871fd3cec 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -12,6 +12,7 @@ import { IPropsAuthoring, ITopBarWidget, IExposedFromAuthoring, + IKeyBindings, } from 'superdesk-api'; import { ButtonGroup, @@ -1055,6 +1056,99 @@ export class AuthoringReact extends React.PureCo const readOnly = state.initialized ? authoringOptions.readOnly : false; const OpenWidgetComponent = getSidePanel == null ? null : this.props.getSidePanel(exposed, readOnly); + const authoringActions: Array = (() => { + const actions = this.props.getActions?.(exposed) ?? []; + const coreActions: Array = []; + + if (appConfig.features.useTansaProofing !== true) { + if (state.spellcheckerEnabled) { + const nextValue = false; + + coreActions.push({ + label: gettext('Disable spellchecker'), + onTrigger: () => { + this.setState({ + ...state, + spellcheckerEnabled: nextValue, + }); + + dispatchEditorEvent('spellchecker__set_status', nextValue); + + preferences.update(SPELLCHECKER_PREFERENCE, { + type: 'bool', + enabled: nextValue, + default: true, + }); + }, + keyBindings: { + 'ctrl+shift+y': () => { + this.setState({ + ...state, + spellcheckerEnabled: nextValue, + }); + + dispatchEditorEvent('spellchecker__set_status', nextValue); + + preferences.update(SPELLCHECKER_PREFERENCE, { + type: 'bool', + enabled: nextValue, + default: true, + }); + }, + }, + }); + } else { + coreActions.push({ + label: gettext('Enable spellchecker'), + onTrigger: () => { + const nextValue = true; + + this.setState({ + ...state, + spellcheckerEnabled: true, + }); + + dispatchEditorEvent('spellchecker__set_status', nextValue); + + preferences.update(SPELLCHECKER_PREFERENCE, { + type: 'bool', + enabled: nextValue, + default: true, + }); + }, + keyBindings: { + 'ctrl+shift+y': () => { + const nextValue = true; + + this.setState({ + ...state, + spellcheckerEnabled: true, + }); + + dispatchEditorEvent('spellchecker__set_status', nextValue); + + preferences.update(SPELLCHECKER_PREFERENCE, { + type: 'bool', + enabled: nextValue, + default: true, + }); + }, + }, + }); + } + } + + return [...coreActions, ...actions]; + })(); + + const keyBindingsFromAuthoringActions: IKeyBindings = + authoringActions.reduce((acc, action) => { + return { + ...acc, + ...(action.keyBindings ?? {}), + }; + }, {}); + const toolbar1Widgets: Array> = [ ...authoringOptions.actions, { @@ -1062,110 +1156,13 @@ export class AuthoringReact extends React.PureCo priority: 0.4, component: () => { return ( - { - return ( - this.props.getActions?.(exposed) ?? Promise.resolve([]) - ).then((actions) => { - const coreActions: Array = []; - - if (appConfig.features.useTansaProofing !== true) { - if (state.spellcheckerEnabled) { - const nextValue = false; - - coreActions.push({ - label: gettext('Disable spellchecker'), - onTrigger: () => { - this.setState({ - ...state, - spellcheckerEnabled: nextValue, - }); - - dispatchEditorEvent('spellchecker__set_status', nextValue); - - preferences.update(SPELLCHECKER_PREFERENCE, { - type: 'bool', - enabled: nextValue, - default: true, - }); - }, - keyBindings: { - 'ctrl+shift+y': () => { - this.setState({ - ...state, - spellcheckerEnabled: nextValue, - }); - - dispatchEditorEvent('spellchecker__set_status', nextValue); - - preferences.update(SPELLCHECKER_PREFERENCE, { - type: 'bool', - enabled: nextValue, - default: true, - }); - }, - }, - }); - } else { - coreActions.push({ - label: gettext('Enable spellchecker'), - onTrigger: () => { - const nextValue = true; - - this.setState({ - ...state, - spellcheckerEnabled: true, - }); - - dispatchEditorEvent('spellchecker__set_status', nextValue); - - preferences.update(SPELLCHECKER_PREFERENCE, { - type: 'bool', - enabled: nextValue, - default: true, - }); - }, - keyBindings: { - 'ctrl+shift+y': () => { - const nextValue = true; - - this.setState({ - ...state, - spellcheckerEnabled: true, - }); - - dispatchEditorEvent('spellchecker__set_status', nextValue); - - preferences.update(SPELLCHECKER_PREFERENCE, { - type: 'bool', - enabled: nextValue, - default: true, - }); - }, - }, - }); - } - } - - return [...coreActions, ...actions]; - }); - }} - /> + Promise.resolve(authoringActions)} /> ); }, availableOffline: true, }, ]; - console.log(toolbar1Widgets); - - // eslint-disable-next-line array-callback-return - toolbar1Widgets.filter((e) => { - if (e.keyBindings) { - console.log(e.keyBindings); - } - }); - const pinned = state.openWidget?.pinned === true; const preview = { @@ -1206,6 +1203,7 @@ export class AuthoringReact extends React.PureCo { ...preview.keybindings, ...getKeyBindingsFromActions(authoringOptions.actions), + ...keyBindingsFromAuthoringActions, } } > diff --git a/scripts/apps/authoring-react/fields/register-fields.ts b/scripts/apps/authoring-react/fields/register-fields.ts index 8a32a61abb..fd4ed3af57 100644 --- a/scripts/apps/authoring-react/fields/register-fields.ts +++ b/scripts/apps/authoring-react/fields/register-fields.ts @@ -32,9 +32,9 @@ export function registerAuthoringReactFields() { }, }; - return Promise.resolve([checkSpellingAction]); + return [checkSpellingAction]; } else { - return Promise.resolve([]); + return []; } }, customFieldTypes: [ diff --git a/scripts/apps/authoring-react/subcomponents/authoring-actions-menu.tsx b/scripts/apps/authoring-react/subcomponents/authoring-actions-menu.tsx index 75f3bcccee..e5c30faaa9 100644 --- a/scripts/apps/authoring-react/subcomponents/authoring-actions-menu.tsx +++ b/scripts/apps/authoring-react/subcomponents/authoring-actions-menu.tsx @@ -6,7 +6,7 @@ import {gettext} from 'core/utils'; import {IMenuItem} from 'superdesk-ui-framework/react/components/Menu'; interface IProps { - getActions: () => Promise>; + getActions: () => Promise>; // TODO: convert to sync interface } interface IState { diff --git a/scripts/apps/authoring/authoring/directives/AuthoringTopbarDirective.ts b/scripts/apps/authoring/authoring/directives/AuthoringTopbarDirective.ts index e85ff85866..28ba1aa96a 100644 --- a/scripts/apps/authoring/authoring/directives/AuthoringTopbarDirective.ts +++ b/scripts/apps/authoring/authoring/directives/AuthoringTopbarDirective.ts @@ -25,11 +25,7 @@ export function AuthoringTopbarDirective( templateUrl: 'scripts/apps/authoring/views/authoring-topbar.html', link: function(scope) { function setActionsFromExtensions() { - scope.articleActionsFromExtensions = []; - - getArticleActionsFromExtensions(scope.item).then((articleActions) => { - scope.articleActionsFromExtensions = articleActions; - }); + scope.articleActionsFromExtensions = getArticleActionsFromExtensions(scope.item); } scope.additionalButtons = authoringWorkspace.authoringTopBarAdditionalButtons; diff --git a/scripts/apps/monitoring/directives/ItemActionsMenu.ts b/scripts/apps/monitoring/directives/ItemActionsMenu.ts index b53cf353b8..64aa275cf2 100644 --- a/scripts/apps/monitoring/directives/ItemActionsMenu.ts +++ b/scripts/apps/monitoring/directives/ItemActionsMenu.ts @@ -96,105 +96,103 @@ export function ItemActionsMenu( function getActions(item: IArticle): void { scope.menuGroups = []; - getArticleActionsFromExtensions(item) - .then((actionsFromExtensions) => { - let intent = {action: 'list', type: getType(item)}; - let activitiesByGroupName: {[groupName: string]: Array} = {}; - - // group activities by `activity.group` - superdesk.findActivities(intent, item).forEach((activity: IActivity) => { - if (workflowService.isActionAllowed(scope.item, activity.action)) { - let group = activity.group ?? 'default'; - - if (activitiesByGroupName[group] == null) { - activitiesByGroupName[group] = []; - } - if (scope.allowedActions?.length > 0) { - if (scope.allowedActions.includes(activity._id)) { - activitiesByGroupName[group].push(activity); - } - } else { - activitiesByGroupName[group].push(activity); - } - } - }); + const actionsFromExtensions = getArticleActionsFromExtensions(item); + let intent = {action: 'list', type: getType(item)}; + let activitiesByGroupName: {[groupName: string]: Array} = {}; - let menuGroups: Array = []; - - // take default menu groups, add activities and push to `menuGroups` - getAuthoringMenuGroups().forEach((group) => { - if (activitiesByGroupName[group._id] && activitiesByGroupName[group._id].length > 0) { - menuGroups.push({ - _id: group._id, - label: group.label, - concate: group.concate, - actions: activitiesByGroupName[group._id] - .map((activity) => ({kind: 'activity-based', activity: activity})), - }); - } - }); + // group activities by `activity.group` + superdesk.findActivities(intent, item).forEach((activity: IActivity) => { + if (workflowService.isActionAllowed(scope.item, activity.action)) { + let group = activity.group ?? 'default'; - // go over `activitiesByGroupName` and add groups not present - // in default groups (getAuthoringMenuGroups) - Object.keys(activitiesByGroupName).forEach((groupName) => { - var existingGroup = getAuthoringMenuGroups().find((g) => g._id === groupName); - - if (!existingGroup) { - menuGroups.push({ - _id: groupName, - label: groupName, - actions: activitiesByGroupName[groupName] - .map((activity) => ({kind: 'activity-based', activity: activity})), - }); + if (activitiesByGroupName[group] == null) { + activitiesByGroupName[group] = []; + } + if (scope.allowedActions?.length > 0) { + if (scope.allowedActions.includes(activity._id)) { + activitiesByGroupName[group].push(activity); } + } else { + activitiesByGroupName[group].push(activity); + } + } + }); + + let menuGroups: Array = []; + + // take default menu groups, add activities and push to `menuGroups` + getAuthoringMenuGroups().forEach((group) => { + if (activitiesByGroupName[group._id] && activitiesByGroupName[group._id].length > 0) { + menuGroups.push({ + _id: group._id, + label: group.label, + concate: group.concate, + actions: activitiesByGroupName[group._id] + .map((activity) => ({kind: 'activity-based', activity: activity})), + }); + } + }); + + // go over `activitiesByGroupName` and add groups not present + // in default groups (getAuthoringMenuGroups) + Object.keys(activitiesByGroupName).forEach((groupName) => { + var existingGroup = getAuthoringMenuGroups().find((g) => g._id === groupName); + + if (!existingGroup) { + menuGroups.push({ + _id: groupName, + label: groupName, + actions: activitiesByGroupName[groupName] + .map((activity) => ({kind: 'activity-based', activity: activity})), }); + } + }); - // actions(except viewing an item) are not allowed for items in legal archive - if (item._type !== 'legal_archive' && scope.allowedActions == null) { - // handle actions from extensions - let extensionActionsByGroupName: {[groupName: string]: Array} = {}; + // actions(except viewing an item) are not allowed for items in legal archive + if (item._type !== 'legal_archive' && scope.allowedActions == null) { + // handle actions from extensions + let extensionActionsByGroupName: {[groupName: string]: Array} = {}; - for (const action of actionsFromExtensions) { - const name = action.groupId ?? 'default'; + for (const action of actionsFromExtensions) { + const name = action.groupId ?? 'default'; - if (extensionActionsByGroupName[name] == null) { - extensionActionsByGroupName[name] = []; - } + if (extensionActionsByGroupName[name] == null) { + extensionActionsByGroupName[name] = []; + } - extensionActionsByGroupName[name].push(action); + extensionActionsByGroupName[name].push(action); + } + + Object.keys(extensionActionsByGroupName).forEach((group) => { + const existingGroup = menuGroups.find((_group) => _group._id === group); + + if (existingGroup == null) { + menuGroups.push({ + _id: group, + label: group, + actions: extensionActionsByGroupName[group] + .map((articleAction) => ({ + kind: 'extension-action', + articleAction: articleAction, + })), + }); + } else { + if (existingGroup.actions == null) { + existingGroup.actions = []; } - Object.keys(extensionActionsByGroupName).forEach((group) => { - const existingGroup = menuGroups.find((_group) => _group._id === group); - - if (existingGroup == null) { - menuGroups.push({ - _id: group, - label: group, - actions: extensionActionsByGroupName[group] - .map((articleAction) => ({ - kind: 'extension-action', - articleAction: articleAction, - })), - }); - } else { - if (existingGroup.actions == null) { - existingGroup.actions = []; - } - - existingGroup.actions = existingGroup.actions.concat( - extensionActionsByGroupName[group] - .map((articleAction) => ({ - kind: 'extension-action', - articleAction: articleAction, - })), - ); - } - }); + existingGroup.actions = existingGroup.actions.concat( + extensionActionsByGroupName[group] + .map((articleAction) => ({ + kind: 'extension-action', + articleAction: articleAction, + })), + ); } - - scope.menuGroups = menuGroups; }); + } + + scope.menuGroups = menuGroups; } /** diff --git a/scripts/apps/search/components/actions-menu/MenuItems.tsx b/scripts/apps/search/components/actions-menu/MenuItems.tsx index d25c0a25ae..3d7d0600f5 100644 --- a/scripts/apps/search/components/actions-menu/MenuItems.tsx +++ b/scripts/apps/search/components/actions-menu/MenuItems.tsx @@ -64,10 +64,8 @@ export default class MenuItems extends React.Component { // actions(except viewing an item) are not allowed for items in legal archive if (this.props.item._type !== 'legal_archive') { - getArticleActionsFromExtensions(this.props.item).then((actions) => { - this.setState({ - actionsFromExtensions: actions, - }); + this.setState({ + actionsFromExtensions: getArticleActionsFromExtensions(this.props.item), }); } else { this.setState({ diff --git a/scripts/core/superdesk-api-helpers.tsx b/scripts/core/superdesk-api-helpers.tsx index a7c8242d46..b1d3b91d71 100644 --- a/scripts/core/superdesk-api-helpers.tsx +++ b/scripts/core/superdesk-api-helpers.tsx @@ -2,7 +2,7 @@ import {IArticle, IAuthoringAction, IExtensionActivationResult} from 'superdesk- import {flatMap} from 'lodash'; import {extensions} from 'appConfig'; -export function getArticleActionsFromExtensions(item: IArticle): Promise> { +export function getArticleActionsFromExtensions(item: IArticle): Array { const actionGetters : Array = flatMap( @@ -10,8 +10,7 @@ export function getArticleActionsFromExtensions(item: IArticle): Promise extension.activationResult.contributions?.entities?.article?.getActions ?? [], ); - return Promise.all(actionGetters.map((getPromise) => getPromise(item))) - .then((res) => { - return flatMap(res); - }); + return flatMap( + actionGetters.map((getAction) => getAction(item)), + ); } diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index 6ba8970ade..74b4a8a3f4 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -181,7 +181,7 @@ declare module 'superdesk-api' { authoringStorage: IAuthoringStorage; storageAdapter: IStorageAdapter; fieldsAdapter: IFieldsAdapter; - getActions?(options: IExposedFromAuthoring): Promise>; // three dots menu actions + getActions?(options: IExposedFromAuthoring): Array; // three dots menu actions getInlineToolbarActions(options: IExposedFromAuthoring): IAuthoringOptions; getAuthoringTopBarWidgets( options: IExposedFromAuthoring, @@ -433,6 +433,9 @@ declare module 'superdesk-api' { */ export type IDisplayPriority = number; + /** + * TODO: add a comment, specify data shape and behavior + */ export interface IKeyBindings { [key: string]: () => void; } @@ -631,7 +634,7 @@ declare module 'superdesk-api' { article: IArticle, contentProfile: IContentProfileV2, fieldsData: import('immutable').Map, - ): Promise>; + ): Array; mediaActions?: Array>; pages?: Array; @@ -645,7 +648,7 @@ declare module 'superdesk-api' { }; entities?: { article?: { - getActions?(article: IArticle): Promise>; + getActions?(article: IArticle): Array; getActionsBulk?(articles: Array): Promise>; onPatchBefore?(id: IArticle['_id'], patch: Partial, dangerousOptions?: IDangerousArticlePatchingOptions,): Promise>; // can alter patch(immutably), can cancel patching onSpike?(item: IArticle): Promise; From ea8ec29cc4e0eec15794e6a7a4a510fa0fa07ed1 Mon Sep 17 00:00:00 2001 From: dzonidoo Date: Thu, 2 Feb 2023 17:32:01 +0100 Subject: [PATCH 07/11] Finished keybindings basic functionality. --- .../authoring-integration-wrapper.tsx | 15 +++++++++ .../apps/authoring-react/authoring-react.tsx | 32 +++++++++++++++---- .../apps/authoring-react/with-keybindings.tsx | 1 - scripts/apps/highlights/views/menu.html | 1 - scripts/core/superdesk-api.d.ts | 4 ++- 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx index 00e3283a29..6af35dd36d 100644 --- a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx +++ b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx @@ -193,6 +193,18 @@ const getHighlightsAction = (getItem: () => IArticle): IAuthoringAction => { ); }) ), + keyBindings: { + 'ctrl+shift+h': () => { + showModal(({closeModal}) => { + return ( + + ); + }); + }, + }, }; }; @@ -470,6 +482,9 @@ export class AuthoringIntegrationWrapper extends React.PureComponent { + return getWidgetsFromExtensions(article)[index].label; + }} /> ); }} diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index a871fd3cec..d2a41d1613 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -1141,13 +1141,32 @@ export class AuthoringReact extends React.PureCo return [...coreActions, ...actions]; })(); - const keyBindingsFromAuthoringActions: IKeyBindings = - authoringActions.reduce((acc, action) => { - return { - ...acc, - ...(action.keyBindings ?? {}), + const keyBindingsFromAuthoringActions: IKeyBindings = authoringActions.reduce((acc, action) => { + return { + ...acc, + ...(action.keyBindings ?? {}), + }; + }, {}); + + const widgetsCount = this.props.getSidebar(exposed).props.items.length; + + const widgetKeybindings: IKeyBindings = {}; + + for (let i = 0; i < widgetsCount; i++) { + widgetKeybindings[`ctrl+alt+${i + 1}`] = () => { + const nextWidgetName: string = this.props.getSideWidgetNameAtIndex(exposed.item, i); + + const nextState: IStateLoaded = { + ...state, + openWidget: { + name: nextWidgetName, + pinned: state.openWidget?.pinned ?? false, + }, }; - }, {}); + + this.setState(nextState); + }; + } const toolbar1Widgets: Array> = [ ...authoringOptions.actions, @@ -1204,6 +1223,7 @@ export class AuthoringReact extends React.PureCo ...preview.keybindings, ...getKeyBindingsFromActions(authoringOptions.actions), ...keyBindingsFromAuthoringActions, + ...widgetKeybindings, } } > diff --git a/scripts/apps/authoring-react/with-keybindings.tsx b/scripts/apps/authoring-react/with-keybindings.tsx index 4a4b0500eb..f1e26af576 100644 --- a/scripts/apps/authoring-react/with-keybindings.tsx +++ b/scripts/apps/authoring-react/with-keybindings.tsx @@ -29,7 +29,6 @@ export class WithKeyBindings extends React.PureComponent { if (matchingKeyBinding != null) { event.stopPropagation(); - this.props.keyBindings[matchingKeyBinding](); } } diff --git a/scripts/apps/highlights/views/menu.html b/scripts/apps/highlights/views/menu.html index d67ec62139..8a2d28d79e 100644 --- a/scripts/apps/highlights/views/menu.html +++ b/scripts/apps/highlights/views/menu.html @@ -5,4 +5,3 @@ sd-hotkey-callback="highlightsHotkey" dropdown sd-package-highlights-dropdown> - diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index 74b4a8a3f4..584d9e6897 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -198,6 +198,8 @@ declare module 'superdesk-api' { onFieldChange?(fieldId: string, fieldsData: IFieldsData): IFieldsData; validateBeforeSaving?: boolean; // will block saving if invalid. defaults to true + + getSideWidgetNameAtIndex?(item: T, index: number): string; } // AUTHORING-REACT FIELD TYPES - attachments @@ -434,7 +436,7 @@ declare module 'superdesk-api' { export type IDisplayPriority = number; /** - * TODO: add a comment, specify data shape and behavior + * EXAMPLE: 'ctrl+shift+s': () => save():' */ export interface IKeyBindings { [key: string]: () => void; From ecebafa294a761baaeb8c8469f6f8fa8874d1a2c Mon Sep 17 00:00:00 2001 From: dzonidoo Date: Fri, 3 Feb 2023 15:43:51 +0100 Subject: [PATCH 08/11] FIxed issue with dupicated code, and improve conditions for locking item with keybindings. --- package-lock.json | 18 +++--- .../authoring-angular-integration.tsx | 5 +- .../authoring-integration-wrapper.tsx | 29 ++++----- .../apps/authoring-react/authoring-react.tsx | 63 ++++++++++--------- .../apps/authoring-react/multi-edit-modal.tsx | 1 + .../subcomponents/lock-info.tsx | 3 +- .../apps/authoring-react/with-keybindings.tsx | 12 ++-- scripts/core/superdesk-api.d.ts | 8 +-- scripts/index.ts | 2 +- 9 files changed, 72 insertions(+), 69 deletions(-) diff --git a/package-lock.json b/package-lock.json index cee8643da8..f58d07e913 100644 --- a/package-lock.json +++ b/package-lock.json @@ -597,9 +597,9 @@ } }, "@superdesk/primereact": { - "version": "5.0.2-8", - "resolved": "https://registry.npmjs.org/@superdesk/primereact/-/primereact-5.0.2-8.tgz", - "integrity": "sha512-KsqnypKVtAcEWsk0pQS5uZRNN5m7hDVH5RIkLeTO4YQU492pVRs3AhNkfzLKJENVU1D8VUrsJKG/T1wXnFafTQ==", + "version": "5.0.2-7", + "resolved": "https://registry.npmjs.org/@superdesk/primereact/-/primereact-5.0.2-7.tgz", + "integrity": "sha512-pgA1Fu+3kifz33nf8zUu40WVJ/aKaIKVfyZwFbS5WqGAZlyut2Ro2sA5iaS0mYHezR/bfQ7GvCiZP4Y8zLx6sw==", "requires": { "react-transition-group": "^4.4.1" }, @@ -15955,9 +15955,9 @@ } }, "superdesk-ui-framework": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/superdesk-ui-framework/-/superdesk-ui-framework-3.0.2.tgz", - "integrity": "sha512-0aQZ+7oTs4O5SNckQSevhAKNl5wL5Fb35cR1JwtsSZLj+x8l6ry7gAyCTt9Z9MqVl5IqfzuDd4f3OYAeZ/Vc4g==", + "version": "3.0.1-beta.28", + "resolved": "https://registry.npmjs.org/superdesk-ui-framework/-/superdesk-ui-framework-3.0.1-beta.28.tgz", + "integrity": "sha512-dUi6GK6OQTN8RCs/ejxegfHW8e/3KVw7h53OMukXQ44AqKBiz0+duCHeYHjh6ZUo/9Pd91uU2rji8naqsyYOvQ==", "requires": { "@material-ui/lab": "^4.0.0-alpha.56", "@popperjs/core": "^2.4.0", @@ -15975,9 +15975,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz", - "integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==" + "version": "14.18.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", + "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==" }, "moment": { "version": "2.29.4", diff --git a/scripts/apps/authoring-react/authoring-angular-integration.tsx b/scripts/apps/authoring-react/authoring-angular-integration.tsx index 7a40005d82..570f3a3352 100644 --- a/scripts/apps/authoring-react/authoring-angular-integration.tsx +++ b/scripts/apps/authoring-react/authoring-angular-integration.tsx @@ -207,11 +207,14 @@ function getInlineToolbarActions(options: IExposedFromAuthoring): IAut unlock={() => { stealLock(); }} + isLockedInOtherSession={(article) => sdApi.article.isLockedInOtherSession(article)} /> ), keyBindings: { 'ctrl+shift+u': () => { - stealLock(); + if (sdApi.article.isLockedInOtherSession(item)) { + stealLock(); + } }, }, availableOffline: false, diff --git a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx index 6af35dd36d..c879dfa75a 100644 --- a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx +++ b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx @@ -181,28 +181,25 @@ const getExportModal = ( }); const getHighlightsAction = (getItem: () => IArticle): IAuthoringAction => { + const showHighlightsModal = () => { + showModal(({closeModal}) => { + return ( + + ); + }); + }; + return { label: gettext('Highlights'), onTrigger: () => ( - showModal(({closeModal}) => { - return ( - - ); - }) + showHighlightsModal() ), keyBindings: { 'ctrl+shift+h': () => { - showModal(({closeModal}) => { - return ( - - ); - }); + showHighlightsModal(); }, }, }; diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index d2a41d1613..d20896d199 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -1184,29 +1184,41 @@ export class AuthoringReact extends React.PureCo const pinned = state.openWidget?.pinned === true; - const preview = { - jsxButton: () => { - return ( - { - previewAuthoringEntity( - state.profile, - state.fieldsDataWithChanges, - ); - }} - /> + const printPreviewAction = (() => { + const execute = () => { + previewAuthoringEntity( + state.profile, + state.fieldsDataWithChanges, ); - }, - keybindings: { - 'ctrl+shift+i': () => { - previewAuthoringEntity( - state.profile, - state.fieldsDataWithChanges, + }; + + const preview = { + jsxButton: () => { + return ( + { + execute(); + }} + /> ); }, - }, + keybindings: { + 'ctrl+shift+i': () => { + execute(); + }, + }, + }; + + return preview; + })(); + + const ObjectOfKeybindings = { + ...printPreviewAction.keybindings, + ...getKeyBindingsFromActions(authoringOptions.actions), + ...keyBindingsFromAuthoringActions, + ...widgetKeybindings, }; return ( @@ -1218,14 +1230,7 @@ export class AuthoringReact extends React.PureCo } {(panelState, panelActions) => { @@ -1266,7 +1271,7 @@ export class AuthoringReact extends React.PureCo - {preview.jsxButton()} + {printPreviewAction.jsxButton()} )} diff --git a/scripts/apps/authoring-react/multi-edit-modal.tsx b/scripts/apps/authoring-react/multi-edit-modal.tsx index 6b57c944cd..5e8aaadd26 100644 --- a/scripts/apps/authoring-react/multi-edit-modal.tsx +++ b/scripts/apps/authoring-react/multi-edit-modal.tsx @@ -141,6 +141,7 @@ export class MultiEditModal extends React.PureComponent { unlock={() => { stealLock(); }} + isLockedInOtherSession={(article) => sdApi.article.isLockedInOtherSession(article)} /> ), availableOffline: false, diff --git a/scripts/apps/authoring-react/subcomponents/lock-info.tsx b/scripts/apps/authoring-react/subcomponents/lock-info.tsx index 4589c2eebc..e8d3282e09 100644 --- a/scripts/apps/authoring-react/subcomponents/lock-info.tsx +++ b/scripts/apps/authoring-react/subcomponents/lock-info.tsx @@ -10,6 +10,7 @@ import {store} from 'core/data'; interface IProps { article: IArticle; unlock(): void; + isLockedInOtherSession(article: IArticle): boolean; } interface IState { @@ -63,7 +64,7 @@ class LockInfoComponent extends React.PureComponent { export class LockInfo extends React.PureComponent { render() { - if (sdApi.article.isLockedInOtherSession(this.props.article) !== true) { + if (this.props.isLockedInOtherSession(this.props.article) !== true) { return null; } diff --git a/scripts/apps/authoring-react/with-keybindings.tsx b/scripts/apps/authoring-react/with-keybindings.tsx index f1e26af576..97793fc5ba 100644 --- a/scripts/apps/authoring-react/with-keybindings.tsx +++ b/scripts/apps/authoring-react/with-keybindings.tsx @@ -1,10 +1,8 @@ - import React from 'react'; +import {IKeyBindings} from 'superdesk-api'; interface IProps { - keyBindings: { - [key: string]: () => void; - }; + keyBindings: IKeyBindings; } export class WithKeyBindings extends React.PureComponent { @@ -22,9 +20,9 @@ export class WithKeyBindings extends React.PureComponent { const ctrlRequired = split.includes('ctrl'); return (altRequired ? event.altKey : !event.altKey) - && (shiftRequired ? event.shiftKey : !event.shiftKey) - && (ctrlRequired ? event.ctrlKey : !event.ctrlKey) - && (split[split.length - 1] === event.key.toLowerCase()); + && (shiftRequired ? event.shiftKey : !event.shiftKey) + && (ctrlRequired ? event.ctrlKey : !event.ctrlKey) + && (split[split.length - 1] === event.key.toLowerCase()); }); if (matchingKeyBinding != null) { diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index 584d9e6897..f9325aea10 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -162,9 +162,7 @@ declare module 'superdesk-api' { availableOffline: boolean; priority: IDisplayPriority; group: 'start' | 'middle' | 'end'; - keyBindings?: { - [key: string]: () => void; - }; + keyBindings?: IKeyBindings; } interface IPropsAuthoring { @@ -199,7 +197,7 @@ declare module 'superdesk-api' { validateBeforeSaving?: boolean; // will block saving if invalid. defaults to true - getSideWidgetNameAtIndex?(item: T, index: number): string; + getSideWidgetNameAtIndex(item: T, index: number): string; } // AUTHORING-REACT FIELD TYPES - attachments @@ -436,7 +434,7 @@ declare module 'superdesk-api' { export type IDisplayPriority = number; /** - * EXAMPLE: 'ctrl+shift+s': () => save():' + * EXAMPLE: `{'ctrl+shift+s': () => save()}` */ export interface IKeyBindings { [key: string]: () => void; diff --git a/scripts/index.ts b/scripts/index.ts index 5039af1689..adc031b930 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -253,4 +253,4 @@ setTimeout(() => { if (started !== true) { startApp([], {}); } -}, 501); +}, 500); From 0587e92fdafa0c6858e655766535a549495482de Mon Sep 17 00:00:00 2001 From: dzonidoo Date: Tue, 7 Feb 2023 12:29:43 +0100 Subject: [PATCH 09/11] Naming Impovement --- scripts/apps/authoring-react/authoring-react.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index d20896d199..7f7ead0005 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -1214,7 +1214,7 @@ export class AuthoringReact extends React.PureCo return preview; })(); - const ObjectOfKeybindings = { + const allKeyBindings: IKeyBindings = { ...printPreviewAction.keybindings, ...getKeyBindingsFromActions(authoringOptions.actions), ...keyBindingsFromAuthoringActions, @@ -1229,9 +1229,7 @@ export class AuthoringReact extends React.PureCo ) } - + {(panelState, panelActions) => { return ( From d3bf13deb37bb4f575d93ade62bb062e9056d071 Mon Sep 17 00:00:00 2001 From: dzonidoo Date: Tue, 7 Feb 2023 12:46:18 +0100 Subject: [PATCH 10/11] FIxed Interface --- scripts/apps/authoring-react/authoring-react.tsx | 2 +- .../subcomponents/authoring-actions-menu.tsx | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index 7f7ead0005..3bcd6865c7 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -1175,7 +1175,7 @@ export class AuthoringReact extends React.PureCo priority: 0.4, component: () => { return ( - Promise.resolve(authoringActions)} /> + authoringActions} /> ); }, availableOffline: true, diff --git a/scripts/apps/authoring-react/subcomponents/authoring-actions-menu.tsx b/scripts/apps/authoring-react/subcomponents/authoring-actions-menu.tsx index e5c30faaa9..2a28983be6 100644 --- a/scripts/apps/authoring-react/subcomponents/authoring-actions-menu.tsx +++ b/scripts/apps/authoring-react/subcomponents/authoring-actions-menu.tsx @@ -6,7 +6,7 @@ import {gettext} from 'core/utils'; import {IMenuItem} from 'superdesk-ui-framework/react/components/Menu'; interface IProps { - getActions: () => Promise>; // TODO: convert to sync interface + getActions: () => Array; } interface IState { @@ -25,9 +25,7 @@ export class AuthoringActionsMenu extends React.PureComponent { } getActions() { - this.props.getActions().then((actions) => { - this.setState({actions}); - }); + this.setState({actions: this.props.getActions()}); } render() { From 4d790ed3a896efc0a7271d7f2e296522272bd1c5 Mon Sep 17 00:00:00 2001 From: dzonidoo Date: Tue, 7 Feb 2023 12:57:57 +0100 Subject: [PATCH 11/11] Added return type for getKeyBindigsFromActions function --- scripts/apps/authoring-react/authoring-react.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index 3bcd6865c7..c8239b0c63 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -192,7 +192,7 @@ function getInitialState( return initialState; } -function getKeyBindingsFromActions(actions: Array>) { +function getKeyBindingsFromActions(actions: Array>): IKeyBindings { return actions .filter((action) => action.keyBindings != null) .reduce((acc, action) => {