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
24 changes: 24 additions & 0 deletions src/hooks/sync-sites/use-sync-push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type SyncPushState = {
status: PushStateProgressInfo;
selectedSite: SiteDetails;
remoteSiteUrl: string;
uploadProgress?: number;
};

type PushSiteOptions = {
Expand Down Expand Up @@ -99,11 +100,13 @@ export function useSyncPush( {
const {
pushStatesProgressInfo,
isKeyPushing,
isKeyUploading,
isKeyImporting,
isKeyFinished,
isKeyFailed,
isKeyCancelled,
getPushStatusWithProgress,
mapUploadProgressToOverallProgress,
} = useSyncStatesProgressInfo();

const updatePushState = useCallback< UpdateState< SyncPushState > >(
Expand Down Expand Up @@ -324,6 +327,7 @@ export function useSyncPush( {
if ( response.success ) {
updatePushState( selectedSite.id, remoteSiteId, {
status: pushStatesProgressInfo.creatingRemoteBackup,
uploadProgress: undefined, // Clear upload progress when transitioning to next state
} );
} else {
throw response;
Expand Down Expand Up @@ -392,12 +396,32 @@ export function useSyncPush( {
useIpcListener(
'sync-upload-resumed',
( _event, payload: { selectedSiteId: string; remoteSiteId: number } ) => {
const currentState = getPushState( payload.selectedSiteId, payload.remoteSiteId );
updatePushState( payload.selectedSiteId, payload.remoteSiteId, {
status: pushStatesProgressInfo.uploading,
uploadProgress: currentState?.uploadProgress,
} );
}
);

useIpcListener(
'sync-upload-progress',
( _event, payload: { selectedSiteId: string; remoteSiteId: number; progress: number } ) => {
const currentState = getPushState( payload.selectedSiteId, payload.remoteSiteId );
if ( currentState && isKeyUploading( currentState.status.key ) ) {
const mappedProgress = mapUploadProgressToOverallProgress( payload.progress );

updatePushState( payload.selectedSiteId, payload.remoteSiteId, {
status: {
...currentState.status,
progress: mappedProgress,
},
uploadProgress: payload.progress,
} );
}
}
);

const isAnySitePushing = useMemo< boolean >( () => {
return Object.values( pushStates ).some( ( state ) => isKeyPushing( state.status.key ) );
}, [ pushStates, isKeyPushing ] );
Expand Down
52 changes: 51 additions & 1 deletion src/hooks/use-sync-states-progress-info.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { sprintf } from '@wordpress/i18n';
import { useI18n } from '@wordpress/react-i18n';
import { useCallback, useMemo } from 'react';
import { ImportProgressState } from './use-import-export';
Expand Down Expand Up @@ -104,7 +105,7 @@ export function useSyncStatesProgressInfo() {
uploading: {
key: 'uploading',
progress: 40,
message: __( 'Uploading Studio site…' ),
message: __( 'Uploading site…' ),
},
uploadingPaused: {
key: 'uploadingPaused',
Expand Down Expand Up @@ -174,6 +175,10 @@ export function useSyncStatesProgressInfo() {
return key === 'uploadingPaused';
};

const isKeyUploading = useCallback( ( key: PushStateProgressInfo[ 'key' ] | undefined ) => {
return key === 'uploading';
}, [] );

const isKeyImporting = ( key: PushStateProgressInfo[ 'key' ] | undefined ) => {
const pushingStateKeys: PushStateProgressInfo[ 'key' ][] = [
'creatingRemoteBackup',
Expand Down Expand Up @@ -294,6 +299,47 @@ export function useSyncStatesProgressInfo() {
]
);

const getPushUploadPercentage = useCallback(
(
statusKey: PushStateProgressInfo[ 'key' ] | undefined,
uploadProgress: number | undefined
): number | null => {
if ( isKeyUploading( statusKey ) && uploadProgress !== undefined ) {
return Math.round( uploadProgress );
}
return null;
},
[ isKeyUploading ]
);

const getPushUploadMessage = useCallback(
( message: string, uploadPercentage: number | null ): string => {
if ( uploadPercentage !== null ) {
// translators: %d is the upload progress percentage
return sprintf( __( 'Uploading site (%d%%)…' ), uploadPercentage );
}
return message;
},
[ __ ]
);

const mapUploadProgressToOverallProgress = useCallback(
( uploadProgress: number ): number => {
// Map upload progress (0-100%) to the uploading state range (40-50%)
const uploadingProgressRange =
pushStatesProgressInfo.creatingRemoteBackup.progress -
pushStatesProgressInfo.uploading.progress;
return (
pushStatesProgressInfo.uploading.progress +
( uploadProgress / 100 ) * uploadingProgressRange
);
},
[
pushStatesProgressInfo.creatingRemoteBackup.progress,
pushStatesProgressInfo.uploading.progress,
]
);

return {
pullStatesProgressInfo,
pushStatesProgressInfo,
Expand All @@ -303,9 +349,13 @@ export function useSyncStatesProgressInfo() {
isKeyFinished,
isKeyFailed,
isKeyCancelled,
isKeyUploading,
getBackupStatusWithProgress,
getPullStatusWithProgress,
getPushStatusWithProgress,
getPushUploadPercentage,
getPushUploadMessage,
mapUploadProgressToOverallProgress,
isKeyUploadingPaused,
};
}
1 change: 1 addition & 0 deletions src/ipc-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface IpcEvents {
'site-context-menu-action': [ { action: string; siteId: string } ];
'sync-upload-paused': [ { error: string; selectedSiteId: string; remoteSiteId: number } ];
'sync-upload-resumed': [ { selectedSiteId: string; remoteSiteId: number } ];
'sync-upload-progress': [ { selectedSiteId: string; remoteSiteId: number; progress: number } ];
'snapshot-error': [ { operationId: crypto.UUID; data: SnapshotEventData } ];
'snapshot-fatal-error': [ { operationId: crypto.UUID; data: { message: string } } ];
'snapshot-output': [ { operationId: crypto.UUID; data: SnapshotEventData } ];
Expand Down
9 changes: 8 additions & 1 deletion src/modules/sync/components/sync-connected-sites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ const SyncConnectedSitesSectionItem = ( {
isKeyFailed,
isKeyCancelled,
getPullStatusWithProgress,
getPushUploadPercentage,
getPushUploadMessage,
isKeyUploadingPaused,
} = useSyncStatesProgressInfo();

Expand All @@ -217,6 +219,11 @@ const SyncConnectedSitesSectionItem = ( {
const hasPushFinished = pushState && isKeyFinished( pushState.status.key );
const hasPushCancelled = pushState && isKeyCancelled( pushState.status.key );

const uploadPercentage = getPushUploadPercentage(
pushState?.status.key,
pushState?.uploadProgress
);

return (
<div className="grid grid-cols-[max-content_1fr_max-content]">
<div
Expand Down Expand Up @@ -322,7 +329,7 @@ const SyncConnectedSitesSectionItem = ( {
<div className="flex flex-col gap-2 min-w-44 flex-shrink">
<div className="a8c-body-small flex items-center gap-0.5">
<Icon icon={ info } size={ 16 } />
{ pushState.status.message }
{ getPushUploadMessage( pushState.status.message, uploadPercentage ) }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if this is best practice but this is best I could come up with. Let me know if you have any better idea to implement this in other way.

</div>
<ProgressBar value={ pushState.status.progress } maxValue={ 100 } />
</div>
Expand Down
10 changes: 9 additions & 1 deletion src/modules/sync/lib/ipc-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export async function pushArchive(
console.error( '[TUS] Upload error', error );
reject( error );
},
onProgress: () => {
onProgress: ( bytesSent: number, bytesTotal: number ) => {
if ( isUploadingPaused ) {
isUploadingPaused = false;
void sendIpcEventToRenderer( 'sync-upload-resumed', {
Expand All @@ -207,6 +207,14 @@ export async function pushArchive(
if ( ! hasUploadStarted ) {
hasUploadStarted = true;
}

// Calculate upload progress percentage (0-100)
const uploadProgress = bytesTotal > 0 ? ( bytesSent / bytesTotal ) * 100 : 0;
void sendIpcEventToRenderer( 'sync-upload-progress', {
selectedSiteId: selectedSiteId,
remoteSiteId: remoteSiteId,
progress: uploadProgress,
} );
},
onSuccess: ( payload ) => {
if ( ! payload.lastResponse ) {
Expand Down