From 98b1328448ea4ed905f23f46ef92d08f0edb8d89 Mon Sep 17 00:00:00 2001 From: Fransolet Thomas Date: Wed, 19 Oct 2022 17:54:15 +0200 Subject: [PATCH] Ajout instance + clean widgets - WIP --- .flutter-plugins-dependencies | 2 +- lib/Helpers/modelsHelper.dart | 40 +++++ lib/Models/visitContext.dart | 1 + lib/Screens/Home/configurations_list.dart | 149 ++++++++++++++++++ lib/Screens/Home/home.dart | 174 +++------------------- lib/Services/apiService.dart | 84 +++++++++++ lib/Services/downloadConfiguration.dart | 51 +++++++ 7 files changed, 345 insertions(+), 156 deletions(-) diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 11f53d0..9eba2d5 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"macos":[{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"qr_code_scanner","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2022-10-19 16:40:10.000329","version":"3.0.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"macos":[{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"qr_code_scanner","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2022-10-19 17:45:15.549712","version":"3.0.3"} \ No newline at end of file diff --git a/lib/Helpers/modelsHelper.dart b/lib/Helpers/modelsHelper.dart index e69de29..d1a0241 100644 --- a/lib/Helpers/modelsHelper.dart +++ b/lib/Helpers/modelsHelper.dart @@ -0,0 +1,40 @@ +import 'dart:convert'; + +import 'package:manager_api/api.dart'; + +class ModelsHelper { + static Map configurationToMap(ConfigurationDTO configuration) { + return { + 'id': configuration.id, + 'label': configuration.label, + 'title': jsonEncode(configuration.title), + 'imageId': configuration.imageId, + 'imageSource': configuration.imageSource, + 'primaryColor': configuration.primaryColor, + 'secondaryColor': configuration.secondaryColor, + 'languages': configuration.languages, + 'dateCreation': configuration.dateCreation!.toUtc().toIso8601String(), + 'isMobile': configuration.isMobile, + 'isTablet': configuration.isTablet, + 'isOffline': configuration.isOffline + }; + } + + static Map sectionToMap(SectionDTO section) { + return { + 'id': section.id, + 'label': section.label, + 'title': jsonEncode(section.title), + 'description': jsonEncode(section.description), + 'imageId': section.imageId, + 'imageSource': section.imageSource, + 'configurationId': section.configurationId, + 'isSubSection': section.isSubSection, + 'parentId': section.parentId, + 'type': section.type!.value, + 'data': section.data, + 'dateCreation': section.dateCreation!.toUtc().toIso8601String(), + 'orderOfElement': section.order, + }; + } +} \ No newline at end of file diff --git a/lib/Models/visitContext.dart b/lib/Models/visitContext.dart index fc61375..17e94c4 100644 --- a/lib/Models/visitContext.dart +++ b/lib/Models/visitContext.dart @@ -4,6 +4,7 @@ import 'package:manager_api/api.dart'; class VisitAppContext with ChangeNotifier{ String? id = ""; String? language = ""; + String instanceId = "633ee379d9405f32f166f047"; List? configurations; ConfigurationDTO? configuration; List? sectionIds; // Use to valid QR code found diff --git a/lib/Screens/Home/configurations_list.dart b/lib/Screens/Home/configurations_list.dart index e69de29..bab7dd5 100644 --- a/lib/Screens/Home/configurations_list.dart +++ b/lib/Screens/Home/configurations_list.dart @@ -0,0 +1,149 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:manager_api/api.dart'; +import 'package:mymuseum_visitapp/Components/Loading.dart'; +import 'package:mymuseum_visitapp/Components/SearchBox.dart'; +import 'package:mymuseum_visitapp/Components/SearchNumberBox.dart'; +import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; +import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; +import 'package:mymuseum_visitapp/Screens/Article/article.dart'; +import 'package:mymuseum_visitapp/Screens/Visit/visit.dart'; +import 'package:mymuseum_visitapp/Services/downloadConfiguration.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; +import 'package:mymuseum_visitapp/constants.dart'; +import 'package:provider/provider.dart'; + + +class ConfigurationsList extends StatefulWidget { + const ConfigurationsList({Key? key, required this.configurations, required this.alreadyDownloaded}) : super(key: key); + + final List configurations; + final List alreadyDownloaded; + + @override + State createState() => _ConfigurationsListState(); +} + +class _ConfigurationsListState extends State { + List configurations = []; + List alreadyDownloaded = []; + VisitAppContext? visitAppContext; + + @override + void initState() { + configurations = widget.configurations; + alreadyDownloaded = widget.alreadyDownloaded; + super.initState(); + } + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + final appContext = Provider.of(context); + visitAppContext = appContext.getContext(); + + return ListView.builder( + shrinkWrap: true, //I've set this as true here depending on what your listview content is + //physics: NeverScrollableScrollPhysics(),//This prevents scrolling, but may inhibit refresh indicator, remove as you need + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () async { + if(configurations[index].isOffline! && alreadyDownloaded.any((c) => c == configurations[index].id)) { + VisitAppContext visitAppContext = appContext.getContext(); + + if(!configurations[index].languages!.contains(visitAppContext.language)) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(TranslationHelper.getFromLocale("languageNotSupported", appContext)), backgroundColor: kBlue2), + ); + } else { + // Update context + visitAppContext.configuration = configurations[index]; + visitAppContext.sectionIds = List.from(await DatabaseHelper.instance.queryWithConfigurationId(DatabaseTableType.sections, visitAppContext.configuration!.id!)).map((e) => e.id).toList(); + appContext.setContext(visitAppContext); + + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => VisitPage(configurationId: configurations[index].id!), + )); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(TranslationHelper.getFromLocale("visitDownloadWarning", appContext)), backgroundColor: kBlue2), + ); + } + }, + child: Container( + height: size.height*0.15, + decoration: boxDecoration(configurations[index], false), + margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Padding( + padding: const EdgeInsets.only(top: 20, left: 10), + child: AutoSizeText( + TranslationHelper.get(configurations[index].title, appContext), + style: const TextStyle(fontSize: kMenuTitleDetailSize), + maxLines: 1, + ), + ), + ), + if(configurations[index].isOffline!) + Positioned( + bottom: 0, + right: 0, + child: Container( + width: 45, + height: 45, + decoration: BoxDecoration( + shape: BoxShape.rectangle, + color: kBlue1, + borderRadius: BorderRadius.circular(20.0), + ), + margin: const EdgeInsets.all(8), + child: InkWell( + onTap: () async { + downloadClicked(appContext, configurations[index]); + setState(() {}); // Force refresh + }, + child: configurations[index].isOffline! && !alreadyDownloaded.any((c) => c == configurations[index].id) ? + const Icon(Icons.download, color: Colors.white) : const Icon(Icons.refresh, color: Colors.white), + ), + ) + ) + ], + ), + ), + ); + }, + itemCount: configurations.length + ); + } + + Future downloadClicked(AppContext appContext, ConfigurationDTO configuration) async { + var isFinish = await DownloadConfiguration.downloadClicked(appContext, configuration); + print("C4EST FINIITO"); + print(isFinish); + } +} + +boxDecoration(ConfigurationDTO configuration, bool isSelected) { + return BoxDecoration( + color: Colors.white, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(20.0), + border: Border.all( + color: kBlue0.withOpacity(0.35), + width: 0.2, + ), + boxShadow: [ + BoxShadow( + color: kBlue0.withOpacity(0.35), + //spreadRadius: 0.15, + blurRadius: 27, + offset: const Offset(0, 15), // changes position of shadow + ), + ], + ); +} \ No newline at end of file diff --git a/lib/Screens/Home/home.dart b/lib/Screens/Home/home.dart index 646e5b7..edb04d6 100644 --- a/lib/Screens/Home/home.dart +++ b/lib/Screens/Home/home.dart @@ -1,24 +1,21 @@ import 'package:flutter/material.dart'; - -import 'dart:convert'; -import 'dart:io'; - import 'package:auto_size_text/auto_size_text.dart'; import 'package:manager_api/api.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; import 'package:mymuseum_visitapp/Components/Loading.dart'; -import 'package:mymuseum_visitapp/Components/ScannerBouton.dart'; import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; -import 'package:mymuseum_visitapp/Helpers/networkCheck.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; -import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Screens/Visit/visit.dart'; +import 'package:mymuseum_visitapp/Services/apiService.dart'; +import 'package:mymuseum_visitapp/Services/downloadConfiguration.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:provider/provider.dart'; +import 'configurations_list.dart'; + class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @@ -47,7 +44,7 @@ class _HomePageState extends State { width: size.width, height: size.height, child: FutureBuilder( - future: getConfigurations(appContext.clientAPI), + future: getConfigurationsCall(appContext.clientAPI, visitAppContext), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { configurations = List.from(snapshot.data).where((configuration) => configuration.isMobile!).toList(); @@ -57,7 +54,7 @@ class _HomePageState extends State { return Future(() => null); }, color: kSecondColor, - child: ListView.builder( + child: ConfigurationsList(alreadyDownloaded: alreadyDownloaded, configurations: configurations),/*ListView.builder( shrinkWrap: true, //I've set this as true here depending on what your listview content is //physics: NeverScrollableScrollPhysics(),//This prevents scrolling, but may inhibit refresh indicator, remove as you need itemBuilder: (BuildContext context, int index) { @@ -132,7 +129,7 @@ class _HomePageState extends State { ); }, itemCount: configurations.length - ), + ),*/ ); } else if (snapshot.connectionState == ConnectionState.none) { return Text(TranslationHelper.getFromLocale("noData", appContext)); @@ -153,154 +150,21 @@ class _HomePageState extends State { ); } - Future?> getConfigurations(Client client) async { - try { - List? configurations; - bool isOnline = await hasNetwork(); - configurations = List.from(await DatabaseHelper.instance.getData(DatabaseTableType.configurations)); - alreadyDownloaded = configurations.map((c) => c.id).toList(); - - if(isOnline) { - configurations = await client.configurationApi!.configurationGet(); - } - - return configurations; - - } catch (e) { - print(e); - print("IN CATCH"); - return []; - } + Future?> getConfigurationsCall(Client client, VisitAppContext? visitAppContext) async { + List? configurations; + configurations = List.from(await DatabaseHelper.instance.getData(DatabaseTableType.configurations)); + alreadyDownloaded = configurations.map((c) => c.id).toList(); + return ApiService.getConfigurations(client, visitAppContext); } - Future?> getAllSections(Client client, String configurationId) async { - try { - bool isOnline = await hasNetwork(); - if(isOnline) { - List? sections = await client.sectionApi!.sectionGetFromConfiguration(configurationId); - return sections; - } else { - return []; // TODO return local list.. - } - } catch (e) { - print(e); - print("IN CATCH"); - return []; - } - } - - Future getAndDownloadImage(Client client, ImageDTO imageDTO) async { - try { - bool isOnline = await hasNetwork(); - if(isOnline) { - var source = imageDTO.source_; - if(imageDTO.source_!.contains("localhost:5000")) { - source = imageDTO.source_!.replaceAll("http://localhost:5000", client.apiApi!.basePath); - } - HttpClient client2 = HttpClient(); - var _downloadData = []; - client2.getUrl(Uri.parse(source!)) - .then((HttpClientRequest request) { - return request.close(); - }) - .then((HttpClientResponse response) { - response.listen((d) => _downloadData.addAll(d), - onDone: () async { - final base64Str = base64.encode(_downloadData); - ResourceModel resourceModel = ResourceModel(id: imageDTO.resourceId, data: base64Str, type: ResourceType.Image); - await DatabaseHelper.instance.insert(DatabaseTableType.resources, resourceModel.toMap()); - //var test = base64.decode(base64Str); - //return base64Str; - /*setState(() { - base64TEST = base64Str; - });*/ - } - ); - }); - - //return file; - } else { - //return null; - } - } catch (e) { - print(e); - print("IN CATCH"); - //return []; - } - } - - Future downloadClicked(AppContext appContext, ConfigurationDTO configuration) async { - // Display config only downloaded - TODO - - // Update local DB - Configuration - await DatabaseHelper.instance.insert(DatabaseTableType.configurations, configurationToMap(configuration)); - - if(configuration.imageId != null) { - ImageDTO image = ImageDTO(resourceId: configuration.imageId, source_: configuration.imageSource); - await getAndDownloadImage(appContext.clientAPI, image); - } - - List? sections = await getAllSections(appContext.clientAPI, configuration.id!); - - if(sections!.isNotEmpty) { - List sectionsToKeep = sections.where((s) => s.type == SectionType.Article).toList(); // TODO handle other type of section - sectionsToKeep.sort((a,b) => a.order!.compareTo(b.order!)); - int newOrder = 0; - // Update local DB - Sections - for(var section in sectionsToKeep) { - section.order = newOrder; - await DatabaseHelper.instance.insert(DatabaseTableType.sections, sectionToMap(section)); - - // Download all images.. - ArticleDTO? articleDTO = ArticleDTO.fromJson(jsonDecode(section.data!)); - - if(articleDTO != null) { - for(var image in articleDTO.images!) { - await getAndDownloadImage(appContext.clientAPI, image); - } - } - newOrder = newOrder + 1; - } - } - } - - Map configurationToMap(ConfigurationDTO configuration) { - return { - 'id': configuration.id, - 'label': configuration.label, - 'title': jsonEncode(configuration.title), - 'imageId': configuration.imageId, - 'imageSource': configuration.imageSource, - 'primaryColor': configuration.primaryColor, - 'secondaryColor': configuration.secondaryColor, - 'languages': configuration.languages, - 'dateCreation': configuration.dateCreation!.toUtc().toIso8601String(), - 'isMobile': configuration.isMobile, - 'isTablet': configuration.isTablet, - 'isOffline': configuration.isOffline - }; - } - - Map sectionToMap(SectionDTO section) { - return { - 'id': section.id, - 'label': section.label, - 'title': jsonEncode(section.title), - 'description': jsonEncode(section.description), - 'imageId': section.imageId, - 'imageSource': section.imageSource, - 'configurationId': section.configurationId, - 'isSubSection': section.isSubSection, - 'parentId': section.parentId, - 'type': section.type!.value, - 'data': section.data, - 'dateCreation': section.dateCreation!.toUtc().toIso8601String(), - 'orderOfElement': section.order, - }; - } + /*Future downloadClicked(AppContext appContext, ConfigurationDTO configuration) async { + var isFinish = await DownloadConfiguration.downloadClicked(appContext, configuration); + print("C4EST FINIITO"); + print(isFinish); + }*/ } -boxDecoration(ConfigurationDTO configuration, bool isSelected) { +/*boxDecoration(ConfigurationDTO configuration, bool isSelected) { return BoxDecoration( color: Colors.white, shape: BoxShape.rectangle, @@ -318,4 +182,4 @@ boxDecoration(ConfigurationDTO configuration, bool isSelected) { ), ], ); -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/lib/Services/apiService.dart b/lib/Services/apiService.dart index e69de29..3d89b32 100644 --- a/lib/Services/apiService.dart +++ b/lib/Services/apiService.dart @@ -0,0 +1,84 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:manager_api/api.dart'; +import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; +import 'package:mymuseum_visitapp/Helpers/networkCheck.dart'; +import 'package:mymuseum_visitapp/Models/resourceModel.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; +import 'package:mymuseum_visitapp/client.dart'; + +class ApiService { + static Future?> getConfigurations(Client client, VisitAppContext? visitAppContext) async { + try { + List? configurations; + bool isOnline = await hasNetwork(); + if(isOnline) { + configurations = await client.configurationApi!.configurationGet(instanceId: visitAppContext?.instanceId); + } + return configurations; + + } catch (e) { + print(e); + print("IN CATCH"); + return []; + } + } + + static Future?> getAllSections(Client client, String configurationId) async { + try { + bool isOnline = await hasNetwork(); + if(isOnline) { + List? sections = await client.sectionApi!.sectionGetFromConfiguration(configurationId); + return sections; + } else { + return []; // TODO return local list.. + } + } catch (e) { + print(e); + print("IN CATCH"); + return []; + } + } + + static Future getAndDownloadImage(Client client, ImageDTO imageDTO) async { + try { + bool isOnline = await hasNetwork(); + if(isOnline) { + var source = imageDTO.source_; + if(imageDTO.source_!.contains("localhost:5000")) { + source = imageDTO.source_!.replaceAll("http://localhost:5000", client.apiApi!.basePath); + } + HttpClient client2 = HttpClient(); + var _downloadData = []; + client2.getUrl(Uri.parse(source!)) + .then((HttpClientRequest request) { + return request.close(); + }) + .then((HttpClientResponse response) { + response.listen((d) => _downloadData.addAll(d), + onDone: () async { + final base64Str = base64.encode(_downloadData); + ResourceModel resourceModel = ResourceModel(id: imageDTO.resourceId, data: base64Str, type: ResourceType.Image); + await DatabaseHelper.instance.insert(DatabaseTableType.resources, resourceModel.toMap()); + //var test = base64.decode(base64Str); + //return base64Str; + /*setState(() { + base64TEST = base64Str; + });*/ + } + ); + }); + + //return file; + } else { + //return null; + } + } catch (e) { + print(e); + print("IN CATCH"); + //return []; + } + } +} + diff --git a/lib/Services/downloadConfiguration.dart b/lib/Services/downloadConfiguration.dart index e69de29..6d5327a 100644 --- a/lib/Services/downloadConfiguration.dart +++ b/lib/Services/downloadConfiguration.dart @@ -0,0 +1,51 @@ +import 'dart:convert'; + +import 'package:manager_api/api.dart'; +import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; +import 'package:mymuseum_visitapp/Helpers/modelsHelper.dart'; +import 'package:mymuseum_visitapp/Services/apiService.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; + +class DownloadConfiguration { + static Future downloadClicked(AppContext appContext, ConfigurationDTO configuration) async { + + print("coucou downloadClicked"); + // Update local DB - Configuration + await DatabaseHelper.instance.insert(DatabaseTableType.configurations, ModelsHelper.configurationToMap(configuration)); + + + print("coucou downloadClicked 0"); + + if(configuration.imageId != null) { + ImageDTO image = ImageDTO(resourceId: configuration.imageId, source_: configuration.imageSource); + await ApiService.getAndDownloadImage(appContext.clientAPI, image); + } + + print("coucou downloadClicked 1"); + + List? sections = await ApiService.getAllSections(appContext.clientAPI, configuration.id!); + + if(sections!.isNotEmpty) { + List sectionsToKeep = sections.where((s) => s.type == SectionType.Article).toList(); // TODO handle other type of section + sectionsToKeep.sort((a,b) => a.order!.compareTo(b.order!)); + int newOrder = 0; + // Update local DB - Sections + for(var section in sectionsToKeep) { + section.order = newOrder; + await DatabaseHelper.instance.insert(DatabaseTableType.sections, ModelsHelper.sectionToMap(section)); + + // Download all images.. + ArticleDTO? articleDTO = ArticleDTO.fromJson(jsonDecode(section.data!)); + + if(articleDTO != null) { + for(var image in articleDTO.images!) { + await ApiService.getAndDownloadImage(appContext.clientAPI, image); + } + } + newOrder = newOrder + 1; + } + } + + return true; + } +}