11import 'dart:io' ;
22
33import 'package:flutter/material.dart' ;
4+ import 'package:flutter_riverpod/flutter_riverpod.dart' ;
45import 'package:powersync_attachments_helper/powersync_attachments_helper.dart' ;
6+ import 'package:riverpod_annotation/riverpod_annotation.dart' ;
57import 'package:supabase_todolist_drift/attachments/camera_helpers.dart' ;
68import 'package:supabase_todolist_drift/attachments/photo_capture_widget.dart' ;
79import 'package:supabase_todolist_drift/attachments/queue.dart' ;
810import 'package:supabase_todolist_drift/database.dart' ;
911
10- class PhotoWidget extends StatefulWidget {
12+ part 'photo_widget.g.dart' ;
13+
14+ final class PhotoWidget extends ConsumerWidget {
1115 final TodoItem todo;
1216
13- PhotoWidget ({
14- required this .todo,
15- }) : super (key: ObjectKey (todo.id));
17+ const PhotoWidget ({super .key, required this .todo});
1618
1719 @override
18- State <StatefulWidget > createState () {
19- return _PhotoWidgetState ();
20+ Widget build (BuildContext context, WidgetRef ref) {
21+ final photoState = ref.watch (_getPhotoStateProvider (todo.photoId));
22+ if (! photoState.hasValue) {
23+ return Container ();
24+ }
25+
26+ final data = photoState.value;
27+ Widget takePhotoButton = ElevatedButton (
28+ onPressed: () async {
29+ final camera = await setupCamera ();
30+ if (! context.mounted) return ;
31+
32+ if (camera == null ) {
33+ const snackBar = SnackBar (
34+ content: Text ('No camera available' ),
35+ backgroundColor: Colors .red, // Optional: to highlight it's an error
36+ );
37+
38+ ScaffoldMessenger .of (context).showSnackBar (snackBar);
39+ return ;
40+ }
41+
42+ Navigator .push (
43+ context,
44+ MaterialPageRoute (
45+ builder: (context) =>
46+ TakePhotoWidget (todoId: todo.id, camera: camera),
47+ ),
48+ );
49+ },
50+ child: const Text ('Take Photo' ),
51+ );
52+
53+ if (data == null ) {
54+ return takePhotoButton;
55+ }
56+
57+ String ? filePath = data.photoPath;
58+ bool fileIsDownloading = ! data.fileExists;
59+ bool fileArchived =
60+ data.attachment? .state == AttachmentState .archived.index;
61+
62+ if (fileArchived) {
63+ return Column (
64+ crossAxisAlignment: CrossAxisAlignment .center,
65+ mainAxisAlignment: MainAxisAlignment .center,
66+ children: [
67+ const Text ("Unavailable" ),
68+ const SizedBox (height: 8 ),
69+ takePhotoButton
70+ ],
71+ );
72+ }
73+
74+ if (fileIsDownloading) {
75+ return const Text ("Downloading..." );
76+ }
77+
78+ File imageFile = File (filePath! );
79+ int lastModified = imageFile.existsSync ()
80+ ? imageFile.lastModifiedSync ().millisecondsSinceEpoch
81+ : 0 ;
82+ Key key = ObjectKey ('$filePath :$lastModified ' );
83+
84+ return Image .file (
85+ key: key,
86+ imageFile,
87+ width: 50 ,
88+ height: 50 ,
89+ );
2090 }
2191}
2292
@@ -29,104 +99,25 @@ class _ResolvedPhotoState {
2999 {required this .photoPath, required this .fileExists, this .attachment});
30100}
31101
32- class _PhotoWidgetState extends State <PhotoWidget > {
33- late String photoPath;
34-
35- Future <_ResolvedPhotoState > _getPhotoState (photoId) async {
36- if (photoId == null ) {
37- return _ResolvedPhotoState (photoPath: null , fileExists: false );
38- }
39- photoPath = await attachmentQueue.getLocalUri ('$photoId .jpg' );
40-
41- bool fileExists = await File (photoPath).exists ();
102+ @riverpod
103+ Future <_ResolvedPhotoState > _getPhotoState (Ref ref, String ? photoId) async {
104+ if (photoId == null ) {
105+ return _ResolvedPhotoState (photoPath: null , fileExists: false );
106+ }
107+ final queue = await ref.read (attachmentQueueProvider.future);
108+ final photoPath = await queue.getLocalUri ('$photoId .jpg' );
42109
43- final row = await attachmentQueue.db
44- .getOptional ('SELECT * FROM attachments_queue WHERE id = ?' , [photoId]);
110+ bool fileExists = await File (photoPath).exists ();
45111
46- if (row != null ) {
47- Attachment attachment = Attachment .fromRow (row);
48- return _ResolvedPhotoState (
49- photoPath: photoPath, fileExists: fileExists, attachment: attachment);
50- }
112+ final row = await queue.db
113+ .getOptional ('SELECT * FROM attachments_queue WHERE id = ?' , [photoId]);
51114
115+ if (row != null ) {
116+ Attachment attachment = Attachment .fromRow (row);
52117 return _ResolvedPhotoState (
53- photoPath: photoPath, fileExists: fileExists, attachment: null );
118+ photoPath: photoPath, fileExists: fileExists, attachment: attachment );
54119 }
55120
56- @override
57- Widget build (BuildContext context) {
58- return FutureBuilder (
59- future: _getPhotoState (widget.todo.photoId),
60- builder: (BuildContext context,
61- AsyncSnapshot <_ResolvedPhotoState > snapshot) {
62- if (snapshot.data == null ) {
63- return Container ();
64- }
65- final data = snapshot.data! ;
66- Widget takePhotoButton = ElevatedButton (
67- onPressed: () async {
68- final camera = await setupCamera ();
69- if (! context.mounted) return ;
70-
71- if (camera == null ) {
72- const snackBar = SnackBar (
73- content: Text ('No camera available' ),
74- backgroundColor:
75- Colors .red, // Optional: to highlight it's an error
76- );
77-
78- ScaffoldMessenger .of (context).showSnackBar (snackBar);
79- return ;
80- }
81-
82- Navigator .push (
83- context,
84- MaterialPageRoute (
85- builder: (context) =>
86- TakePhotoWidget (todoId: widget.todo.id, camera: camera),
87- ),
88- );
89- },
90- child: const Text ('Take Photo' ),
91- );
92-
93- if (widget.todo.photoId == null ) {
94- return takePhotoButton;
95- }
96-
97- String ? filePath = data.photoPath;
98- bool fileIsDownloading = ! data.fileExists;
99- bool fileArchived =
100- data.attachment? .state == AttachmentState .archived.index;
101-
102- if (fileArchived) {
103- return Column (
104- crossAxisAlignment: CrossAxisAlignment .center,
105- mainAxisAlignment: MainAxisAlignment .center,
106- children: [
107- const Text ("Unavailable" ),
108- const SizedBox (height: 8 ),
109- takePhotoButton
110- ],
111- );
112- }
113-
114- if (fileIsDownloading) {
115- return const Text ("Downloading..." );
116- }
117-
118- File imageFile = File (filePath! );
119- int lastModified = imageFile.existsSync ()
120- ? imageFile.lastModifiedSync ().millisecondsSinceEpoch
121- : 0 ;
122- Key key = ObjectKey ('$filePath :$lastModified ' );
123-
124- return Image .file (
125- key: key,
126- imageFile,
127- width: 50 ,
128- height: 50 ,
129- );
130- });
131- }
121+ return _ResolvedPhotoState (
122+ photoPath: photoPath, fileExists: fileExists, attachment: null );
132123}
0 commit comments