Skip to content

Commit 9de1b18

Browse files
committed
Store and render images from Browser local storage
Signed-off-by: Charles Thao <[email protected]>
1 parent 4a18444 commit 9de1b18

File tree

5 files changed

+45
-6
lines changed

5 files changed

+45
-6
lines changed

workspaces/frontend/src/app/components/WorkspaceTable.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ const WorkspaceTable = React.forwardRef<WorkspaceTableRef, WorkspaceTableProps>(
652652
imageSrc={kindLogoDict[workspace.workspaceKind.name]}
653653
/>
654654
}
655+
storageKey={`workspace-kind-logo-${workspace.workspaceKind.name}`}
655656
>
656657
{(validSrc) => (
657658
<Tooltip content={workspace.workspaceKind.name}>

workspaces/frontend/src/app/pages/WorkspaceKinds/WorkspaceKinds.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
493493
imageSrc={workspaceKind.icon.url}
494494
skeletonWidth="20px"
495495
fallback={<ImageFallback imageSrc={workspaceKind.icon.url} />}
496+
storageKey={`workspace-kind-icon-${workspaceKind.name}`}
496497
>
497498
{(validSrc) => (
498499
<img

workspaces/frontend/src/app/pages/WorkspaceKinds/details/WorkspaceKindDetailsOverview.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export const WorkspaceKindDetailsOverview: React.FunctionComponent<
5959
message="Cannot load icon image"
6060
/>
6161
}
62+
storageKey={`workspace-kind-icon-${workspaceKind.name}`}
6263
>
6364
{(validSrc) => <img src={validSrc} alt={workspaceKind.name} style={{ width: '40px' }} />}
6465
</WithValidImage>
@@ -84,6 +85,7 @@ export const WorkspaceKindDetailsOverview: React.FunctionComponent<
8485
message="Cannot load logo image"
8586
/>
8687
}
88+
storageKey={`workspace-kind-logo-${workspaceKind.name}`}
8789
>
8890
{(validSrc) => <img src={validSrc} alt={workspaceKind.name} style={{ width: '40px' }} />}
8991
</WithValidImage>

workspaces/frontend/src/app/pages/Workspaces/Form/kind/WorkspaceFormKindList.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export const WorkspaceFormKindList: React.FunctionComponent<WorkspaceFormKindLis
124124
message="Cannot load logo image"
125125
/>
126126
}
127+
storageKey={`workspace-kind-logo-${kind.name}`}
127128
>
128129
{(validSrc) => (
129130
<img

workspaces/frontend/src/shared/components/WithValidImage.tsx

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import React, { useEffect, useState } from 'react';
22
import { Skeleton, SkeletonProps } from '@patternfly/react-core/dist/esm/components/Skeleton';
3+
import { useBrowserStorage } from 'mod-arch-core';
34

45
type WithValidImageProps = {
56
imageSrc: string | undefined | null;
67
fallback: React.ReactNode;
78
children: (validImageSrc: string) => React.ReactNode;
89
skeletonWidth?: SkeletonProps['width'];
910
skeletonShape?: SkeletonProps['shape'];
11+
storageKey?: string;
1012
};
1113

1214
const DEFAULT_SKELETON_WIDTH = '32px';
@@ -20,10 +22,11 @@ const WithValidImage: React.FC<WithValidImageProps> = ({
2022
children,
2123
skeletonWidth = DEFAULT_SKELETON_WIDTH,
2224
skeletonShape = DEFAULT_SKELETON_SHAPE,
25+
storageKey = '',
2326
}) => {
2427
const [status, setStatus] = useState<LoadState>('loading');
2528
const [resolvedSrc, setResolvedSrc] = useState<string>('');
26-
29+
const [image, setImage] = useBrowserStorage(storageKey, '');
2730
useEffect(() => {
2831
let cancelled = false;
2932

@@ -32,15 +35,46 @@ const WithValidImage: React.FC<WithValidImageProps> = ({
3235
return;
3336
}
3437

35-
const img = new Image();
36-
img.onload = () => !cancelled && (setResolvedSrc(imageSrc), setStatus('valid'));
37-
img.onerror = () => !cancelled && setStatus('invalid');
38-
img.src = imageSrc;
38+
const fetchImage = async () => {
39+
// Check if we have a cached base64 data URL
40+
if (image.length > 0) {
41+
setResolvedSrc(image);
42+
setStatus('valid');
43+
return;
44+
}
45+
try {
46+
const response = await fetch(imageSrc);
47+
const blob = await response.blob();
48+
const reader = new FileReader();
49+
reader.onloadend = () => {
50+
if (!cancelled && reader.result) {
51+
const dataUrl = reader.result as string;
52+
setResolvedSrc(dataUrl);
53+
setStatus('valid');
54+
setImage(dataUrl);
55+
}
56+
};
57+
reader.onerror = () => {
58+
console.error('Failed to convert image to data URL');
59+
if (!cancelled) {
60+
setStatus('invalid');
61+
}
62+
};
63+
reader.readAsDataURL(blob);
64+
} catch (error) {
65+
console.error('Failed to fetch image:', error);
66+
if (!cancelled) {
67+
setStatus('invalid');
68+
}
69+
}
70+
};
71+
72+
fetchImage();
3973

4074
return () => {
4175
cancelled = true;
4276
};
43-
}, [imageSrc]);
77+
}, [imageSrc, setImage, image]);
4478

4579
if (status === 'loading') {
4680
return (

0 commit comments

Comments
 (0)