From f401cfb5cb612e8397c99aba03a88e7ed5a4e9b9 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Tue, 22 Oct 2024 04:35:25 -0700 Subject: [PATCH] Social | Fix reconnection for broken Bluesky connections (#39844) --- ...luesky-reconnection-for-broken-connections | 4 ++++ .../connection-management/reconnect.tsx | 21 ++++++++++------ .../confirmation-form/index.tsx | 7 +++--- .../manage-connections-modal/index.tsx | 5 ++-- .../src/components/services/custom-inputs.tsx | 15 ++++++++++++ .../src/components/services/service-item.tsx | 24 +++++++++++++++---- .../src/components/services/services-list.tsx | 8 ++++++- .../components/services/use-request-access.ts | 5 +++- .../social-store/actions/connection-data.js | 2 +- .../social-store/selectors/connection-data.js | 2 +- .../src/social-store/types.ts | 2 +- 11 files changed, 73 insertions(+), 22 deletions(-) create mode 100644 projects/js-packages/publicize-components/changelog/fix-social-bluesky-reconnection-for-broken-connections diff --git a/projects/js-packages/publicize-components/changelog/fix-social-bluesky-reconnection-for-broken-connections b/projects/js-packages/publicize-components/changelog/fix-social-bluesky-reconnection-for-broken-connections new file mode 100644 index 0000000000000..eb4d17f671962 --- /dev/null +++ b/projects/js-packages/publicize-components/changelog/fix-social-bluesky-reconnection-for-broken-connections @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed reconnection for broken Bluesky connections diff --git a/projects/js-packages/publicize-components/src/components/connection-management/reconnect.tsx b/projects/js-packages/publicize-components/src/components/connection-management/reconnect.tsx index 107fdc5240c10..d6e11e80cf36e 100644 --- a/projects/js-packages/publicize-components/src/components/connection-management/reconnect.tsx +++ b/projects/js-packages/publicize-components/src/components/connection-management/reconnect.tsx @@ -58,11 +58,7 @@ export function Reconnect( { connection, service, variant = 'link' }: ReconnectP return; } - await setReconnectingAccount( - // Join service name and external ID - // just in case the external ID alone is not unique. - `${ connection.service_name }:${ connection.external_id }` - ); + await setReconnectingAccount( connection ); const formData = new FormData(); @@ -70,8 +66,19 @@ export function Reconnect( { connection, service, variant = 'link' }: ReconnectP formData.set( 'instance', connection.external_display ); } - requestAccess( formData ); - }, [ connection, deleteConnectionById, requestAccess, service.ID, setReconnectingAccount ] ); + if ( service.ID === 'bluesky' ) { + openConnectionsModal(); + } else { + requestAccess( formData ); + } + }, [ + connection, + deleteConnectionById, + openConnectionsModal, + requestAccess, + service.ID, + setReconnectingAccount, + ] ); if ( ! connection.can_disconnect ) { return null; diff --git a/projects/js-packages/publicize-components/src/components/manage-connections-modal/confirmation-form/index.tsx b/projects/js-packages/publicize-components/src/components/manage-connections-modal/confirmation-form/index.tsx index 4eecc6d050a37..6ffc93c8ca59b 100644 --- a/projects/js-packages/publicize-components/src/components/manage-connections-modal/confirmation-form/index.tsx +++ b/projects/js-packages/publicize-components/src/components/manage-connections-modal/confirmation-form/index.tsx @@ -90,7 +90,7 @@ export function ConfirmationForm( { keyringResult, onComplete, isAdmin }: Confir // If user account is supported, add it to the list if ( ! service.external_users_only ) { options.push( { - label: keyringResult.external_display, + label: keyringResult.external_display || keyringResult.external_name, value: keyringResult.external_ID, profile_picture: keyringResult.external_profile_picture, } ); @@ -150,7 +150,7 @@ export function ConfirmationForm( { keyringResult, onComplete, isAdmin }: Confir ); if ( reconnectingAccount ) { - setReconnectingAccount( '' ); + setReconnectingAccount( undefined ); } // Do not await the connection creation to unblock the UI @@ -215,7 +215,8 @@ export function ConfirmationForm( { keyringResult, onComplete, isAdmin }: Confir // If we are reconnecting an account, preselect it, // otherwise, preselect the first account const defaultChecked = reconnectingAccount - ? reconnectingAccount === `${ service?.ID }:${ option.value }` + ? reconnectingAccount.service_name === service?.ID && + reconnectingAccount.external_id === option.value : index === 0; return ( diff --git a/projects/js-packages/publicize-components/src/components/manage-connections-modal/index.tsx b/projects/js-packages/publicize-components/src/components/manage-connections-modal/index.tsx index 129dcbc54d9ba..336597e2dcaf2 100644 --- a/projects/js-packages/publicize-components/src/components/manage-connections-modal/index.tsx +++ b/projects/js-packages/publicize-components/src/components/manage-connections-modal/index.tsx @@ -24,14 +24,15 @@ export const ManageConnectionsModal = () => { }; }, [] ); - const { setKeyringResult, closeConnectionsModal } = useDispatch( store ); + const { setKeyringResult, closeConnectionsModal, setReconnectingAccount } = useDispatch( store ); const [ isSmall ] = useBreakpointMatch( 'sm' ); const closeModal = useCallback( () => { setKeyringResult( null ); + setReconnectingAccount( undefined ); closeConnectionsModal(); - }, [ closeConnectionsModal, setKeyringResult ] ); + }, [ closeConnectionsModal, setKeyringResult, setReconnectingAccount ] ); const hasKeyringResult = Boolean( keyringResult?.ID ); diff --git a/projects/js-packages/publicize-components/src/components/services/custom-inputs.tsx b/projects/js-packages/publicize-components/src/components/services/custom-inputs.tsx index 4064facfbf9ed..95e02ed8c8729 100644 --- a/projects/js-packages/publicize-components/src/components/services/custom-inputs.tsx +++ b/projects/js-packages/publicize-components/src/components/services/custom-inputs.tsx @@ -1,6 +1,9 @@ +import { Alert } from '@automattic/jetpack-components'; import { ExternalLink } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; import { createInterpolateElement, useId } from '@wordpress/element'; import { __, _x } from '@wordpress/i18n'; +import { store } from '../../social-store'; import { SupportedService } from '../services/use-supported-services'; import styles from './style.module.scss'; @@ -17,6 +20,8 @@ type CustomInputsProps = { export function CustomInputs( { service }: CustomInputsProps ) { const id = useId(); + const reconnectingAccount = useSelect( select => select( store ).getReconnectingAccount(), [] ); + if ( 'mastodon' === service.ID ) { return (
@@ -58,6 +63,11 @@ export function CustomInputs( { service }: CustomInputsProps ) { required type="text" name="handle" + defaultValue={ + reconnectingAccount?.service_name === 'bluesky' + ? reconnectingAccount?.external_name + : undefined + } autoComplete="off" autoCapitalize="off" autoCorrect="off" @@ -93,6 +103,11 @@ export function CustomInputs( { service }: CustomInputsProps ) { aria-describedby={ `${ id }-password-description` } placeholder={ 'xxxx-xxxx-xxxx-xxxx' } /> + { reconnectingAccount?.service_name === 'bluesky' && ( + + { __( 'Please provide an app password to fix the connection.', 'jetpack' ) } + + ) }
); diff --git a/projects/js-packages/publicize-components/src/components/services/service-item.tsx b/projects/js-packages/publicize-components/src/components/services/service-item.tsx index 2020e883690a1..00754aa1a1e38 100644 --- a/projects/js-packages/publicize-components/src/components/services/service-item.tsx +++ b/projects/js-packages/publicize-components/src/components/services/service-item.tsx @@ -1,6 +1,6 @@ import { Button, useBreakpointMatch } from '@automattic/jetpack-components'; import { Panel, PanelBody } from '@wordpress/components'; -import { useReducer } from '@wordpress/element'; +import { useEffect, useReducer, useRef } from '@wordpress/element'; import { __, _x } from '@wordpress/i18n'; import { Icon, chevronDown, chevronUp } from '@wordpress/icons'; import { ConnectForm } from './connect-form'; @@ -8,7 +8,9 @@ import { ServiceItemDetails, ServicesItemDetailsProps } from './service-item-det import { ServiceStatus } from './service-status'; import styles from './style.module.scss'; -export type ServicesItemProps = ServicesItemDetailsProps; +export type ServicesItemProps = ServicesItemDetailsProps & { + isPanelDefaultOpen?: boolean; +}; /** * Service item component @@ -17,10 +19,22 @@ export type ServicesItemProps = ServicesItemDetailsProps; * * @return {import('react').ReactNode} Service item component */ -export function ServiceItem( { service, serviceConnections }: ServicesItemProps ) { +export function ServiceItem( { + service, + serviceConnections, + isPanelDefaultOpen, +}: ServicesItemProps ) { const [ isSmall ] = useBreakpointMatch( 'sm' ); - const [ isPanelOpen, togglePanel ] = useReducer( state => ! state, false ); + const [ isPanelOpen, togglePanel ] = useReducer( state => ! state, isPanelDefaultOpen ); + const panelRef = useRef< HTMLDivElement >( null ); + + useEffect( () => { + if ( isPanelDefaultOpen ) { + panelRef.current?.scrollIntoView( { block: 'center', behavior: 'smooth' } ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [] ); const areCustomInputsVisible = isPanelOpen && service.needsCustomInputs; @@ -94,7 +108,7 @@ export function ServiceItem( { service, serviceConnections }: ServicesItemProps - + { diff --git a/projects/js-packages/publicize-components/src/components/services/services-list.tsx b/projects/js-packages/publicize-components/src/components/services/services-list.tsx index c2cdf4c4ac8b9..1a5c030c07820 100644 --- a/projects/js-packages/publicize-components/src/components/services/services-list.tsx +++ b/projects/js-packages/publicize-components/src/components/services/services-list.tsx @@ -27,11 +27,17 @@ export function ServicesList() { }, {} ); }, [] ); + const reconnectingAccount = useSelect( select => select( store ).getReconnectingAccount(), [] ); + return (
    { supportedServices.map( service => (
  • - +
  • ) ) }
diff --git a/projects/js-packages/publicize-components/src/components/services/use-request-access.ts b/projects/js-packages/publicize-components/src/components/services/use-request-access.ts index 18afab50c92d2..e12af39dfefca 100644 --- a/projects/js-packages/publicize-components/src/components/services/use-request-access.ts +++ b/projects/js-packages/publicize-components/src/components/services/use-request-access.ts @@ -96,7 +96,10 @@ export function useRequestAccess( { service, onConfirm }: RequestAccessOptions ) } url.searchParams.set( 'handle', handle ); - url.searchParams.set( 'app_password', formData.get( 'app_password' ).toString().trim() ); + url.searchParams.set( + 'app_password', + ( formData.get( 'app_password' )?.toString() || '' ).trim() + ); break; } diff --git a/projects/js-packages/publicize-components/src/social-store/actions/connection-data.js b/projects/js-packages/publicize-components/src/social-store/actions/connection-data.js index 8c23c207ad21d..78012e39b3db5 100644 --- a/projects/js-packages/publicize-components/src/social-store/actions/connection-data.js +++ b/projects/js-packages/publicize-components/src/social-store/actions/connection-data.js @@ -441,7 +441,7 @@ export function updatingConnection( connectionId, updating = true ) { /** * Sets the reconnecting account. * - * @param {string} reconnectingAccount - Account being reconnected. + * @param {import('../types').Connection} reconnectingAccount - Account being reconnected. * * @return {object} Reconnecting account action. */ 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 1a324a65d3cd7..25b675d301c4d 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 @@ -181,7 +181,7 @@ export function getUpdatingConnections( state ) { * @return {import("../types").ConnectionData['reconnectingAccount']} The account being reconnected. */ export function getReconnectingAccount( state ) { - return state.connectionData?.reconnectingAccount ?? ''; + return state.connectionData?.reconnectingAccount; } /** diff --git a/projects/js-packages/publicize-components/src/social-store/types.ts b/projects/js-packages/publicize-components/src/social-store/types.ts index a391f53024082..afe11754eb291 100644 --- a/projects/js-packages/publicize-components/src/social-store/types.ts +++ b/projects/js-packages/publicize-components/src/social-store/types.ts @@ -25,7 +25,7 @@ export type ConnectionData = { connections: Connection[]; deletingConnections?: Array< number | string >; updatingConnections?: Array< number | string >; - reconnectingAccount?: string; + reconnectingAccount?: Connection; keyringResult?: KeyringResult; };