Skip to content
Draft
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
1 change: 0 additions & 1 deletion electron.vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { resolve } from 'path';
import { pathToFileURL } from 'url';
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
import react from '@vitejs/plugin-react';
import topLevelAwait from 'vite-plugin-top-level-await';
Expand Down
9 changes: 7 additions & 2 deletions src/components/content-tab-import-export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ProgressBar from 'src/components/progress-bar';
import { Tooltip } from 'src/components/tooltip';
import { ACCEPTED_IMPORT_FILE_TYPES } from 'src/constants';
import { useSyncSites } from 'src/hooks/sync-sites/sync-sites-context';
import { useAuth } from 'src/hooks/use-auth';
import { useConfirmationDialog } from 'src/hooks/use-confirmation-dialog';
import { useDragAndDropFile } from 'src/hooks/use-drag-and-drop-file';
import { useImportExport } from 'src/hooks/use-import-export';
Expand All @@ -19,7 +20,7 @@ import { cx } from 'src/lib/cx';
import { getIpcApi } from 'src/lib/get-ipc-api';
import { getLocalizedLink } from 'src/lib/get-localized-link';
import { useI18nLocale } from 'src/stores';
import { useConnectedSitesData } from 'src/stores/sync';
import { useGetConnectedSitesForLocalSiteQuery } from 'src/stores/sync/connected-sites-api';

interface ContentTabImportExportProps {
selectedSite: SiteDetails;
Expand Down Expand Up @@ -320,7 +321,11 @@ export function ContentTabImportExport( { selectedSite }: ContentTabImportExport
const { __ } = useI18n();
const [ isSupported, setIsSupported ] = useState< boolean | null >( null );
const { isSiteIdPulling, isSiteIdPushing } = useSyncSites();
const { connectedSites } = useConnectedSitesData();
const { user } = useAuth();
const { data: connectedSites = [] } = useGetConnectedSitesForLocalSiteQuery( {
localSiteId: selectedSite.id,
userId: user?.id,
} );
const isPulling = connectedSites.some( ( site ) => isSiteIdPulling( selectedSite.id, site.id ) );
const isPushing = connectedSites.some( ( site ) => isSiteIdPushing( selectedSite.id, site.id ) );
const isThisSiteSyncing = isPulling || isPushing;
Expand Down
62 changes: 19 additions & 43 deletions src/hooks/sync-sites/sync-sites-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@ import {
mapImportResponseToPushState,
} from 'src/hooks/sync-sites/use-sync-push';
import { useAuth } from 'src/hooks/use-auth';
import { useFetchWpComSites } from 'src/hooks/use-fetch-wpcom-sites';
import { useFormatLocalizedTimestamps } from 'src/hooks/use-format-localized-timestamps';
import { useSiteDetails } from 'src/hooks/use-site-details';
import { useSyncStatesProgressInfo } from 'src/hooks/use-sync-states-progress-info';
import { getIpcApi } from 'src/lib/get-ipc-api';
import { useAppDispatch } from 'src/stores';
import { useConnectedSitesData, useSyncSitesData, connectedSitesActions } from 'src/stores/sync';
import {
useGetConnectedSitesForLocalSiteQuery,
useUpdateSiteTimestampMutation,
} from 'src/stores/sync/connected-sites-api';
import type { ImportResponse } from 'src/hooks/use-sync-states-progress-info';

type GetLastSyncTimeText = ( timestamp: string | null, type: 'pull' | 'push' ) => string;
type UpdateSiteTimestamp = (
siteId: number | undefined,
localSiteId: string,
type: 'pull' | 'push'
) => Promise< void >;

export type SyncSitesContextType = Omit< UseSyncPull, 'pullStates' > &
Omit< UseSyncPush, 'pushStates' > &
ReturnType< typeof useSyncSitesData > & {
ReturnType< typeof useFetchWpComSites > & {
getLastSyncTimeText: GetLastSyncTimeText;
};

Expand Down Expand Up @@ -54,46 +53,21 @@ export function SyncSitesProvider( { children }: { children: React.ReactNode } )
[ formatRelativeTime ]
);

const { connectedSites } = useConnectedSitesData();
const dispatch = useAppDispatch();
const { selectedSite } = useSiteDetails();
const { user } = useAuth();
const { data: connectedSites = [] } = useGetConnectedSitesForLocalSiteQuery( {
localSiteId: selectedSite?.id,
userId: user?.id,
} );

const updateSiteTimestamp = useCallback< UpdateSiteTimestamp >(
async ( siteId, localSiteIdParam, type ) => {
const site = connectedSites.find(
( { id, localSiteId: siteLocalId } ) => siteId === id && localSiteIdParam === siteLocalId
);

if ( ! site ) {
return;
}

try {
const updatedSite = {
...site,
[ type === 'pull' ? 'lastPullTimestamp' : 'lastPushTimestamp' ]: new Date().toISOString(),
};

await getIpcApi().updateSingleConnectedWpcomSite( updatedSite );

dispatch(
connectedSitesActions.updateSite( {
localSiteId: localSiteIdParam,
site: updatedSite,
} )
);
} catch ( error ) {
console.error( 'Failed to update timestamp:', error );
}
},
[ connectedSites, dispatch ]
);
const [ updateSiteTimestamp ] = useUpdateSiteTimestampMutation();

const { pullSite, isAnySitePulling, isSiteIdPulling, clearPullState, getPullState, cancelPull } =
useSyncPull( {
pullStates,
setPullStates,
onPullSuccess: ( remoteSiteId, localSiteId ) =>
updateSiteTimestamp( remoteSiteId, localSiteId, 'pull' ),
updateSiteTimestamp( { siteId: remoteSiteId, localSiteId, type: 'pull' } ),
} );

const [ pushStates, setPushStates ] = useState< PushStates >( {} );
Expand All @@ -102,10 +76,12 @@ export function SyncSitesProvider( { children }: { children: React.ReactNode } )
pushStates,
setPushStates,
onPushSuccess: ( remoteSiteId, localSiteId ) =>
updateSiteTimestamp( remoteSiteId, localSiteId, 'push' ),
updateSiteTimestamp( { siteId: remoteSiteId, localSiteId, type: 'push' } ),
} );

const { syncSites, isFetching, refetchSites } = useSyncSitesData();
const { syncSites, isFetching, refetchSites } = useFetchWpComSites(
connectedSites.map( ( { id } ) => id )
);
useListenDeepLinkConnection( { refetchSites } );

const { client } = useAuth();
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/sync-sites/use-listen-deep-link-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { SyncSitesContextType } from 'src/hooks/sync-sites/sync-sites-context';
import { useContentTabs } from 'src/hooks/use-content-tabs';
import { useIpcListener } from 'src/hooks/use-ipc-listener';
import { useSiteDetails } from 'src/hooks/use-site-details';
import { useConnectedSitesOperations } from 'src/stores/sync';
import { useConnectSiteMutation } from 'src/stores/sync/connected-sites-api';

export function useListenDeepLinkConnection( {
refetchSites,
}: {
refetchSites: SyncSitesContextType[ 'refetchSites' ];
} ) {
const { connectSite } = useConnectedSitesOperations();
const [ connectSite ] = useConnectSiteMutation();
const { selectedSite, setSelectedSiteId } = useSiteDetails();
const { setSelectedTab, selectedTab } = useContentTabs();

Expand All @@ -22,7 +22,7 @@ export function useListenDeepLinkConnection( {
// Select studio site that started the sync
setSelectedSiteId( studioSiteId );
}
await connectSite( newConnectedSite, studioSiteId );
await connectSite( { site: newConnectedSite, localSiteId: studioSiteId } );
if ( selectedTab !== 'sync' ) {
// Switch to sync tab
setSelectedTab( 'sync' );
Expand Down
60 changes: 22 additions & 38 deletions src/hooks/tests/use-add-site.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Run tests: yarn test -- src/hooks/tests/use-add-site.test.tsx
import { configureStore } from '@reduxjs/toolkit';
import { renderHook, act } from '@testing-library/react';
import nock from 'nock';
import { Provider } from 'react-redux';
Expand All @@ -8,8 +7,8 @@ import { useAddSite } from 'src/hooks/use-add-site';
import { useContentTabs } from 'src/hooks/use-content-tabs';
import { useSiteDetails } from 'src/hooks/use-site-details';
import { getWordPressProvider } from 'src/lib/wordpress-provider';
import providerConstantsReducer from 'src/stores/provider-constants-slice';
import { useConnectedSitesOperations } from 'src/stores/sync';
import { store } from 'src/stores';
import { setProviderConstants } from 'src/stores/provider-constants-slice';
import type { SyncSite } from 'src/hooks/use-fetch-wpcom-sites/types';

jest.mock( 'src/hooks/use-site-details' );
Expand All @@ -23,10 +22,7 @@ jest.mock( 'src/hooks/use-import-export', () => ( {
} ),
} ) );

jest.mock( 'src/stores/sync', () => ( {
useConnectedSitesOperations: jest.fn(),
} ) );

const mockConnectWpcomSites = jest.fn().mockResolvedValue( undefined );
jest.mock( 'src/lib/get-ipc-api', () => ( {
getIpcApi: () => ( {
generateProposedSitePath: jest.fn().mockResolvedValue( {
Expand All @@ -37,32 +33,12 @@ jest.mock( 'src/lib/get-ipc-api', () => ( {
} ),
showNotification: jest.fn(),
getAllCustomDomains: jest.fn().mockResolvedValue( [] ),
connectWpcomSites: mockConnectWpcomSites,
getConnectedWpcomSites: jest.fn().mockResolvedValue( [] ),
} ),
} ) );

// Helper to create a store with preloaded provider constants
function makeStoreWithProviderConstants( overrides = {} ) {
return configureStore( {
reducer: {
providerConstants: providerConstantsReducer,
// ...add other reducers as needed
},
preloadedState: {
providerConstants: {
defaultPhpVersion: '8.3',
defaultWordPressVersion: 'latest',
allowedPhpVersions: [ '8.0', '8.1', '8.2', '8.3' ],
minimumWordPressVersion: '5.9.9',
...overrides,
},
},
} );
}

const renderHookWithProvider = (
hook: () => ReturnType< typeof useAddSite >,
store = makeStoreWithProviderConstants()
) => {
const renderHookWithProvider = ( hook: () => ReturnType< typeof useAddSite > ) => {
return renderHook< ReturnType< typeof useAddSite >, void >( hook, {
wrapper: ( { children } ) => <Provider store={ store }>{ children }</Provider>,
} );
Expand All @@ -72,13 +48,22 @@ describe( 'useAddSite', () => {
const mockCreateSite = jest.fn();
const mockUpdateSite = jest.fn();
const mockStartServer = jest.fn();
const mockConnectSite = jest.fn();
const mockPullSite = jest.fn();
const mockSetSelectedTab = jest.fn();

beforeEach( () => {
jest.clearAllMocks();

// Prepopulate store with provider constants
store.dispatch(
setProviderConstants( {
defaultPhpVersion: '8.3',
defaultWordPressVersion: 'latest',
allowedPhpVersions: [ '8.0', '8.1', '8.2', '8.3' ],
minimumWordPressVersion: '5.9.9',
} )
);

( useSiteDetails as jest.Mock ).mockReturnValue( {
createSite: mockCreateSite,
updateSite: mockUpdateSite,
Expand All @@ -87,12 +72,6 @@ describe( 'useAddSite', () => {
startServer: mockStartServer,
} );

mockConnectSite.mockResolvedValue( undefined );

( useConnectedSitesOperations as jest.Mock ).mockReturnValue( {
connectSite: mockConnectSite,
} );

mockPullSite.mockReset();
( useSyncSites as jest.Mock ).mockReturnValue( {
pullSite: mockPullSite,
Expand Down Expand Up @@ -296,7 +275,12 @@ describe( 'useAddSite', () => {
await result.current.handleAddSiteClick();
} );

expect( mockConnectSite ).toHaveBeenCalledWith( remoteSite, createdSite.id );
expect( mockConnectWpcomSites ).toHaveBeenCalledWith( [
{
sites: [ remoteSite ],
localSiteId: createdSite.id,
},
] );
expect( mockPullSite ).toHaveBeenCalledWith( remoteSite, createdSite, {
optionsToSync: [ 'all' ],
} );
Expand Down
Loading
Loading