diff --git a/projects/js-packages/publicize-components/changelog/update-extract-connection-state-logic b/projects/js-packages/publicize-components/changelog/update-extract-connection-state-logic
new file mode 100644
index 0000000000000..e4e00762c2c4f
--- /dev/null
+++ b/projects/js-packages/publicize-components/changelog/update-extract-connection-state-logic
@@ -0,0 +1,4 @@
+Significance: patch
+Type: changed
+
+Extracted the connection toggle state logic
diff --git a/projects/js-packages/publicize-components/src/components/form/connections-list.tsx b/projects/js-packages/publicize-components/src/components/form/connections-list.tsx
index f04f42e84d86b..404f3d72c1e2f 100644
--- a/projects/js-packages/publicize-components/src/components/form/connections-list.tsx
+++ b/projects/js-packages/publicize-components/src/components/form/connections-list.tsx
@@ -1,83 +1,24 @@
-import { useSelect } from '@wordpress/data';
-import { useCallback } from '@wordpress/element';
-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 useSocialMediaConnections from '../../hooks/use-social-media-connections';
-import { store as socialStore } from '../../social-store';
import PublicizeConnection from '../connection';
import PublicizeSettingsButton from '../settings-button';
import styles from './styles.module.scss';
+import { useConnectionState } from './use-connection-state';
export const ConnectionsList: React.FC = () => {
- const { connections, toggleById, enabledConnections } = useSocialMediaConnections();
- const { isPublicizeEnabled, isPublicizeDisabledBySitePlan } = usePublicizeConfig();
- const { isEnabled: isSocialImageGeneratorEnabledForPost } = useImageGeneratorConfig();
- const { showShareLimits, numberOfSharesRemaining } = useSelect( select => {
- return {
- showShareLimits: select( socialStore ).showShareLimits(),
- numberOfSharesRemaining: select( socialStore ).numberOfSharesRemaining(),
- };
- }, [] );
+ const { connections, toggleById } = useSocialMediaConnections();
- const outOfConnections = showShareLimits && numberOfSharesRemaining <= enabledConnections.length;
-
- const isAutoConversionEnabled = useSelect(
- select => select( socialStore ).isAutoConversionEnabled(),
- []
- );
-
- const { attachedMedia, shouldUploadAttachedMedia } = useAttachedMedia();
- const featuredImageId = useFeaturedImage();
- const mediaId = attachedMedia[ 0 ]?.id || featuredImageId;
-
- const { validationErrors, isConvertible } = useMediaRestrictions(
- connections,
- useMediaDetails( mediaId )[ 0 ],
- {
- isSocialImageGeneratorEnabledForPost,
- shouldUploadAttachedMedia,
- }
- );
- const shouldAutoConvert = isAutoConversionEnabled && isConvertible;
-
- const isConnectionEnabled = useCallback(
- ( { enabled, is_healthy = true, connection_id } ) =>
- enabled &&
- ! isPublicizeDisabledBySitePlan &&
- false !== is_healthy &&
- ( ! validationErrors[ connection_id ] || shouldAutoConvert ) &&
- validationErrors[ connection_id ] !== NO_MEDIA_ERROR,
- [ isPublicizeDisabledBySitePlan, validationErrors, shouldAutoConvert ]
- );
+ const { canBeTurnedOn, shouldBeDisabled } = useConnectionState();
return (
{ connections.map( conn => {
- const {
- display_name,
- enabled,
- id,
- service_name,
- toggleable,
- profile_picture,
- is_healthy,
- connection_id,
- } = conn;
+ const { display_name, id, service_name, profile_picture, connection_id } = conn;
const currentId = connection_id ? connection_id : id;
+
return (
canBeTurnedOn( connection ) && ! shouldBeDisabled( connection )
+ );
+
+ return validConnections.length && isPublicizeEnabled ? (
{ sprintf(
@@ -21,10 +27,10 @@ export function EnabledConnectionsNotice() {
_n(
'This post will be shared to %d connection.',
'This post will be shared to %d connections.',
- enabledConnections.length,
+ validConnections.length,
'jetpack'
),
- enabledConnections.length
+ validConnections.length
) }
diff --git a/projects/js-packages/publicize-components/src/components/form/index.js b/projects/js-packages/publicize-components/src/components/form/index.tsx
similarity index 100%
rename from projects/js-packages/publicize-components/src/components/form/index.js
rename to projects/js-packages/publicize-components/src/components/form/index.tsx
diff --git a/projects/js-packages/publicize-components/src/components/form/use-connection-state.ts b/projects/js-packages/publicize-components/src/components/form/use-connection-state.ts
new file mode 100644
index 0000000000000..612397ff48f1c
--- /dev/null
+++ b/projects/js-packages/publicize-components/src/components/form/use-connection-state.ts
@@ -0,0 +1,128 @@
+import { useSelect } from '@wordpress/data';
+import { useCallback } from '@wordpress/element';
+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 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(),
+ numberOfSharesRemaining: select( socialStore ).numberOfSharesRemaining(),
+ };
+ }, [] );
+ const { attachedMedia, shouldUploadAttachedMedia } = useAttachedMedia();
+ const featuredImageId = useFeaturedImage();
+ const mediaId = attachedMedia[ 0 ]?.id || featuredImageId;
+
+ const { validationErrors, isConvertible } = useMediaRestrictions(
+ connections,
+ useMediaDetails( mediaId )[ 0 ],
+ {
+ isSocialImageGeneratorEnabledForPost,
+ shouldUploadAttachedMedia,
+ }
+ );
+
+ const isAutoConversionEnabled = useSelect(
+ select => select( socialStore ).isAutoConversionEnabled(),
+ []
+ );
+ const shouldAutoConvert = isAutoConversionEnabled && isConvertible;
+
+ const outOfConnections = showShareLimits && numberOfSharesRemaining <= enabledConnections.length;
+
+ /**
+ * Returns whether a connection is in good shape.
+ *
+ * A connection is in good shape if:
+ * - It is healthy
+ * - It has no validation errors
+ * - It does not have a NO_MEDIA_ERROR when media is required
+ */
+ const isInGoodShape = useCallback(
+ ( connection: Connection ) => {
+ const { id, is_healthy, connection_id } = connection;
+ const currentId = connection_id ? connection_id : id;
+
+ // 1. Be healthy
+ const isHealthy = false !== is_healthy;
+
+ // 2. Have no validation errors
+ const hasValidationErrors =
+ validationErrors[ currentId ] !== undefined && ! shouldAutoConvert;
+
+ // 3. Not have a NO_MEDIA_ERROR when media is required
+ const hasNoMediaError = validationErrors[ currentId ] === NO_MEDIA_ERROR;
+
+ return isHealthy && ! hasValidationErrors && ! hasNoMediaError;
+ },
+ [ shouldAutoConvert, validationErrors ]
+ );
+
+ /**
+ * Returns whether a connection should be disabled.
+ * Disabled here means the disabled prop of the ToggleControl
+ *
+ * A connection can be disabled if:
+ * - Publicize is disabled
+ * - There are no more connections available
+ * - The connection is not in good shape
+ */
+ const shouldBeDisabled = useCallback(
+ ( connection: Connection ) => {
+ const { enabled, toggleable } = connection;
+
+ const isOutOfConnections = ! enabled && toggleable && outOfConnections;
+ // A connection toggle should be disabled if
+ return (
+ // Publicize is disabled
+ ! isPublicizeEnabled ||
+ // or if there are no more connections available
+ isOutOfConnections ||
+ // or the connection is not in good shape
+ ! isInGoodShape( connection )
+ );
+ },
+ [ isInGoodShape, isPublicizeEnabled, outOfConnections ]
+ );
+
+ /**
+ * Returns whether a connection can be enabled.
+ * Enabled here means the checked state of the ToggleControl
+ *
+ * A connection can be enabled if:
+ * - Publicize is not disabled due to the current site plan
+ * - The connection is in good shape
+ */
+ const canBeTurnedOn = useCallback(
+ ( connection: Connection ) => {
+ // A connection toggle can be turned ON if
+ return (
+ // Publicize is not disabled due to the current site plan
+ ! isPublicizeDisabledBySitePlan &&
+ // and the connection is in good shape
+ isInGoodShape( connection )
+ );
+ },
+ [ isInGoodShape, isPublicizeDisabledBySitePlan ]
+ );
+
+ return useMemo(
+ () => ( {
+ shouldBeDisabled,
+ canBeTurnedOn,
+ } ),
+ [ shouldBeDisabled, canBeTurnedOn ]
+ );
+};
diff --git a/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js b/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js
index af7561d9f0192..026433b35f531 100644
--- a/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js
+++ b/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js
@@ -58,7 +58,7 @@ export function getMustReauthConnections( state ) {
*
* @param {import("../types").SocialStoreState} state - State object.
*
- * @returns {Array} List of enabled connections.
+ * @returns {Array} List of enabled connections.
*/
export function getEnabledConnections( state ) {
return getConnections( state ).filter( connection => connection.enabled );