Chore/v2/improve uploads (#5351)

Co-authored-by: James Brunton <[email protected]>
This commit is contained in:
Reece Browne
2026-01-06 16:54:14 +00:00
committed by GitHub
co-authored by James Brunton
parent d9caa3482c
commit c158664d2c
6 changed files with 160 additions and 116 deletions
@@ -43,7 +43,7 @@ const FileEditor = ({
const { clearAllFileErrors } = fileContextActions;
// Extract needed values from state (memoized to prevent infinite loops)
const activeStirlingFileStubs = useMemo(() => selectors.getStirlingFileStubs(), [selectors.getFilesSignature()]);
const activeStirlingFileStubs = useMemo(() => selectors.getStirlingFileStubs(), [state.files.byId, state.files.ids]);
const selectedFileIds = state.ui.selectedFileIds;
const totalItems = state.files.ids.length;
const selectedCount = selectedFileIds.length;
@@ -365,7 +365,7 @@ const FileEditor = ({
activateOnDrag={true}
>
<Box pos="relative" style={{ overflow: 'auto' }}>
<LoadingOverlay visible={false} />
<LoadingOverlay visible={state.ui.isProcessing} />
<Box p="md">
@@ -1,5 +1,5 @@
import React, { useState, useCallback, useRef, useMemo } from 'react';
import { Text, ActionIcon, CheckboxIndicator, Tooltip, Modal, Button, Group, Stack } from '@mantine/core';
import { Text, ActionIcon, CheckboxIndicator, Tooltip, Modal, Button, Group, Stack, Loader } from '@mantine/core';
import { useIsMobile } from '@app/hooks/useIsMobile';
import { alert } from '@app/components/toast';
import { useTranslation } from 'react-i18next';
@@ -389,7 +389,7 @@ const FileEditorThumbnail = ({
style={isSupported || hasError ? undefined : { filter: 'grayscale(80%)', opacity: 0.6 }}
>
<div className={styles.previewPaper}>
{file.thumbnailUrl && (
{file.thumbnailUrl ? (
<PrivateContent>
<img
src={file.thumbnailUrl}
@@ -416,7 +416,12 @@ const FileEditorThumbnail = ({
}}
/>
</PrivateContent>
)}
) : file.type?.startsWith('application/pdf') ? (
<Stack align="center" justify="center" gap="xs" style={{ height: '100%' }}>
<Loader size="sm" />
<Text size="xs" c="dimmed">Loading thumbnail...</Text>
</Stack>
) : null}
</div>
{/* Drag handle (span wrapper so we can attach a ref reliably) */}
@@ -1,5 +1,5 @@
import { useState } from "react";
import { Card, Stack, Text, Group, Badge, Button, Box, Image, ThemeIcon, ActionIcon, Tooltip } from "@mantine/core";
import { Card, Stack, Text, Group, Badge, Button, Box, Image, ThemeIcon, ActionIcon, Tooltip, Loader } from "@mantine/core";
import { useTranslation } from "react-i18next";
import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf";
import StorageIcon from "@mui/icons-material/Storage";
@@ -29,6 +29,10 @@ const FileCard = ({ file, fileStub, onRemove, onDoubleClick, onView, onEdit, isS
const thumb = fileStub?.thumbnailUrl || indexedDBThumb;
const [isHovered, setIsHovered] = useState(false);
// Show loading state during hydration: PDF file without thumbnail yet
const isPdf = file.type === 'application/pdf';
const isHydrating = isPdf && !thumb && !isGenerating;
return (
<Card
shadow="xs"
@@ -125,24 +129,11 @@ const FileCard = ({ file, fileStub, onRemove, onDoubleClick, onView, onEdit, isS
fit="contain"
radius="sm"
/>
) : isGenerating ? (
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center'
}}>
<div style={{
width: 20,
height: 20,
border: '2px solid #ddd',
borderTop: '2px solid #666',
borderRadius: '50%',
animation: 'spin 1s linear infinite',
marginBottom: 8
}} />
<Text size="xs" c="dimmed">Generating...</Text>
</div>
) : (isGenerating || isHydrating) ? (
<Stack align="center" justify="center" gap="xs">
<Loader size="sm" />
<Text size="xs" c="dimmed">Loading...</Text>
</Stack>
) : (
<div style={{
display: 'flex',
@@ -1,12 +1,12 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button, Text, Stack, Group, Card, Progress } from "@mantine/core";
import { Button, Text, Stack, Group, Card, Progress, Loader } from "@mantine/core";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import CheckIcon from "@mui/icons-material/Check";
import { useFileSelection } from "@app/contexts/FileContext";
import { useToolRegistry } from "@app/contexts/ToolRegistryContext";
import { AutomationConfig, ExecutionStep } from "@app/types/automation";
import { AUTOMATION_CONSTANTS, EXECUTION_STATUS } from "@app/constants/automation";
import { EXECUTION_STATUS } from "@app/constants/automation";
import { useResourceCleanup } from "@app/utils/resourceManager";
interface AutomationRunProps {
@@ -119,14 +119,7 @@ export default function AutomationRun({ automation, onComplete, automateOperatio
case EXECUTION_STATUS.ERROR:
return <span style={{ fontSize: 16, color: 'red' }}></span>;
case EXECUTION_STATUS.RUNNING:
return <div style={{
width: 16,
height: 16,
border: '2px solid #ccc',
borderTop: '2px solid #007bff',
borderRadius: '50%',
animation: `spin ${AUTOMATION_CONSTANTS.SPINNER_ANIMATION_DURATION} linear infinite`
}} />;
return <Loader size={16} />;
default:
return <div style={{
width: 16,