Skip to content

Commit

Permalink
Ai assistant widget (#4508)
Browse files Browse the repository at this point in the history
  • Loading branch information
thecalcc authored May 2, 2024
1 parent 2b4a6ae commit 9c502d8
Show file tree
Hide file tree
Showing 25 changed files with 3,343 additions and 57 deletions.
9 changes: 9 additions & 0 deletions scripts/api/article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,13 @@ function createNewUsingDeskTemplate(): void {
});
}

function createNewWithData(data: Partial<IArticle>, contentProfileId: string): void {
dataApi.create('archive', {type: 'text', ...data, profile: contentProfileId})
.then((item) => {
openArticle(item._id, 'edit');
});
}

/**
* Checks if associations is with rewrite_of item then open then modal to add associations.
* The user has options to add associated media to the current item and review the media change
Expand Down Expand Up @@ -556,6 +563,7 @@ interface IArticleApi {
unlock(itemId: IArticle['_id']): Promise<IArticle>;

createNewUsingDeskTemplate(): void;
createNewWithData(data: Partial<IArticle>, contentProfileId: string): void;
getWorkQueueItems(): Array<IArticle>;
rewrite(item: IArticle): void;
canPublishOnDesk(deskType: string): boolean;
Expand Down Expand Up @@ -618,6 +626,7 @@ export const article: IArticleApi = {
lock,
unlock,
createNewUsingDeskTemplate,
createNewWithData,
getWorkQueueItems,
get,
canPublishOnDesk,
Expand Down
12 changes: 6 additions & 6 deletions scripts/apps/authoring-react/authoring-integration-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ interface IPropsWrapper extends IProps {
interface IState {
sidebarMode: boolean | 'hidden';
sideWidget: null | {
name: string;
id: string;
pinned: boolean;
};
}
Expand Down Expand Up @@ -313,19 +313,19 @@ export class AuthoringIntegrationWrapper extends React.PureComponent<IPropsWrapp
icon: widget.icon,
size: 'big',
tooltip: widget.label,
id: widget.label,
id: widget._id,
};

return tab;
});

return (
<Nav.SideBarTabs
activeTab={this.state.sideWidget?.name}
activeTab={this.state.sideWidget?.id}
onActiveTabChange={(val) => {
this.setState({
sideWidget: {
name: val,
id: val,
pinned: this.state.sideWidget?.pinned ?? false,
},
});
Expand Down Expand Up @@ -483,8 +483,8 @@ export class AuthoringIntegrationWrapper extends React.PureComponent<IPropsWrapp
getSidebar={this.state.sidebarMode !== true ? null : getSidebar}
secondaryToolbarWidgets={secondaryToolbarWidgetsReady}
validateBeforeSaving={false}
getSideWidgetNameAtIndex={(article, index) => {
return getWidgetsFromExtensions(article)[index].label;
getSideWidgetIdAtIndex={(article, index) => {
return getWidgetsFromExtensions(article)[index]._id;
}}
/>
);
Expand Down
16 changes: 8 additions & 8 deletions scripts/apps/authoring-react/authoring-react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,14 +335,14 @@ export class AuthoringReact<T extends IBaseRestApiResponse> extends React.PureCo
};

widgetReactIntegration.getActiveWidget = () => {
return this.props.sideWidget?.name ?? null;
return this.props.sideWidget?.id ?? null;
};

widgetReactIntegration.getPinnedWidget = () => {
const pinned = this.props.sideWidget?.pinned === true;

if (pinned) {
return this.props.sideWidget.name;
return this.props.sideWidget.id;
} else {
return null;
}
Expand Down Expand Up @@ -1113,13 +1113,13 @@ export class AuthoringReact<T extends IBaseRestApiResponse> extends React.PureCo
authoringStorage: authoringStorage,
storageAdapter: storageAdapter,
fieldsAdapter: fieldsAdapter,
sideWidget: this.props.sideWidget?.name ?? null,
toggleSideWidget: (name) => {
if (name == null || this.props.sideWidget?.name === name) {
sideWidget: this.props.sideWidget?.id ?? null,
toggleSideWidget: (id) => {
if (id == null || this.props.sideWidget?.id === id) {
this.props.onSideWidgetChange(null);
} else {
this.props.onSideWidgetChange({
name: name,
id: id,
pinned: false,
});
}
Expand Down Expand Up @@ -1229,10 +1229,10 @@ export class AuthoringReact<T extends IBaseRestApiResponse> extends React.PureCo

for (let i = 0; i < widgetsCount; i++) {
widgetKeybindings[`ctrl+alt+${i + 1}`] = () => {
const nextWidgetName: string = this.props.getSideWidgetNameAtIndex(exposed.item, i);
const nextWidgetName: string = this.props.getSideWidgetIdAtIndex(exposed.item, i);

this.props.onSideWidgetChange({
name: nextWidgetName,
id: nextWidgetName,
pinned: this.props.sideWidget?.pinned ?? false,
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,7 @@ export function AuthoringDirective(
angular.extend($scope.item, event.detail);
angular.extend($scope.origItem, event.detail);
$scope.$apply();
$scope.refresh();
}
},
);
Expand Down
9 changes: 8 additions & 1 deletion scripts/core/get-superdesk-api-implementation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
stripHtmlTags,
stripLockingFields,
getProjectedFieldsArticle,
getArticleLabel,
} from 'core/utils';
import {ListItem, ListItemColumn, ListItemRow, ListItemActionsMenu} from './components/ListItem';
import {getFormFieldPreviewComponent} from './ui/components/generic-form/form-field';
Expand Down Expand Up @@ -68,7 +69,7 @@ import {Icon} from './ui/components/Icon2';
import {AuthoringWorkspaceService} from 'apps/authoring/authoring/services/AuthoringWorkspaceService';
import ng from 'core/services/ng';
import {Spacer, SpacerBlock, SpacerInlineFlex} from './ui/components/Spacer';
import {appConfig} from 'appConfig';
import {appConfig, authoringReactViewEnabled} from 'appConfig';
import {httpRequestJsonLocal, httpRequestVoidLocal, httpRequestRawLocal} from './helpers/network';
import {memoize as memoizeLocal} from './memoize';
import {generatePatch} from './patch';
Expand Down Expand Up @@ -117,6 +118,7 @@ import {getCustomFieldVocabularies, getLanguageVocabulary} from './helpers/busin
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';

function getContentType(id): Promise<IContentProfile> {
return dataApi.findOne('content_types', id);
Expand Down Expand Up @@ -277,10 +279,12 @@ export function getSuperdeskApiImplementation(
fixPatchRequest,
fixPatchResponse,
computeEditor3Output,
editor3ToOperationalFormat: editor3ToOperationalFormat,
getContentStateFromHtml: (html) => getContentStateFromHtml(html),
tryLocking,
tryUnlocking,
superdeskToElasticQuery: toElasticQuery,
getArticleLabel,
},
httpRequestJsonLocal,
httpRequestRawLocal,
Expand All @@ -293,6 +297,7 @@ export function getSuperdeskApiImplementation(
isLockedInCurrentSession: sdApi.article.isLockedInCurrentSession,
isLockedInOtherSession: sdApi.article.isLockedInOtherSession,
patch: patchArticle,
createNewWithData: sdApi.article.createNewWithData,
isArchived: sdApi.article.isArchived,
isPublished: (article) => sdApi.article.isPublished(article),
itemAction: (article) => sdApi.article.itemAction(article),
Expand All @@ -307,6 +312,7 @@ export function getSuperdeskApiImplementation(
.then((response) => response._items),
getActiveDeskId: sdApi.desks.getActiveDeskId,
waitTilReady: sdApi.desks.waitTilReady,
getDeskById: sdApi.desks.getDeskById,
},
contentProfile: {
get: (id) => {
Expand Down Expand Up @@ -353,6 +359,7 @@ export function getSuperdeskApiImplementation(
state: applicationState,
instance: {
config,
authoringReactViewEnabled,
},
ui: {
article: {
Expand Down
59 changes: 32 additions & 27 deletions scripts/core/superdesk-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ declare module 'superdesk-api' {
*/
export interface IExposedFromAuthoring<T> {
item: T;
sideWidget: string | null; // side widget name
sideWidget: string | null; // side widget id

/**
* Computes the latest entity from fields data. `item` property in
Expand All @@ -146,7 +146,7 @@ declare module 'superdesk-api' {
* we are passing a function instead.
*/
getLatestItem(options?: {preferIncomplete?: IStoreValueIncomplete}): T;
toggleSideWidget(name: string | null): void;
toggleSideWidget(id: string | null): void;
contentProfile: IContentProfileV2;
fieldsData: IFieldsData;
authoringStorage: IAuthoringStorage<T>;
Expand Down Expand Up @@ -215,11 +215,11 @@ declare module 'superdesk-api' {
getSidebarWidgetsCount(options: IExposedFromAuthoring<T>): number;

sideWidget: null | {
name: string;
id: string;
pinned: boolean;
};

getSideWidgetNameAtIndex(item: T, index: number): string;
getSideWidgetIdAtIndex(item: T, index: number): string;
onSideWidgetChange(openWidget: IPropsAuthoring<T>['sideWidget']): void;

// Runs before re-render.
Expand Down Expand Up @@ -601,6 +601,28 @@ declare module 'superdesk-api' {
isAllowed: (entity: T) => boolean;
}

export interface IArticleSideWidgetComponentType {
article: IArticle;

getLatestArticle: IExposedFromAuthoring<IArticle>['getLatestItem'];

// other props below are specific to authoring-react implementation

readOnly: boolean;
contentProfile?: IContentProfileV2;
fieldsData?: OrderedMap<string, unknown>;
authoringStorage: IAuthoringStorage<IArticle>;
fieldsAdapter: IFieldsAdapter<IArticle>;
storageAdapter: IStorageAdapter<IArticle>;

onItemChange?(article: IArticle): void;
onFieldsDataChange?(fieldsData?: OrderedMap<string, unknown>): void;
/**
* Will prompt user to save changes. The promise will get rejected if user cancels saving.
*/
handleUnsavedChanges(): Promise<IArticle>;
}

/**
* @deprecated: prefer {@link IGenericSideWidget}
*/
Expand All @@ -609,28 +631,7 @@ declare module 'superdesk-api' {
label: string;
order: number; // Integer. // NICE-TO-HAVE: manage order in the UI instead of here
icon: string;
component: React.ComponentType<{
article: IArticle;


getLatestArticle: IExposedFromAuthoring<IArticle>['getLatestItem'];

// other props below are specific to authoring-react implementation

readOnly: boolean;
contentProfile?: IContentProfileV2;
fieldsData?: OrderedMap<string, unknown>;
authoringStorage: IAuthoringStorage<IArticle>;
fieldsAdapter: IFieldsAdapter<IArticle>;
storageAdapter: IStorageAdapter<IArticle>;

onItemChange?(article: IArticle): void;
onFieldsDataChange?(fieldsData?: OrderedMap<string, unknown>): void;
/**
* Will prompt user to save changes. The promise will get rejected if user cancels saving.
*/
handleUnsavedChanges(): Promise<IArticle>;
}>;
component: React.ComponentType<IArticleSideWidgetComponentType>;
isAllowed?(article: IArticle): boolean; // enables limiting widgets depending on article data
}

Expand Down Expand Up @@ -2741,6 +2742,7 @@ declare module 'superdesk-api' {
};
instance: {
config: ISuperdeskGlobalConfig;
authoringReactViewEnabled: boolean;
};

/** Retrieves configuration options passed when registering an extension. */
Expand Down Expand Up @@ -2790,7 +2792,7 @@ declare module 'superdesk-api' {
patch: Partial<IArticle>,
dangerousOptions?: IDangerousArticlePatchingOptions,
): Promise<void>;

createNewWithData(data: Partial<IArticle>, contentProfileId: string): void;
isArchived(article: IArticle): boolean;
isPublished(article: IArticle): boolean;
itemAction(article: IArticle): {[key in IAuthoringActionType]: boolean};
Expand Down Expand Up @@ -2828,6 +2830,7 @@ declare module 'superdesk-api' {
getStagesOrdered(deskId: IDesk['_id']): Promise<Array<IStage>>;
getActiveDeskId(): IDesk['_id'] | null;
waitTilReady(): Promise<void>;
getDeskById(id: IDesk['_id']): IDesk;
};
attachment: IAttachmentsApi;
users: {
Expand All @@ -2844,6 +2847,7 @@ declare module 'superdesk-api' {
};
};
helpers: {
getArticleLabel(item: IArticle): string;
assertNever(x: never): never;
stripBaseRestApiFields<T extends IBaseRestApiResponse>(entity: T): Omit<T, keyof IBaseRestApiResponse>;
fixPatchResponse<T extends IBaseRestApiResponse>(entity: T & {_status: string}): T;
Expand All @@ -2864,6 +2868,7 @@ declare module 'superdesk-api' {
endpoint: string,
entityId: string,
): Promise<void>;
editor3ToOperationalFormat(value: IEditor3ValueStorage, language: string): IEditor3ValueOperational;
computeEditor3Output(
rawContentState: import('draft-js').RawDraftContentState,
config: IEditor3Config,
Expand Down
1 change: 1 addition & 0 deletions scripts/extensions/ai-widget/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
Loading

0 comments on commit 9c502d8

Please sign in to comment.