Last active
May 2, 2023 20:33
-
-
Save maverick42/eb1ea3415a1f024fe28b9d677741e483 to your computer and use it in GitHub Desktop.
MobDR
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:objectbox/objectbox.dart'; | |
import 'package:intl/intl.dart'; | |
import 'package:mobdr/service/shared_prefs.dart'; | |
// ignore_for_file: public_member_api_docs | |
@Entity() | |
class VisitPhoto { | |
// specify the id | |
@Id() | |
int id = 0; | |
int id_visite; | |
int id_photo_typologie; | |
String image_name; | |
DateTime date_photo; | |
int id_photo_mp4; | |
int photo_privee; | |
int photo_principale; | |
String tags; | |
int uploaded; | |
VisitPhoto( | |
{this.id = 0, | |
required this.id_visite, | |
required this.id_photo_typologie, | |
required this.image_name, | |
this.id_photo_mp4 = 0, | |
this.photo_privee = 0, | |
this.photo_principale = 0, | |
this.tags = '', | |
DateTime? date_photo, | |
this.uploaded = 0}) | |
: date_photo = date_photo ?? DateTime.now(); | |
static String? _photosDir = SharedPrefs().photosDir; | |
String getImage() { | |
if (_photosDir == null) { | |
throw Exception('Photos directory not initialized'); | |
} | |
return '$_photosDir/$image_name'; | |
} | |
String get dateFormat => DateFormat('dd.MM.yyyy hh:mm:ss').format(date_photo); | |
VisitPhoto copyWith({ | |
int? id, | |
int? id_visite, | |
int? id_photo_typologie, | |
String? image, | |
String? image_name, | |
DateTime? date_photo, | |
int? id_photo_mp4, | |
int? photo_privee, | |
int? photo_principale, | |
String? tags, | |
int? uploaded, | |
}) { | |
return VisitPhoto( | |
id: id ?? this.id, | |
id_visite: id_visite ?? this.id_visite, | |
id_photo_typologie: id_photo_typologie ?? this.id_photo_typologie, | |
image_name: image_name ?? this.image_name, | |
date_photo: date_photo ?? this.date_photo, | |
id_photo_mp4: id_photo_mp4 ?? this.id_photo_mp4, | |
photo_privee: photo_privee ?? this.photo_privee, | |
photo_principale: photo_principale ?? this.photo_principale, | |
tags: tags ?? this.tags, | |
uploaded: uploaded ?? this.uploaded, | |
); | |
} | |
/* | |
Photo.fromJson(Map<String, dynamic> json) | |
: id_visite = json['id_visite'], | |
id_photo_typologie = json['id_photo_typologie'], | |
image = json['image']; | |
*/ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:mobdr/db/box_user.dart'; | |
import 'package:mobdr/db/box_log.dart'; | |
import 'package:mobdr/db/box_etab.dart'; | |
import 'package:mobdr/db/box_photo_competitor.dart'; | |
import 'package:mobdr/db/box_visit.dart'; | |
import 'package:mobdr/db/box_visit_tag.dart'; | |
import 'package:mobdr/db/box_photo.dart'; | |
import 'package:mobdr/db/box_photo_typology.dart'; | |
import 'model.dart'; | |
import 'objectbox.g.dart'; // created by `flutter pub run build_runner build` | |
/// Provides access to the ObjectBox Store throughout the app. | |
/// | |
/// Create this in the apps main function. | |
class ObjectBox { | |
/// The Store of this app. | |
late final Store store; | |
/// A Box of notes. | |
late final Box<Note> noteBox; | |
/// A Box of user | |
late final Box<User> userBox; | |
/// A Box of etablishment | |
late final Box<Etab> etabBox; | |
/// A Box of etablishment competitor | |
late final Box<EtabCompetitor> etabCompetitorBox; | |
/// A Box of visit | |
late final Box<Visit> visitBox; | |
/// A Box for all visit tag by distributor / language | |
late final Box<VisitTag> visitTagBox; | |
/// A Box of visit Photo | |
late final Box<VisitPhoto> visitPhotoBox; | |
/// A Box of photo typology | |
late final Box<PhotoTypology> PhotoTypologyBox; | |
/// A Box of log. | |
late final Box<Log> logBox; | |
ObjectBox._create(this.store) { | |
noteBox = Box<Note>(store); | |
userBox = Box<User>(store); | |
etabBox = Box<Etab>(store); | |
etabCompetitorBox = Box<EtabCompetitor>(store); | |
visitBox = Box<Visit>(store); | |
visitTagBox = Box<VisitTag>(store); | |
PhotoTypologyBox = Box<PhotoTypology>(store); | |
visitPhotoBox = Box<VisitPhoto>(store); | |
logBox = Box<Log>(store); | |
// Add some demo data if the box is empty. | |
if (noteBox.isEmpty()) { | |
_putDemoData(); | |
} | |
userBox.removeAll(); | |
/* | |
etabBox.removeAll(); | |
etabCompetitorBox.removeAll(); | |
visitBox.removeAll(); | |
visitTagBox.removeAll(); | |
visitPhotoBox.removeAll(); | |
PhotoTypologyBox.removeAll(); | |
*/ | |
// Add some demo data if the box is empty. | |
if (userBox.isEmpty()) { | |
_putUserAdminData(); | |
} | |
} | |
/// Create an instance of ObjectBox to use throughout the app. | |
static Future<ObjectBox> create() async { | |
// Future<Store> openStore() {...} is defined in the generated objectbox.g.dart | |
final store = await openStore(); | |
return ObjectBox._create(store); | |
} | |
void _putDemoData() { | |
final demoNotes = [ | |
Note('Quickly add a note by writing text and pressing Enter'), | |
Note('Delete notes by tapping on one'), | |
Note('Write a demo app for ObjectBox') | |
]; | |
store.runInTransactionAsync(TxMode.write, _putNotesInTx, demoNotes); | |
} | |
void _putUserAdminData() { | |
//addUSer(0, 'root', 'admim', 'admin', ''); | |
} | |
Stream<List<Note>> getNotes() { | |
// Query for all notes, sorted by their date. | |
// https://docs.objectbox.io/queries | |
final builder = noteBox.query().order(Note_.date, flags: Order.descending); | |
// Build and watch the query, | |
// set triggerImmediately to emit the query immediately on listen. | |
return builder | |
.watch(triggerImmediately: true) | |
// Map it to a list of notes to be used by a StreamBuilder. | |
.map((query) => query.find()); | |
} | |
static void _putNotesInTx(Store store, List<Note> notes) => | |
store.box<Note>().putMany(notes); | |
/// Add a note within a transaction. | |
/// | |
/// To avoid frame drops, run ObjectBox operations that take longer than a | |
/// few milliseconds, e.g. putting many objects, in an isolate with its | |
/// own Store instance. | |
/// For this example only a single object is put which would also be fine if | |
/// done here directly. | |
Future<void> addNote(String text) => | |
store.runInTransactionAsync(TxMode.write, _addNoteInTx, text); | |
/// Note: due to [dart-lang/sdk#36983](https://github.com/dart-lang/sdk/issues/36983) | |
/// not using a closure as it may capture more objects than expected. | |
/// These might not be send-able to an isolate. See Store.runAsync for details. | |
static void _addNoteInTx(Store store, String text) { | |
// Perform ObjectBox operations that take longer than a few milliseconds | |
// here. To keep it simple, this example just puts a single object. | |
store.box<Note>().put(Note(text)); | |
} | |
/// USER --------------------------------------------------------------------- | |
/// | |
Future<void> addUSer(int _id_utilisateur, String _login, String _nom, | |
String _prenom, String _photo) => | |
store.runInTransactionAsync( | |
TxMode.write, | |
_addUserInTx, | |
User( | |
id_utilisateur: _id_utilisateur, | |
login: _login, | |
nom: _nom, | |
prenom: _prenom, | |
photo: _photo)); | |
static void _addUserInTx(Store store, _User) { | |
store.box<User>().put(_User); | |
} | |
String getUserAvatar(int id_utilisateur) { | |
final query = | |
userBox.query(User_.id_utilisateur.equals(id_utilisateur)).build(); | |
var p = query.findFirst(); | |
return p!.photo; | |
} | |
/// ETAB --------------------------------------------------------------------- | |
/// | |
// A function that converts a response body into a List<Etab>. | |
List<Etab> parseEtabs(List responseDataList) { | |
final parsed = responseDataList.cast<Map<String, dynamic>>(); | |
return parsed.map<Etab>((json) => Etab.fromJson(json)).toList(); | |
} | |
Future<void> addEtabs(List<dynamic> _listEtabs) => | |
store.runInTransactionAsync( | |
TxMode.write, _addEtabsInTx, parseEtabs(_listEtabs)); | |
static void _addEtabsInTx(Store store, _Etabs) { | |
store.box<Etab>().putMany(_Etabs); | |
} | |
Future<void> addEtab(int _id_etab, String _nom, String _mail, String _tel, | |
String _photo_principale, String _longitude, String _latitude) => | |
store.runInTransactionAsync( | |
TxMode.write, | |
_addEtabInTx, | |
Etab( | |
id_etab: _id_etab, | |
nom: _nom, | |
mail: _mail, | |
tel: _tel, | |
url_photo_principale: _photo_principale, | |
longitude: _longitude, | |
latitude: _latitude)); | |
static void _addEtabInTx(Store store, _Etab) { | |
store.box<Etab>().put(_Etab); | |
} | |
String getEtabPhotoPrincipale(int _id_etab) { | |
final query = etabBox.query(Etab_.id_etab.equals(_id_etab)).build(); | |
var p = query.findFirst(); | |
return p!.url_photo_principale; | |
} | |
int getEtabCount() { | |
return etabBox.count(); | |
} | |
/// ETAB COMPETITOR --------------------------------------------------------------- | |
/// | |
// A function that converts a response body list into a List<Competitor>. | |
List<EtabCompetitor> parseEtabCompetitor(List responseDataList) { | |
final parsed = responseDataList.cast<Map<String, dynamic>>(); | |
return parsed | |
.map<EtabCompetitor>((json) => EtabCompetitor.fromJson(json)) | |
.toList(); | |
} | |
Future<void> addEtabCompetitors(List<dynamic> _listPhotoCompetitors) => | |
store.runInTransactionAsync(TxMode.write, _addEtabCompetitorsInTx, | |
parseEtabCompetitor(_listPhotoCompetitors)); | |
static void _addEtabCompetitorsInTx(Store store, _Competitors) { | |
store.box<EtabCompetitor>().putMany(_Competitors); | |
} | |
Future<void> addEtabCompetitor( | |
int _id_concurrence_lien, int _id_etab, _nom) => | |
store.runInTransactionAsync( | |
TxMode.write, | |
_addEtabCompetitorInTx, | |
EtabCompetitor( | |
id_concurrence_lien: _id_concurrence_lien, | |
id_etab: _id_etab, | |
nom: _nom, | |
)); | |
static void _addEtabCompetitorInTx(Store store, _Competitor) { | |
store.box<EtabCompetitor>().put(_Competitor); | |
} | |
List<EtabCompetitor> getEtabCompetitorList() { | |
final query = etabCompetitorBox | |
.query(EtabCompetitor_.id_etab.equals(1417)) | |
.order(EtabCompetitor_.nom) | |
.build(); | |
final photoCompetitors = query.find(); | |
return photoCompetitors.toList(); | |
} | |
int getEtabCompetitorsCount() { | |
return etabCompetitorBox.count(); | |
} | |
/// VISITE ------------------------------------------------------------------ | |
/// | |
Stream<List<Visit>> getVisitStream() { | |
// Query for all visites, sorted by their date. | |
// https://docs.objectbox.io/queries | |
final builder = | |
visitBox.query().order(Visit_.date_visite, flags: Order.descending); | |
// Build and watch the query, | |
// set triggerImmediately to emit the query immediately on listen. | |
return builder | |
.watch(triggerImmediately: true) | |
// Map it to a list of notes to be used by a StreamBuilder. | |
.map((query) => query.find()); | |
} | |
List<Visit> getAllVisit() { | |
// Query for all visites, sorted by their date. | |
final builder = visitBox | |
.query() | |
.order(Visit_.date_visite, flags: Order.descending) | |
.build(); | |
// Execute the query and return the result. | |
return builder.find(); | |
} | |
List<Visit> getTodayVisit() { | |
final now = DateTime.now(); | |
// Get the start and end of the current day as timestamps. | |
final startOfDay = | |
DateTime(now.year, now.month, now.day).millisecondsSinceEpoch; | |
final endOfDay = DateTime(now.year, now.month, now.day, 23, 59, 59) | |
.millisecondsSinceEpoch; | |
// Query for all visits that occur on the current day and have not been abandoned. | |
final builder = visitBox | |
.query(Visit_.date_visite.between(startOfDay, endOfDay) & | |
Visit_.abandon.equals(0)) | |
.order(Visit_.date_visite, flags: Order.descending) | |
.build(); | |
// Execute the query and return the result. | |
return builder.find(); | |
} | |
List<Visit> getPreviousVisit() { | |
// Get the previous date at midnight. | |
final now = DateTime.now(); | |
final midnight = DateTime(now.year, now.month, now.day); | |
// Convert the date to an integer. | |
final millisecondsSinceEpoch = midnight.millisecondsSinceEpoch; | |
// Query for all visits that match the date and abandonment criteria, sorted by their date. | |
final builder = visitBox | |
.query( | |
Visit_.date_visite.lessThan(millisecondsSinceEpoch) & | |
Visit_.abandon.equals(0), | |
) | |
.order(Visit_.date_visite, flags: Order.descending) | |
.build(); | |
// Execute the query and return the result. | |
return builder.find(); | |
} | |
// A function that converts a response body list into a List<Visite>. | |
List<Visit> parseVisit(List responseDataList) { | |
final parsed = responseDataList.cast<Map<String, dynamic>>(); | |
return parsed.map<Visit>((json) => Visit.fromJson(json)).toList(); | |
} | |
// TODO : Enregistre urlphotoprincipale sur le disque pour l'avoir en cache ? | |
Future<void> addVisits(List<dynamic> _listVisits) => | |
store.runInTransactionAsync( | |
TxMode.write, _addVisitsInTx, parseVisit(_listVisits)); | |
static void _addVisitsInTx(Store store, _Visits) { | |
store.box<Visit>().putMany(_Visits); | |
} | |
Future<void> addVisit( | |
int _id_visite, | |
DateTime _date_visite, | |
DateTime _date_debut, | |
DateTime _date_fin, | |
DateTime _date_validation, | |
String _type_visite, | |
String _title, | |
bool _allDay, | |
String _start, | |
int _id_distrib_visite, | |
int _id_etab, | |
int _abandon, | |
String _end, | |
String _url_photo_principale, | |
String _langage) => | |
store.runInTransactionAsync( | |
TxMode.write, | |
_addVisitInTx, | |
Visit( | |
id_visite: _id_visite, | |
date_visite: _date_visite, | |
date_debut: _date_debut, | |
date_fin: _date_fin, | |
date_validation: _date_validation, | |
type_visite: _type_visite, | |
title: _title, | |
allDay: _allDay, | |
id_distrib_visite: _id_distrib_visite, | |
id_etab: _id_etab, | |
abandon: _abandon, | |
url_photo_principale: _url_photo_principale, | |
langage: _langage)); | |
static void _addVisitInTx(Store store, _Visite) { | |
store.box<Visit>().put(_Visite); | |
} | |
Future<void> syncVisits(List<dynamic> _listVisits) async { | |
final toSyncId_visite = | |
_listVisits.map<int>((v) => v['id_visite']).toList(); | |
final existingVisitsQuery = | |
visitBox.query(Visit_.id_visite.oneOf(toSyncId_visite)); | |
final existingVisits = existingVisitsQuery.build().find(); | |
final existingVisitIds = | |
existingVisits.map<int>((v) => v.id_visite).toList(); | |
final newVisits = _listVisits | |
.where((v) => !existingVisitIds.contains(v['id_visite'])) | |
.toList(); | |
final updatedVisits = _listVisits | |
.where((v) => existingVisitIds.contains(v['id_visite'])) | |
.toList(); | |
newVisits.forEach((v) { | |
visitBox.put(Visit.fromJson(v)); | |
}); | |
updatedVisits.forEach((v) { | |
final existingVisit = | |
existingVisits.firstWhere((ev) => ev.id_visite == v['id_visite']); | |
final updatedVisit = Visit.fromJson(v)..id = existingVisit.id; | |
visitBox.put(updatedVisit); | |
}); | |
} | |
int getVisitCount() { | |
return visitBox.count(); | |
} | |
/// VISIT TAG --------------------------------------------------------------- | |
/// | |
// converts a response body list into a List<VisitTag>. | |
List<VisitTag> parseVisitTags(List responseDataList) { | |
final parsed = responseDataList.cast<Map<String, dynamic>>(); | |
return parsed.map<VisitTag>((json) => VisitTag.fromJson(json)).toList(); | |
} | |
Future<void> addVisitTags(List<dynamic> _listVisitTags) => | |
store.runInTransactionAsync( | |
TxMode.write, _addVisitTagsInTx, parseVisitTags(_listVisitTags)); | |
static void _addVisitTagsInTx(Store store, _VisitTags) { | |
store.box<VisitTag>().putMany(_VisitTags); | |
} | |
/// save new tag for a distributor / langage | |
Future<void> addVisitTag( | |
int id_visite_tag, | |
int _id_distrib, | |
String _libelle, | |
String _langage, | |
) => | |
store.runInTransactionAsync( | |
TxMode.write, | |
_addVisitTagInTx, | |
VisitTag( | |
id_visite_tag: id_visite_tag, | |
id_distrib: _id_distrib, | |
libelle: _libelle, | |
langage: _langage, | |
)); | |
static void _addVisitTagInTx(Store store, _VisitTag) { | |
store.box<VisitTag>().put(_VisitTag); | |
} | |
List<String> getVisitTagsLabels(int distribId, String langage) { | |
final query = visitTagBox | |
.query(VisitTag_.id_distrib.equals(distribId) & | |
VisitTag_.langage.equals(langage)) | |
//.order(VisitTag_.libelle) | |
.build(); | |
PropertyQuery<String> pq = query.property(VisitTag_.libelle); | |
pq.distinct = true; | |
pq.caseSensitive = false; | |
return pq.find(); | |
} | |
int getVisitTagCount() { | |
return visitTagBox.count(); | |
} | |
/// PHOTO -------------------------------------------------------------------- | |
/// | |
/// | |
Future<List<VisitPhoto>> addPhotos(List<VisitPhoto> photos) async { | |
final addedPhotos = await visitPhotoBox.putAndGetManyAsync(photos); | |
return addedPhotos; | |
} | |
Future<void> addPhoto( | |
int id_visite, int id_photo_typologie, String image_name) => | |
store.runInTransactionAsync( | |
TxMode.write, | |
_addPhotoInTx, | |
VisitPhoto( | |
id_visite: id_visite, | |
id_photo_typologie: id_photo_typologie, | |
image_name: image_name)); | |
static void _addPhotoInTx(Store store, _Photo) { | |
// Perform ObjectBox operations that take longer than a few milliseconds | |
// here. To keep it simple, this example just puts a single object. | |
store.box<VisitPhoto>().put(_Photo); | |
} | |
List<VisitPhoto> getAllVisitTypologyPhotos( | |
int id_visite, int _id_photo_typologie) { | |
// Query for all photos, sorted by their date. | |
// https://docs.objectbox.io/queries | |
final query = visitPhotoBox | |
.query(VisitPhoto_.id_visite.equals(id_visite) & | |
VisitPhoto_.id_photo_typologie.equals(_id_photo_typologie)) | |
.order(VisitPhoto_.date_photo, flags: Order.descending) | |
.build(); | |
return query.find(); | |
} | |
/// Retrieves a Photo object from the ObjectBox database with the specified ID. | |
/// | |
/// Parameters: | |
/// id_photo: The ID of the photo to retrieve. | |
/// | |
/// Returns: | |
/// A Photo object, or null if no object is found. | |
VisitPhoto? getPhotoById(int id_photo) { | |
final photo = visitPhotoBox.get(id_photo); | |
return photo; | |
} | |
Stream<List<VisitPhoto>> getPhotos() { | |
// Query for all photos, sorted by their date. | |
// https://docs.objectbox.io/queries | |
final builder = visitPhotoBox | |
.query() | |
.order(VisitPhoto_.date_photo, flags: Order.descending); | |
// Build and watch the query, | |
// set triggerImmediately to emit the query immediately on listen. | |
return builder | |
.watch(triggerImmediately: true) | |
// Map it to a list of notes to be used by a StreamBuilder. | |
.map((query) => query.find()); | |
} | |
void delPhoto(String _name) { | |
final query = | |
visitPhotoBox.query(VisitPhoto_.image_name.equals(_name)).build(); | |
final results = query.find(); | |
if (results.isNotEmpty) { | |
final photoToDelete = results.first; | |
visitPhotoBox.remove(photoToDelete.id); | |
} | |
} | |
int getVisitPhotoCount(int _id_visite) { | |
final builder = | |
visitPhotoBox.query(VisitPhoto_.id_visite.equals(_id_visite)).build(); | |
return builder.count(); | |
} | |
int getVisitTypologiePhotoCount(int _id_visite, int _id_photo_typologie) { | |
final builder = visitPhotoBox | |
.query(VisitPhoto_.id_visite.equals(_id_visite) & | |
VisitPhoto_.id_photo_typologie.equals(_id_photo_typologie)) | |
.build(); | |
return builder.count(); | |
} | |
Future<void> putPhotoTypologie(int photoId, int typologieId) async { | |
final photo = visitPhotoBox.get(photoId); | |
if (photo != null) { | |
final updatedPhoto = photo.copyWith(id_photo_typologie: typologieId); | |
await visitPhotoBox.putAsync(updatedPhoto); | |
} | |
} | |
Future<void> putPhotoTags(int photoId, List<String> tags) async { | |
final photo = visitPhotoBox.get(photoId); | |
if (photo != null) { | |
final updatedPhoto = photo.copyWith(tags: tags.join(",")); | |
await visitPhotoBox.putAsync(updatedPhoto); | |
} | |
} | |
Future<void> putPhotoVisibilities( | |
int photoId, List<String> visibilities) async { | |
final photo = visitPhotoBox.get(photoId); | |
if (photo != null) { | |
final updatedPhoto = photo.copyWith( | |
photo_principale: visibilities.contains('principal') ? 1 : 0, | |
photo_privee: visibilities.contains('private') ? 1 : 0); | |
await visitPhotoBox.putAsync(updatedPhoto); | |
} | |
} | |
/* remettre les principal à zero | |
final queryBuilder = box.query(VisitPhoto_.Visit_id.equals(idVisite) & VisitPhoto_.photo_principale.equals(1)); | |
final updatedPhotos = queryBuilder.build().find(); | |
updatedPhotos.forEach((photo) { | |
photo.photo_principale = 0; | |
}); | |
box.putMany(updatedPhotos); | |
*/ | |
/// PHOTO TYPOLOGY ----------------------------------------------------------- | |
/// | |
List<PhotoTypology> parsePhotoTypologies(List responseDataList) { | |
final parsed = responseDataList.cast<Map<String, dynamic>>(); | |
return parsed | |
.map<PhotoTypology>((json) => PhotoTypology.fromJson(json)) | |
.toList(); | |
} | |
Future<void> addPhotoTypologies(List<dynamic> _listPhotoTypologies) => | |
store.runInTransactionAsync(TxMode.write, _addPhotoTypologiesInTx, | |
parsePhotoTypologies(_listPhotoTypologies)); | |
static void _addPhotoTypologiesInTx(Store store, _PhotoTypologies) { | |
store.box<PhotoTypology>().putMany(_PhotoTypologies); | |
} | |
Future<void> addPhotoTypology( | |
int id_photo_typologie, String libelle, int ordre) => | |
store.runInTransactionAsync( | |
TxMode.write, | |
_addPhotoTypologyInTx, | |
PhotoTypology( | |
id_photo_typologie: id_photo_typologie, | |
libelle: libelle, | |
ordre: ordre)); | |
static void _addPhotoTypologyInTx(Store store, _PhotoTypology) { | |
// Perform ObjectBox operations that take longer than a few milliseconds | |
// here. To keep it simple, this example just puts a single object. | |
store.box<PhotoTypology>().put(_PhotoTypology); | |
} | |
List<String> getPhotoTypologiesLabels() { | |
final query = PhotoTypologyBox.query().order(PhotoTypology_.ordre).build(); | |
final photoTypologies = query.find(); | |
return photoTypologies | |
.map((photoTypology) => photoTypology.libelle) | |
.toList(); | |
} | |
List<PhotoTypology> getPhotoTypologiesList() { | |
final query = PhotoTypologyBox.query().order(PhotoTypology_.ordre).build(); | |
final photoTypologies = query.find(); | |
return photoTypologies.toList(); | |
} | |
Stream<List<PhotoTypology>> getPhotoTypologies() { | |
// Query for all Typologies, sorted by their order. | |
// https://docs.objectbox.io/queries | |
final builder = PhotoTypologyBox.query().order(PhotoTypology_.ordre); | |
// Build and watch the query, | |
// set triggerImmediately to emit the query immediately on listen. | |
return builder | |
.watch(triggerImmediately: true) | |
// Map it to a list of notes to be used by a StreamBuilder. | |
.map((query) => query.find()); | |
} | |
/// LOG ---------------------------------------------------------------------- | |
/// | |
Future<void> addLog(String type, String module, String libelle, int duree) => | |
store.runInTransactionAsync( | |
TxMode.write, | |
_addLogInTx, | |
Log( | |
type, | |
module, | |
libelle, | |
duree, | |
)); | |
static void _addLogInTx(Store store, _Log) { | |
// Perform ObjectBox operations that take longer than a few milliseconds | |
// here. To keep it simple, this example just puts a single object. | |
store.box<Log>().put(_Log); | |
} | |
Stream<List<Log>> getLogs() { | |
// Query for all logs, sorted by their date. | |
// https://docs.objectbox.io/queries | |
final builder = logBox.query().order(Log_.date, flags: Order.descending); | |
// Build and watch the query, | |
// set triggerImmediately to emit the query immediately on listen. | |
return builder | |
.watch(triggerImmediately: true) | |
// Map it to a list of notes to be used by a StreamBuilder. | |
.map((query) => query.find()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
import 'package:mobdr/main.dart'; | |
import 'package:mobdr/config/constant.dart'; | |
import 'package:mobdr/config/global_style.dart'; | |
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 | |
_TabVisitListPageState createState() => _TabVisitListPageState(); | |
} | |
class _TabVisitListPageState extends State<TabVisitListPage> | |
with AutomaticKeepAliveClientMixin { | |
// _listKey is used for AnimatedList | |
final GlobalKey<AnimatedListState> _listKey = GlobalKey(); | |
// keep the state to do not refresh when switch navbar | |
@override | |
bool get wantKeepAlive => true; | |
bool _isLoading = true; | |
String _errorMessage = ''; | |
late List<VisiteModel> modelData = []; | |
late StreamSubscription subVisitPhotoCountEvent; | |
@override | |
void initState() { | |
super.initState(); | |
// Listen particular event | |
subVisitPhotoCountEvent = eventBus.on<VisitPhotoCountEvent>().listen((e) { | |
setState(() { | |
for (int i = 0; i < modelData.length; i++) { | |
if (modelData[i].id_visite == e.id_visite) { | |
modelData[i].photoCount = e.photoCount; | |
break; | |
} | |
} | |
}); | |
}); | |
loadData().then((_) { | |
setState(() { | |
_isLoading = false; | |
}); | |
}); | |
} | |
@override | |
void dispose() { | |
subVisitPhotoCountEvent.cancel(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
// if we used AutomaticKeepAliveClientMixin, we must call super.build(context); | |
super.build(context); | |
final double boxImageSize = (MediaQuery.of(context).size.width / 4); | |
if (_isLoading) { | |
return Center(child: CircularProgressIndicator()); | |
} else if (modelData.isEmpty) { | |
return Center( | |
child: Text('No visits to synchronise.'), | |
); | |
} | |
return Scaffold( | |
appBar: AppBar( | |
iconTheme: IconThemeData( | |
color: GlobalStyle.appBarIconThemeColor, | |
), | |
elevation: GlobalStyle.appBarElevation, | |
title: Text( | |
'Visit List', | |
style: GlobalStyle.appBarTitle, | |
), | |
backgroundColor: GlobalStyle.appBarBackgroundColor, | |
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle), | |
body: Column(children: [ | |
Flexible( | |
child: AnimatedList( | |
key: _listKey, | |
initialItemCount: modelData.length, | |
physics: AlwaysScrollableScrollPhysics(), | |
itemBuilder: (context, index, animation) { | |
return _buildVisitelistCard( | |
modelData[index], boxImageSize, animation, index); | |
}, | |
)), | |
Container( | |
padding: EdgeInsets.all(12), | |
decoration: BoxDecoration( | |
color: Colors.white, | |
boxShadow: [ | |
BoxShadow( | |
color: Colors.grey, | |
offset: Offset(0.0, 1.0), //(x,y) | |
blurRadius: 2.0, | |
), | |
], | |
), | |
child: Row( | |
children: [ | |
Container( | |
child: GestureDetector( | |
onTap: () { | |
// TODO functionality to be implemented | |
/*` | |
Navigator.push( | |
context, | |
MaterialPageRoute( | |
builder: (context) => ChatUsPage())); | |
*/ | |
}, | |
child: ClipOval( | |
child: Container( | |
color: SOFT_BLUE, | |
padding: EdgeInsets.all(9), | |
child: Icon(Icons.filter_list, | |
color: Colors.white, size: 16)), | |
), | |
), | |
), | |
SizedBox( | |
width: 10, | |
), | |
Expanded( | |
child: GestureDetector( | |
onTap: () {}, | |
child: Container( | |
alignment: Alignment.center, | |
padding: EdgeInsets.fromLTRB(12, 8, 12, 8), | |
margin: EdgeInsets.only(right: 8), | |
decoration: BoxDecoration( | |
color: Colors.white, | |
border: Border.all(width: 1, color: SOFT_BLUE), | |
borderRadius: BorderRadius.all(Radius.circular( | |
10) // <--- border radius here | |
)), | |
child: Text('Synchronize ALL', | |
style: TextStyle( | |
color: SOFT_BLUE, fontWeight: FontWeight.bold)), | |
), | |
), | |
), | |
], | |
), | |
) | |
])); | |
} | |
Widget _buildVisitelistCard( | |
VisiteModel visiteData, boxImageSize, animation, index) { | |
return SizeTransition( | |
sizeFactor: animation, | |
child: GestureDetector( | |
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)); | |
Navigator.push(context, route); | |
}, | |
child: Container( | |
margin: EdgeInsets.fromLTRB(12, 6, 12, 0), | |
child: Card( | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(10), | |
), | |
elevation: 2, | |
color: Colors.white, | |
child: Container( | |
margin: EdgeInsets.all(8), | |
child: Column( | |
children: [ | |
Row( | |
mainAxisAlignment: MainAxisAlignment.start, | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: <Widget>[ | |
ClipRRect( | |
borderRadius: BorderRadius.all(Radius.circular(10)), | |
child: buildCacheNetworkImage( | |
width: boxImageSize, | |
height: boxImageSize, | |
url: visiteData.image, | |
), | |
), | |
SizedBox( | |
width: 10, | |
), | |
Expanded( | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Container( | |
margin: EdgeInsets.only(top: 5), | |
child: Row( | |
children: [ | |
Text(visiteData.name, | |
style: GlobalStyle.productPrice) | |
], | |
), | |
), | |
Container(height: 8), | |
Text( | |
visiteData.date, | |
style: GlobalStyle.productSale, | |
), | |
Container( | |
margin: EdgeInsets.only(top: 5), | |
child: Text( | |
'${visiteData.photoCount} Photo(s)', | |
style: GlobalStyle.productPrice, | |
), | |
), | |
Container(height: 8), | |
Container( | |
margin: EdgeInsets.only(top: 5), | |
child: Row( | |
children: [ | |
Icon( | |
Icons.store, | |
color: SOFT_GREY, | |
size: 20, | |
), | |
Text( | |
' ' + visiteData.type_visite, | |
style: GlobalStyle.productName.copyWith( | |
fontSize: 13, | |
), | |
maxLines: 1, | |
overflow: TextOverflow.ellipsis, | |
) | |
], | |
), | |
), | |
], | |
), | |
) | |
], | |
), | |
Container( | |
margin: EdgeInsets.only(top: 12), | |
child: Row( | |
children: [ | |
Expanded( | |
child: (visiteData.photoCount == 0) | |
? TextButton( | |
style: ButtonStyle( | |
minimumSize: | |
MaterialStateProperty.all(Size(0, 30)), | |
backgroundColor: MaterialStateProperty | |
.resolveWith<Color>( | |
(Set<MaterialState> states) => | |
Colors.grey[300]!, | |
), | |
overlayColor: MaterialStateProperty.all( | |
Colors.transparent), | |
shape: MaterialStateProperty.all( | |
RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(5.0), | |
)), | |
), | |
onPressed: () {}, | |
child: Text( | |
'Synchronize', | |
style: TextStyle( | |
color: Colors.grey[600], | |
fontWeight: FontWeight.bold, | |
fontSize: 13), | |
textAlign: TextAlign.center, | |
)) | |
: OutlinedButton( | |
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)), | |
overlayColor: MaterialStateProperty.all( | |
Colors.transparent), | |
shape: MaterialStateProperty.all( | |
RoundedRectangleBorder( | |
borderRadius: | |
BorderRadius.circular(5.0), | |
)), | |
side: MaterialStateProperty.all( | |
BorderSide( | |
color: SOFT_BLUE, width: 1.0), | |
)), | |
child: Text( | |
'Synchronize', | |
style: TextStyle( | |
color: SOFT_BLUE, | |
fontWeight: FontWeight.bold, | |
fontSize: 13), | |
textAlign: TextAlign.center, | |
)), | |
), | |
], | |
), | |
) | |
], | |
), | |
), | |
), | |
), | |
), | |
); | |
} | |
/// Initializes data when the page loads. | |
Future<void> loadData() async { | |
try { | |
// data initialisation with today visits | |
modelData = await VisiteModel.getAllVisit(); | |
} catch (e) { | |
// set errorMessage for debug | |
_errorMessage = 'Error loading visits : $e'; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
), | |
), | |
], | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment