Skip to content

Conversation

@ActiveChooN
Copy link

📝 Description

Reworked status bar to support model training, model export, media upload and WebRTC status. Updated designs

Screen.Recording.2025-12-03.at.11.15.38.mov
  • Provide a clear summary of the changes and the issue that has been addressed.
  • 🛠️ Fixes # (issue number)

✨ Changes

Select what type of change your PR is:

  • 🚀 New feature (non-breaking change which adds functionality)
  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • 🔄 Refactor (non-breaking change which refactors the code base)
  • ⚡ Performance improvements
  • 🎨 Style changes (code style/formatting)
  • 🧪 Tests (adding/modifying tests)
  • 📚 Documentation update
  • 📦 Build system changes
  • 🚧 CI/CD configuration
  • 🔧 Chore (general maintenance)
  • 🔒 Security update
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)

✅ Checklist

Before you submit your pull request, please make sure you have completed the following steps:

  • 📚 I have made the necessary updates to the documentation (if applicable).
  • 🧪 I have written tests that support my changes and prove that my fix is effective or my feature works (if applicable).
  • 🏷️ My PR title follows conventional commit format.

For more information about code review checklists, see the Code Review Checklist.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR reworks the status bar to consolidate training, model export, media upload, and WebRTC connection status into a unified component with priority-based display and modern visual design.

Key Changes:

  • Replaces individual status components with a centralized StatusBar that displays the highest-priority active operation
  • Implements context-based state management for status tracking across features
  • Updates export and upload flows to use the new status bar instead of toast notifications

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
application/ui/src/providers.tsx Wraps app with StatusBarProvider for global status state
application/ui/src/features/inspect/models/export-model-dialog.component.tsx Integrates export operations with status bar, removes toast notifications and loading states from dialog
application/ui/src/features/inspect/footer/training-status-item.component.tsx Removes old training status component (replaced by adapter pattern)
application/ui/src/features/inspect/footer/status-bar/status-bar.module.scss Defines styles for unified status bar with progress indicator and connection status
application/ui/src/features/inspect/footer/status-bar/status-bar.interface.ts Defines TypeScript interfaces and priority system for status management
application/ui/src/features/inspect/footer/status-bar/status-bar.component.tsx Implements main status bar UI with connection indicator and active operation display
application/ui/src/features/inspect/footer/status-bar/status-bar-context.tsx Provides React context for managing multiple concurrent status states with priority-based active selection
application/ui/src/features/inspect/footer/status-bar/index.ts Barrel export for status bar module
application/ui/src/features/inspect/footer/footer.component.tsx Replaces old progress bar with new StatusBar component and adapter components
application/ui/src/features/inspect/footer/adapters/use-upload-status.ts Hook for managing upload operation status lifecycle
application/ui/src/features/inspect/footer/adapters/use-export-status.ts Hook for managing export operation status lifecycle
application/ui/src/features/inspect/footer/adapters/training-status.adapter.tsx Adapter component that syncs training job state to status bar
application/ui/src/features/inspect/footer/adapters/index.ts Barrel export for adapter modules
application/ui/src/features/inspect/footer/adapters/connection-status.adapter.tsx Adapter that maps WebRTC connection state to status bar connection display
application/ui/src/features/inspect/dataset/upload-images.component.tsx Updates image upload to track progress and report to status bar instead of using toasts

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

classes[activeStatus.variant],
isIndeterminate && classes.indeterminate
)}
style={{ '--progress': `${activeStatus.progress ?? 100}%` }}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The inline style uses a CSS custom property without proper typing. Consider using as React.CSSProperties or defining a typed interface for the style object to avoid TypeScript errors with custom CSS properties.

Suggested change
style={{ '--progress': `${activeStatus.progress ?? 100}%` }}
style={{ '--progress': `${activeStatus.progress ?? 100}%` } as React.CSSProperties}

Copilot uses AI. Check for mistakes.
}
},
});
}, [trainingJob?.id, trainingJob?.payload.model_name, trainingJob?.message, trainingJob?.progress, trainingJob?.status, setStatus, removeStatus, cancelJob]);
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The dependency array includes trainingJob?.status but this property is not used in the effect body. This can cause unnecessary re-renders. Remove trainingJob?.status from the dependency array since it's only used in the filtering condition outside the effect.

Suggested change
}, [trainingJob?.id, trainingJob?.payload.model_name, trainingJob?.message, trainingJob?.progress, trainingJob?.status, setStatus, removeStatus, cancelJob]);
}, [trainingJob?.id, trainingJob?.payload.model_name, trainingJob?.message, trainingJob?.progress, setStatus, removeStatus, cancelJob]);

Copilot uses AI. Check for mistakes.
detail: `0 / ${total}`,
progress: 0,
variant: 'info',
isCancellable: false,
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The isCancellable is set to false but an onCancel callback is provided. This inconsistency means the cancel button won't be shown but the cancel logic exists. Set isCancellable: true to make the cancel functionality accessible.

Copilot uses AI. Check for mistakes.
detail,
progress: percent,
variant: progress.failed > 0 ? 'warning' : 'info',
isCancellable: false,
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The isCancellable is set to false but an onCancel callback is provided. This inconsistency means the cancel button won't be shown but the cancel logic exists. Set isCancellable: true to make the cancel functionality accessible.

Copilot uses AI. Check for mistakes.
Signed-off-by: Dmitry Kalinin <[email protected]>
@ActiveChooN ActiveChooN marked this pull request as ready for review December 3, 2025 18:03
Copilot AI review requested due to automatic review settings December 3, 2025 18:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +56 to +65
}, [
trainingJob?.id,
trainingJob?.payload.model_name,
trainingJob?.message,
trainingJob?.progress,
trainingJob?.status,
setStatus,
removeStatus,
cancelJob,
]);
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Missing trainingJob itself in the dependency array. If the entire trainingJob object changes but the individual properties remain the same, the effect won't re-run. Add trainingJob to the dependency array or restructure to avoid relying on nested property access.

Copilot uses AI. Check for mistakes.
Comment on lines +91 to 92
completeExport(true);
downloadBlob(blob, filename);
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The dialog closes immediately on export start (line 115), but the success callback (line 91) and download still execute. If the component unmounts before the mutation completes, this could cause issues. Consider adding cleanup logic or ensure the mutation callback handles unmounted state.

Copilot uses AI. Check for mistakes.
detail,
progress: percent,
variant: progress.failed > 0 ? 'warning' : 'info',
isCancellable: false,
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Same issue as in startUpload: isCancellable is false but an onCancel handler exists (lines 59-62). This is inconsistent and prevents users from actually canceling the upload.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +18
// Track progress across parallel uploads
const progressRef = useRef({ completed: 0, failed: 0, total: 0 });
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Using a mutable ref for tracking progress in parallel uploads creates a race condition. Multiple onSuccess/onError callbacks from captureImageMutation could update progressRef.current.completed and progressRef.current.failed simultaneously, leading to inaccurate counts. Consider using atomic state updates or a reducer pattern.

Copilot uses AI. Check for mistakes.
progress: 100,
isCancellable: false,
});
setTimeout(() => removeStatus('export'), 5000);

Choose a reason for hiding this comment

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

we need to clear then to avoid memory leaks

Copy link
Author

Choose a reason for hiding this comment

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

Reworked, moved timers to the context


return (
<div className={classes.statusBar}>
{/* Progress bar */}

Choose a reason for hiding this comment

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

Instead of leaving a comment, let’s split this into a component with a descriptive name, that way the code is easier to recognize

Copy link
Author

Choose a reason for hiding this comment

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

Fixed

Copilot AI review requested due to automatic review settings December 4, 2025 15:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +56 to +65
}, [
trainingJob?.id,
trainingJob?.payload.model_name,
trainingJob?.message,
trainingJob?.progress,
trainingJob?.status,
setStatus,
removeStatus,
cancelJob,
]);
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

The dependency array includes individual properties from trainingJob (like trainingJob?.id, trainingJob?.message, etc.), but the useEffect also checks if (!trainingJob) at the start. If trainingJob changes from one defined object to another with the same property values, the effect won't re-run. This could cause stale status to persist. Consider including trainingJob itself as a dependency or using a stable identifier.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +35
const captureImageMutation = $api.useMutation('post', '/api/projects/{project_id}/images', {
onSuccess: () => {
progressRef.current.completed++;
updateProgress({
completed: progressRef.current.completed + progressRef.current.failed,
total: progressRef.current.total,
failed: progressRef.current.failed,
});
},
onError: () => {
progressRef.current.failed++;
updateProgress({
completed: progressRef.current.completed + progressRef.current.failed,
total: progressRef.current.total,
failed: progressRef.current.failed,
});
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

The mutation callbacks update progressRef.current and call updateProgress synchronously. However, since Promise.allSettled runs multiple mutations in parallel, there's a potential race condition where multiple callbacks could read and update progressRef.current.completed or progressRef.current.failed simultaneously, leading to incorrect counts. Consider using atomic updates or proper state management for concurrent operations.

Suggested change
const captureImageMutation = $api.useMutation('post', '/api/projects/{project_id}/images', {
onSuccess: () => {
progressRef.current.completed++;
updateProgress({
completed: progressRef.current.completed + progressRef.current.failed,
total: progressRef.current.total,
failed: progressRef.current.failed,
});
},
onError: () => {
progressRef.current.failed++;
updateProgress({
completed: progressRef.current.completed + progressRef.current.failed,
total: progressRef.current.total,
failed: progressRef.current.failed,
});
// Atomically increment progress counters to avoid race conditions
const incrementProgress = (type: 'completed' | 'failed') => {
// Use a single synchronous block to update and report
progressRef.current[type]++;
updateProgress({
completed: progressRef.current.completed + progressRef.current.failed,
total: progressRef.current.total,
failed: progressRef.current.failed,
});
};
const captureImageMutation = $api.useMutation('post', '/api/projects/{project_id}/images', {
onSuccess: () => {
incrementProgress('completed');
},
onError: () => {
incrementProgress('failed');

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +62
onCancel: () => {
abortControllerRef.current?.abort();
removeStatus('batch-upload');
},
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

The isCancellable is set to false, but an onCancel callback is provided in the updateProgress function. This is inconsistent - either enable cancellation by setting isCancellable: true, or remove the onCancel callback.

Suggested change
onCancel: () => {
abortControllerRef.current?.abort();
removeStatus('batch-upload');
},

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +115
exportMutation.mutate();
close(); // Close dialog immediately, status bar will show progress
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

The dialog closes immediately when export starts, but if the export fails synchronously (e.g., validation error before the API call), the user won't see the error in context. Consider keeping the dialog open until the mutation actually begins executing, or handle synchronous errors before closing.

Suggested change
exportMutation.mutate();
close(); // Close dialog immediately, status bar will show progress
try {
exportMutation.mutate();
close(); // Close dialog only if mutation started successfully
} catch (error) {
toast.error(error instanceof Error ? error.message : String(error));
}

Copilot uses AI. Check for mistakes.
Signed-off-by: Dmitry Kalinin <[email protected]>
Signed-off-by: Dmitry Kalinin <[email protected]>
Copilot AI review requested due to automatic review settings December 5, 2025 11:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -1,18 +1,47 @@
import { useRef } from 'react';
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

[nitpick] The import statement should be grouped with other React imports. Move this line to be with the existing React imports on line 3, maintaining alphabetical ordering within React imports.

Copilot uses AI. Check for mistakes.
classes[activeStatus.variant],
isIndeterminate && classes.indeterminate
)}
style={{ '--progress': `${activeStatus.progress ?? 100}%` }}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The inline style object uses a CSS custom property without TypeScript type safety. Consider adding a proper type definition for style props that include CSS custom properties, or use as React.CSSProperties to avoid potential type issues.

Suggested change
style={{ '--progress': `${activeStatus.progress ?? 100}%` }}
style={{ '--progress': `${activeStatus.progress ?? 100}%` } as React.CSSProperties}

Copilot uses AI. Check for mistakes.
@codecov-commenter
Copy link

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Signed-off-by: Dmitry Kalinin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants