diff --git a/assets/images/simulator.jpeg b/assets/images/simulator.jpeg new file mode 100644 index 0000000..fb272dd Binary files /dev/null and b/assets/images/simulator.jpeg differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a1618e5..8a9a4b2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,8 @@ PODS: + - camera_avfoundation (0.0.1): + - Flutter + - device_info_plus (0.0.1): + - Flutter - Flutter (1.0.0) - fluttertoast (0.0.2): - Flutter @@ -6,6 +10,8 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - image_picker_ios (0.0.1): + - Flutter - ObjectBox (1.8.1) - objectbox_flutter_libs (0.0.1): - Flutter @@ -15,6 +21,8 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - permission_handler_apple (9.0.4): + - Flutter - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -22,15 +30,22 @@ PODS: - Flutter - FMDB (>= 2.7.5) - Toast (4.0.0) + - wakelock (0.0.1): + - Flutter DEPENDENCIES: + - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) + - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) + - wakelock (from `.symlinks/plugins/wakelock/ios`) SPEC REPOS: trunk: @@ -39,32 +54,47 @@ SPEC REPOS: - Toast EXTERNAL SOURCES: + camera_avfoundation: + :path: ".symlinks/plugins/camera_avfoundation/ios" + device_info_plus: + :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" objectbox_flutter_libs: :path: ".symlinks/plugins/objectbox_flutter_libs/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/ios" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" + wakelock: + :path: ".symlinks/plugins/wakelock/ios" SPEC CHECKSUMS: + camera_avfoundation: 3125e8cd1a4387f6f31c6c63abb8a55892a9eeeb + device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5 ObjectBox: a7900d5335218cd437cbc080b7ccc38a5211f7b4 objectbox_flutter_libs: 61d74196d924fbc773da5f5757d1e9fab7b3cc78 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 + wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f PODFILE CHECKSUM: b634fd49380cdd3837626153fb977533b1916433 diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 1974f7b..f34142f 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -48,7 +48,10 @@ UIApplicationSupportsIndirectInputEvents NSPhotoLibraryUsageDescription + Cette application a besoin de votre autorisation pour accéder à votre galerie photo afin que vous puissiez sélectionner des photos pour votre profil. NSCameraUsageDescription + Cette application a besoin de votre autorisation pour accéder à votre caméra afin que vous puissiez prendre des photos pour votre profil. NSMicrophoneUsageDescription + Cette application a besoin de votre autorisation pour accéder à votre microphone afin que vous puissiez enregistrer des messages audio pour envoyer à vos amis. diff --git a/lib/db/box_photo.dart b/lib/db/box_photo.dart index 5b24a28..8fc6b16 100644 --- a/lib/db/box_photo.dart +++ b/lib/db/box_photo.dart @@ -1,7 +1,9 @@ import 'package:objectbox/objectbox.dart'; -import 'package:mobdr/objectbox.g.dart'; import 'package:intl/intl.dart'; +import 'package:mobdr/objectbox.g.dart'; +import 'package:mobdr/service/shared_prefs.dart'; + // ignore_for_file: public_member_api_docs @Entity() @@ -12,7 +14,6 @@ class Photo { int id_visite; int id_photo_typologie; - String image; String image_name; DateTime date_photo; int id_photo_mp4; @@ -25,7 +26,6 @@ class Photo { {this.id = 0, required this.id_visite, required this.id_photo_typologie, - required this.image, required this.image_name, this.id_photo_mp4 = 0, this.photo_privee = 0, @@ -35,6 +35,15 @@ class 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); Photo copyWith({ @@ -54,7 +63,6 @@ class Photo { id: id ?? this.id, id_visite: id_visite ?? this.id_visite, id_photo_typologie: id_photo_typologie ?? this.id_photo_typologie, - image: image ?? this.image, image_name: image_name ?? this.image_name, date_photo: date_photo ?? this.date_photo, id_photo_mp4: id_photo_mp4 ?? this.id_photo_mp4, diff --git a/lib/db/box_visite.dart b/lib/db/box_visite.dart index cec182f..59bc27e 100644 --- a/lib/db/box_visite.dart +++ b/lib/db/box_visite.dart @@ -1,4 +1,3 @@ -import 'package:intl/intl.dart'; import 'package:objectbox/objectbox.dart'; import 'package:mobdr/objectbox.g.dart'; import 'package:mobdr/config/constant.dart'; @@ -20,6 +19,7 @@ class Visite { int id_etab; int abandon; String url_photo_principale; + String langage; Visite( {this.id = 0, @@ -31,7 +31,8 @@ class Visite { required this.id_distrib_visite, required this.id_etab, required this.abandon, - required this.url_photo_principale}); + required this.url_photo_principale, + required this.langage}); Visite.fromJson(Map json) : id_visite = json['id_visite'], @@ -43,5 +44,6 @@ class Visite { id_etab = json['id_etab'], abandon = json['abandon'], url_photo_principale = - ApiConstants.baseUrl + json['url_photo_principale']; + ApiConstants.baseUrl + json['url_photo_principale'], + langage = 'fr'; } diff --git a/lib/main.dart b/lib/main.dart index 3a6a67f..d879d1d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,17 +1,11 @@ // ignore_for_file: prefer_const_constructors import 'dart:ui'; +import 'dart:developer' as developer; import 'dart:io'; -import 'package:mobdr/config/constant.dart'; - -import 'package:mobdr/cubit/language/language_cubit.dart'; -import 'package:mobdr/cubit/language/app_localizations.dart'; -import 'package:mobdr/cubit/language/initial_language.dart'; - -import 'package:mobdr/service/shared_prefs.dart'; - -import 'package:mobdr/ui/splash_screen.dart'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -22,9 +16,19 @@ import 'package:path_provider/path_provider.dart'; import 'objectbox.dart'; import 'package:wakelock/wakelock.dart'; +import 'package:mobdr/config/constant.dart'; + +import 'package:mobdr/cubit/language/language_cubit.dart'; +import 'package:mobdr/cubit/language/app_localizations.dart'; +import 'package:mobdr/cubit/language/initial_language.dart'; +import 'package:mobdr/service/shared_prefs.dart'; +import 'package:mobdr/ui/splash_screen.dart'; + /// Provides access to the ObjectBox Store throughout the app. late ObjectBox objectbox; +final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); + Future main() async { // This is required so ObjectBox can get the application directory // to store the database in. @@ -62,6 +66,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { initDirectories(); + initPlatformState(); // Initialize all bloc provider used on this entire application here return MultiBlocProvider( @@ -139,5 +144,193 @@ class MyApp extends StatelessWidget { print( 'The "photos" directory has been created in the documents directory.'); } + + // save directories into shared Prefs + SharedPrefs().documentsDir = documentsDir.path; + SharedPrefs().photosDir = photosDir.path; + + // Get a list of all files in the "photos" directory + final List files = await photosDir.list().toList(); + + // Print out the names of the files in the directory + for (FileSystemEntity file in files) { + print('File name: ${file.path.split('/').last}'); + } + } + + Future initPlatformState() async { + var deviceData = {}; + + try { + if (kIsWeb) { + deviceData = _readWebBrowserInfo(await deviceInfoPlugin.webBrowserInfo); + } else { + if (Platform.isAndroid) { + deviceData = + _readAndroidBuildData(await deviceInfoPlugin.androidInfo); + } else if (Platform.isIOS) { + deviceData = _readIosDeviceInfo(await deviceInfoPlugin.iosInfo); + } else if (Platform.isLinux) { + deviceData = _readLinuxDeviceInfo(await deviceInfoPlugin.linuxInfo); + } else if (Platform.isMacOS) { + deviceData = _readMacOsDeviceInfo(await deviceInfoPlugin.macOsInfo); + } else if (Platform.isWindows) { + deviceData = + _readWindowsDeviceInfo(await deviceInfoPlugin.windowsInfo); + } + } + + // save if we are on a simulator + if (deviceData['isPhysicalDevice'] == false) { + SharedPrefs().isSimulator = true; + } + + print(deviceData); + } on PlatformException { + deviceData = { + 'Error:': 'Failed to get platform version.' + }; + } + } + + Map _readAndroidBuildData(AndroidDeviceInfo build) { + return { + 'version.securityPatch': build.version.securityPatch, + 'version.sdkInt': build.version.sdkInt, + 'version.release': build.version.release, + 'version.previewSdkInt': build.version.previewSdkInt, + 'version.incremental': build.version.incremental, + 'version.codename': build.version.codename, + 'version.baseOS': build.version.baseOS, + 'board': build.board, + 'bootloader': build.bootloader, + 'brand': build.brand, + 'device': build.device, + 'display': build.display, + 'fingerprint': build.fingerprint, + 'hardware': build.hardware, + 'host': build.host, + 'id': build.id, + 'manufacturer': build.manufacturer, + 'model': build.model, + 'product': build.product, + 'supported32BitAbis': build.supported32BitAbis, + 'supported64BitAbis': build.supported64BitAbis, + 'supportedAbis': build.supportedAbis, + 'tags': build.tags, + 'type': build.type, + 'isPhysicalDevice': build.isPhysicalDevice, + 'systemFeatures': build.systemFeatures, + 'displaySizeInches': + ((build.displayMetrics.sizeInches * 10).roundToDouble() / 10), + 'displayWidthPixels': build.displayMetrics.widthPx, + 'displayWidthInches': build.displayMetrics.widthInches, + 'displayHeightPixels': build.displayMetrics.heightPx, + 'displayHeightInches': build.displayMetrics.heightInches, + 'displayXDpi': build.displayMetrics.xDpi, + 'displayYDpi': build.displayMetrics.yDpi, + 'serialNumber': build.serialNumber, + }; + } + + Map _readIosDeviceInfo(IosDeviceInfo data) { + return { + 'name': data.name, + 'systemName': data.systemName, + 'systemVersion': data.systemVersion, + 'model': data.model, + 'localizedModel': data.localizedModel, + 'identifierForVendor': data.identifierForVendor, + 'isPhysicalDevice': data.isPhysicalDevice, + 'utsname.sysname:': data.utsname.sysname, + 'utsname.nodename:': data.utsname.nodename, + 'utsname.release:': data.utsname.release, + 'utsname.version:': data.utsname.version, + 'utsname.machine:': data.utsname.machine, + }; + } + + Map _readLinuxDeviceInfo(LinuxDeviceInfo data) { + return { + 'name': data.name, + 'version': data.version, + 'id': data.id, + 'idLike': data.idLike, + 'versionCodename': data.versionCodename, + 'versionId': data.versionId, + 'prettyName': data.prettyName, + 'buildId': data.buildId, + 'variant': data.variant, + 'variantId': data.variantId, + 'machineId': data.machineId, + }; + } + + Map _readWebBrowserInfo(WebBrowserInfo data) { + return { + 'browserName': describeEnum(data.browserName), + 'appCodeName': data.appCodeName, + 'appName': data.appName, + 'appVersion': data.appVersion, + 'deviceMemory': data.deviceMemory, + 'language': data.language, + 'languages': data.languages, + 'platform': data.platform, + 'product': data.product, + 'productSub': data.productSub, + 'userAgent': data.userAgent, + 'vendor': data.vendor, + 'vendorSub': data.vendorSub, + 'hardwareConcurrency': data.hardwareConcurrency, + 'maxTouchPoints': data.maxTouchPoints, + }; + } + + Map _readMacOsDeviceInfo(MacOsDeviceInfo data) { + return { + 'computerName': data.computerName, + 'hostName': data.hostName, + 'arch': data.arch, + 'model': data.model, + 'kernelVersion': data.kernelVersion, + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'patchVersion': data.patchVersion, + 'osRelease': data.osRelease, + 'activeCPUs': data.activeCPUs, + 'memorySize': data.memorySize, + 'cpuFrequency': data.cpuFrequency, + 'systemGUID': data.systemGUID, + }; + } + + Map _readWindowsDeviceInfo(WindowsDeviceInfo data) { + return { + 'numberOfCores': data.numberOfCores, + 'computerName': data.computerName, + 'systemMemoryInMegabytes': data.systemMemoryInMegabytes, + 'userName': data.userName, + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'buildNumber': data.buildNumber, + 'platformId': data.platformId, + 'csdVersion': data.csdVersion, + 'servicePackMajor': data.servicePackMajor, + 'servicePackMinor': data.servicePackMinor, + 'suitMask': data.suitMask, + 'productType': data.productType, + 'reserved': data.reserved, + 'buildLab': data.buildLab, + 'buildLabEx': data.buildLabEx, + 'digitalProductId': data.digitalProductId, + 'displayVersion': data.displayVersion, + 'editionId': data.editionId, + 'installDate': data.installDate, + 'productId': data.productId, + 'productName': data.productName, + 'registeredOwner': data.registeredOwner, + 'releaseId': data.releaseId, + 'deviceId': data.deviceId, + }; } } diff --git a/lib/model/photo_model.dart b/lib/model/photo_model.dart index 80b06cb..f59d486 100644 --- a/lib/model/photo_model.dart +++ b/lib/model/photo_model.dart @@ -2,13 +2,11 @@ class PhotoModel { late int id; late int id_visite; late int id_photo_typologie; - late String image; late String image_name; PhotoModel( {required this.id, required this.id_visite, required this.id_photo_typologie, - required this.image, required this.image_name}); } diff --git a/lib/model/visite_model.dart b/lib/model/visite_model.dart index 1ae3425..f32a88b 100644 --- a/lib/model/visite_model.dart +++ b/lib/model/visite_model.dart @@ -2,32 +2,25 @@ import 'package:mobdr/main.dart'; class VisiteModel { late int id; + late int id_distrib; + late int id_visite; late String name; - late int photo; + late int photoCount; late String date; late String image; late String type_visite; - late double price; - late double rating; - late int review; - late int sale; - late int stock; - late String location; + late String langage; - VisiteModel({ - required this.id, - required this.name, - required this.photo, - required this.date, - required this.image, - required this.type_visite, - required this.price, - required this.rating, - required this.review, - required this.sale, - required this.stock, - required this.location, - }); + VisiteModel( + {required this.id, + required this.id_distrib, + required this.id_visite, + required this.name, + required this.photoCount, + required this.date, + required this.image, + required this.type_visite, + required this.langage}); static Future> getAllVisites() async { // Retrieve all visits from the database using the getAllVisites() method @@ -36,18 +29,15 @@ class VisiteModel { // Map each retrieved visit to VisiteModel final visiteModels = visites .map((visite) => VisiteModel( + id_distrib: visite.id_distrib_visite, id: visite.id, + id_visite: visite.id_visite, name: visite.title, - photo: 1, + photoCount: objectbox.getVisitPhotoCount(visite.id_visite), date: visite.date_visite.toString(), image: visite.url_photo_principale, type_visite: visite.type_visite, - price: 0, - rating: 0, - review: 0, - sale: 0, - stock: 10, - location: "")) + langage: visite.langage)) .toList(); // Return the list of VisiteModel diff --git a/lib/objectbox-model.json b/lib/objectbox-model.json index d6ef092..b98ec04 100644 --- a/lib/objectbox-model.json +++ b/lib/objectbox-model.json @@ -195,7 +195,7 @@ }, { "id": "7:8290500625256822711", - "lastPropertyId": "13:1900114397693432703", + "lastPropertyId": "14:5831680857919010234", "name": "Visite", "properties": [ { @@ -248,6 +248,11 @@ "id": "13:1900114397693432703", "name": "url_photo_principale", "type": 9 + }, + { + "id": "14:5831680857919010234", + "name": "langage", + "type": 9 } ], "relations": [] @@ -332,11 +337,6 @@ "name": "uploaded", "type": 6 }, - { - "id": "15:1865824860595482227", - "name": "image", - "type": 9 - }, { "id": "16:539065583624712715", "name": "image_name", @@ -404,7 +404,8 @@ 5293139139799032553, 2141346538986140281, 7877546811840884522, - 3784190804330297742 + 3784190804330297742, + 1865824860595482227 ], "retiredRelationUids": [], "version": 1 diff --git a/lib/objectbox.dart b/lib/objectbox.dart index e5c7b44..83db299 100644 --- a/lib/objectbox.dart +++ b/lib/objectbox.dart @@ -8,9 +8,6 @@ import 'package:mobdr/db/box_photo.dart'; import 'package:mobdr/db/box_photo_typology.dart'; import 'model.dart'; -import 'package:mobdr/model/visite_model.dart'; -import 'package:mobdr/model/photo_model.dart'; - import 'objectbox.g.dart'; // created by `flutter pub run build_runner build` /// Provides access to the ObjectBox Store throughout the app. @@ -66,6 +63,7 @@ class ObjectBox { userBox.removeAll(); etabBox.removeAll(); concurrentBox.removeAll(); + //visiteBox.removeAll(); //visiteTagBox.removeAll(); //photoBox.removeAll(); @@ -289,7 +287,8 @@ class ObjectBox { int _id_etab, int _abandon, String _end, - String _url_photo_principale) => + String _url_photo_principale, + String _langage) => store.runInTransactionAsync( TxMode.write, _addVisiteInTx, @@ -302,7 +301,8 @@ class ObjectBox { id_distrib_visite: _id_distrib_visite, id_etab: _id_etab, abandon: _abandon, - url_photo_principale: _url_photo_principale)); + url_photo_principale: _url_photo_principale, + langage: _langage)); static void _addVisiteInTx(Store store, _Visite) { store.box().put(_Visite); @@ -367,15 +367,6 @@ class ObjectBox { return visiteTagBox.count(); } - Future putPhotoTags(int photoId, List tags) async { - final photo = photoBox.get(photoId); - - if (photo != null) { - final updatedPhoto = photo.copyWith(tags: tags.join(",")); - await photoBox.putAsync(updatedPhoto); - } - } - /// PHOTO -------------------------------------------------------------------- /// /// @@ -385,15 +376,14 @@ class ObjectBox { return addedPhotos; } - Future addPhoto(int id_visite, int id_photo_typologie, String image, - String image_name) => + Future addPhoto( + int id_visite, int id_photo_typologie, String image_name) => store.runInTransactionAsync( TxMode.write, _addPhotoInTx, Photo( id_visite: id_visite, id_photo_typologie: id_photo_typologie, - image: image, image_name: image_name)); static void _addPhotoInTx(Store store, _Photo) { @@ -402,11 +392,13 @@ class ObjectBox { store.box().put(_Photo); } - List getAllPhotos() { + List getAllVisitTypologyPhotos( + int id_visite, int _id_photo_typologie) { // Query for all photos, sorted by their date. // https://docs.objectbox.io/queries final query = photoBox - .query() + .query(Photo_.id_visite.equals(id_visite) & + Photo_.id_photo_typologie.equals(_id_photo_typologie)) .order(Photo_.date_photo, flags: Order.descending) .build(); return query.find(); @@ -446,14 +438,58 @@ class ObjectBox { } } - int getVisitPhotoCount(int _id_visite, int _id_Photo_typologie) { + int getVisitPhotoCount(int _id_visite) { + final builder = photoBox.query(Photo_.id_visite.equals(_id_visite)).build(); + return builder.count(); + } + + int getVisitTypologiePhotoCount(int _id_visite, int _id_photo_typologie) { final builder = photoBox .query(Photo_.id_visite.equals(_id_visite) & - Photo_.id_photo_typologie.equals(_id_Photo_typologie)) + Photo_.id_photo_typologie.equals(_id_photo_typologie)) .build(); return builder.count(); } + Future putPhotoTypologie(int photoId, int typologieId) async { + final photo = photoBox.get(photoId); + + if (photo != null) { + final updatedPhoto = photo.copyWith(id_photo_typologie: typologieId); + await photoBox.putAsync(updatedPhoto); + } + } + + Future putPhotoTags(int photoId, List tags) async { + final photo = photoBox.get(photoId); + + if (photo != null) { + final updatedPhoto = photo.copyWith(tags: tags.join(",")); + await photoBox.putAsync(updatedPhoto); + } + } + + Future putPhotoVisibilities( + int photoId, List visibilities) async { + final photo = photoBox.get(photoId); + + if (photo != null) { + final updatedPhoto = photo.copyWith( + photo_principale: visibilities.contains('principal') ? 1 : 0, + photo_privee: visibilities.contains('private') ? 1 : 0); + await photoBox.putAsync(updatedPhoto); + } + } + + /* remettre les principal à zero + final queryBuilder = box.query(Photo_.visite_id.equals(idVisite) & Photo_.photo_principale.equals(1)); + final updatedPhotos = queryBuilder.build().find(); + updatedPhotos.forEach((photo) { + photo.photo_principale = 0; + }); + box.putMany(updatedPhotos); + */ + /// PHOTO TYPOLOGY ----------------------------------------------------------- /// @@ -496,6 +532,13 @@ class ObjectBox { .toList(); } + List getPhotoTypologiesList() { + final query = photoTypologyBox.query().order(PhotoTypology_.ordre).build(); + final photoTypologies = query.find(); + + return photoTypologies.toList(); + } + Stream> getPhotoTypologies() { // Query for all Typologies, sorted by their order. // https://docs.objectbox.io/queries diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart index 51a7dd0..2932a61 100644 --- a/lib/objectbox.g.dart +++ b/lib/objectbox.g.dart @@ -220,7 +220,7 @@ final _entities = [ ModelEntity( id: const IdUid(7, 8290500625256822711), name: 'Visite', - lastPropertyId: const IdUid(13, 1900114397693432703), + lastPropertyId: const IdUid(14, 5831680857919010234), flags: 0, properties: [ ModelProperty( @@ -272,6 +272,11 @@ final _entities = [ id: const IdUid(13, 1900114397693432703), name: 'url_photo_principale', type: 9, + flags: 0), + ModelProperty( + id: const IdUid(14, 5831680857919010234), + name: 'langage', + type: 9, flags: 0) ], relations: [], @@ -356,11 +361,6 @@ final _entities = [ name: 'uploaded', type: 6, flags: 0), - ModelProperty( - id: const IdUid(15, 1865824860595482227), - name: 'image', - type: 9, - flags: 0), ModelProperty( id: const IdUid(16, 539065583624712715), name: 'image_name', @@ -443,7 +443,8 @@ ModelDefinition getObjectBoxModel() { 5293139139799032553, 2141346538986140281, 7877546811840884522, - 3784190804330297742 + 3784190804330297742, + 1865824860595482227 ], retiredRelationUids: const [], modelVersion: 5, @@ -669,7 +670,8 @@ ModelDefinition getObjectBoxModel() { final titleOffset = fbb.writeString(object.title); final url_photo_principaleOffset = fbb.writeString(object.url_photo_principale); - fbb.startTable(14); + final langageOffset = fbb.writeString(object.langage); + fbb.startTable(15); fbb.addInt64(0, object.id); fbb.addInt64(1, object.id_visite); fbb.addOffset(2, type_visiteOffset); @@ -680,6 +682,7 @@ ModelDefinition getObjectBoxModel() { fbb.addInt64(8, object.abandon); fbb.addInt64(10, object.date_visite.millisecondsSinceEpoch); fbb.addOffset(12, url_photo_principaleOffset); + fbb.addOffset(13, langageOffset); fbb.finish(fbb.endTable()); return object.id; }, @@ -705,9 +708,10 @@ ModelDefinition getObjectBoxModel() { const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0), abandon: const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0), - url_photo_principale: - const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 28, '')); + url_photo_principale: const fb.StringReader(asciiOptimization: true) + .vTableGet(buffer, rootOffset, 28, ''), + langage: + const fb.StringReader(asciiOptimization: true).vTableGet(buffer, rootOffset, 30, '')); return object; }), @@ -757,7 +761,6 @@ ModelDefinition getObjectBoxModel() { object.id = id; }, objectToFB: (Photo object, fb.Builder fbb) { - final imageOffset = fbb.writeString(object.image); final image_nameOffset = fbb.writeString(object.image_name); final tagsOffset = fbb.writeString(object.tags); fbb.startTable(18); @@ -769,7 +772,6 @@ ModelDefinition getObjectBoxModel() { fbb.addInt64(7, object.photo_privee); fbb.addInt64(8, object.photo_principale); fbb.addInt64(13, object.uploaded); - fbb.addOffset(14, imageOffset); fbb.addOffset(15, image_nameOffset); fbb.addOffset(16, tagsOffset); fbb.finish(fbb.endTable()); @@ -785,8 +787,6 @@ ModelDefinition getObjectBoxModel() { const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0), id_photo_typologie: const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0), - image: const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 32, ''), image_name: const fb.StringReader(asciiOptimization: true) .vTableGet(buffer, rootOffset, 34, ''), id_photo_mp4: @@ -990,6 +990,10 @@ class Visite_ { /// see [Visite.url_photo_principale] static final url_photo_principale = QueryStringProperty(_entities[5].properties[9]); + + /// see [Visite.langage] + static final langage = + QueryStringProperty(_entities[5].properties[10]); } /// [VisiteTag] entity fields to define ObjectBox queries. @@ -1047,15 +1051,12 @@ class Photo_ { static final uploaded = QueryIntegerProperty(_entities[7].properties[7]); - /// see [Photo.image] - static final image = QueryStringProperty(_entities[7].properties[8]); - /// see [Photo.image_name] static final image_name = - QueryStringProperty(_entities[7].properties[9]); + QueryStringProperty(_entities[7].properties[8]); /// see [Photo.tags] - static final tags = QueryStringProperty(_entities[7].properties[10]); + static final tags = QueryStringProperty(_entities[7].properties[9]); } /// [PhotoTypology] entity fields to define ObjectBox queries. diff --git a/lib/service/shared_prefs.dart b/lib/service/shared_prefs.dart index a48a33c..1ccb2c6 100644 --- a/lib/service/shared_prefs.dart +++ b/lib/service/shared_prefs.dart @@ -102,4 +102,25 @@ class SharedPrefs { set id_visite(int value) { _sharedPrefs.setInt('key_id_visite', value); } + + /// get/set isSimulator + bool get isSimulator => _sharedPrefs.getBool('key_issimulator') ?? false; + + set isSimulator(bool value) { + _sharedPrefs.setBool('key_issimulator', value); + } + + /// get/set application's document directory + String get documentsDir => _sharedPrefs.getString('documentsDir') ?? ""; + + set documentsDir(String value) { + _sharedPrefs.setString('documentsDir', value); + } + + /// get/set application's photo directory + String get photosDir => _sharedPrefs.getString('photosDir') ?? ""; + + set photosDir(String value) { + _sharedPrefs.setString('photosDir', value); + } } diff --git a/lib/ui/home/photo_typology.dart b/lib/ui/home/photo_typology.dart deleted file mode 100644 index b71a078..0000000 --- a/lib/ui/home/photo_typology.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'dart:async'; -import 'package:mobdr/main.dart'; -import 'package:mobdr/config/global_style.dart'; -import 'package:mobdr/config/constant.dart'; -import 'package:flutter/material.dart'; -import 'package:mobdr/db/box_photo_typology.dart'; -import 'package:mobdr/ui/home/photo_list.dart'; - -class PhotoTypologyPage extends StatefulWidget { - @override - _PhotoTypologyPageState createState() => _PhotoTypologyPageState(); -} - -class _PhotoTypologyPageState extends State { - GestureDetector Function(BuildContext, int) _itemBuilder( - List PhotoTypology) => - (BuildContext context, int index) => GestureDetector( - onTap: () { - Route route = - MaterialPageRoute(builder: (context) => PhotoListPage()); - Navigator.push(context, route).then(onGoBack); - }, - - ///objectbox.noteBox.remove(PhotoTypology[index].id), - child: Row( - children: [ - Expanded( - child: Container( - decoration: const BoxDecoration( - border: - Border(bottom: BorderSide(color: Colors.black12))), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 15.0, horizontal: 10.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - PhotoTypology[index].libelle + - " " + - objectbox - .getVisitPhotoCount(0, - PhotoTypology[index].id_photo_typologie) - .toString(), - style: const TextStyle( - fontSize: 15.0, - ), - // Provide a Key for the integration test - key: Key('list_log_$index'), - ), - Icon(Icons.chevron_right, size: 20, color: SOFT_GREY), - ], - ), - ), - ), - ), - ], - ), - ); - - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - FutureOr onGoBack(dynamic value) { - //refreshData(); - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - appBar: AppBar( - iconTheme: IconThemeData( - color: GlobalStyle.appBarIconThemeColor, - ), - elevation: GlobalStyle.appBarElevation, - title: Text( - 'Visite de ...', - style: GlobalStyle.appBarTitle, - ), - backgroundColor: GlobalStyle.appBarBackgroundColor, - systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle), - body: Column(children: [ - Expanded( - child: StreamBuilder>( - 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 ?? [])))) - ])); - } -} diff --git a/lib/ui/home/tab_home.dart b/lib/ui/home/tab_home.dart index ed8d123..3340acb 100644 --- a/lib/ui/home/tab_home.dart +++ b/lib/ui/home/tab_home.dart @@ -3,20 +3,20 @@ This is home page we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime */ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/material.dart'; + +import 'package:shared_preferences/shared_preferences.dart'; + import 'package:mobdr/config/constant.dart'; import 'package:mobdr/cubit/language/language_cubit.dart'; import 'package:mobdr/cubit/language/app_localizations.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/config/global_style.dart'; import 'package:mobdr/model/visite_model.dart'; import 'package:mobdr/ui/general/chat_us.dart'; import 'package:mobdr/ui/general/notification.dart'; -import 'package:mobdr/ui/home/photo_typology.dart'; -import 'package:mobdr/ui/general/product_detail/product_detail.dart'; +import 'package:mobdr/ui/home/visit_photo_typology.dart'; import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/ui/reusable/cache_image_network.dart'; @@ -44,7 +44,7 @@ class _TabHomePageState extends State bool _isLoading = true; String _errorMessage = ''; - late List visiteData = []; + late List modelData = []; @override void initState() { @@ -91,7 +91,7 @@ class _TabHomePageState extends State final double boxImageSize = (MediaQuery.of(context).size.width / 4); if (_isLoading) { return Center(child: CircularProgressIndicator()); - } else if (visiteData.isEmpty) { + } else if (modelData.isEmpty) { return Center( child: Text('Aucune visite trouvée.'), ); @@ -127,11 +127,11 @@ class _TabHomePageState extends State ), body: AnimatedList( key: _listKey, - initialItemCount: visiteData.length, + initialItemCount: modelData.length, physics: AlwaysScrollableScrollPhysics(), itemBuilder: (context, index, animation) { return _buildVisitelistCard( - visiteData[index], boxImageSize, animation, index); + modelData[index], boxImageSize, animation, index); }, )); } @@ -142,41 +142,44 @@ class _TabHomePageState extends State sizeFactor: animation, 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: [ - GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ProductDetailPage( - name: visiteData.name, - image: visiteData.image, - price: visiteData.price, - photo: visiteData.photo, - rating: visiteData.rating, - review: visiteData.review, - sale: visiteData.sale, - date: visiteData.date))); - }, - child: Row( + 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, + onRefreshVisit: (int photoCount) { + setState(() { + modelData[index].photoCount = photoCount; + }); + }, + )); + Navigator.push(context, route); + }, + 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: [ ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(10)), - child: buildCacheNetworkImage( - width: boxImageSize, - height: boxImageSize, - url: visiteData.image)), + borderRadius: BorderRadius.all(Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: visiteData.image, + ), + ), SizedBox( width: 10, ), @@ -187,34 +190,45 @@ class _TabHomePageState extends State Container(height: 8), Text( visiteData.name, - style: GlobalStyle.productName - .copyWith(fontSize: 13), + style: GlobalStyle.productName.copyWith( + fontSize: 13, + ), maxLines: 3, overflow: TextOverflow.ellipsis, ), Container( margin: EdgeInsets.only(top: 5), - child: Text(visiteData.date, - style: GlobalStyle.productSale), + child: Text( + visiteData.date, + style: GlobalStyle.productSale, + ), ), Container(height: 5), Container( margin: EdgeInsets.only(top: 5), child: Text( - visiteData.photo.toString() + ' Photo(s)', - style: GlobalStyle.productPrice), + '${visiteData.photoCount} Photo(s)', + style: GlobalStyle.productPrice, + ), ), Container(height: 5), 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) + Icon( + Icons.store, + color: SOFT_GREY, + size: 20, + ), + Text( + ' ' + visiteData.type_visite, + style: GlobalStyle.productName.copyWith( + fontSize: 13, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ) ], ), ), @@ -223,72 +237,8 @@ class _TabHomePageState extends State ) ], ), - ), - Container( - margin: EdgeInsets.only(top: 12), - child: Row( - children: [ - Expanded( - child: (visiteData.stock == 0) - ? TextButton( - style: ButtonStyle( - minimumSize: - MaterialStateProperty.all(Size(0, 30)), - backgroundColor: - MaterialStateProperty.resolveWith( - (Set states) => - Colors.grey[300]!, - ), - overlayColor: MaterialStateProperty.all( - Colors.transparent), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5.0), - )), - ), - onPressed: null, - child: Text( - 'Out of Stock', - style: TextStyle( - color: Colors.grey[600], - fontWeight: FontWeight.bold, - fontSize: 13), - textAlign: TextAlign.center, - )) - : OutlinedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - PhotoTypologyPage())); - }, - 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( - AppLocalizations.of(context)! - .translate('i18n_take_pictures')!, - style: TextStyle( - color: SOFT_BLUE, - fontWeight: FontWeight.bold, - fontSize: 13), - textAlign: TextAlign.center, - )), - ), - ], - ), - ) - ], + ], + ), ), ), ), @@ -296,11 +246,21 @@ class _TabHomePageState extends State ); } - // data initialization on loading. + /// Called when a visit is refreshed with new photo count. + /// + /// [index]: The index of the visit being refreshed. + /// [newPhotoCount]: The new photo count for the visit. + void onRefreshVisit(int index, int newPhotoCount) { + setState(() { + modelData[index].photoCount = newPhotoCount; + }); + } + + /// Initializes data when the page loads. Future loadData() async { try { - // visite initialisation - visiteData = await VisiteModel.getAllVisites(); + // visite model initialisation + modelData = await VisiteModel.getAllVisites(); } catch (e) { // set errorMessage for debug _errorMessage = 'Error loading visites : $e'; diff --git a/lib/ui/home/photo_tag.dart b/lib/ui/home/visit_photo_tag.dart similarity index 88% rename from lib/ui/home/photo_tag.dart rename to lib/ui/home/visit_photo_tag.dart index dad5b75..7c3edf9 100644 --- a/lib/ui/home/photo_tag.dart +++ b/lib/ui/home/visit_photo_tag.dart @@ -9,13 +9,19 @@ import 'package:super_tag_editor/widgets/rich_text_widget.dart'; class PhotoTagPage extends StatefulWidget { // variables corresponding to the data parameters - final int photoId; - final List currentTags; + final int pp_id_distrib; + final String pp_langage; + final int pp_photoId; + final List pp_currentTags; // Requiring data parameters - const PhotoTagPage( - {Key? key, required this.photoId, required this.currentTags}) - : super(key: key); + const PhotoTagPage({ + Key? key, + required this.pp_id_distrib, + required this.pp_langage, + required this.pp_photoId, + required this.pp_currentTags, + }) : super(key: key); @override _PhotoTagPageState createState() => _PhotoTagPageState(); @@ -187,8 +193,9 @@ class _PhotoTagPageState extends State { const Divider(), ElevatedButton( onPressed: () async { - // Save the selected tags to the database //TODO 1,fr === - await saveSelectedTags(_selectedTags, 1, 'fr'); + // Save the selected tags to the database + await saveSelectedTags( + _selectedTags, widget.pp_id_distrib, widget.pp_langage); Navigator.pop( context, _selectedTags.map((tag) => tag).toList()); @@ -203,9 +210,9 @@ class _PhotoTagPageState extends State { } Future loadData() async { - // TODO 1,fr - allTagsList = await objectbox.getVisiteTagsLabels(1, 'fr'); - _selectedTags = List.from(widget.currentTags); + allTagsList = await objectbox.getVisiteTagsLabels( + widget.pp_id_distrib, widget.pp_langage); + _selectedTags = List.from(widget.pp_currentTags); } /// Saves the selected tags for a photo to the ObjectBox database. @@ -223,20 +230,18 @@ class _PhotoTagPageState extends State { /// Nothing. Future saveSelectedTags( List tags, int distribId, String langage) async { - if (tags.isEmpty) { - return; // exit function without saving tags - } + if (tags.isNotEmpty) { + // determines if there are any new tags + final newTags = tags.where((tag) => !allTagsList.contains(tag)).toList(); - // determines if there are any new tags - final newTags = tags.where((tag) => !allTagsList.contains(tag)).toList(); - - for (final tag in newTags) { - // insert new tag in the database - objectbox.addVisiteTag(0, distribId, tag, langage); + for (final tag in newTags) { + // insert new tag in the database + objectbox.addVisiteTag(0, distribId, tag, langage); + } } // save photo tag in the database - objectbox.putPhotoTags(widget.photoId, tags); + objectbox.putPhotoTags(widget.pp_photoId, tags); } } diff --git a/lib/ui/home/visit_photo_typology.dart b/lib/ui/home/visit_photo_typology.dart new file mode 100644 index 0000000..2e00cd0 --- /dev/null +++ b/lib/ui/home/visit_photo_typology.dart @@ -0,0 +1,166 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; + +import 'package:mobdr/main.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/db/box_photo_typology.dart'; +import 'package:mobdr/ui/home/visit_photo_typology_list.dart'; + +class VisitPhotoTypologyPage extends StatefulWidget { + final int pp_id_distrib; + 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); + + @override + _VisitPhotoTypologyPageState createState() => _VisitPhotoTypologyPageState(); +} + +class _VisitPhotoTypologyPageState extends State { + GestureDetector Function(BuildContext, int) _itemBuilder( + List PhotoTypology) => + (BuildContext context, int index) => GestureDetector( + onTap: () { + Route route = MaterialPageRoute( + builder: (context) => VisitPhotoTypologyListPage( + pp_id_distrib: widget.pp_id_distrib, + pp_langage: widget.pp_langage, + pp_id_visite: widget.pp_id_visite, + pp_id_typologie: + PhotoTypology[index].id_photo_typologie, + pp_libelle_typologie: PhotoTypology[index].libelle, + )); + Navigator.push(context, route).then(onGoBack); + }, + + /// TODO objectbox.noteBox.remove(PhotoTypology[index].id), + child: Container( + decoration: const BoxDecoration( + border: Border(bottom: BorderSide(color: Colors.black12)), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 15.0, horizontal: 10.0), + child: Row( + children: [ + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildBadge( + widget.pp_id_visite, + PhotoTypology[index].id_photo_typologie, + PhotoTypology[index].libelle), + Icon(Icons.chevron_right, size: 20, color: SOFT_GREY), + ], + ), + ), + ], + ), + ), + ), + ); + + Widget _buildBadge(int visitId, int photoTypologyId, String libelle) { + int photoCount = + objectbox.getVisitTypologiePhotoCount(visitId, photoTypologyId); + if (photoCount > 0) { + return Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + libelle, + style: const TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.bold, + ), + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 6, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.blue, + ), + child: Text( + photoCount.toString(), + style: TextStyle(color: Colors.white), + ), + ), + ], + ), + ); + } else { + return Text( + libelle, + style: const TextStyle( + fontSize: 15.0, + ), + ); + } + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + FutureOr onGoBack(dynamic value) { + setState(() {}); + } + + Future 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: [ + Expanded( + child: StreamBuilder>( + 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 ?? [])))) + ]))); + } +} diff --git a/lib/ui/home/photo_list.dart b/lib/ui/home/visit_photo_typology_list.dart similarity index 59% rename from lib/ui/home/photo_list.dart rename to lib/ui/home/visit_photo_typology_list.dart index 9292ebf..83bdb55 100644 --- a/lib/ui/home/photo_list.dart +++ b/lib/ui/home/visit_photo_typology_list.dart @@ -1,21 +1,24 @@ -import 'dart:developer' as developer; import 'dart:async'; import 'dart:io'; +import 'package:intl/intl.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:image/image.dart' as img; + import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/main.dart'; import 'package:mobdr/ui/reusable/global_widget.dart'; import 'package:mobdr/ui/home/photo_camera.dart'; +import 'package:mobdr/ui/home/visite_photo_typology_detail.dart'; import 'package:mobdr/db/box_photo.dart'; -import 'package:mobdr/ui/home/photo_detail.dart'; // TODO Il faut supprimer les possibles photos du répertoire cache ! @@ -26,17 +29,32 @@ extension FileNameExtension on File { } } -class PhotoListPage extends StatefulWidget { +class VisitPhotoTypologyListPage extends StatefulWidget { + final int pp_id_distrib; + final String pp_langage; + final int pp_id_visite; + final int pp_id_typologie; + final String pp_libelle_typologie; + + VisitPhotoTypologyListPage( + {required this.pp_id_distrib, + required this.pp_langage, + required this.pp_id_visite, + required this.pp_id_typologie, + required this.pp_libelle_typologie}); + @override - _PhotoListPageState createState() => _PhotoListPageState(); + _VisitPhotoTypologyListPageState createState() => + _VisitPhotoTypologyListPageState(); } -class _PhotoListPageState extends State { +class _VisitPhotoTypologyListPageState + extends State { final _globalWidget = GlobalWidget(); // initialize photos files list - final List photoFiles = []; - List _photoData = []; + final List _visitPhotoFiles = []; + List _visitPhotoData = []; Color _color1 = Color(0xff777777); Color _color2 = Color(0xFF515151); @@ -47,7 +65,7 @@ class _PhotoListPageState extends State { @override void initState() { super.initState(); - _loadData(); + loadData(); } @override @@ -55,10 +73,6 @@ class _PhotoListPageState extends State { super.dispose(); } - void _loadData() { - _photoData = objectbox.getAllPhotos(); - } - @override Widget build(BuildContext context) { final double boxImageSize = (MediaQuery.of(context).size.width / 4); @@ -70,7 +84,7 @@ class _PhotoListPageState extends State { systemOverlayStyle: SystemUiOverlayStyle.dark, elevation: 0, title: Text( - 'Catégorie : A trier', + widget.pp_libelle_typologie, style: TextStyle(fontSize: 18, color: Colors.black), ), backgroundColor: Colors.white, @@ -95,16 +109,107 @@ class _PhotoListPageState extends State { }), ], ), - body: AnimatedList( - key: _listKey, - initialItemCount: _photoData.length, - physics: AlwaysScrollableScrollPhysics(), - itemBuilder: (context, index, animation) { - return _buildPhotolistCard( - _photoData[index], boxImageSize, animation, index); - }, - ), - floatingActionButton: fabCart(context)); + body: Column(children: [ + Flexible( + child: AnimatedList( + key: _listKey, + initialItemCount: _visitPhotoData.length, + physics: AlwaysScrollableScrollPhysics(), + itemBuilder: (context, index, animation) { + return _buildPhotolistCard( + _visitPhotoData[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.chat, color: Colors.white, size: 16)), + ), + ), + ), + SizedBox( + width: 10, + ), + Expanded( + child: GestureDetector( + onTap: () async { + // clear photoFiles list before taking photos + _visitPhotoFiles.clear(); + + // if we are not on a simulator + if (SharedPrefs().isSimulator == false) { + Route route = MaterialPageRoute( + builder: (context) => + CameraPage(photoFiles: _visitPhotoFiles)); + Navigator.push(context, route).then((val) { + // Restore the constraint before navigating away + // DO await here to avoid any other screen in + // landscape mode + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + ]); + + // if the user has validated photos + if (val == true) { + savePhotos(); + } + }); + } else { + // simulates the taking of a photo + final File imageFile = + await copyImageFromAssetsToTemporaryDirectory(); + _visitPhotoFiles.add(imageFile); + savePhotos(); + } + }, + 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('Prendre des photos', + style: TextStyle( + color: SOFT_BLUE, fontWeight: FontWeight.bold)), + ), + ), + ), + ], + ), + ) + ])); } Widget _buildPhotolistCard(Photo photoData, boxImageSize, animation, index) { @@ -124,24 +229,31 @@ class _PhotoListPageState extends State { children: [ GestureDetector( onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PhotoDetailPage( - id: _photoData[index].id, - name: 'visiteData.name', - image: _photoData[index].image, - price: 0, - photo: 1, - rating: 0, - review: 0, - sale: 0, - date: ''))); + Route route = MaterialPageRoute( + builder: (context) => VisitPhotoTypologyDetailPage( + pp_id: _visitPhotoData[index].id, + pp_id_distrib: widget.pp_id_distrib, + pp_langage: widget.pp_langage, + pp_image: _visitPhotoData[index].getImage(), + pp_id_typologie: widget.pp_id_typologie, + )); - // todo if qq chose à changé - // ne raffraichir que le widget en cours == - setState(() { - _listKey = GlobalKey(); + Navigator.push(context, route).then((result) { + if (result['change_typologie']) { + // the photo must be removed + setState(() { + _visitPhotoData.removeAt(index); + _listKey = GlobalKey(); + }); + } else { + setState(() { + _visitPhotoData[index].tags = result['tags']; + _visitPhotoData[index].photo_principale = + result['photo_principale']; + _visitPhotoData[index].photo_privee = + result['photo_privee']; + }); + } }); }, child: Row( @@ -150,7 +262,7 @@ class _PhotoListPageState extends State { children: [ ClipRRect( borderRadius: BorderRadius.all(Radius.circular(10)), - child: Image.file(File(photoData.image), + child: Image.file(File(photoData.getImage()), fit: BoxFit.cover, height: 100, width: 150), ), SizedBox( @@ -160,54 +272,34 @@ class _PhotoListPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Text( + DateFormat('dd/MM/yyyy HH:mm:ss') + .format(photoData.date_photo), + style: TextStyle( + fontSize: 11, color: SOFT_GREY)) + ], + ), + ), + Container(height: 8), Text( - photoData.id_photo_typologie.toString(), + "Private : ${photoData.photo_privee == 1 ? 'Yes' : 'No'}", style: TextStyle(fontSize: 13, color: _color2), maxLines: 3, overflow: TextOverflow.ellipsis, ), Container( margin: EdgeInsets.only(top: 5), - child: Text((index + 1).toString(), + child: Text( + '${photoData.tags.isNotEmpty ? (photoData.tags.length > 53 ? '${photoData.tags.substring(0, 50)}...' : photoData.tags) : "notag"}', style: TextStyle( fontSize: 13, fontWeight: FontWeight.bold)), ), - Container( - margin: EdgeInsets.only(top: 5), - child: Row( - children: [ - Icon(Icons.location_on, - color: SOFT_GREY, size: 12), - Text(' ' + photoData.image_name, - style: TextStyle( - fontSize: 11, color: SOFT_GREY)) - ], - ), - ), - Container( - margin: EdgeInsets.only(top: 5), - child: Row( - children: [ - Text( - '(' + - photoData.id_photo_typologie - .toString() + - ')', - style: TextStyle( - fontSize: 11, color: SOFT_GREY)) - ], - ), - ), - Container( - margin: EdgeInsets.only(top: 5), - child: Text( - photoData.id_photo_typologie.toString() + - ' ' + - 'Sale', - style: TextStyle( - fontSize: 11, color: SOFT_GREY)), - ), + Container(height: 8), ], ), ) @@ -279,12 +371,12 @@ class _PhotoListPageState extends State { borderRadius: BorderRadius.circular(5.0), )), side: MaterialStateProperty.all( - BorderSide(color: SOFT_BLUE, width: 1.0), + BorderSide(color: SOFT_GREY, width: 1.0), )), child: Text( 'Copier dans galerie', style: TextStyle( - color: SOFT_BLUE, + color: SOFT_GREY, fontWeight: FontWeight.bold, fontSize: 13), textAlign: TextAlign.center, @@ -297,7 +389,7 @@ class _PhotoListPageState extends State { behavior: HitTestBehavior.translucent, onTap: () async { await rotateAndReplaceImage( - File(_photoData[index].image), 90); + File(_visitPhotoData[index].getImage()), 90); setState(() { _listKey = GlobalKey(); }); @@ -322,7 +414,7 @@ class _PhotoListPageState extends State { behavior: HitTestBehavior.translucent, onTap: () async { await rotateAndReplaceImage( - File(_photoData[index].image), -90); + File(_visitPhotoData[index].getImage()), -90); setState(() { _listKey = GlobalKey(); }); @@ -351,78 +443,33 @@ class _PhotoListPageState extends State { ); } - Widget fabCart(context) { - return FloatingActionButton( - onPressed: () { - photoFiles.clear(); - Route route = MaterialPageRoute( - builder: (context) => CameraPage(photoFiles: photoFiles)); - Navigator.push(context, route).then((val) { - // Restore the constraint before navigating away - // DO await here to avoid any other screen in - // landscape mode - SystemChrome.setPreferredOrientations([ - DeviceOrientation.portraitUp, - ]); - - /// if the user has validated photos - if (val == true) { - savePhotos(); - } - }); - }, - child: Stack(children: [ - Icon(Icons.add_a_photo, color: BLACK21, size: 42), - Positioned( - right: 0, - bottom: 0, - child: Container( - padding: EdgeInsets.all(1), - decoration: BoxDecoration( - color: PRIMARY_COLOR, - borderRadius: BorderRadius.circular(14), - ), - constraints: BoxConstraints( - minWidth: 16, - minHeight: 16, - ), - child: Center( - child: Text( - _photoData.length.toString(), - style: TextStyle( - color: Colors.white, - fontSize: 8, - ), - textAlign: TextAlign.center, - ), - ), - ), - ) - ]), - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4.0))), - ); + // TODO ƒuture void ? + void loadData() { + _visitPhotoData = objectbox.getAllVisitTypologyPhotos( + widget.pp_id_visite, widget.pp_id_typologie); } + /// Removes the image at the specified [imageURL] from the cache of the [NetworkImage] provider. + /// Returns a boolean indicating whether the image was successfully evicted. Future evictImage(String imageURL) async { final NetworkImage provider = NetworkImage(imageURL); return await provider.evict(); } + /// Saves photos to the database and updates the widget with the new photos. + /// void savePhotos() async { - if (photoFiles.length > 0) { + if (_visitPhotoFiles.length > 0) { final List _listPhotos = []; - for (var myTmpPhoto in photoFiles) { + for (var myTmpPhoto in _visitPhotoFiles) { /// move jpg file to photo directory final myPhoto = await moveFileFromTempToPhotosDir(myTmpPhoto); /// to insert into database _listPhotos.add(Photo( - id_visite: 0, - id_photo_typologie: 0, - image: myPhoto.path, + id_visite: widget.pp_id_visite, + id_photo_typologie: widget.pp_id_typologie, image_name: myPhoto.path.split('/').last)); } @@ -430,7 +477,7 @@ class _PhotoListPageState extends State { final addedPhotos = await objectbox.addPhotos(_listPhotos); /// insert photo(s) in widget at the beginning (0) - _photoData.insertAll(0, addedPhotos); + _visitPhotoData.insertAll(0, addedPhotos); /// refresh widget setState(() { @@ -439,6 +486,11 @@ class _PhotoListPageState extends State { } } + /// Deletes the given file if it exists. + /// + /// Returns nothing. + /// + /// Throws an error if there was an issue deleting the file. Future deleteFile(File file) async { try { if (await file.exists()) { @@ -451,6 +503,13 @@ class _PhotoListPageState extends State { } } + /// Shows an alert dialog to confirm the deletion of a photo from the visit and + /// removes it from the list of photos. Also deletes the photo file from the + /// database and local storage. + /// + /// Parameters: + /// - index : index of the photo in the list to be deleted. + /// - boxImageSize : size of the image to be displayed in the list. void showPopupDeletePhoto(index, boxImageSize) { // set up the buttons Widget cancelButton = TextButton( @@ -461,13 +520,13 @@ class _PhotoListPageState extends State { Widget continueButton = TextButton( onPressed: () { int removeIndex = index; - var removedItem = _photoData.removeAt(removeIndex); + var removedItem = _visitPhotoData.removeAt(removeIndex); // delete file on database objectbox.delPhoto(removedItem.image_name); // delete file on local storage - deleteFile(new File(removedItem.image)); + deleteFile(new File(removedItem.getImage())); // This builder is just so that the animation has something // to work with before it disappears from view since the original @@ -512,13 +571,18 @@ class _PhotoListPageState extends State { ); } + // Moves a temporary file to the photos directory in the app's document directory. + /// + /// Returns a `File` object for the new file in the photos directory. + /// + /// Parameters: + /// * `tempFile`: The temporary `File` object to move to the photos directory. + /// + /// Throws a `FileSystemException` if there is an error renaming the file. Future moveFileFromTempToPhotosDir(File tempFile) async { - // Get the application's document directory - final Directory documentsDir = await getApplicationDocumentsDirectory(); - // Set the new file path with the original file name final String newPath = - '${documentsDir.path}/photos/${tempFile.path.split('/').last}'; + '${SharedPrefs().photosDir}/${tempFile.path.split('/').last}'; // Rename the file to move it to the documents directory await tempFile.rename(newPath); @@ -527,6 +591,13 @@ class _PhotoListPageState extends State { return File(newPath); } + /// Rotates the image file clockwise by the specified angle (in degrees) and overwrites the original file with the rotated image. + /// + /// Parameters: + /// - `imageFile`: The image file to rotate and replace. + /// - `angle`: The angle (in degrees) by which to rotate the image. Valid values are 90, 180, and 270. + /// + /// Throws an exception if there is an error in the rotation process. Future rotateAndReplaceImage(File imageFile, int angle) async { // Read the image file into a Uint8List Uint8List bytes = await imageFile.readAsBytes(); @@ -546,4 +617,19 @@ class _PhotoListPageState extends State { // remove the flutter cache image FileImage(imageFile).evict(); } + + /// Copies the 'simulator.jpg' image file from the app's assets to a randomly named file in the app's temporary directory and returns the File object. + /// The temporary file name will start with 'sim_'. + Future copyImageFromAssetsToTemporaryDirectory() async { + final String randomName = 'sim_${DateTime.now().microsecondsSinceEpoch}'; + final Directory tempDir = await getTemporaryDirectory(); + final String tempPath = tempDir.path; + final String assetPath = 'assets/images/simulator.jpeg'; + + final ByteData data = await rootBundle.load(assetPath); + final File tempFile = File('$tempPath/$randomName.jpeg'); + await tempFile.writeAsBytes(data.buffer.asUint8List(), flush: true); + + return tempFile; + } } diff --git a/lib/ui/home/photo_detail.dart b/lib/ui/home/visite_photo_typology_detail.dart similarity index 58% rename from lib/ui/home/photo_detail.dart rename to lib/ui/home/visite_photo_typology_detail.dart index 32b8ac2..cb13d7e 100644 --- a/lib/ui/home/photo_detail.dart +++ b/lib/ui/home/visite_photo_typology_detail.dart @@ -5,79 +5,59 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/config/global_style.dart'; import 'package:mobdr/main.dart'; import 'package:mobdr/ui/general/chat_us.dart'; -import 'package:mobdr/ui/general/notification.dart'; -import 'package:mobdr/ui/home/product_category.dart'; -import 'package:mobdr/ui/home/photo_tag.dart'; -import 'package:mobdr/ui/home/search.dart'; -import 'package:mobdr/ui/reusable/reusable_widget.dart'; -import 'package:mobdr/ui/shopping_cart/tab_shopping_cart.dart'; -import 'package:mobdr/ui/reusable/global_function.dart'; +import 'package:mobdr/ui/home/visit_photo_tag.dart'; import 'package:mobdr/db/box_photo.dart'; +import 'package:mobdr/db/box_photo_typology.dart'; -class PhotoDetailPage extends StatefulWidget { +//TODO : faire concurrents === + +class VisitPhotoTypologyDetailPage extends StatefulWidget { // variables corresponding to the data parameters - final int id; - final String name; - final String image; - final String tags; - final double price; - final int photo; - final double rating; - final int review; - final int sale; - final String date; + final int pp_id; + final int pp_id_distrib; + final String pp_langage; + final String pp_image; + final int pp_id_typologie; // Requiring data parameters - const PhotoDetailPage( - {Key? key, - this.id = 0, - this.name = '', - this.image = '', - this.tags = '', - this.price = 24, - this.photo = 1, - this.rating = 4, - this.review = 45, - this.sale = 63, - this.date = ''}) - : super(key: key); + VisitPhotoTypologyDetailPage({ + Key? key, + required this.pp_id, + required this.pp_id_distrib, + required this.pp_langage, + required this.pp_image, + required this.pp_id_typologie, + }) : super(key: key); @override - _PhotoDetailPageState createState() => _PhotoDetailPageState(); + _VisitPhotoTypologyDetailPageState createState() => + _VisitPhotoTypologyDetailPageState(); } -class _PhotoDetailPageState extends State { - // initialize global function and reusable widget - final _globalFunction = GlobalFunction(); - final _reusableWidget = ReusableWidget(); - +class _VisitPhotoTypologyDetailPageState + extends State { bool _isLoading = true; String _errorMessage = ''; // Typology list - late List _typologyList = []; + late List _typologyList = []; int _typologyIndex = 0; - List _chickenParts = []; - int _maxChickenParts = 2; + List _visibilities = []; - // shopping cart count - int _shoppingCartCount = 3; - - late String tags = ""; late List tagList = []; - late Photo _photo; @override void initState() { super.initState(); - loadData(widget.id).then((_) { + loadData(widget.pp_id).then((_) { setState(() { _isLoading = false; }); @@ -111,71 +91,31 @@ class _PhotoDetailPageState extends State { } return Scaffold( appBar: AppBar( - iconTheme: IconThemeData( - color: GlobalStyle.appBarIconThemeColor, - ), - elevation: GlobalStyle.appBarElevation, - titleSpacing: 0.0, - // create search text field in the app bar - title: Container( - margin: EdgeInsets.only(right: 16), - child: TextButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) => Colors.grey[100]!, - ), - overlayColor: MaterialStateProperty.all(Colors.transparent), - shape: MaterialStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5.0), - )), - ), - onPressed: () { - Navigator.push(context, - MaterialPageRoute(builder: (context) => SearchPage())); - }, - child: Row( - children: [ - SizedBox(width: 8), - Icon(Icons.search, color: Colors.grey[500], size: 18), - SizedBox(width: 8), - Text( - 'Search Product', - style: TextStyle( - fontSize: 13, - color: Colors.grey[600], - fontWeight: FontWeight.normal), - ) - ], - )), - ), - backgroundColor: GlobalStyle.appBarBackgroundColor, - systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle, - actions: [ - IconButton( - padding: EdgeInsets.all(0), - constraints: BoxConstraints(), - icon: _customShoppingCart(_shoppingCartCount), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => TabShoppingCartPage())); - }), - IconButton( - icon: _reusableWidget.customNotifIcon( - count: 8, notifColor: BLACK_GREY), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => NotificationPage())); - }), - ], - bottom: _reusableWidget.bottomAppBar(), - ), + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Photo', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle), body: WillPopScope( onWillPop: () { - Navigator.pop(context); + // fred + Map result = { + 'change_typologie': _typologyIndex != + _typologyList.indexWhere((typology) => + typology.id_photo_typologie == widget.pp_id_typologie), + 'tags': tagList.join(","), + 'photo_principale': _visibilities.contains('principal') ? 1 : 0, + 'photo_privee': _visibilities.contains('private') ? 1 : 0, + }; + + // return to the parent page fred + Navigator.pop(context, result); + return Future.value(true); }, child: Column( @@ -183,8 +123,7 @@ class _PhotoDetailPageState extends State { Flexible( child: ListView( children: [ - Image.file(File(widget.image), fit: BoxFit.cover), - //_createProductSlider(), + Image.file(File(widget.pp_image), fit: BoxFit.cover), _buildPhotoTypology(), _buildPhotoVisibility(), _buildPhotoTag(context), @@ -230,7 +169,7 @@ class _PhotoDetailPageState extends State { child: GestureDetector( onTap: () { setState(() { - _shoppingCartCount++; + //_shoppingCartCount++; }); Fluttertoast.showToast( msg: 'Item has been added to Shopping Cart', @@ -246,7 +185,7 @@ class _PhotoDetailPageState extends State { borderRadius: BorderRadius.all(Radius.circular( 10) // <--- border radius here )), - child: Text('Add to Shopping Cart', + child: Text('Copier dans galerie', style: TextStyle( color: SOFT_BLUE, fontWeight: FontWeight.bold)), @@ -261,38 +200,6 @@ class _PhotoDetailPageState extends State { )); } - Widget _customShoppingCart(int count) { - return Stack( - children: [ - Icon(Icons.shopping_cart, color: BLACK_GREY), - Positioned( - right: 0, - child: Container( - padding: EdgeInsets.all(1), - decoration: BoxDecoration( - color: ASSENT_COLOR, - borderRadius: BorderRadius.circular(10), - ), - constraints: BoxConstraints( - minWidth: 14, - minHeight: 14, - ), - child: Center( - child: Text( - count.toString(), - style: TextStyle( - color: Colors.white, - fontSize: 8, - ), - textAlign: TextAlign.center, - ), - ), - ), - ) - ], - ); - } - Widget _buildPhotoTypology() { return Container( margin: EdgeInsets.only(top: 12), @@ -307,7 +214,7 @@ class _PhotoDetailPageState extends State { ), Wrap( children: List.generate(_typologyList.length, (index) { - return radioSize(_typologyList[index], index); + return radioSize(_typologyList[index].libelle, index); }), ), ], @@ -316,7 +223,11 @@ class _PhotoDetailPageState extends State { Widget radioSize(String txt, int index) { return GestureDetector( - onTap: () { + onTap: () async { + // save photo typology in the database + objectbox.putPhotoTypologie( + widget.pp_id, _typologyList[index].id_photo_typologie); + setState(() { _typologyIndex = index; }); @@ -339,20 +250,20 @@ class _PhotoDetailPageState extends State { ); } - // TODO changer chicken - Widget _checboxChicken({value = 'breast', primaryText = 'Chicken Breast'}) { + Widget _checkboxVisibility({value, primaryText}) { return GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { setState(() { - if (_chickenParts.contains(value)) { - _chickenParts.remove(value); + if (_visibilities.contains(value)) { + _visibilities.remove(value); } else { - if (_chickenParts.length < _maxChickenParts) { - _chickenParts.add(value); - } + _visibilities.add(value); } }); + + // save photo visibilities in the database + objectbox.putPhotoVisibilities(widget.pp_id, _visibilities); }, child: Row( children: [ @@ -360,14 +271,14 @@ class _PhotoDetailPageState extends State { decoration: BoxDecoration( border: Border.all( width: 1, - color: (_chickenParts.contains(value)) + color: (_visibilities.contains(value)) ? PRIMARY_COLOR : BLACK77), borderRadius: BorderRadius.all(Radius.circular(4.0)), ), child: Padding( padding: const EdgeInsets.all(2), - child: (_chickenParts.contains(value)) + child: (_visibilities.contains(value)) ? Icon( Icons.check, size: 12.0, @@ -385,7 +296,7 @@ class _PhotoDetailPageState extends State { style: TextStyle( fontSize: 13, color: BLACK77, - fontWeight: (_chickenParts.contains(value)) + fontWeight: (_visibilities.contains(value)) ? FontWeight.bold : FontWeight.normal)), ], @@ -406,12 +317,12 @@ class _PhotoDetailPageState extends State { ], ), SizedBox(height: 16), - _checboxChicken(value: 'public', primaryText: 'Public'), + _checkboxVisibility(value: 'private', primaryText: 'Privée'), Divider( height: 32, color: Colors.grey[400], ), - _checboxChicken(value: 'principal', primaryText: 'Principal') + _checkboxVisibility(value: 'principal', primaryText: 'Principale') ], ), ); @@ -424,8 +335,11 @@ class _PhotoDetailPageState extends State { final newTags = await Navigator.push>( context, MaterialPageRoute( - builder: (context) => - PhotoTagPage(photoId: this._photo.id, currentTags: tagList), + builder: (context) => PhotoTagPage( + pp_langage: widget.pp_langage, + pp_id_distrib: widget.pp_id_distrib, + pp_photoId: this._photo.id, + pp_currentTags: tagList), ), ); @@ -465,18 +379,27 @@ class _PhotoDetailPageState extends State { ); } - /// data initialization on loading. + /// Initializes data when the page loads. Future loadData(int photoId) async { + String tags = ""; + try { // photo typologies initialization - _typologyList = objectbox.getPhotoTypologiesLabels(); + _typologyList = objectbox.getPhotoTypologiesList(); + + _typologyIndex = _typologyList.indexWhere( + (typology) => typology.id_photo_typologie == widget.pp_id_typologie); // get photo object _photo = objectbox.getPhotoById(photoId)!; + // visibilities initialization + if (_photo.photo_privee == 1) _visibilities.add('private'); + if (_photo.photo_principale == 1) _visibilities.add('principal'); + // photo tag initialization tags = _photo.tags; - tagList = tags.isEmpty ? [] : tags.split(","); + tagList = tags.isEmpty ? [] : _photo.tags.split(","); } catch (e) { // set errorMessage for debug _errorMessage = 'Error loading photo: $e'; diff --git a/lib/ui/sync/tab_sync.dart b/lib/ui/sync/tab_sync.dart index 40d2895..325ae86 100644 --- a/lib/ui/sync/tab_sync.dart +++ b/lib/ui/sync/tab_sync.dart @@ -4,7 +4,6 @@ we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navba */ import 'package:mobdr/config/global_style.dart'; -import 'package:mobdr/main.dart'; import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/network/api_provider.dart'; import 'package:flutter/material.dart'; diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 049788b..83c4826 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import device_info_plus import objectbox_flutter_libs import package_info_plus import path_provider_foundation @@ -13,6 +14,7 @@ import sqflite import wakelock_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 7e1f7a8..ce8e0ea 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -149,10 +149,10 @@ packages: dependency: "direct main" description: name: camera - sha256: ad1c53c554a2f3e5708f3b01eb738d60b902bb61f7f4ad420c65c715e65a7379 + sha256: "7afc256902062cab191540c09908b98bc71e93d5e20b6486dbee51aa7731e9b2" url: "https://pub.dev" source: hosted - version: "0.10.3+2" + version: "0.10.4" camera_android: dependency: transitive description: @@ -297,6 +297,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: "435383ca05f212760b0a70426b5a90354fe6bd65992b3a5e27ab6ede74c02f5c" + url: "https://pub.dev" + source: hosted + version: "8.2.0" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + url: "https://pub.dev" + source: hosted + version: "7.0.0" dio: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 64b04d0..2f0dd83 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,7 +52,10 @@ dependencies: shimmer: 2.0.0 image_picker: ^0.8.7 - camera: ^0.10.3+2 + + # https://pub.dev/packages/camera + camera: ^0.10.4 + permission_handler: 10.2.0 image: ^4.0.15 @@ -75,6 +78,9 @@ dependencies: universal_io: 2.2.0 xml: ^6.2.2 + # https://pub.dev/packages/device_info_plus/install + device_info_plus: ^8.2.0 + dev_dependencies: flutter_test: sdk: flutter @@ -148,6 +154,7 @@ flutter: - assets/images/process_timeline/status3.png - assets/images/process_timeline/status4.png - assets/images/process_timeline/status5.png + - assets/images/simulator.jpeg - assets/lang/fr.json - assets/lang/en.json