feat: upload photo

release/mobdr-v0.0.1
Frédérik Benoist 2023-05-01 23:57:35 +02:00
parent abb13b588b
commit 492851ffc9
7 changed files with 253 additions and 60 deletions

View File

@ -46,3 +46,5 @@ class ApiConstants {
const String LOGIN_API = 'https://mp4.ikksgroup.com' + "/authentication/login";
const String PRODUCT_API = 'https://mp4.ikksgroup.com' + "/example/getProduct";
const String SERVLET_API =
'https://mp4.ikksgroup.com' + "/MobilePortal4_external/UploadPhotoServlet";

View File

@ -1,3 +1,4 @@
import 'package:intl/intl.dart';
import 'package:mobdr/main.dart';
class VisiteModel {
@ -34,7 +35,8 @@ class VisiteModel {
id_visite: visite.id_visite,
name: visite.id_etab.toString() + ' - ' + visite.title,
photoCount: objectbox.getVisitPhotoCount(visite.id_visite),
date: visite.date_visite.toString(),
date: DateFormat('EEEE d MMMM HH:mm', 'fr_FR')
.format(visite.date_visite),
image: visite.url_photo_principale,
type_visite: visite.type_visite,
langage: visite.langage))
@ -56,7 +58,8 @@ class VisiteModel {
id_visite: visite.id_visite,
name: visite.id_etab.toString() + ' - ' + visite.title,
photoCount: objectbox.getVisitPhotoCount(visite.id_visite),
date: visite.date_visite.toString(),
date: DateFormat('EEEE d MMMM HH:mm', 'fr_FR')
.format(visite.date_visite),
image: visite.url_photo_principale,
type_visite: visite.type_visite,
langage: visite.langage))
@ -78,7 +81,8 @@ class VisiteModel {
id_visite: visite.id_visite,
name: visite.id_etab.toString() + ' - ' + visite.title,
photoCount: objectbox.getVisitPhotoCount(visite.id_visite),
date: visite.date_visite.toString(),
date: DateFormat('EEEE d MMMM HH:mm', 'fr_FR')
.format(visite.date_visite),
image: visite.url_photo_principale,
type_visite: visite.type_visite,
langage: visite.langage))

View File

@ -525,7 +525,7 @@ class ObjectBox {
final results = query.find();
if (results.isNotEmpty) {
final photoToDelete = results.first;
visitPhotoBox.removeAsync(photoToDelete.id);
visitPhotoBox.remove(photoToDelete.id);
}
}

View File

@ -256,7 +256,6 @@ class _TabHomePageState extends State<TabHomePage>
pp_langage: data.langage,
pp_id_visite: data.id_visite,
pp_name: data.name,
onRefreshVisit: (int photoCount) {},
),
);
Navigator.push(context, route);

View File

@ -0,0 +1,187 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:mobdr/service/shared_prefs.dart';
import 'package:mobdr/config/constant.dart';
class UploadPhotosPage extends StatefulWidget {
final int id_visite;
final List<String> photoPaths;
UploadPhotosPage({required this.id_visite, required this.photoPaths});
@override
_UploadPhotosPageState createState() => _UploadPhotosPageState();
}
class _UploadPhotosPageState extends State<UploadPhotosPage> {
bool _isUploading = false;
bool _isFinished = false;
int _totalUploaded = 0;
int _totalPhotos = 0;
@override
void initState() {
super.initState();
_totalPhotos = widget.photoPaths.length;
}
Future<void> _uploadPhotos() async {
setState(() {
_isUploading = true;
_isFinished = false;
});
for (int i = 0; i < widget.photoPaths.length; i++) {
String photoPath = SharedPrefs().photosDir + "/" + widget.photoPaths[i];
//String filename = path.basename(photoPath);
// Upload the photo
int photoId = await uploadPhoto(photoPath);
if (photoId != -1) {
_totalUploaded++;
}
}
setState(() {
_isUploading = false;
_isFinished = true;
});
}
Future<int> uploadPhoto(String photoPath) async {
try {
final url = Uri.parse(SERVLET_API);
final file = File(photoPath);
final bytes = await file.readAsBytes();
final multipartRequest = http.MultipartRequest('POST', url)
..fields['id_visite'] = widget.id_visite.toString()
..files.add(http.MultipartFile.fromBytes(
'photo',
bytes,
filename: path.basename(photoPath),
contentType: MediaType('image', 'jpeg'),
));
final headers = {'Cookie': "pguid=${SharedPrefs().guid};"};
multipartRequest.headers.addAll(headers);
final response = await multipartRequest.send();
final responseString = await response.stream.bytesToString();
final jsonResponse = jsonDecode(responseString);
final idPhotoString = jsonResponse[0]['id_photo'];
final idPhoto = int.parse(idPhotoString);
return idPhoto;
} catch (e) {
print('Error uploading photo: $e');
return -1;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Upload Photos'),
centerTitle: true,
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Upload Photos',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
SizedBox(height: 16),
Expanded(
child: Stack(
children: [
Center(
child: Visibility(
visible: _isUploading,
child: Container(
height: 150,
width: 150,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).primaryColor,
),
strokeWidth: 10,
),
),
),
),
Center(
child: Visibility(
visible: !_isUploading && _totalUploaded == _totalPhotos,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle,
size: 48,
color: Theme.of(context).primaryColor,
),
SizedBox(height: 16),
Text(
'Upload complete',
textAlign: TextAlign.center,
),
],
),
),
),
Center(
child: Visibility(
visible: !_isUploading && _totalUploaded != _totalPhotos,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.cloud_upload,
size: 48,
color: Colors.grey,
),
SizedBox(height: 16),
Text(
'Tap the button below to upload your photos',
textAlign: TextAlign.center,
),
],
),
),
),
],
),
),
SizedBox(height: 16),
Visibility(
visible: !_isUploading,
child: ElevatedButton(
child: Text('Upload Photos ($_totalUploaded / $_totalPhotos)'),
onPressed: _isFinished ? null : _uploadPhotos,
),
),
],
),
),
);
}
}

View File

@ -8,6 +8,7 @@ import 'package:mobdr/events.dart';
import 'package:mobdr/model/visite_model.dart';
import 'package:mobdr/ui/visit/visit_photo_typology.dart';
import 'package:mobdr/ui/reusable/cache_image_network.dart';
import 'package:mobdr/ui/sync/upload_photos.dart';
class TabVisitListPage extends StatefulWidget {
@override
@ -68,7 +69,7 @@ class _TabVisitListPageState extends State<TabVisitListPage>
return Center(child: CircularProgressIndicator());
} else if (modelData.isEmpty) {
return Center(
child: Text('Aucune visite trouvée.'),
child: Text('No visits to synchronise.'),
);
}
return Scaffold(
@ -164,12 +165,10 @@ class _TabVisitListPageState extends State<TabVisitListPage>
onTap: () {
Route route = MaterialPageRoute(
builder: (context) => VisitPhotoTypologyPage(
pp_id_distrib: visiteData.id_distrib,
pp_langage: visiteData.langage,
pp_id_visite: visiteData.id_visite,
pp_name: visiteData.name,
onRefreshVisit: (int photoCount) {},
));
pp_id_distrib: visiteData.id_distrib,
pp_langage: visiteData.langage,
pp_id_visite: visiteData.id_visite,
pp_name: visiteData.name));
Navigator.push(context, route);
},
child: Container(
@ -208,8 +207,7 @@ class _TabVisitListPageState extends State<TabVisitListPage>
child: Row(
children: [
Text(visiteData.name,
style: TextStyle(
fontSize: 11, color: SOFT_GREY))
style: GlobalStyle.productPrice)
],
),
),
@ -273,7 +271,7 @@ class _TabVisitListPageState extends State<TabVisitListPage>
borderRadius: BorderRadius.circular(5.0),
)),
),
onPressed: null,
onPressed: () {},
child: Text(
'Synchronize',
style: TextStyle(
@ -283,7 +281,17 @@ class _TabVisitListPageState extends State<TabVisitListPage>
textAlign: TextAlign.center,
))
: OutlinedButton(
onPressed: () {},
onPressed: () {
Route route = MaterialPageRoute(
builder: (context) => UploadPhotosPage(
id_visite: visiteData.id_visite,
photoPaths: [
'sim_1682957196322406.jpeg',
],
),
);
Navigator.push(context, route);
},
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(
Size(0, 30)),

View File

@ -13,16 +13,14 @@ class VisitPhotoTypologyPage extends StatefulWidget {
final String pp_langage;
final int pp_id_visite;
final String pp_name;
final Function(int) onRefreshVisit;
VisitPhotoTypologyPage(
{Key? key,
required this.pp_id_distrib,
required this.pp_langage,
required this.pp_id_visite,
required this.pp_name,
required this.onRefreshVisit})
: super(key: key);
VisitPhotoTypologyPage({
Key? key,
required this.pp_id_distrib,
required this.pp_langage,
required this.pp_id_visite,
required this.pp_name,
}) : super(key: key);
@override
_VisitPhotoTypologyPageState createState() => _VisitPhotoTypologyPageState();
@ -127,42 +125,37 @@ class _VisitPhotoTypologyPageState extends State<VisitPhotoTypologyPage> {
setState(() {});
}
Future<bool> onBackPressed() async {
// Navigate back to the visits page and refresh the data
int newPhotoCount = objectbox.getVisitPhotoCount(widget.pp_id_visite);
widget.onRefreshVisit(newPhotoCount);
return true;
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
return onBackPressed();
},
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
iconTheme: IconThemeData(
color: GlobalStyle.appBarIconThemeColor,
),
elevation: GlobalStyle.appBarElevation,
title: Text(
widget.pp_name,
style: GlobalStyle.appBarTitle,
),
backgroundColor: GlobalStyle.appBarBackgroundColor,
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle),
body: Column(children: <Widget>[
Expanded(
child: StreamBuilder<List<PhotoTypology>>(
stream: objectbox.getPhotoTypologies(),
builder: (context, snapshot) => ListView.builder(
shrinkWrap: true,
//padding: const EdgeInsets.symmetric(horizontal: 20.0),
itemCount:
snapshot.hasData ? snapshot.data!.length : 0,
itemBuilder: _itemBuilder(snapshot.data ?? []))))
])));
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
iconTheme: IconThemeData(
color: GlobalStyle.appBarIconThemeColor,
),
elevation: GlobalStyle.appBarElevation,
title: Text(
widget.pp_name,
style: GlobalStyle.appBarTitle,
),
backgroundColor: GlobalStyle.appBarBackgroundColor,
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
),
body: Column(
children: <Widget>[
Expanded(
child: StreamBuilder<List<PhotoTypology>>(
stream: objectbox.getPhotoTypologies(),
builder: (context, snapshot) => ListView.builder(
shrinkWrap: true,
//padding: const EdgeInsets.symmetric(horizontal: 20.0),
itemCount: snapshot.hasData ? snapshot.data!.length : 0,
itemBuilder: _itemBuilder(snapshot.data ?? []),
),
),
),
],
),
);
}
}