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: Create a social service connect button #37196

Merged
merged 19 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Fixed notices z-index for global notices when modal is open
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
inset-block-start: auto; // top
inset-block-end: 0; // bottom
inset-inline: 0; // left and right
// Modals have 100000, so this needs to be above them
z-index: 100001;

@include break-small {
width: auto;
Expand Down
2 changes: 1 addition & 1 deletion projects/js-packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@automattic/jetpack-components",
"version": "0.53.1",
"version": "0.53.2-alpha",
"description": "Jetpack Components Package",
"author": "Automattic",
"license": "GPL-2.0-or-later",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Add connect form/button for connection management
1 change: 1 addition & 0 deletions projects/js-packages/publicize-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@automattic/jetpack-components": "workspace:*",
"@automattic/jetpack-connection": "workspace:*",
"@automattic/jetpack-shared-extension-utils": "workspace:*",
"@automattic/popup-monitor": "1.0.2",
"@automattic/social-previews": "2.0.1-beta.13",
"@wordpress/annotations": "2.56.0",
"@wordpress/api-fetch": "6.53.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Button, useGlobalNotices } from '@automattic/jetpack-components';
import { __ } from '@wordpress/i18n';
import classNames from 'classnames';
import { useCallback } from 'react';
import { requestExternalAccess } from '../../utils';
import styles from './style.module.scss';
import type { SupportedService } from './use-supported-services';

type ConnectFormProps = {
service: SupportedService;
isSmall?: boolean;
onConfirm: ( data: unknown ) => void;
onSubmit?: VoidFunction;
displayInputs?: boolean;
isMastodonAlreadyConnected?: ( username: string ) => boolean;
};

const isValidMastodonUsername = ( username: string ) =>
/^@?\b([A-Z0-9_]+)@([A-Z0-9.-]+\.[A-Z]{2,})$/gi.test( username );

/**
* Connect form component
*
* @param {ConnectFormProps} props - Component props
*
* @returns {import('react').ReactNode} Connect form component
*/
export function ConnectForm( {
service,
isSmall,
onConfirm,
onSubmit,
displayInputs,
isMastodonAlreadyConnected,
}: ConnectFormProps ) {
const { createErrorNotice } = useGlobalNotices();

const onSubmitForm = useCallback(
( event: React.FormEvent ) => {
event.preventDefault();
// Prevent Jetpack settings from being submitted
event.stopPropagation();

if ( onSubmit ) {
return onSubmit();
}
const formData = new FormData( event.target as HTMLFormElement );
const url = new URL( service.connect_URL );

switch ( service.ID ) {
case 'mastodon': {
const instance = formData.get( 'instance' ).toString().trim();

if ( ! isValidMastodonUsername( instance ) ) {
createErrorNotice( __( 'Invalid Mastodon username', 'jetpack' ) );

return;
}

if ( isMastodonAlreadyConnected?.( instance ) ) {
createErrorNotice( __( 'This Mastodon account is already connected', 'jetpack' ) );

return;
}

url.searchParams.set( 'instance', formData.get( 'instance' ) as string );
break;
}

default:
break;
}

requestExternalAccess( url.toString(), onConfirm );
},
[
createErrorNotice,
isMastodonAlreadyConnected,
onConfirm,
onSubmit,
service.ID,
service.connect_URL,
]
);

return (
<form
className={ classNames( styles[ 'connect-form' ], { [ styles.small ]: isSmall } ) }
onSubmit={ onSubmitForm }
>
{ displayInputs ? (
<>
{ 'mastodon' === service.ID ? (
<input
required
type="text"
name="instance"
aria-label={ __( 'Mastodon username', 'jetpack' ) }
placeholder={ '@[email protected]' }
/>
) : null }
</>
) : null }
<Button variant="primary" type="submit" size={ isSmall ? 'small' : 'normal' }>
{ __( 'Connect', 'jetpack' ) }
</Button>
</form>
);
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
import { Button, useBreakpointMatch } from '@automattic/jetpack-components';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import classNames from 'classnames';
import { SupportedService } from '../constants';
import { useCallback } from 'react';
import { store as socialStore } from '../../../social-store';
import { ConnectForm } from '../connect-form';
import styles from './style.module.scss';
import type { SupportedService } from '../use-supported-services';

type ConnectPageProps = {
service: SupportedService;
onBackClicked: VoidFunction;
onConfirm: ( data: unknown ) => void;
};

export const ConnectPage: React.FC< ConnectPageProps > = ( { service, onBackClicked } ) => {
export const ConnectPage: React.FC< ConnectPageProps > = ( {
service,
onBackClicked,
onConfirm,
} ) => {
const [ isSmall ] = useBreakpointMatch( 'sm' );

const connections = useSelect( select => {
return select( socialStore ).getConnections();
}, [] );

const isMastodonAlreadyConnected = useCallback(
( username: string ) => {
return connections.some( connection => {
return connection.service_name === 'mastodon' && connection.external_display === username;
} );
},
[ connections ]
);

return (
<>
<div
Expand All @@ -20,7 +42,7 @@ export const ConnectPage: React.FC< ConnectPageProps > = ( { service, onBackClic
} ) }
>
{ service.examples.map( ( Example, idx ) => (
<div key={ service.name + idx } className={ styles.example }>
<div key={ service.ID + idx } className={ styles.example }>
<Example />
</div>
) ) }
Expand All @@ -33,19 +55,13 @@ export const ConnectPage: React.FC< ConnectPageProps > = ( { service, onBackClic
>
{ __( 'Back', 'jetpack' ) }
</Button>
<form className={ classNames( styles[ 'connect-form' ], { [ styles.small ]: isSmall } ) }>
{ 'mastodon' === service.name ? (
<input
required
type="text"
aria-label={ __( 'Mastodon username', 'jetpack' ) }
placeholder={ '@[email protected]' }
/>
) : null }
<Button type="submit" variant="primary">
{ __( 'Connect', 'jetpack' ) }
</Button>
</form>
<ConnectForm
service={ service }
isSmall={ isSmall }
onConfirm={ onConfirm }
displayInputs
isMastodonAlreadyConnected={ isMastodonAlreadyConnected }
/>
</div>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,3 @@
.chevron-back {
display: block;
}

.connect-form {
display: grid;
gap: 0.5rem;

& > input {
height: 100%;
width: 18rem;
}

&:not(.small) {
grid-auto-flow: column;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Button, useBreakpointMatch } from '@automattic/jetpack-components';
import { Modal } from '@wordpress/components';
import { useCallback } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { Icon, chevronDown } from '@wordpress/icons';
import classNames from 'classnames';
import { useCallback } from 'react';
import { ConnectForm } from './connect-form';
import { ConnectPage } from './connect-page/connect-page';
import { SupportedService, getSupportedServices } from './constants';
import styles from './style.module.scss';
import { SupportedService, useSupportedServices } from './use-supported-services';

type AddConnectionModalProps = {
onCloseModal: VoidFunction;
Expand All @@ -19,6 +20,8 @@ const AddConnectionModal = ( {
currentService,
setCurrentService,
}: AddConnectionModalProps ) => {
const supportedServices = useSupportedServices();

const [ isSmall ] = useBreakpointMatch( 'sm' );

const onServiceSelected = useCallback(
Expand All @@ -32,6 +35,11 @@ const AddConnectionModal = ( {
setCurrentService( null );
}, [ setCurrentService ] );

const onConfirm = useCallback( ( data: unknown ) => {
// eslint-disable-next-line no-console
console.log( data );
}, [] );

return (
<Modal
className={ classNames( styles.modal, {
Expand All @@ -44,13 +52,17 @@ const AddConnectionModal = ( {
? sprintf(
// translators: %s: Name of the service the user connects to.
__( 'Connecting a new %s account', 'jetpack' ),
currentService.title
currentService.label
)
: __( 'Add a new connection to Jetpack Social', 'jetpack' )
}
>
{ currentService ? (
<ConnectPage service={ currentService } onBackClicked={ onBackClicked } />
<ConnectPage
service={ currentService }
onBackClicked={ onBackClicked }
onConfirm={ onConfirm }
/>
) : (
<table>
<thead>
Expand All @@ -61,8 +73,8 @@ const AddConnectionModal = ( {
</tr>
</thead>
<tbody>
{ getSupportedServices().map( service => (
<tr key={ service.name }>
{ supportedServices.map( service => (
<tr key={ service.ID }>
<td>
<service.icon iconSize={ isSmall ? 36 : 48 } />
</td>
Expand All @@ -71,16 +83,21 @@ const AddConnectionModal = ( {
[ styles.small ]: ! isSmall,
} ) }
>
<h2 className={ styles.title }>{ service.title }</h2>
<h2 className={ styles.title }>{ service.label }</h2>
{ ! isSmall ? (
<p className={ styles.description }>{ service.description }</p>
) : null }
</td>
<td>
<div className={ styles[ 'column-actions' ] }>
<Button type="submit" variant="primary" size={ isSmall ? 'small' : 'normal' }>
{ __( 'Connect', 'jetpack' ) }
</Button>
<ConnectForm
service={ service }
isSmall={ isSmall }
onConfirm={ onConfirm }
onSubmit={
service.needsCustomInputs ? onServiceSelected( service ) : undefined
}
/>
<Button
size={ isSmall ? 'small' : 'normal' }
className={ styles[ 'chevron-button' ] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,17 @@ tr:not(:last-child) > td {
padding-inline-end: 0.5rem;
margin-inline-start: 0.4rem;
}

.connect-form {
display: grid;
gap: 0.5rem;

& > input {
height: 100%;
width: 18rem;
}

&:not(.small) {
grid-auto-flow: column;
}
}
Loading
Loading