Skip to content

Commit

Permalink
Social: Add Social Share Status modal for published posts (#39051)
Browse files Browse the repository at this point in the history
* Register meta with name of `jetpack_social_post_shares`

* Add new meta to usePostMeta hook

* Add bare components with fetching data

* changelog

* Add moment

* Populate the modal

* Remove moment and schema as we use store

* Improve share retrieval and css

* Only order the list

* Rebase onto the global modal

* Remove leftover file

* revert bad change

* Fix minification issue

* remove external_id from types

* Improve normalizeShareStatus

---------

Co-authored-by: Manzoor Wani <[email protected]>
  • Loading branch information
gmjuhasz and manzoorwanijk authored Aug 29, 2024
1 parent 79a7757 commit 48ac019
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add share status log modal to published posts

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Modal } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { store as socialStore } from '../../social-store';
import { ShareList } from './share-list';
import styles from './styles.module.scss';

/**
Expand All @@ -14,13 +15,15 @@ export function ShareStatusModal() {
const { closeShareStatusModal } = useDispatch( socialStore );

return (
<Modal
className={ styles.modal }
onRequestClose={ closeShareStatusModal }
title={ __( 'Share status', 'jetpack' ) }
>
Content goes here
</Modal>
<div className={ styles.wrapper }>
<Modal
onRequestClose={ closeShareStatusModal }
title={ __( 'Sharing status', 'jetpack' ) }
className={ styles.modal }
>
<ShareList />
</Modal>
</div>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { getDate, humanTimeDiff } from '@wordpress/date';
import ConnectionIcon from '../connection-icon';
import { ShareStatusAction } from './share-status-action';
import { ShareStatusLabel } from './share-status-label';
import styles from './styles.module.scss';

/**
*
* ShareInfo component
*
* @param {object} props - component props
* @param {object} props.share - share object
* @return {import('react').ReactNode} - React element
*/
export function ShareInfo( { share } ) {
const { service, external_name, profile_picture, timestamp, status, message } = share;

return (
<div className={ styles[ 'share-item' ] }>
<ConnectionIcon
serviceName={ service }
label={ external_name }
profilePicture={ profile_picture }
/>
<div className={ styles[ 'share-item-name-wrapper' ] }>
<div className={ styles[ 'share-item-name' ] }>{ external_name }</div>
</div>
<div>
{
// @ts-expect-error - humanTimeDiff is incorrectly typed, first argument can be a timestamp
humanTimeDiff( timestamp * 1000, getDate() )
}
</div>
<ShareStatusLabel status={ status } message={ message } />
<ShareStatusAction status={ status } shareLink={ 'success' === status ? message : '' } />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Spinner } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
import { __ } from '@wordpress/i18n';
import { store as socialStore } from '../../social-store';
import { ShareInfo } from './share-info';
import styles from './styles.module.scss';

/**
* ShareList component
*
* @return {import('react').ReactNode} - Share status modal component.
*/
export function ShareList() {
const { shareStatus } = useSelect( select => {
const store = select( socialStore );
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- `@wordpress/editor` is a nightmare to work with TypeScript
const _editorStore = select( editorStore ) as any;

return {
shareStatus: store.getPostShareStatus( _editorStore.getCurrentPostId() ),
};
}, [] );

return (
<div className="connection-management">
{ shareStatus.loading && (
<div className={ styles.spinner }>
<Spinner /> { __( 'Loading…', 'jetpack' ) }
</div>
) }
{ shareStatus.shares.length > 0 && (
<ul className={ styles[ 'share-log-list' ] }>
{ shareStatus.shares.map( ( share, idx ) => (
<li
key={ `${ share.external_id || share.connection_id }${ idx }}` }
className={ styles[ 'share-log-list-item' ] }
>
<ShareInfo share={ share } />
</li>
) ) }
</ul>
) }
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ExternalLink } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import styles from './styles.module.scss';

/**
*
* Share status action component.
*
* @param {object} props - component props
* @param {boolean} props.status - status of the share
* @param {string} props.shareLink - link to the share
* @return {import('react').ReactNode} - React element
*/
export function ShareStatusAction( { status, shareLink } ) {
return (
<div className={ styles[ 'share-status-action-wrapper' ] }>
{ 'success' !== status ? (
<span>Retry</span>
) : (
<ExternalLink className={ styles[ 'profile-link' ] } href={ shareLink }>
{ __( 'View', 'jetpack' ) }
</ExternalLink>
) }
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { IconTooltip, Text } from '@automattic/jetpack-components';
import { __, _x } from '@wordpress/i18n';
import { Icon, check } from '@wordpress/icons';
import clsx from 'clsx';
import React from 'react';
import styles from './styles.module.scss';

/**
*
* Share status label component.
*
* @param {object} props - component props
* @param {boolean} props.status - status of the share
* @param {string} props.message - link to the share, or error message if failed
* @return {import('react').ReactNode} - React element
*/
export function ShareStatusLabel( { status, message } ) {
const isSuccessful = 'success' === status;

const icon = isSuccessful ? (
<Icon className={ styles[ 'share-status-icon' ] } icon={ check } />
) : (
<IconTooltip
title={ __( 'Sharing failed with the following message:', 'jetpack' ) }
className={ styles[ 'share-status-icon-tooltip' ] }
>
<Text variant="body-small">{ message }</Text>
</IconTooltip>
);

return (
<div
className={ clsx( styles[ 'share-status-wrapper' ], {
[ styles[ 'share-status-success' ] ]: isSuccessful,
[ styles[ 'share-status-failure' ] ]: ! isSuccessful,
} ) }
>
<div className={ styles[ 'share-status-icon' ] }>{ icon }</div>
<div className={ styles[ 'share-status-label' ] }>
{ isSuccessful
? _x( 'Shared', 'The sharing is successful', 'jetpack' )
: __( 'Failed', 'jetpack' ) }
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,88 @@
margin-top: 1rem;
padding-block: 1rem;
}

.wrapper {
margin-top: 1rem;
padding-block: 1rem;
}

.spinner{
margin: 0 1rem 1rem 1rem;
}

.modal {
width: 60rem;
}

.share-log-list {
outline: 1px solid var(--jp-gray-5);
border-radius: 4px;
margin: 0;
width: 100%;

.share-log-list-item {
margin-bottom: 0px;
padding: 0.8rem 1rem;

&:not(:last-child) {
border-bottom: 1px solid var(--jp-gray-5);
}
}

.share-item {
display: flex;
gap: 1rem;
align-items: center;
}
}

.share-item-name-wrapper {
display: flex;
flex-direction: column;
gap: 0.5rem;
flex: 1;
overflow: auto;
}

.share-item-name {
display: flex;
align-items: center;
}

.share-status-wrapper {
display: flex;
align-items: center;
width: 5rem;

&.share-status-success {
color: var(--jp-green-50);
}

&.share-status-failure {
color: var(--jp-red-50);
height: 29px;
}
}

.share-status-label {
flex: 1;
}

.share-status-icon-tooltip {
width: 24px;
top: 2px;
margin-inline-start: 2px;

> button {
color: var(--jp-red-50) !important;
}
}

.share-status-icon {
fill: var(--jp-green-50);
}

.share-status-action-wrapper {
width: 3rem;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import apiFetch from '@wordpress/api-fetch';
import { store as editorStore } from '@wordpress/editor';
import { normalizeShareStatus } from '../utils/share-status';
import { setConnections } from './actions/connection-data';
import { setJetpackSettings } from './actions/jetpack-settings';
import { fetchPostShareStatus, receivePostShareStaus } from './actions/share-status';
Expand Down Expand Up @@ -74,10 +75,12 @@ export function getPostShareStatus( _postId ) {

try {
dispatch( fetchPostShareStatus( postId ) );
const result = await apiFetch( {
let result = await apiFetch( {
path: `jetpack/v4/social/share-status/${ postId }`,
} );

result = normalizeShareStatus( result );

dispatch( receivePostShareStaus( result, postId ) );
} catch ( error ) {
dispatch( fetchPostShareStatus( postId, false ) );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PostShareStatus } from '../social-store/types';

/**
* Normalizes the share status object.
*
* @param {PostShareStatus} shareStatus - Share status object.
* @return {PostShareStatus} - Normalized share status object.
*/
export function normalizeShareStatus( shareStatus: PostShareStatus ) {
if ( shareStatus && 'shares' in shareStatus && shareStatus.done ) {
// Sort shares to show the latest shares on the top.
shareStatus.shares.sort( ( a, b ) => b.timestamp - a.timestamp );
}

return shareStatus;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add share status log modal to published posts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add share status log modal to published posts

0 comments on commit 48ac019

Please sign in to comment.