11import { CameraCapturedPicture } from 'expo-camera' ;
22import React from 'react' ;
3- import { ActivityIndicator , View , Modal , StyleSheet } from 'react-native' ;
3+ import { ActivityIndicator , View , Modal , StyleSheet , Platform } from 'react-native' ;
44import { ListItem , Button , Icon , Image } from 'react-native-elements' ;
55import { CameraWidget } from './CameraWidget' ;
66import { 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