Skip to content

Commit

Permalink
Social: Disable sharing UI for invalid connections (#39346)
Browse files Browse the repository at this point in the history
* Move checkConnectionCode utility function to a common place

* Create getBrokenConnections selector

* Create useIsSharingPossible hook

* Disable reshare if sharing not possible at all

* Disable post publish share status if not sharing

* Improve JSDoc comment

* Add changelog

* Add explanatory comment
  • Loading branch information
manzoorwanijk authored Sep 13, 2024
1 parent 4e697d6 commit 313dad5
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Social: Disable reshare button and hide post publish share status when all the enabled connections are invalid
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import usePublicizeConfig from '../../hooks/use-publicize-config';
import useSocialMediaConnections from '../../hooks/use-social-media-connections';
import { store } from '../../social-store';
import { Connection } from '../../social-store/types';
import { checkConnectionCode } from '../../utils/connections';
import Notice from '../notice';
import { useServiceLabel } from '../services/use-service-label';
import styles from './styles.module.scss';
import { checkConnectionCode } from './utils';

export const BrokenConnectionsNotice: React.FC = () => {
const { connections } = useSocialMediaConnections();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { createInterpolateElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { usePublicizeConfig } from '../../..';
import useSocialMediaConnections from '../../hooks/use-social-media-connections';
import { checkConnectionCode } from '../../utils/connections';
import Notice from '../notice';
import { checkConnectionCode } from './utils';

export const UnsupportedConnectionsNotice: React.FC = () => {
const { connections } = useSocialMediaConnections();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useDispatch, useSelect } from '@wordpress/data';
import { PluginPostPublishPanel } from '@wordpress/edit-post';
import { store as editorStore } from '@wordpress/editor';
import { useIsSharingPossible } from '../../hooks/use-is-sharing-possible';
import { usePostMeta } from '../../hooks/use-post-meta';
import { usePostPrePublishValue } from '../../hooks/use-post-pre-publish-value';
import { usePostJustPublished } from '../../hooks/use-saving-post';
Expand All @@ -27,11 +28,13 @@ export function PostPublishShareStatus() {
};
}, [] );

const isSharingPossible = usePostPrePublishValue( useIsSharingPossible() );

const enabledConnections = usePostPrePublishValue(
useSelect( select => select( socialStore ).getEnabledConnections(), [] )
);

const willPostBeShared = isPublicizeEnabled && enabledConnections.length > 0;
const willPostBeShared = isPublicizeEnabled && enabledConnections.length > 0 && isSharingPossible;

const showStatus = featureFlags.useShareStatus && willPostBeShared && isPostPublised;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { store as editorStore } from '@wordpress/editor';
import { useEffect, useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { useIsSharingPossible } from '../../hooks/use-is-sharing-possible';
import usePublicizeConfig from '../../hooks/use-publicize-config';
import useSharePost from '../../hooks/use-share-post';
import useSocialMediaConnections from '../../hooks/use-social-media-connections';
import { store as socialStore } from '../../social-store';

/**
Expand Down Expand Up @@ -45,7 +45,6 @@ function showSuccessNotice() {
* @return {object} A button component that will share the current post when clicked.
*/
export function SharePostButton() {
const { hasEnabledConnections } = useSocialMediaConnections();
const { isPublicizeEnabled } = usePublicizeConfig();
const { isFetching, isError, isSuccess, doPublicize } = useSharePost();
const isPostPublished = useSelect( select => select( editorStore ).isCurrentPostPublished(), [] );
Expand All @@ -68,6 +67,8 @@ export function SharePostButton() {
showSuccessNotice();
}, [ isFetching, isError, isSuccess ] );

const isSharingPossible = useIsSharingPossible();

/*
* Disabled button when
* - sharing is disabled
Expand All @@ -76,7 +77,7 @@ export function SharePostButton() {
* - is sharing post
*/
const isButtonDisabled =
! isPublicizeEnabled || ! hasEnabledConnections || ! isPostPublished || isFetching;
! isPublicizeEnabled || ! isSharingPossible || ! isPostPublished || isFetching;

const sharePost = useCallback( async () => {
if ( ! isPostPublished ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useSelect } from '@wordpress/data';
import useAttachedMedia from '../../hooks/use-attached-media';
import useFeaturedImage from '../../hooks/use-featured-image';
import useMediaDetails from '../../hooks/use-media-details';
import useMediaRestrictions from '../../hooks/use-media-restrictions';
import { NO_MEDIA_ERROR } from '../../hooks/use-media-restrictions/constants';
import useSocialMediaConnections from '../../hooks/use-social-media-connections';
import { store as socialStore } from '../../social-store';

/**
* Returns whether sharing is possible based on the current media and connections.
*
* Returns true if at least one of the enabled connections is valid for sharing, false otherwise.
*
* @return {boolean} Whether sharing is possible.
*/
export function useIsSharingPossible() {
const { enabledConnections } = useSocialMediaConnections();
const { attachedMedia } = useAttachedMedia();
const featuredImageId = useFeaturedImage();

const mediaId = attachedMedia[ 0 ]?.id || featuredImageId;
const { validationErrors, isConvertible } = useMediaRestrictions(
enabledConnections,
useMediaDetails( mediaId )[ 0 ]
);

const brokenConnectionIds = useSelect( select => {
return select( socialStore )
.getBrokenConnections()
.map( c => c.connection_id );
}, [] );

// Sharing will be possible if any of the enabled connections are valid for sharing.
return enabledConnections.some( function isValidForSharing( { connection_id, service_name } ) {
// Return early if the connection is broken
if ( brokenConnectionIds.includes( connection_id ) ) {
return false;
}

// Return early if the connection is not in the validation errors
if ( ! ( connection_id in validationErrors ) ) {
return true;
}

// We need some media for Instagram
if (
service_name === 'instagram-business' &&
Object.values( validationErrors ).includes( NO_MEDIA_ERROR )
) {
return false;
}

// Media won't be shared if it's not convertible
return isConvertible;
} );
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ function getHumanReadableError( result ) {
/**
* A hook to get the necessary data and callbacks to reshare a post.
*
* @param {number} postId - The ID of the post to share.
* @return { { doPublicize: (connectionsToSkip: Array<string>) => Promise<void>, data: object } } The doPublicize callback to share the post.
* @param {number} [postId] - The ID of the post to share.
* @return { { doPublicize: (connectionsToSkip?: Array<string>) => Promise<void>, data: object } } The doPublicize callback to share the post.
*/
export default function useSharePost( postId ) {
// Sharing data.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { checkConnectionCode } from '../../utils/connections';
import { REQUEST_TYPE_DEFAULT } from '../actions/constants';

/**
Expand All @@ -23,6 +24,24 @@ export function getConnectionById( state, connectionId ) {
return getConnections( state ).find( connection => connection.connection_id === connectionId );
}

/**
* Returns the broken connections.
*
* @param {import("../types").SocialStoreState} state - State object.
* @return {Array<import("../types").Connection>} List of broken connections.
*/
export function getBrokenConnections( state ) {
return getConnections( state ).filter( connection => {
return (
connection.status === 'broken' ||
// This is a legacy check for connections that are not healthy.
// TODO remove this check when we are sure that all connections have
// the status property (same schema for connections endpoints), e.g. on Simple/Atomic sites
checkConnectionCode( connection, 'broken' )
);
} );
}

/**
* Returns connections by service name/ID.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Connection } from '../../social-store/types';
import { Connection } from '../social-store/types';

export const checkConnectionCode = ( connection: Connection, code: string ) => {
return false === connection.is_healthy && code === ( connection.error_code ?? 'broken' );
Expand Down

0 comments on commit 313dad5

Please sign in to comment.