u
This commit is contained in:
@@ -26,10 +26,6 @@ import brandAutomat from '../../automats/brandAutomat';
|
|||||||
import { orderAutomat } from '../../automats/orderAutomat';
|
import { orderAutomat } from '../../automats/orderAutomat';
|
||||||
import SkeleHashImage from './SkeleHashImage';
|
import SkeleHashImage from './SkeleHashImage';
|
||||||
|
|
||||||
// Keep only the most recent N image events in state to avoid unbounded growth
|
|
||||||
// on large imports (thousands of images).
|
|
||||||
const MAX_STREAMED_IMAGES = 60;
|
|
||||||
|
|
||||||
function formatBytes(bytes) {
|
function formatBytes(bytes) {
|
||||||
if (bytes == null || isNaN(bytes) || bytes === 0) return '0 B';
|
if (bytes == null || isNaN(bytes) || bytes === 0) return '0 B';
|
||||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||||
@@ -104,8 +100,8 @@ export default class ImportExportSettingsPanel extends Component {
|
|||||||
uploadLoaded: 0,
|
uploadLoaded: 0,
|
||||||
uploadTotal: 0,
|
uploadTotal: 0,
|
||||||
importProgress: null, // raw counters from the `progress` event
|
importProgress: null, // raw counters from the `progress` event
|
||||||
// Most recent SkeleHashes from the stream ({ id, hash })
|
// Latest SkeleHash shown (first hash of the most recent images batch)
|
||||||
importedImages: [],
|
streamedHash: null,
|
||||||
importedImageCount: 0,
|
importedImageCount: 0,
|
||||||
statusMessage: null,
|
statusMessage: null,
|
||||||
importStats: null,
|
importStats: null,
|
||||||
@@ -252,7 +248,6 @@ export default class ImportExportSettingsPanel extends Component {
|
|||||||
|
|
||||||
this.uploadAbortController = new AbortController();
|
this.uploadAbortController = new AbortController();
|
||||||
|
|
||||||
this.imageSeq = 0;
|
|
||||||
this.setState({
|
this.setState({
|
||||||
uploading: true,
|
uploading: true,
|
||||||
uploadStage: 'upload',
|
uploadStage: 'upload',
|
||||||
@@ -260,7 +255,7 @@ export default class ImportExportSettingsPanel extends Component {
|
|||||||
uploadLoaded: 0,
|
uploadLoaded: 0,
|
||||||
uploadTotal: selectedFile.size,
|
uploadTotal: selectedFile.size,
|
||||||
importProgress: null,
|
importProgress: null,
|
||||||
importedImages: [],
|
streamedHash: null,
|
||||||
importedImageCount: 0,
|
importedImageCount: 0,
|
||||||
statusMessage: null,
|
statusMessage: null,
|
||||||
importStats: null,
|
importStats: null,
|
||||||
@@ -294,25 +289,16 @@ export default class ImportExportSettingsPanel extends Component {
|
|||||||
onImages: (images) => {
|
onImages: (images) => {
|
||||||
const list = Array.isArray(images) ? images.filter(Boolean) : [];
|
const list = Array.isArray(images) ? images.filter(Boolean) : [];
|
||||||
if (!list.length) return;
|
if (!list.length) return;
|
||||||
this.setState((prev) => {
|
// Only the first hash of each batch; it replaces the previously
|
||||||
let seq = this.imageSeq ?? 0;
|
// shown placeholder.
|
||||||
const batch = list.map((item) => {
|
const first = list[0];
|
||||||
seq += 1;
|
const hash = typeof first === 'string' ? first : (first?.skelehash ?? null);
|
||||||
return {
|
this.setState((prev) => ({
|
||||||
id: seq,
|
uploadStage: 'server',
|
||||||
hash: typeof item === 'string' ? item : (item?.skelehash ?? null),
|
streamedHash: hash,
|
||||||
};
|
importedImageCount: prev.importedImageCount + list.length,
|
||||||
});
|
statusMessage: null,
|
||||||
this.imageSeq = seq;
|
}));
|
||||||
const next = [...batch.reverse(), ...prev.importedImages];
|
|
||||||
if (next.length > MAX_STREAMED_IMAGES) next.length = MAX_STREAMED_IMAGES;
|
|
||||||
return {
|
|
||||||
uploadStage: 'server',
|
|
||||||
importedImages: next,
|
|
||||||
importedImageCount: prev.importedImageCount + list.length,
|
|
||||||
statusMessage: null,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
onDone: (stats) => {
|
onDone: (stats) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -383,7 +369,7 @@ export default class ImportExportSettingsPanel extends Component {
|
|||||||
uploadLoaded,
|
uploadLoaded,
|
||||||
uploadTotal,
|
uploadTotal,
|
||||||
importProgress,
|
importProgress,
|
||||||
importedImages,
|
streamedHash,
|
||||||
importedImageCount,
|
importedImageCount,
|
||||||
statusMessage,
|
statusMessage,
|
||||||
importStats,
|
importStats,
|
||||||
@@ -831,88 +817,44 @@ export default class ImportExportSettingsPanel extends Component {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Live stream of decoded SkeleHash placeholders */}
|
{/* Latest decoded SkeleHash placeholder (replaces previous) */}
|
||||||
{importedImages.length > 0 && (
|
{streamedHash && (
|
||||||
<Box sx={{ mt: 2, pt: 2, borderTop: '1px dashed', borderColor: 'divider' }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 1,
|
||||||
|
mt: 2,
|
||||||
|
pt: 2,
|
||||||
|
borderTop: '1px dashed',
|
||||||
|
borderColor: 'divider',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
|
title={streamedHash}
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
width: 36,
|
||||||
alignItems: 'center',
|
height: 36,
|
||||||
justifyContent: 'space-between',
|
flex: '0 0 auto',
|
||||||
mb: 1,
|
borderRadius: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
bgcolor: 'background.paper',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="caption" fontWeight={600} color="text.secondary">
|
<SkeleHashImage hash={streamedHash} />
|
||||||
SkeleHash-Platzhalter (Vorschau)
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="caption" color="text.secondary">
|
|
||||||
{importedImageCount} Bild(er) verarbeitet
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Typography
|
||||||
sx={{
|
variant="caption"
|
||||||
display: 'grid',
|
color="text.secondary"
|
||||||
gridTemplateColumns: 'repeat(auto-fill, minmax(72px, 1fr))',
|
sx={{ fontFamily: 'monospace' }}
|
||||||
gap: 1,
|
|
||||||
maxHeight: 220,
|
|
||||||
overflowY: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{importedImages.map((image) => (
|
{shortHash(streamedHash)}
|
||||||
<Box
|
</Typography>
|
||||||
key={image.id}
|
<Typography variant="caption" color="text.secondary" sx={{ ml: 'auto' }}>
|
||||||
title={image.hash || 'kein Hash'}
|
{importedImageCount} Bild(er) verarbeitet
|
||||||
sx={{
|
</Typography>
|
||||||
position: 'relative',
|
|
||||||
aspectRatio: '1 / 1',
|
|
||||||
borderRadius: 1,
|
|
||||||
overflow: 'hidden',
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor: 'divider',
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{image.hash ? (
|
|
||||||
<SkeleHashImage hash={image.hash} />
|
|
||||||
) : (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
color: 'text.disabled',
|
|
||||||
fontSize: '0.6rem',
|
|
||||||
textAlign: 'center',
|
|
||||||
p: 0.5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Kein Hash
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
inset: 'auto 0 0 0',
|
|
||||||
bgcolor: 'rgba(0,0,0,0.55)',
|
|
||||||
color: '#fff',
|
|
||||||
px: 0.5,
|
|
||||||
py: 0.25,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
variant="caption"
|
|
||||||
noWrap
|
|
||||||
display="block"
|
|
||||||
sx={{ fontSize: '0.6rem', lineHeight: 1.2, fontFamily: 'monospace' }}
|
|
||||||
>
|
|
||||||
{shortHash(image.hash)}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user