* WIP * feat: Add collection.memberships endpoint * feat: Add ability to filter collection.memberships with query * WIP * Merge stashed work * feat: Add ability to filter memberships by permission * continued refactoring * paginated list component * Collection member management * fix: Incorrect policy data sent down after collection.update * Reduce duplication, add empty state * cleanup * fix: Modal close should be a real button * fix: Allow opening edit from modal * fix: remove unused methods * test: fix * Passing test suite * Refactor * fix: Flow UI errors * test: Add collections.update tests * lint * test: moar tests * fix: Missing scopes, more missing tests * fix: Handle collection privacy change over socket * fix: More membership scopes * fix: view endpoint permissions * fix: respond to privacy change on socket event * policy driven menus * fix: share endpoint policies * chore: Use policies to drive documents UI * alignment * fix: Header height * fix: Correct behavior when collection becomes private * fix: Header height for read-only collection * send id's over socket instead of serialized objects * fix: Remote policy change * fix: reduce collection fetching * More websocket efficiencies * fix: Document collection pinning * fix: Restored ability to edit drafts fix: Removed ability to star drafts * fix: Require write permissions to pin doc to collection * fix: Header title overlaying document actions at small screen sizes * fix: Jank on load caused by previous commit * fix: Double collection fetch post-publish * fix: Hide publish button if draft is in no longer accessible collection * fix: Always allow deleting drafts fix: Improved handling of deleted documents * feat: Show collections in drafts view feat: Show more obvious 'draft' badge on documents * fix: incorrect policies after publish to private collection * fix: Duplicating a draft publishes it
221 lines
5.9 KiB
JavaScript
221 lines
5.9 KiB
JavaScript
// @flow
|
|
import * as React from 'react';
|
|
import { Redirect } from 'react-router-dom';
|
|
import { observable } from 'mobx';
|
|
import { inject, observer } from 'mobx-react';
|
|
|
|
import Document from 'models/Document';
|
|
import UiStore from 'stores/UiStore';
|
|
import AuthStore from 'stores/AuthStore';
|
|
import CollectionStore from 'stores/CollectionsStore';
|
|
import PoliciesStore from 'stores/PoliciesStore';
|
|
import {
|
|
documentMoveUrl,
|
|
documentEditUrl,
|
|
documentHistoryUrl,
|
|
newDocumentUrl,
|
|
} from 'utils/routeHelpers';
|
|
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
|
|
|
type Props = {
|
|
ui: UiStore,
|
|
auth: AuthStore,
|
|
position?: 'left' | 'right' | 'center',
|
|
document: Document,
|
|
collections: CollectionStore,
|
|
policies: PoliciesStore,
|
|
className: string,
|
|
showPrint?: boolean,
|
|
showToggleEmbeds?: boolean,
|
|
showPin?: boolean,
|
|
onOpen?: () => void,
|
|
onClose?: () => void,
|
|
};
|
|
|
|
@observer
|
|
class DocumentMenu extends React.Component<Props> {
|
|
@observable redirectTo: ?string;
|
|
|
|
componentDidUpdate() {
|
|
this.redirectTo = undefined;
|
|
}
|
|
|
|
handleNewChild = (ev: SyntheticEvent<>) => {
|
|
const { document } = this.props;
|
|
this.redirectTo = newDocumentUrl(document.collectionId, document.id);
|
|
};
|
|
|
|
handleDelete = (ev: SyntheticEvent<>) => {
|
|
const { document } = this.props;
|
|
this.props.ui.setActiveModal('document-delete', { document });
|
|
};
|
|
|
|
handleDocumentHistory = () => {
|
|
this.redirectTo = documentHistoryUrl(this.props.document);
|
|
};
|
|
|
|
handleMove = (ev: SyntheticEvent<>) => {
|
|
this.redirectTo = documentMoveUrl(this.props.document);
|
|
};
|
|
|
|
handleEdit = (ev: SyntheticEvent<>) => {
|
|
this.redirectTo = documentEditUrl(this.props.document);
|
|
};
|
|
|
|
handleDuplicate = async (ev: SyntheticEvent<>) => {
|
|
const duped = await this.props.document.duplicate();
|
|
|
|
// when duplicating, go straight to the duplicated document content
|
|
this.redirectTo = duped.url;
|
|
this.props.ui.showToast('Document duplicated');
|
|
};
|
|
|
|
handleArchive = async (ev: SyntheticEvent<>) => {
|
|
await this.props.document.archive();
|
|
this.props.ui.showToast('Document archived');
|
|
};
|
|
|
|
handleRestore = async (ev: SyntheticEvent<>) => {
|
|
await this.props.document.restore();
|
|
this.props.ui.showToast('Document restored');
|
|
};
|
|
|
|
handlePin = (ev: SyntheticEvent<>) => {
|
|
this.props.document.pin();
|
|
};
|
|
|
|
handleUnpin = (ev: SyntheticEvent<>) => {
|
|
this.props.document.unpin();
|
|
};
|
|
|
|
handleStar = (ev: SyntheticEvent<>) => {
|
|
this.props.document.star();
|
|
};
|
|
|
|
handleUnstar = (ev: SyntheticEvent<>) => {
|
|
this.props.document.unstar();
|
|
};
|
|
|
|
handleExport = (ev: SyntheticEvent<>) => {
|
|
this.props.document.download();
|
|
};
|
|
|
|
handleShareLink = async (ev: SyntheticEvent<>) => {
|
|
const { document } = this.props;
|
|
if (!document.shareUrl) await document.share();
|
|
|
|
this.props.ui.setActiveModal('document-share', { document });
|
|
};
|
|
|
|
render() {
|
|
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
|
|
|
|
const {
|
|
policies,
|
|
document,
|
|
position,
|
|
className,
|
|
showPrint,
|
|
showPin,
|
|
auth,
|
|
onOpen,
|
|
onClose,
|
|
} = this.props;
|
|
|
|
const can = policies.abilities(document.id);
|
|
const canShareDocuments = can.share && auth.team && auth.team.sharing;
|
|
|
|
return (
|
|
<DropdownMenu
|
|
className={className}
|
|
position={position}
|
|
onOpen={onOpen}
|
|
onClose={onClose}
|
|
>
|
|
{can.unarchive && (
|
|
<DropdownMenuItem onClick={this.handleRestore}>
|
|
Restore
|
|
</DropdownMenuItem>
|
|
)}
|
|
{showPin &&
|
|
(document.pinned
|
|
? can.unpin && (
|
|
<DropdownMenuItem onClick={this.handleUnpin}>
|
|
Unpin
|
|
</DropdownMenuItem>
|
|
)
|
|
: can.pin && (
|
|
<DropdownMenuItem onClick={this.handlePin}>
|
|
Pin to collection
|
|
</DropdownMenuItem>
|
|
))}
|
|
{document.isStarred
|
|
? can.unstar && (
|
|
<DropdownMenuItem onClick={this.handleUnstar}>
|
|
Unstar
|
|
</DropdownMenuItem>
|
|
)
|
|
: can.star && (
|
|
<DropdownMenuItem onClick={this.handleStar}>
|
|
Star
|
|
</DropdownMenuItem>
|
|
)}
|
|
{canShareDocuments && (
|
|
<DropdownMenuItem
|
|
onClick={this.handleShareLink}
|
|
title="Create a public share link"
|
|
>
|
|
Share link…
|
|
</DropdownMenuItem>
|
|
)}
|
|
<hr />
|
|
{can.read && (
|
|
<DropdownMenuItem onClick={this.handleDocumentHistory}>
|
|
Document history
|
|
</DropdownMenuItem>
|
|
)}
|
|
{can.update && (
|
|
<DropdownMenuItem
|
|
onClick={this.handleNewChild}
|
|
title="Create a new child document for the current document"
|
|
>
|
|
New child document
|
|
</DropdownMenuItem>
|
|
)}
|
|
{can.update && (
|
|
<DropdownMenuItem onClick={this.handleEdit}>Edit</DropdownMenuItem>
|
|
)}
|
|
{can.update && (
|
|
<DropdownMenuItem onClick={this.handleDuplicate}>
|
|
Duplicate
|
|
</DropdownMenuItem>
|
|
)}
|
|
{can.archive && (
|
|
<DropdownMenuItem onClick={this.handleArchive}>
|
|
Archive
|
|
</DropdownMenuItem>
|
|
)}
|
|
{can.delete && (
|
|
<DropdownMenuItem onClick={this.handleDelete}>
|
|
Delete…
|
|
</DropdownMenuItem>
|
|
)}
|
|
{can.move && (
|
|
<DropdownMenuItem onClick={this.handleMove}>Move…</DropdownMenuItem>
|
|
)}
|
|
<hr />
|
|
{can.download && (
|
|
<DropdownMenuItem onClick={this.handleExport}>
|
|
Download
|
|
</DropdownMenuItem>
|
|
)}
|
|
{showPrint && (
|
|
<DropdownMenuItem onClick={window.print}>Print</DropdownMenuItem>
|
|
)}
|
|
</DropdownMenu>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default inject('ui', 'auth', 'collections', 'policies')(DocumentMenu);
|