From 72d86791314932ada2dcc2bda20a39808f7cbc97 Mon Sep 17 00:00:00 2001 From: Konstantin Markov Date: Fri, 1 Nov 2024 13:36:39 +0200 Subject: [PATCH] =?UTF-8?q?Cosmetic=20metadata=20widget=20changes,=20persi?= =?UTF-8?q?st=20changes=20on=20full=20width=20toggl=E2=80=A6=20(#4667)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 12 +- package.json | 2 +- scripts/api/content-profiles.ts | 2 + .../article-widgets/metadata/metadata.tsx | 232 ++++++++--------- .../authoring-angular-integration.tsx | 6 +- .../authoring-integration-wrapper.tsx | 14 +- .../apps/authoring-react/authoring-react.tsx | 241 ++++++++++-------- .../field-adapters/authors.tsx | 2 +- .../apps/authoring-react/multi-edit-modal.tsx | 56 ++-- .../content-profile-dropdown.tsx | 62 +++++ .../subcomponents/created-modified-info.tsx | 1 + .../components/HeaderComponent.tsx | 4 +- scripts/core/superdesk-api.d.ts | 10 +- .../src/rundown-templates/template-edit.tsx | 2 +- .../src/rundowns/rundown-view-edit.tsx | 2 +- 15 files changed, 358 insertions(+), 290 deletions(-) create mode 100644 scripts/apps/authoring-react/subcomponents/content-profile-dropdown.tsx diff --git a/package-lock.json b/package-lock.json index 086e715757..b798a3f603 100644 --- a/package-lock.json +++ b/package-lock.json @@ -586,9 +586,9 @@ }, "dependencies": { "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" } } }, @@ -13955,9 +13955,9 @@ } }, "superdesk-ui-framework": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/superdesk-ui-framework/-/superdesk-ui-framework-3.1.23.tgz", - "integrity": "sha512-0p7polXtSz/0jGizk/y/GIVPAazoPBR4U2PIE4BOx6xxRMH1phbp/c9hqbH46JgDOFcznuyJS/82XWbMeDgGfw==", + "version": "3.1.28", + "resolved": "https://registry.npmjs.org/superdesk-ui-framework/-/superdesk-ui-framework-3.1.28.tgz", + "integrity": "sha512-H/DE7gcJviHE5jR2vhuiTB2WLUxmpFstomFoEHWE5zGDqkcqGyPZUwRrwDTvtZLnMuMZXj1czeShbhJr9AxMhg==", "requires": { "@popperjs/core": "^2.4.0", "@superdesk/common": "0.0.28", diff --git a/package.json b/package.json index ca1c18b8e1..4ec785dce4 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "sass-loader": "6.0.6", "shortid": "2.2.8", "style-loader": "0.20.2", - "superdesk-ui-framework": "^3.1.23", + "superdesk-ui-framework": "^3.1.28", "ts-loader": "3.5.0", "typescript": "4.9.5", "uuid": "8.3.1", diff --git a/scripts/api/content-profiles.ts b/scripts/api/content-profiles.ts index f14cc8f788..1da84b83a3 100644 --- a/scripts/api/content-profiles.ts +++ b/scripts/api/content-profiles.ts @@ -3,8 +3,10 @@ import {dataStore} from 'data-store'; interface IContentProfilesApi { get(id: IContentProfile['_id']): IContentProfile; + getAll(): Array; } export const contentProfiles: IContentProfilesApi = { get: (id) => dataStore.contentProfiles.get(id), + getAll: () => dataStore.contentProfiles.toArray(), }; diff --git a/scripts/apps/authoring-react/article-widgets/metadata/metadata.tsx b/scripts/apps/authoring-react/article-widgets/metadata/metadata.tsx index 62e7276fab..ce12465f5d 100644 --- a/scripts/apps/authoring-react/article-widgets/metadata/metadata.tsx +++ b/scripts/apps/authoring-react/article-widgets/metadata/metadata.tsx @@ -47,7 +47,6 @@ class MetadataWidget extends React.PureComponent v.schema_field).includes('genre') === false; + const hasPlace = allVocabularies.map((v) => v.schema_field).includes('place') === false; return ( )} - { - <> - - - {gettext('State').toUpperCase()} - - - - {article.embargo && ( - + {<> + + + {gettext('State').toUpperCase()} + + + + {article.embargo && ( + - - - } + + + } {ingest_provider != null && ( )} - { - (ingest_provider_sequence?.length ?? 0) > 0 && ( - - ) - } + {(ingest_provider_sequence?.length ?? 0) > 0 && ( + + )} {expiry && ( } + value={} /> )} @@ -303,67 +298,52 @@ class MetadataWidget extends React.PureComponent - { - signal && ( - - {(signal.map(({name, qcode}) => ( - {name ?? qcode} - )))} - - )} - /> - ) - } + {signal && ( + + {(signal.map(({name, qcode}) => ( + {name ?? qcode} + )))} + + )} + /> + )} + + {anpa_category?.name != null && ( + + )} - { - anpa_category?.name != null && ( + {allVocabularies.filter((cv) => article[cv.schema_field] != null).toArray() + .map((vocabulary) => ( - ) + )) } - { - allVocabularies - .filter((cv) => article[cv.schema_field] != null) - .toArray() - .map((vocabulary) => ( - - )) - } - - { - (genre.length ?? 0) > 0 - && allVocabularies.map((v) => v.schema_field).includes('genre') === false - && ( - - ) - } + {(genre?.length ?? 0) > 0 && hasGenre && ( + + )} - { - (place.length ?? 0) > 0 - && allVocabularies.map((v) => v.schema_field).includes('place') === false - && ( - - ) - } + {(place?.length ?? 0) > 0 && hasPlace && ( + + )} {(ednote?.length ?? 0) > 0 && } @@ -438,22 +418,16 @@ class MetadataWidget extends React.PureComponent - { - renditions?.original != null && ( - - ) - } + {renditions?.original != null && ( + + )} - { - article.type === 'picture' - && article.archive_description !== article.description_text - && ( - - ) - } + {article.type === 'picture' && article.archive_description !== article.description_text && ( + + )} )} /> diff --git a/scripts/apps/authoring-react/authoring-angular-integration.tsx b/scripts/apps/authoring-react/authoring-angular-integration.tsx index 83f55ff317..bd182e0267 100644 --- a/scripts/apps/authoring-react/authoring-angular-integration.tsx +++ b/scripts/apps/authoring-react/authoring-angular-integration.tsx @@ -145,7 +145,11 @@ function getInlineToolbarActions( priority: 0.1, component: () => ( setFullWidth()} + setFullWidth={() => { + options.authoringStorage.autosave.flush().then(() => { + setFullWidth(); + }); + }} fullWidth={fullWidth} /> ), diff --git a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx index a92c01da83..157c7f9acf 100644 --- a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx +++ b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx @@ -43,6 +43,7 @@ import {WidgetStatePersistenceHOC, widgetState} from './widget-persistance-hoc'; import {PINNED_WIDGET_USER_PREFERENCE_SETTINGS, closedIntentionally} from 'apps/authoring/widgets/widgets'; import {AuthoringIntegrationWrapperSidebar} from './authoring-integration-wrapper-sidebar'; import {assertNever} from 'core/helpers/typescript-helpers'; +import {ContentProfileDropdown} from './subcomponents/content-profile-dropdown'; export function getWidgetsFromExtensions(article: IArticle): Array { return Object.values(extensions) @@ -51,7 +52,12 @@ export function getWidgetsFromExtensions(article: IArticle): Array a.order - b.order); } -const defaultToolbarItems: Array> = [CreatedModifiedInfo]; +const defaultToolbarItems: Array void; +}>> = [ + CreatedModifiedInfo, +]; interface IProps { itemId: IArticle['_id']; @@ -317,10 +323,8 @@ export class AuthoringIntegrationWrapper extends React.PureComponent activationResult?.contributions?.authoringTopbar2Widgets ?? []); - const secondaryToolbarWidgetsReady: Array> = - defaultToolbarItems.concat(secondaryToolbarWidgetsFromExtensions).map( - (Component) => (props: {item: IArticle}) => , - ); + const secondaryToolbarWidgetsReady = defaultToolbarItems.concat(secondaryToolbarWidgetsFromExtensions) + .map((Component) => (props) => ); return ( diff --git a/scripts/apps/authoring-react/authoring-react.tsx b/scripts/apps/authoring-react/authoring-react.tsx index 1cc4f0befe..08264f2c62 100644 --- a/scripts/apps/authoring-react/authoring-react.tsx +++ b/scripts/apps/authoring-react/authoring-react.tsx @@ -58,6 +58,7 @@ import {IFontSizeOption, ITheme, ProofreadingThemeModal} from './toolbar/proofre import {showModal} from '@superdesk/common'; import ng from 'core/services/ng'; import {focusFirstChildInput} from 'utils/focus-first-child-input'; +import {ContentProfileDropdown} from './subcomponents/content-profile-dropdown'; export function getFieldsData( item: T, @@ -1146,7 +1147,11 @@ export class AuthoringReact extends React.PureCo * and when outside code sends updated `{item:T }` there is not other way for authoring-react * to apply it to `fieldsData`, but to re-initialize. */ - reinitialize(state: IStateLoaded, itemWithUpdates: T) { + reinitialize( + state: IStateLoaded, + itemWithUpdates: T, + newProfile?: IContentProfileV2, + ) { const item: { saved: T; autosaved: T; @@ -1157,7 +1162,7 @@ export class AuthoringReact extends React.PureCo this.setState(getInitialState( item, - state.profile, + newProfile ?? state.profile, state.userPreferencesForFields, state.spellcheckerEnabled, this.props.fieldsAdapter, @@ -1411,6 +1416,13 @@ export class AuthoringReact extends React.PureCo }, }; + const onChangeSideWidget = (item: T) => { + authoringStorage.getContentProfile(item, this.props.fieldsAdapter) + .then((res) => { + this.reinitialize(state, item, res); + }); + }; + return (
{ @@ -1421,85 +1433,92 @@ export class AuthoringReact extends React.PureCo - {(panelState, panelActions) => { - return ( - - ( + + + + ) + } + main={( + + {this.props.secondaryToolbarWidgets.map((Component, i) => ( + { + if (this.hasUnsavedChanges()) { + exposed.handleUnsavedChanges().then(() => { + onChangeSideWidget(item); + }); + } else { + onChangeSideWidget(item); + } + }} + item={state.itemWithChanges} /> - - ) - } - main={( - -
+ {printPreviewAction.jsxButton()} + {this.props.themingEnabled === true && ( + <> + { + this.setState({ + ...state, + proofreadingEnabled: + !state.proofreadingEnabled, + }); + }} + /> + { + this.showThemeConfigModal(state); + }} + /> + + )} + + +
+ )} + headerPadding={{top: 8}} + authoringHeader={( +
+
+ { + if (this.hasUnsavedChanges()) { + exposed.handleUnsavedChanges().then(() => { + onChangeSideWidget(item); + }); + } else { + onChangeSideWidget(item); + } }} - > - { - this.props.secondaryToolbarWidgets - .map((Component, i) => { - return ( - - ); - }) - } -
- - - - {printPreviewAction.jsxButton()} - - {this.props.themingEnabled === true && ( - <> - { - this.setState({ - ...state, - proofreadingEnabled: - !state.proofreadingEnabled, - }); - }} - /> - { - this.showThemeConfigModal(state); - }} - /> - - )} - - - - - )} - authoringHeader={( + /> +
extends React.PureCo item={state.itemWithChanges} computeLatestEntity={this.computeLatestEntity} /> - )} - > - { - this.reinitialize(state, item); - }} - language={getLanguage(state.itemWithChanges)} - userPreferencesForFields={state.userPreferencesForFields} - setUserPreferencesForFields={this.setUserPreferences} - getVocabularyItems={this.getVocabularyItems} - toggledFields={state.toggledFields} - toggleField={this.toggleField} - readOnly={readOnly} - validationErrors={state.validationErrors} - item={state.itemWithChanges} - computeLatestEntity={this.computeLatestEntity} - /> -
- )} - sideOverlay={!pinned && OpenWidgetComponent != null && OpenWidgetComponent} - sideOverlayOpen={!pinned && OpenWidgetComponent != null} - sidePanel={pinned && OpenWidgetComponent != null && OpenWidgetComponent} - sidePanelOpen={pinned && OpenWidgetComponent != null} - sideBar={this.props.getSidebar?.(exposed)} - /> - ); - }} +
+ )} + > + { + this.reinitialize(state, item); + }} + language={getLanguage(state.itemWithChanges)} + userPreferencesForFields={state.userPreferencesForFields} + setUserPreferencesForFields={this.setUserPreferences} + getVocabularyItems={this.getVocabularyItems} + toggledFields={state.toggledFields} + toggleField={this.toggleField} + readOnly={readOnly} + validationErrors={state.validationErrors} + item={state.itemWithChanges} + computeLatestEntity={this.computeLatestEntity} + /> + + )} + 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/field-adapters/authors.tsx b/scripts/apps/authoring-react/field-adapters/authors.tsx index b39cbb4ec4..dc8b3eab50 100644 --- a/scripts/apps/authoring-react/field-adapters/authors.tsx +++ b/scripts/apps/authoring-react/field-adapters/authors.tsx @@ -51,7 +51,7 @@ export const authors: IFieldAdapter = { const metadata = ng.get('metadata'); const authorsTree = arrayToTree( - metadata.values.authors as Array, + (metadata.values.authors ?? []) as Array, (item) => getId(item), (item) => { if (isAuthorRole(item)) { diff --git a/scripts/apps/authoring-react/multi-edit-modal.tsx b/scripts/apps/authoring-react/multi-edit-modal.tsx index 8a15e9751f..9b822df5d9 100644 --- a/scripts/apps/authoring-react/multi-edit-modal.tsx +++ b/scripts/apps/authoring-react/multi-edit-modal.tsx @@ -249,36 +249,34 @@ export class MultiEditModal extends React.PureComponent { }) } - { - availableArticles.length > 0 && ( -
- { - const leaf: IMenuItem = { - onClick: () => this.add(a._id), - label: getArticleLabel(a), - }; + {availableArticles.length > 0 && ( +
+ { + const leaf: IMenuItem = { + onClick: () => this.add(a._id), + label: getArticleLabel(a), + }; - return leaf; - })} - > - {(toggle) => ( - -
- ) - } + return leaf; + })} + > + {(toggle) => ( +
+
+ )} ); diff --git a/scripts/apps/authoring-react/subcomponents/content-profile-dropdown.tsx b/scripts/apps/authoring-react/subcomponents/content-profile-dropdown.tsx new file mode 100644 index 0000000000..3c63738b55 --- /dev/null +++ b/scripts/apps/authoring-react/subcomponents/content-profile-dropdown.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import {IArticle, IContentProfile} from 'superdesk-api'; +import {gettext} from 'core/utils'; +import {Spacer} from 'superdesk-ui-framework/react'; +import {sdApi} from 'api'; + +interface IProps { + item: T; + reinitialize: (itemWithChanges: T) => void; +} + +interface IState { + profiles: Array; + selectedProfileId: string; +} + +export class ContentProfileDropdown extends React.PureComponent, IState> { + constructor(props: IProps) { + super(props); + + const allProfiles = sdApi.contentProfiles.getAll().filter((x) => x.enabled === true && x.type === 'text'); + + this.state = { + profiles: allProfiles, + selectedProfileId: allProfiles.find((x) => x._id === (this.props.item as IArticle)?.profile)._id ?? '', + }; + } + + render() { + return ( + + {gettext('PROFILE')} +
+ +
+
+ ); + } +} diff --git a/scripts/apps/authoring-react/subcomponents/created-modified-info.tsx b/scripts/apps/authoring-react/subcomponents/created-modified-info.tsx index e9abb7958e..b63743c6a4 100644 --- a/scripts/apps/authoring-react/subcomponents/created-modified-info.tsx +++ b/scripts/apps/authoring-react/subcomponents/created-modified-info.tsx @@ -5,6 +5,7 @@ import {ModifiedInfo} from 'apps/authoring/authoring/modified-info'; interface IProps { article: IArticle; + reinitialize: () => void; } export class CreatedModifiedInfo extends React.PureComponent { diff --git a/scripts/apps/master-desk/components/HeaderComponent.tsx b/scripts/apps/master-desk/components/HeaderComponent.tsx index c80035ca37..5976a1ad3e 100644 --- a/scripts/apps/master-desk/components/HeaderComponent.tsx +++ b/scripts/apps/master-desk/components/HeaderComponent.tsx @@ -5,13 +5,11 @@ import { CheckButtonGroup, RadioButtonGroup, Switch, - Dropdown, - NavButton, ButtonGroup, Button, } from 'superdesk-ui-framework/react'; import {IMenuItem} from 'superdesk-ui-framework/app-typescript/components/Dropdown'; -import {IMasterDeskTab, IMasterDeskViews, getLabelForMasterDeskTab, USER_PREFERENCE_SETTINGS} from '../MasterDesk'; +import {IMasterDeskTab, getLabelForMasterDeskTab, USER_PREFERENCE_SETTINGS} from '../MasterDesk'; import {gettext} from 'core/utils'; interface IProps { diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index c8b087e7cb..a303c51c82 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -210,7 +210,10 @@ declare module 'superdesk-api' { // used for side widgets getSidePanel?(options: IExposedFromAuthoring, readOnly: boolean): React.ReactNode; - secondaryToolbarWidgets: Array>; + secondaryToolbarWidgets: Array>; disableWidgetPinning?: boolean; // defaults to false @@ -769,7 +772,10 @@ declare module 'superdesk-api' { /** * Display custom components in the second toolbar in authoring panel */ - authoringTopbar2Widgets?: Array>; + authoringTopbar2Widgets?: Array void; + }>>; authoringSideWidgets?: Array; diff --git a/scripts/extensions/broadcasting/src/rundown-templates/template-edit.tsx b/scripts/extensions/broadcasting/src/rundown-templates/template-edit.tsx index a1e043fd28..ca0b2e0321 100644 --- a/scripts/extensions/broadcasting/src/rundown-templates/template-edit.tsx +++ b/scripts/extensions/broadcasting/src/rundown-templates/template-edit.tsx @@ -300,7 +300,7 @@ export class RundownTemplateViewEdit extends React.PureComponent diff --git a/scripts/extensions/broadcasting/src/rundowns/rundown-view-edit.tsx b/scripts/extensions/broadcasting/src/rundowns/rundown-view-edit.tsx index 51c2a20e8d..5b2d7c4541 100644 --- a/scripts/extensions/broadcasting/src/rundowns/rundown-view-edit.tsx +++ b/scripts/extensions/broadcasting/src/rundowns/rundown-view-edit.tsx @@ -480,7 +480,7 @@ export class RundownViewEditComponent extends React.PureComponent