Skip to content

Commit

Permalink
Social: Add Bluesky connection UI (#39561)
Browse files Browse the repository at this point in the history
* Add Bluesky to the supported services

* Update connect URL logic for Bluesky

* Create a dedicated component for custom inputs

* Update ConnectForm to use CustomInputs

* Update ServiceItem component to handle Bluesky custom inputs

* Do the make up

* Add changelog

* Fix messed up translations

* Clean up
  • Loading branch information
manzoorwanijk authored Oct 1, 2024
1 parent c7e170e commit b337121
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Social: Added support for Bluesky
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Button } from '@automattic/jetpack-components';
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
import { __, _x } from '@wordpress/i18n';
import clsx from 'clsx';
import { useCallback } from 'react';
import { store } from '../../social-store';
import { KeyringResult } from '../../social-store/types';
import { SupportedService } from '../services/use-supported-services';
import { CustomInputs } from './custom-inputs';
import styles from './style.module.scss';
import { useRequestAccess } from './use-request-access';

Expand Down Expand Up @@ -63,6 +64,7 @@ export function ConnectForm( {
}

const formData = new FormData( event.target as HTMLFormElement );

requestAccess( formData );
},
[ onSubmit, requestAccess ]
Expand All @@ -74,31 +76,30 @@ export function ConnectForm( {
onSubmit={ onSubmitForm }
>
{ displayInputs ? (
<>
{ 'mastodon' === service.ID ? (
<input
required
type="text"
name="instance"
aria-label={ __( 'Mastodon username', 'jetpack' ) }
placeholder={ '@[email protected]' }
/>
) : null }
</>
<div className={ styles[ 'fields-wrapper' ] }>
<CustomInputs service={ service } />
</div>
) : null }
<Button
variant={ hasConnections ? 'secondary' : 'primary' }
type="submit"
className={ styles[ 'connect-button' ] }
>
{ ( label => {
if ( label ) {
return label;
}

return hasConnections ? _x( 'Connect more', '', 'jetpack' ) : __( 'Connect', 'jetpack' );
} )( buttonLabel ) }
</Button>
<div className={ styles[ 'fields-wrapper' ] }>
<div className={ styles[ 'fields-item' ] }>
<Button
variant={ hasConnections ? 'secondary' : 'primary' }
type="submit"
className={ styles[ 'connect-button' ] }
>
{ ( label => {
if ( label ) {
return label;
}

return hasConnections
? _x( 'Connect more', '', 'jetpack' )
: __( 'Connect', 'jetpack' );
} )( buttonLabel ) }
</Button>
</div>
</div>
</form>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { ExternalLink } from '@wordpress/components';
import { createInterpolateElement, useId } from '@wordpress/element';
import { __, _x } from '@wordpress/i18n';
import { SupportedService } from '../services/use-supported-services';
import styles from './style.module.scss';

type CustomInputsProps = {
service: SupportedService;
};

/**
* Custom inputs component
* @param {CustomInputsProps} props - Component props
*
* @return {import('react').ReactNode} Custom inputs component
*/
export function CustomInputs( { service }: CustomInputsProps ) {
const id = useId();

if ( 'mastodon' === service.ID ) {
return (
<div className={ styles[ 'fields-item' ] }>
<label htmlFor={ `${ id }-handle` }>
{ _x( 'Handle', 'The handle of a social media account.', 'jetpack' ) }
</label>
<p className="description" id={ `${ id }-handle-description` }>
{ __( 'You can find the handle in your Mastodon profile.', 'jetpack' ) }
</p>
<input
id={ `${ id }-handle` }
required
type="text"
name="instance"
autoComplete="off"
autoCapitalize="off"
autoCorrect="off"
spellCheck="false"
aria-label={ __( 'Mastodon handle', 'jetpack' ) }
aria-describedby={ `${ id }-handle-description` }
placeholder={ '@[email protected]' }
/>
</div>
);
}

if ( 'bluesky' === service.ID ) {
return (
<>
<div className={ styles[ 'fields-item' ] }>
<label htmlFor={ `${ id }-handle` }>
{ _x( 'Handle', 'The handle of a social media account.', 'jetpack' ) }
</label>
<p className="description" id={ `${ id }-handle-description` }>
{ __( 'You can find the handle in your Bluesky profile.', 'jetpack' ) }
</p>
<input
id={ `${ id }-handle` }
required
type="text"
name="handle"
autoComplete="off"
autoCapitalize="off"
autoCorrect="off"
spellCheck="false"
aria-label={ __( 'Bluesky handle', 'jetpack' ) }
aria-describedby={ `${ id }-handle-description` }
placeholder={ 'username.bsky.social' }
/>
</div>
<div className={ styles[ 'fields-item' ] }>
<label htmlFor={ `${ id }-password` }>{ __( 'App password', 'jetpack' ) }</label>
<p className="description" id={ `${ id }-password-description` }>
{ createInterpolateElement(
__(
'App password is needed to safely connect your account. App password is different from your account password. You can <link>generate it in Bluesky</link>.',
'jetpack'
),
{
link: <ExternalLink href="https://bsky.app/settings/app-passwords" />,
}
) }
</p>
<input
id={ `${ id }-password` }
required
type="password"
name="app_password"
autoComplete="off"
autoCapitalize="off"
autoCorrect="off"
spellCheck="false"
aria-label={ __( 'App password', 'jetpack' ) }
aria-describedby={ `${ id }-password-description` }
placeholder={ 'xxxx-xxxx-xxxx-xxxx' }
/>
</div>
</>
);
}

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function ServiceItem( { service, serviceConnections }: ServicesItemProps

const [ isPanelOpen, togglePanel ] = useReducer( state => ! state, false );

const isMastodonPanelOpen = isPanelOpen && service.ID === 'mastodon';
const areCustomInputsVisible = isPanelOpen && service.needsCustomInputs;

const brokenConnections = serviceConnections.filter( ( { status } ) => status === 'broken' );

Expand All @@ -31,9 +31,9 @@ export function ServiceItem( { service, serviceConnections }: ServicesItemProps
);

const hideInitialConnectForm =
// For Mastodon, the initial connect form opens the panel,
// For services with custom inputs, the initial Connect button opens the panel,
// so we don't want to show it if the panel is already open
isMastodonPanelOpen ||
areCustomInputsVisible ||
// For services with broken connections, we want to show the "Fix connections" button
// which opens the panel, so we don't want to show the initial connect form when the panel is already open
( hasOwnBrokenConnections && isPanelOpen );
Expand Down Expand Up @@ -97,18 +97,20 @@ export function ServiceItem( { service, serviceConnections }: ServicesItemProps
<Panel className={ styles[ 'service-panel' ] }>
<PanelBody opened={ isPanelOpen } onToggle={ togglePanel }>
<ServiceItemDetails service={ service } serviceConnections={ serviceConnections } />

{ /* Only show the connect form for Mastodon if there are no broken connections */ }
{ service.ID === 'mastodon' && ! hasOwnBrokenConnections ? (
<div className={ styles[ 'connect-form-wrapper' ] }>
<ConnectForm
service={ service }
displayInputs
isSmall={ false }
buttonLabel={ __( 'Connect', 'jetpack' ) }
/>
</div>
) : null }
{
// Connect form for services that need custom inputs
// should be shown only if there are no broken connections
service.needsCustomInputs && ! hasOwnBrokenConnections ? (
<div className={ styles[ 'connect-form-wrapper' ] }>
<ConnectForm
service={ service }
displayInputs
isSmall={ false }
buttonLabel={ __( 'Connect', 'jetpack' ) }
/>
</div>
) : null
}
</PanelBody>
</Panel>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
@import '@automattic/jetpack-base-styles/gutenberg-base-styles';



.services {
outline: 1px solid var(--jp-gray-5);
border-radius: 4px;
Expand Down Expand Up @@ -95,23 +93,6 @@
:global(.components-panel__body.is-opened) {
margin-top: 1rem;
}

.connect-form-wrapper {
margin-top: 1rem;
display: flex;

form {
display: flex;
gap: 1rem;
flex-direction: column;
justify-content: flex-end;
width: 100%;

@include break-small {
flex-direction: row;
}
}
}
}

.active-connection {
Expand Down Expand Up @@ -194,7 +175,23 @@
}
}

.connect-form-wrapper {
margin-top: 2rem;
display: flex;
justify-content: flex-end;
}

.connect-form {
display: flex;
gap: 1.5rem;
flex-direction: column;
justify-content: flex-end;
width: 100%;

@include break-medium {
max-width: 25rem;
}

.connect-button {
&:global(.components-button) {
padding: 6px 16px;
Expand All @@ -204,12 +201,39 @@
}
}

.fields-wrapper {
display: flex;
flex-direction: column;
align-items: flex-end;
flex-wrap: wrap;
width: 100%;
gap: 1rem;

@include break-small {
flex-direction: row;
}
}

.fields-item {
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
}

label {
font-weight: 600;
}

input {
min-width: 220px;
height: auto;
font-size: var(--font-body);
line-height: 22px;
padding: var(--spacing-base) calc(var(--spacing-base)* 3); // To make the inputs of the same size as the Connect button

// Override the default input styles for small screens
@media (max-width: #{ ($break-medium) }) {
min-height: 30px;
padding: 0 8px;
font-size: 14px;
}
Expand Down
Loading

0 comments on commit b337121

Please sign in to comment.