From 2b47b022d484ae53301aef2384862643935aa5f9 Mon Sep 17 00:00:00 2001 From: Konstantin Markov Date: Wed, 29 May 2024 12:34:29 +0300 Subject: [PATCH 01/16] First draft implementation --- scripts/apps/monitoring/directives/MonitoringView.ts | 4 +++- .../apps/translations/directives/TranslationDropdown.ts | 2 ++ scripts/core/get-superdesk-api-implementation.tsx | 2 ++ scripts/core/superdesk-api.d.ts | 8 ++++++++ scripts/extensions/ai-widget/src/configuration.ts | 7 ++++++- scripts/extensions/ai-widget/src/extension.ts | 2 +- 6 files changed, 22 insertions(+), 3 deletions(-) diff --git a/scripts/apps/monitoring/directives/MonitoringView.ts b/scripts/apps/monitoring/directives/MonitoringView.ts index a6a70cfce9..fd2282534b 100644 --- a/scripts/apps/monitoring/directives/MonitoringView.ts +++ b/scripts/apps/monitoring/directives/MonitoringView.ts @@ -1,10 +1,12 @@ import _ from 'lodash'; import {gettext} from 'core/utils'; import {AuthoringWorkspaceService} from 'apps/authoring/authoring/services/AuthoringWorkspaceService'; -import {IDesk, ISuperdeskQuery, IUser} from 'superdesk-api'; +import {IDesk, ISuperdeskQuery, IUser, IOnTranslateActions} from 'superdesk-api'; const PAGE_SIZE = 50; +export const onTranslateActions: IOnTranslateActions = {}; + interface IScope extends ng.IScope { contentStyle: {}; monitoringItemsLoading: boolean; diff --git a/scripts/apps/translations/directives/TranslationDropdown.ts b/scripts/apps/translations/directives/TranslationDropdown.ts index 13c82d8d3e..aa683a548b 100644 --- a/scripts/apps/translations/directives/TranslationDropdown.ts +++ b/scripts/apps/translations/directives/TranslationDropdown.ts @@ -1,3 +1,4 @@ +import {onTranslateActions} from 'apps/monitoring/directives/MonitoringView'; import _ from 'lodash'; /** @@ -33,6 +34,7 @@ export function TranslationDropdown(TranslationService) { * @param {Object} New language */ scope.translateItem = function(language) { + onTranslateActions.onTranslateMonitoring?.(); TranslationService.set(scope.item, language); }; }, diff --git a/scripts/core/get-superdesk-api-implementation.tsx b/scripts/core/get-superdesk-api-implementation.tsx index bb67224804..e97038b577 100644 --- a/scripts/core/get-superdesk-api-implementation.tsx +++ b/scripts/core/get-superdesk-api-implementation.tsx @@ -119,6 +119,7 @@ import {PreviewFieldType} from 'apps/authoring/preview/previewFieldByType'; import {getLabelNameResolver} from 'apps/workspace/helpers/getLabelForFieldId'; import {getSortedFields, getSortedFieldsFiltered} from 'apps/authoring/preview/utils'; import {editor3ToOperationalFormat} from 'apps/authoring-react/fields/editor3'; +import {onTranslateActions} from 'apps/monitoring/directives/MonitoringView'; function getContentType(id): Promise { return dataApi.findOne('content_types', id); @@ -451,6 +452,7 @@ export function getSuperdeskApiImplementation( getFormFieldPreviewComponent, }, authoringGeneric: { + onTranslateActions: onTranslateActions, sideWidgets: { inlineComments: getInlineCommentsWidgetGeneric(), comments: ( diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index e7469c4465..72304d8a75 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -1,5 +1,7 @@ /* tslint:disable */ +import {IOnTranslateActions} from 'apps/monitoring/directives/MonitoringView'; + declare module 'superdesk-api' { // TYPESCRIPT TYPES @@ -2726,6 +2728,11 @@ declare module 'superdesk-api' { undefinedEqNull: boolean; } + export interface IOnTranslateActions { + onTranslateMonitoring?: () => void; + onTranslateAuthoring?: () => void; + } + export type ISuperdesk = DeepReadonly<{ dataApi: IDataApi, dataApiByEntity: { @@ -2949,6 +2956,7 @@ declare module 'superdesk-api' { }; }; authoringGeneric: { + onTranslateActions: IOnTranslateActions; sideWidgets: { comments: ( getComments: (entityId: string) => Promise>, diff --git a/scripts/extensions/ai-widget/src/configuration.ts b/scripts/extensions/ai-widget/src/configuration.ts index bdce4237fa..d72c2afe3a 100644 --- a/scripts/extensions/ai-widget/src/configuration.ts +++ b/scripts/extensions/ai-widget/src/configuration.ts @@ -1,4 +1,5 @@ -import {IArticle, ISuperdesk} from 'superdesk-api'; +import {IArticle, ISuperdesk, IOnTranslateActions} from 'superdesk-api'; +import {superdesk} from './superdesk'; export interface IConfigurationOptions { generateHeadlines?: (article: IArticle, superdesk: ISuperdesk) => Promise>; @@ -10,3 +11,7 @@ export const configuration: IConfigurationOptions = {}; export function configure(_configuration: IConfigurationOptions) { Object.assign(configuration, _configuration); } + +export function configureOnTranslate(_onTranslateActions: IOnTranslateActions) { + Object.assign(superdesk.authoringGeneric.onTranslateActions, _onTranslateActions) +} diff --git a/scripts/extensions/ai-widget/src/extension.ts b/scripts/extensions/ai-widget/src/extension.ts index aa0a0f94b0..f89bb3fc4b 100644 --- a/scripts/extensions/ai-widget/src/extension.ts +++ b/scripts/extensions/ai-widget/src/extension.ts @@ -29,6 +29,6 @@ const extension: IExtension = { }, }; -export {configure} from './configuration'; +export {configure, configureOnTranslate} from './configuration'; export default extension; From 7f096d4902bb43818b210ca147e728158272288c Mon Sep 17 00:00:00 2001 From: Konstantin Markov Date: Tue, 4 Jun 2024 12:08:11 +0300 Subject: [PATCH 02/16] Implementation --- package-lock.json | 43 ++-- package.json | 2 +- scripts/api/index.ts | 2 + scripts/api/translations.ts | 29 +++ .../authoring-integration-wrapper.tsx | 4 +- .../toolbar/translate-modal.tsx | 7 +- scripts/apps/authoring/widgets/widgets.ts | 14 +- .../directives/TranslationDropdown.ts | 2 - .../services/TranslationService.ts | 5 +- scripts/core/superdesk-api.d.ts | 9 - .../extensions/ai-widget/src/ai-assistant.tsx | 57 ++++- .../extensions/ai-widget/src/configuration.ts | 8 +- scripts/extensions/ai-widget/src/extension.ts | 9 +- .../extensions/ai-widget/src/main-panel.tsx | 10 + scripts/extensions/ai-widget/src/superdesk.ts | 2 +- .../src/translations/translations-body.tsx | 199 ++++++++++++++++++ .../src/translations/translations-footer.tsx | 80 +++++++ .../src/translations/translations-widget.tsx | 81 +++++++ 18 files changed, 518 insertions(+), 45 deletions(-) create mode 100644 scripts/api/translations.ts create mode 100644 scripts/extensions/ai-widget/src/translations/translations-body.tsx create mode 100644 scripts/extensions/ai-widget/src/translations/translations-footer.tsx create mode 100644 scripts/extensions/ai-widget/src/translations/translations-widget.tsx diff --git a/package-lock.json b/package-lock.json index 22188a56b3..24e1819a00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -818,7 +818,7 @@ }, "@typescript-eslint/parser": { "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.57.0.tgz", + "resolved": "https://verdaccio.sourcefabric.org/@typescript-eslint/parser/-/parser-5.57.0.tgz", "integrity": "sha512-orrduvpWYkgLCyAdNtR1QIWovcNZlEm6yL8nwH/eTxWLd8gsP+25pdLHYzL2QdkqrieaDwLpytHqycncv0woUQ==", "requires": { "@typescript-eslint/scope-manager": "5.57.0", @@ -829,7 +829,7 @@ "dependencies": { "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "resolved": "https://verdaccio.sourcefabric.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" @@ -1315,7 +1315,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "prop-types": { "version": "15.8.1", @@ -8473,7 +8473,7 @@ "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" }, "json-loader": { "version": "0.5.7", @@ -11962,6 +11962,11 @@ "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-2.0.0.tgz", "integrity": "sha512-GJTCeMSQU8UU1GqbsaDrg/IH+b/vSinJQl52NVpdJ7sShYLZA8Eq6jLF48Ye3N/dQloGrE07i7XsZvxQ9pNbqg==" }, + "primereact": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/primereact/-/primereact-6.6.0.tgz", + "integrity": "sha512-onoowjhlOz9Kcjna1DYEKWTGjKah4MjqZchm9E3BkcWjeXoCLv3F5QnIo8eVF9q+xymEK/6Oh7zHlfCJYyWwDw==" + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -12032,7 +12037,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" } } }, @@ -12527,7 +12532,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "prop-types": { "version": "15.7.2", @@ -12814,7 +12819,7 @@ "regexpu-core": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha512-Ci+lDRlvAElKjFp5keqmVUaJLqZiHywekXhshT6wVUyDObGPdymNPhxBmf38ZVsaUGOnZ3Fot9YzxvoI31ymYw==", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "requires": { "regenerate": "^1.2.1", "regjsgen": "^0.2.0", @@ -12824,12 +12829,12 @@ "regjsgen": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==" + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" }, "regjsparser": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha512-jlQ9gYLfk2p3V5Ag5fYhA7fv7OHzd1KUH0PRP46xc3TgwjwgROIW572AfYg/X9kaNq/LJnu6oJcFRXlIrGoTRw==", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "requires": { "jsesc": "~0.5.0" } @@ -14805,11 +14810,12 @@ } }, "superdesk-ui-framework": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/superdesk-ui-framework/-/superdesk-ui-framework-3.1.3.tgz", - "integrity": "sha512-COqb+IPwqY4PE+ns2PIACp4cy1GPyZxanoJ/GUj1tHe3JW6FJWr6zkU5PV0tFU2p5JQcUI8SHHTCdJHgbER1ZQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/superdesk-ui-framework/-/superdesk-ui-framework-3.1.9.tgz", + "integrity": "sha512-mgBkdsv/mvG02WUNt+7szbw+V0xDK0BtG5nuZag/GuvHjJmTGW0s0OhuC/Q+/SGkwg/iR+C7DLYqUY4l+7TQXQ==", "requires": { "@popperjs/core": "^2.4.0", + "@superdesk/common": "0.0.28", "@superdesk/primereact": "^5.0.2-12", "@superdesk/react-resizable-panels": "0.0.39", "@types/enzyme-adapter-react-16": "^1.0.6", @@ -14827,6 +14833,19 @@ "react-scrollspy": "^3.4.3" }, "dependencies": { + "@superdesk/common": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/@superdesk/common/-/common-0.0.28.tgz", + "integrity": "sha512-EhsYMm340r3FVrakH00lLvQbxVYYTzL61J5GXI3BI2xLN2dPI3N0AJEaMGqjbt0xUpUFxE3T08OtYvIC5koZvg==", + "requires": { + "date-fns": "2.7.0", + "lodash": "4.17.19", + "primereact": "^6.0.2", + "react": "16.9.0", + "react-dom": "16.9.0", + "react-sortable-hoc": "^1.11.0" + } + }, "@types/node": { "version": "14.18.63", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", diff --git a/package.json b/package.json index 57c8ec0ac9..70228597cd 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "sass-loader": "6.0.6", "shortid": "2.2.8", "style-loader": "0.20.2", - "superdesk-ui-framework": "^3.1.3", + "superdesk-ui-framework": "^3.1.9", "ts-loader": "3.5.0", "typescript": "4.9.5", "uuid": "8.3.1", diff --git a/scripts/api/index.ts b/scripts/api/index.ts index 9fda9a3bba..ff1bcdd82c 100644 --- a/scripts/api/index.ts +++ b/scripts/api/index.ts @@ -11,6 +11,7 @@ import {time} from './time'; import {user} from './user'; import {vocabularies} from './vocabularies'; import {contentProfiles} from './content-profiles'; +import {translations} from './translations'; /** * This is core API, not extensions API. @@ -31,4 +32,5 @@ export const sdApi = { vocabularies, highlights, contentProfiles, + translations, }; diff --git a/scripts/api/translations.ts b/scripts/api/translations.ts new file mode 100644 index 0000000000..b818511a3c --- /dev/null +++ b/scripts/api/translations.ts @@ -0,0 +1,29 @@ +import {IArticle} from 'superdesk-api'; +import ng from 'core/services/ng'; +import {extensions} from 'appConfig'; +import notify from 'core/notify/notify'; +import {gettext} from 'core/utils'; + +function aiTranslationActions(item: IArticle): void { + if (extensions['ai-widget'].extension.exposes?.overrideTranslations === true) { + localStorage.setItem( + 'sideWidget', + JSON.stringify({ + id: 'ai-assistant', + pinned: true, + activeSection: 'translations', + }), + ); + } + + ng.get('authoringWorkspace').open(item); + notify.success(gettext('Item Translated')); +} + +interface ITranslationsApi { + aiTranslationActions(item: IArticle): void; +} + +export const translations: ITranslationsApi = { + aiTranslationActions, +}; diff --git a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx index b54f719236..8583bcd6b0 100644 --- a/scripts/apps/authoring-react/authoring-integration-wrapper.tsx +++ b/scripts/apps/authoring-react/authoring-integration-wrapper.tsx @@ -260,9 +260,11 @@ export class AuthoringIntegrationWrapper extends React.PureComponent { translate() { if (this.state.initialized) { - return httpRequestJsonLocal({ + return httpRequestJsonLocal({ method: 'POST', path: '/archive/translate', payload: { @@ -59,7 +59,10 @@ export class TranslateModal extends React.PureComponent { language: this.state.selectedLanguage, guid: this.props.article._id, }, - }).then(() => this.props.closeModal()); + }).then((item) => { + sdApi.translations.aiTranslationActions(item); + this.props.closeModal(); + }); } } diff --git a/scripts/apps/authoring/widgets/widgets.ts b/scripts/apps/authoring/widgets/widgets.ts index 426016dbdd..81e7fe7a4c 100644 --- a/scripts/apps/authoring/widgets/widgets.ts +++ b/scripts/apps/authoring/widgets/widgets.ts @@ -157,7 +157,13 @@ function WidgetsManagerCtrl( preferencesService, $rootScope, ) { - $scope.active = null; + const lsSideWidget = localStorage.getItem('sideWidget'); + const localStorageWidgetState = lsSideWidget != null ? JSON.parse(lsSideWidget) : null; + const widgetValue = lsSideWidget == null + ? null + : authoringWidgets.find((widget) => widget._id === localStorageWidgetState?.id); + + $scope.active = widgetValue != null ? widgetValue : null; preferencesService.get(USER_PREFERENCE_SETTINGS).then((preferences) => this.widgetFromPreferences = preferences, @@ -338,7 +344,7 @@ function WidgetsManagerCtrl( widgetReactIntegration.pinWidget = $scope.pinWidget; widgetReactIntegration.getActiveWidget = () => $scope.active ?? $scope.pinnedWidget; widgetReactIntegration.getPinnedWidget = - () => $scope.widgets.find(({pinned}) => pinned === true)?.name ?? null; + () => $scope.widgets?.find(({pinned}) => pinned === true)?.name ?? null; widgetReactIntegration.WidgetHeaderComponent = WidgetHeaderComponent; widgetReactIntegration.WidgetLayoutComponent = WidgetLayoutComponent; @@ -392,6 +398,10 @@ function WidgetsManagerCtrl( }); }; + if (widgetValue?.component != null && localStorageWidgetState?.pinned === true) { + $scope.pinWidget(widgetValue); + } + $scope.$on('$destroy', () => { unbindAllShortcuts(); }); diff --git a/scripts/apps/translations/directives/TranslationDropdown.ts b/scripts/apps/translations/directives/TranslationDropdown.ts index aa683a548b..13c82d8d3e 100644 --- a/scripts/apps/translations/directives/TranslationDropdown.ts +++ b/scripts/apps/translations/directives/TranslationDropdown.ts @@ -1,4 +1,3 @@ -import {onTranslateActions} from 'apps/monitoring/directives/MonitoringView'; import _ from 'lodash'; /** @@ -34,7 +33,6 @@ export function TranslationDropdown(TranslationService) { * @param {Object} New language */ scope.translateItem = function(language) { - onTranslateActions.onTranslateMonitoring?.(); TranslationService.set(scope.item, language); }; }, diff --git a/scripts/apps/translations/services/TranslationService.ts b/scripts/apps/translations/services/TranslationService.ts index e6c08706d9..c22ac2fef2 100644 --- a/scripts/apps/translations/services/TranslationService.ts +++ b/scripts/apps/translations/services/TranslationService.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {gettext} from 'core/utils'; import {AuthoringWorkspaceService} from 'apps/authoring/authoring/services/AuthoringWorkspaceService'; +import {sdApi} from 'api'; /** * @ngdoc service @@ -64,9 +64,8 @@ export function TranslationService( }; api.save('translate', params).then((_item) => { - authoringWorkspace.open(_item); + sdApi.translations.aiTranslationActions(_item); $rootScope.$broadcast('item:translate'); - notify.success(gettext('Item Translated')); }); }; diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index 72304d8a75..8160c332f1 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -1,7 +1,4 @@ /* tslint:disable */ - -import {IOnTranslateActions} from 'apps/monitoring/directives/MonitoringView'; - declare module 'superdesk-api' { // TYPESCRIPT TYPES @@ -2728,11 +2725,6 @@ declare module 'superdesk-api' { undefinedEqNull: boolean; } - export interface IOnTranslateActions { - onTranslateMonitoring?: () => void; - onTranslateAuthoring?: () => void; - } - export type ISuperdesk = DeepReadonly<{ dataApi: IDataApi, dataApiByEntity: { @@ -2956,7 +2948,6 @@ declare module 'superdesk-api' { }; }; authoringGeneric: { - onTranslateActions: IOnTranslateActions; sideWidgets: { comments: ( getComments: (entityId: string) => Promise>, diff --git a/scripts/extensions/ai-widget/src/ai-assistant.tsx b/scripts/extensions/ai-widget/src/ai-assistant.tsx index 6616ac8080..1d0fe98c99 100644 --- a/scripts/extensions/ai-widget/src/ai-assistant.tsx +++ b/scripts/extensions/ai-widget/src/ai-assistant.tsx @@ -1,13 +1,15 @@ import React from 'react'; -import {IArticleSideWidgetComponentType} from 'superdesk-api'; +import {IArticleSideWidgetComponentType, ITranslation} from 'superdesk-api'; import {Spacer} from 'superdesk-ui-framework/react'; import {superdesk} from './superdesk'; import {configuration} from './configuration'; import getHeadlinesWidget from './headlines/headlines-widget'; import getSummaryWidget from './summary/summary-widget'; import DefaultAiAssistantPanel from './main-panel'; +import getTranslationsWidget from './translations/translations-widget'; -export type IAiAssistantSection = 'headlines' | 'summary' | null; +export type IAiAssistantSection = 'headlines' | 'summary' | 'translations' | null; +export type ITranslationLanguage = ITranslation['_id']; interface IState { activeSection: IAiAssistantSection; @@ -17,28 +19,39 @@ interface IState { */ loadingHeadlines: boolean; loadingSummary: boolean; + loadingTranslations: boolean; headlines: Array; error: boolean; summary: string; + translations: string; + activeLanguage: ITranslationLanguage; } export class AiAssistantWidget extends React.PureComponent { constructor(props: IArticleSideWidgetComponentType) { super(props); + const lsSideWidget = localStorage.getItem('sideWidget') + this.state = { - activeSection: null, + activeSection: lsSideWidget != null ? JSON.parse(lsSideWidget).activeSection : null, headlines: [], - error: false, + summary: '', + translations: '', loadingSummary: true, loadingHeadlines: true, - summary: '', + loadingTranslations: false, + error: false, + activeLanguage: this.props.article.language, }; + localStorage.removeItem('sideWidget'); + this.setError = this.setError.bind(this); this.generateHeadlines = this.generateHeadlines.bind(this); this.generateSummary = this.generateSummary.bind(this); + this.generateTranslations = this.generateTranslations.bind(this); } setError() { @@ -59,6 +72,18 @@ export class AiAssistantWidget extends React.PureComponent { + this.setState({ + loadingTranslations: false, + translations: res, + }); + }).catch(() => { + this.setError(); + }); + } + generateSummary() { configuration.generateSummary?.(this.props.article, superdesk) .then((res) => { @@ -92,6 +117,26 @@ export class AiAssistantWidget extends React.PureComponent { + this.setState({ + activeLanguage: language, + }) + }, + activeLanguage: this.state.activeLanguage, + generateTranslations: () => { + this.setState({ + loadingTranslations: true, + }, () => this.generateTranslations()); + }, + translations: this.state.translations, + loading: this.state.loadingTranslations, + fieldsData: this.props.fieldsData, + onFieldsDataChange: this.props.onFieldsDataChange, + }); const summaryWidget = getSummaryWidget({ closeActiveSection, article: this.props.article, @@ -114,6 +159,8 @@ export class AiAssistantWidget extends React.PureComponent Promise>; + generateTranslations?: (article: IArticle, language: string, superdesk: ISuperdesk) => Promise; generateSummary?: (article: IArticle, superdesk: ISuperdesk) => Promise; + overrideTranslations?: boolean; } export const configuration: IConfigurationOptions = {}; @@ -12,6 +13,3 @@ export function configure(_configuration: IConfigurationOptions) { Object.assign(configuration, _configuration); } -export function configureOnTranslate(_onTranslateActions: IOnTranslateActions) { - Object.assign(superdesk.authoringGeneric.onTranslateActions, _onTranslateActions) -} diff --git a/scripts/extensions/ai-widget/src/extension.ts b/scripts/extensions/ai-widget/src/extension.ts index f89bb3fc4b..671058b766 100644 --- a/scripts/extensions/ai-widget/src/extension.ts +++ b/scripts/extensions/ai-widget/src/extension.ts @@ -4,6 +4,12 @@ import {superdesk} from './superdesk'; import {configuration} from './configuration'; const extension: IExtension = { + exposes: { + get overrideTranslations() { + console.log(configuration.overrideTranslations) + return configuration.overrideTranslations; + }, + }, activate: () => { const hasConfiguredServices = Object.keys(configuration).length > 0; @@ -24,11 +30,10 @@ const extension: IExtension = { }, }; - return Promise.resolve(result); }, }; -export {configure, configureOnTranslate} from './configuration'; +export {configure} from './configuration'; export default extension; diff --git a/scripts/extensions/ai-widget/src/main-panel.tsx b/scripts/extensions/ai-widget/src/main-panel.tsx index 8e0b46f34b..60c48906a7 100644 --- a/scripts/extensions/ai-widget/src/main-panel.tsx +++ b/scripts/extensions/ai-widget/src/main-panel.tsx @@ -40,6 +40,16 @@ export default function DefaultAiAssistantPanel({setSection}: IProps) { )} + {configuration.generateTranslations != null && ( + { + setSection('translations'); + }} + > + + + )} ); } diff --git a/scripts/extensions/ai-widget/src/superdesk.ts b/scripts/extensions/ai-widget/src/superdesk.ts index e2d76b3749..6c18daf514 100644 --- a/scripts/extensions/ai-widget/src/superdesk.ts +++ b/scripts/extensions/ai-widget/src/superdesk.ts @@ -2,4 +2,4 @@ import {ISuperdesk} from 'superdesk-api'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -export const superdesk = window['extensionsApiInstances']['ai-assistant-widget'] as ISuperdesk; +export const superdesk = window['extensionsApiInstances']['ai-widget'] as ISuperdesk; diff --git a/scripts/extensions/ai-widget/src/translations/translations-body.tsx b/scripts/extensions/ai-widget/src/translations/translations-body.tsx new file mode 100644 index 0000000000..2eb7d46ade --- /dev/null +++ b/scripts/extensions/ai-widget/src/translations/translations-body.tsx @@ -0,0 +1,199 @@ +import React from 'react'; +import {ReactNode} from 'react'; +import {convertToRaw, ContentState} from 'draft-js'; +import {IArticle, OrderedMap} from 'superdesk-api'; +import { + IconButton, + Container, + ButtonGroup, + Button, + Text, + Loader, + Heading, + Spacer, + Label, + Icon, +} from 'superdesk-ui-framework/react'; +import {superdesk} from '../superdesk'; +import {ITranslationLanguage} from '../ai-assistant'; + +interface IProps { + article: IArticle; + error: boolean; + loading: boolean; + translation: string; + generateTranslation: () => void; + fieldsData?: OrderedMap; + onFieldsDataChange?(fieldsData?: OrderedMap): void; + activeLanguage: ITranslationLanguage; +} + +interface IState { + translatedFromLanguage: string; +} + +export default class TranslationsBody extends React.Component { + constructor(props: IProps) { + super(props); + + this.state = { + translatedFromLanguage: this.props.activeLanguage, + } + } + + componentDidUpdate(prevProps: Readonly): void { + console.log(this.props.activeLanguage, prevProps.activeLanguage) + if (this.props.activeLanguage !== prevProps.activeLanguage) { + this.setState({ + translatedFromLanguage: this.props.activeLanguage, + }); + } + } + + componentDidMount(): void { + if (this.props.article.translated_from != null) { + superdesk.dataApi.findOne( + 'archive', + this.props.article.translated_from + ).then((article) => { + this.setState({ + translatedFromLanguage: article.language + }); + }); + } + } + + render(): ReactNode { + const {error, loading, translation, article, generateTranslation} = this.props; + const {gettext} = superdesk.localization; + + if (error) { + return ( + +