Skip to content

Commit f59be56

Browse files
manzoorwanijkmatticbot
authored andcommitted
Social | Improve service connect url generation (#42019)
* Move the publicize/services endpoint to publicize package This commit: - Moves the `wpcom/v2/publicize/services` endpoint from Jetpack plugin to publicize package - Adds Services:: wpcom_get_all() method - Updates the schema, using 'id' instead of 'name' because that helps us use the core getEntityRecords selectors - Initializes the endpoint in Publicize_Setup * Update services class to fix caching This commit: - Updates the `Services::get_all()` method to use `self::wpcom_get_all()` - Updates the endpoint to use the updated `publicize/services` endpoint to get the service data - Updates caching to use the same mechanism as used for connections caching to handle concurrent requests - Removes URL from the cached services to avoid caching it. - Updates the transient name with v1 to avoid PHP warnings when using an old transient * Update the service fields as per the schema This commit updates the fields in the front-end components to use the correct schema * Create selectors and actions for services This commit creates the required selectors and actions for the services list using the core APIs to avoid boilerplate for API call handling. * Hydrate the core store with our entity config and initial state data * Refresh the services list for on connect When a user clicks on connect, it looks for the connect URL for the service, if it's not present, it refreshes the services list to get the connect URL. * Disable connect button when loading services When the user clicks on connect, an API call gets fired and till then we need to disable the connect buttons and also show "Connecting..." only for the button that was clicked. * Use the supported services from the store instead of the initial state Committed via a GitHub action: https://github.com/Automattic/jetpack/actions/runs/13521731190 Upstream-Ref: Automattic/jetpack@e31f822
1 parent 2a49501 commit f59be56

20 files changed

+201
-72
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.79.1-alpha] - unreleased
9+
10+
This is an alpha version! The changes listed here are not final.
11+
12+
### Changed
13+
- Social | Improve connect URL generation
14+
815
## [0.79.0] - 2025-02-24
916
### Added
1017
- Add support for Bluesky video selection. [#41669]
@@ -1105,6 +1112,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11051112
### Changed
11061113
- Updated package dependencies. [#24470]
11071114

1115+
[0.79.1-alpha]: https://github.com/Automattic/jetpack-publicize-components/compare/v0.79.0...v0.79.1-alpha
11081116
[0.79.0]: https://github.com/Automattic/jetpack-publicize-components/compare/v0.78.0...v0.79.0
11091117
[0.78.0]: https://github.com/Automattic/jetpack-publicize-components/compare/v0.77.2...v0.78.0
11101118
[0.77.2]: https://github.com/Automattic/jetpack-publicize-components/compare/v0.77.1...v0.77.2

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "@automattic/jetpack-publicize-components",
4-
"version": "0.79.0",
4+
"version": "0.79.1-alpha",
55
"description": "A library of JS components required by the Publicize editor plugin",
66
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/publicize-components/#readme",
77
"bugs": {

src/components/connection-management/reconnect.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ export function Reconnect( { connection, service, variant = 'link' }: ReconnectP
6363

6464
const formData = new FormData();
6565

66-
if ( service.ID === 'mastodon' ) {
66+
if ( service.id === 'mastodon' ) {
6767
formData.set( 'instance', connection.external_handle );
6868
}
6969

70-
if ( service.ID === 'bluesky' ) {
70+
if ( service.id === 'bluesky' ) {
7171
openConnectionsModal();
7272
} else {
7373
requestAccess( formData );
@@ -77,7 +77,7 @@ export function Reconnect( { connection, service, variant = 'link' }: ReconnectP
7777
deleteConnectionById,
7878
openConnectionsModal,
7979
requestAccess,
80-
service.ID,
80+
service.id,
8181
setReconnectingAccount,
8282
] );
8383

src/components/manage-connections-modal/confirmation-form/index.tsx

+10-10
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,16 @@ export function ConfirmationForm( {
7070
const { createErrorNotice } = useGlobalNotices();
7171

7272
const service = supportedServices.find(
73-
supportedService => supportedService.ID === keyringResult.service
73+
supportedService => supportedService.id === keyringResult.service
7474
);
7575
const isAlreadyConnected = useCallback(
7676
( externalID: string ) => {
7777
return existingConnections.some(
7878
connection =>
79-
connection.service_name === service?.ID && connection.external_id === externalID
79+
connection.service_name === service?.id && connection.external_id === externalID
8080
);
8181
},
82-
[ existingConnections, service.ID ]
82+
[ existingConnections, service.id ]
8383
);
8484

8585
const accounts = useMemo( () => {
@@ -94,7 +94,7 @@ export function ConfirmationForm( {
9494
const options: Array< AccountOption > = [];
9595

9696
// If user account is supported, add it to the list
97-
if ( ! service.external_users_only ) {
97+
if ( ! service.supports.additional_users_only ) {
9898
options.push( {
9999
label: keyringResult.external_display || keyringResult.external_name,
100100
value: keyringResult.external_ID,
@@ -103,7 +103,7 @@ export function ConfirmationForm( {
103103
}
104104

105105
if (
106-
service.multiple_external_user_ID_support &&
106+
service.supports.additional_users_only &&
107107
keyringResult.additional_external_users?.length
108108
) {
109109
for ( const user of keyringResult.additional_external_users ) {
@@ -148,7 +148,7 @@ export function ConfirmationForm( {
148148
}
149149

150150
const data = {
151-
external_user_ID: service.multiple_external_user_ID_support ? external_user_ID : undefined,
151+
external_user_ID: service.supports.additional_users ? external_user_ID : undefined,
152152
keyring_connection_ID: keyringResult.ID,
153153
shared: formData.get( 'shared' ) === '1' ? true : undefined,
154154
};
@@ -165,7 +165,7 @@ export function ConfirmationForm( {
165165
createConnection( data, {
166166
display_name: accountInfo?.label,
167167
profile_picture: accountInfo?.profile_picture,
168-
service_name: service.ID,
168+
service_name: service.id,
169169
external_id: external_user_ID.toString(),
170170
} );
171171

@@ -178,8 +178,8 @@ export function ConfirmationForm( {
178178
createErrorNotice,
179179
keyringResult.ID,
180180
onComplete,
181-
service.multiple_external_user_ID_support,
182-
service.ID,
181+
service.supports,
182+
service.id,
183183
accounts.not_connected,
184184
]
185185
);
@@ -240,7 +240,7 @@ export function ConfirmationForm( {
240240
// If we are reconnecting an account, preselect it,
241241
// otherwise, preselect the first account
242242
const defaultChecked = reconnectingAccount
243-
? reconnectingAccount.service_name === service?.ID &&
243+
? reconnectingAccount.service_name === service?.id &&
244244
reconnectingAccount.external_id === option.value
245245
: index === 0;
246246

src/components/services/connect-form.tsx

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Button } from '@automattic/jetpack-components';
22
import { useDispatch, useSelect } from '@wordpress/data';
3-
import { useCallback } from '@wordpress/element';
3+
import { useCallback, useState } from '@wordpress/element';
44
import { __, _x } from '@wordpress/i18n';
55
import clsx from 'clsx';
66
import { store } from '../../social-store';
@@ -38,6 +38,13 @@ export function ConnectForm( {
3838

3939
const { isConnectionsModalOpen } = useSelect( select => select( store ), [] );
4040

41+
const [ isConnecting, setIsConnecting ] = useState( false );
42+
43+
const isFetchingServicesList = useSelect(
44+
select => select( store ).isFetchingServicesList(),
45+
[]
46+
);
47+
4148
const onConfirm = useCallback(
4249
( result: KeyringResult ) => {
4350
// Set the keyring result only if the modal is open
@@ -54,7 +61,7 @@ export function ConnectForm( {
5461
} );
5562

5663
const onSubmitForm = useCallback(
57-
( event: React.FormEvent ) => {
64+
async ( event: React.FormEvent ) => {
5865
event.preventDefault();
5966
// Prevent Jetpack settings from being submitted
6067
event.stopPropagation();
@@ -63,9 +70,11 @@ export function ConnectForm( {
6370
return onSubmit();
6471
}
6572

73+
setIsConnecting( true );
74+
6675
const formData = new FormData( event.target as HTMLFormElement );
6776

68-
requestAccess( formData );
77+
await requestAccess( formData );
6978
},
7079
[ onSubmit, requestAccess ]
7180
);
@@ -87,12 +96,17 @@ export function ConnectForm( {
8796
variant={ hasConnections ? 'secondary' : 'primary' }
8897
type="submit"
8998
className={ styles[ 'connect-button' ] }
99+
disabled={ isFetchingServicesList }
90100
>
91101
{ ( label => {
92102
if ( label ) {
93103
return label;
94104
}
95105

106+
if ( isFetchingServicesList && isConnecting ) {
107+
return __( 'Connecting…', 'jetpack-publicize-components' );
108+
}
109+
96110
return hasConnections
97111
? _x( 'Connect more', '', 'jetpack-publicize-components' )
98112
: __( 'Connect', 'jetpack-publicize-components' );

src/components/services/custom-inputs.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function CustomInputs( { service }: CustomInputsProps ) {
5252
[ validateBskyHandle ]
5353
);
5454

55-
if ( 'mastodon' === service.ID ) {
55+
if ( 'mastodon' === service.id ) {
5656
return (
5757
<div className={ styles[ 'fields-item' ] }>
5858
<label htmlFor={ `${ id }-handle` }>
@@ -85,7 +85,7 @@ export function CustomInputs( { service }: CustomInputsProps ) {
8585
);
8686
}
8787

88-
if ( 'bluesky' === service.ID ) {
88+
if ( 'bluesky' === service.id ) {
8989
return (
9090
<>
9191
<div className={ styles[ 'fields-item' ] }>

src/components/services/service-item-details.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export function ServiceItemDetails( { service, serviceConnections }: ServicesIte
6666
} ) }
6767
>
6868
{ service.examples.map( ( Example, idx ) => (
69-
<div key={ service.ID + idx } className={ styles.example }>
69+
<div key={ service.id + idx } className={ styles.example }>
7070
<Example />
7171
</div>
7272
) ) }

src/components/services/services-list.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ export function ServicesList() {
3232
return (
3333
<ul className={ styles.services }>
3434
{ supportedServices.map( service => (
35-
<li key={ service.ID } className={ styles[ 'service-list-item' ] }>
35+
<li key={ service.id } className={ styles[ 'service-list-item' ] }>
3636
<ServiceItem
3737
service={ service }
38-
serviceConnections={ connections[ service.ID ] || [] }
39-
isPanelDefaultOpen={ reconnectingAccount?.service_name === service.ID }
38+
serviceConnections={ connections[ service.id ] || [] }
39+
isPanelDefaultOpen={ reconnectingAccount?.service_name === service.id }
4040
/>
4141
</li>
4242
) ) }

src/components/services/use-request-access.ts

+24-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useGlobalNotices } from '@automattic/jetpack-components';
2-
import { useSelect } from '@wordpress/data';
2+
import { useDispatch, useSelect } from '@wordpress/data';
33
import { useCallback } from '@wordpress/element';
44
import { __ } from '@wordpress/i18n';
55
import { store } from '../../social-store';
@@ -40,7 +40,7 @@ export type RequestAccessOptions = {
4040
* Hook to request access to a service.
4141
*
4242
* @param {RequestAccessOptions} options - Options
43-
* @return {(formData: FormData) => void} - Function to request access
43+
* @return - Function to request access
4444
*/
4545
export function useRequestAccess( { service, onConfirm }: RequestAccessOptions ) {
4646
const { createErrorNotice } = useGlobalNotices();
@@ -55,11 +55,27 @@ export function useRequestAccess( { service, onConfirm }: RequestAccessOptions )
5555
[]
5656
);
5757

58+
const { refreshServicesList } = useDispatch( store );
59+
60+
const { getService } = useSelect( select => select( store ), [] );
61+
5862
return useCallback(
59-
( formData: FormData ) => {
60-
const url = new URL( service.connect_URL );
63+
async ( formData: FormData ) => {
64+
let connectUrl = service.url;
65+
66+
if ( ! connectUrl ) {
67+
await refreshServicesList();
68+
// Wait until the services list is refreshed
69+
do {
70+
await new Promise( resolve => setTimeout( resolve, 100 ) );
71+
72+
connectUrl = getService( service.id )?.url;
73+
} while ( ! connectUrl );
74+
}
75+
76+
const url = new URL( connectUrl );
6177

62-
switch ( service.ID ) {
78+
switch ( service.id ) {
6379
case 'mastodon': {
6480
const instance = formData.get( 'instance' ).toString().trim();
6581

@@ -115,11 +131,12 @@ export function useRequestAccess( { service, onConfirm }: RequestAccessOptions )
115131
},
116132
[
117133
createErrorNotice,
134+
getService,
118135
isBlueskyAccountAlreadyConnected,
119136
isMastodonAlreadyConnected,
120137
onConfirm,
121-
service.ID,
122-
service.connect_URL,
138+
refreshServicesList,
139+
service,
123140
]
124141
);
125142
}

src/components/services/use-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function useService() {
2020

2121
const servicesMap = useMemo( () => {
2222
return supportedServices.reduce< SupportedServicesMap >( ( acc, service ) => {
23-
acc[ service.ID ] = service;
23+
acc[ service.id ] = service;
2424
return acc;
2525
}, {} );
2626
}, [ supportedServices ] );

src/components/services/use-supported-services.tsx

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SocialServiceIcon } from '@automattic/jetpack-components';
22
import { ExternalLink } from '@wordpress/components';
3+
import { useSelect } from '@wordpress/data';
34
import { createInterpolateElement, useMemo } from '@wordpress/element';
45
import { __ } from '@wordpress/i18n';
56
import connectionsFacebook from '../../assets/connections-facebook.png';
@@ -8,8 +9,8 @@ import connectionsLinkedin from '../../assets/connections-linkedin.png';
89
import connectionsNextdoor from '../../assets/connections-nextdoor.png';
910
import connectionsThreads from '../../assets/connections-threads.png';
1011
import connectionsTumblr from '../../assets/connections-tumblr.png';
12+
import { store as socialStore } from '../../social-store';
1113
import { ConnectionService } from '../../types';
12-
import { getSocialScriptData } from '../../utils/script-data';
1314

1415
export type Badge = {
1516
text: string;
@@ -29,17 +30,17 @@ export interface SupportedService extends ConnectionService {
2930
* @return {Array< SupportedService >} The list of supported services
3031
*/
3132
export function useSupportedServices(): Array< SupportedService > {
32-
const availableServices = useMemo( () => {
33-
const { supported_services } = getSocialScriptData();
33+
const supported_services = useSelect( select => select( socialStore ).getServicesList(), [] );
3434

35+
const availableServices = useMemo( () => {
3536
return supported_services.reduce< Record< string, ConnectionService > >(
3637
( serviceData, service ) => ( {
3738
...serviceData,
38-
[ service.ID ]: service,
39+
[ service.id ]: service,
3940
} ),
4041
{}
4142
);
42-
}, [] );
43+
}, [ supported_services ] );
4344

4445
const badgeNew: Badge = {
4546
text: __( 'New', 'jetpack-publicize-components' ),
@@ -261,6 +262,6 @@ export function useSupportedServices(): Array< SupportedService > {
261262
];
262263
return supportedServices.filter(
263264
// Return only the ones that are present in the available services.
264-
service => Boolean( service.ID )
265+
service => Boolean( service.id )
265266
);
266267
}

src/social-store/actions/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as connectionData from './connection-data';
22
import * as pricingPageSettings from './pricing-page';
3+
import * as servicesActions from './services';
34
import * as shareStatus from './share-status';
45
import * as sigActions from './social-image-generator';
56
import * as socialModuleSettings from './social-module-settings';
@@ -14,6 +15,7 @@ const actions = {
1415
...socialNoteSettings,
1516
...pricingPageSettings,
1617
...socialModuleSettings,
18+
...servicesActions,
1719
};
1820

1921
export default actions;

src/social-store/actions/services.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { store as coreStore } from '@wordpress/core-data';
2+
3+
/**
4+
* Refreshes the list of supported services.
5+
*
6+
* @return A thunk to refresh the list of supported services.
7+
*/
8+
export function refreshServicesList() {
9+
return async function ( { registry } ) {
10+
registry
11+
.dispatch( coreStore )
12+
.invalidateResolution( 'getEntityRecords', [ 'wpcom/v2', 'publicize/services' ] );
13+
};
14+
}

0 commit comments

Comments
 (0)