Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Forms: add integrations store.
14 changes: 11 additions & 3 deletions projects/packages/forms/src/dashboard/integrations/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
* External dependencies
*/
import jetpackAnalytics from '@automattic/jetpack-analytics';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { useState, useCallback } from 'react';
/**
* Internal dependencies
*/
import { useIntegrationsStatus } from '../../blocks/contact-form/components/jetpack-integrations-modal/hooks/use-integrations-status';
import { INTEGRATIONS_STORE } from '../../store/integrations';
import AkismetDashboardCard from './akismet-card';
import CreativeMailDashboardCard from './creative-mail-card';
import GoogleSheetsDashboardCard from './google-sheets-card';
Expand All @@ -18,10 +19,17 @@ import './style.scss';
/**
* Types
*/
import type { SelectIntegrations, IntegrationsDispatch } from '../../store/integrations';
import type { Integration } from '../../types';

const Integrations = () => {
const { integrations, refreshIntegrations } = useIntegrationsStatus();
const { integrations } = useSelect( ( select: SelectIntegrations ) => {
const store = select( INTEGRATIONS_STORE );
return {
integrations: store.getIntegrations() || [],
};
}, [] ) as { integrations: Integration[] };
const { refreshIntegrations } = useDispatch( INTEGRATIONS_STORE ) as IntegrationsDispatch;
const [ expandedCards, setExpandedCards ] = useState( {
akismet: false,
googleSheets: false,
Expand Down Expand Up @@ -63,7 +71,7 @@ const Integrations = () => {
const handleToggleMailPoet = useCallback( () => toggleCard( 'mailpoet' ), [ toggleCard ] );

const findIntegrationById = ( id: string ) =>
integrations?.find( ( integration: Integration ) => integration.id === id );
integrations.find( integration => integration.id === id );

// Only supported integrations will be returned from endpoint.
const akismetData = findIntegrationById( 'akismet' );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const RECEIVE_INTEGRATIONS = 'RECEIVE_INTEGRATIONS';
export const INVALIDATE_INTEGRATIONS = 'INVALIDATE_INTEGRATIONS';
export const SET_INTEGRATIONS_LOADING = 'SET_INTEGRATIONS_LOADING';
export const SET_INTEGRATIONS_ERROR = 'SET_INTEGRATIONS_ERROR';
30 changes: 30 additions & 0 deletions projects/packages/forms/src/store/integrations/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
RECEIVE_INTEGRATIONS,
INVALIDATE_INTEGRATIONS,
SET_INTEGRATIONS_LOADING,
SET_INTEGRATIONS_ERROR,
} from './action-types';
import { getIntegrations } from './resolvers';
import type { Integration } from '../../types';

export const receiveIntegrations = ( items: Integration[] ) => ( {
type: RECEIVE_INTEGRATIONS,
items,
} );

export const invalidateIntegrations = () => ( {
type: INVALIDATE_INTEGRATIONS,
} );

export const setIntegrationsLoading = ( isLoading: boolean ) => ( {
type: SET_INTEGRATIONS_LOADING,
isLoading,
} );

export const setIntegrationsError = ( error: string | null ) => ( {
type: SET_INTEGRATIONS_ERROR,
error,
} );

// Thunk-like action to immediately refresh from the endpoint
export const refreshIntegrations = () => getIntegrations();
20 changes: 20 additions & 0 deletions projects/packages/forms/src/store/integrations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createReduxStore, register } from '@wordpress/data';
import * as actions from './actions';
import reducer from './reducer';
import * as resolvers from './resolvers';
import * as selectors from './selectors';

export const INTEGRATIONS_STORE = 'jetpack/forms/integrations';

export const store = createReduxStore( INTEGRATIONS_STORE, {
reducer,
actions,
selectors,
resolvers,
} );

register( store );

export * from './actions';
export * from './selectors';
export * from './types';
55 changes: 55 additions & 0 deletions projects/packages/forms/src/store/integrations/reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
RECEIVE_INTEGRATIONS,
INVALIDATE_INTEGRATIONS,
SET_INTEGRATIONS_LOADING,
SET_INTEGRATIONS_ERROR,
} from './action-types';
import type { IntegrationsState, IntegrationsAction } from './types';

const DEFAULT_STATE: IntegrationsState = {
items: null,
isLoading: false,
error: null,
};

/**
* Integrations store reducer.
*
* @param state - Current state
* @param action - Dispatched action
* @return Updated state
*/
export default function reducer(
state: IntegrationsState = DEFAULT_STATE,
action: IntegrationsAction
): IntegrationsState {
switch ( action.type ) {
case SET_INTEGRATIONS_LOADING:
return {
...state,
isLoading: !! action.isLoading,
error: action.isLoading ? null : state.error,
};
case SET_INTEGRATIONS_ERROR:
return {
...state,
isLoading: false,
error: action.error ?? 'Unknown error',
};
case RECEIVE_INTEGRATIONS:
return {
...state,
items: action.items,
isLoading: false,
error: null,
};
case INVALIDATE_INTEGRATIONS:
return {
...state,
items: null,
isLoading: false,
};
default:
return state;
}
}
26 changes: 26 additions & 0 deletions projects/packages/forms/src/store/integrations/resolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import { INVALIDATE_INTEGRATIONS } from './action-types';
import { receiveIntegrations, setIntegrationsError, setIntegrationsLoading } from './actions';
import type { IntegrationsAction } from './types';
import type { Integration } from '../../types';

export const getIntegrations =
() =>
async ( { dispatch }: { dispatch: ( action: IntegrationsAction ) => void } ) => {
dispatch( setIntegrationsLoading( true ) );
try {
const path = addQueryArgs( '/wp/v2/feedback/integrations', { version: 2 } );
const result = await apiFetch< Integration[] >( { path } );
dispatch( receiveIntegrations( result ) );
} catch ( e ) {
const message = e instanceof Error ? e.message : 'Unknown error';
dispatch( setIntegrationsError( message ) );
} finally {
dispatch( setIntegrationsLoading( false ) );
}
};

// Attach invalidation rule
getIntegrations.shouldInvalidate = ( action: IntegrationsAction ) =>
action.type === INVALIDATE_INTEGRATIONS;
6 changes: 6 additions & 0 deletions projects/packages/forms/src/store/integrations/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { IntegrationsState } from './types';
import type { Integration } from '../../types';

export const getIntegrations = ( state: IntegrationsState ): Integration[] | null => state.items;
export const isIntegrationsLoading = ( state: IntegrationsState ): boolean => state.isLoading;
export const getIntegrationsError = ( state: IntegrationsState ): string | null => state.error;
28 changes: 28 additions & 0 deletions projects/packages/forms/src/store/integrations/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { INTEGRATIONS_STORE } from '.';
import type { Integration } from '../../types';

export type IntegrationsState = {
items: Integration[] | null;
isLoading: boolean;
error: string | null;
};

export type IntegrationsAction = {
type: string;
items?: Integration[];
isLoading?: boolean;
error?: string | null;
};

export type IntegrationsSelectors = {
getIntegrations: () => Integration[] | null;
isIntegrationsLoading: () => boolean;
getIntegrationsError: () => string | null;
};

export type IntegrationsDispatch = {
refreshIntegrations: () => Promise< void >;
invalidateIntegrations: () => void;
};

export type SelectIntegrations = ( store: typeof INTEGRATIONS_STORE ) => IntegrationsSelectors;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: enhancement

Forms: add integrations store.
Loading