+ disconnectSite( { siteId: id, localSiteId: selectedSite.id } )
+ }
+ openModal={ () => setIsModalOpen( true ) }
/>
dispatch( connectedSitesActions.openModal( 'connect' ) ) }
+ connectSite={ () => {
+ setIsModalOpen( true );
+ setModalMode( 'connect' );
+ } }
>
{ __( 'Connect another site' ) }
@@ -244,23 +241,23 @@ export function ContentTabSync( { selectedSite }: { selectedSite: SiteDetails }
{ isModalOpen && (
{
- dispatch( connectedSitesActions.closeModal() );
+ setIsModalOpen( false );
} }
syncSites={ syncSites }
onInitialRender={ refetchSites }
onConnect={ async ( siteId: number ) => {
- await handleSiteSelection( siteId, reduxModalMode );
+ await handleSiteSelection( siteId, modalMode );
} }
selectedSite={ selectedSite }
/>
) }
- { reduxModalMode && reduxModalMode !== 'connect' && selectedRemoteSite && (
+ { modalMode && modalMode !== 'connect' && selectedRemoteSite && (
{
@@ -275,7 +272,7 @@ export function ContentTabSync( { selectedSite }: { selectedSite: SiteDetails }
} }
onRequestClose={ () => {
setSelectedRemoteSite( null );
- dispatch( connectedSitesActions.setModalMode( null ) );
+ setModalMode( null );
} }
/>
) }
diff --git a/src/modules/sync/tests/index.test.tsx b/src/modules/sync/tests/index.test.tsx
index 870fb7fa4b..43f549ecfd 100644
--- a/src/modules/sync/tests/index.test.tsx
+++ b/src/modules/sync/tests/index.test.tsx
@@ -5,22 +5,17 @@ import { SyncSitesProvider, useSyncSites } from 'src/hooks/sync-sites';
import { SyncPushState } from 'src/hooks/sync-sites/use-sync-push';
import { useAuth } from 'src/hooks/use-auth';
import { ContentTabsProvider } from 'src/hooks/use-content-tabs';
+import { useFetchWpComSites } from 'src/hooks/use-fetch-wpcom-sites';
import { SyncSite } from 'src/hooks/use-fetch-wpcom-sites/types';
import { getIpcApi } from 'src/lib/get-ipc-api';
import { ContentTabSync } from 'src/modules/sync';
import { useSelectedItemsPushSize } from 'src/modules/sync/hooks/use-selected-items-push-size';
import { store } from 'src/stores';
-import {
- useLatestRewindId,
- useRemoteFileTree,
- useConnectedSitesData,
- useSyncSitesData,
- useConnectedSitesOperations,
- connectedSitesSelectors,
-} from 'src/stores/sync';
+import { useLatestRewindId, useRemoteFileTree } from 'src/stores/sync';
-jest.mock( 'src/hooks/use-auth' );
jest.mock( 'src/lib/get-ipc-api' );
+jest.mock( 'src/hooks/use-auth' );
+jest.mock( 'src/hooks/use-fetch-wpcom-sites' );
jest.mock( 'src/hooks/sync-sites/sync-sites-context', () => ( {
...jest.requireActual( '../../../hooks/sync-sites/sync-sites-context' ),
useSyncSites: jest.fn(),
@@ -39,9 +34,6 @@ jest.mock( 'src/stores/sync', () => ( {
error: null,
isLoading: false,
} ),
- useConnectedSitesData: jest.fn(),
- useSyncSitesData: jest.fn(),
- useConnectedSitesOperations: jest.fn(),
connectedSitesSelectors: {
selectIsModalOpen: jest.fn(),
selectModalMode: jest.fn(),
@@ -64,6 +56,7 @@ jest.mock( 'src/modules/sync/hooks/use-selected-items-push-size' );
const createAuthMock = ( isAuthenticated: boolean = false ) => ( {
isAuthenticated,
authenticate: jest.fn(),
+ user: isAuthenticated ? { id: 123, email: 'user@example.com' } : null,
} );
const selectedSite: SiteDetails = {
@@ -87,24 +80,30 @@ const inProgressPushState: SyncPushState = {
remoteSiteUrl: 'https://example.com',
};
-const fakeSyncSite = {
+const fakeSyncSite: SyncSite = {
id: 6,
name: 'My simple business site that needs a transfer',
url: 'https://developer.wordpress.com/studio/',
syncSupport: 'already-connected',
+ isStaging: false,
+ isPressable: false,
+ localSiteId: 'site-id',
+ lastPullTimestamp: null,
+ lastPushTimestamp: null,
};
describe( 'ContentTabSync', () => {
const mockSyncSites = {
pullSite: jest.fn(),
+ pushSite: jest.fn(),
isAnySitePulling: false,
isAnySitePushing: false,
getPullState: jest.fn(),
- getPushState: jest.fn().mockReturnValue( inProgressPushState ),
+ getPushState: jest.fn(),
updateTimestamp: jest.fn(),
- getLastSyncTimeWithType: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
+ getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
+ isSiteIdPulling: jest.fn().mockReturnValue( false ),
+ isSiteIdPushing: jest.fn().mockReturnValue( false ),
clearTimeout: jest.fn(),
};
@@ -112,12 +111,11 @@ describe( 'ContentTabSync', () => {
connectedSites: SyncSite[] = [],
syncSites: SyncSite[] = []
) => {
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites,
- loading: false,
- localSiteId: 'site-id',
- } );
- ( useSyncSitesData as jest.Mock ).mockReturnValue( {
+ // Update the IPC API mock to return the connected sites
+ const currentMock = ( getIpcApi as jest.Mock )();
+ currentMock.getConnectedWpcomSites.mockResolvedValue( connectedSites );
+
+ ( useFetchWpComSites as jest.Mock ).mockReturnValue( {
syncSites,
isFetching: false,
refetchSites: jest.fn(),
@@ -125,6 +123,8 @@ describe( 'ContentTabSync', () => {
};
beforeEach( () => {
jest.resetAllMocks();
+ // Clear RTK Query cache
+ store.dispatch( { type: 'connectedSitesApi/resetApiState' } );
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( false ) );
( getIpcApi as jest.Mock ).mockReturnValue( {
authenticate: jest.fn(),
@@ -134,6 +134,7 @@ describe( 'ContentTabSync', () => {
updateConnectedWpcomSites: jest.fn(),
getConnectedWpcomSites: jest.fn().mockResolvedValue( [] ),
getDirectorySize: jest.fn().mockResolvedValue( 0 ),
+ connectWpcomSites: jest.fn(),
listLocalFileTree: jest.fn().mockResolvedValue( [
{
name: 'plugins',
@@ -172,28 +173,15 @@ describe( 'ContentTabSync', () => {
error: null,
} );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [],
- loading: false,
- localSiteId: 'site-id',
- } );
-
- ( useSyncSitesData as jest.Mock ).mockReturnValue( {
+ ( useFetchWpComSites as jest.Mock ).mockReturnValue( {
syncSites: [],
isFetching: false,
refetchSites: jest.fn(),
} );
- ( useConnectedSitesOperations as jest.Mock ).mockReturnValue( {
- connectSite: jest.fn(),
- disconnectSite: jest.fn(),
- } );
-
const { useAppDispatch } = jest.requireMock( 'src/stores' );
useAppDispatch.mockReturnValue( jest.fn() );
- ( connectedSitesSelectors.selectIsModalOpen as jest.Mock ).mockReturnValue( false );
- ( connectedSitesSelectors.selectModalMode as jest.Mock ).mockReturnValue( null );
( useRemoteFileTree as jest.Mock ).mockReturnValue( {
fetchChildren: jest.fn().mockResolvedValue( [
{
@@ -283,76 +271,50 @@ describe( 'ContentTabSync', () => {
const importButton = screen.getByRole( 'button', { name: /Pull site/i } );
fireEvent.click( importButton );
- ( connectedSitesSelectors.selectIsModalOpen as jest.Mock ).mockReturnValue( true );
- ( connectedSitesSelectors.selectModalMode as jest.Mock ).mockReturnValue( null );
-
- renderWithProvider( );
expect( screen.getByTestId( 'sync-sites-modal-selector' ) ).toBeInTheDocument();
} );
it( 'displays the list of connected sites', async () => {
- const fakeSyncSite = {
+ const fakeSyncSite: SyncSite = {
id: 6,
name: 'My simple business site that needs a transfer',
url: 'https://developer.wordpress.com/studio/',
+ isPressable: false,
isStaging: false,
+ lastPullTimestamp: null,
+ lastPushTimestamp: null,
+ localSiteId: 'site-id',
syncSupport: 'already-connected',
};
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- loading: false,
- localSiteId: 'site-id',
- } );
- ( useSyncSites as jest.Mock ).mockReturnValue( {
- pullSite: jest.fn(),
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
- getPushState: jest.fn().mockReturnValue( undefined ),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
- clearTimeout: jest.fn(),
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- expect( screen.getByText( fakeSyncSite.name ) ).toBeInTheDocument();
+ await screen.findByText( fakeSyncSite.name );
expect( screen.getByRole( 'button', { name: /Disconnect/i } ) ).toBeInTheDocument();
- expect( screen.getByRole( 'button', { name: /Pull/i } ) ).toBeInTheDocument();
- expect( screen.getByRole( 'button', { name: /Push/i } ) ).toBeInTheDocument();
+ expect( screen.getByRole( 'button', { name: 'Pull' } ) ).toBeInTheDocument();
+ expect( screen.getByRole( 'button', { name: 'Push' } ) ).toBeInTheDocument();
expect( screen.getByText( 'Production' ) ).toBeInTheDocument();
} );
it( 'opens URL for connected sites', async () => {
- const fakeSyncSite = {
+ const fakeSyncSite: SyncSite = {
id: 6,
name: 'My simple business site that needs a transfer',
url: 'https://developer.wordpress.com/studio/',
isStaging: false,
syncSupport: 'already-connected',
+ localSiteId: 'site-id',
+ isPressable: false,
+ lastPullTimestamp: null,
+ lastPushTimestamp: null,
};
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- loading: false,
- localSiteId: 'site-id',
- } );
- ( useSyncSites as jest.Mock ).mockReturnValue( {
- pullSite: jest.fn(),
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
- getPushState: jest.fn().mockReturnValue( inProgressPushState ),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
- clearTimeout: jest.fn(),
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- const readableUrl = fakeSyncSite.url.replace( 'https://', '' );
- const urlButton = screen.getByRole( 'button', {
+ const readableUrl = fakeSyncSite.url.replace( /^https?:\/\//, '' );
+ const urlButton = await screen.findByRole( 'button', {
name: ( content ) => content.includes( readableUrl ),
} );
expect( urlButton ).toBeInTheDocument();
@@ -367,9 +329,6 @@ describe( 'ContentTabSync', () => {
const importButton = screen.getByRole( 'button', { name: /Pull site/i } );
expect( importButton ).toBeInTheDocument();
fireEvent.click( importButton );
- ( connectedSitesSelectors.selectIsModalOpen as jest.Mock ).mockReturnValue( true );
- ( connectedSitesSelectors.selectModalMode as jest.Mock ).mockReturnValue( null );
- renderWithProvider( );
const createNewSiteButton = screen.getByRole( 'button', {
name: /Create a new WordPress.com site ↗/i,
} );
@@ -387,7 +346,7 @@ describe( 'ContentTabSync', () => {
expect( importButton ).toBeInTheDocument();
} );
- it( 'displays environment badges for Pressable sites with production, staging and development environments', () => {
+ it( 'displays environment badges for Pressable sites with production, staging and development environments', async () => {
const fakePressableProductionSite: SyncSite = {
id: 6,
name: 'My Pressable Production site',
@@ -432,24 +391,9 @@ describe( 'ContentTabSync', () => {
fakePressableDevelopmentSite,
];
setupConnectedSitesMocks( allSites, [ fakePressableProductionSite ] );
-
- ( useSyncSites as jest.Mock ).mockReturnValue( {
- connectedSites: allSites,
- syncSites: [ fakePressableProductionSite ],
- pullSite: jest.fn(),
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
- getPushState: jest.fn().mockReturnValue( undefined ),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
- clearTimeout: jest.fn(),
- } );
-
renderWithProvider( );
- expect( screen.getByText( fakePressableProductionSite.name ) ).toBeInTheDocument();
+ await screen.findByText( fakePressableProductionSite.name );
expect( screen.getByText( fakePressableStagingSite.name ) ).toBeInTheDocument();
expect( screen.getByText( fakePressableDevelopmentSite.name ) ).toBeInTheDocument();
@@ -457,7 +401,7 @@ describe( 'ContentTabSync', () => {
expect( screen.getByText( 'Staging' ) ).toBeInTheDocument();
expect( screen.getByText( 'Development' ) ).toBeInTheDocument();
} );
- it( 'displays the progress bar when the site is being pushed', () => {
+ it( 'displays the progress bar when the site is being pushed', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
const fakeSyncSite: SyncSite = {
id: 6,
@@ -472,55 +416,41 @@ describe( 'ContentTabSync', () => {
};
setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
( useSyncSites as jest.Mock ).mockReturnValue( {
- pullSite: jest.fn(),
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
+ ...mockSyncSites,
getPushState: jest.fn().mockReturnValue( inProgressPushState ),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
isSiteIdPushing: jest.fn().mockReturnValue( true ),
- clearTimeout: jest.fn(),
} );
renderWithProvider( );
- expect( screen.getByRole( 'progressbar' ) ).toBeInTheDocument();
+ await screen.findByRole( 'progressbar' );
} );
it( 'opens sync pullSite dialog with development environment label', async () => {
const mockPullSite = jest.fn();
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- const fakeSyncSite = {
+ const fakeSyncSite: SyncSite = {
id: 6,
name: 'My simple business site that needs a transfer',
url: 'https://developer.wordpress.com/studio/',
syncSupport: 'already-connected',
+ isStaging: false,
isPressable: true,
environmentType: 'development',
+ localSiteId: 'site-id',
+ lastPullTimestamp: null,
+ lastPushTimestamp: null,
};
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
( useSyncSites as jest.Mock ).mockReturnValue( {
+ ...mockSyncSites,
syncSites: [ fakeSyncSite ],
pullSite: mockPullSite,
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
- getPushState: jest.fn(),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
- clearTimeout: jest.fn(),
} );
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
expect( pullButton ).toBeInTheDocument();
fireEvent.click( pullButton );
@@ -532,33 +462,28 @@ describe( 'ContentTabSync', () => {
it( 'opens sync pullSite dialog and displays production when the environment is not supported', async () => {
const mockPullSite = jest.fn();
- ( useAuth as jest.Mock ).mockReturnValue( { isAuthenticated: true, authenticate: jest.fn() } );
- const fakeSyncSite = {
+ ( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
+ const fakeSyncSite: SyncSite = {
id: 6,
name: 'My simple business site that needs a transfer',
url: 'https://developer.wordpress.com/studio/',
syncSupport: 'already-connected',
+ isStaging: false,
isPressable: true,
environmentType: 'non-supported-environment-example-or-sandbox',
+ localSiteId: 'site-id',
+ lastPullTimestamp: null,
+ lastPushTimestamp: null,
};
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
( useSyncSites as jest.Mock ).mockReturnValue( {
+ ...mockSyncSites,
pullSite: mockPullSite,
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
- getPushState: jest.fn(),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
- clearTimeout: jest.fn(),
} );
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
expect( pullButton ).toBeInTheDocument();
fireEvent.click( pullButton );
@@ -571,30 +496,26 @@ describe( 'ContentTabSync', () => {
it( 'calls pullSite with correct optionsToSync when all options are selected', async () => {
const mockPullSite = jest.fn();
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- const fakeSyncSite = {
+ const fakeSyncSite: SyncSite = {
id: 6,
name: 'My simple business site that needs a transfer',
url: 'https://developer.wordpress.com/studio/',
syncSupport: 'already-connected',
+ localSiteId: 'site-id',
+ isStaging: false,
+ isPressable: false,
+ lastPullTimestamp: null,
+ lastPushTimestamp: null,
};
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
( useSyncSites as jest.Mock ).mockReturnValue( {
+ ...mockSyncSites,
pullSite: mockPullSite,
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
- getPushState: jest.fn(),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
- clearTimeout: jest.fn(),
} );
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
expect( pullButton ).toBeInTheDocument();
fireEvent.click( pullButton );
@@ -605,8 +526,8 @@ describe( 'ContentTabSync', () => {
const databaseCheckbox = screen.getByRole( 'checkbox', { name: 'Database' } );
fireEvent.click( databaseCheckbox );
- const dialogPullButton = screen.getAllByRole( 'button', { name: /Pull/i } );
- fireEvent.click( dialogPullButton[ 1 ] );
+ const dialogPullButton = await screen.findByRole( 'button', { name: 'Pull' } );
+ fireEvent.click( dialogPullButton );
expect( mockPullSite ).toHaveBeenCalledWith( fakeSyncSite, selectedSite, {
optionsToSync: [ 'all' ],
@@ -616,30 +537,26 @@ describe( 'ContentTabSync', () => {
it( 'calls pullSite with correct optionsToSync when only database is selected', async () => {
const mockPullSite = jest.fn();
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- const fakeSyncSite = {
+ const fakeSyncSite: SyncSite = {
id: 6,
name: 'My simple business site that needs a transfer',
url: 'https://developer.wordpress.com/studio/',
syncSupport: 'already-connected',
+ localSiteId: 'site-id',
+ isStaging: false,
+ isPressable: false,
+ lastPullTimestamp: null,
+ lastPushTimestamp: null,
};
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
( useSyncSites as jest.Mock ).mockReturnValue( {
+ ...mockSyncSites,
pullSite: mockPullSite,
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
- getPushState: jest.fn(),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
- clearTimeout: jest.fn(),
} );
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
expect( pullButton ).toBeInTheDocument();
fireEvent.click( pullButton );
@@ -648,8 +565,8 @@ describe( 'ContentTabSync', () => {
const databaseCheckbox = screen.getByRole( 'checkbox', { name: 'Database' } );
fireEvent.click( databaseCheckbox );
- const dialogPullButton = screen.getAllByRole( 'button', { name: /Pull/i } );
- fireEvent.click( dialogPullButton[ 1 ] );
+ const dialogPullButton = await screen.findByRole( 'button', { name: 'Pull' } );
+ fireEvent.click( dialogPullButton );
expect( mockPullSite ).toHaveBeenCalledWith( fakeSyncSite, selectedSite, {
optionsToSync: [ 'sqls' ],
@@ -724,30 +641,26 @@ describe( 'ContentTabSync', () => {
error: null,
isLoading: false,
} );
- const fakeSyncSite = {
+ const fakeSyncSite: SyncSite = {
id: 6,
name: 'My simple business site that needs a transfer',
url: 'https://developer.wordpress.com/studio/',
syncSupport: 'already-connected',
+ isStaging: false,
+ localSiteId: 'site-id',
+ isPressable: false,
+ lastPullTimestamp: null,
+ lastPushTimestamp: null,
};
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
( useSyncSites as jest.Mock ).mockReturnValue( {
+ ...mockSyncSites,
pullSite: mockPullSite,
- isAnySitePulling: false,
- isAnySitePushing: false,
- getPullState: jest.fn(),
- getPushState: jest.fn(),
- getLastSyncTimeText: jest.fn().mockReturnValue( 'You have not pulled this site yet.' ),
- isSiteIdPulling: jest.fn(),
- isSiteIdPushing: jest.fn(),
- clearTimeout: jest.fn(),
} );
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
expect( pullButton ).toBeInTheDocument();
fireEvent.click( pullButton );
@@ -766,8 +679,8 @@ describe( 'ContentTabSync', () => {
const uploadsCheckbox = screen.getByRole( 'checkbox', { name: 'uploads' } );
fireEvent.click( uploadsCheckbox );
- const dialogPullButton = screen.getAllByRole( 'button', { name: /Pull/i } );
- fireEvent.click( dialogPullButton[ 1 ] );
+ const dialogPullButton = await screen.findByRole( 'button', { name: 'Pull' } );
+ fireEvent.click( dialogPullButton );
expect( mockPullSite ).toHaveBeenCalledWith( fakeSyncSite, selectedSite, {
optionsToSync: [ 'paths', 'sqls' ],
@@ -777,45 +690,39 @@ describe( 'ContentTabSync', () => {
it( 'disables the pull button when all checkboxes are unchecked, which is the initial state', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
fireEvent.click( pullButton );
await screen.findByText( 'Pull from Production' );
- const dialogPullButton = screen.getAllByRole( 'button', { name: /Pull/i } )[ 1 ];
+ const dialogPullButton = await screen.findByRole( 'button', { name: 'Pull' } );
expect( dialogPullButton ).toBeDisabled();
} );
it( 'disables the push button when all checkboxes are unchecked, which is the initial state', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- const pushButton = screen.getByRole( 'button', { name: /Push/i } );
+ const pushButton = await screen.findByRole( 'button', { name: 'Push' } );
fireEvent.click( pushButton );
await screen.findByText( 'Push to Production' );
- const dialogPushButton = screen.getAllByRole( 'button', { name: /Push/i } )[ 1 ];
+ const dialogPushButton = await screen.findByRole( 'button', { name: 'Push' } );
expect( dialogPushButton ).toBeDisabled();
} );
it( 'enables the pull button when at least one checkbox is checked', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
fireEvent.click( pullButton );
await screen.findByText( 'Pull from Production' );
@@ -824,19 +731,17 @@ describe( 'ContentTabSync', () => {
const databaseCheckbox = screen.getByRole( 'checkbox', { name: 'Database' } );
fireEvent.click( databaseCheckbox );
- const dialogPullButton = screen.getAllByRole( 'button', { name: /Pull/i } )[ 1 ];
+ const dialogPullButton = await screen.findByRole( 'button', { name: 'Pull' } );
expect( dialogPullButton ).toBeEnabled();
} );
it( 'enables the pull button when at least one checkbox children is checked', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
fireEvent.click( pullButton );
await screen.findByText( 'Pull from Production' );
@@ -852,53 +757,48 @@ describe( 'ContentTabSync', () => {
expect( databaseCheckbox ).not.toBeChecked();
expect( filesAndFoldersCheckbox ).not.toBeChecked();
- const dialogPullButton = screen.getAllByRole( 'button', { name: /Pull/i } )[ 1 ];
+ const dialogPullButton = await screen.findByRole( 'button', { name: 'Pull' } );
expect( dialogPullButton ).toBeEnabled();
} );
+
it( 'disables the push button when all checkboxes are unchecked', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- const pushButton = screen.getByRole( 'button', { name: /Push/i } );
+ const pushButton = await screen.findByRole( 'button', { name: 'Push' } );
fireEvent.click( pushButton );
await screen.findByText( 'Push to Production' );
- const dialogPushButton = screen.getAllByRole( 'button', { name: /Push/i } )[ 1 ];
+ const dialogPushButton = await screen.findByRole( 'button', { name: 'Push' } );
expect( dialogPushButton ).toBeDisabled();
} );
it( 'enables the push button when at least one checkbox is checked', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- const pushButton = screen.getByRole( 'button', { name: /Push/i } );
+ const pushButton = await screen.findByRole( 'button', { name: 'Push' } );
fireEvent.click( pushButton );
await screen.findByText( 'Push to Production' );
const databaseCheckbox = screen.getByRole( 'checkbox', { name: 'Database' } );
fireEvent.click( databaseCheckbox );
- const dialogPushButton = screen.getAllByRole( 'button', { name: /Push/i } )[ 1 ];
+ const dialogPushButton = await screen.findByRole( 'button', { name: 'Push' } );
expect( dialogPushButton ).toBeEnabled();
} );
it( 'enables the push button when at least one checkbox children is checked', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
renderWithProvider( );
- const pushButton = screen.getByRole( 'button', { name: /Push/i } );
+ const pushButton = await screen.findByRole( 'button', { name: 'Push' } );
fireEvent.click( pushButton );
await screen.findByText( 'Push to Production' );
@@ -915,18 +815,14 @@ describe( 'ContentTabSync', () => {
expect( databaseCheckbox ).not.toBeChecked();
expect( filesAndFoldersCheckbox ).not.toBeChecked();
- const dialogPushButton = screen.getAllByRole( 'button', { name: /Push/i } )[ 1 ];
+ const dialogPushButton = await screen.findByRole( 'button', { name: 'Push' } );
expect( dialogPushButton ).toBeEnabled();
} );
describe( 'Sync Dialog Push Selection Over Limit Notice', () => {
it( 'shows warning notice when push selection exceeds limit', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- loading: false,
- localSiteId: 'site-id',
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
( useSelectedItemsPushSize as jest.Mock ).mockReturnValue( {
isPushSelectionOverLimit: true,
isLoading: false,
@@ -934,7 +830,7 @@ describe( 'ContentTabSync', () => {
renderWithProvider( );
- const pushButton = screen.getByRole( 'button', { name: /Push/i } );
+ const pushButton = await screen.findByRole( 'button', { name: 'Push' } );
fireEvent.click( pushButton );
await screen.findByText( 'Push to Production' );
@@ -942,17 +838,13 @@ describe( 'ContentTabSync', () => {
const warningNotice = screen.getByTestId( 'push-selection-over-limit-notice' );
expect( warningNotice ).toBeInTheDocument();
- const dialogPushButton = screen.getAllByRole( 'button', { name: /Push/i } )[ 1 ];
+ const dialogPushButton = await screen.findByRole( 'button', { name: 'Push' } );
expect( dialogPushButton ).toBeDisabled();
} );
it( 'does not show warning notice when push selection is within limit', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- loading: false,
- localSiteId: 'site-id',
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
( useSelectedItemsPushSize as jest.Mock ).mockReturnValue( {
isPushSelectionOverLimit: false,
isLoading: false,
@@ -960,7 +852,7 @@ describe( 'ContentTabSync', () => {
renderWithProvider( );
- const pushButton = screen.getByRole( 'button', { name: /Push/i } );
+ const pushButton = await screen.findByRole( 'button', { name: 'Push' } );
fireEvent.click( pushButton );
await screen.findByText( 'Push to Production' );
@@ -971,11 +863,7 @@ describe( 'ContentTabSync', () => {
it( 'does not show warning notice for pull operations even when limit exceeded', async () => {
( useAuth as jest.Mock ).mockReturnValue( createAuthMock( true ) );
- ( useConnectedSitesData as jest.Mock ).mockReturnValue( {
- connectedSites: [ fakeSyncSite ],
- loading: false,
- localSiteId: 'site-id',
- } );
+ setupConnectedSitesMocks( [ fakeSyncSite ], [ fakeSyncSite ] );
( useSelectedItemsPushSize as jest.Mock ).mockReturnValue( {
isPushSelectionOverLimit: true,
isLoading: false,
@@ -983,7 +871,7 @@ describe( 'ContentTabSync', () => {
renderWithProvider( );
- const pullButton = screen.getByRole( 'button', { name: /Pull/i } );
+ const pullButton = await screen.findByRole( 'button', { name: 'Pull' } );
fireEvent.click( pullButton );
await screen.findByText( 'Pull from Production' );
diff --git a/src/stores/index.ts b/src/stores/index.ts
index a3b9140a5e..790281fda8 100644
--- a/src/stores/index.ts
+++ b/src/stores/index.ts
@@ -24,10 +24,7 @@ import {
snapshotActions,
} from 'src/stores/snapshot-slice';
import { syncReducer } from 'src/stores/sync';
-import {
- connectedSitesReducer,
- loadAllConnectedSites,
-} from 'src/stores/sync/connected-sites-slice';
+import { connectedSitesApi } from 'src/stores/sync/connected-sites-api';
import { wpcomApi, wpcomPublicApi } from 'src/stores/wpcom-api';
import { wordpressVersionsApi } from './wordpress-versions-api';
import type { SupportedLocale } from 'common/lib/locale';
@@ -41,7 +38,7 @@ export type RootState = {
providerConstants: ReturnType< typeof providerConstantsReducer >;
snapshot: ReturnType< typeof snapshotReducer >;
sync: ReturnType< typeof syncReducer >;
- connectedSites: ReturnType< typeof connectedSitesReducer >;
+ connectedSitesApi: ReturnType< typeof connectedSitesApi.reducer >;
wordpressVersionsApi: ReturnType< typeof wordpressVersionsApi.reducer >;
wpcomApi: ReturnType< typeof wpcomApi.reducer >;
wpcomPublicApi: ReturnType< typeof wpcomPublicApi.reducer >;
@@ -93,11 +90,11 @@ export const rootReducer = combineReducers( {
chat: chatReducer,
newSites: newSitesReducer,
installedAppsApi: installedAppsApi.reducer,
+ connectedSitesApi: connectedSitesApi.reducer,
onboarding: onboardingReducer,
providerConstants: providerConstantsReducer,
snapshot: snapshotReducer,
sync: syncReducer,
- connectedSites: connectedSitesReducer,
wordpressVersionsApi: wordpressVersionsApi.reducer,
wpcomApi: wpcomApi.reducer,
wpcomPublicApi: wpcomPublicApi.reducer,
@@ -112,6 +109,7 @@ export const store = configureStore( {
.prepend( listenerMiddleware.middleware )
.concat( appVersionApi.middleware )
.concat( installedAppsApi.middleware )
+ .concat( connectedSitesApi.middleware )
.concat( wordpressVersionsApi.middleware )
.concat( wpcomApi.middleware )
.concat( wpcomPublicApi.middleware )
@@ -140,8 +138,6 @@ async function initializeProviderConstants() {
// Initialize provider constants immediately, but skip in test environment
if ( typeof jest === 'undefined' && process.env.NODE_ENV !== 'test' ) {
void initializeProviderConstants();
- // Initialize connected sites on store initialization only in non-test environment
- void store.dispatch( loadAllConnectedSites() );
}
export type AppDispatch = typeof store.dispatch;
diff --git a/src/stores/sync/connected-sites-api.ts b/src/stores/sync/connected-sites-api.ts
new file mode 100644
index 0000000000..906678bff9
--- /dev/null
+++ b/src/stores/sync/connected-sites-api.ts
@@ -0,0 +1,98 @@
+import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
+import { getIpcApi } from 'src/lib/get-ipc-api';
+import type { SyncSite } from 'src/hooks/use-fetch-wpcom-sites/types';
+
+export const connectedSitesApi = createApi( {
+ reducerPath: 'connectedSitesApi',
+ baseQuery: fetchBaseQuery(),
+ tagTypes: [ 'ConnectedSites' ],
+ endpoints: ( builder ) => ( {
+ getConnectedSitesForLocalSite: builder.query<
+ SyncSite[],
+ { localSiteId?: string; userId?: number }
+ >( {
+ queryFn: async ( { localSiteId } ) => {
+ if ( ! localSiteId ) {
+ return { data: [] };
+ }
+
+ const sites = await getIpcApi().getConnectedWpcomSites( localSiteId );
+ return { data: sites };
+ },
+ providesTags: ( result, error, arg ) => [
+ { type: 'ConnectedSites', localSiteId: arg.localSiteId, userId: arg.userId },
+ ],
+ } ),
+
+ connectSite: builder.mutation< SyncSite[], { site: SyncSite; localSiteId: string } >( {
+ queryFn: async ( { site, localSiteId } ) => {
+ await getIpcApi().connectWpcomSites( [
+ {
+ sites: [ site ],
+ localSiteId,
+ },
+ ] );
+
+ const actualConnectedSites = await getIpcApi().getConnectedWpcomSites( localSiteId );
+
+ return { data: actualConnectedSites };
+ },
+ invalidatesTags: ( result, error, { localSiteId } ) => [
+ { type: 'ConnectedSites', localSiteId },
+ ],
+ } ),
+
+ disconnectSite: builder.mutation< SyncSite[], { siteId: number; localSiteId: string } >( {
+ queryFn: async ( { siteId, localSiteId } ) => {
+ await getIpcApi().disconnectWpcomSites( [
+ {
+ siteIds: [ siteId ],
+ localSiteId,
+ },
+ ] );
+
+ const actualConnectedSites = await getIpcApi().getConnectedWpcomSites( localSiteId );
+
+ return { data: actualConnectedSites };
+ },
+ invalidatesTags: ( result, error, { localSiteId } ) => [
+ { type: 'ConnectedSites', localSiteId },
+ ],
+ } ),
+
+ updateSiteTimestamp: builder.mutation<
+ void,
+ { siteId: number; localSiteId: string; type: 'pull' | 'push' }
+ >( {
+ queryFn: async ( { siteId, localSiteId, type } ) => {
+ const sites = await getIpcApi().getConnectedWpcomSites( localSiteId );
+ const site = sites.find(
+ ( { id, localSiteId: siteLocalId } ) => siteId === id && localSiteId === siteLocalId
+ );
+
+ if ( ! site ) {
+ return { error: { status: 'CUSTOM_ERROR', error: 'Site not found' } };
+ }
+
+ const updatedSite = {
+ ...site,
+ [ type === 'pull' ? 'lastPullTimestamp' : 'lastPushTimestamp' ]: new Date().toISOString(),
+ };
+
+ await getIpcApi().updateSingleConnectedWpcomSite( updatedSite );
+
+ return { data: undefined };
+ },
+ invalidatesTags: ( result, error, { localSiteId } ) => [
+ { type: 'ConnectedSites', localSiteId },
+ ],
+ } ),
+ } ),
+} );
+
+export const {
+ useGetConnectedSitesForLocalSiteQuery,
+ useConnectSiteMutation,
+ useDisconnectSiteMutation,
+ useUpdateSiteTimestampMutation,
+} = connectedSitesApi;
diff --git a/src/stores/sync/connected-sites-hooks.ts b/src/stores/sync/connected-sites-hooks.ts
deleted file mode 100644
index f91f64ad98..0000000000
--- a/src/stores/sync/connected-sites-hooks.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { useCallback } from 'react';
-import { useFetchWpComSites } from 'src/hooks/use-fetch-wpcom-sites';
-import { useSiteDetails } from 'src/hooks/use-site-details';
-import { useAppDispatch, useRootSelector } from 'src/stores';
-import {
- connectedSitesActions,
- connectedSitesSelectors,
- connectSite,
- disconnectSite,
- loadAllConnectedSites,
-} from 'src/stores/sync/connected-sites-slice';
-import type { SyncSite } from 'src/hooks/use-fetch-wpcom-sites/types';
-
-export const useConnectedSitesData = () => {
- const { selectedSite } = useSiteDetails();
- const localSiteId = selectedSite?.id;
- const connectedSites = useRootSelector( ( state ) =>
- connectedSitesSelectors.selectSitesByLocalSiteId( state, localSiteId )
- );
-
- return { connectedSites, localSiteId };
-};
-
-export const useSyncSitesData = () => {
- const { connectedSites } = useConnectedSitesData();
- const { syncSites, isFetching, refetchSites } = useFetchWpComSites(
- connectedSites.map( ( { id } ) => id )
- );
-
- return { syncSites, isFetching, refetchSites };
-};
-
-export const useConnectedSitesOperations = () => {
- const dispatch = useAppDispatch();
- const { localSiteId, connectedSites } = useConnectedSitesData();
-
- const connectSiteToLocal = useCallback(
- async ( site: SyncSite, overrideLocalSiteId?: string ) => {
- const targetLocalSiteId = overrideLocalSiteId || localSiteId;
-
- if ( ! targetLocalSiteId ) {
- throw new Error( 'No local site ID available' );
- }
-
- try {
- await dispatch(
- connectSite( {
- site,
- localSiteId: targetLocalSiteId,
- } )
- ).unwrap();
-
- dispatch( connectedSitesActions.closeModal() );
-
- if ( overrideLocalSiteId && overrideLocalSiteId !== localSiteId ) {
- await dispatch( loadAllConnectedSites() );
- }
- } catch ( error ) {
- console.error( 'Failed to connect site:', error );
- throw error;
- }
- },
- [ dispatch, localSiteId ]
- );
-
- const disconnectSiteFromLocal = useCallback(
- async ( siteId: number ) => {
- if ( ! localSiteId ) {
- throw new Error( 'No local site ID available' );
- }
-
- try {
- const siteToDisconnect = connectedSites.find( ( site ) => site.id === siteId );
-
- if ( ! siteToDisconnect ) {
- throw new Error( 'Site not found' );
- }
-
- await dispatch( disconnectSite( { siteId, localSiteId } ) ).unwrap();
- } catch ( error ) {
- console.error( 'Failed to disconnect site:', error );
- throw error;
- }
- },
- [ dispatch, localSiteId, connectedSites ]
- );
-
- return {
- connectSite: connectSiteToLocal,
- disconnectSite: disconnectSiteFromLocal,
- };
-};
diff --git a/src/stores/sync/connected-sites-slice.ts b/src/stores/sync/connected-sites-slice.ts
deleted file mode 100644
index 2e78853e10..0000000000
--- a/src/stores/sync/connected-sites-slice.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-import { createAsyncThunk, createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
-import fastDeepEqual from 'fast-deep-equal';
-import { getIpcApi } from 'src/lib/get-ipc-api';
-import { RootState, store } from 'src/stores';
-import type { SyncSite } from 'src/hooks/use-fetch-wpcom-sites/types';
-import type { SyncModalMode } from 'src/modules/sync/types';
-
-type ConnectedSites = SyncSite[];
-type ModalState = false | true | { disconnectSiteId?: number };
-
-interface ConnectedSitesState {
- sites: Record< string, ConnectedSites >; // Keyed by localSiteId for efficient lookups
- isModalOpen: ModalState;
- modalMode: SyncModalMode | null;
-}
-
-interface ConnectSiteParams {
- site: SyncSite;
- localSiteId: string;
-}
-
-interface DisconnectSiteParams {
- siteId: number;
- localSiteId: string;
-}
-
-const initialState: ConnectedSitesState = {
- sites: {},
- isModalOpen: false,
- modalMode: null,
-};
-
-export const loadAllConnectedSites = createAsyncThunk( 'connectedSites/loadAll', async () => {
- const allSites = await getIpcApi().getConnectedWpcomSites();
-
- const sitesByLocalSiteId: Record< string, ConnectedSites > = {};
- allSites.forEach( ( site ) => {
- if ( ! sitesByLocalSiteId[ site.localSiteId ] ) {
- sitesByLocalSiteId[ site.localSiteId ] = [];
- }
- sitesByLocalSiteId[ site.localSiteId ].push( site );
- } );
-
- return sitesByLocalSiteId;
-} );
-
-export const connectSite = createAsyncThunk(
- 'connectedSites/connect',
- async ( { site, localSiteId }: ConnectSiteParams ) => {
- await getIpcApi().connectWpcomSites( [
- {
- sites: [ site ],
- localSiteId,
- },
- ] );
-
- const actualConnectedSites = await getIpcApi().getConnectedWpcomSites( localSiteId );
-
- return {
- localSiteId,
- connectedSites: actualConnectedSites,
- };
- }
-);
-
-export const disconnectSite = createAsyncThunk(
- 'connectedSites/disconnect',
- async ( { siteId, localSiteId }: DisconnectSiteParams ) => {
- await getIpcApi().disconnectWpcomSites( [
- {
- siteIds: [ siteId ],
- localSiteId,
- },
- ] );
-
- const actualConnectedSites = await getIpcApi().getConnectedWpcomSites( localSiteId );
-
- return {
- localSiteId,
- connectedSites: actualConnectedSites,
- };
- }
-);
-
-const connectedSitesSlice = createSlice( {
- name: 'connectedSites',
- initialState,
- reducers: {
- updateSite: ( state, action: PayloadAction< { localSiteId: string; site: SyncSite } > ) => {
- const { localSiteId, site } = action.payload;
- const sites = state.sites[ localSiteId ] || [];
- const index = sites.findIndex( ( s ) => s.id === site.id );
-
- if ( index !== -1 ) {
- sites[ index ] = site;
- }
- },
-
- clearSitesForLocalSite: ( state, action: PayloadAction< string > ) => {
- delete state.sites[ action.payload ];
- },
-
- openModal: ( state, action: PayloadAction< SyncModalMode | undefined > ) => {
- state.isModalOpen = true;
- if ( action.payload ) {
- state.modalMode = action.payload;
- }
- },
-
- setModalMode: ( state, action: PayloadAction< SyncModalMode | null > ) => {
- state.modalMode = action.payload;
- },
-
- closeModal: ( state ) => {
- state.isModalOpen = false;
- },
- },
- extraReducers: ( builder ) => {
- builder
- .addCase( loadAllConnectedSites.fulfilled, ( state, action ) => {
- state.sites = action.payload;
- } )
- .addCase( connectSite.fulfilled, ( state, action ) => {
- const { localSiteId, connectedSites } = action.payload;
- state.sites[ localSiteId ] = connectedSites;
- } )
- .addCase( disconnectSite.fulfilled, ( state, action ) => {
- const { localSiteId, connectedSites } = action.payload;
- state.sites[ localSiteId ] = connectedSites;
- } );
- },
-} );
-
-export const connectedSitesActions = connectedSitesSlice.actions;
-export const connectedSitesReducer = connectedSitesSlice.reducer;
-
-export const connectedSitesSelectors = {
- selectIsModalOpen: ( state: RootState ) => state.connectedSites.isModalOpen,
- selectModalMode: ( state: RootState ) => state.connectedSites.modalMode,
- selectSitesByLocalSiteId: createSelector(
- [
- ( state: RootState ) => state.connectedSites,
- ( _: RootState, localSiteId: string | undefined ) => localSiteId,
- ],
- ( connectedSitesState, localSiteId ) =>
- localSiteId ? connectedSitesState.sites[ localSiteId ] || [] : []
- ),
-};
-
-window.ipcListener.subscribe( 'user-data-updated', async ( _, userData ) => {
- const state = store.getState();
- const currentUserId = userData.authToken?.id;
-
- if ( ! currentUserId ) {
- return;
- }
-
- const connectedSitesFromUserData = userData.connectedWpcomSites?.[ currentUserId ] || [];
- const connectedSitesFromState = Object.values( state.connectedSites.sites ).flat();
-
- if ( ! fastDeepEqual( connectedSitesFromUserData, connectedSitesFromState ) ) {
- void store.dispatch( loadAllConnectedSites() );
- }
-} );
diff --git a/src/stores/sync/index.ts b/src/stores/sync/index.ts
index c462f4f349..1b36091b56 100644
--- a/src/stores/sync/index.ts
+++ b/src/stores/sync/index.ts
@@ -1,17 +1,4 @@
export { syncReducer, syncActions, syncSelectors } from './sync-slice';
export { useLatestRewindId, useRemoteFileTree, useLocalFileTree } from './sync-hooks';
export { useGetLatestRewindIdQuery, fetchRemoteFileTree } from './sync-api';
-export {
- connectedSitesReducer,
- connectedSitesActions,
- connectedSitesSelectors,
- loadAllConnectedSites,
- connectSite,
- disconnectSite,
-} from './connected-sites-slice';
-export {
- useConnectedSitesData,
- useSyncSitesData,
- useConnectedSitesOperations,
-} from './connected-sites-hooks';
export * from './sync-types';