Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-edit view in React authoring #4143

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
472 changes: 236 additions & 236 deletions package-lock.json

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions scripts/api/article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {duplicateItems} from './article-duplicate';
import {sdApi} from 'api';
import {appConfig} from 'appConfig';
import {KILLED_STATES, ITEM_STATE, PUBLISHED_STATES} from 'apps/archive/constants';
import {dataApi} from 'core/helpers/CrudManager';
import {assertNever} from 'core/helpers/typescript-helpers';

const isLocked = (_article: IArticle) => _article.lock_session != null;
const isLockedInCurrentSession = (_article: IArticle) => _article.lock_session === ng.get('session').sessionId;
Expand Down Expand Up @@ -173,8 +175,48 @@ function createNewUsingDeskTemplate(): void {
});
}

/**
* Gets opened items from your workspace.
*/
function getWorkQueueItems(): Array<IArticle> {
return ng.get('workqueue').items;
}

function get(id: IArticle['_id']): Promise<IArticle> {
return dataApi.findOne<IArticle>('archive', id);
}

function isEditable(_article: IArticle): boolean {
const itemState: ITEM_STATE = _article.state;
const authoring = ng.get('authoring');

switch (itemState) {
case ITEM_STATE.DRAFT:
case ITEM_STATE.CORRECTION:
case ITEM_STATE.SUBMITTED:
case ITEM_STATE.IN_PROGRESS:
case ITEM_STATE.ROUTED:
case ITEM_STATE.FETCHED:
case ITEM_STATE.UNPUBLISHED:
return authoring.itemActions(_article).edit === true;
case ITEM_STATE.INGESTED:
case ITEM_STATE.SPIKED:
case ITEM_STATE.SCHEDULED:
case ITEM_STATE.PUBLISHED:
case ITEM_STATE.CORRECTED:
case ITEM_STATE.BEING_CORRECTED:
case ITEM_STATE.KILLED:
case ITEM_STATE.RECALLED:
return false;
default:
assertNever(itemState);
}
}

interface IArticleApi {
get(id: IArticle['_id']): Promise<IArticle>;
isLocked(article: IArticle): boolean;
isEditable(article: IArticle): boolean;
isLockedInCurrentSession(article: IArticle): boolean;
isLockedInOtherSession(article: IArticle): boolean;
isLockedByCurrentUser(article: IArticle): boolean;
Expand Down Expand Up @@ -226,10 +268,12 @@ interface IArticleApi {
unlock(itemId: IArticle['_id']): Promise<IArticle>;

createNewUsingDeskTemplate(): void;
getWorkQueueItems(): Array<IArticle>;
}

export const article: IArticleApi = {
isLocked,
isEditable,
isLockedInCurrentSession,
isLockedInOtherSession,
isLockedByCurrentUser,
Expand All @@ -252,4 +296,6 @@ export const article: IArticleApi = {
lock,
unlock,
createNewUsingDeskTemplate,
getWorkQueueItems,
get,
};
222 changes: 222 additions & 0 deletions scripts/apps/authoring-react/authoring-angular-integration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/* eslint-disable react/display-name */
/* eslint-disable react/no-multi-comp */
import {assertNever} from 'core/helpers/typescript-helpers';
import {DeskAndStage} from './subcomponents/desk-and-stage';
import {LockInfo} from './subcomponents/lock-info';
import {Button, NavButton} from 'superdesk-ui-framework/react';
import {
IArticle,
ITopBarWidget,
IExposedFromAuthoring,
IAuthoringOptions,
} from 'superdesk-api';
import {appConfig} from 'appConfig';
import {ITEM_STATE} from 'apps/archive/constants';
import React from 'react';
import {gettext} from 'core/utils';
import {sdApi} from 'api';
import {AuthoringIntegrationWrapper} from './authoring-integration-wrapper';
import ng from 'core/services/ng';

export interface IProps {
itemId: IArticle['_id'];
}

function onClose() {
ng.get('authoringWorkspace').close();
ng.get('$rootScope').$applyAsync();
}

function getInlineToolbarActions(options: IExposedFromAuthoring<IArticle>): IAuthoringOptions<IArticle> {
const {
item,
hasUnsavedChanges,
handleUnsavedChanges,
save,
initiateClosing,
keepChangesAndClose,
stealLock,
} = options;
const itemState: ITEM_STATE = item.state;

const saveButton: ITopBarWidget<IArticle> = {
group: 'end',
priority: 0.2,
component: () => (
<Button
text={gettext('Save')}
style="filled"
type="primary"
disabled={!hasUnsavedChanges()}
onClick={() => {
save();
}}
/>
),
availableOffline: true,
};

const closeButton: ITopBarWidget<IArticle> = {
group: 'end',
priority: 0.1,
component: () => (
<Button
text={gettext('Close')}
style="hollow"
onClick={() => {
initiateClosing();
}}
/>
),
availableOffline: true,
};

const minimizeButton: ITopBarWidget<IArticle> = {
group: 'end',
priority: 0.3,
component: () => (
<NavButton
text={gettext('Minimize')}
onClick={() => {
keepChangesAndClose();
}}
icon="minimize"
iconSize="big"
/>
),
availableOffline: true,
};

switch (itemState) {
case ITEM_STATE.DRAFT:
return {
readOnly: false,
actions: [saveButton, minimizeButton],
};

case ITEM_STATE.SUBMITTED:
case ITEM_STATE.IN_PROGRESS:
case ITEM_STATE.ROUTED:
case ITEM_STATE.FETCHED:
case ITEM_STATE.UNPUBLISHED:
// eslint-disable-next-line no-case-declarations
const actions: Array<ITopBarWidget<IArticle>> = [
minimizeButton,
closeButton,
];

actions.push({
group: 'start',
priority: 0.2,
component: ({entity}) => <DeskAndStage article={entity} />,
availableOffline: false,
});

// FINISH: ensure locking is available in generic version of authoring
actions.push({
group: 'start',
priority: 0.1,
component: ({entity}) => (
<LockInfo
article={entity}
unlock={() => {
stealLock();
}}
/>
),
availableOffline: false,
});

if (sdApi.article.isLockedInCurrentSession(item)) {
actions.push(saveButton);
}

if (
sdApi.article.isLockedInCurrentSession(item)
&& appConfig.features.customAuthoringTopbar.toDesk === true
&& sdApi.article.isPersonal(item) !== true
) {
actions.push({
group: 'middle',
priority: 0.2,
component: () => (
<Button
text={gettext('TD')}
style="filled"
onClick={() => {
handleUnsavedChanges()
.then(() => sdApi.article.sendItemToNextStage(item))
.then(() => initiateClosing());
}}
/>
),
availableOffline: false,
});
}

return {
readOnly: sdApi.article.isLockedInCurrentSession(item) !== true,
actions: actions,
};

case ITEM_STATE.INGESTED:
return {
readOnly: true,
actions: [], // fetch
};

case ITEM_STATE.SPIKED:
return {
readOnly: true,
actions: [], // un-spike
};

case ITEM_STATE.SCHEDULED:
return {
readOnly: true,
actions: [], // un-schedule
};

case ITEM_STATE.PUBLISHED:
case ITEM_STATE.CORRECTED:
return {
readOnly: true,
actions: [], // correct update kill takedown
};

case ITEM_STATE.BEING_CORRECTED:
return {
readOnly: true,
actions: [], // cancel correction
};

case ITEM_STATE.CORRECTION:
return {
readOnly: false,
actions: [], // cancel correction, save, publish
};

case ITEM_STATE.KILLED:
case ITEM_STATE.RECALLED:
return {
readOnly: true,
actions: [], // NONE
};
default:
assertNever(itemState);
}
}

export class AuthoringAngularIntegration extends React.PureComponent<IProps> {
render(): React.ReactNode {
return (
<div className="sd-authoring-react">
<AuthoringIntegrationWrapper
itemId={this.props.itemId}
onClose={onClose}
getInlineToolbarActions={getInlineToolbarActions}
/>
</div>
);
}
}
Loading