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

Social: Update media auto-conversion/validation notices in the editor #38499

Merged
merged 11 commits into from
Jul 30, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Social | Fixed and improved media auto conversion notices

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import useSocialMediaConnections from '../../hooks/use-social-media-connections'
import { store } from '../../social-store';
import { Connection } from '../../social-store/types';
import Notice from '../notice';
import { SupportedService, useSupportedServices } from '../services/use-supported-services';
import { useServiceLabel } from '../services/use-service-label';
import styles from './styles.module.scss';
import { checkConnectionCode } from './utils';

Expand Down Expand Up @@ -40,20 +40,12 @@ export const BrokenConnectionsNotice: React.FC = () => {
<ExternalLink href={ connectionsAdminUrl } />
);

const supportedServices = useSupportedServices();
const getServiceLabel = useServiceLabel();

if ( ! brokenConnections.length ) {
return null;
}

const servicesMap = supportedServices.reduce< Record< string, SupportedService > >(
( acc, service ) => {
acc[ service.ID ] = service;
return acc;
},
{}
);

// Group broken connections by service
// Since Object.groupBy is not supported widely yet, we use a manual grouping
const brokenConnectionsList = brokenConnections.reduce< Record< string, Array< Connection > > >(
Expand All @@ -73,12 +65,7 @@ export const BrokenConnectionsNotice: React.FC = () => {
{ __( 'Your following connections need to be reconnected:', 'jetpack' ) }
<ul>
{ Object.entries( brokenConnectionsList ).map( ( [ service_name, connectionsList ] ) => {
const serviceLabel =
// For Jetpack sites, we should have the service in the map
// But for WPCOM sites, we might not have the service in the map yet
servicesMap[ service_name ]?.label ||
// So we capitalize the service name
service_name[ 0 ].toUpperCase() + service_name.substring( 1 );
const serviceLabel = getServiceLabel( service_name );

return (
<li key={ service_name }>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,90 +8,45 @@

import { Disabled, PanelRow } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { Fragment, useMemo } from '@wordpress/element';
import { Fragment } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { usePublicizeConfig } from '../../..';
import useAttachedMedia from '../../hooks/use-attached-media';
import useDismissNotice from '../../hooks/use-dismiss-notice';
import useFeaturedImage from '../../hooks/use-featured-image';
import useImageGeneratorConfig from '../../hooks/use-image-generator-config';
import useMediaDetails from '../../hooks/use-media-details';
import useMediaRestrictions, { NO_MEDIA_ERROR } from '../../hooks/use-media-restrictions';
import useRefreshAutoConversionSettings from '../../hooks/use-refresh-auto-conversion-settings';
import useSocialMediaConnections from '../../hooks/use-social-media-connections';
import { store as socialStore } from '../../social-store';
import { ThemedConnectionsModal as ManageConnectionsModal } from '../manage-connections-modal';
import { AdvancedPlanNudge } from './advanced-plan-nudge';
import { AutoConversionNotice } from './auto-conversion-notice';
import { BrokenConnectionsNotice } from './broken-connections-notice';
import { ConnectionsList } from './connections-list';
import { InstagramNoMediaNotice } from './instagram-no-media-notice';
import { MediaValidationNotices } from './media-validation-notices';
import { SettingsButton } from './settings-button';
import { ShareCountInfo } from './share-count-info';
import { SharePostForm } from './share-post-form';
import styles from './styles.module.scss';
import { UnsupportedConnectionsNotice } from './unsupported-connections-notice';
import { ValidationNotice } from './validation-notice';

/**
* The Publicize form component. It contains the connection list, and the message box.
*
* @returns {object} - Publicize form component.
*/
export default function PublicizeForm() {
const { connections, hasConnections, hasEnabledConnections } = useSocialMediaConnections();
const { isEnabled: isSocialImageGeneratorEnabledForPost } = useImageGeneratorConfig();
const { shouldShowNotice, NOTICES } = useDismissNotice();
const { hasConnections, hasEnabledConnections } = useSocialMediaConnections();
const {
isPublicizeEnabled,
isPublicizeDisabledBySitePlan,
needsUserConnection,
userConnectionUrl,
} = usePublicizeConfig();

const { numberOfSharesRemaining, useAdminUiV1 } = useSelect( select => {
const { useAdminUiV1 } = useSelect( select => {
const store = select( socialStore );
return {
numberOfSharesRemaining: store.numberOfSharesRemaining(),
useAdminUiV1: store.useAdminUiV1(),
};
}, [] );

const Wrapper = isPublicizeDisabledBySitePlan ? Disabled : Fragment;

const isAutoConversionEnabled = useSelect(
gmjuhasz marked this conversation as resolved.
Show resolved Hide resolved
select => select( socialStore ).isAutoConversionEnabled(),
[]
);

const { attachedMedia } = useAttachedMedia();
const featuredImageId = useFeaturedImage();
const mediaId = attachedMedia[ 0 ]?.id || featuredImageId;

const { validationErrors, isConvertible } = useMediaRestrictions(
connections,
useMediaDetails( mediaId )[ 0 ],
{
isSocialImageGeneratorEnabledForPost,
}
);
const shouldAutoConvert = isAutoConversionEnabled && isConvertible;

const invalidIds = useMemo( () => Object.keys( validationErrors ), [ validationErrors ] );

const showValidationNotice = numberOfSharesRemaining !== 0 && invalidIds.length > 0;

const { refreshAutoConversionSettings } = useRefreshAutoConversionSettings();

if (
shouldAutoConvert &&
showValidationNotice &&
mediaId &&
shouldShowNotice( NOTICES.autoConversion )
) {
refreshAutoConversionSettings();
}

return (
<Wrapper>
{
Expand All @@ -106,17 +61,7 @@ export default function PublicizeForm() {
<ShareCountInfo />
<BrokenConnectionsNotice />
<UnsupportedConnectionsNotice />
{ shouldAutoConvert && showValidationNotice && mediaId && <AutoConversionNotice /> }
{ showValidationNotice &&
( Object.values( validationErrors ).includes( NO_MEDIA_ERROR ) ? (
<InstagramNoMediaNotice />
) : (
<ValidationNotice
connectionsCount={ connections.length }
invalidConnectionIdsCount={ invalidIds.length }
shouldAutoConvert={ shouldAutoConvert }
/>
) ) }
<MediaValidationNotices />
</>
) : null }
<PanelRow>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { getRedirectUrl } from '@automattic/jetpack-components';
import { ExternalLink } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { Fragment, useMemo } from '@wordpress/element';
import { __, _x } from '@wordpress/i18n';
import { getErrorLabel } from '../../hooks/use-media-restrictions/constants';
import { ValidationErrors } from '../../hooks/use-media-restrictions/types';
import { store as socialStore } from '../../social-store';
import Notice from '../notice';
import { useServiceLabel } from '../services/use-service-label';

export type MediaRequirementsNoticeProps = {
validationErrors: ValidationErrors;
};

export const MediaRequirementsNotice: React.FC< MediaRequirementsNoticeProps > = ( {
validationErrors,
} ) => {
const { getConnectionById } = useSelect( select => select( socialStore ), [] );

const getServiceLabel = useServiceLabel();

const errorTypesToServicesMap = useMemo( () => {
return Object.entries( validationErrors ).reduce< Record< string, Array< string > > >(
( map, [ connectionId, errorType ] ) => {
if ( ! errorType ) {
return map;
}

if ( ! map[ errorType ] ) {
map[ errorType ] = [];
}

const label = getServiceLabel( getConnectionById( connectionId )?.service_name );

if ( label && ! map[ errorType ].includes( label ) ) {
map[ errorType ].push( label );
}

return map;
},
{}
);
}, [ getConnectionById, getServiceLabel, validationErrors ] );

return (
<Notice type={ 'warning' }>
<p>
{ __( 'The selected media cannot be share to some social media platforms.', 'jetpack' ) }
</p>
<ul>
{ /* Let us be a little more helpful and help them by listing the services that need attention */ }
{ Object.entries( errorTypesToServicesMap ).map( ( [ errorType, services ] ) => {
if ( ! services.length ) {
return null;
}

return (
<li key={ errorType }>
<i>{ getErrorLabel( errorType ) }</i>
{ _x( ':', 'Colon to display before the list of social media platforms', 'jetpack' ) +
' ' }
{
// Since Intl.ListFormat is not allowed in Jetpack yet,
// we join the strings with a comma and space
services.map( ( label, i, { length } ) => (
<Fragment key={ label }>
<b>{ label }</b>
{ i < length - 1 &&
_x( ',', 'Comma to separate list of social media platforms', 'jetpack' ) +
' ' }
</Fragment>
) )
}
</li>
);
} ) }
</ul>
<ExternalLink href={ getRedirectUrl( 'jetpack-social-media-support-information' ) }>
{ __( 'Troubleshooting tips', 'jetpack' ) }
</ExternalLink>
</Notice>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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 { InstagramNoMediaNotice } from './instagram-no-media-notice';
import { MediaRequirementsNotice } from './media-requirements-notice';

export const MediaValidationNotices: React.FC = () => {
const { connections } = useSocialMediaConnections();
const { attachedMedia } = useAttachedMedia();
const featuredImageId = useFeaturedImage();

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

const invalidIds = Object.keys( validationErrors );

if ( ! invalidIds.length ) {
return null;
}

if ( Object.values( validationErrors ).includes( NO_MEDIA_ERROR ) ) {
return <InstagramNoMediaNotice />;
}

if ( ! isConvertible ) {
return <MediaRequirementsNotice validationErrors={ validationErrors } />;
}

return null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import { useMemo } from 'react';
import { usePublicizeConfig } from '../../..';
import useAttachedMedia from '../../hooks/use-attached-media';
import useFeaturedImage from '../../hooks/use-featured-image';
import useImageGeneratorConfig from '../../hooks/use-image-generator-config';
import useMediaDetails from '../../hooks/use-media-details';
import useMediaRestrictions, { NO_MEDIA_ERROR } from '../../hooks/use-media-restrictions';
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';
import { Connection } from '../../social-store/types';

export const useConnectionState = () => {
const { connections, enabledConnections } = useSocialMediaConnections();
const { isPublicizeEnabled, isPublicizeDisabledBySitePlan } = usePublicizeConfig();
const { isEnabled: isSocialImageGeneratorEnabledForPost } = useImageGeneratorConfig();
const { showShareLimits, numberOfSharesRemaining } = useSelect( select => {
return {
showShareLimits: select( socialStore ).showShareLimits(),
Expand All @@ -27,18 +26,9 @@ export const useConnectionState = () => {

const { validationErrors, isConvertible } = useMediaRestrictions(
connections,
useMediaDetails( mediaId )[ 0 ],
{
isSocialImageGeneratorEnabledForPost,
}
useMediaDetails( mediaId )[ 0 ]
);

const isAutoConversionEnabled = useSelect(
select => select( socialStore ).isAutoConversionEnabled(),
[]
);
const shouldAutoConvert = isAutoConversionEnabled && isConvertible;
gmjuhasz marked this conversation as resolved.
Show resolved Hide resolved

const outOfConnections = showShareLimits && numberOfSharesRemaining <= enabledConnections.length;

/**
Expand All @@ -58,15 +48,14 @@ export const useConnectionState = () => {
const isHealthy = false !== is_healthy && status !== 'broken';

// 2. Have no validation errors
const hasValidationErrors =
validationErrors[ currentId ] !== undefined && ! shouldAutoConvert;
const hasValidationErrors = validationErrors[ currentId ] !== undefined && ! isConvertible;

// 3. Not have a NO_MEDIA_ERROR when media is required
const hasNoMediaError = validationErrors[ currentId ] === NO_MEDIA_ERROR;

return isHealthy && ! hasValidationErrors && ! hasNoMediaError;
},
[ shouldAutoConvert, validationErrors ]
[ isConvertible, validationErrors ]
);

/**
Expand Down
Loading
Loading