Skip to content

Commit aec02b1

Browse files
committed
fix: web attachment display using blob url
1 parent 8cfc680 commit aec02b1

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

demos/react-native-web-supabase-todolist/library/widgets/TodoItemWidget.tsx

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CameraCapturedPicture } from 'expo-camera';
22
import React from 'react';
3-
import { ActivityIndicator, View, Modal, StyleSheet } from 'react-native';
3+
import { ActivityIndicator, View, Modal, StyleSheet, Platform } from 'react-native';
44
import { ListItem, Button, Icon, Image } from 'react-native-elements';
55
import { CameraWidget } from './CameraWidget';
66
import { TodoRecord } from '../powersync/AppSchema';
@@ -21,12 +21,52 @@ export const TodoItemWidget: React.FC<TodoItemWidgetProps> = (props) => {
2121
const { record, photoAttachment, onDelete, onToggleCompletion, onSavePhoto } = props;
2222
const [loading, setLoading] = React.useState(false);
2323
const [isCameraVisible, setCameraVisible] = React.useState(false);
24+
const [imageUri, setImageUri] = React.useState<string | null>(null);
2425
const system = useSystem();
2526

2627
const handleCancel = React.useCallback(() => {
2728
setCameraVisible(false);
2829
}, []);
2930

31+
React.useEffect(() => {
32+
let blobUrl: string | null = null;
33+
34+
const loadImage = async () => {
35+
if (!photoAttachment?.local_uri) {
36+
setImageUri(null);
37+
return;
38+
}
39+
40+
// On web, convert IndexedDB URI to blob URL
41+
if (Platform.OS === 'web' && photoAttachment.local_uri.startsWith('indexeddb://')) {
42+
try {
43+
const localStorage = system.photoAttachmentQueue?.localStorage;
44+
if (localStorage && typeof localStorage === 'object' && 'downloadFile' in localStorage) {
45+
const downloadFile = (localStorage.downloadFile as (path: string) => Promise<Blob>).bind(localStorage);
46+
const blob = await downloadFile(photoAttachment.local_uri);
47+
blobUrl = URL.createObjectURL(blob);
48+
setImageUri(blobUrl);
49+
}
50+
} catch (error) {
51+
console.error('Failed to load image from IndexedDB:', error);
52+
setImageUri(null);
53+
}
54+
} else {
55+
// On native, use the URI directly
56+
setImageUri(photoAttachment.local_uri);
57+
}
58+
};
59+
60+
loadImage();
61+
62+
// Cleanup blob URL on unmount or when photoAttachment changes
63+
return () => {
64+
if (blobUrl) {
65+
URL.revokeObjectURL(blobUrl);
66+
}
67+
};
68+
}, [photoAttachment?.local_uri, system.photoAttachmentQueue]);
69+
3070
return (
3171
<View key={`todo-item-${record.id}`} style={{ padding: 10 }}>
3272
<Modal animationType="slide" transparent={false} visible={isCameraVisible} onRequestClose={handleCancel}>
@@ -58,7 +98,7 @@ export const TodoItemWidget: React.FC<TodoItemWidgetProps> = (props) => {
5898
iconType="material-community"
5999
checkedIcon="checkbox-marked"
60100
uncheckedIcon="checkbox-blank-outline"
61-
checked={record.completed}
101+
checked={Boolean(record.completed)}
62102
onPress={async () => {
63103
setLoading(true);
64104
await onToggleCompletion(!record.completed);
@@ -74,7 +114,7 @@ export const TodoItemWidget: React.FC<TodoItemWidgetProps> = (props) => {
74114
<Icon name={'camera'} type="font-awesome" onPress={() => setCameraVisible(true)} />
75115
) : photoAttachment?.local_uri != null ? (
76116
<Image
77-
source={{ uri: system.attachmentQueue.getLocalUri(photoAttachment.local_uri) }}
117+
source={{ uri: imageUri || undefined }}
78118
containerStyle={styles.item}
79119
PlaceholderContent={<ActivityIndicator />}
80120
/>

0 commit comments

Comments
 (0)