Skip to content

Commit 274d987

Browse files
authored
List Views: Add a "Copy Link" menu item for each post and page. (#37049)
1 parent 2cda95b commit 274d987

File tree

6 files changed

+143
-6
lines changed

6 files changed

+143
-6
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/** @format */
2+
3+
/**
4+
* External dependencies
5+
*/
6+
7+
import React from 'react';
8+
import PropTypes from 'prop-types';
9+
import { noop } from 'lodash';
10+
import classNames from 'classnames';
11+
import Gridicon from 'components/gridicon';
12+
13+
/**
14+
* Internal dependencies
15+
*/
16+
import ClipboardButton from 'components/forms/clipboard-button';
17+
18+
function PopoverMenuItemClipboard( {
19+
children,
20+
className,
21+
text,
22+
onCopy = noop,
23+
icon = 'clipboard',
24+
...rest
25+
} ) {
26+
return (
27+
<ClipboardButton
28+
text={ text }
29+
onCopy={ onCopy }
30+
role="menuitem"
31+
tabIndex="-1"
32+
className={ classNames( 'popover__menu-item', className ) }
33+
{ ...rest }
34+
>
35+
<Gridicon icon={ icon } size={ 18 } />
36+
{ children }
37+
</ClipboardButton>
38+
);
39+
}
40+
41+
PopoverMenuItemClipboard.propTypes = {
42+
className: PropTypes.string,
43+
icon: PropTypes.string,
44+
onCopy: PropTypes.func,
45+
text: PropTypes.string,
46+
};
47+
48+
export default PopoverMenuItemClipboard;

client/my-sites/pages/page/index.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import CompactCard from 'components/card/compact';
1818
import Gridicon from 'components/gridicon';
1919
import EllipsisMenu from 'components/ellipsis-menu';
2020
import PopoverMenuItem from 'components/popover/menu-item';
21+
import PopoverMenuItemClipboard from 'components/popover/menu-item-clipboard';
2122
import Notice from 'components/notice';
2223
import NoticeAction from 'components/notice/notice-action';
2324
import SiteIcon from 'blocks/site-icon';
@@ -35,7 +36,7 @@ import { recordGoogleEvent } from 'state/analytics/actions';
3536
import { setPreviewUrl } from 'state/ui/preview/actions';
3637
import { setLayoutFocus } from 'state/ui/layout-focus/actions';
3738
import { savePost, deletePost, trashPost, restorePost } from 'state/posts/actions';
38-
import { withoutNotice } from 'state/notices/actions';
39+
import { infoNotice, withoutNotice } from 'state/notices/actions';
3940
import { shouldRedirectGutenberg } from 'state/selectors/should-redirect-gutenberg';
4041
import getEditorUrl from 'state/selectors/get-editor-url';
4142
import { getEditorDuplicatePostPath } from 'state/ui/editor/selectors';
@@ -306,7 +307,7 @@ class Page extends Component {
306307
];
307308
}
308309

309-
getCopyItem() {
310+
getCopyPageItem() {
310311
const { wpAdminGutenberg, page: post, duplicateUrl } = this.props;
311312
if (
312313
! includes( [ 'draft', 'future', 'pending', 'private', 'publish' ], post.status ) ||
@@ -318,11 +319,20 @@ class Page extends Component {
318319
return (
319320
<PopoverMenuItem onClick={ this.copyPage } href={ duplicateUrl }>
320321
<Gridicon icon="clipboard" size={ 18 } />
321-
{ this.props.translate( 'Copy' ) }
322+
{ this.props.translate( 'Copy Page' ) }
322323
</PopoverMenuItem>
323324
);
324325
}
325326

327+
getCopyLinkItem() {
328+
const { page, translate } = this.props;
329+
return (
330+
<PopoverMenuItemClipboard text={ page.URL } onCopy={ this.copyPageLink } icon={ 'link' }>
331+
{ translate( 'Copy Link' ) }
332+
</PopoverMenuItemClipboard>
333+
);
334+
}
335+
326336
getRestoreItem() {
327337
if ( this.props.page.status !== 'trash' || ! utils.userCan( 'delete_post', this.props.page ) ) {
328338
return null;
@@ -421,7 +431,8 @@ class Page extends Component {
421431
const postsPageItem = this.getPostsPageItem();
422432
const restoreItem = this.getRestoreItem();
423433
const sendToTrashItem = this.getSendToTrashItem();
424-
const copyItem = this.getCopyItem();
434+
const copyPageItem = this.getCopyPageItem();
435+
const copyLinkItem = this.getCopyLinkItem();
425436
const statsItem = this.getStatsItem();
426437
const moreInfoItem = this.popoverMoreInfo();
427438
const hasMenuItems =
@@ -444,7 +455,8 @@ class Page extends Component {
444455
{ publishItem }
445456
{ viewItem }
446457
{ statsItem }
447-
{ copyItem }
458+
{ copyPageItem }
459+
{ copyLinkItem }
448460
{ restoreItem }
449461
{ frontPageItem }
450462
{ postsPageItem }
@@ -653,6 +665,13 @@ class Page extends Component {
653665
this.props.recordEvent( 'Clicked Copy Page' );
654666
};
655667

668+
copyPageLink = () => {
669+
this.props.infoNotice( this.props.translate( 'Link copied to clipboard.' ), {
670+
duration: 3000,
671+
} );
672+
this.props.recordEvent( 'Clicked Copy Page Link' );
673+
};
674+
656675
handleMenuToggle = isVisible => {
657676
if ( isVisible ) {
658677
// record a GA event when the menu is opened
@@ -688,6 +707,7 @@ const mapState = ( state, props ) => {
688707
};
689708

690709
const mapDispatch = {
710+
infoNotice,
691711
savePost: withoutNotice( savePost ),
692712
deletePost: withoutNotice( deletePost ),
693713
trashPost: withoutNotice( trashPost ),
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/** @format */
2+
3+
/**
4+
* External dependencies
5+
*/
6+
import React from 'react';
7+
import PropTypes from 'prop-types';
8+
import { connect } from 'react-redux';
9+
import { localize, translate } from 'i18n-calypso';
10+
11+
/**
12+
* Internal dependencies
13+
*/
14+
import PopoverMenuItemClipboard from 'components/popover/menu-item-clipboard';
15+
import { getPost } from 'state/posts/selectors';
16+
import { bumpStat, recordTracksEvent } from 'state/analytics/actions';
17+
import { bumpStatGenerator } from './utils';
18+
import { infoNotice } from 'state/notices/actions';
19+
20+
function PostActionsEllipsisMenuCopyLink( { onCopyLinkClick, copyLink } ) {
21+
return (
22+
<PopoverMenuItemClipboard text={ copyLink } onCopy={ onCopyLinkClick } icon={ 'link' }>
23+
{ translate( 'Copy Link' ) }
24+
</PopoverMenuItemClipboard>
25+
);
26+
}
27+
28+
PostActionsEllipsisMenuCopyLink.propTypes = {
29+
onCopyLinkClick: PropTypes.func,
30+
copyLink: PropTypes.string,
31+
translate: PropTypes.func,
32+
};
33+
34+
const mapStateToProps = ( state, { globalId } ) => {
35+
const post = getPost( state, globalId );
36+
if ( ! post ) {
37+
return {};
38+
}
39+
40+
return {
41+
copyLink: post.URL,
42+
type: post.type,
43+
};
44+
};
45+
46+
const mapDispatchToProps = { bumpStat, infoNotice, recordTracksEvent };
47+
48+
const mergeProps = ( stateProps, dispatchProps, ownProps ) => {
49+
const bumpCopyLinkStat = bumpStatGenerator( stateProps.type, 'copy_link', dispatchProps.bumpStat );
50+
const onCopyLinkClick = () => {
51+
dispatchProps.infoNotice( translate( 'Link copied to clipboard.' ), { duration: 3000 } );
52+
bumpCopyLinkStat();
53+
dispatchProps.recordTracksEvent( 'calypso_post_type_list_copy_link' );
54+
};
55+
return Object.assign( {}, ownProps, stateProps, dispatchProps, { onCopyLinkClick } );
56+
};
57+
58+
export default connect(
59+
mapStateToProps,
60+
mapDispatchToProps,
61+
mergeProps
62+
)( localize( PostActionsEllipsisMenuCopyLink ) );

client/my-sites/post-type-list/post-actions-ellipsis-menu/duplicate.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function PostActionsEllipsisMenuDuplicate( {
4141
return (
4242
<PopoverMenuItem href={ duplicateUrl } onClick={ onDuplicateClick } icon="clipboard">
4343
<QueryJetpackModules siteId={ siteId } />
44-
{ translate( 'Copy', { context: 'verb' } ) }
44+
{ translate( 'Copy Post' ) }
4545
</PopoverMenuItem>
4646
);
4747
}

client/my-sites/post-type-list/post-actions-ellipsis-menu/index.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import PostActionsEllipsisMenuTrash from './trash';
2020
import PostActionsEllipsisMenuView from './view';
2121
import PostActionsEllipsisMenuRestore from './restore';
2222
import PostActionsEllipsisMenuDuplicate from './duplicate';
23+
import PostActionsEllipsisMenuCopyLink from './copy-link';
2324

2425
/**
2526
* Style dependencies
@@ -39,6 +40,7 @@ export default function PostActionsEllipsisMenu( { globalId, includeDefaultActio
3940
<PostActionsEllipsisMenuShare key="share" />,
4041
<PostActionsEllipsisMenuRestore key="restore" />,
4142
<PostActionsEllipsisMenuDuplicate key="duplicate" />,
43+
<PostActionsEllipsisMenuCopyLink key="copyLink" />,
4244
<PostActionsEllipsisMenuTrash key="trash" />
4345
);
4446
}

client/my-sites/post-type-list/post-actions-ellipsis-menu/style.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@
1212
margin-right: -16px;
1313
}
1414
}
15+
16+
.ellipsis-menu__menu .button .gridicon {
17+
top: inherit;
18+
margin-top: inherit;
19+
}

0 commit comments

Comments
 (0)