commit 5890884adb8bc56673eaf91ba1457c62fb340f86 Author: barhen Date: Tue Feb 15 19:01:31 2022 +0000 init diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..fe5a922 Binary files /dev/null and b/.DS_Store differ diff --git a/._.DS_Store b/._.DS_Store new file mode 100644 index 0000000..338bd7b Binary files /dev/null and b/._.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6742f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# ---> Dart +# See https://www.dartlang.org/guides/libraries/private-files + +# Files and directories created by pub +.dart_tool/ +.packages +build/ +.vscode/ +.VSCodeCounter/ +# If you're building an application, you may want to check-in your pubspec.lock +pubspec.lock + +# Directory created by dartdoc +# If you don't generate documentation locally you can remove this line. +doc/api/ + +# dotenv environment variables file +.env* + +# Avoid committing generated Javascript files: +*.dart.js +*.info.json # Produced by the --dump-info flag. +*.js # When generated by dart2js. Don't specify *.js if your + # project includes source files written in JavaScript. +*.js_ +*.js.deps +*.js.map + +.flutter-plugins +.flutter-plugins-dependencies diff --git a/InViewList.txt b/InViewList.txt new file mode 100644 index 0000000..bda900d --- /dev/null +++ b/InViewList.txt @@ -0,0 +1,79 @@ + // Stack( + // fit: StackFit.expand, + // children: [ + // InViewNotifierList( + // scrollDirection: Axis.vertical, + // initialInViewIds: ['0'], + // isInViewPortCondition: (double deltaTop, double deltaBottom, + // double viewPortDimension) { + // return deltaTop < (0.5 * viewPortDimension) && + // deltaBottom > (0.5 * viewPortDimension); + // }, + // throttleDuration: Duration(seconds: 1), + // itemCount: 10, + // builder: (BuildContext context, int index) { + // return Container( + // width: double.infinity, + // height: MediaQuery.of(context).size.height, + // alignment: Alignment.center, + // // margin: EdgeInsets.symmetric(vertical: 50.0), + // child: LayoutBuilder( + // builder: (BuildContext context, BoxConstraints constraints) { + // return InViewNotifierWidget( + // id: '$index', + // builder: + // (BuildContext context, bool isInView, Widget child) { + // return ViewPost( + // play: isInView, + // postedAd: new Post( + // aspect: widget.postedAd[index].aspect, + // path: + // "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", + // postID: widget.postedAd[index].postID, + // publisherID: widget.postedAd[index].publisherID, + // thumbnail: widget.postedAd[index].thumbnail, + // timestamp: widget.postedAd[index].timestamp, + // title: widget.postedAd[index].title, + // ), + // //url: + // // 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' + // ); + // }, + // ); + // }, + // ), + // ); + // }, + // ), + // Align( + // alignment: Alignment.center, + // child: Container( + // height: 1.0, + // color: Colors.redAccent, + // ), + // ), + // Align( + // alignment: Alignment.topLeft, + // child: InkWell( + // onTap: () { + // Navigator.pop(context); + // }, + // child: Container( + // height: 40, + // width: 40, + // margin: EdgeInsets.symmetric( + // vertical: 30.0, + // horizontal: 10, + // ), + // decoration: BoxDecoration( + // color: Color.fromRGBO(0, 0, 0, 0.4), + // shape: BoxShape.circle), + // child: Icon( + // Icons.arrow_back_ios, + // color: Colors.white, + // ), + // ), + // ), + // ), + // ], + // ), \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..49d9662 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# teso + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/android/._.gitignore b/android/._.gitignore new file mode 100644 index 0000000..338bd7b Binary files /dev/null and b/android/._.gitignore differ diff --git a/android/._.gradle b/android/._.gradle new file mode 100644 index 0000000..338bd7b Binary files /dev/null and b/android/._.gradle differ diff --git a/android/._.idea b/android/._.idea new file mode 100644 index 0000000..338bd7b Binary files /dev/null and b/android/._.idea differ diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..bc2100d --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,7 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java diff --git a/android/.idea/.gitignore b/android/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/android/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/android/.idea/assetWizardSettings.xml b/android/.idea/assetWizardSettings.xml new file mode 100644 index 0000000..dcde322 --- /dev/null +++ b/android/.idea/assetWizardSettings.xml @@ -0,0 +1,156 @@ + + + + + + \ No newline at end of file diff --git a/android/.idea/codeStyles/Project.xml b/android/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..88ea3aa --- /dev/null +++ b/android/.idea/codeStyles/Project.xml @@ -0,0 +1,122 @@ + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/android/.idea/codeStyles/codeStyleConfig.xml b/android/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/android/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/android/.idea/compiler.xml b/android/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/android/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml new file mode 100644 index 0000000..1a265c6 --- /dev/null +++ b/android/.idea/gradle.xml @@ -0,0 +1,76 @@ + + + + + + + \ No newline at end of file diff --git a/android/.idea/jarRepositories.xml b/android/.idea/jarRepositories.xml new file mode 100644 index 0000000..cfbe04b --- /dev/null +++ b/android/.idea/jarRepositories.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__C__src_flutter__pub_cache_hosted_pub_dartlang_org_camera_deep_ar_0_0_1_android_libs_deepar_aar.xml b/android/.idea/libraries/Gradle__C__src_flutter__pub_cache_hosted_pub_dartlang_org_camera_deep_ar_0_0_1_android_libs_deepar_aar.xml new file mode 100644 index 0000000..a503c4a --- /dev/null +++ b/android/.idea/libraries/Gradle__C__src_flutter__pub_cache_hosted_pub_dartlang_org_camera_deep_ar_0_0_1_android_libs_deepar_aar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle_______build_app_intermediates_flutter_debug_libs_jar.xml b/android/.idea/libraries/Gradle_______build_app_intermediates_flutter_debug_libs_jar.xml new file mode 100644 index 0000000..106c5b3 --- /dev/null +++ b/android/.idea/libraries/Gradle_______build_app_intermediates_flutter_debug_libs_jar.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml new file mode 100644 index 0000000..458ea8f --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_activity_activity_1_2_3_aar.xml b/android/.idea/libraries/Gradle__androidx_activity_activity_1_2_3_aar.xml new file mode 100644 index 0000000..e732b26 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_activity_activity_1_2_3_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0.xml b/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0.xml new file mode 100644 index 0000000..b2158ac --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0_jar.xml b/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0_jar.xml new file mode 100644 index 0000000..5b17db6 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0_jar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_2_0.xml b/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_2_0.xml new file mode 100644 index 0000000..74437d7 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_annotation_annotation_1_2_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_0_0_aar.xml new file mode 100644 index 0000000..7114b1a --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_0_2_aar.xml b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_0_2_aar.xml new file mode 100644 index 0000000..d64d5a8 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_0_2_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_1_0_aar.xml new file mode 100644 index 0000000..24aabd0 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_1_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_2_0_aar.xml new file mode 100644 index 0000000..be80a6d --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_2_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_3_0_aar.xml b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_3_0_aar.xml new file mode 100644 index 0000000..b49aedd --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_3_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_1_0_aar.xml new file mode 100644 index 0000000..416e446 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_2_0_aar.xml new file mode 100644 index 0000000..781df5a --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_2_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_3_0_aar.xml b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_3_0_aar.xml new file mode 100644 index 0000000..49e4e24 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_3_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml b/android/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml new file mode 100644 index 0000000..2208415 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0_jar.xml b/android/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0_jar.xml new file mode 100644 index 0000000..a7f501b --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0_jar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml new file mode 100644 index 0000000..231a5b3 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_1_0_aar.xml new file mode 100644 index 0000000..ab8cda3 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml new file mode 100644 index 0000000..3437956 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_browser_browser_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_browser_browser_1_0_0_aar.xml new file mode 100644 index 0000000..a32ed76 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_browser_browser_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_browser_browser_1_3_0_aar.xml b/android/.idea/libraries/Gradle__androidx_browser_browser_1_3_0_aar.xml new file mode 100644 index 0000000..d7e7180 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_browser_browser_1_3_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml new file mode 100644 index 0000000..22bc731 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml b/android/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml new file mode 100644 index 0000000..eafc05e --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_collection_collection_1_1_0_jar.xml b/android/.idea/libraries/Gradle__androidx_collection_collection_1_1_0_jar.xml new file mode 100644 index 0000000..ecb16c3 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_collection_collection_1_1_0_jar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_1_1_3_aar.xml b/android/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_1_1_3_aar.xml new file mode 100644 index 0000000..c90f313 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_1_1_3_aar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_1_1_3.xml b/android/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_1_1_3.xml new file mode 100644 index 0000000..e040f45 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_1_1_3.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_0_0_aar.xml new file mode 100644 index 0000000..d330830 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_0_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_core_core_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_core_core_1_1_0_aar.xml new file mode 100644 index 0000000..c20cd6d --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_core_core_1_1_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_core_core_1_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_core_core_1_2_0_aar.xml new file mode 100644 index 0000000..88e6e46 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_core_core_1_2_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_core_core_1_3_0_aar.xml b/android/.idea/libraries/Gradle__androidx_core_core_1_3_0_aar.xml new file mode 100644 index 0000000..27b3e2f --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_core_core_1_3_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_core_core_1_3_1_aar.xml b/android/.idea/libraries/Gradle__androidx_core_core_1_3_1_aar.xml new file mode 100644 index 0000000..f9147cf --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_core_core_1_3_1_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_core_core_1_3_2_aar.xml b/android/.idea/libraries/Gradle__androidx_core_core_1_3_2_aar.xml new file mode 100644 index 0000000..67868aa --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_core_core_1_3_2_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_core_core_1_5_0_aar.xml b/android/.idea/libraries/Gradle__androidx_core_core_1_5_0_aar.xml new file mode 100644 index 0000000..c15ec16 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_core_core_1_5_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_core_core_ktx_1_3_2_aar.xml b/android/.idea/libraries/Gradle__androidx_core_core_ktx_1_3_2_aar.xml new file mode 100644 index 0000000..0a4b25a --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_core_core_ktx_1_3_2_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml new file mode 100644 index 0000000..9732751 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml new file mode 100644 index 0000000..e2aa786 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_databinding_viewbinding_4_2_1_aar.xml b/android/.idea/libraries/Gradle__androidx_databinding_viewbinding_4_2_1_aar.xml new file mode 100644 index 0000000..5942d8a --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_databinding_viewbinding_4_2_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml new file mode 100644 index 0000000..ab91b59 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml new file mode 100644 index 0000000..6db1200 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_0_0_aar.xml new file mode 100644 index 0000000..39f8b14 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_3_0_aar.xml b/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_3_0_aar.xml new file mode 100644 index 0000000..c5da476 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_3_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_3_2_aar.xml b/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_3_2_aar.xml new file mode 100644 index 0000000..4c69758 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_3_2_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml new file mode 100644 index 0000000..bf6125c --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_fragment_fragment_1_3_4_aar.xml b/android/.idea/libraries/Gradle__androidx_fragment_fragment_1_3_4_aar.xml new file mode 100644 index 0000000..3c33b63 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_fragment_fragment_1_3_4_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml new file mode 100644 index 0000000..bf1f715 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml new file mode 100644 index 0000000..6a11726 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml new file mode 100644 index 0000000..9ce25f0 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_v4_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_v4_1_0_0_aar.xml new file mode 100644 index 0000000..a41ba15 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_legacy_legacy_support_v4_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_2_0.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_2_0.xml new file mode 100644 index 0000000..f7d6479 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_2_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_3_1.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_3_1.xml new file mode 100644 index 0000000..a1e1912 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_3_1.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_java8_2_2_0.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_java8_2_2_0.xml new file mode 100644 index 0000000..fc8d677 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_java8_2_2_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_extensions_2_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_extensions_2_1_0_aar.xml new file mode 100644 index 0000000..c6278db --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_extensions_2_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml new file mode 100644 index 0000000..33a56f8 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_1_0_aar.xml new file mode 100644 index 0000000..6242132 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml new file mode 100644 index 0000000..1722737 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_1_0_aar.xml new file mode 100644 index 0000000..4c7595c --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_3_1_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_3_1_aar.xml new file mode 100644 index 0000000..5fcddfb --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_3_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_process_2_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_process_2_1_0_aar.xml new file mode 100644 index 0000000..beaeb32 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_process_2_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_2_0_aar.xml new file mode 100644 index 0000000..866c83f --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_2_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_3_1_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_3_1_aar.xml new file mode 100644 index 0000000..dc4d837 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_3_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_service_2_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_service_2_1_0_aar.xml new file mode 100644 index 0000000..ca08d04 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_service_2_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml new file mode 100644 index 0000000..4206bab --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_3_1_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_3_1_aar.xml new file mode 100644 index 0000000..5c0364a --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_3_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_savedstate_2_3_1_aar.xml b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_savedstate_2_3_1_aar.xml new file mode 100644 index 0000000..9dc2797 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_savedstate_2_3_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml new file mode 100644 index 0000000..487b7a5 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml new file mode 100644 index 0000000..65c7e48 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_media_media_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_media_media_1_0_0_aar.xml new file mode 100644 index 0000000..c77bcfd --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_media_media_1_0_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_media_media_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_media_media_1_1_0_aar.xml new file mode 100644 index 0000000..8cfd7d7 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_media_media_1_1_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml new file mode 100644 index 0000000..34f2bce --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_0_0_aar.xml new file mode 100644 index 0000000..e31d8ab --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml new file mode 100644 index 0000000..0f039c1 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_1_0_aar.xml new file mode 100644 index 0000000..56c5eb9 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml new file mode 100644 index 0000000..96842d2 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml new file mode 100644 index 0000000..102aee0 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_1_0_aar.xml new file mode 100644 index 0000000..c8038ed --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_1_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_core_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_core_1_0_0_aar.xml new file mode 100644 index 0000000..e62fac7 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_core_1_0_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_core_1_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_core_1_2_0_aar.xml new file mode 100644 index 0000000..9fafd22 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_core_1_2_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_core_1_3_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_core_1_3_0_aar.xml new file mode 100644 index 0000000..844e738 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_core_1_3_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_espresso_espresso_core_3_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_espresso_espresso_core_3_2_0_aar.xml new file mode 100644 index 0000000..0e30610 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_espresso_espresso_core_3_2_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_espresso_espresso_idling_resource_3_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_espresso_espresso_idling_resource_3_2_0_aar.xml new file mode 100644 index 0000000..f76b616 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_espresso_espresso_idling_resource_3_2_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_monitor_1_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_monitor_1_2_0_aar.xml new file mode 100644 index 0000000..7f1c03a --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_monitor_1_2_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_monitor_1_3_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_monitor_1_3_0_aar.xml new file mode 100644 index 0000000..06204c6 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_monitor_1_3_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_rules_1_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_rules_1_2_0_aar.xml new file mode 100644 index 0000000..9bef00e --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_rules_1_2_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_test_runner_1_2_0_aar.xml b/android/.idea/libraries/Gradle__androidx_test_runner_1_2_0_aar.xml new file mode 100644 index 0000000..52bcd4a --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_test_runner_1_2_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_transition_transition_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_transition_transition_1_0_0_aar.xml new file mode 100644 index 0000000..5055e4c --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_transition_transition_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_transition_transition_1_4_1_aar.xml b/android/.idea/libraries/Gradle__androidx_transition_transition_1_4_1_aar.xml new file mode 100644 index 0000000..cd4b8ec --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_transition_transition_1_4_1_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_0_1_aar.xml b/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_0_1_aar.xml new file mode 100644 index 0000000..7333da0 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_0_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml new file mode 100644 index 0000000..d2a1226 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_0_0_aar.xml new file mode 100644 index 0000000..3205cee --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml new file mode 100644 index 0000000..de23b15 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml b/android/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml new file mode 100644 index 0000000..62a7361 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_1_aar.xml b/android/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_1_aar.xml new file mode 100644 index 0000000..b020836 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml new file mode 100644 index 0000000..21f2000 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_webkit_webkit_1_0_0_aar.xml b/android/.idea/libraries/Gradle__androidx_webkit_webkit_1_0_0_aar.xml new file mode 100644 index 0000000..a4c7123 --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_webkit_webkit_1_0_0_aar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__androidx_webkit_webkit_1_4_0_aar.xml b/android/.idea/libraries/Gradle__androidx_webkit_webkit_1_4_0_aar.xml new file mode 100644 index 0000000..6e6c36d --- /dev/null +++ b/android/.idea/libraries/Gradle__androidx_webkit_webkit_1_4_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__backport_util_concurrent_backport_util_concurrent_3_1.xml b/android/.idea/libraries/Gradle__backport_util_concurrent_backport_util_concurrent_3_1.xml new file mode 100644 index 0000000..63bd40c --- /dev/null +++ b/android/.idea/libraries/Gradle__backport_util_concurrent_backport_util_concurrent_3_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__classworlds_classworlds_1_1_alpha_2.xml b/android/.idea/libraries/Gradle__classworlds_classworlds_1_1_alpha_2.xml new file mode 100644 index 0000000..1b2e2c4 --- /dev/null +++ b/android/.idea/libraries/Gradle__classworlds_classworlds_1_1_alpha_2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_almworks_sqlite4java_sqlite4java_0_282.xml b/android/.idea/libraries/Gradle__com_almworks_sqlite4java_sqlite4java_0_282.xml new file mode 100644 index 0000000..d6d593b --- /dev/null +++ b/android/.idea/libraries/Gradle__com_almworks_sqlite4java_sqlite4java_0_282.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_arthenica_mobile_ffmpeg_full_gpl_4_4_LTS_aar.xml b/android/.idea/libraries/Gradle__com_arthenica_mobile_ffmpeg_full_gpl_4_4_LTS_aar.xml new file mode 100644 index 0000000..d761f51 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_arthenica_mobile_ffmpeg_full_gpl_4_4_LTS_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_github_MasayukiSuda_Mp4Composer_android_v0_3_9_aar.xml b/android/.idea/libraries/Gradle__com_github_MasayukiSuda_Mp4Composer_android_v0_3_9_aar.xml new file mode 100644 index 0000000..f3362a6 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_github_MasayukiSuda_Mp4Composer_android_v0_3_9_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_github_yalantis_ucrop_2_2_7_aar.xml b/android/.idea/libraries/Gradle__com_github_yalantis_ucrop_2_2_7_aar.xml new file mode 100644 index 0000000..7cc028c --- /dev/null +++ b/android/.idea/libraries/Gradle__com_github_yalantis_ucrop_2_2_7_aar.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_annotations_4_1_1_4.xml b/android/.idea/libraries/Gradle__com_google_android_annotations_4_1_1_4.xml new file mode 100644 index 0000000..d6a5e2f --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_annotations_4_1_1_4.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework_2_1.xml b/android/.idea/libraries/Gradle__com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework_2_1.xml new file mode 100644 index 0000000..c2c61c3 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_api_2_2_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_api_2_2_1_aar.xml new file mode 100644 index 0000000..611ca42 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_api_2_2_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_api_3_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_api_3_0_0_aar.xml new file mode 100644 index 0000000..62b73ff --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_api_3_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_backend_cct_2_3_3_aar.xml b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_backend_cct_2_3_3_aar.xml new file mode 100644 index 0000000..f4faa6c --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_backend_cct_2_3_3_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_backend_cct_3_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_backend_cct_3_0_0_aar.xml new file mode 100644 index 0000000..7e813a5 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_backend_cct_3_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_runtime_2_2_6_aar.xml b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_runtime_2_2_6_aar.xml new file mode 100644 index 0000000..b1cdf86 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_runtime_2_2_6_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_runtime_3_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_runtime_3_0_0_aar.xml new file mode 100644 index 0000000..3b67a8e --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_datatransport_transport_runtime_3_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_common_2_12_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_common_2_12_1_aar.xml new file mode 100644 index 0000000..f2be4d9 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_common_2_12_1_aar.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_core_2_12_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_core_2_12_1_aar.xml new file mode 100644 index 0000000..50fb837 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_core_2_12_1_aar.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_dash_2_12_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_dash_2_12_1_aar.xml new file mode 100644 index 0000000..189e454 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_dash_2_12_1_aar.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_extractor_2_12_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_extractor_2_12_1_aar.xml new file mode 100644 index 0000000..f6399e2 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_extractor_2_12_1_aar.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_hls_2_12_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_hls_2_12_1_aar.xml new file mode 100644 index 0000000..b9c5823 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_hls_2_12_1_aar.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_smoothstreaming_2_12_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_smoothstreaming_2_12_1_aar.xml new file mode 100644 index 0000000..391602c --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_exoplayer_exoplayer_smoothstreaming_2_12_1_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_16_0_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_16_0_1_aar.xml new file mode 100644 index 0000000..ba64677 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_16_0_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_api_phone_16_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_api_phone_16_0_0_aar.xml new file mode 100644 index 0000000..bbe067c --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_api_phone_16_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_api_phone_17_4_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_api_phone_17_4_0_aar.xml new file mode 100644 index 0000000..f6d6aa6 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_api_phone_17_4_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_base_16_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_base_16_0_0_aar.xml new file mode 100644 index 0000000..4406780 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_auth_base_16_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_16_0_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_16_0_1_aar.xml new file mode 100644 index 0000000..2147066 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_16_0_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_0_0_aar.xml new file mode 100644 index 0000000..bd11a08 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_1_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_1_0_aar.xml new file mode 100644 index 0000000..bccf01e --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_5_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_5_0_aar.xml new file mode 100644 index 0000000..8f5f08b --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_5_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_6_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_6_0_aar.xml new file mode 100644 index 0000000..7f74ca3 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_base_17_6_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_16_0_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_16_0_1_aar.xml new file mode 100644 index 0000000..f67639b --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_16_0_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_0_0_aar.xml new file mode 100644 index 0000000..d69c573 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_1_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_1_0_aar.xml new file mode 100644 index 0000000..84d15b3 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_5_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_5_0_aar.xml new file mode 100644 index 0000000..99a43be --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_5_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_6_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_6_0_aar.xml new file mode 100644 index 0000000..98ca021 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_basement_17_6_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_clearcut_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_clearcut_17_0_0_aar.xml new file mode 100644 index 0000000..8b996ff --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_clearcut_17_0_0_aar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_cloud_messaging_16_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_cloud_messaging_16_0_0_aar.xml new file mode 100644 index 0000000..71b0a9e --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_cloud_messaging_16_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_flags_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_flags_17_0_0_aar.xml new file mode 100644 index 0000000..927bf05 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_flags_17_0_0_aar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_location_16_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_location_16_0_0_aar.xml new file mode 100644 index 0000000..1f533d5 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_location_16_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_location_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_location_17_0_0_aar.xml new file mode 100644 index 0000000..74443c7 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_location_17_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_maps_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_maps_17_0_0_aar.xml new file mode 100644 index 0000000..7d49e8a --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_maps_17_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_barcode_scanning_16_1_4_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_barcode_scanning_16_1_4_aar.xml new file mode 100644 index 0000000..e41ce50 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_barcode_scanning_16_1_4_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_face_detection_16_1_6_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_face_detection_16_1_6_aar.xml new file mode 100644 index 0000000..d0ae51b --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_face_detection_16_1_6_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_text_recognition_16_1_3_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_text_recognition_16_1_3_aar.xml new file mode 100644 index 0000000..00d9610 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_mlkit_text_recognition_16_1_3_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_phenotype_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_phenotype_17_0_0_aar.xml new file mode 100644 index 0000000..c0a896f --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_phenotype_17_0_0_aar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_places_placereport_16_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_places_placereport_16_0_0_aar.xml new file mode 100644 index 0000000..9b15220 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_places_placereport_16_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_places_placereport_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_places_placereport_17_0_0_aar.xml new file mode 100644 index 0000000..ac597b5 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_places_placereport_17_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_safetynet_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_safetynet_17_0_0_aar.xml new file mode 100644 index 0000000..4cba870 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_safetynet_17_0_0_aar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_stats_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_stats_17_0_0_aar.xml new file mode 100644 index 0000000..40951c8 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_stats_17_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_16_0_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_16_0_1_aar.xml new file mode 100644 index 0000000..06e84e2 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_16_0_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_0_0_aar.xml new file mode 100644 index 0000000..4a53e9d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_2_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_2_0_aar.xml new file mode 100644 index 0000000..23f5c93 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_2_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_2_1_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_2_1_aar.xml new file mode 100644 index 0000000..d6c0263 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_tasks_17_2_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_20_1_3_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_20_1_3_aar.xml new file mode 100644 index 0000000..561315d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_20_1_3_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_common_19_1_3_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_common_19_1_3_aar.xml new file mode 100644 index 0000000..cb4ca12 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_common_19_1_3_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_face_contour_internal_16_1_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_face_contour_internal_16_1_0_aar.xml new file mode 100644 index 0000000..946b0f1 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_gms_play_services_vision_face_contour_internal_16_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_android_material_material_1_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_android_material_material_1_0_0_aar.xml new file mode 100644 index 0000000..ab4e8d4 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_android_material_material_1_0_0_aar.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_auto_auto_common_0_8.xml b/android/.idea/libraries/Gradle__com_google_auto_auto_common_0_8.xml new file mode 100644 index 0000000..2ad93de --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_auto_auto_common_0_8.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_auto_service_auto_service_1_0_rc4.xml b/android/.idea/libraries/Gradle__com_google_auto_service_auto_service_1_0_rc4.xml new file mode 100644 index 0000000..f262130 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_auto_service_auto_service_1_0_rc4.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_2_0_1.xml b/android/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_2_0_1.xml new file mode 100644 index 0000000..2b834ea --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_2_0_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_3_0_2.xml b/android/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_3_0_2.xml new file mode 100644 index 0000000..a96d725 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_3_0_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_code_gson_gson_2_8_6.xml b/android/.idea/libraries/Gradle__com_google_code_gson_gson_2_8_6.xml new file mode 100644 index 0000000..45500a2 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_code_gson_gson_2_8_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_2_0.xml b/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_2_0.xml new file mode 100644 index 0000000..9aaff30 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_2_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_3_2.xml b/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_3_2.xml new file mode 100644 index 0000000..61dea6f --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_3_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_3_4.xml b/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_3_4.xml new file mode 100644 index 0000000..705d2d6 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_errorprone_error_prone_annotations_2_3_4.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_annotations_16_0_0.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_annotations_16_0_0.xml new file mode 100644 index 0000000..0d56cda --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_annotations_16_0_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_21_0_1_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_21_0_1_aar.xml new file mode 100644 index 0000000..16b2556 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_21_0_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_interop_19_0_2_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_interop_19_0_2_aar.xml new file mode 100644 index 0000000..a72fb8e --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_interop_19_0_2_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_interop_20_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_interop_20_0_0_aar.xml new file mode 100644 index 0000000..7d946c6 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_auth_interop_20_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_common_20_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_common_20_0_0_aar.xml new file mode 100644 index 0000000..3531f0b --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_common_20_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_components_16_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_components_16_0_0_aar.xml new file mode 100644 index 0000000..fd79fd9 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_components_16_0_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_components_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_components_17_0_0_aar.xml new file mode 100644 index 0000000..a3f38fc --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_components_17_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_database_collection_18_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_database_collection_18_0_0_aar.xml new file mode 100644 index 0000000..2072f4b --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_database_collection_18_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_datatransport_18_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_datatransport_18_0_0_aar.xml new file mode 100644 index 0000000..a3d7d6d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_datatransport_18_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_dynamic_links_20_1_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_dynamic_links_20_1_0_aar.xml new file mode 100644 index 0000000..2a93c65 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_dynamic_links_20_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_16_1_0.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_16_1_0.xml new file mode 100644 index 0000000..2a624fc --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_16_1_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_17_0_0.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_17_0_0.xml new file mode 100644 index 0000000..5d78a1d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_17_0_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_json_17_1_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_json_17_1_0_aar.xml new file mode 100644 index 0000000..9b96bfc --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_json_17_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_json_18_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_json_18_0_0_aar.xml new file mode 100644 index 0000000..c4bbdb7 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_encoders_json_18_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_firestore_23_0_1_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_firestore_23_0_1_aar.xml new file mode 100644 index 0000000..d3427e0 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_firestore_23_0_1_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_iid_interop_17_1_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_iid_interop_17_1_0_aar.xml new file mode 100644 index 0000000..d298cfc --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_iid_interop_17_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_installations_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_installations_17_0_0_aar.xml new file mode 100644 index 0000000..cb719e2 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_installations_17_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_installations_interop_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_installations_interop_17_0_0_aar.xml new file mode 100644 index 0000000..8a7604d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_installations_interop_17_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_measurement_connector_19_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_measurement_connector_19_0_0_aar.xml new file mode 100644 index 0000000..a4dc94a --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_measurement_connector_19_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_firebase_messaging_22_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_firebase_messaging_22_0_0_aar.xml new file mode 100644 index 0000000..f03940d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_firebase_messaging_22_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_firebase_protolite_well_known_types_18_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_firebase_protolite_well_known_types_18_0_0_aar.xml new file mode 100644 index 0000000..d6463a5 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_firebase_protolite_well_known_types_18_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_guava_failureaccess_1_0_1.xml b/android/.idea/libraries/Gradle__com_google_guava_failureaccess_1_0_1.xml new file mode 100644 index 0000000..aeb2fc7 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_guava_failureaccess_1_0_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_guava_guava_20_0.xml b/android/.idea/libraries/Gradle__com_google_guava_guava_20_0.xml new file mode 100644 index 0000000..11bc9db --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_guava_guava_20_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_guava_guava_27_0_1_jre.xml b/android/.idea/libraries/Gradle__com_google_guava_guava_27_0_1_jre.xml new file mode 100644 index 0000000..bb089ad --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_guava_guava_27_0_1_jre.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_guava_guava_27_1_android.xml b/android/.idea/libraries/Gradle__com_google_guava_guava_27_1_android.xml new file mode 100644 index 0000000..62babcf --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_guava_guava_27_1_android.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_guava_guava_28_1_android.xml b/android/.idea/libraries/Gradle__com_google_guava_guava_28_1_android.xml new file mode 100644 index 0000000..703e775 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_guava_guava_28_1_android.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_guava_listenablefuture_1_0.xml b/android/.idea/libraries/Gradle__com_google_guava_listenablefuture_1_0.xml new file mode 100644 index 0000000..09da23b --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_guava_listenablefuture_1_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_guava_listenablefuture_9999_0_empty_to_avoid_conflict_with_guava.xml b/android/.idea/libraries/Gradle__com_google_guava_listenablefuture_9999_0_empty_to_avoid_conflict_with_guava.xml new file mode 100644 index 0000000..11f8cce --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_guava_listenablefuture_9999_0_empty_to_avoid_conflict_with_guava.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_j2objc_j2objc_annotations_1_1.xml b/android/.idea/libraries/Gradle__com_google_j2objc_j2objc_annotations_1_1.xml new file mode 100644 index 0000000..51270bf --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_j2objc_j2objc_annotations_1_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_j2objc_j2objc_annotations_1_3.xml b/android/.idea/libraries/Gradle__com_google_j2objc_j2objc_annotations_1_3.xml new file mode 100644 index 0000000..7a61cf9 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_j2objc_j2objc_annotations_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_barcode_scanning_16_1_1_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_barcode_scanning_16_1_1_aar.xml new file mode 100644 index 0000000..010c59b --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_barcode_scanning_16_1_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_common_17_1_1_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_common_17_1_1_aar.xml new file mode 100644 index 0000000..c1416b4 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_common_17_1_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_face_detection_16_0_7_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_face_detection_16_0_7_aar.xml new file mode 100644 index 0000000..6db2f52 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_face_detection_16_0_7_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_17_0_3_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_17_0_3_aar.xml new file mode 100644 index 0000000..eb2681c --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_17_0_3_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_common_17_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_common_17_0_0_aar.xml new file mode 100644 index 0000000..35915bf --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_common_17_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_default_common_16_0_0_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_default_common_16_0_0_aar.xml new file mode 100644 index 0000000..e5a009a --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_image_labeling_default_common_16_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_object_detection_16_2_4_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_object_detection_16_2_4_aar.xml new file mode 100644 index 0000000..8343c0e --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_object_detection_16_2_4_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_object_detection_common_17_0_1_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_object_detection_common_17_0_1_aar.xml new file mode 100644 index 0000000..98cbd11 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_object_detection_common_17_0_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_vision_common_16_4_0_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_vision_common_16_4_0_aar.xml new file mode 100644 index 0000000..043c878 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_vision_common_16_4_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_mlkit_vision_internal_vkp_18_1_0_aar.xml b/android/.idea/libraries/Gradle__com_google_mlkit_vision_internal_vkp_18_1_0_aar.xml new file mode 100644 index 0000000..12a460f --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_mlkit_vision_internal_vkp_18_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_protobuf_protobuf_java_2_6_1.xml b/android/.idea/libraries/Gradle__com_google_protobuf_protobuf_java_2_6_1.xml new file mode 100644 index 0000000..63fd339 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_protobuf_protobuf_java_2_6_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_protobuf_protobuf_javalite_3_14_0.xml b/android/.idea/libraries/Gradle__com_google_protobuf_protobuf_javalite_3_14_0.xml new file mode 100644 index 0000000..52b2a18 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_protobuf_protobuf_javalite_3_14_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_google_zxing_core_3_3_0.xml b/android/.idea/libraries/Gradle__com_google_zxing_core_3_3_0.xml new file mode 100644 index 0000000..6238603 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_google_zxing_core_3_3_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_googlecode_libphonenumber_libphonenumber_8_12_24.xml b/android/.idea/libraries/Gradle__com_googlecode_libphonenumber_libphonenumber_8_12_24.xml new file mode 100644 index 0000000..4a9f03d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_googlecode_libphonenumber_libphonenumber_8_12_24.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_ibm_icu_icu4j_53_1.xml b/android/.idea/libraries/Gradle__com_ibm_icu_icu4j_53_1.xml new file mode 100644 index 0000000..d8c1108 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_ibm_icu_icu4j_53_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_jakewharton_threetenabp_threetenabp_1_2_3_aar.xml b/android/.idea/libraries/Gradle__com_jakewharton_threetenabp_threetenabp_1_2_3_aar.xml new file mode 100644 index 0000000..28bc2f0 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_jakewharton_threetenabp_threetenabp_1_2_3_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_journeyapps_zxing_android_embedded_4_1_0_aar.xml b/android/.idea/libraries/Gradle__com_journeyapps_zxing_android_embedded_4_1_0_aar.xml new file mode 100644 index 0000000..213ae93 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_journeyapps_zxing_android_embedded_4_1_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_karumi_dexter_6_2_1_aar.xml b/android/.idea/libraries/Gradle__com_karumi_dexter_6_2_1_aar.xml new file mode 100644 index 0000000..2bb8bb4 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_karumi_dexter_6_2_1_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_javawriter_2_1_1.xml b/android/.idea/libraries/Gradle__com_squareup_javawriter_2_1_1.xml new file mode 100644 index 0000000..662b001 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_javawriter_2_1_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okhttp3_mockwebserver_3_14_7.xml b/android/.idea/libraries/Gradle__com_squareup_okhttp3_mockwebserver_3_14_7.xml new file mode 100644 index 0000000..f2ebd6c --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okhttp3_mockwebserver_3_14_7.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_0_0.xml b/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_0_0.xml new file mode 100644 index 0000000..039b57d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_0_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_12_13.xml b/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_12_13.xml new file mode 100644 index 0000000..a3c5c8f --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_12_13.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_14_7.xml b/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_14_7.xml new file mode 100644 index 0000000..d978f4d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_14_7.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okhttp_okhttp_2_7_5.xml b/android/.idea/libraries/Gradle__com_squareup_okhttp_okhttp_2_7_5.xml new file mode 100644 index 0000000..980c165 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okhttp_okhttp_2_7_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_13_0.xml b/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_13_0.xml new file mode 100644 index 0000000..913412c --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_13_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_15_0.xml b/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_15_0.xml new file mode 100644 index 0000000..4d41f7a --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_15_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_17_2.xml b/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_17_2.xml new file mode 100644 index 0000000..f0b1401 --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_17_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_6_0.xml b/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_6_0.xml new file mode 100644 index 0000000..ca6737d --- /dev/null +++ b/android/.idea/libraries/Gradle__com_squareup_okio_okio_1_6_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_flutter_arm64_v8a_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml b/android/.idea/libraries/Gradle__io_flutter_arm64_v8a_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml new file mode 100644 index 0000000..e17b3f0 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_flutter_arm64_v8a_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_flutter_armeabi_v7a_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml b/android/.idea/libraries/Gradle__io_flutter_armeabi_v7a_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml new file mode 100644 index 0000000..6acbc04 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_flutter_armeabi_v7a_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_flutter_flutter_embedding_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml b/android/.idea/libraries/Gradle__io_flutter_flutter_embedding_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml new file mode 100644 index 0000000..1c09e87 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_flutter_flutter_embedding_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_flutter_x86_64_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml b/android/.idea/libraries/Gradle__io_flutter_x86_64_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml new file mode 100644 index 0000000..9347162 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_flutter_x86_64_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_flutter_x86_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml b/android/.idea/libraries/Gradle__io_flutter_x86_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml new file mode 100644 index 0000000..7cb597d --- /dev/null +++ b/android/.idea/libraries/Gradle__io_flutter_x86_debug_1_0_0_91c9fc8fe011352879e3bb6660966eafc0847233.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_grpc_grpc_android_1_28_0_aar.xml b/android/.idea/libraries/Gradle__io_grpc_grpc_android_1_28_0_aar.xml new file mode 100644 index 0000000..71aed14 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_grpc_grpc_android_1_28_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_grpc_grpc_api_1_28_0.xml b/android/.idea/libraries/Gradle__io_grpc_grpc_api_1_28_0.xml new file mode 100644 index 0000000..81418a3 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_grpc_grpc_api_1_28_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_grpc_grpc_context_1_28_0.xml b/android/.idea/libraries/Gradle__io_grpc_grpc_context_1_28_0.xml new file mode 100644 index 0000000..c3a7a28 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_grpc_grpc_context_1_28_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_grpc_grpc_core_1_28_0.xml b/android/.idea/libraries/Gradle__io_grpc_grpc_core_1_28_0.xml new file mode 100644 index 0000000..d3e97dc --- /dev/null +++ b/android/.idea/libraries/Gradle__io_grpc_grpc_core_1_28_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_grpc_grpc_okhttp_1_28_0.xml b/android/.idea/libraries/Gradle__io_grpc_grpc_okhttp_1_28_0.xml new file mode 100644 index 0000000..4e36813 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_grpc_grpc_okhttp_1_28_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_grpc_grpc_protobuf_lite_1_28_0.xml b/android/.idea/libraries/Gradle__io_grpc_grpc_protobuf_lite_1_28_0.xml new file mode 100644 index 0000000..3f12f22 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_grpc_grpc_protobuf_lite_1_28_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_grpc_grpc_stub_1_28_0.xml b/android/.idea/libraries/Gradle__io_grpc_grpc_stub_1_28_0.xml new file mode 100644 index 0000000..eb084c0 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_grpc_grpc_stub_1_28_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__io_perfmark_perfmark_api_0_19_0.xml b/android/.idea/libraries/Gradle__io_perfmark_perfmark_api_0_19_0.xml new file mode 100644 index 0000000..42a2f96 --- /dev/null +++ b/android/.idea/libraries/Gradle__io_perfmark_perfmark_api_0_19_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__javax_annotation_javax_annotation_api_1_3_2.xml b/android/.idea/libraries/Gradle__javax_annotation_javax_annotation_api_1_3_2.xml new file mode 100644 index 0000000..10c7813 --- /dev/null +++ b/android/.idea/libraries/Gradle__javax_annotation_javax_annotation_api_1_3_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__javax_inject_javax_inject_1.xml b/android/.idea/libraries/Gradle__javax_inject_javax_inject_1.xml new file mode 100644 index 0000000..62012ea --- /dev/null +++ b/android/.idea/libraries/Gradle__javax_inject_javax_inject_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__junit_junit_4_12.xml b/android/.idea/libraries/Gradle__junit_junit_4_12.xml new file mode 100644 index 0000000..6c078d6 --- /dev/null +++ b/android/.idea/libraries/Gradle__junit_junit_4_12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__junit_junit_4_12_jar.xml b/android/.idea/libraries/Gradle__junit_junit_4_12_jar.xml new file mode 100644 index 0000000..24e5b72 --- /dev/null +++ b/android/.idea/libraries/Gradle__junit_junit_4_12_jar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__junit_junit_4_13_1.xml b/android/.idea/libraries/Gradle__junit_junit_4_13_1.xml new file mode 100644 index 0000000..4405e64 --- /dev/null +++ b/android/.idea/libraries/Gradle__junit_junit_4_13_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__nekohtml_nekohtml_1_9_6_2.xml b/android/.idea/libraries/Gradle__nekohtml_nekohtml_1_9_6_2.xml new file mode 100644 index 0000000..6494c6f --- /dev/null +++ b/android/.idea/libraries/Gradle__nekohtml_nekohtml_1_9_6_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__nekohtml_xercesMinimal_1_9_6_2.xml b/android/.idea/libraries/Gradle__nekohtml_xercesMinimal_1_9_6_2.xml new file mode 100644 index 0000000..299d016 --- /dev/null +++ b/android/.idea/libraries/Gradle__nekohtml_xercesMinimal_1_9_6_2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_10_15.xml b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_10_15.xml new file mode 100644 index 0000000..3611b6a --- /dev/null +++ b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_10_15.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_10_5.xml b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_10_5.xml new file mode 100644 index 0000000..1249c11 --- /dev/null +++ b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_10_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_9_10.xml b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_9_10.xml new file mode 100644 index 0000000..cb99df1 --- /dev/null +++ b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_9_10.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_9_10_jar.xml b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_9_10_jar.xml new file mode 100644 index 0000000..ade5463 --- /dev/null +++ b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_1_9_10_jar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_10_15.xml b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_10_15.xml new file mode 100644 index 0000000..809e4b0 --- /dev/null +++ b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_10_15.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_10_5.xml b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_10_5.xml new file mode 100644 index 0000000..f9a3975 --- /dev/null +++ b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_10_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_9_10.xml b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_9_10.xml new file mode 100644 index 0000000..c281715 --- /dev/null +++ b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_9_10.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_9_10_jar.xml b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_9_10_jar.xml new file mode 100644 index 0000000..e1f93e3 --- /dev/null +++ b/android/.idea/libraries/Gradle__net_bytebuddy_byte_buddy_agent_1_9_10_jar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__net_sf_kxml_kxml2_2_3_0.xml b/android/.idea/libraries/Gradle__net_sf_kxml_kxml2_2_3_0.xml new file mode 100644 index 0000000..fbe9697 --- /dev/null +++ b/android/.idea/libraries/Gradle__net_sf_kxml_kxml2_2_3_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_ant_ant_1_8_0.xml b/android/.idea/libraries/Gradle__org_apache_ant_ant_1_8_0.xml new file mode 100644 index 0000000..92b47eb --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_ant_ant_1_8_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_ant_ant_launcher_1_8_0.xml b/android/.idea/libraries/Gradle__org_apache_ant_ant_launcher_1_8_0.xml new file mode 100644 index 0000000..4242676 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_ant_ant_launcher_1_8_0.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_ant_tasks_2_1_3.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_ant_tasks_2_1_3.xml new file mode 100644 index 0000000..bc6f73d --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_ant_tasks_2_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_artifact_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_artifact_2_2_1.xml new file mode 100644 index 0000000..68294bc --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_artifact_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_artifact_manager_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_artifact_manager_2_2_1.xml new file mode 100644 index 0000000..306563f --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_artifact_manager_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_error_diagnostics_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_error_diagnostics_2_2_1.xml new file mode 100644 index 0000000..3f19977 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_error_diagnostics_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_model_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_model_2_2_1.xml new file mode 100644 index 0000000..f139cd7 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_model_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_plugin_registry_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_plugin_registry_2_2_1.xml new file mode 100644 index 0000000..d4df38f --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_plugin_registry_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_profile_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_profile_2_2_1.xml new file mode 100644 index 0000000..c9200a0 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_profile_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_project_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_project_2_2_1.xml new file mode 100644 index 0000000..b3c646e --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_project_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_repository_metadata_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_repository_metadata_2_2_1.xml new file mode 100644 index 0000000..ccc7df1 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_repository_metadata_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_maven_settings_2_2_1.xml b/android/.idea/libraries/Gradle__org_apache_maven_maven_settings_2_2_1.xml new file mode 100644 index 0000000..6d99611 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_maven_settings_2_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_file_1_0_beta_6.xml b/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_file_1_0_beta_6.xml new file mode 100644 index 0000000..bb7012c --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_file_1_0_beta_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_http_lightweight_1_0_beta_6.xml b/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_http_lightweight_1_0_beta_6.xml new file mode 100644 index 0000000..8fbfa8e --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_http_lightweight_1_0_beta_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_http_shared_1_0_beta_6.xml b/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_http_shared_1_0_beta_6.xml new file mode 100644 index 0000000..21d3ae8 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_http_shared_1_0_beta_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_provider_api_1_0_beta_6.xml b/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_provider_api_1_0_beta_6.xml new file mode 100644 index 0000000..2926330 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_apache_maven_wagon_wagon_provider_api_1_0_beta_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_bouncycastle_bcprov_jdk15on_1_52.xml b/android/.idea/libraries/Gradle__org_bouncycastle_bcprov_jdk15on_1_52.xml new file mode 100644 index 0000000..be770e8 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_bouncycastle_bcprov_jdk15on_1_52.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_checkerframework_checker_compat_qual_2_5_5.xml b/android/.idea/libraries/Gradle__org_checkerframework_checker_compat_qual_2_5_5.xml new file mode 100644 index 0000000..b511655 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_checkerframework_checker_compat_qual_2_5_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_checkerframework_checker_qual_2_5_2.xml b/android/.idea/libraries/Gradle__org_checkerframework_checker_qual_2_5_2.xml new file mode 100644 index 0000000..3029dfe --- /dev/null +++ b/android/.idea/libraries/Gradle__org_checkerframework_checker_qual_2_5_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_codehaus_mojo_animal_sniffer_annotations_1_17.xml b/android/.idea/libraries/Gradle__org_codehaus_mojo_animal_sniffer_annotations_1_17.xml new file mode 100644 index 0000000..63ace91 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_codehaus_mojo_animal_sniffer_annotations_1_17.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_codehaus_mojo_animal_sniffer_annotations_1_18.xml b/android/.idea/libraries/Gradle__org_codehaus_mojo_animal_sniffer_annotations_1_18.xml new file mode 100644 index 0000000..5a17a38 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_codehaus_mojo_animal_sniffer_annotations_1_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_container_default_1_0_alpha_9_stable_1.xml b/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_container_default_1_0_alpha_9_stable_1.xml new file mode 100644 index 0000000..d12ead0 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_container_default_1_0_alpha_9_stable_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_interpolation_1_11.xml b/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_interpolation_1_11.xml new file mode 100644 index 0000000..0ed0970 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_interpolation_1_11.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_utils_1_5_15.xml b/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_utils_1_5_15.xml new file mode 100644 index 0000000..c3d9ab8 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_codehaus_plexus_plexus_utils_1_5_15.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml b/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml new file mode 100644 index 0000000..09cf23d --- /dev/null +++ b/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3_jar.xml b/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3_jar.xml new file mode 100644 index 0000000..6b1e2e7 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3_jar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_integration_1_3.xml b/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_integration_1_3.xml new file mode 100644 index 0000000..1a77dd8 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_integration_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_library_1_3.xml b/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_library_1_3.xml new file mode 100644 index 0000000..3d45e8e --- /dev/null +++ b/android/.idea/libraries/Gradle__org_hamcrest_hamcrest_library_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml b/android/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml new file mode 100644 index 0000000..1fa0fa9 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_21.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_21.xml new file mode 100644 index 0000000..46d4722 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_21.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_50.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_50.xml new file mode 100644 index 0000000..e03b2f6 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_50.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_72.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_72.xml new file mode 100644 index 0000000..cd57d89 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_72.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_4_20.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_4_20.xml new file mode 100644 index 0000000..ab42e17 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_4_20.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_5_10.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_5_10.xml new file mode 100644 index 0000000..679895a --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_5_10.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_21.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_21.xml new file mode 100644 index 0000000..234e981 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_21.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_50.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_50.xml new file mode 100644 index 0000000..861597d --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_50.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_72.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_72.xml new file mode 100644 index 0000000..c769fec --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_72.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_4_20.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_4_20.xml new file mode 100644 index 0000000..2b85444 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_4_20.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_5_10.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_5_10.xml new file mode 100644 index 0000000..85b2bef --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_5_10.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_21.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_21.xml new file mode 100644 index 0000000..b3d2714 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_21.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_50.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_50.xml new file mode 100644 index 0000000..8dfcb21 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_50.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_72.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_72.xml new file mode 100644 index 0000000..bbc2ab9 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_72.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_4_20.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_4_20.xml new file mode 100644 index 0000000..652c287 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_4_20.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_5_10.xml b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_5_10.xml new file mode 100644 index 0000000..49249c1 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_5_10.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_mockito_mockito_core_1_10_19.xml b/android/.idea/libraries/Gradle__org_mockito_mockito_core_1_10_19.xml new file mode 100644 index 0000000..2cbfdd6 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_mockito_mockito_core_1_10_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_mockito_mockito_core_2_28_2.xml b/android/.idea/libraries/Gradle__org_mockito_mockito_core_2_28_2.xml new file mode 100644 index 0000000..1e41346 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_mockito_mockito_core_2_28_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_mockito_mockito_core_2_28_2_jar.xml b/android/.idea/libraries/Gradle__org_mockito_mockito_core_2_28_2_jar.xml new file mode 100644 index 0000000..eceaa19 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_mockito_mockito_core_2_28_2_jar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_mockito_mockito_core_3_2_4.xml b/android/.idea/libraries/Gradle__org_mockito_mockito_core_3_2_4.xml new file mode 100644 index 0000000..e5c5e6f --- /dev/null +++ b/android/.idea/libraries/Gradle__org_mockito_mockito_core_3_2_4.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_mockito_mockito_core_3_5_13.xml b/android/.idea/libraries/Gradle__org_mockito_mockito_core_3_5_13.xml new file mode 100644 index 0000000..0c7fe05 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_mockito_mockito_core_3_5_13.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_mockito_mockito_inline_2_28_2.xml b/android/.idea/libraries/Gradle__org_mockito_mockito_inline_2_28_2.xml new file mode 100644 index 0000000..f82a718 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_mockito_mockito_inline_2_28_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_mockito_mockito_inline_2_28_2_jar.xml b/android/.idea/libraries/Gradle__org_mockito_mockito_inline_2_28_2_jar.xml new file mode 100644 index 0000000..0d6ca6f --- /dev/null +++ b/android/.idea/libraries/Gradle__org_mockito_mockito_inline_2_28_2_jar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_mockito_mockito_inline_3_5_13.xml b/android/.idea/libraries/Gradle__org_mockito_mockito_inline_3_5_13.xml new file mode 100644 index 0000000..aca221b --- /dev/null +++ b/android/.idea/libraries/Gradle__org_mockito_mockito_inline_3_5_13.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_objenesis_objenesis_2_6.xml b/android/.idea/libraries/Gradle__org_objenesis_objenesis_2_6.xml new file mode 100644 index 0000000..325842c --- /dev/null +++ b/android/.idea/libraries/Gradle__org_objenesis_objenesis_2_6.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_objenesis_objenesis_2_6_jar.xml b/android/.idea/libraries/Gradle__org_objenesis_objenesis_2_6_jar.xml new file mode 100644 index 0000000..699ee35 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_objenesis_objenesis_2_6_jar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_objenesis_objenesis_3_1.xml b/android/.idea/libraries/Gradle__org_objenesis_objenesis_3_1.xml new file mode 100644 index 0000000..74cff28 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_objenesis_objenesis_3_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_ow2_asm_asm_7_0.xml b/android/.idea/libraries/Gradle__org_ow2_asm_asm_7_0.xml new file mode 100644 index 0000000..3456c1b --- /dev/null +++ b/android/.idea/libraries/Gradle__org_ow2_asm_asm_7_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_ow2_asm_asm_analysis_7_0.xml b/android/.idea/libraries/Gradle__org_ow2_asm_asm_analysis_7_0.xml new file mode 100644 index 0000000..184b5ab --- /dev/null +++ b/android/.idea/libraries/Gradle__org_ow2_asm_asm_analysis_7_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_ow2_asm_asm_commons_7_0.xml b/android/.idea/libraries/Gradle__org_ow2_asm_asm_commons_7_0.xml new file mode 100644 index 0000000..933a0aa --- /dev/null +++ b/android/.idea/libraries/Gradle__org_ow2_asm_asm_commons_7_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_ow2_asm_asm_tree_7_0.xml b/android/.idea/libraries/Gradle__org_ow2_asm_asm_tree_7_0.xml new file mode 100644 index 0000000..7ad2ccd --- /dev/null +++ b/android/.idea/libraries/Gradle__org_ow2_asm_asm_tree_7_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_ow2_asm_asm_util_7_0.xml b/android/.idea/libraries/Gradle__org_ow2_asm_asm_util_7_0.xml new file mode 100644 index 0000000..393422f --- /dev/null +++ b/android/.idea/libraries/Gradle__org_ow2_asm_asm_util_7_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_annotations_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_annotations_4_3.xml new file mode 100644 index 0000000..15918c5 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_annotations_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_junit_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_junit_4_3.xml new file mode 100644 index 0000000..e77546e --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_junit_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_pluginapi_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_pluginapi_4_3.xml new file mode 100644 index 0000000..50a3c61 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_pluginapi_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_plugins_maven_dependency_resolver_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_plugins_maven_dependency_resolver_4_3.xml new file mode 100644 index 0000000..b8ac20d --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_plugins_maven_dependency_resolver_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_resources_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_resources_4_3.xml new file mode 100644 index 0000000..67d097b --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_resources_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_robolectric_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_robolectric_4_3.xml new file mode 100644 index 0000000..0375256 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_robolectric_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_sandbox_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_sandbox_4_3.xml new file mode 100644 index 0000000..f2c9508 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_sandbox_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_shadowapi_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_shadowapi_4_3.xml new file mode 100644 index 0000000..bdd9382 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_shadowapi_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_shadows_framework_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_shadows_framework_4_3.xml new file mode 100644 index 0000000..ce7a276 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_shadows_framework_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_utils_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_utils_4_3.xml new file mode 100644 index 0000000..2537286 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_utils_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_robolectric_utils_reflector_4_3.xml b/android/.idea/libraries/Gradle__org_robolectric_utils_reflector_4_3.xml new file mode 100644 index 0000000..4f585fe --- /dev/null +++ b/android/.idea/libraries/Gradle__org_robolectric_utils_reflector_4_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/libraries/Gradle__org_threeten_threetenbp_1_4_2_no_tzdb.xml b/android/.idea/libraries/Gradle__org_threeten_threetenbp_1_4_2_no_tzdb.xml new file mode 100644 index 0000000..fd349c9 --- /dev/null +++ b/android/.idea/libraries/Gradle__org_threeten_threetenbp_1_4_2_no_tzdb.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml new file mode 100644 index 0000000..860da66 --- /dev/null +++ b/android/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules.xml b/android/.idea/modules.xml new file mode 100644 index 0000000..7501c7e --- /dev/null +++ b/android/.idea/modules.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1049254250/android.flutter_local_notifications.iml b/android/.idea/modules/-1049254250/android.flutter_local_notifications.iml new file mode 100644 index 0000000..45852df --- /dev/null +++ b/android/.idea/modules/-1049254250/android.flutter_local_notifications.iml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1055924779/android.shared_preferences.iml b/android/.idea/modules/-1055924779/android.shared_preferences.iml new file mode 100644 index 0000000..e467485 --- /dev/null +++ b/android/.idea/modules/-1055924779/android.shared_preferences.iml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1276585138/android.image_cropper.iml b/android/.idea/modules/-1276585138/android.image_cropper.iml new file mode 100644 index 0000000..3239e92 --- /dev/null +++ b/android/.idea/modules/-1276585138/android.image_cropper.iml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1421881264/android.flutter_country_picker.iml b/android/.idea/modules/-1421881264/android.flutter_country_picker.iml new file mode 100644 index 0000000..34a1722 --- /dev/null +++ b/android/.idea/modules/-1421881264/android.flutter_country_picker.iml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1535218591/android.path_provider_linux.iml b/android/.idea/modules/-1535218591/android.path_provider_linux.iml new file mode 100644 index 0000000..1daa47d --- /dev/null +++ b/android/.idea/modules/-1535218591/android.path_provider_linux.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1650556252/android.shared_preferences_web.iml b/android/.idea/modules/-1650556252/android.shared_preferences_web.iml new file mode 100644 index 0000000..db48941 --- /dev/null +++ b/android/.idea/modules/-1650556252/android.shared_preferences_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1728921030/android.firebase_core.iml b/android/.idea/modules/-1728921030/android.firebase_core.iml new file mode 100644 index 0000000..fd11fd4 --- /dev/null +++ b/android/.idea/modules/-1728921030/android.firebase_core.iml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1778056475/android.url_launcher_windows.iml b/android/.idea/modules/-1778056475/android.url_launcher_windows.iml new file mode 100644 index 0000000..5ca22cd --- /dev/null +++ b/android/.idea/modules/-1778056475/android.url_launcher_windows.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1802215405/android.shared_preferences_windows.iml b/android/.idea/modules/-1802215405/android.shared_preferences_windows.iml new file mode 100644 index 0000000..4adc2ac --- /dev/null +++ b/android/.idea/modules/-1802215405/android.shared_preferences_windows.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-18267466/android.google_ml_vision.iml b/android/.idea/modules/-18267466/android.google_ml_vision.iml new file mode 100644 index 0000000..9ef466a --- /dev/null +++ b/android/.idea/modules/-18267466/android.google_ml_vision.iml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1866279700/android.video_player_web.iml b/android/.idea/modules/-1866279700/android.video_player_web.iml new file mode 100644 index 0000000..8fba3c0 --- /dev/null +++ b/android/.idea/modules/-1866279700/android.video_player_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1981871334/android.cloud_firestore_web.iml b/android/.idea/modules/-1981871334/android.cloud_firestore_web.iml new file mode 100644 index 0000000..3fc7c22 --- /dev/null +++ b/android/.idea/modules/-1981871334/android.cloud_firestore_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-1989292847/android.image_picker.iml b/android/.idea/modules/-1989292847/android.image_picker.iml new file mode 100644 index 0000000..b729ad9 --- /dev/null +++ b/android/.idea/modules/-1989292847/android.image_picker.iml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-206538769/android.flutter_webview_plugin.iml b/android/.idea/modules/-206538769/android.flutter_webview_plugin.iml new file mode 100644 index 0000000..d1e5ff2 --- /dev/null +++ b/android/.idea/modules/-206538769/android.flutter_webview_plugin.iml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-2085143847/android.flutter_inappwebview.iml b/android/.idea/modules/-2085143847/android.flutter_inappwebview.iml new file mode 100644 index 0000000..e8c4840 --- /dev/null +++ b/android/.idea/modules/-2085143847/android.flutter_inappwebview.iml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-2124854993/android.qr_code_scanner.iml b/android/.idea/modules/-2124854993/android.qr_code_scanner.iml new file mode 100644 index 0000000..424021d --- /dev/null +++ b/android/.idea/modules/-2124854993/android.qr_code_scanner.iml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-243381778/android.geolocator_web.iml b/android/.idea/modules/-243381778/android.geolocator_web.iml new file mode 100644 index 0000000..c1b15cf --- /dev/null +++ b/android/.idea/modules/-243381778/android.geolocator_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-307671483/android.firebase_auth.iml b/android/.idea/modules/-307671483/android.firebase_auth.iml new file mode 100644 index 0000000..7f52145 --- /dev/null +++ b/android/.idea/modules/-307671483/android.firebase_auth.iml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-313411879/android.google_sign_in.iml b/android/.idea/modules/-313411879/android.google_sign_in.iml new file mode 100644 index 0000000..eff8732 --- /dev/null +++ b/android/.idea/modules/-313411879/android.google_sign_in.iml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-324416137/android.url_launcher_web.iml b/android/.idea/modules/-324416137/android.url_launcher_web.iml new file mode 100644 index 0000000..8890d86 --- /dev/null +++ b/android/.idea/modules/-324416137/android.url_launcher_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-330149543/android.wakelock_macos.iml b/android/.idea/modules/-330149543/android.wakelock_macos.iml new file mode 100644 index 0000000..f82abd6 --- /dev/null +++ b/android/.idea/modules/-330149543/android.wakelock_macos.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-44543866/android.cloud_firestore.iml b/android/.idea/modules/-44543866/android.cloud_firestore.iml new file mode 100644 index 0000000..bc2633e --- /dev/null +++ b/android/.idea/modules/-44543866/android.cloud_firestore.iml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-46297941/android.url_launcher.iml b/android/.idea/modules/-46297941/android.url_launcher.iml new file mode 100644 index 0000000..61e8b5c --- /dev/null +++ b/android/.idea/modules/-46297941/android.url_launcher.iml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-47428831/android.google_maps_flutter.iml b/android/.idea/modules/-47428831/android.google_maps_flutter.iml new file mode 100644 index 0000000..868ecdf --- /dev/null +++ b/android/.idea/modules/-47428831/android.google_maps_flutter.iml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-60485287/android.firebase_auth_web.iml b/android/.idea/modules/-60485287/android.firebase_auth_web.iml new file mode 100644 index 0000000..79dbc72 --- /dev/null +++ b/android/.idea/modules/-60485287/android.firebase_auth_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-612426157/android.permission_handler.iml b/android/.idea/modules/-612426157/android.permission_handler.iml new file mode 100644 index 0000000..96be5c4 --- /dev/null +++ b/android/.idea/modules/-612426157/android.permission_handler.iml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-650502802/android.path_provider.iml b/android/.idea/modules/-650502802/android.path_provider.iml new file mode 100644 index 0000000..35df564 --- /dev/null +++ b/android/.idea/modules/-650502802/android.path_provider.iml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-879170400/android.path_provider_macos.iml b/android/.idea/modules/-879170400/android.path_provider_macos.iml new file mode 100644 index 0000000..06703ba --- /dev/null +++ b/android/.idea/modules/-879170400/android.path_provider_macos.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/-947249395/android.firebase_core_web.iml b/android/.idea/modules/-947249395/android.firebase_core_web.iml new file mode 100644 index 0000000..d15a24c --- /dev/null +++ b/android/.idea/modules/-947249395/android.firebase_core_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1008465727/android.webview_flutter.iml b/android/.idea/modules/1008465727/android.webview_flutter.iml new file mode 100644 index 0000000..2f4da49 --- /dev/null +++ b/android/.idea/modules/1008465727/android.webview_flutter.iml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1012871005/android.video_player.iml b/android/.idea/modules/1012871005/android.video_player.iml new file mode 100644 index 0000000..23e4477 --- /dev/null +++ b/android/.idea/modules/1012871005/android.video_player.iml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1117910010/android.camera_deep_ar.iml b/android/.idea/modules/1117910010/android.camera_deep_ar.iml new file mode 100644 index 0000000..6f1fe76 --- /dev/null +++ b/android/.idea/modules/1117910010/android.camera_deep_ar.iml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1144720755/android.image_gallery_saver.iml b/android/.idea/modules/1144720755/android.image_gallery_saver.iml new file mode 100644 index 0000000..4f4581d --- /dev/null +++ b/android/.idea/modules/1144720755/android.image_gallery_saver.iml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1230809797/android.camera.iml b/android/.idea/modules/1230809797/android.camera.iml new file mode 100644 index 0000000..8dd7551 --- /dev/null +++ b/android/.idea/modules/1230809797/android.camera.iml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1251705732/android.share.iml b/android/.idea/modules/1251705732/android.share.iml new file mode 100644 index 0000000..a8fa576 --- /dev/null +++ b/android/.idea/modules/1251705732/android.share.iml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1253542358/android.url_launcher_linux.iml b/android/.idea/modules/1253542358/android.url_launcher_linux.iml new file mode 100644 index 0000000..d57d5b6 --- /dev/null +++ b/android/.idea/modules/1253542358/android.url_launcher_linux.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1297275587/android.shared_preferences_macos.iml b/android/.idea/modules/1297275587/android.shared_preferences_macos.iml new file mode 100644 index 0000000..3eb1cc0 --- /dev/null +++ b/android/.idea/modules/1297275587/android.shared_preferences_macos.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1338561407/android.sqflite.iml b/android/.idea/modules/1338561407/android.sqflite.iml new file mode 100644 index 0000000..936dabf --- /dev/null +++ b/android/.idea/modules/1338561407/android.sqflite.iml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1526031749/android.wakelock.iml b/android/.idea/modules/1526031749/android.wakelock.iml new file mode 100644 index 0000000..49f483d --- /dev/null +++ b/android/.idea/modules/1526031749/android.wakelock.iml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1642035704/android.firebase_dynamic_links.iml b/android/.idea/modules/1642035704/android.firebase_dynamic_links.iml new file mode 100644 index 0000000..979d08c --- /dev/null +++ b/android/.idea/modules/1642035704/android.firebase_dynamic_links.iml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1655291913/android.flutter_libphonenumber.iml b/android/.idea/modules/1655291913/android.flutter_libphonenumber.iml new file mode 100644 index 0000000..aba231f --- /dev/null +++ b/android/.idea/modules/1655291913/android.flutter_libphonenumber.iml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1659896315/android.flutter_ffmpeg.iml b/android/.idea/modules/1659896315/android.flutter_ffmpeg.iml new file mode 100644 index 0000000..50fbb30 --- /dev/null +++ b/android/.idea/modules/1659896315/android.flutter_ffmpeg.iml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1662699662/android.google_sign_in_web.iml b/android/.idea/modules/1662699662/android.google_sign_in_web.iml new file mode 100644 index 0000000..794da20 --- /dev/null +++ b/android/.idea/modules/1662699662/android.google_sign_in_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1725698125/android.video_thumbnail.iml b/android/.idea/modules/1725698125/android.video_thumbnail.iml new file mode 100644 index 0000000..a9cf91d --- /dev/null +++ b/android/.idea/modules/1725698125/android.video_thumbnail.iml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1856912349/android.geolocator.iml b/android/.idea/modules/1856912349/android.geolocator.iml new file mode 100644 index 0000000..b4f7d56 --- /dev/null +++ b/android/.idea/modules/1856912349/android.geolocator.iml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1909590549/android.url_launcher_macos.iml b/android/.idea/modules/1909590549/android.url_launcher_macos.iml new file mode 100644 index 0000000..3c4b332 --- /dev/null +++ b/android/.idea/modules/1909590549/android.url_launcher_macos.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/1986125268/android.firebase_messaging_web.iml b/android/.idea/modules/1986125268/android.firebase_messaging_web.iml new file mode 100644 index 0000000..c5b8b6e --- /dev/null +++ b/android/.idea/modules/1986125268/android.firebase_messaging_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/212933758/android.firebase_messaging.iml b/android/.idea/modules/212933758/android.firebase_messaging.iml new file mode 100644 index 0000000..a5cd06d --- /dev/null +++ b/android/.idea/modules/212933758/android.firebase_messaging.iml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/261057220/android.flutter_plugin_android_lifecycle.iml b/android/.idea/modules/261057220/android.flutter_plugin_android_lifecycle.iml new file mode 100644 index 0000000..7e82009 --- /dev/null +++ b/android/.idea/modules/261057220/android.flutter_plugin_android_lifecycle.iml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/368333755/android.wakelock_web.iml b/android/.idea/modules/368333755/android.wakelock_web.iml new file mode 100644 index 0000000..f920152 --- /dev/null +++ b/android/.idea/modules/368333755/android.wakelock_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/412216427/android.location.iml b/android/.idea/modules/412216427/android.location.iml new file mode 100644 index 0000000..c7fb245 --- /dev/null +++ b/android/.idea/modules/412216427/android.location.iml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/469809666/android.image_picker_for_web.iml b/android/.idea/modules/469809666/android.image_picker_for_web.iml new file mode 100644 index 0000000..f60bb14 --- /dev/null +++ b/android/.idea/modules/469809666/android.image_picker_for_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/566149743/android.tapioca.iml b/android/.idea/modules/566149743/android.tapioca.iml new file mode 100644 index 0000000..42679d6 --- /dev/null +++ b/android/.idea/modules/566149743/android.tapioca.iml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/641227396/android.shared_preferences_linux.iml b/android/.idea/modules/641227396/android.shared_preferences_linux.iml new file mode 100644 index 0000000..469f5fc --- /dev/null +++ b/android/.idea/modules/641227396/android.shared_preferences_linux.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/769777073/android.path_provider_windows.iml b/android/.idea/modules/769777073/android.path_provider_windows.iml new file mode 100644 index 0000000..f6d37d6 --- /dev/null +++ b/android/.idea/modules/769777073/android.path_provider_windows.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/830712510/android.location_web.iml b/android/.idea/modules/830712510/android.location_web.iml new file mode 100644 index 0000000..18f384d --- /dev/null +++ b/android/.idea/modules/830712510/android.location_web.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules/app/android.app.iml b/android/.idea/modules/app/android.app.iml new file mode 100644 index 0000000..706232c --- /dev/null +++ b/android/.idea/modules/app/android.app.iml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/runConfigurations.xml b/android/.idea/runConfigurations.xml new file mode 100644 index 0000000..e497da9 --- /dev/null +++ b/android/.idea/runConfigurations.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/android/android.iml b/android/android.iml new file mode 100644 index 0000000..a56cf0c --- /dev/null +++ b/android/android.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..1888222 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,114 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +/* release config */ +def keystoreProperties = new Properties() + def keystorePropertiesFile = rootProject.file('key.properties') + if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + } + /* release config end */ +android { + compileSdkVersion 31 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + packagingOptions { + pickFirst 'lib/x86/libc++_shared.so' + pickFirst 'lib/x86_64/libc++_shared.so' + pickFirst 'lib/armeabi-v7a/libc++_shared.so' + pickFirst 'lib/arm64-v8a/libc++_shared.so' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.sparentechBacware.teso" + minSdkVersion 21 + targetSdkVersion 31 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { + release { + ndk { + abiFilters 'armeabi-v7a','arm64-v8a','x86_64' + } + minifyEnabled true + signingConfig signingConfigs.release + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + // buildTypes { + // release { + // // TODO: Add your own signing config for the release build. + // // Signing with the debug keys for now, so `flutter run --release` works. + // ndk { + // abiFilters 'armeabi-v7a','arm64-v8a','x86_64' + // } + // minifyEnabled true + // signingConfig signingConfigs.debug + // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + // } + // } + +} + + +flutter { + source '../..' +} + +apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.google.firebase.crashlytics' + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' + implementation 'com.google.firebase:firebase-crashlytics:17.3.0' + implementation 'com.github.AbedElazizShe:LightCompressor:0.9.4' + def camerax_version = "1.0.0" +// CameraX core library using camera2 implementation +implementation "androidx.camera:camera-camera2:$camerax_version" +// CameraX Lifecycle Library +implementation "androidx.camera:camera-lifecycle:$camerax_version" +// CameraX View class +implementation "androidx.camera:camera-view:1.0.0-alpha24" +} diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..ec35575 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,66 @@ +{ + "project_info": { + "project_number": "280510379185", + "firebase_url": "https://teso-ghana-default-rtdb.firebaseio.com", + "project_id": "teso-ghana", + "storage_bucket": "teso-ghana.appspot.com" + }, + "client": [{ + "client_info": { + "mobilesdk_app_id": "1:280510379185:android:b778de8dca1cf49219de90", + "android_client_info": { + "package_name": "com.sparentechBacware.teso" + } + }, + "oauth_client": [{ + "client_id": "280510379185-0shp8sia3i5m589bov645t3kuddb1032.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.sparentechBacware.teso", + "certificate_hash": "40b68890dd51d72454ccdc7289623c4d56273248" + } + }, + { + "client_id": "280510379185-87k85sksrd30mu9fjgfmacb3bkcjnmdq.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.sparentechBacware.teso", + "certificate_hash": "4f655cf94cd94864838475611b711c2528e33601" + } + }, + { + "client_id": "280510379185-ac7qjglm9cct9u0diqrns6om8t2mb9ug.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.sparentechBacware.teso", + "certificate_hash": "063304c2da41f90c824fad163b5ad481edf6db11" + } + }, + { + "client_id": "280510379185-58chj1fi4pts4p7p7sieq16miapvtepi.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [{ + "current_key": "AIzaSyAHBguF6oqPEq7em1vLrh_NP2LoPuabe6o" + }], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [{ + "client_id": "280510379185-58chj1fi4pts4p7p7sieq16miapvtepi.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "280510379185-67vianhh973klriv75ip2tb9cf8ibitl.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.tesoapp", + "app_store_id": "1573175173" + } + } + ] + } + } + }], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..0ee5f4b --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,13 @@ +-dontwarn android.** +-keep class io.flutter.app.** { *; } +-keep class io.flutter.plugin.** { *; } +-keep class io.flutter.util.** { *; } +-keep class io.flutter.view.** { *; } +-keep class io.flutter.** { *; } +-keep class io.flutter.plugins.** { *; } + +-keep class com.arthenica.mobileffmpeg.Config { + native ; + void log(int, byte[]); + void statistics(int, float, float, long , int, double, double); + } \ No newline at end of file diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..35e1620 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..39f4814 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/example/teso/MainActivity.kt b/android/app/src/main/kotlin/com/example/teso/MainActivity.kt new file mode 100644 index 0000000..cf2e6ad --- /dev/null +++ b/android/app/src/main/kotlin/com/example/teso/MainActivity.kt @@ -0,0 +1,6 @@ +package com.sparentechBacware.teso + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable-hdpi/app_notf_icon.png b/android/app/src/main/res/drawable-hdpi/app_notf_icon.png new file mode 100644 index 0000000..ac6f6f5 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/app_notf_icon.png differ diff --git a/android/app/src/main/res/drawable-mdpi/app_notf_icon.png b/android/app/src/main/res/drawable-mdpi/app_notf_icon.png new file mode 100644 index 0000000..fc09fe1 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/app_notf_icon.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/app_notf_icon.png b/android/app/src/main/res/drawable-xhdpi/app_notf_icon.png new file mode 100644 index 0000000..c187a9b Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/app_notf_icon.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/app_notf_icon.png b/android/app/src/main/res/drawable-xxhdpi/app_notf_icon.png new file mode 100644 index 0000000..9e3861a Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/app_notf_icon.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/app_notf_icon.png b/android/app/src/main/res/drawable-xxxhdpi/app_notf_icon.png new file mode 100644 index 0000000..7a5e288 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/app_notf_icon.png differ diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..8ca5462 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..8ca5462 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..8ca5462 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..8ca5462 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..8ca5462 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..96a0b36 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + Upload started + Upload in progress + Upload canceled + Upload failed + Upload complete + \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..1f83a33 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..35e1620 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..c76eee0 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,54 @@ +buildscript { + //ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.5.21' + repositories { + google() + jcenter() + } + + dependencies { + // classpath 'com.android.tools.build:gradle:3.5.4' + classpath 'com.android.tools.build:gradle:4.0.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.google.gms:google-services:4.3.5' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' + } +} + +allprojects { + repositories { + mavenCentral() + google() + //maven { url 'https://jitpack.io' } + jcenter() + } + configurations.all { + exclude group: 'com.google.guava', module: 'failureaccess' + + resolutionStrategy { + eachDependency { details -> + if('guava' == details.requested.name) { + details.useVersion '27.0-android' + } + } + } + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + + ext { + flutterFFmpegPackage = "full-gpl-lts" + } + + + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..468e05a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,6 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true +android.enableDexingArtifactTransform=false + diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..08cef93 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip diff --git a/android/key.properties b/android/key.properties new file mode 100644 index 0000000..4344f92 --- /dev/null +++ b/android/key.properties @@ -0,0 +1,4 @@ +storePassword=BrazilbrenAPKDEV2020 +keyPassword=BrazilbrenAPKDEV2020 +keyAlias=key +storeFile=C:/Users/BARHEN/key.jks \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..121d30d --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} \ No newline at end of file diff --git a/android/settings_aar.gradle b/android/settings_aar.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/android/teso_android.iml b/android/teso_android.iml new file mode 100644 index 0000000..1029d72 --- /dev/null +++ b/android/teso_android.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/AirtelTigo.png b/assets/images/AirtelTigo.png new file mode 100644 index 0000000..0d46136 Binary files /dev/null and b/assets/images/AirtelTigo.png differ diff --git a/assets/images/MTN.png b/assets/images/MTN.png new file mode 100644 index 0000000..fedc36d Binary files /dev/null and b/assets/images/MTN.png differ diff --git a/assets/images/Vodafone.png b/assets/images/Vodafone.png new file mode 100644 index 0000000..dbdb35f Binary files /dev/null and b/assets/images/Vodafone.png differ diff --git a/assets/images/background.png b/assets/images/background.png new file mode 100644 index 0000000..f50033f Binary files /dev/null and b/assets/images/background.png differ diff --git a/assets/images/blank.jpg b/assets/images/blank.jpg new file mode 100644 index 0000000..f951c2a Binary files /dev/null and b/assets/images/blank.jpg differ diff --git a/assets/images/blue.png b/assets/images/blue.png new file mode 100644 index 0000000..1e051ed Binary files /dev/null and b/assets/images/blue.png differ diff --git a/assets/images/cashOut.png b/assets/images/cashOut.png new file mode 100644 index 0000000..69016f5 Binary files /dev/null and b/assets/images/cashOut.png differ diff --git a/assets/images/categories/agrix.png b/assets/images/categories/agrix.png new file mode 100644 index 0000000..7877e56 Binary files /dev/null and b/assets/images/categories/agrix.png differ diff --git a/assets/images/categories/antique.png b/assets/images/categories/antique.png new file mode 100644 index 0000000..d7df0a1 Binary files /dev/null and b/assets/images/categories/antique.png differ diff --git a/assets/images/categories/antique2.png b/assets/images/categories/antique2.png new file mode 100644 index 0000000..f586409 Binary files /dev/null and b/assets/images/categories/antique2.png differ diff --git a/assets/images/categories/autos.png b/assets/images/categories/autos.png new file mode 100644 index 0000000..88483c4 Binary files /dev/null and b/assets/images/categories/autos.png differ diff --git a/assets/images/categories/bags.png b/assets/images/categories/bags.png new file mode 100644 index 0000000..91de92e Binary files /dev/null and b/assets/images/categories/bags.png differ diff --git a/assets/images/categories/clothes.png b/assets/images/categories/clothes.png new file mode 100644 index 0000000..b54cb64 Binary files /dev/null and b/assets/images/categories/clothes.png differ diff --git a/assets/images/categories/electronics.png b/assets/images/categories/electronics.png new file mode 100644 index 0000000..a2a6535 Binary files /dev/null and b/assets/images/categories/electronics.png differ diff --git a/assets/images/categories/food.png b/assets/images/categories/food.png new file mode 100644 index 0000000..2d8df3d Binary files /dev/null and b/assets/images/categories/food.png differ diff --git a/assets/images/categories/gifts.png b/assets/images/categories/gifts.png new file mode 100644 index 0000000..e27bd76 Binary files /dev/null and b/assets/images/categories/gifts.png differ diff --git a/assets/images/categories/health.png b/assets/images/categories/health.png new file mode 100644 index 0000000..568b580 Binary files /dev/null and b/assets/images/categories/health.png differ diff --git a/assets/images/categories/home.png b/assets/images/categories/home.png new file mode 100644 index 0000000..23bd1f7 Binary files /dev/null and b/assets/images/categories/home.png differ diff --git a/assets/images/categories/machinery.png b/assets/images/categories/machinery.png new file mode 100644 index 0000000..836bc1c Binary files /dev/null and b/assets/images/categories/machinery.png differ diff --git a/assets/images/categories/pets.png b/assets/images/categories/pets.png new file mode 100644 index 0000000..5b1fac0 Binary files /dev/null and b/assets/images/categories/pets.png differ diff --git a/assets/images/color-filters.png b/assets/images/color-filters.png new file mode 100644 index 0000000..05e86b7 Binary files /dev/null and b/assets/images/color-filters.png differ diff --git a/assets/images/destination_map_marker.png b/assets/images/destination_map_marker.png new file mode 100644 index 0000000..ec6c8af Binary files /dev/null and b/assets/images/destination_map_marker.png differ diff --git a/assets/images/driving_pin.png b/assets/images/driving_pin.png new file mode 100644 index 0000000..4b493b0 Binary files /dev/null and b/assets/images/driving_pin.png differ diff --git a/assets/images/empty.png b/assets/images/empty.png new file mode 100644 index 0000000..0f2d8b5 Binary files /dev/null and b/assets/images/empty.png differ diff --git a/assets/images/emptyBox.png b/assets/images/emptyBox.png new file mode 100644 index 0000000..15be001 Binary files /dev/null and b/assets/images/emptyBox.png differ diff --git a/assets/images/facebook.png b/assets/images/facebook.png new file mode 100644 index 0000000..f31095b Binary files /dev/null and b/assets/images/facebook.png differ diff --git a/assets/images/facebook_new.png b/assets/images/facebook_new.png new file mode 100644 index 0000000..d2e92a6 Binary files /dev/null and b/assets/images/facebook_new.png differ diff --git a/assets/images/firstTime.png b/assets/images/firstTime.png new file mode 100644 index 0000000..a9e2b11 Binary files /dev/null and b/assets/images/firstTime.png differ diff --git a/assets/images/gold1.png b/assets/images/gold1.png new file mode 100644 index 0000000..833fc45 Binary files /dev/null and b/assets/images/gold1.png differ diff --git a/assets/images/google.png b/assets/images/google.png new file mode 100644 index 0000000..c5103c5 Binary files /dev/null and b/assets/images/google.png differ diff --git a/assets/images/grey.png b/assets/images/grey.png new file mode 100644 index 0000000..75be16c Binary files /dev/null and b/assets/images/grey.png differ diff --git a/assets/images/loading.png b/assets/images/loading.png new file mode 100644 index 0000000..839a138 Binary files /dev/null and b/assets/images/loading.png differ diff --git a/assets/images/payGold.png b/assets/images/payGold.png new file mode 100644 index 0000000..6469dd4 Binary files /dev/null and b/assets/images/payGold.png differ diff --git a/assets/images/prominent-disclosure.jpg b/assets/images/prominent-disclosure.jpg new file mode 100644 index 0000000..9e9af3c Binary files /dev/null and b/assets/images/prominent-disclosure.jpg differ diff --git a/assets/images/rawLogo.png b/assets/images/rawLogo.png new file mode 100644 index 0000000..066a4e4 Binary files /dev/null and b/assets/images/rawLogo.png differ diff --git a/assets/images/rawLogoOverlay.png b/assets/images/rawLogoOverlay.png new file mode 100644 index 0000000..9a30986 Binary files /dev/null and b/assets/images/rawLogoOverlay.png differ diff --git a/assets/images/rawLogoOverlay1.png b/assets/images/rawLogoOverlay1.png new file mode 100644 index 0000000..16c7703 Binary files /dev/null and b/assets/images/rawLogoOverlay1.png differ diff --git a/assets/images/rawLogoOverlayOLD.png b/assets/images/rawLogoOverlayOLD.png new file mode 100644 index 0000000..724b707 Binary files /dev/null and b/assets/images/rawLogoOverlayOLD.png differ diff --git a/assets/images/red.png b/assets/images/red.png new file mode 100644 index 0000000..a526478 Binary files /dev/null and b/assets/images/red.png differ diff --git a/assets/images/redBack.png b/assets/images/redBack.png new file mode 100644 index 0000000..99d259e Binary files /dev/null and b/assets/images/redBack.png differ diff --git a/assets/images/refer.png b/assets/images/refer.png new file mode 100644 index 0000000..26a0815 Binary files /dev/null and b/assets/images/refer.png differ diff --git a/assets/images/silver1.png b/assets/images/silver1.png new file mode 100644 index 0000000..2a07e43 Binary files /dev/null and b/assets/images/silver1.png differ diff --git a/assets/images/silverAnimated.png b/assets/images/silverAnimated.png new file mode 100644 index 0000000..452c06f Binary files /dev/null and b/assets/images/silverAnimated.png differ diff --git a/assets/images/store.png b/assets/images/store.png new file mode 100644 index 0000000..ce33a14 Binary files /dev/null and b/assets/images/store.png differ diff --git a/assets/images/tesoCouponInsignia.png b/assets/images/tesoCouponInsignia.png new file mode 100644 index 0000000..8ca5462 Binary files /dev/null and b/assets/images/tesoCouponInsignia.png differ diff --git a/assets/images/tesoDP/dp1.png b/assets/images/tesoDP/dp1.png new file mode 100644 index 0000000..79002d3 Binary files /dev/null and b/assets/images/tesoDP/dp1.png differ diff --git a/assets/images/twitter.png b/assets/images/twitter.png new file mode 100644 index 0000000..6bda388 Binary files /dev/null and b/assets/images/twitter.png differ diff --git a/assets/images/wallet.png b/assets/images/wallet.png new file mode 100644 index 0000000..dec1f87 Binary files /dev/null and b/assets/images/wallet.png differ diff --git a/assets/lovw.gif b/assets/lovw.gif new file mode 100644 index 0000000..e864332 Binary files /dev/null and b/assets/lovw.gif differ diff --git a/assets/styles/dark.txt b/assets/styles/dark.txt new file mode 100644 index 0000000..a84674d --- /dev/null +++ b/assets/styles/dark.txt @@ -0,0 +1,285 @@ +[ + { + "elementType": "geometry", + "stylers": [ + { + "color": "#1d2c4d" + } + ] + }, + { + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#8ec3b9" + } + ] + }, + { + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#1a3646" + } + ] + }, + { + "featureType": "administrative.country", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#4b6878" + } + ] + }, + { + "featureType": "administrative.land_parcel", + "elementType": "labels", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "administrative.land_parcel", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#64779e" + } + ] + }, + { + "featureType": "administrative.province", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#4b6878" + } + ] + }, + { + "featureType": "landscape.man_made", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#334e87" + } + ] + }, + { + "featureType": "landscape.natural", + "elementType": "geometry", + "stylers": [ + { + "color": "#023e58" + } + ] + }, + { + "featureType": "poi", + "elementType": "geometry", + "stylers": [ + { + "color": "#283d6a" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#6f9ba5" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#1d2c4d" + } + ] + }, + { + "featureType": "poi.business", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#023e58" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#3C7680" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry", + "stylers": [ + { + "color": "#304a7d" + } + ] + }, + { + "featureType": "road", + "elementType": "labels.icon", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "road", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#98a5be" + } + ] + }, + { + "featureType": "road", + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#1d2c4d" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry", + "stylers": [ + { + "color": "#2c6675" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#255763" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#b0d5ce" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#023e58" + } + ] + }, + { + "featureType": "road.local", + "elementType": "labels", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "transit", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "transit", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#98a5be" + } + ] + }, + { + "featureType": "transit", + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#1d2c4d" + } + ] + }, + { + "featureType": "transit.line", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#283d6a" + } + ] + }, + { + "featureType": "transit.station", + "elementType": "geometry", + "stylers": [ + { + "color": "#3a4762" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry", + "stylers": [ + { + "color": "#0e1626" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#4e6d70" + } + ] + } + ] \ No newline at end of file diff --git a/assets/styles/light.txt b/assets/styles/light.txt new file mode 100644 index 0000000..add938a --- /dev/null +++ b/assets/styles/light.txt @@ -0,0 +1,160 @@ +[ + { + "elementType": "geometry", + "stylers": [ + { + "color": "#f5f5f5" + } + ] + }, + { + "elementType": "labels.icon", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#616161" + } + ] + }, + { + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#f5f5f5" + } + ] + }, + { + "featureType": "administrative.land_parcel", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#bdbdbd" + } + ] + }, + { + "featureType": "poi", + "elementType": "geometry", + "stylers": [ + { + "color": "#eeeeee" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry", + "stylers": [ + { + "color": "#e5e5e5" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9e9e9e" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry", + "stylers": [ + { + "color": "#ffffff" + } + ] + }, + { + "featureType": "road.arterial", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry", + "stylers": [ + { + "color": "#dadada" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#616161" + } + ] + }, + { + "featureType": "road.local", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9e9e9e" + } + ] + }, + { + "featureType": "transit.line", + "elementType": "geometry", + "stylers": [ + { + "color": "#e5e5e5" + } + ] + }, + { + "featureType": "transit.station", + "elementType": "geometry", + "stylers": [ + { + "color": "#eeeeee" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry", + "stylers": [ + { + "color": "#c9c9c9" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9e9e9e" + } + ] + } + ] \ No newline at end of file diff --git a/commentOld.txt b/commentOld.txt new file mode 100644 index 0000000..fff9197 --- /dev/null +++ b/commentOld.txt @@ -0,0 +1,339 @@ +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Classes/Firebase/Comments.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/Posts/user3P_commentTitle.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/CommentsPost.dart'; +import 'package:teso/Pages/PageWidgets/ChatScreen/bottomBar.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:intl/intl.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +class CommentSection extends StatefulWidget { + final Post postedAd; + final TesoUser user; + CommentSection({ + Key key, + @required this.postedAd, + @required this.user, + }) : assert(postedAd != null), + assert(user != null), + super(key: key); + @override + _CommentSectionState createState() => _CommentSectionState(); +} + +class _CommentSectionState extends State { + TextEditingController controller = new TextEditingController(); + final ScrollController listScrollController = ScrollController(); + List listMessage = new List.from([]); + int _limit = 20; + final int _limitIncrement = 20; + int total = 0; + var userDocs; + + _scrollListener() { + if (listScrollController.offset >= + listScrollController.position.maxScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the bottom"); + setState(() { + print("reach the bottom"); + _limit += _limitIncrement; + + FirebaseFirestore.instance + .collection('posts') + .doc(widget.postedAd.postID) + .collection("comments") + .orderBy('timestamp') + .limit(_limit) + .snapshots() + .map((event) => { + listMessage = event.docs + .map((e) => FBComments.fromJSON(e.data())) + .toList() + }); + }); + } + if (listScrollController.offset <= + listScrollController.position.minScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the top"); + setState(() { + print("reach the top"); + }); + } + } + + @override + void initState() { + super.initState(); + listScrollController.addListener(_scrollListener); + _listenComments(); + FirebaseFirestore.instance + .collection("users") + .doc(widget.postedAd.publisherID) + .get() + .then((value) { + setState(() { + userDocs = value.data(); + }); + }); + } + + @override + void dispose() { + controller.dispose(); + listScrollController.dispose(); + super.dispose(); + } + + void sendComment(String text, int position) async { + if (controller.text.isNotEmpty) { + CommentsPost comment = new CommentsPost(); + SharedPreferences.getInstance().then((value) async { + comment.postId = widget.postedAd.postID; + comment.comment = text; + comment.timestamp = DateTime.now().toIso8601String(); + comment.commenterId = value.getString("id"); + comment.commentId = + "TESCPCM" + DateTime.now().toString() + value.getString("id"); + }); + Provider.of(context, listen: false).commentPost(comment); + controller.clear(); + } + } + + _listenComments() { + FirebaseFirestore.instance + .collection('posts') + .doc(widget.postedAd.postID) + .collection("comments") + .orderBy('timestamp') + .limit(_limit) + .snapshots() + .map((event) => { + setState(() { + listMessage = event.docs + .map((e) => FBComments.fromJSON(e.data())) + .toList(); + }) + }); + + FirebaseFirestore.instance + .collection('posts') + .doc(widget.postedAd.postID) + .collection("comments") + .orderBy('timestamp') + .snapshots() + .listen((event) { + if (mounted) { + setState(() { + total = event.docs.length; + }); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("$total Comments"), + actions: [ + IconButton( + onPressed: () => Navigator.pop(context), + icon: Icon( + Icons.close, + ), + ), + ], + ), + body: Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Stack( + children: [ + Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only(bottom: 60), + child: StreamBuilder( + stream: FirebaseFirestore.instance + .collection('posts') + .doc(widget.postedAd.postID) + .collection("comments") + .orderBy('timestamp') + .limit(_limit) + .snapshots(), + builder: (context, snapshot) { + if (snapshot.data == null && + snapshot.connectionState == ConnectionState.waiting) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else if (snapshot.data.docs.length == 0) { + if (widget.postedAd.title != null) { + return Align( + alignment: Alignment.topCenter, + child: buildPostTile3P( + context, widget.user, widget.postedAd), + ); + } else { + return Container(); + } + } else { + listMessage = snapshot.data.docs; + return ListView.builder( + padding: EdgeInsets.all(10.0), + itemCount: listMessage.length, + itemBuilder: (context, index) { + int timeInMillis = int.parse(snapshot.data.docs[index] + .data()['timestamp'] + .toString()); + var date = + DateTime.fromMillisecondsSinceEpoch(timeInMillis); + var formattedDate = + DateFormat("yyyy-MM-dd HH:mm:ss").format(date); + if (index == 0 && widget.postedAd.title != null) { + return Column(children: [ + buildPostTile3P( + context, widget.user, widget.postedAd), + Divider(), + ListTile( + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + )), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: NetworkImage(serverLocation + + "api/pulldp/" + + snapshot.data.docs[index] + .data()['commenterID']), + placeholder: AssetImage( + "assets/images/tesoDP/dp1.png"), + ), + ), + ), + title: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: snapshot.data.docs[index] + .data()['commenter'] + + " ", + style: new TextStyle( + fontWeight: FontWeight.bold)), + new TextSpan( + text: snapshot.data.docs[index] + .data()['comment']), + ], + ), + ), + subtitle: Text( + TimeElapsed().fromDateTime( + DateTime.parse(formattedDate)), + ), + ), + ]); + } + return ListTile( + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + )), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: NetworkImage(serverLocation + + "api/pulldp/" + + snapshot.data.docs[index] + .data()['commenterID']), + placeholder: + AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + title: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: snapshot.data.docs[index] + .data()['commenter'] + + " ", + style: new TextStyle( + fontWeight: FontWeight.bold)), + new TextSpan( + text: snapshot.data.docs[index] + .data()['comment']), + ], + ), + ), + subtitle: Text( + TimeElapsed() + .fromDateTime(DateTime.parse(formattedDate)), + ), + ); + }, + // reverse: true, + controller: listScrollController, + ); + } + }, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: buildBottom( + context, + controller, + sendComment, + ), + ), + ], + ), + ), + ); + } +} diff --git a/fonts/.DS_Store b/fonts/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/fonts/.DS_Store differ diff --git a/fonts/._.DS_Store b/fonts/._.DS_Store new file mode 100644 index 0000000..338bd7b Binary files /dev/null and b/fonts/._.DS_Store differ diff --git a/fonts/AlexBrush-Regular.ttf b/fonts/AlexBrush-Regular.ttf new file mode 100644 index 0000000..58c43a9 Binary files /dev/null and b/fonts/AlexBrush-Regular.ttf differ diff --git a/fonts/Allura-Regular.otf b/fonts/Allura-Regular.otf new file mode 100644 index 0000000..cad9a53 Binary files /dev/null and b/fonts/Allura-Regular.otf differ diff --git a/fonts/Arizonia-Regular.ttf b/fonts/Arizonia-Regular.ttf new file mode 100644 index 0000000..37f2a2c Binary files /dev/null and b/fonts/Arizonia-Regular.ttf differ diff --git a/fonts/Billabong.ttf b/fonts/Billabong.ttf new file mode 100644 index 0000000..0df4bf6 Binary files /dev/null and b/fonts/Billabong.ttf differ diff --git a/fonts/ChunkFive-Regular.otf b/fonts/ChunkFive-Regular.otf new file mode 100644 index 0000000..7711f20 Binary files /dev/null and b/fonts/ChunkFive-Regular.otf differ diff --git a/fonts/Deadhead_Script.ttf b/fonts/Deadhead_Script.ttf new file mode 100644 index 0000000..24670dd Binary files /dev/null and b/fonts/Deadhead_Script.ttf differ diff --git a/fonts/GrandHotel-Regular.otf b/fonts/GrandHotel-Regular.otf new file mode 100644 index 0000000..e79c588 Binary files /dev/null and b/fonts/GrandHotel-Regular.otf differ diff --git a/fonts/GreatVibes-Regular.otf b/fonts/GreatVibes-Regular.otf new file mode 100644 index 0000000..530866e Binary files /dev/null and b/fonts/GreatVibes-Regular.otf differ diff --git a/fonts/Lobster_1.3.otf b/fonts/Lobster_1.3.otf new file mode 100644 index 0000000..5e42281 Binary files /dev/null and b/fonts/Lobster_1.3.otf differ diff --git a/fonts/Locanita.ttf b/fonts/Locanita.ttf new file mode 100644 index 0000000..79d26f5 Binary files /dev/null and b/fonts/Locanita.ttf differ diff --git a/fonts/OpenSans-Regular.ttf b/fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..db43334 Binary files /dev/null and b/fonts/OpenSans-Regular.ttf differ diff --git a/fonts/OstrichSans-Medium.otf b/fonts/OstrichSans-Medium.otf new file mode 100644 index 0000000..6102534 Binary files /dev/null and b/fonts/OstrichSans-Medium.otf differ diff --git a/fonts/Oswald-Regular.ttf b/fonts/Oswald-Regular.ttf new file mode 100644 index 0000000..3f3a1d2 Binary files /dev/null and b/fonts/Oswald-Regular.ttf differ diff --git a/fonts/Pacifico.ttf b/fonts/Pacifico.ttf new file mode 100644 index 0000000..6d47cdc Binary files /dev/null and b/fonts/Pacifico.ttf differ diff --git a/fonts/Quicksand-Regular.otf b/fonts/Quicksand-Regular.otf new file mode 100644 index 0000000..c0ffc32 Binary files /dev/null and b/fonts/Quicksand-Regular.otf differ diff --git a/fonts/Roboto-Regular.ttf b/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000..2c97eea Binary files /dev/null and b/fonts/Roboto-Regular.ttf differ diff --git a/fonts/SEASRN.ttf b/fonts/SEASRN.ttf new file mode 100644 index 0000000..b1d548e Binary files /dev/null and b/fonts/SEASRN.ttf differ diff --git a/fonts/WickedGrit.ttf b/fonts/WickedGrit.ttf new file mode 100644 index 0000000..3554c56 Binary files /dev/null and b/fonts/WickedGrit.ttf differ diff --git a/fonts/Windsong.ttf b/fonts/Windsong.ttf new file mode 100644 index 0000000..22c8e69 Binary files /dev/null and b/fonts/Windsong.ttf differ diff --git a/ios/.DS_Store b/ios/.DS_Store new file mode 100644 index 0000000..ce45c48 Binary files /dev/null and b/ios/.DS_Store differ diff --git a/ios/._.DS_Store b/ios/._.DS_Store new file mode 100644 index 0000000..338bd7b Binary files /dev/null and b/ios/._.DS_Store differ diff --git a/ios/._.gitignore b/ios/._.gitignore new file mode 100644 index 0000000..338bd7b Binary files /dev/null and b/ios/._.gitignore differ diff --git a/ios/._Runner.xcodeproj b/ios/._Runner.xcodeproj new file mode 100644 index 0000000..31d640b Binary files /dev/null and b/ios/._Runner.xcodeproj differ diff --git a/ios/._Runner.xcworkspace b/ios/._Runner.xcworkspace new file mode 100644 index 0000000..beab7eb Binary files /dev/null and b/ios/._Runner.xcworkspace differ diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..e96ef60 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..9367d48 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 8.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..905d290 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,80 @@ +platform :ios, '12.0' +$FirebaseSDKVersion = '7.11.0' +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + + # "fork" of method flutter_install_ios_plugin_pods (in fluttertools podhelpers.rb) to get lts version of ffmpeg + def flutter_install_ios_plugin_pods(ios_application_path = nil) + # defined_in_file is set by CocoaPods and is a Pathname to the Podfile. + ios_application_path ||= File.dirname(defined_in_file.realpath) if self.respond_to?(:defined_in_file) + raise 'Could not find iOS application path' unless ios_application_path + + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + + symlink_dir = File.expand_path('.symlinks', ios_application_path) + system('rm', '-rf', symlink_dir) # Avoid the complication of dependencies like FileUtils. + + symlink_plugins_dir = File.expand_path('plugins', symlink_dir) + system('mkdir', '-p', symlink_plugins_dir) + + plugins_file = File.join(ios_application_path, '..', '.flutter-plugins-dependencies') + plugin_pods = flutter_parse_plugins_file(plugins_file) + plugin_pods.each do |plugin_hash| + plugin_name = plugin_hash['name'] + plugin_path = plugin_hash['path'] + + if (plugin_name && plugin_path) + symlink = File.join(symlink_plugins_dir, plugin_name) + File.symlink(plugin_path, symlink) + + if plugin_name == 'flutter_ffmpeg' + pod plugin_name+'/full-gpl-lts', :path => File.join('.symlinks', 'plugins', plugin_name, 'ios') + else + pod plugin_name, :path => File.join('.symlinks', 'plugins', plugin_name, 'ios') + end + end + end + end + + +target 'Runner' do + # pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '6.33.0' + pod 'GoogleMLKit/ImageLabeling' + pod 'GoogleMLKit/TextRecognition' + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' + end + end +end \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..902c79f --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,734 @@ +PODS: + - abseil/algorithm (0.20200225.0): + - abseil/algorithm/algorithm (= 0.20200225.0) + - abseil/algorithm/container (= 0.20200225.0) + - abseil/algorithm/algorithm (0.20200225.0): + - abseil/base/config + - abseil/algorithm/container (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/base (0.20200225.0): + - abseil/base/atomic_hook (= 0.20200225.0) + - abseil/base/base (= 0.20200225.0) + - abseil/base/base_internal (= 0.20200225.0) + - abseil/base/bits (= 0.20200225.0) + - abseil/base/config (= 0.20200225.0) + - abseil/base/core_headers (= 0.20200225.0) + - abseil/base/dynamic_annotations (= 0.20200225.0) + - abseil/base/endian (= 0.20200225.0) + - abseil/base/errno_saver (= 0.20200225.0) + - abseil/base/exponential_biased (= 0.20200225.0) + - abseil/base/log_severity (= 0.20200225.0) + - abseil/base/malloc_internal (= 0.20200225.0) + - abseil/base/periodic_sampler (= 0.20200225.0) + - abseil/base/pretty_function (= 0.20200225.0) + - abseil/base/raw_logging_internal (= 0.20200225.0) + - abseil/base/spinlock_wait (= 0.20200225.0) + - abseil/base/throw_delegate (= 0.20200225.0) + - abseil/base/atomic_hook (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/base (0.20200225.0): + - abseil/base/atomic_hook + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/log_severity + - abseil/base/raw_logging_internal + - abseil/base/spinlock_wait + - abseil/meta/type_traits + - abseil/base/base_internal (0.20200225.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/base/bits (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/config (0.20200225.0) + - abseil/base/core_headers (0.20200225.0): + - abseil/base/config + - abseil/base/dynamic_annotations (0.20200225.0) + - abseil/base/endian (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver (0.20200225.0): + - abseil/base/config + - abseil/base/exponential_biased (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal (0.20200225.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/base/periodic_sampler (0.20200225.0): + - abseil/base/core_headers + - abseil/base/exponential_biased + - abseil/base/pretty_function (0.20200225.0) + - abseil/base/raw_logging_internal (0.20200225.0): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/base/spinlock_wait (0.20200225.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/throw_delegate (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/container/compressed_tuple (0.20200225.0): + - abseil/utility/utility + - abseil/container/inlined_vector (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/inlined_vector_internal + - abseil/memory/memory + - abseil/container/inlined_vector_internal (0.20200225.0): + - abseil/base/core_headers + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/span + - abseil/memory (0.20200225.0): + - abseil/memory/memory (= 0.20200225.0) + - abseil/memory/memory (0.20200225.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/meta (0.20200225.0): + - abseil/meta/type_traits (= 0.20200225.0) + - abseil/meta/type_traits (0.20200225.0): + - abseil/base/config + - abseil/numeric/int128 (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/strings/internal (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/strings/str_format (0.20200225.0): + - abseil/strings/str_format_internal + - abseil/strings/str_format_internal (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/types/span + - abseil/strings/strings (0.20200225.0): + - abseil/base/base + - abseil/base/bits + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/internal + - abseil/time (0.20200225.0): + - abseil/time/internal (= 0.20200225.0) + - abseil/time/time (= 0.20200225.0) + - abseil/time/internal (0.20200225.0): + - abseil/time/internal/cctz (= 0.20200225.0) + - abseil/time/internal/cctz (0.20200225.0): + - abseil/time/internal/cctz/civil_time (= 0.20200225.0) + - abseil/time/internal/cctz/time_zone (= 0.20200225.0) + - abseil/time/internal/cctz/civil_time (0.20200225.0): + - abseil/base/config + - abseil/time/internal/cctz/time_zone (0.20200225.0): + - abseil/base/config + - abseil/time/internal/cctz/civil_time + - abseil/time/time (0.20200225.0): + - abseil/base/base + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/time/internal/cctz/civil_time + - abseil/time/internal/cctz/time_zone + - abseil/types (0.20200225.0): + - abseil/types/any (= 0.20200225.0) + - abseil/types/bad_any_cast (= 0.20200225.0) + - abseil/types/bad_any_cast_impl (= 0.20200225.0) + - abseil/types/bad_optional_access (= 0.20200225.0) + - abseil/types/bad_variant_access (= 0.20200225.0) + - abseil/types/compare (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - abseil/types/span (= 0.20200225.0) + - abseil/types/variant (= 0.20200225.0) + - abseil/types/any (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_any_cast + - abseil/utility/utility + - abseil/types/bad_any_cast (0.20200225.0): + - abseil/base/config + - abseil/types/bad_any_cast_impl + - abseil/types/bad_any_cast_impl (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/bad_optional_access (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/bad_variant_access (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/compare (0.20200225.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/optional (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/bad_optional_access + - abseil/utility/utility + - abseil/types/span (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/meta/type_traits + - abseil/types/variant (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_variant_access + - abseil/utility/utility + - abseil/utility/utility (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/meta/type_traits + - AppAuth (1.4.0): + - AppAuth/Core (= 1.4.0) + - AppAuth/ExternalUserAgent (= 1.4.0) + - AppAuth/Core (1.4.0) + - AppAuth/ExternalUserAgent (1.4.0) + - BoringSSL-GRPC (0.0.7): + - BoringSSL-GRPC/Implementation (= 0.0.7) + - BoringSSL-GRPC/Interface (= 0.0.7) + - BoringSSL-GRPC/Implementation (0.0.7): + - BoringSSL-GRPC/Interface (= 0.0.7) + - BoringSSL-GRPC/Interface (0.0.7) + - camera (0.0.1): + - Flutter + - cloud_firestore (1.0.1): + - Firebase/Firestore (= 7.3.0) + - firebase_core + - Flutter + - Firebase/Auth (7.3.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 7.3.0) + - Firebase/CoreOnly (7.3.0): + - FirebaseCore (= 7.3.0) + - Firebase/Firestore (7.3.0): + - Firebase/CoreOnly + - FirebaseFirestore (~> 7.3.0) + - Firebase/Messaging (7.3.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 7.3.0) + - firebase_auth (1.0.1): + - Firebase/Auth (= 7.3.0) + - firebase_core + - Flutter + - firebase_core (1.0.1): + - Firebase/CoreOnly (= 7.3.0) + - Flutter + - firebase_messaging (9.0.0): + - Firebase/Messaging (= 7.3.0) + - firebase_core + - Flutter + - firebase_ml_vision (0.1.1): + - firebase_core + - Flutter + - GoogleMLKit/BarcodeScanning + - GoogleMLKit/FaceDetection + - GoogleMLKit/ImageLabeling + - GoogleMLKit/TextRecognition + - FirebaseAuth (7.3.0): + - FirebaseCore (~> 7.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.0) + - GoogleUtilities/Environment (~> 7.0) + - GTMSessionFetcher/Core (~> 1.4) + - FirebaseCore (7.3.0): + - FirebaseCoreDiagnostics (~> 7.0) + - GoogleUtilities/Environment (~> 7.0) + - GoogleUtilities/Logger (~> 7.0) + - FirebaseCoreDiagnostics (7.3.0): + - GoogleDataTransport (~> 8.0) + - GoogleUtilities/Environment (~> 7.0) + - GoogleUtilities/Logger (~> 7.0) + - nanopb (~> 2.30906.0) + - FirebaseFirestore (7.3.0): + - abseil/algorithm (= 0.20200225.0) + - abseil/base (= 0.20200225.0) + - abseil/memory (= 0.20200225.0) + - abseil/meta (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/time (= 0.20200225.0) + - abseil/types (= 0.20200225.0) + - FirebaseCore (~> 7.0) + - "gRPC-C++ (~> 1.28.0)" + - leveldb-library (~> 1.22) + - nanopb (~> 2.30906.0) + - FirebaseInstallations (7.8.0): + - FirebaseCore (~> 7.0) + - GoogleUtilities/Environment (~> 7.0) + - GoogleUtilities/UserDefaults (~> 7.0) + - PromisesObjC (~> 1.2) + - FirebaseInstanceID (7.8.0): + - FirebaseCore (~> 7.0) + - FirebaseInstallations (~> 7.0) + - GoogleUtilities/Environment (~> 7.0) + - GoogleUtilities/UserDefaults (~> 7.0) + - FirebaseMessaging (7.3.0): + - FirebaseCore (~> 7.0) + - FirebaseInstanceID (~> 7.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.0) + - GoogleUtilities/Environment (~> 7.0) + - GoogleUtilities/Reachability (~> 7.0) + - GoogleUtilities/UserDefaults (~> 7.0) + - Flutter (1.0.0) + - flutter_country_picker (0.0.1): + - Flutter + - flutter_ffmpeg (0.4.0): + - Flutter + - flutter_ffmpeg/https (= 0.4.0) + - flutter_ffmpeg/https (0.4.0): + - Flutter + - mobile-ffmpeg-https (= 4.4) + - flutter_inappwebview (0.0.1): + - Flutter + - flutter_inappwebview/Core (= 0.0.1) + - OrderedSet (~> 5.0) + - flutter_inappwebview/Core (0.0.1): + - Flutter + - OrderedSet (~> 5.0) + - flutter_local_notifications (0.0.1): + - Flutter + - flutter_webview_plugin (0.0.1): + - Flutter + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) + - geolocator (6.2.0): + - Flutter + - google_maps_flutter (0.0.1): + - Flutter + - GoogleMaps (< 3.10) + - google_sign_in (0.0.1): + - Flutter + - GoogleSignIn (~> 5.0) + - GoogleDataTransport (8.1.0): + - nanopb (~> 2.30906.0) + - GoogleMaps (3.9.0): + - GoogleMaps/Maps (= 3.9.0) + - GoogleMaps/Base (3.9.0) + - GoogleMaps/Maps (3.9.0): + - GoogleMaps/Base + - GoogleMLKit/BarcodeScanning (2.1.0): + - GoogleMLKit/MLKitCore + - MLKitBarcodeScanning (~> 1.2.0) + - GoogleMLKit/FaceDetection (2.1.0): + - GoogleMLKit/MLKitCore + - MLKitFaceDetection (~> 1.2.0) + - GoogleMLKit/ImageLabeling (2.1.0): + - GoogleMLKit/MLKitCore + - MLKitImageLabeling (~> 1.2.0) + - GoogleMLKit/MLKitCore (2.1.0): + - MLKitCommon (~> 2.1.0) + - GoogleMLKit/TextRecognition (2.1.0): + - GoogleMLKit/MLKitCore + - MLKitTextRecognition (~> 1.2.0) + - GoogleSignIn (5.0.2): + - AppAuth (~> 1.2) + - GTMAppAuth (~> 1.0) + - GTMSessionFetcher/Core (~> 1.1) + - GoogleToolboxForMac/DebugUtils (2.3.1): + - GoogleToolboxForMac/Defines (= 2.3.1) + - GoogleToolboxForMac/Defines (2.3.1) + - GoogleToolboxForMac/Logger (2.3.1): + - GoogleToolboxForMac/Defines (= 2.3.1) + - "GoogleToolboxForMac/NSData+zlib (2.3.1)": + - GoogleToolboxForMac/Defines (= 2.3.1) + - "GoogleToolboxForMac/NSDictionary+URLArguments (2.3.1)": + - GoogleToolboxForMac/DebugUtils (= 2.3.1) + - GoogleToolboxForMac/Defines (= 2.3.1) + - "GoogleToolboxForMac/NSString+URLArguments (= 2.3.1)" + - "GoogleToolboxForMac/NSString+URLArguments (2.3.1)" + - GoogleUtilities/AppDelegateSwizzler (7.3.1): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.3.1): + - PromisesObjC (~> 1.2) + - GoogleUtilities/Logger (7.3.1): + - GoogleUtilities/Environment + - GoogleUtilities/Network (7.3.1): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.3.1)" + - GoogleUtilities/Reachability (7.3.1): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.3.1): + - GoogleUtilities/Logger + - GoogleUtilitiesComponents (1.0.0): + - GoogleUtilities/Logger + - "gRPC-C++ (1.28.2)": + - "gRPC-C++/Implementation (= 1.28.2)" + - "gRPC-C++/Interface (= 1.28.2)" + - "gRPC-C++/Implementation (1.28.2)": + - abseil/container/inlined_vector (= 0.20200225.0) + - abseil/memory/memory (= 0.20200225.0) + - abseil/strings/str_format (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - "gRPC-C++/Interface (= 1.28.2)" + - gRPC-Core (= 1.28.2) + - "gRPC-C++/Interface (1.28.2)" + - gRPC-Core (1.28.2): + - gRPC-Core/Implementation (= 1.28.2) + - gRPC-Core/Interface (= 1.28.2) + - gRPC-Core/Implementation (1.28.2): + - abseil/container/inlined_vector (= 0.20200225.0) + - abseil/memory/memory (= 0.20200225.0) + - abseil/strings/str_format (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - BoringSSL-GRPC (= 0.0.7) + - gRPC-Core/Interface (= 1.28.2) + - gRPC-Core/Interface (1.28.2) + - GTMAppAuth (1.1.0): + - AppAuth/Core (~> 1.4) + - GTMSessionFetcher (~> 1.4) + - GTMSessionFetcher (1.5.0): + - GTMSessionFetcher/Full (= 1.5.0) + - GTMSessionFetcher/Core (1.5.0) + - GTMSessionFetcher/Full (1.5.0): + - GTMSessionFetcher/Core (= 1.5.0) + - image_cropper (0.0.4): + - Flutter + - TOCropViewController (~> 2.6.0) + - image_gallery_saver (1.5.0): + - Flutter + - image_picker (0.0.1): + - Flutter + - leveldb-library (1.22.1) + - libwebp (1.2.0): + - libwebp/demux (= 1.2.0) + - libwebp/mux (= 1.2.0) + - libwebp/webp (= 1.2.0) + - libwebp/demux (1.2.0): + - libwebp/webp + - libwebp/mux (1.2.0): + - libwebp/demux + - libwebp/webp (1.2.0) + - location (0.0.1): + - Flutter + - MLKitBarcodeScanning (1.2.0): + - MLKitCommon (~> 2.1) + - MLKitVision (~> 1.2) + - MLKitCommon (2.1.0): + - GoogleDataTransport (~> 8.0) + - GoogleToolboxForMac/Logger (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" + - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" + - GoogleUtilities/UserDefaults (~> 7.0) + - GoogleUtilitiesComponents (~> 1.0) + - GTMSessionFetcher/Core (~> 1.1) + - Protobuf (~> 3.12) + - MLKitFaceDetection (1.2.0): + - MLKitCommon (~> 2.1) + - MLKitVision (~> 1.2) + - MLKitImageLabeling (1.2.0): + - MLKitCommon (~> 2.1) + - MLKitImageLabelingCommon (~> 1.2) + - MLKitVision (~> 1.2) + - MLKitVisionKit (~> 2.1) + - MLKitImageLabelingCommon (1.2.0): + - MLKitCommon (~> 2.1) + - MLKitVision (~> 1.2) + - MLKitObjectDetectionCommon (1.2.0): + - MLKitCommon (~> 2.1) + - MLKitVision (~> 1.2) + - MLKitTextRecognition (1.2.0): + - MLKitCommon (~> 2.1) + - MLKitVision (~> 1.2) + - MLKitVision (1.2.0): + - GoogleToolboxForMac/Logger (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" + - GTMSessionFetcher/Core (~> 1.1) + - MLKitCommon (~> 2.1) + - Protobuf (~> 3.12) + - MLKitVisionKit (2.1.0): + - MLKitCommon (~> 2.1) + - MLKitImageLabelingCommon (~> 1.2) + - MLKitObjectDetectionCommon (~> 1.2) + - MLKitVision (~> 1.2) + - mobile-ffmpeg-https (4.4) + - MTBBarcodeScanner (5.0.11) + - nanopb (2.30906.0): + - nanopb/decode (= 2.30906.0) + - nanopb/encode (= 2.30906.0) + - nanopb/decode (2.30906.0) + - nanopb/encode (2.30906.0) + - OrderedSet (5.0.0) + - path_provider (0.0.1): + - Flutter + - "permission_handler (5.1.0+2)": + - Flutter + - PromisesObjC (1.2.12) + - Protobuf (3.14.0) + - qr_code_scanner (0.2.0): + - Flutter + - MTBBarcodeScanner + - share (0.0.1): + - Flutter + - shared_preferences (0.0.1): + - Flutter + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + - TOCropViewController (2.6.0) + - url_launcher (0.0.1): + - Flutter + - video_player (0.0.1): + - Flutter + - video_thumbnail (0.0.1): + - Flutter + - libwebp + - wakelock (0.0.1): + - Flutter + - webview_flutter (0.0.1): + - Flutter + +DEPENDENCIES: + - camera (from `.symlinks/plugins/camera/ios`) + - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) + - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) + - firebase_ml_vision (from `.symlinks/plugins/firebase_ml_vision/ios`) + - Flutter (from `Flutter`) + - flutter_country_picker (from `.symlinks/plugins/flutter_country_picker/ios`) + - flutter_ffmpeg (from `.symlinks/plugins/flutter_ffmpeg/ios`) + - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) + - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) + - flutter_webview_plugin (from `.symlinks/plugins/flutter_webview_plugin/ios`) + - geolocator (from `.symlinks/plugins/geolocator/ios`) + - google_maps_flutter (from `.symlinks/plugins/google_maps_flutter/ios`) + - google_sign_in (from `.symlinks/plugins/google_sign_in/ios`) + - GoogleMLKit/ImageLabeling + - GoogleMLKit/TextRecognition + - image_cropper (from `.symlinks/plugins/image_cropper/ios`) + - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) + - image_picker (from `.symlinks/plugins/image_picker/ios`) + - location (from `.symlinks/plugins/location/ios`) + - path_provider (from `.symlinks/plugins/path_provider/ios`) + - permission_handler (from `.symlinks/plugins/permission_handler/ios`) + - qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`) + - share (from `.symlinks/plugins/share/ios`) + - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + - url_launcher (from `.symlinks/plugins/url_launcher/ios`) + - video_player (from `.symlinks/plugins/video_player/ios`) + - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) + - wakelock (from `.symlinks/plugins/wakelock/ios`) + - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) + +SPEC REPOS: + trunk: + - abseil + - AppAuth + - BoringSSL-GRPC + - Firebase + - FirebaseAuth + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseFirestore + - FirebaseInstallations + - FirebaseInstanceID + - FirebaseMessaging + - FMDB + - GoogleDataTransport + - GoogleMaps + - GoogleMLKit + - GoogleSignIn + - GoogleToolboxForMac + - GoogleUtilities + - GoogleUtilitiesComponents + - "gRPC-C++" + - gRPC-Core + - GTMAppAuth + - GTMSessionFetcher + - leveldb-library + - libwebp + - MLKitBarcodeScanning + - MLKitCommon + - MLKitFaceDetection + - MLKitImageLabeling + - MLKitImageLabelingCommon + - MLKitObjectDetectionCommon + - MLKitTextRecognition + - MLKitVision + - MLKitVisionKit + - mobile-ffmpeg-https + - MTBBarcodeScanner + - nanopb + - OrderedSet + - PromisesObjC + - Protobuf + - TOCropViewController + +EXTERNAL SOURCES: + camera: + :path: ".symlinks/plugins/camera/ios" + cloud_firestore: + :path: ".symlinks/plugins/cloud_firestore/ios" + firebase_auth: + :path: ".symlinks/plugins/firebase_auth/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_messaging: + :path: ".symlinks/plugins/firebase_messaging/ios" + firebase_ml_vision: + :path: ".symlinks/plugins/firebase_ml_vision/ios" + Flutter: + :path: Flutter + flutter_country_picker: + :path: ".symlinks/plugins/flutter_country_picker/ios" + flutter_ffmpeg: + :path: ".symlinks/plugins/flutter_ffmpeg/ios" + flutter_inappwebview: + :path: ".symlinks/plugins/flutter_inappwebview/ios" + flutter_local_notifications: + :path: ".symlinks/plugins/flutter_local_notifications/ios" + flutter_webview_plugin: + :path: ".symlinks/plugins/flutter_webview_plugin/ios" + geolocator: + :path: ".symlinks/plugins/geolocator/ios" + google_maps_flutter: + :path: ".symlinks/plugins/google_maps_flutter/ios" + google_sign_in: + :path: ".symlinks/plugins/google_sign_in/ios" + image_cropper: + :path: ".symlinks/plugins/image_cropper/ios" + image_gallery_saver: + :path: ".symlinks/plugins/image_gallery_saver/ios" + image_picker: + :path: ".symlinks/plugins/image_picker/ios" + location: + :path: ".symlinks/plugins/location/ios" + path_provider: + :path: ".symlinks/plugins/path_provider/ios" + permission_handler: + :path: ".symlinks/plugins/permission_handler/ios" + qr_code_scanner: + :path: ".symlinks/plugins/qr_code_scanner/ios" + share: + :path: ".symlinks/plugins/share/ios" + shared_preferences: + :path: ".symlinks/plugins/shared_preferences/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" + url_launcher: + :path: ".symlinks/plugins/url_launcher/ios" + video_player: + :path: ".symlinks/plugins/video_player/ios" + video_thumbnail: + :path: ".symlinks/plugins/video_thumbnail/ios" + wakelock: + :path: ".symlinks/plugins/wakelock/ios" + webview_flutter: + :path: ".symlinks/plugins/webview_flutter/ios" + +SPEC CHECKSUMS: + abseil: 6c8eb7892aefa08d929b39f9bb108e5367e3228f + AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7 + BoringSSL-GRPC: 8edf627ee524575e2f8d19d56f068b448eea3879 + camera: a0ca5080336f7af47b88436e5e26da3dee5568f0 + cloud_firestore: 69d71054fcab9a1c0d4779362f85b94aec7ff7d7 + Firebase: 26223c695fe322633274198cb19dca8cb7e54416 + firebase_auth: 9f6491ea8e44570323361ae713a2ae3175b3f21a + firebase_core: d2e03528e2a600891f6f460b5e92932624480d1d + firebase_messaging: fc1811236795c2313b8339c35d31295b1cd8486f + firebase_ml_vision: b3b6c056391d73d19419d237d755056a9a7be1e2 + FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2 + FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972 + FirebaseCoreDiagnostics: d50e11039e5984d92c8a512be2395f13df747350 + FirebaseFirestore: 1906bf163afdb7c432d2e3b5c40ceb9dd2df5820 + FirebaseInstallations: 7f7ed0e7e27fb51f57291e1876e2ddb1524126c1 + FirebaseInstanceID: aaecc93b4528bbcafea12c477e26827719ca1183 + FirebaseMessaging: 68d1bcb14880189558a8ae57167abe0b7e417232 + Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + flutter_country_picker: b73efbc3f1b01519adb99bf8635865737b9422df + flutter_ffmpeg: bc8496ea20331e486cd29a03a3c6ff10d32db565 + flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 + flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 + flutter_webview_plugin: ed9e8a6a96baf0c867e90e1bce2673913eeac694 + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + geolocator: f5e3de65e241caba7ce3e8a618803387bda73384 + google_maps_flutter: c7f9c73576de1fbe152a227bfd6e6c4ae8088619 + google_sign_in: 6bd214b9c154f881422f5fe27b66aaa7bbd580cc + GoogleDataTransport: 116c84c4bdeb76be2a7a46de51244368f9794eab + GoogleMaps: 4b5346bddfe6911bb89155d43c903020170523ac + GoogleMLKit: 6ca2a10de262ee1017b52ac045e8967884ace992 + GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213 + GoogleToolboxForMac: 471e0c05d39506e50e6398f46fa9a12ae0efeff9 + GoogleUtilities: e1d9ed4e544fc32a93e00e721400cbc3f377200d + GoogleUtilitiesComponents: a69c0b3b369ba443e988141e75ef49d9010b1c80 + "gRPC-C++": 13d8ccef97d5c3c441b7e3c529ef28ebee86fad2 + gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5 + GTMAppAuth: 197a8dabfea5d665224aa00d17f164fc2248dab9 + GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 + image_cropper: f1668dd8d2cad2d357955caad15a40547856edcb + image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2 + image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a + leveldb-library: 50c7b45cbd7bf543c81a468fe557a16ae3db8729 + libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0 + location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740 + MLKitBarcodeScanning: e9487b3e7c25d0f347dcbf253e7a540478919d9d + MLKitCommon: 9ed187a042139d51c0d8bf6a8301bb20b375c576 + MLKitFaceDetection: 5b92261dd6e4205e3dab0df62537ac3f4e90e5db + MLKitImageLabeling: 21c8acbbfac1e9dd912f0cdc5bf8cb09f0f98125 + MLKitImageLabelingCommon: 621e55f48a1709b886489925edb5939791b66f8c + MLKitObjectDetectionCommon: 7b197838c58568d44f34cf2fe874787ab43d63c1 + MLKitTextRecognition: 332ce64ab281af7b38c806746e1fc53a58eb59ef + MLKitVision: 51385878c9100024478971856510f9271ff555b5 + MLKitVisionKit: 92bddf95a91152419cdf3cbf2c2aed43cdf97cc2 + mobile-ffmpeg-https: 311fc4a8f671cf36d1d6b8f421e8246b790978d9 + MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb + nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53 + OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c + path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 + PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 + Protobuf: 0cde852566359049847168e51bd1c690e0f70056 + qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e + share: 0b2c3e82132f5888bccca3351c504d0003b3b410 + shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + TOCropViewController: 3105367e808b7d3d886a74ff59bf4804e7d3ab38 + url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef + video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e + video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 + wakelock: b0843b2479edbf6504d8d262c2959446f35373aa + webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b + +PODFILE CHECKSUM: 12f79c4590ce6d5ca6232b452ffba88637bd6807 + +COCOAPODS: 1.10.1 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..51e4299 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,575 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + DE7D3950774AF2253178F202 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29578401A5FB3088ABB7635B /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 29578401A5FB3088ABB7635B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 38CB9D02DDDA86A3D451C4D0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A59F426903D2E7C7F7F74FB8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + DD7F4377AC18215A9CA08DC8 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DE7D3950774AF2253178F202 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 23D06BD6AD1FD965EEBFDFB4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 29578401A5FB3088ABB7635B /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 4334E30C393B71F7A6E589D8 /* Pods */ = { + isa = PBXGroup; + children = ( + 38CB9D02DDDA86A3D451C4D0 /* Pods-Runner.debug.xcconfig */, + A59F426903D2E7C7F7F74FB8 /* Pods-Runner.release.xcconfig */, + DD7F4377AC18215A9CA08DC8 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 4334E30C393B71F7A6E589D8 /* Pods */, + 23D06BD6AD1FD965EEBFDFB4 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 559AC78690E5F3AE154B12A1 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 5E474C80B70EC554E7278F77 /* [CP] Embed Pods Frameworks */, + 1065E7A3A2329C46618FAF60 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1240; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1065E7A3A2329C46618FAF60 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 559AC78690E5F3AE154B12A1 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 5E474C80B70EC554E7278F77 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = W62TU85F86; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tesoapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = W62TU85F86; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tesoapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = W62TU85F86; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tesoapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..46d2d0d --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 0000000..d6470bf --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,38 @@ + + + + + CLIENT_ID + 280510379185-67vianhh973klriv75ip2tb9cf8ibitl.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.280510379185-67vianhh973klriv75ip2tb9cf8ibitl + ANDROID_CLIENT_ID + 280510379185-ac7qjglm9cct9u0diqrns6om8t2mb9ug.apps.googleusercontent.com + API_KEY + AIzaSyCBDtZO2_Z5JjrNj1TocK6YAlk_wDbzZHs + GCM_SENDER_ID + 280510379185 + PLIST_VERSION + 1 + BUNDLE_ID + com.tesoapp + PROJECT_ID + teso-ghana + STORAGE_BUCKET + teso-ghana.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:280510379185:ios:52af12a619d5443119de90 + DATABASE_URL + https://teso-ghana-default-rtdb.firebaseio.com + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..2ef776c --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,127 @@ + + + + + BGTaskSchedulerPermittedIdentifiers + + $(PRODUCT_BUNDLE_IDENTIFIER) + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Teso + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + teso + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.googleusercontent.apps.280510379185-67vianhh973klriv75ip2tb9cf8ibitl + fb1759156777594606 + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSApplicationQueriesSchemes + + fbapi + fbapi20130214 + fbapi20130410 + fbapi20130702 + fbapi20131010 + fbapi20131219 + fbapi20140410 + fbapi20140116 + fbapi20150313 + fbapi20150629 + fbapi20160328 + fbauth + fb-messenger-share-api + fbauth2 + fbshareextension + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsArbitraryLoadsInWebContent + + + NSCameraUsageDescription + This app needs camera access to scan QR codes and capture photos + NSLocationAlwaysAndWhenInUseUsageDescription + Teso needs access to location. + NSLocationAlwaysUsageDescription + Teso needs access to location. + NSLocationWhenInUseUsageDescription + Teso needs access to location. + NSMicrophoneUsageDescription + Allow access to microphone + NSPhotoLibraryAddUsageDescription + Teso needs permission to write videos and photos + NSPhotoLibraryUsageDescription + Teso needs permission to select videos and photos + UIBackgroundModes + + fetch + location + processing + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + io.flutter.embedded_views_preview + + + FUMaximumConnectionsPerHost + 5 + + FUMaximumUploadOperation + 5 + + FUTimeoutInSeconds + 3600 + FUAllFilesUploadedMessage + Posts successfully published on Teso + LSApplicationQueriesSchemes + + https + http + + + \ No newline at end of file diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 0000000..6b8c1cb Binary files /dev/null and b/lib/.DS_Store differ diff --git a/lib/Classes/.DS_Store b/lib/Classes/.DS_Store new file mode 100644 index 0000000..2879abf Binary files /dev/null and b/lib/Classes/.DS_Store differ diff --git a/lib/Classes/API Clasess/BusinessProfile.dart b/lib/Classes/API Clasess/BusinessProfile.dart new file mode 100644 index 0000000..fbfef27 --- /dev/null +++ b/lib/Classes/API Clasess/BusinessProfile.dart @@ -0,0 +1,49 @@ +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API%20Clasess/Product.dart'; + +import '../TesoUser.dart'; + +class BusinessProfileClass { + List products; + List coupons; + List subscribers; + bool subscribed; + + BusinessProfileClass({ + this.coupons, + this.products, + this.subscribed, + this.subscribers, + }); + + factory BusinessProfileClass.fromJSON(Map json) { + var subscribers = json["subscribers"] as List; + List subs = subscribers.map((e) => TesoUser.fromJSON(e)).toList(); + subs.removeWhere( + (element) => element.userGUID == "null" || element.userGUID == null); + var comments = json["coupons"] as List; + List cmnt = + comments.map((e) => CouponDetails.fromJSON(e)).toList(); + var prod = json["products"] as List; + List pro = prod.map((e) => Product.fromJson(e)).toList(); + + return BusinessProfileClass( + coupons: cmnt, + products: pro, + subscribed: json["subscribed"], + subscribers: subs); + } + Map toJson() { + final Map data = Map(); + data['coupons'] = this.coupons; + data['subscribers'] = this.subscribers; + data['products'] = this.products; + data['subscribed'] = this.subscribed; + return data; + } + + @override + String toString() { + return '{"coupons": "$coupons", "subscribers": "$subscribers", "subscribed": "$subscribed","products" :"$products"}'; + } +} diff --git a/lib/Classes/API Clasess/CampAd.dart b/lib/Classes/API Clasess/CampAd.dart new file mode 100644 index 0000000..2b23306 --- /dev/null +++ b/lib/Classes/API Clasess/CampAd.dart @@ -0,0 +1,32 @@ +class CampAd { + String postId; + String campaignId; + bool approved; + + CampAd({ + this.approved, + this.campaignId, + this.postId, + }); + + factory CampAd.fromJSON(Map json) { + return CampAd( + postId: json["postID"], + campaignId: json["campaignId"], + approved: json["approved"] != null ? json["approved"] : false, + ); + } + + Map toJson() { + final Map data = Map(); + data['postId'] = this.postId; + data['approved'] = this.approved; + data['campaignId'] = this.campaignId; + return data; + } + + @override + String toString() { + return '{"postId": "$postId","approved":"$approved","campaignId" :"$campaignId"}'; + } +} diff --git a/lib/Classes/API Clasess/Campaign.dart b/lib/Classes/API Clasess/Campaign.dart new file mode 100644 index 0000000..7dc5c72 --- /dev/null +++ b/lib/Classes/API Clasess/Campaign.dart @@ -0,0 +1,57 @@ +class Campaign { + String campaignID; + String description; + String businessID; + String status; + String title; + DateTime startDate; + String targetProduct; + int rewards; + + Campaign({ + this.businessID, + this.campaignID, + this.description, + this.rewards, + this.startDate, + this.status, + this.targetProduct, + this.title, + }); + + factory Campaign.fromJSON(Map json) { + return Campaign( + businessID: json["businessId"], + campaignID: json["campaignId"], + description: json["description"], + status: json["status"], + title: json["title"], + startDate: DateTime.tryParse((json["startDate"].toString())), + targetProduct: json["targetProduct"], + rewards: int.tryParse((json["rewards"].toString())), + ); + } + + Map toJson() { + final Map data = Map(); + try { + data['businessId'] = this.businessID; + data['campaignId'] = this.campaignID; + data['description'] = this.description; + data['status'] = this.status.toString(); + data['startDate'] = this.startDate.toString(); + data['title'] = this.title; + data['targetProduct'] = this.targetProduct; + data['rewards'] = this.rewards; + } catch (e) { + print(e); + } + return data; + } + + @override + String toString() { + return '{"businessId": "$businessID", "campaignId": "$campaignID", "description": "$description", "status": "$status",' + + '"startDate": "$startDate", "title": "$title", "targetProduct": "$targetProduct", "rewards": "$rewards"}'; + } +} diff --git a/lib/Classes/API Clasess/CommentsPost.dart b/lib/Classes/API Clasess/CommentsPost.dart new file mode 100644 index 0000000..be54b3c --- /dev/null +++ b/lib/Classes/API Clasess/CommentsPost.dart @@ -0,0 +1,38 @@ +class CommentsPost { + String postId; + String commentId; + String comment; + String timestamp; + String commenterId; + + CommentsPost({ + this.postId, + this.commentId, + this.comment, + this.commenterId, + this.timestamp, + }); + factory CommentsPost.fromJSON(Map json) { + return CommentsPost( + commenterId: json["commenterId"], + postId: json["postId"], + commentId: json["pommentId"], + comment: json["comment"], + timestamp: json["timestamp"]); + } + + Map toJson() { + final Map data = Map(); + data['commenterId'] = this.commenterId; + data['postId'] = this.postId; + data['commentId'] = this.commentId; + data['comment'] = this.comment; + data['timestamp'] = this.timestamp; + return data; + } + + @override + String toString() { + return '{"comment":"$comment","commenterId": "$commenterId", "postId": "$postId","commentId":"$commentId","timestamp":"$timestamp"}'; + } +} diff --git a/lib/Classes/API Clasess/CouponDetails.dart b/lib/Classes/API Clasess/CouponDetails.dart new file mode 100644 index 0000000..8bc174e --- /dev/null +++ b/lib/Classes/API Clasess/CouponDetails.dart @@ -0,0 +1,109 @@ +import 'TesoBusinessDetail.dart'; +import 'Product.dart'; + +class CouponDetails { + String couponId; + String businessId; + Product targetProduct; + String type; + int quantity; + DateTime expiration; + double worth; + String state; + TesoBusinessDetail issuer; + double productCost; + double lowerLimit; + double upperLimit; + String countID; + String condition; + + CouponDetails( + {this.businessId, + this.couponId, + this.expiration, + this.issuer, + this.productCost, + this.quantity, + this.state, + this.targetProduct, + this.type, + this.worth, + this.lowerLimit, + this.upperLimit, + this.countID, + this.condition}); + + factory CouponDetails.fromJSON(Map json) { + try { + return CouponDetails( + businessId: json['businessId'], + couponId: json['couponId'], + targetProduct: Product.fromJson(json['target']), + expiration: DateTime.parse((json['expiration'].toString())), + issuer: TesoBusinessDetail.fromJSON(json['issuer']), + productCost: double.tryParse((json['productCost']).toString()), + lowerLimit: double.parse((json['lowerLimit']).toString()), + upperLimit: double.parse((json['upperLimit']).toString()), + quantity: json['quantity'], + state: json['state'], + type: json['type'], + worth: double.parse((json['worth']).toString()) == 0.0 + ? double.parse((json['lowerLimit']).toString()) + : double.parse((json['worth']).toString()), + countID: json['countID'].toString(), + condition: json['condition'].toString()); + } catch (e) { + print(e); + return null; + } + } + + factory CouponDetails.fromJSON2(Map json) { + return CouponDetails( + businessId: json['businessId'], + couponId: json['couponId'], + expiration: DateTime.parse((json['expiration'].toString())), + issuer: TesoBusinessDetail.fromJSON(json['issuer']), + productCost: double.tryParse((json['productCost']).toString()), + lowerLimit: double.parse((json['lowerLimit']).toString()), + upperLimit: double.parse((json['upperLimit']).toString()), + quantity: json['quantity'], + state: json['state'], + type: json['type'], + worth: double.parse((json['worth']).toString()) == 0.0 + ? double.parse((json['lowerLimit']).toString()) + : double.parse((json['worth']).toString()), + countID: json['countID'].toString(), + condition: json['condition'].toString()); + } + + Map toJson() { + final Map data = Map(); + try { + data['businessId'] = this.businessId; + data['couponId'] = this.couponId; + data['target'] = this.targetProduct; + data['expiration'] = this.expiration.toIso8601String(); + data['issuer'] = this.issuer; + data['productCost'] = this.productCost.toString(); + data['quantity'] = this.quantity; + data['state'] = this.state; + data['type'] = this.type; + data['worth'] = this.worth; + data['lowerLimit'] = this.lowerLimit; + data['upperLimit'] = this.upperLimit; + data['countID'] = this.countID; + data['condition'] = this.condition; + } catch (e) { + print(e); + } + return data; + } + + @override + String toString() { + return '{"businessId": "$businessId", "couponId": "$couponId", "target": "$targetProduct", "couponId": "$couponId", "expiration": "$expiration"' + + '"issuer": "$issuer", "productCost": "$productCost", "quantity": "$quantity", "state": "$state", "type": "$type", "worth": "$worth",' + + '"lowerLimit": "$lowerLimit", "upperLimit": "$upperLimit","countID":"$countID","condition":"$condition"}'; + } +} diff --git a/lib/Classes/API Clasess/CouponHead.dart b/lib/Classes/API Clasess/CouponHead.dart new file mode 100644 index 0000000..e46571f --- /dev/null +++ b/lib/Classes/API Clasess/CouponHead.dart @@ -0,0 +1,59 @@ +class CouponsHead { + String couponId; + String businessId; + String targetProduct; + String type; + int quantity; + DateTime expiration; + double lower; + double upper; + String state; + DateTime generated; + + CouponsHead( + {this.businessId, + this.couponId, + this.expiration, + this.quantity, + this.state, + this.targetProduct, + this.type, + this.lower, + this.upper, + this.generated}); + + CouponsHead.fromJSON(Map json) + : businessId = json['businessId'], + couponId = json['couponId'], + expiration = DateTime.tryParse((json['expiration']).toString()), + generated = DateTime.tryParse((json['generated']).toString()), + quantity = int.parse((json['quantity']).toString()), + state = json['state'], + type = json['type'], + targetProduct = json['targetProduct'], + lower = double.parse((json['lower']).toString()), + upper = double.parse((json['upper']).toString()); + + Map toJson() { + final Map data = Map(); + data['businessID'] = this.businessId; + data['couponID'] = this.couponId; + data['expiration'] = this.expiration.toIso8601String(); + data['generated'] = this.generated != null + ? this.generated.toIso8601String() + : DateTime.now().toIso8601String(); + data['quantity'] = this.quantity; + data['state'] = this.state; + data['type'] = this.type; + data['targetProduct'] = this.targetProduct; + data['lowerlimit'] = this.lower.toString(); + data['upperlimit'] = this.upper.toString(); + return data; + } + + @override + String toString() { + return '{"businessID": "$businessId","couponID": "$couponId", "expiration": "$expiration", "generated": "$generated","quantity": "$quantity",' + + '"state": "$state", "type": "$type", "targetProduct": "$targetProduct","lower": "$lower","upper": "$upper"}'; + } +} diff --git a/lib/Classes/API Clasess/Desire.dart b/lib/Classes/API Clasess/Desire.dart new file mode 100644 index 0000000..2b3682b --- /dev/null +++ b/lib/Classes/API Clasess/Desire.dart @@ -0,0 +1,41 @@ +class Desire { + String productName; + String productID; + String productImage; + String enlisted; + double price; + String category; + + Desire( + {this.productName, + this.enlisted, + this.productID, + this.productImage, + this.price, + this.category}); + factory Desire.fromJSON(Map json) { + return Desire( + productID: json["productID"], + productName: json["productName"], + productImage: json["productImage"], + enlisted: json["enlisted"], + category: json["category"], + price: double.tryParse(json["cost"].toString())); + } + + Map toJson() { + final Map data = Map(); + data['productID'] = this.productID; + data['productName'] = this.productName; + data['enlisted'] = this.enlisted; + data['productImage'] = this.productImage; + data['cost'] = this.price; + data['category'] = this.category; + return data; + } + + @override + String toString() { + return '{"productID": "$productID", "productName": "$productName", "enlisted": "$enlisted","productImage":"$productImage","cost":"$price","category":"$category"}'; + } +} diff --git a/lib/Classes/API Clasess/ExploreObject.dart b/lib/Classes/API Clasess/ExploreObject.dart new file mode 100644 index 0000000..c879edb --- /dev/null +++ b/lib/Classes/API Clasess/ExploreObject.dart @@ -0,0 +1,36 @@ +import 'package:teso/Classes/API%20Clasess/Product.dart'; + +class ExploreClass { + List trending; + List latest; + + ExploreClass({ + this.trending, + this.latest, + }); + factory ExploreClass.fromJSON(Map json) { + var trends = json["trending"] as List; + List trendProducts = + trends.map((e) => Product.fromJson(e)).toList(); + var newProducts = json["latest"] as List; + List newewst = + newProducts.map((e) => Product.fromJson(e)).toList(); + + return ExploreClass( + trending: trendProducts, + latest: newewst, + ); + } + + Map toJson() { + final Map data = Map(); + data['trending'] = this.trending; + data['latest'] = this.latest; + return data; + } + + @override + String toString() { + return '{"trending": "$trending", "latest": "$latest"}'; + } +} diff --git a/lib/Classes/API Clasess/FacebookUser.dart b/lib/Classes/API Clasess/FacebookUser.dart new file mode 100644 index 0000000..1f27787 --- /dev/null +++ b/lib/Classes/API Clasess/FacebookUser.dart @@ -0,0 +1,53 @@ +class FacebookUser { + String firstname; + String surname; + String email; + String gender; + String username; + String userGUID; + String country; + String pictureUri; + String deviceToken; + String referralCode; + + FacebookUser( + {this.firstname, + this.surname, + this.email, + this.gender, + this.username, + this.referralCode, + this.userGUID, + this.country, + this.pictureUri, + this.deviceToken}); + + factory FacebookUser.fromJSON(Map json) { + return FacebookUser( + firstname: json['firstname'], + surname: json['surname'], + email: json['email'], + gender: json['gender'], + userGUID: json['userGUID'], + username: json['username'], + country: json['country'], + pictureUri: json['pictureuri'], + referralCode: json['referral'], + deviceToken: json['devicetoken']); + } + + Map toJson() { + final Map data = Map(); + data['firstname'] = this.firstname; + data['surname'] = this.surname; + data['email'] = this.email; + data['gender'] = this.gender; + data['userGUID'] = this.userGUID; + data['username'] = this.username; + data['country'] = this.country; + data['pictureuri'] = this.pictureUri; + data['devicetoken'] = this.deviceToken; + data['referral'] = this.referralCode; + return data; + } +} diff --git a/lib/Classes/API Clasess/GoogleUser.dart b/lib/Classes/API Clasess/GoogleUser.dart new file mode 100644 index 0000000..63a653d --- /dev/null +++ b/lib/Classes/API Clasess/GoogleUser.dart @@ -0,0 +1,55 @@ +class GoogleUser { + String firstname; + String surname; + String email; + String gender; + String username; + String userGUID; + String country; + String pictureUri; + String deviceToken; + String referralCode; + + GoogleUser({ + this.firstname, + this.surname, + this.email, + this.gender, + this.username, + this.userGUID, + this.country, + this.pictureUri, + this.deviceToken, + this.referralCode, + }); + + factory GoogleUser.fromJSON(Map json) { + return GoogleUser( + firstname: json['firstname'], + surname: json['surname'], + email: json['email'], + gender: json['gender'], + userGUID: json['userGUID'], + username: json['username'], + country: json['country'], + pictureUri: json['pictureuri'], + deviceToken: json['devicetoken'], + referralCode: json['referral'], + ); + } + + Map toJson() { + final Map data = Map(); + data['firstname'] = this.firstname; + data['surname'] = this.surname; + data['email'] = this.email; + data['gender'] = this.gender; + data['userGUID'] = this.userGUID; + data['username'] = this.username; + data['country'] = this.country; + data['pictureuri'] = this.pictureUri; + data['devicetoken'] = this.deviceToken; + data['referral'] = this.referralCode; + return data; + } +} diff --git a/lib/Classes/API Clasess/MuxURL.dart b/lib/Classes/API Clasess/MuxURL.dart new file mode 100644 index 0000000..67d123c --- /dev/null +++ b/lib/Classes/API Clasess/MuxURL.dart @@ -0,0 +1 @@ +class MuxAuthURL {} diff --git a/lib/Classes/API Clasess/Post.dart b/lib/Classes/API Clasess/Post.dart new file mode 100644 index 0000000..c1d4af2 --- /dev/null +++ b/lib/Classes/API Clasess/Post.dart @@ -0,0 +1,51 @@ +class Post { + String postID; + DateTime timestamp; + String playbackID; + String publisherID; + String title; + String assetID; + String aspect; + String rendition; + + Post({ + this.postID, + this.playbackID, + this.publisherID, + this.title, + this.timestamp, + this.assetID, + this.aspect, + this.rendition, + }); + factory Post.fromJSON(Map json) { + return Post( + publisherID: json["publisherId"], + postID: json["postId"], + title: json["title"], + playbackID: json["playbackID"], + assetID: json["assetID"], + aspect: json["aspect"], + rendition: json["rendition"], + timestamp: DateTime.tryParse(json["timestamp"].toString())); + } + + Map toJson() { + final Map data = Map(); + data['publisherId'] = this.publisherID; + data['postId'] = this.postID; + data['playbackID'] = this.playbackID; + data['title'] = this.title; + data['timestamp'] = this.timestamp.toIso8601String(); + data['assetID'] = this.assetID; + data['aspect'] = this.aspect; + data['rendition'] = this.rendition; + return data; + } + + @override + String toString() { + return '{"publisherId": "$publisherID", "postId": "$postID", "playbackID": "$playbackID","title":"$title","timestamp":' + + '"$timestamp","assetID":"$assetID","aspect":"$aspect","rendition":"$rendition"}'; + } +} diff --git a/lib/Classes/API Clasess/PostFav.dart b/lib/Classes/API Clasess/PostFav.dart new file mode 100644 index 0000000..8826ed9 --- /dev/null +++ b/lib/Classes/API Clasess/PostFav.dart @@ -0,0 +1,34 @@ +class PostFav { + String postId; + String admirerId; + String timestamp; + String countId; + + PostFav({ + this.postId, + this.admirerId, + this.countId, + this.timestamp, + }); + factory PostFav.fromJSON(Map json) { + return PostFav( + countId: json["countId"], + postId: json["postId"], + admirerId: json["admirerId"], + timestamp: json["timestamp"]); + } + + Map toJson() { + final Map data = Map(); + data['countId'] = this.countId; + data['postId'] = this.postId; + data['admirerId'] = this.admirerId; + data['timestamp'] = this.timestamp; + return data; + } + + @override + String toString() { + return '{"countId": "$countId", "postId": "$postId","admirerId":"$admirerId","timestamp":"$timestamp"}'; + } +} diff --git a/lib/Classes/API Clasess/PostUpload.dart b/lib/Classes/API Clasess/PostUpload.dart new file mode 100644 index 0000000..67f95b6 --- /dev/null +++ b/lib/Classes/API Clasess/PostUpload.dart @@ -0,0 +1,38 @@ +class PostUpload { + String title; + String thumbnail; + String aspect; + String campaignID; + String path; + + PostUpload({ + this.title, + this.aspect, + this.campaignID, + this.path, + this.thumbnail, + }); + + PostUpload.fromJson(Map json) + : title = json['title'], + aspect = json['aspect'], + campaignID = json['campaignID'], + path = json['path'], + thumbnail = json['thumbnail']; + + Map toJson() { + final Map data = Map(); + data['title'] = this.title; + data['aspect'] = this.aspect; + data['campaignID'] = this.campaignID; + data['path'] = this.path; + data['thumbnail'] = this.thumbnail; + return data; + } + + @override + String toString() { + return '{"title": "$title","aspect": "$aspect", "campaignID": "$campaignID","path": "$path",' + + '"thumbnail": "$thumbnail"}'; + } +} diff --git a/lib/Classes/API Clasess/PostedAd.dart b/lib/Classes/API Clasess/PostedAd.dart new file mode 100644 index 0000000..a463380 --- /dev/null +++ b/lib/Classes/API Clasess/PostedAd.dart @@ -0,0 +1,60 @@ +import 'package:teso/Classes/API%20Clasess/CampAd.dart'; +import 'package:teso/Classes/API%20Clasess/CommentsPost.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Classes/API%20Clasess/PostFav.dart'; +import 'package:teso/Classes/TesoUser.dart'; + +class PostedAd { + Post post; + List likes; + List comments; + bool campaignAd; + CampAd campAdvert; + TesoUser publisher; + + PostedAd({ + this.post, + this.likes, + this.comments, + List friends, + this.campaignAd, + this.campAdvert, + this.publisher, + }); + factory PostedAd.fromJSON(Map json) { + var favorites = json["likes"] as List; + List favs = favorites.map((e) => PostFav.fromJSON(e)).toList(); + var comments = json["comments"] as List; + List cmnt = + comments.map((e) => CommentsPost.fromJSON(e)).toList(); + + return PostedAd( + post: Post.fromJSON(json["post"]), + likes: favs, + comments: cmnt, + campaignAd: json["campaignAd"], + campAdvert: json["campaignAdvert"] != null + ? CampAd.fromJSON(json["campaignAdvert"]) + : null, + publisher: json["publisher"] != null + ? TesoUser.fromJSON(json["publisher"]) + : null, + ); + } + + Map toJson() { + final Map data = Map(); + data['post'] = this.post; + data['likes'] = this.likes; + data['comments'] = this.comments; + data['campaignAd'] = this.campaignAd; + data['campaignAdvert'] = this.campaignAd; + data['publisher'] = this.publisher; + return data; + } + + @override + String toString() { + return '{"post": "$post", "likes": "$likes", "comments": "$comments","campaignAd" :"$campaignAd","campaignAdvert":"$campAdvert", "publisher":"$publisher"}'; + } +} diff --git a/lib/Classes/API Clasess/PostionAP.dart b/lib/Classes/API Clasess/PostionAP.dart new file mode 100644 index 0000000..de6a2e7 --- /dev/null +++ b/lib/Classes/API Clasess/PostionAP.dart @@ -0,0 +1,25 @@ +class Position1 { + double latitude; + double longitude; + + Position1({ + this.latitude, + this.longitude, + }); + + Position1.fromJSON(Map json) + : latitude = double.parse((json['latitude']).toString()), + longitude = double.parse((json['longitude']).toString()); + + Map toJson() { + final Map data = Map(); + data['latitude'] = this.latitude; + data['longitude'] = this.longitude; + return data; + } + + @override + String toString() { + return '{"latitude": "$latitude","longitude": "$longitude"}'; + } +} diff --git a/lib/Classes/API Clasess/Product.dart b/lib/Classes/API Clasess/Product.dart new file mode 100644 index 0000000..aa01850 --- /dev/null +++ b/lib/Classes/API Clasess/Product.dart @@ -0,0 +1,47 @@ +class Product { + String productName; + String businessID; + String productDesc; + String productID; + String categoryID; + double unitPrice; + String productImage; + + Product( + {this.businessID, + this.categoryID, + this.productDesc, + this.productID, + this.productImage, + this.productName, + this.unitPrice}); + + Product.fromJson(Map json) + : businessID = + json['businessId'] != null ? json['businessId'].toString() : "....", + categoryID = + json['category'] != null ? json['category'].toString() : "....", + productDesc = json['description'], + productID = json['productId'], + productImage = json['productImage'], + productName = json['name'], + unitPrice = double.parse((json['unitPrice']).toString()); + + Map toJson() { + final Map data = Map(); + data['businessId'] = this.businessID; + data['category'] = this.categoryID; + data['description'] = this.productDesc; + data['productId'] = this.productID; + data['productImage'] = this.productImage; + data['name'] = this.productName; + data['unitPrice'] = this.unitPrice; + return data; + } + + @override + String toString() { + return '{"businessId": "$businessID","category": "$categoryID", "description": "$productDesc","productId": "$productID",' + + '"productImage": "$productImage", "name": "$productName", "unitPrice": "$unitPrice"}'; + } +} diff --git a/lib/Classes/API Clasess/ProximityCoupon.dart b/lib/Classes/API Clasess/ProximityCoupon.dart new file mode 100644 index 0000000..be40af4 --- /dev/null +++ b/lib/Classes/API Clasess/ProximityCoupon.dart @@ -0,0 +1,80 @@ +import 'TesoBusinessDetail.dart'; + +class ProximityCoupon { + String couponId; + TesoBusinessDetail business; + String targetName; + String targetID; + String targetImage; + double targetCost; + String tagretDescription; + String type; + int quantity; + DateTime expiration; + double lowerLimit; + double upperLimit; + String state; + String condition; + + ProximityCoupon({ + this.business, + this.couponId, + this.expiration, + this.targetCost, + this.quantity, + this.state, + this.targetID, + this.targetImage, + this.targetName, + this.tagretDescription, + this.lowerLimit, + this.upperLimit, + this.type, + this.condition, + }); + + factory ProximityCoupon.fromJSON(Map json) { + return ProximityCoupon( + business: TesoBusinessDetail.fromJSON(json['business']), + couponId: json['couponId'], + targetID: json['targetID'], + expiration: DateTime.parse(json['expiration']), + targetCost: double.parse((json['targetCost']).toString()), + targetName: json['targetName'], + targetImage: json['targetImage'], + tagretDescription: json['targetDescription'], + lowerLimit: double.parse((json['lowerLimit']).toString()), + upperLimit: double.parse((json['upperLimit']).toString()), + quantity: int.parse(json['quantity'].toString()), + state: json['state'], + type: json['type'], + condition: json['condition']); + } + + Map toJson() { + final Map data = Map(); + data['business'] = this.business; + data['couponId'] = this.couponId; + data['target'] = this.targetID; + data['targetID'] = this.targetID; + data['targetName'] = this.targetName; + data['targetCost'] = this.targetCost; + data['targetImage'] = this.targetImage; + data['targetDescription'] = this.tagretDescription; + data['lowerLimit'] = this.lowerLimit; + data['upperLimit'] = this.upperLimit; + data['expiration'] = this.expiration; + data['quantity'] = this.quantity; + data['state'] = this.state; + data['type'] = this.type; + data['condition'] = this.condition; + return data; + } + + @override + String toString() { + return '{"businessId": "$business", "couponId": "$couponId", "targetID": "$targetID", "couponId": "$couponId", "expiration": "$expiration"' + + '"quantity": "$quantity", "state": "$state", "type": "$type", "lowerLimit":"$lowerLimit","upperLimit":"$upperLimit","targetName":"$targetName",' + + '"targetImage":"$targetImage","targetCost":"$targetCost","targetDescription":"$tagretDescription","condition":"$condition"}'; + } +} diff --git a/lib/Classes/API Clasess/ReferralClass.dart b/lib/Classes/API Clasess/ReferralClass.dart new file mode 100644 index 0000000..d1750b6 --- /dev/null +++ b/lib/Classes/API Clasess/ReferralClass.dart @@ -0,0 +1,11 @@ +class ReferralClass { + String referrer; + String referred; + bool reward; + + ReferralClass({ + this.referred, + this.referrer, + this.reward, + }); +} diff --git a/lib/Classes/API Clasess/Registrar.dart b/lib/Classes/API Clasess/Registrar.dart new file mode 100644 index 0000000..0198e03 --- /dev/null +++ b/lib/Classes/API Clasess/Registrar.dart @@ -0,0 +1,26 @@ +import 'package:teso/Classes/API%20Clasess/TesoUserDetail.dart'; +import 'package:teso/Classes/API%20Clasess/UserAuth.dart'; + +class Registrar { + UserAuth authentication; + TesoUserDetail user; + String referral; + Registrar({ + this.authentication, + this.user, + this.referral, + }); + + Map toJson() { + final Map data = Map(); + data['user'] = this.user; + data['authentication'] = this.authentication; + data['referral'] = this.referral; + return data; + } + + @override + String toString() { + return '{"user": "$user", "authentication": "$authentication"}'; + } +} diff --git a/lib/Classes/API Clasess/ResetClass.dart b/lib/Classes/API Clasess/ResetClass.dart new file mode 100644 index 0000000..bafec46 --- /dev/null +++ b/lib/Classes/API Clasess/ResetClass.dart @@ -0,0 +1,30 @@ +class ResetClass { + String password; + String resetGuid; + String resetcode; + + ResetClass({ + this.password, + this.resetGuid, + this.resetcode, + }); + factory ResetClass.fromJSON(Map json) { + return ResetClass( + password: json["password"], + resetGuid: json["resetGuid"], + resetcode: json["resetcode"]); + } + + Map toJson() { + final Map data = Map(); + data['password'] = this.password; + data['resetGuid'] = this.resetGuid; + data['resetcode'] = this.resetcode; + return data; + } + + @override + String toString() { + return '{"password": "$password", "resetGuid": "$resetGuid","resetcode":$resetcode}'; + } +} diff --git a/lib/Classes/API Clasess/SilverPurchaseRequest.dart b/lib/Classes/API Clasess/SilverPurchaseRequest.dart new file mode 100644 index 0000000..4bac07c --- /dev/null +++ b/lib/Classes/API Clasess/SilverPurchaseRequest.dart @@ -0,0 +1,36 @@ +class SilverPurchaseRequest { + int coinamount; + String method; + double amount; + + SilverPurchaseRequest({ + this.coinamount, + this.method, + this.amount, + }); + + factory SilverPurchaseRequest.fromJSON(Map json) { + return SilverPurchaseRequest( + coinamount: json["coinamount"], + method: json["method"], + amount: json["amount"], + ); + } + + Map toJson() { + final Map data = Map(); + try { + data['coinamount'] = this.coinamount; + data['method'] = this.method; + data['amount'] = this.amount; + } catch (e) { + print(e); + } + return data; + } + + @override + String toString() { + return '{"coinamount": "$coinamount", "costamount": "$amount", "method": "$method"}'; + } +} diff --git a/lib/Classes/API Clasess/TesoBusinessDetail.dart b/lib/Classes/API Clasess/TesoBusinessDetail.dart new file mode 100644 index 0000000..d8d016d --- /dev/null +++ b/lib/Classes/API Clasess/TesoBusinessDetail.dart @@ -0,0 +1,70 @@ +class TesoBusinessDetail { + String businessId; + String handle; + String businessName; + String businessTin; + String businessDescription; + String businessCategory; + String businessAddress; + String businessContact; + String businessLogo; + DateTime dateOfEst; + String businessEmail; + String businessLat; + String businessLng; + + TesoBusinessDetail( + {this.businessId, + this.businessAddress, + this.businessCategory, + this.businessContact, + this.businessDescription, + this.businessEmail, + this.businessLat, + this.businessLng, + this.businessLogo, + this.businessName, + this.businessTin, + this.dateOfEst, + this.handle}); + + TesoBusinessDetail.fromJSON(Map json) + : businessId = json['businessId'], + businessAddress = json['businessAddress'], + businessCategory = json['businessCategory'], + businessContact = json['businessContact'], + businessDescription = json['businessDescription'], + businessEmail = json['businessEmail'], + businessLat = json['businessLat'], + businessLng = json['businessLng'], + businessLogo = json['businessLogo'], + businessName = json['businessName'], + businessTin = json['businessTin'], + dateOfEst = DateTime.tryParse((json['dateOfEst']).toString()), + handle = json['handle']; + + Map toJson() { + final Map data = Map(); + data['businessId'] = this.businessId; + data['businessAddress'] = this.businessAddress; + data['businessCategory'] = this.businessCategory; + data['businessContact'] = this.businessContact; + data['businessDescription'] = this.businessDescription; + data['businessEmail'] = this.businessEmail; + data['businessLat'] = this.businessLat; + data['businessLng'] = this.businessLng; + data['businessLogo'] = this.businessLogo; + data['businessName'] = this.businessName; + data['businessTin'] = this.businessTin; + data['dateOfEst'] = this.dateOfEst.toIso8601String(); + data['handle'] = this.handle; + return data; + } + + @override + String toString() { + return '{"businessId": "$businessId","businessAddress": "$businessAddress", "businessCategory": "$businessCategory","businessContact": "$businessContact",' + + '"businessDescription": "$businessDescription", "businessEmail": "$businessEmail", "businessLat": "$businessLat","businessLng": "$businessLng","businessLogo":' + + '"$businessLogo","businessName": "$businessName","businessTin": "$businessTin","dateOfEst": "$dateOfEst","handle": "$handle"}'; + } +} diff --git a/lib/Classes/API Clasess/TesoUserDetail.dart b/lib/Classes/API Clasess/TesoUserDetail.dart new file mode 100644 index 0000000..bb98e11 --- /dev/null +++ b/lib/Classes/API Clasess/TesoUserDetail.dart @@ -0,0 +1,81 @@ +import 'package:teso/Classes/TesoUser.dart'; + +class TesoUserDetail { + String userGUID; + String username; + String firstname; + String surname; + String description; + String address; + String email; + String thumbnailDp; + String phonenumber; + String country; + String gender; + DateTime dateOfBirth; + + TesoUserDetail( + {this.username, + this.userGUID, + this.firstname, + this.surname, + this.address, + this.country, + this.description, + this.dateOfBirth, + this.email, + this.gender, + this.phonenumber, + this.thumbnailDp}); + + TesoUserDetail.fromJSON(Map json) + : username = json['username'], + userGUID = json['userGUID'], + firstname = json['firstname'], + surname = json['surname'], + address = json['address'], + country = json['country'], + dateOfBirth = DateTime.tryParse(json["dateOfBirth"].toString()), + description = json['description'], + email = json['email'], + gender = json['gender'], + phonenumber = json['phonenumber'], + thumbnailDp = json['thumbnailDp']; + + TesoUserDetail.fromUSER(TesoUser user) + : userGUID = user.userGUID, + username = user.username, + firstname = user.firstname, + surname = user.lastname, + address = user.address, + country = user.country, + dateOfBirth = user.dateOfBirth, + description = user.description, + email = user.email, + gender = user.gender, + phonenumber = user.phonenumber, + thumbnailDp = user.thumbnail_dp; + + Map toJson() { + final Map data = Map(); + data['username'] = this.username; + data['userGUID'] = this.userGUID; + data['firstname'] = this.firstname; + data['surname'] = this.surname; + data['description'] = this.description; + data['address'] = this.address; + data['phonenumber'] = this.phonenumber; + data['dateOfBirth'] = this.dateOfBirth.toIso8601String(); + data['thumbnailDp'] = this.thumbnailDp; + data['email'] = this.email; + data['gender'] = this.gender; + data['country'] = this.country; + return data; + } + + @override + String toString() { + return '{"userGUID": "$userGUID","firstname": "$firstname", "username": "$username","surname": "$surname", "description": "$description", "address": "$address", "phonenumber": "$phonenumber",' + + '"thumbnailDp": "$thumbnailDp","email": "$email","gender": "$gender","country": "$country","dateOfBirth":"$dateOfBirth"}'; + } +} diff --git a/lib/Classes/API Clasess/ThirdPerson.dart b/lib/Classes/API Clasess/ThirdPerson.dart new file mode 100644 index 0000000..ebe56b3 --- /dev/null +++ b/lib/Classes/API Clasess/ThirdPerson.dart @@ -0,0 +1,45 @@ +import 'package:teso/Classes/TesoUser.dart'; +import 'Post.dart'; + +class ThirdPerson { + TesoUser user; + List posts = []; + List following = []; + List friends = []; + String relation; + + ThirdPerson({ + this.user, + this.posts, + this.following, + this.friends, + this.relation, + }); + + factory ThirdPerson.fromJSON(Map json) { + var posts = json["posts"] as List; + List adverts = posts.map((e) => Post.fromJSON(e)).toList(); + return ThirdPerson( + user: json["user"] != null ? TesoUser.fromJSON(json["user"]) : null, + posts: adverts != null ? adverts : [], + following: json["following"] as List, + friends: json["friends"] as List, + relation: json["relation"], + ); + } + + Map toJson() { + final Map data = Map(); + data['posts'] = this.posts; + data['user'] = this.user; + data['following'] = this.following; + data['friends'] = this.friends; + data['relation'] = this.relation; + return data; + } + + @override + String toString() { + return '{"posts": "$posts", "user": "$user","relation":"$relation","friends":"$friends","following":"$following"}'; + } +} diff --git a/lib/Classes/API Clasess/TokenHandler.dart b/lib/Classes/API Clasess/TokenHandler.dart new file mode 100644 index 0000000..1e511b8 --- /dev/null +++ b/lib/Classes/API Clasess/TokenHandler.dart @@ -0,0 +1,30 @@ +import 'package:teso/Classes/TesoUser.dart'; + +class TokenHandler { + String tokenTeso; + String tokenFirebase; + TesoUser user; + + TokenHandler({this.tokenTeso, this.user, this.tokenFirebase}); + + factory TokenHandler.fromJSON(Map json) { + return TokenHandler( + tokenTeso: json['tokenTeso'], + tokenFirebase: json['tokenFirebase'] != null ? json['tokenFirebase'] : "", + user: TesoUser.fromJSON(json['user']), + ); + } + + Map toJson() { + final Map data = Map(); + data['tokenTeso'] = this.tokenTeso; + data['tokenFirebase'] = this.tokenFirebase; + data['user'] = this.user; + return data; + } + + @override + String toString() { + return '{"user": "$user", "tokenTeso": "$tokenTeso", "tokenFirebase": "$tokenFirebase"}'; + } +} diff --git a/lib/Classes/API Clasess/TwitterUser.dart b/lib/Classes/API Clasess/TwitterUser.dart new file mode 100644 index 0000000..9badd07 --- /dev/null +++ b/lib/Classes/API Clasess/TwitterUser.dart @@ -0,0 +1,53 @@ +class TwitterUser { + String firstname; + String surname; + String email; + String gender; + String username; + String userGUID; + String country; + String pictureUri; + String deviceToken; + String referralCode; + + TwitterUser( + {this.firstname, + this.surname, + this.email, + this.gender, + this.username, + this.userGUID, + this.country, + this.pictureUri, + this.referralCode, + this.deviceToken}); + + factory TwitterUser.fromJSON(Map json) { + return TwitterUser( + firstname: json['firstname'], + surname: json['surname'], + email: json['email'], + gender: json['gender'], + userGUID: json['userGUID'], + username: json['username'], + country: json['country'], + pictureUri: json['pictureuri'], + referralCode: json['referral'], + deviceToken: json['devicetoken']); + } + + Map toJson() { + final Map data = Map(); + data['firstname'] = this.firstname; + data['surname'] = this.surname; + data['email'] = this.email; + data['gender'] = this.gender; + data['userGUID'] = this.userGUID; + data['username'] = this.username; + data['country'] = this.country; + data['pictureuri'] = this.pictureUri; + data['devicetoken'] = this.deviceToken; + data['referral'] = this.referralCode; + return data; + } +} diff --git a/lib/Classes/API Clasess/UserAuth.dart b/lib/Classes/API Clasess/UserAuth.dart new file mode 100644 index 0000000..d935568 --- /dev/null +++ b/lib/Classes/API Clasess/UserAuth.dart @@ -0,0 +1,39 @@ +class UserAuth { + String username; + String password; + String accountType; + String status; + String deviceToken; + String referralCode; + UserAuth({ + this.username, + this.password, + this.accountType, + this.status, + this.deviceToken, + this.referralCode, + }); + UserAuth.fromJSON(Map json) + : username = json['username'], + password = json['password'], + accountType = json['accountType'], + status = json['status'], + referralCode = json['referral'], + deviceToken = json['deviceToken']; + + Map toJson() { + final Map data = Map(); + data['username'] = this.username; + data['password'] = this.password; + data['accountType'] = this.accountType; + data['status'] = this.status; + data['deviceToken'] = this.deviceToken; + data['referral'] = this.referralCode; + return data; + } + + @override + String toString() { + return '{"username": "$username", "password": "$password","accountType": "$accountType", "status": "$status","deviceToken": "$deviceToken"}'; + } +} diff --git a/lib/Classes/API Clasess/UserFavCategory.dart b/lib/Classes/API Clasess/UserFavCategory.dart new file mode 100644 index 0000000..6a2a014 --- /dev/null +++ b/lib/Classes/API Clasess/UserFavCategory.dart @@ -0,0 +1,28 @@ +class UserFavCategory { + String userGuid; + String categoryCode; + String countID; + + UserFavCategory({ + this.userGuid, + this.categoryCode, + this.countID, + }); + UserFavCategory.fromJSON(Map json) + : userGuid = json['userguid'], + categoryCode = json['catcode'], + countID = json['countid']; + + Map toJson() { + final Map data = Map(); + data['userguid'] = this.userGuid; + data['catcode'] = this.categoryCode; + data['countid'] = this.countID; + return data; + } + + @override + String toString() { + return '{"userguid": "$userGuid", "catcode": "$categoryCode","countid": "$countID"}'; + } +} diff --git a/lib/Classes/API Clasess/UserFinance.dart b/lib/Classes/API Clasess/UserFinance.dart new file mode 100644 index 0000000..2feadd9 --- /dev/null +++ b/lib/Classes/API Clasess/UserFinance.dart @@ -0,0 +1,23 @@ +class UserFinance { + String userGUID; + int gold; + int silver; + + UserFinance({ + this.userGUID, + this.gold, + this.silver, + }); + factory UserFinance.fromJSON(Map json) { + return UserFinance( + userGUID: json["userGuid"], + gold: json["gold"], + silver: json["silver"], + ); + } + + @override + String toString() { + return '{"userGuid": "$userGUID", "gold": "$gold", "silver": "$silver"}'; + } +} diff --git a/lib/Classes/ChatMessage.dart b/lib/Classes/ChatMessage.dart new file mode 100644 index 0000000..9efe4fb --- /dev/null +++ b/lib/Classes/ChatMessage.dart @@ -0,0 +1,22 @@ +class ChatMessage { + String idFrom; + String idTo; + String content; + int type; + DateTime timestamp; + + ChatMessage({ + this.idFrom, + this.idTo, + this.content, + this.type, + this.timestamp, + }); + + ChatMessage.fromJson(Map json) + : idFrom = json['idFrom'], + idTo = json['idTo'], + content = json['content'], + type = json['type'], + timestamp = json['timestamp']; +} diff --git a/lib/Classes/ColorFilters.dart b/lib/Classes/ColorFilters.dart new file mode 100644 index 0000000..776a3f8 --- /dev/null +++ b/lib/Classes/ColorFilters.dart @@ -0,0 +1,10 @@ +import 'package:flutter/painting.dart'; + +class ColorFilter { + Color code; + String name; + ColorFilter({ + this.code, + this.name, + }); +} diff --git a/lib/Classes/Connection.dart b/lib/Classes/Connection.dart new file mode 100644 index 0000000..44dc966 --- /dev/null +++ b/lib/Classes/Connection.dart @@ -0,0 +1,51 @@ +import 'dart:async'; +import 'package:connectivity/connectivity.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; + +class MyConnectivity { + MyConnectivity._internal(); + + static final MyConnectivity _instance = MyConnectivity._internal(); + + static MyConnectivity get instance => _instance; + + Connectivity connectivity = Connectivity(); + + StreamController controller = StreamController.broadcast(); + + Stream get myStream => controller.stream; + + void initialise() async { + ConnectivityResult result = await connectivity.checkConnectivity(); + _checkStatus(result); + connectivity.onConnectivityChanged.listen((result) { + _checkStatus(result); + }); + } + + void _checkStatus(ConnectivityResult result) async { + bool isOnline = false; + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + // 'Content-type': 'application/json', + 'Authorization': prefs.getString("tokensTeso") + }; + var register2 = serverLocation + 'serverstatus'; + var client1 = await http + .get(Uri.parse(register2), headers: requestHeaders) + .timeout(Duration(seconds: 10)); + if (client1.statusCode == 200) { + await prefs.setString("tokensTeso", "Bearer " + client1.body); + isOnline = true; + } + } catch (e) { + isOnline = false; + } + controller.sink.add({result: isOnline}); + } + + void disposeStream() => controller.close(); +} diff --git a/lib/Classes/CouponRateCalculator.dart b/lib/Classes/CouponRateCalculator.dart new file mode 100644 index 0000000..fce9f2e --- /dev/null +++ b/lib/Classes/CouponRateCalculator.dart @@ -0,0 +1,32 @@ +class CouponRateCalculator { + static int getRate(double discount) { + if (discount < 100) { + String discounted = discount.toStringAsFixed(1); + String first = discounted.substring(0, discounted.indexOf(".")); + String last = discounted.substring(discounted.indexOf(".") + 1); + + int firstNumber = int.parse(first); + int lastNumber = int.parse(last); + if (discount < 0.51) { + return 0; + } else if (discount < 2) { + return 1; + } else if (firstNumber < lastNumber) { + return lastNumber; + } else if (firstNumber > lastNumber) { + return firstNumber; + } else { + return firstNumber; + } + } else if (discount < 1000) { + double result = discount % 100; + if (result == 0) { + return 100; + } else { + return result.ceil(); + } + } else { + return 100; + } + } +} diff --git a/lib/Classes/CustomCacheManager.dart b/lib/Classes/CustomCacheManager.dart new file mode 100644 index 0000000..5116aab --- /dev/null +++ b/lib/Classes/CustomCacheManager.dart @@ -0,0 +1,14 @@ +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; + +class CustomCacheManager { + static const key = 'customCacheKey'; + static CacheManager instance = CacheManager( + Config( + key, + stalePeriod: const Duration(hours: 1), + maxNrOfCacheObjects: 20, + repo: JsonCacheInfoRepository(databaseName: key), + fileService: HttpFileService(), + ), + ); +} diff --git a/lib/Classes/Firebase/Comments.dart b/lib/Classes/Firebase/Comments.dart new file mode 100644 index 0000000..8a81d80 --- /dev/null +++ b/lib/Classes/Firebase/Comments.dart @@ -0,0 +1,29 @@ +class FBComments { + String postID; + DateTime timestamp; + String thumbnail; + String commenterID; + String commenter; + String commentID; + String comment; + + FBComments( + {this.postID, + this.comment, + this.commentID, + this.commenter, + this.commenterID, + this.thumbnail, + this.timestamp}); + factory FBComments.fromJSON(Map json) { + return FBComments( + comment: json["comment"], + commentID: json["commentID"], + commenter: json["commenter"], + commenterID: json["commenterID"], + thumbnail: json["thumbnail"], + postID: json["post"], + timestamp: DateTime.fromMillisecondsSinceEpoch(json["timestamp"]), + ); + } +} diff --git a/lib/Classes/Firebase/Posts.dart b/lib/Classes/Firebase/Posts.dart new file mode 100644 index 0000000..6643953 --- /dev/null +++ b/lib/Classes/Firebase/Posts.dart @@ -0,0 +1,40 @@ +class FBPosts { + String postID; + DateTime timestamp; + String playbackID; + String publisherID; + String title; + String assetID; + String rendition; + String aspect; + String campaignID; + int likes; + int comments; + + FBPosts( + {this.postID, + this.playbackID, + this.publisherID, + this.title, + this.timestamp, + this.assetID, + this.rendition, + this.aspect, + this.campaignID, + this.comments, + this.likes}); + factory FBPosts.fromJSON(Map json) { + return FBPosts( + publisherID: json["publisher"], + postID: json["postId"], + title: json["title"], + playbackID: json["playbackID"], + assetID: json["assetID"], + rendition: json["rendition"], + aspect: json["aspect"], + likes: json["likes"], + campaignID: json["campaignId"], + comments: json["comments"], + timestamp: DateTime.fromMillisecondsSinceEpoch(json["timestamp"])); + } +} diff --git a/lib/Classes/NotificationSpliter.dart b/lib/Classes/NotificationSpliter.dart new file mode 100644 index 0000000..49d6e9d --- /dev/null +++ b/lib/Classes/NotificationSpliter.dart @@ -0,0 +1,103 @@ +import 'dart:convert'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/Payload.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class NotificationSplitter { + static Future getPayload(data, context) async { + Payload payload = new Payload(); + switch (data["notificationType"]) { + case "chats": + payload.loadID = "TESN004"; + payload.load1 = data["senderID"]; + payload.load2 = data["firstname"]; + payload.load3 = data["surname"]; + return payload; + break; + case "likes": + Provider.of(context, listen: false).pullAds(); + payload.loadID = "TESN000"; + payload.load1 = data["post"]; + return payload; + break; + case "comments": + Provider.of(context, listen: false).pullAds(); + payload.loadID = "TESN000"; + payload.load1 = data["post"]; + return payload; + break; + case "friendapproval": + Provider.of(context, listen: false).loadFriends(); + payload.loadID = "TESN000"; + return payload; + break; + case "friendrequest": + payload.loadID = "TESN000"; + return payload; + break; + case "gifted": + Provider.of(context, listen: false).getCoupons(); + payload.loadID = "TESN000"; + payload.load1 = data["couponID"]; + return payload; + break; + case "cancellation": + Provider.of(context, listen: false).getCoupons(); + payload.loadID = "TESN000"; + payload.load1 = data["couponID"]; + return payload; + break; + case "personalized": + payload.loadID = "TESN000"; + payload.load1 = data["coupon"]; + payload.load2 = data["notificationType"]; + payload.load3 = data["documentRef"]; + payload.load4 = data["couponCondition"]; + payload.load5 = data["businessName"]; + payload.load6 = data["originalPrice"]; + payload.load7 = data["productName"]; + return payload; + break; + case "campaign": + var coup = jsonDecode(data["campaign"]); + payload.loadID = "TESN005"; + payload.load1 = coup["businessID"]; + payload.load2 = coup["campaignID"]; + payload.load3 = coup["campaignDescription"]; + payload.load4 = coup["status"]; + payload.load5 = coup["targetProduct"]; + payload.load6 = coup["title"]; + payload.load7 = coup["goldReward"].toString(); + payload.load8 = coup["startDate"].toString(); + return payload; + break; + case "reward": + payload.loadID = "TESN0R0"; + return payload; + break; + case "expired": + Provider.of(context, listen: false).getCoupons(); + payload.loadID = "TESN000"; + payload.load1 = data["couponID"]; + return payload; + break; + case "refund": + Provider.of(context, listen: false).getUserInformation(); + payload.loadID = "TESN000"; + payload.load1 = data["couponID"]; + return payload; + break; + case "coinpurchase": + Provider.of(context, listen: false).getUserInformation(); + payload.loadID = "TESN000"; + payload.load1 = data["orderID"]; + await closeWebView(); + return payload; + break; + default: + return payload; + break; + } + } +} diff --git a/lib/Classes/Payload.dart b/lib/Classes/Payload.dart new file mode 100644 index 0000000..e3e2b60 --- /dev/null +++ b/lib/Classes/Payload.dart @@ -0,0 +1,63 @@ +class Payload { + String loadID; + String load1; + String load2; + String load3; + String load4; + String load5; + String load6; + String load7; + String load8; + String load9; + String load10; + + Payload({ + this.load1, + this.load2, + this.load3, + this.load4, + this.load5, + this.load6, + this.load7, + this.load8, + this.load9, + this.load10, + this.loadID, + }); + + factory Payload.fromJSON(Map json) { + return Payload( + load1: json['load1'].toString(), + load2: json['load2'].toString(), + load3: json['load3'].toString(), + load4: json['load4'].toString(), + load5: json['load5'].toString(), + load6: json['load6'].toString(), + load7: json['load7'].toString(), + load8: json['load8'].toString(), + load9: json['load9'].toString(), + load10: json['load10'].toString(), + loadID: json['loadID'], + ); + } + Map toJson() { + final Map data = Map(); + data['load1'] = this.load1; + data['load2'] = this.load2; + data['load3'] = this.load3; + data['load4'] = this.load4; + data['load5'] = this.load5; + data['load6'] = this.load6; + data['load7'] = this.load7; + data['load8'] = this.load8; + data['load9'] = this.load9; + data['load10'] = this.load10; + data['loadID'] = this.loadID; + return data; + } + + @override + String toString() { + return '{"load1": "$load1","load2": "$load2","load3": "$load3","load4": "$load4","load5": "$load5","load6": "$load6","load7": "$load7","load8": "$load8","load9": "$load9","load10": "$load10","loadID": "$loadID"}'; + } +} diff --git a/lib/Classes/Product.dart b/lib/Classes/Product.dart new file mode 100644 index 0000000..4163880 --- /dev/null +++ b/lib/Classes/Product.dart @@ -0,0 +1,47 @@ +class Product { + String itemID; + String name; + String shopName; + String shopLocation; + String country; + String duration; + String adShort; + String adFull; + + Product( + {this.itemID, + this.name, + this.shopName, + this.shopLocation, + this.country, + this.duration, + this.adShort, + this.adFull}); + Product.fromJSON(Map json) + : itemID = json['productID'], + name = json['name'], + shopName = json['shopName'], + shopLocation = json['shopLocation'], + duration = json['duration'], + adShort = json['adShort'], + adFull = json['adFull'], + country = json['country']; + + Map toJson() { + final Map data = Map(); + data['itemID'] = this.itemID; + data['name'] = this.name; + data['shopName'] = this.shopName; + data['shopLocation'] = this.shopLocation; + data['country'] = this.country; + data['duration'] = this.duration; + data['adShort'] = this.adShort; + data['adFull'] = this.adFull; + return data; + } + + @override + String toString() { + return '{"itemID":"$itemID","name": "$name", "shopName": "$shopName","shopLocation": "$shopLocation","country":"$country","duration":"$duration","adFull":"$adFull","adShort":"$adShort"}'; + } +} diff --git a/lib/Classes/QRClass.dart b/lib/Classes/QRClass.dart new file mode 100644 index 0000000..d2ab12c --- /dev/null +++ b/lib/Classes/QRClass.dart @@ -0,0 +1,15 @@ +class QRClass { + String code; + String type; + List rawBytes; + + QRClass({ + this.code, + this.type, + this.rawBytes, + }); + QRClass.fromJson(Map json) + : code = json['code'], + type = json['type'], + rawBytes = json['rawBytes']; +} diff --git a/lib/Classes/ReportedContent.dart b/lib/Classes/ReportedContent.dart new file mode 100644 index 0000000..4ba572c --- /dev/null +++ b/lib/Classes/ReportedContent.dart @@ -0,0 +1,37 @@ +import 'dart:core'; + +class ReportedContent { + String userGuid; + String postID; + String publisherID; + DateTime timestamp; + int report; + + ReportedContent( + {this.postID, + this.publisherID, + this.report, + this.timestamp, + this.userGuid}); + ReportedContent.fromJSON(Map json) + : postID = json["postID"], + publisherID = json['publisherID'], + report = int.parse(json['report']), + userGuid = json['userGuid'], + timestamp = DateTime.parse(json['timestamp']); + + Map toJson() { + final Map data = Map(); + data['postID'] = this.postID; + data['publisherID'] = this.publisherID; + data['report'] = this.report; + data['userGuid'] = this.userGuid; + data['timestamp'] = this.timestamp; + return data; + } + + @override + String toString() { + return '{"postID":"$postID","publisherID": "$publisherID", "report": "$report","userGuid": "$userGuid","timestamp":"$timestamp"}'; + } +} diff --git a/lib/Classes/Router.dart b/lib/Classes/Router.dart new file mode 100644 index 0000000..c52e7a1 --- /dev/null +++ b/lib/Classes/Router.dart @@ -0,0 +1,3 @@ +class LinkRouter { + static getPayload(data, context) async {} +} diff --git a/lib/Classes/ScalePosition.dart b/lib/Classes/ScalePosition.dart new file mode 100644 index 0000000..51c8210 --- /dev/null +++ b/lib/Classes/ScalePosition.dart @@ -0,0 +1,27 @@ +class ScaledPosition { + static int getWidth( + double screenwidth, double videowidth, double screenposition) { + int scaledWidth; + if (screenwidth < videowidth) { + scaledWidth = ((screenwidth / videowidth) * screenposition).round(); + } else if (screenwidth == videowidth) { + scaledWidth = screenposition.round(); + } else { + scaledWidth = ((videowidth / screenwidth) * screenposition).round(); + } + return scaledWidth; + } + + static int getHeight( + double screenheight, double videoheight, double screenposition) { + int scaledHeight; + if (screenheight < videoheight) { + scaledHeight = ((screenheight / videoheight) * screenposition).round(); + } else if (screenheight == videoheight) { + scaledHeight = screenposition.round(); + } else { + scaledHeight = ((videoheight / screenheight) * screenposition).round(); + } + return scaledHeight; + } +} diff --git a/lib/Classes/TesoShop.dart b/lib/Classes/TesoShop.dart new file mode 100644 index 0000000..0135843 --- /dev/null +++ b/lib/Classes/TesoShop.dart @@ -0,0 +1,70 @@ +class TesoShop { + String shopID; + String shopName; + String shopTin; + String shopAddress; + double latitude; + double longitude; + String shopPhone; + DateTime dateEst; + String handle; + String categoryShop; + String shopDescription; + String logo; + String email; + + TesoShop( + {this.shopID, + this.logo, + this.shopName, + this.shopAddress, + this.latitude, + this.longitude, + this.shopPhone, + this.dateEst, + this.handle, + this.categoryShop, + this.shopDescription, + this.email, + this.shopTin}); + + TesoShop.fromJSON(Map json) + : shopID = json["businessId"].toString(), + shopName = json["businessName"].toString(), + shopAddress = json["businessAddress"].toString(), + latitude = double.tryParse((json["businessLat"]).toString()), + longitude = double.tryParse((json["businessLng"]).toString()), + shopPhone = json["businessContact"].toString(), + dateEst = DateTime.tryParse((json['dateOfEst']).toString()), + handle = json['handle'].toString(), + categoryShop = json['businessCategory'].toString(), + shopDescription = json['businessDescription'].toString(), + logo = json['businessLogo'].toString(), + email = json['businessEmail'].toString(), + shopTin = json["businessTin"].toString(); + + Map toJson() { + final Map data = Map(); + data['businessId'] = this.shopID; + data['businessName'] = this.shopName; + data['businessLng'] = this.longitude; + data['businessLat'] = this.latitude; + data['businessAddress'] = this.shopAddress; + data['businessContact'] = this.shopPhone; + data['businessEmail'] = this.email; + data['dateOfEst'] = this.dateEst; + data['businessCategory'] = this.categoryShop; + data['businessTin'] = this.shopTin; + data['businessDescription'] = this.shopDescription; + data['businessLogo'] = this.logo; + data['handle'] = this.handle; + return data; + } + + @override + String toString() { + return '{"businessId":"$shopID","businessName": "$shopName", "businessLat": "$latitude","businessLng":"$longitude","businessAddress": "$shopAddress",' + + '"businessContact":"$shopPhone","handle":"$handle","dateOfEst":"$dateEst","businessTin":"$shopTin","businessCategory:"$categoryShop",' + + '"businessDescription":"$shopDescription","businessLogo":"$logo","businessEmail":"$email"}'; + } +} diff --git a/lib/Classes/TesoUser.dart b/lib/Classes/TesoUser.dart new file mode 100644 index 0000000..30ce1c4 --- /dev/null +++ b/lib/Classes/TesoUser.dart @@ -0,0 +1,101 @@ +import 'API Clasess/TesoUserDetail.dart'; + +class TesoUser { + String userGUID; + String username; + String firstname; + String lastname; + String description; + String email; + String phonenumber; + String address; + String displaypicture; + DateTime dateOfBirth; + // ignore: non_constant_identifier_names + String thumbnail_dp; + String gold; + String silver; + String friends; + String gender; + String country; + + TesoUser( + {this.userGUID, + this.username, + this.firstname, + this.lastname, + this.description, + this.address, + this.email, + this.dateOfBirth, + // ignore: non_constant_identifier_names + this.displaypicture, + this.phonenumber, + this.gold, + this.silver, + this.gender, + this.country, + // ignore: non_constant_identifier_names + this.thumbnail_dp, + this.friends}); + + TesoUser.fromJSON(Map json) + : username = json['username'], + userGUID = json['userGUID'], + firstname = json['firstname'], + lastname = json['lastname'], + description = json['description'], + dateOfBirth = json["dateOfBirth"] != null + ? DateTime.tryParse(json["dateOfBirth"].toString()) + : null, + address = json['address'], + phonenumber = json['phonenumber'], + email = json['email'], + gold = json['gold'], + silver = json['silver'], + gender = json['gender'], + country = json['country'], + friends = json['friends'], + thumbnail_dp = json['thumbnail_dp']; + + Map toJson() { + final Map data = Map(); + data['username'] = this.username; + data['userguid'] = this.userGUID; + data['firstname'] = this.firstname; + data['lastname'] = this.lastname; + data['description'] = this.description; + data['address'] = this.address; + data["dateOfBirth"] = this.dateOfBirth.toIso8601String(); + data['phonenumber'] = this.phonenumber; + data['thumbnail_dp'] = this.thumbnail_dp; + data['email'] = this.email; + data['gold'] = this.gold; + data['silver'] = this.silver; + data['gender'] = this.gender; + data['country'] = this.country; + data['friends'] = this.friends; + data['displaypicture'] = this.displaypicture; + return data; + } + + @override + String toString() { + return '{"userGUID": "$userGUID","username": "$username", "firstname": "$firstname","lastname": "$lastname", "description": "$description","address": "$address","phonenumber": "$phonenumber",' + + '"email": "$email", "gold": "$gold","silver": "$silver","friends":"$friends","gender":"$gender","country":"$country","thumbnail_dp":"$thumbnail_dp","dateOfBirth":"$dateOfBirth","displaypicture":"$displaypicture"}'; + } + + TesoUser.fromDETAIL(TesoUserDetail user) + : userGUID = user.userGUID, + username = user.username, + firstname = user.firstname, + lastname = user.surname, + address = user.address, + country = user.country, + description = user.description, + email = user.email, + gender = user.gender, + dateOfBirth = user.dateOfBirth, + phonenumber = user.phonenumber.toString(), + thumbnail_dp = user.thumbnailDp; +} diff --git a/lib/Classes/TextE.dart b/lib/Classes/TextE.dart new file mode 100644 index 0000000..9774546 --- /dev/null +++ b/lib/Classes/TextE.dart @@ -0,0 +1,14 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; + +class Textted { + String text; + TextStyle textStyle; + TextAlign textAlign; + + Textted({ + this.text, + this.textAlign, + this.textStyle, + }); +} diff --git a/lib/Classes/Uploading.dart b/lib/Classes/Uploading.dart new file mode 100644 index 0000000..929b88d --- /dev/null +++ b/lib/Classes/Uploading.dart @@ -0,0 +1,31 @@ +import 'package:flutter_upchunk/flutter_upchunk.dart'; + +class Uploading { + String id; + String title; + String path; + String aspect; + String thumbnail; + UpChunk token; + double pending; + String campaignID; + String muxuploadID; + String muxuploadURL; + String muxassetID; + bool isProcessing; + + Uploading({ + this.id, + this.title, + this.path, + this.aspect, + this.thumbnail, + this.token, + this.pending, + this.campaignID, + this.isProcessing, + this.muxuploadID, + this.muxuploadURL, + this.muxassetID, + }); +} diff --git a/lib/Classes/categories.dart b/lib/Classes/categories.dart new file mode 100644 index 0000000..cb2bf2e --- /dev/null +++ b/lib/Classes/categories.dart @@ -0,0 +1,61 @@ +class Category { + String id; + String name; + String image; + Category({this.id, this.name, this.image}); + Category.fromJson(Map json) + : id = json['catid'], + name = json['catname'], + image = json['catimage']; + + static List category = [ + Category( + id: "TECAT001", + name: "ANTIGUES", + image: "assets/images/categories/antique2.png"), + Category( + id: "TECAT002", + name: "AGRICULTURE", + image: "assets/images/categories/agrix.png"), + Category( + id: "TECAT003", + name: "AUTO & TRANSPORTATION", + image: "assets/images/categories/autos.png"), + Category( + id: "TECAT004", + name: "BAGS, SHOES & ACCESSORIES", + image: "assets/images/categories/bags.png"), + Category( + id: "TECAT005", + name: "CLOTHING & ACCESSORIES", + image: "assets/images/categories/clothes.png"), + Category( + id: "TECAT006", + name: "ELECTRONICS", + image: "assets/images/categories/electronics.png"), + Category( + id: "TECAT007", + name: "GIFTS, SPORTS & TOYS", + image: "assets/images/categories/gifts.png"), + Category( + id: "TECAT008", + name: "FOOD", + image: "assets/images/categories/food.png"), + Category( + id: "TECAT009", + name: "HOME & GARDEN", + image: "assets/images/categories/home.png"), + Category( + id: "TECAT010", + name: "PET SUPPLIES", + image: "assets/images/categories/pets.png"), + Category( + id: "TECAT011", + name: "MACHINERY, INDUSTRIAL PARTS & TOOLS", + image: "assets/images/categories/machinery.png"), + Category( + id: "TECAT012", + name: "HEALTH & BEAUTY", + image: "assets/images/categories/health.png"), + ]; +} diff --git a/lib/Classes/customLoginButton.dart b/lib/Classes/customLoginButton.dart new file mode 100644 index 0000000..d6078db --- /dev/null +++ b/lib/Classes/customLoginButton.dart @@ -0,0 +1,87 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class CustomLoginButton extends StatelessWidget { + final Widget child; + final double width; + final double height; + final Function onPressed; + final String icon; + final Color color; + + const CustomLoginButton({ + Key key, + @required this.child, + this.width = double.infinity, + this.height = 50.0, + this.onPressed, + this.icon, + this.color, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Container( + width: width, + height: 40.0, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + boxShadow: [ + BoxShadow( + color: Colors.grey, + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ], + ), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + ), + child: InkWell( + onTap: onPressed, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Image( + image: AssetImage(icon), + height: 20, + ), + child + ], + )), + ), + ), + ), + ); + } +} diff --git a/lib/Classes/customTesoButton.dart b/lib/Classes/customTesoButton.dart new file mode 100644 index 0000000..0fb61a6 --- /dev/null +++ b/lib/Classes/customTesoButton.dart @@ -0,0 +1,55 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class RaisedGradientButton extends StatelessWidget { + final Widget child; + final Gradient gradient; + final double width; + final double height; + final Function onPressed; + + const RaisedGradientButton({ + Key key, + @required this.child, + this.gradient, + this.width = double.infinity, + this.height = 50.0, + this.onPressed, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Container( + width: width, + height: 40.0, + decoration: BoxDecoration(gradient: gradient, boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: onPressed, + child: Center( + child: child, + )), + ), + ), + ); + } +} diff --git a/lib/Classes/friend.dart b/lib/Classes/friend.dart new file mode 100644 index 0000000..d88b00f --- /dev/null +++ b/lib/Classes/friend.dart @@ -0,0 +1,33 @@ +class Friend { + String userID; + String firstname; + String surname; + String dateOfBirth; + String thumbnail; + String description; + String posts; + String following; + String friendsNum; + + Friend( + {this.userID, + this.firstname, + this.surname, + this.dateOfBirth, + this.thumbnail, + this.description, + this.following, + this.friendsNum, + this.posts}); + + Friend.fromJson(Map json) + : userID = json['userID'], + firstname = json['firstname'], + surname = json['surname'], + thumbnail = json['thumbnail'], + description = json['description'], + friendsNum = json['friends'], + following = json['following'], + posts = json['posts'], + dateOfBirth = json['dob']; +} diff --git a/lib/Classes/inbox.dart b/lib/Classes/inbox.dart new file mode 100644 index 0000000..ac54a87 --- /dev/null +++ b/lib/Classes/inbox.dart @@ -0,0 +1,33 @@ +class InboxMessage { + String userID; + String firstname; + String surname; + String messageID; + String message; + String thumbnail; + String bio; + DateTime timestamp; + int unread; + + InboxMessage( + {this.userID, + this.firstname, + this.surname, + this.messageID, + this.message, + this.thumbnail, + this.timestamp, + this.bio, + this.unread}); + + InboxMessage.fromJson(Map json) + : userID = json['userID'], + firstname = json['firstname'], + surname = json['surname'], + thumbnail = json['thumbnail'], + messageID = json['messageID'], + message = json['message'], + timestamp = json['timestamp'], + bio = json['bio'], + unread = json['unread']; +} diff --git a/lib/GeneralWidgets/generalInput.dart b/lib/GeneralWidgets/generalInput.dart new file mode 100644 index 0000000..25f1e53 --- /dev/null +++ b/lib/GeneralWidgets/generalInput.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +fineText(BuildContext context, TextEditingController user, String hint) { + return Container( + width: MediaQuery.of(context).size.width * 0.45, + height: 40, + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.02), + child: TextField( + textAlign: TextAlign.left, + controller: user, + style: TextStyle( + color: Colors.black, + ), + decoration: InputDecoration( + contentPadding: EdgeInsets.only(top: 5, left: 10), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 1.0), + ), + hintText: hint, + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ); +} diff --git a/lib/GeneralWidgets/inputText.dart b/lib/GeneralWidgets/inputText.dart new file mode 100644 index 0000000..9c6105f --- /dev/null +++ b/lib/GeneralWidgets/inputText.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +buildInputContainer(BuildContext context, TextEditingController user, + String title, bool enabled) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + color: Theme.of(context).primaryColorLight, + fontSize: 14.0, + ), + ), + // SizedBox( + // height: 10.0, + // ), + TextField( + enabled: enabled, + autocorrect: false, + textAlign: TextAlign.left, + controller: user, + style: TextStyle( + fontSize: 16, + color: Theme.of(context).primaryColorLight, + ), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.only(top: 10.0), + // hintText: "Enter your " + first.toLowerCase() + " here", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ], + ); +} diff --git a/lib/GeneralWidgets/inputTextLimited.dart b/lib/GeneralWidgets/inputTextLimited.dart new file mode 100644 index 0000000..709883e --- /dev/null +++ b/lib/GeneralWidgets/inputTextLimited.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +buildInputContainerLimit(BuildContext context, TextEditingController user, + String title, bool enabled, int limit) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + color: Theme.of(context).primaryColorLight, + fontSize: 14.0, + ), + ), + // SizedBox( + // height: 10.0, + // ), + TextField( + maxLengthEnforcement: MaxLengthEnforcement.enforced, + maxLength: limit, + enabled: enabled, + autocorrect: false, + textAlign: TextAlign.left, + controller: user, + style: TextStyle( + fontSize: 16, + color: Theme.of(context).primaryColorLight, + ), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.only(top: 10.0), + // hintText: "Enter your " + first.toLowerCase() + " here", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ], + ); +} diff --git a/lib/GeneralWidgets/widgets/uservideo_player_widget.dart b/lib/GeneralWidgets/widgets/uservideo_player_widget.dart new file mode 100644 index 0000000..2f75d59 --- /dev/null +++ b/lib/GeneralWidgets/widgets/uservideo_player_widget.dart @@ -0,0 +1,48 @@ +import 'package:better_player/better_player.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; + +const ASPECT_RATIO = 16 / 9; + +class VideoPlayerWidget extends StatefulWidget { + final BetterPlayerController controller; + final FBPosts ad; + + const VideoPlayerWidget({ + Key key, + @required this.controller, + @required this.ad, + }) : assert(controller != null), + assert(ad != null), + super(key: key); + + @override + _VideoPlayerWidgetState createState() => _VideoPlayerWidgetState(); +} + +class _VideoPlayerWidgetState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return FittedBox( + clipBehavior: Clip.hardEdge, + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: BetterPlayer( + controller: widget.controller, + ), + ), + ); + } + + @override + void dispose() { + widget.controller.dispose(); + super.dispose(); + } +} diff --git a/lib/GeneralWidgets/widgets/video_player_widget.dart b/lib/GeneralWidgets/widgets/video_player_widget.dart new file mode 100644 index 0000000..45d2640 --- /dev/null +++ b/lib/GeneralWidgets/widgets/video_player_widget.dart @@ -0,0 +1,104 @@ +import 'package:better_player/better_player.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Pages/Sub_Pages/ProductDetails/CouponList.dart'; +import 'package:teso/providers/user_provider.dart'; + +const ASPECT_RATIO = 16 / 9; + +class VideoPlayerWidget extends StatefulWidget { + final BetterPlayerController controller; + final Post ad; + final bool play; + final List details; + + const VideoPlayerWidget({ + Key key, + @required this.controller, + @required this.ad, + @required this.play, + this.details, + }) : assert(controller != null), + assert(ad != null), + super(key: key); + + @override + _VideoPlayerWidgetState createState() => _VideoPlayerWidgetState(); +} + +class _VideoPlayerWidgetState extends State { + bool displayed = false; + + @override + void initState() { + widget.controller.videoPlayerController.addListener(() => checkVideo()); + super.initState(); + + // if (widget.play) { + // _chewieController.play(); + // } + } + + checkVideo() async { + // Implement your calls inside these conditions' bodies : + if (widget.controller.videoPlayerController.value.position == + Duration(seconds: 0, minutes: 0, hours: 0)) { + // print('video Started'); + Provider.of(context, listen: false).viewPost(widget.ad); + } + + if (widget.controller.videoPlayerController.value.position.inSeconds > + (widget.controller.videoPlayerController.value.duration.inSeconds) / + 3) { + // print('video Ended'); + if (!displayed && widget.details.length > 0) { + setState(() { + displayed = true; + }); + await Navigator.of(context).push( + PageRouteBuilder( + opaque: false, + pageBuilder: (_, __, ___) => CouponList( + couponsList: widget.details, + ), + ), + ); + } + // _chewieController.play(); + } + } + + // @override + // void didUpdateWidget(VideoPlayerWidget oldWidget) { + // if (oldWidget.play != widget.play) { + // if (widget.play) { + // _chewieController.play(); + // } else { + // _chewieController.pause(); + // } + // } + // super.didUpdateWidget(oldWidget); + // } + + @override + Widget build(BuildContext context) { + return FittedBox( + clipBehavior: Clip.hardEdge, + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: BetterPlayer( + controller: widget.controller, + ), + ), + ); + } + + @override + void dispose() { + widget.controller.dispose(); + super.dispose(); + } +} diff --git a/lib/GeneralWidgets/widgets/widgets.dart b/lib/GeneralWidgets/widgets/widgets.dart new file mode 100644 index 0000000..7994939 --- /dev/null +++ b/lib/GeneralWidgets/widgets/widgets.dart @@ -0,0 +1 @@ +export 'video_player_widget.dart'; \ No newline at end of file diff --git a/lib/Notifications/NotificationPlugin.dart b/lib/Notifications/NotificationPlugin.dart new file mode 100644 index 0000000..4d6119d --- /dev/null +++ b/lib/Notifications/NotificationPlugin.dart @@ -0,0 +1,271 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:path_provider/path_provider.dart'; +import 'dart:io' show File, Platform; +import 'package:http/http.dart' as http; + +import 'package:rxdart/subjects.dart'; + +class NotificationPlugin { + // + FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; + final BehaviorSubject + didReceivedLocalNotificationSubject = + BehaviorSubject(); + var initializationSettings; + + NotificationPlugin._() { + init(); + } + + init() async { + flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); + if (Platform.isIOS) { + _requestIOSPermission(); + } + initializePlatformSpecifics(); + } + + initializePlatformSpecifics() { + var initializationSettingsAndroid = + AndroidInitializationSettings('app_notf_icon'); + + var initializationSettingsIOS = IOSInitializationSettings( + requestAlertPermission: true, + requestBadgePermission: true, + requestSoundPermission: false, + onDidReceiveLocalNotification: (id, title, body, payload) async { + ReceivedNotification receivedNotification = ReceivedNotification( + id: id, title: title, body: body, payload: payload); + didReceivedLocalNotificationSubject.add(receivedNotification); + }, + ); + + initializationSettings = InitializationSettings( + android: initializationSettingsAndroid, iOS: initializationSettingsIOS); + } + + _requestIOSPermission() { + flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + IOSFlutterLocalNotificationsPlugin>() + .requestPermissions( + alert: true, + badge: true, + sound: true, + ); + } + + setListenerForLowerVersions(Function onNotificationInLowerVersions) { + didReceivedLocalNotificationSubject.listen((receivedNotification) { + onNotificationInLowerVersions(receivedNotification); + }); + } + + setOnNotificationClick(Function onNotificationClick) async { + await flutterLocalNotificationsPlugin.initialize(initializationSettings, + onSelectNotification: (String payload) async { + onNotificationClick(payload); + }); + } + + Future showNotification( + String title, String body, String payload) async { + var androidChannelSpecifics = AndroidNotificationDetails( + 'CHANNEL_ID', + 'CHANNEL_NAME', + channelDescription: "CHANNEL_DESCRIPTION", + importance: Importance.max, + priority: Priority.high, + playSound: true, + timeoutAfter: 5000, + styleInformation: DefaultStyleInformation(true, true), + ); + var iosChannelSpecifics = IOSNotificationDetails(); + var platformChannelSpecifics = NotificationDetails( + android: androidChannelSpecifics, iOS: iosChannelSpecifics); + await flutterLocalNotificationsPlugin.show( + 0, + title, + body, //null + platformChannelSpecifics, + payload: payload, + ); + } + + Future showDailyAtTime() async { + var time = Time(21, 3, 0); + var androidChannelSpecifics = AndroidNotificationDetails( + 'CHANNEL_ID 4', + 'CHANNEL_NAME 4', + channelDescription: "CHANNEL_DESCRIPTION 4", + importance: Importance.max, + priority: Priority.high, + ); + var iosChannelSpecifics = IOSNotificationDetails(); + var platformChannelSpecifics = NotificationDetails( + android: androidChannelSpecifics, iOS: iosChannelSpecifics); + // ignore: deprecated_member_use + await flutterLocalNotificationsPlugin.showDailyAtTime( + 0, + 'Test Title at ${time.hour}:${time.minute}.${time.second}', + 'Test Body', //null + time, + platformChannelSpecifics, + payload: 'Test Payload', + ); + } + + Future showWeeklyAtDayTime() async { + var time = Time(21, 5, 0); + var androidChannelSpecifics = AndroidNotificationDetails( + 'CHANNEL_ID 5', + 'CHANNEL_NAME 5', + channelDescription: "CHANNEL_DESCRIPTION 5", + importance: Importance.max, + priority: Priority.high, + ); + var iosChannelSpecifics = IOSNotificationDetails(); + var platformChannelSpecifics = NotificationDetails( + android: androidChannelSpecifics, iOS: iosChannelSpecifics); + // ignore: deprecated_member_use + await flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime( + 0, + 'Test Title at ${time.hour}:${time.minute}.${time.second}', + 'Test Body', //null + Day.saturday, + time, + platformChannelSpecifics, + payload: 'Test Payload', + ); + } + + Future repeatNotification() async { + var androidChannelSpecifics = AndroidNotificationDetails( + 'CHANNEL_ID 3', + 'CHANNEL_NAME 3', + channelDescription: "CHANNEL_DESCRIPTION 3", + importance: Importance.max, + priority: Priority.high, + ); + var iosChannelSpecifics = IOSNotificationDetails(); + var platformChannelSpecifics = NotificationDetails( + android: androidChannelSpecifics, iOS: iosChannelSpecifics); + await flutterLocalNotificationsPlugin.periodicallyShow( + 0, + 'Repeating Test Title', + 'Repeating Test Body', + RepeatInterval.everyMinute, + platformChannelSpecifics, + payload: 'Test Payload', + ); + } + + Future scheduleNotification() async { + var scheduleNotificationDateTime = DateTime.now().add(Duration(seconds: 5)); + var androidChannelSpecifics = AndroidNotificationDetails( + 'CHANNEL_ID 1', + 'CHANNEL_NAME 1', + channelDescription: "CHANNEL_DESCRIPTION 1", + icon: 'secondary_icon', + sound: RawResourceAndroidNotificationSound('my_sound'), + largeIcon: DrawableResourceAndroidBitmap('large_notf_icon'), + enableLights: true, + color: const Color.fromARGB(255, 255, 0, 0), + ledColor: const Color.fromARGB(255, 255, 0, 0), + ledOnMs: 1000, + ledOffMs: 500, + importance: Importance.max, + priority: Priority.high, + playSound: true, + timeoutAfter: 5000, + styleInformation: DefaultStyleInformation(true, true), + ); + var iosChannelSpecifics = IOSNotificationDetails( + sound: 'my_sound.aiff', + ); + var platformChannelSpecifics = NotificationDetails( + android: androidChannelSpecifics, + iOS: iosChannelSpecifics, + ); + // ignore: deprecated_member_use + await flutterLocalNotificationsPlugin.schedule( + 0, + 'Test Title', + 'Test Body', + scheduleNotificationDateTime, + platformChannelSpecifics, + payload: 'Test Payload', + ); + } + + Future showNotificationWithAttachment() async { + var attachmentPicturePath = await _downloadAndSaveFile( + 'https://via.placeholder.com/800x200', 'attachment_img.jpg'); + var iOSPlatformSpecifics = IOSNotificationDetails( + attachments: [IOSNotificationAttachment(attachmentPicturePath)], + ); + var bigPictureStyleInformation = BigPictureStyleInformation( + FilePathAndroidBitmap(attachmentPicturePath), + contentTitle: 'Attached Image', + htmlFormatContentTitle: true, + summaryText: 'Test Image', + htmlFormatSummaryText: true, + ); + var androidChannelSpecifics = AndroidNotificationDetails( + 'CHANNEL ID 2', + 'CHANNEL NAME 2', + channelDescription: 'CHANNEL DESCRIPTION 2', + importance: Importance.high, + priority: Priority.high, + styleInformation: bigPictureStyleInformation, + ); + var notificationDetails = NotificationDetails( + android: androidChannelSpecifics, iOS: iOSPlatformSpecifics); + await flutterLocalNotificationsPlugin.show( + 0, + 'Title with attachment', + 'Body with Attachment', + notificationDetails, + ); + } + + _downloadAndSaveFile(String url, String fileName) async { + var directory = await getApplicationDocumentsDirectory(); + var filePath = '${directory.path}/$fileName'; + var response = await http.get(Uri.parse(url)); + var file = File(filePath); + await file.writeAsBytes(response.bodyBytes); + return filePath; + } + + Future getPendingNotificationCount() async { + List p = + await flutterLocalNotificationsPlugin.pendingNotificationRequests(); + return p.length; + } + + Future cancelNotification() async { + await flutterLocalNotificationsPlugin.cancel(0); + } + + Future cancelAllNotification() async { + await flutterLocalNotificationsPlugin.cancelAll(); + } +} + +NotificationPlugin notificationPlugin = NotificationPlugin._(); + +class ReceivedNotification { + final int id; + final String title; + final String body; + final String payload; + + ReceivedNotification({ + @required this.id, + @required this.title, + @required this.body, + @required this.payload, + }); +} diff --git a/lib/Pages/BusinessLocator.dart b/lib/Pages/BusinessLocator.dart new file mode 100644 index 0000000..1710190 --- /dev/null +++ b/lib/Pages/BusinessLocator.dart @@ -0,0 +1,620 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'dart:async'; +import 'package:flutter/services.dart' show PlatformException, rootBundle; +import 'package:flutter/cupertino.dart'; +import 'package:location/location.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:teso/Classes/TesoShop.dart'; +import 'package:teso/Pages/Sub_Pages/BusinessDetails.dart'; +import 'package:flutter_polyline_points/flutter_polyline_points.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; + +class BusinessLocator extends StatefulWidget { + @override + _BusinessLocatorState createState() => _BusinessLocatorState(); +} + +class _BusinessLocatorState extends State { + String mapstyle; + var _future; + static LatLng _initialPosition; + Set markers = {}; + List shops; + GoogleMapController mapController; + static const double CAMERA_ZOOM = 13.499910354614258; + bool routing = false; + List polylineCoordinates = []; + Map polylines = {}; + String selectedshop = ""; + Location location = Location(); + String routingMessage = "Finding shops...."; + LocationData _location; + String _error; + String _placeDistance; + final startAddressController = TextEditingController(); + final destinationAddressController = TextEditingController(); + bool ios = false; + + Future _determinePosition(context) async { + setState(() { + _error = null; + }); + try { + final LocationData _locationResult = await location.getLocation(); + setState(() { + _location = _locationResult; + _initialPosition = LatLng(_location.latitude, _location.longitude); + }); + + await getLocations(); + return _initialPosition; + } on PlatformException catch (err) { + setState(() { + _error = err.code; + }); + return _initialPosition; + } + } + + getLocations() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'tesobusiness/available'; + var client1 = await http.get(Uri.parse(register2), headers: requestHeaders); + + if (client1.statusCode == 200) { + try { + var data = jsonDecode(client1.body); + shops = List.from( + data.map((model) => TesoShop.fromJSON(model)).toList()); + } catch (e) { + print(e); + } + } + + if (shops.length > 0 && shops != null) + shops.forEach((element) { + MarkerId markerId = MarkerId(element.shopID); + Marker marker = Marker( + markerId: markerId, + position: LatLng(element.latitude, element.longitude), + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan), + infoWindow: InfoWindow( + title: element.shopName, + snippet: element.shopAddress, + ), + onTap: () => showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return buildShopDetails(bc, element, navigateToShop); + }, + ), + ); + if (mounted) { + setState(() { + markers.add(marker); + }); + } + }); + } + + @override + void initState() { + super.initState(); + ios = Platform.isIOS; + SharedPreferences.getInstance().then((prefs) { + String currentTheme = prefs.getString("theme"); + if (currentTheme == "light") { + rootBundle.loadString('assets/styles/light.txt').then((string) { + mapstyle = string; + }); + } else { + rootBundle.loadString('assets/styles/dark.txt').then((string) { + mapstyle = string; + }); + } + }); + _future = _determinePosition(context); + + location.onLocationChanged.listen((LocationData cLoc) { + _initialPosition = LatLng(cLoc.latitude, cLoc.longitude); + }); + } + + void navigateToShop(TesoShop tesoShop) async { + Position _northeastCoordinates; + Position _southwestCoordinates; + + MarkerId markerId = MarkerId(tesoShop.shopName + " Location"); + Marker marker = Marker( + markerId: markerId, + position: LatLng(tesoShop.latitude, tesoShop.longitude), + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), + infoWindow: InfoWindow( + title: tesoShop.shopName, + snippet: tesoShop.shopAddress, + ), + ); + + setState(() { + markers.clear(); + markers.add(marker); + selectedshop = tesoShop.shopName; + destinationAddressController.text = selectedshop; + }); + + Position user = Position( + latitude: _initialPosition.latitude, + longitude: _initialPosition.longitude, + accuracy: 100, + altitude: 100, + heading: 100, + speed: 100, + speedAccuracy: 100, + timestamp: DateTime.now()); + Position shopLoc = Position( + latitude: tesoShop.latitude, + longitude: tesoShop.longitude, + accuracy: 100, + altitude: 100, + heading: 100, + speed: 100, + speedAccuracy: 100, + timestamp: DateTime.now()); + + if (_initialPosition.latitude <= tesoShop.latitude) { + _southwestCoordinates = user; + _northeastCoordinates = shopLoc; + } else { + _southwestCoordinates = shopLoc; + _northeastCoordinates = user; + } + await createPolylines(user, shopLoc); + _calculateDistance(user, shopLoc); + + mapController.animateCamera( + CameraUpdate.newLatLngBounds( + LatLngBounds( + northeast: LatLng( + _northeastCoordinates.latitude, + _northeastCoordinates.longitude, + ), + southwest: LatLng( + _southwestCoordinates.latitude, + _southwestCoordinates.longitude, + ), + ), + 100.0, // padding + ), + ); + } + + Future createPolylines(Position start, Position destination) async { + try { + setState(() { + routing = true; + routingMessage = "Calculating route...."; + startAddressController.text = "Current Location "; + }); + polylineCoordinates.clear(); + PolylinePoints polylinePoints = PolylinePoints(); + PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( + mapsKey, + PointLatLng(start.latitude, start.longitude), + PointLatLng(destination.latitude, destination.longitude), + travelMode: TravelMode.driving, + ); + await Future.delayed(Duration(seconds: 5), () async { + if (result.points.isNotEmpty) { + result.points.forEach((PointLatLng point) { + polylineCoordinates.add(LatLng(point.latitude, point.longitude)); + }); + } + PolylineId id = PolylineId('poly'); + + Polyline polyline = Polyline( + polylineId: id, + color: Theme.of(context).colorScheme.secondary, + points: polylineCoordinates, + width: 5, + ); + polylines[id] = polyline; + setState(() { + routing = false; + routingMessage = "Finding shops....."; + }); + }); + return true; + } catch (e) { + print(e); + return false; + } + } + + @override + void dispose() { + if (mapController != null) mapController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return new Scaffold( + body: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting || routing) { + return Stack( + children: [ + ios + ? Container( + margin: EdgeInsets.only( + top: (MediaQuery.of(context).size.height) - + (MediaQuery.of(context).size.height * 0.935), + left: 10), + child: Material( + elevation: 5, + color: Color.fromRGBO(0, 0, 0, 0.4), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + topLeft: Radius.circular(25), + topRight: Radius.circular(25), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(25.0), + child: IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 20, + ), + color: Colors.white, + onPressed: () => Navigator.pop(context), + ), + ), + ), + ) + : Container(), + Container( + padding: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.7), + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Center( + child: Column( + children: [ + CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + Text(routingMessage), + ], + ), + ), + ), + ], + ); + } else if (snapshot.data == null && + snapshot.connectionState == ConnectionState.done) { + print(_error.toString()); + Navigator.of(context).pop(); + return Container(); + } else { + return Stack( + children: [ + GoogleMap( + padding: EdgeInsets.only( + top: 70.0, + ), + zoomGesturesEnabled: true, + zoomControlsEnabled: false, + compassEnabled: true, + myLocationButtonEnabled: true, + myLocationEnabled: true, + markers: markers, + initialCameraPosition: CameraPosition( + target: _initialPosition, + zoom: CAMERA_ZOOM, + //bearing: CAMERA_BEARING, + ), + onMapCreated: (GoogleMapController controller) { + controller.setMapStyle(mapstyle); + mapController = controller; + }, + onCameraMove: (position) { + setState(() { + try { + _initialPosition = LatLng(position.target.latitude, + position.target.longitude); + } catch (e) { + print(e); + } + }); + }, + polylines: Set.of(polylines.values), + ), + Container( + margin: EdgeInsets.only( + top: (MediaQuery.of(context).size.height) - + (MediaQuery.of(context).size.height * 0.935), + left: 10), + child: Material( + elevation: 5, + color: Color.fromRGBO(0, 0, 0, 0.4), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + topLeft: Radius.circular(25), + topRight: Radius.circular(25), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(25.0), + child: IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 20, + ), + color: Colors.white, + onPressed: () => Navigator.pop(context), + ), + ), + ), + ), + Visibility( + visible: selectedshop == null || selectedshop.isEmpty + ? false + : true, + child: Align( + alignment: Alignment.topCenter, + child: SafeArea( + child: Align( + alignment: Alignment.topCenter, + child: Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Container( + decoration: BoxDecoration( + color: Colors.white70, + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + width: SizeConfig.safeBlockHorizontal * 80, + child: Padding( + padding: const EdgeInsets.only( + top: 10.0, bottom: 10.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Places', + style: TextStyle( + fontSize: + SizeConfig.blockSizeHorizontal * 4.0, + ), + ), + SizedBox(height: 10), + _textField( + label: 'Start', + hint: 'Choose starting point', + prefixIcon: Icon( + Icons.looks_one, + color: Colors.black, + ), + controller: startAddressController, + width: SizeConfig.safeBlockHorizontal * 70, + ), + SizedBox(height: 10), + _textField( + label: 'Destination', + hint: 'Choose destination', + prefixIcon: Icon(Icons.looks_two, + color: Colors.black), + controller: destinationAddressController, + width: SizeConfig.safeBlockHorizontal * 70, + ), + SizedBox(height: 10), + Visibility( + visible: + _placeDistance == null ? false : true, + child: Text( + 'DISTANCE: $_placeDistance km', + style: TextStyle( + fontSize: + SizeConfig.blockSizeHorizontal * + 3.5, + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox(height: 5), + ElevatedButton( + onPressed: () async { + polylines.clear(); + markers.clear(); + await getLocations(); + selectedshop = ""; + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Cancel'.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: + SizeConfig.blockSizeHorizontal * + 3.5, + ), + ), + ), + style: ElevatedButton.styleFrom( + primary: Colors.red, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(20.0), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ), + ], + ); + } + }, + ), + ); + } + + double _coordinateDistance(lat1, lon1, lat2, lon2) { + var p = 0.017453292519943295; + var c = cos; + var a = 0.5 - + c((lat2 - lat1) * p) / 2 + + c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p)) / 2; + return 12742 * asin(sqrt(a)); + } + + Future _calculateDistance( + Position startPos, Position destination) async { + try { + // Calculating to check that the position relative + // to the frame, and pan & zoom the camera accordingly. + double miny = (startPos.latitude <= destination.latitude) + ? startPos.latitude + : destination.latitude; + double minx = (startPos.longitude <= destination.longitude) + ? startPos.longitude + : destination.longitude; + double maxy = (startPos.latitude <= destination.latitude) + ? destination.latitude + : startPos.latitude; + double maxx = (startPos.longitude <= destination.longitude) + ? destination.longitude + : startPos.longitude; + + double southWestLatitude = miny; + double southWestLongitude = minx; + + double northEastLatitude = maxy; + double northEastLongitude = maxx; + + // Accommodate the two locations within the + // camera view of the map + mapController.animateCamera( + CameraUpdate.newLatLngBounds( + LatLngBounds( + northeast: LatLng(northEastLatitude, northEastLongitude), + southwest: LatLng(southWestLatitude, southWestLongitude), + ), + 100.0, + ), + ); + + // Calculating the distance between the start and the end positions + // with a straight path, without considering any route + // double distanceInMeters = await Geolocator.bearingBetween( + // startLatitude, + // startLongitude, + // destinationLatitude, + // destinationLongitude, + // ); + + await createPolylines(startPos, destination); + + double totalDistance = 0.0; + + // Calculating the total distance by adding the distance + // between small segments + for (int i = 0; i < polylineCoordinates.length - 1; i++) { + totalDistance += _coordinateDistance( + polylineCoordinates[i].latitude, + polylineCoordinates[i].longitude, + polylineCoordinates[i + 1].latitude, + polylineCoordinates[i + 1].longitude, + ); + } + + setState(() { + _placeDistance = totalDistance.toStringAsFixed(2); + print('DISTANCE: $_placeDistance km'); + }); + + return true; + } catch (e) { + print(e); + } + return false; + } + + Widget _textField({ + TextEditingController controller, + FocusNode focusNode, + String label, + String hint, + double width, + Icon prefixIcon, + Widget suffixIcon, + }) { + return Container( + width: width * 0.8, + child: TextField( + controller: controller, + enabled: false, + focusNode: focusNode, + style: TextStyle( + color: Colors.black, + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + decoration: new InputDecoration( + prefixIcon: prefixIcon, + suffixIcon: suffixIcon, + labelText: label, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10.0), + ), + borderSide: BorderSide( + color: Colors.grey.shade400, + width: 2, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10.0), + ), + borderSide: BorderSide( + color: Colors.blue.shade300, + width: 2, + ), + ), + contentPadding: EdgeInsets.all(15), + hintText: hint, + ), + ), + ); + } +} diff --git a/lib/Pages/Campaigns.dart b/lib/Pages/Campaigns.dart new file mode 100644 index 0000000..1663e04 --- /dev/null +++ b/lib/Pages/Campaigns.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'PageWidgets/Campaigns/header.dart'; +import 'package:teso/Classes/API Clasess/Campaign.dart'; +import 'PageWidgets/Campaigns/campaignTile.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'dart:async'; + +class Campaigns extends StatefulWidget { + @override + _CampaignsState createState() => _CampaignsState(); +} + +class _CampaignsState extends State { + TextEditingController searchkey; + List campaignMain; + List campaign; + var _future; + + void clearText() { + setState(() { + searchkey.clear(); + }); + } + + Future> getCampaigns() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'adverts/businesscampaigns'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(searchkey.text), headers: requestHeaders); + + if (client1.statusCode == 200) { + var details = jsonDecode(client1.body); + if (mounted) + setState(() { + campaign = List.from( + details.map((model) => Campaign.fromJSON(model)).toList()); + }); + if (campaignMain == null) { + setState(() { + campaignMain = campaign; + }); + } + } + return campaign; + } + + @override + void initState() { + super.initState(); + searchkey = new TextEditingController(); + _future = getCampaigns(); + searchkey.addListener(() async { + if (searchkey.text.isNotEmpty) { + getCampaigns(); + } else { + setState(() { + campaign = campaignMain; + }); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + automaticallyImplyLeading: true, + title: Text("Join a Campaign"), + centerTitle: true, + ), + body: Container( + // padding: EdgeInsets.only( + // left: 10, + // right: 10, + // ), + child: Column( + children: [ + buildCampaignHead(context, searchkey, clearText), + SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + width: MediaQuery.of(context).size.width, + // height: MediaQuery.of(context).size.height, + child: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.data == null && + snapshot.connectionState == ConnectionState.waiting) { + return Container( + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } else if (snapshot.data == null && + snapshot.connectionState == ConnectionState.done) { + return Container( + height: MediaQuery.of(context).size.width, + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "Sorry there are no open campaigns at the moment"), + ), + ); + } else { + return ListView.builder( + primary: true, + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemCount: campaign.length, + itemBuilder: (context, index) { + return buildCampaign( + context, + campaign.elementAt(index), + ); + }, + ); + } + }, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/DesireComeTrue.dart b/lib/Pages/DesireComeTrue.dart new file mode 100644 index 0000000..a57b935 --- /dev/null +++ b/lib/Pages/DesireComeTrue.dart @@ -0,0 +1,293 @@ +import 'package:flutter/material.dart'; +import 'dart:math' as math; +import 'PageWidgets/DesireComeTrue/DesiredItem.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/Desire.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'Sub_Pages/Desires Come True/AddDesire.dart'; +import 'package:http/http.dart' as http; +import 'package:shared_preferences/shared_preferences.dart'; +import 'dart:convert'; +import 'package:jiffy/jiffy.dart'; + +class DesireComeTrue extends StatefulWidget { + @override + _DesireComeTrueState createState() => _DesireComeTrueState(); +} + +class _DesireComeTrueState extends State + with TickerProviderStateMixin { + AnimationController _controller; + Animation _fabScale; + List desires = []; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, duration: const Duration(milliseconds: 500)); + + _fabScale = Tween(begin: 0, end: 1) + .animate(CurvedAnimation(parent: _controller, curve: Curves.bounceOut)); + + _fabScale.addListener(() { + setState(() {}); + }); + SharedPreferences.getInstance().then((value) { + var jiffy = Jiffy()..add(months: 1); + if (value.getString("desire" + jiffy.format("MMMM, yyyy")).isNotEmpty) { + var desired = + jsonDecode(value.getString("desire" + jiffy.format("MMMM, yyyy"))); + setState(() { + desires = List.from( + desired.map((model) => Desire.fromJSON(model)).toList()); + }); + print(desires.toString()); + } + }); + } + + Future desiredList(context) async { + if (desires.length != 0) + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'monthly-desires/submit-newdesire'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(desires), headers: requestHeaders); + + if (client1.statusCode == 200) { + await tesoSuccessDialog(context); + Future.delayed(const Duration(seconds: 5), () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + } else { + await error(context); + } + } catch (e) { + await error(context); + } + } + + error(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + actions: [ + TextButton( + child: Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Sorry an error occurred please try again!", + style: TextStyle(color: Colors.redAccent[100]), + ), + ); + }); + } + + tesoSuccessDialog(context) { + var jiffy = Jiffy()..add(months: 1); + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Success", + style: TextStyle(color: Colors.green[400]), + ), + actions: [ + // TextButton( + // child: Text( + // 'OK', + // style: TextStyle(color: Colors.green[400]), + // ), + // onPressed: () { + // Navigator.of(context).pop(true); + // }, + // ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Monthly Desire Come True List for " + + jiffy.format("MMMM, yyyy") + + " has been set successfully", + style: TextStyle(color: Colors.green[400]), + ), + ); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: true, + title: Text( + "Desires Come True", + style: TextStyle( + color: Theme.of(context).primaryColorLight, + fontFamily: 'DeadheadScript', + fontSize: 35, + letterSpacing: 3.0, + ), + ), + actions: [ + Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.green, + ), + child: IconButton( + icon: Icon( + Icons.add, + color: Colors.white, + ), + onPressed: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: NewDesire( + selected: desires, + ), + ), + ), + ), + ), + ], + ), + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + physics: NeverScrollableScrollPhysics(), + child: Column( + children: [ + Container( + padding: EdgeInsets.all(10), + width: double.infinity, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.02, + top: MediaQuery.of(context).size.height * 0.02, + ), + child: Center( + child: Text( + "Welcome to your monthly desires come true, here you can set up a list of items you would like to get discount or freebie coupons on " + + "for the month ahead...", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + Container( + padding: EdgeInsets.all(8), + child: Transform.rotate( + angle: math.pi / -45, + child: Card( + color: Colors.white, + elevation: 6, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + child: Column( + children: [ + Container( + color: Colors.white, + padding: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width / 3), + ), + child: Text( + "Items", + style: TextStyle( + color: Colors.black87, + fontFamily: 'DeadheadScript', + fontSize: 40, + letterSpacing: 3.0, + ), + ), + ), + _headSeparator(), + Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.width - 10, + child: ListView.builder( + scrollDirection: Axis.vertical, + itemCount: desires.length, + itemBuilder: (context, index) { + if (desires.isNotEmpty) { + return Column( + children: [ + DesiredItem( + item: desires.elementAt(index), + number: index + 1, + selected: desires, + ), + _separator(), + ], + ); + } else { + return Container(); + } + }, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + floatingActionButton: Container( + margin: EdgeInsets.only( + top: 10, + ), + width: MediaQuery.of(context).size.width / 2, + height: 40, + decoration: new BoxDecoration( + shape: BoxShape.circle, + //color: Colors.grey, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + primary: accentMain, + ), + onPressed: () async => desiredList(context), + child: Text( + "Submit", + style: TextStyle(color: Colors.white), + ), + ), + ), + ); + } +} + +Widget _separator() { + return Container( + height: 1, + decoration: BoxDecoration(color: Colors.blue.withAlpha(100)), + ); +} + +Widget _headSeparator() { + return Container( + height: 3, + decoration: BoxDecoration(color: tesoGold), + ); +} diff --git a/lib/Pages/LandingPage.dart b/lib/Pages/LandingPage.dart new file mode 100644 index 0000000..9a59be8 --- /dev/null +++ b/lib/Pages/LandingPage.dart @@ -0,0 +1,196 @@ +import 'package:camera/camera.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:teso/Classes/customTesoButton.dart'; +import 'package:teso/Pages/PageWidgets/Settings/privacy.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/Login.dart'; +import 'package:teso/Pages/PageWidgets/Login/bottomCurve.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/SizeConfig.dart'; + +import 'package:teso/util/consts.dart'; + +import 'PageWidgets/Settings/terms.dart'; + +class LandingPage extends StatefulWidget { + final List connectedCameras; + + const LandingPage({Key key, this.connectedCameras}) : super(key: key); + @override + _LandingPageState createState() => _LandingPageState(); +} + +class _LandingPageState extends State { + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBody: true, + backgroundColor: Colors.white, + body: AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + padding: EdgeInsets.only(bottom: 10), + child: Stack( + children: [ + // Transform.rotate( + // angle: 15.712, + // child: + Container( + height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + ), + ), + child: CustomPaint( + painter: CurvePainter(), + ), + ), + Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.height * 0.10), + height: MediaQuery.of(context).size.height * 0.20, + width: double.infinity, + child: Align( + alignment: Alignment.topCenter, + child: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white, + image: DecorationImage( + image: AssetImage( + "assets/images/tesoCouponInsignia.png", + ), + fit: BoxFit.contain), + ), + ), + ), + ), + Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.height * 0.10), + height: MediaQuery.of(context).size.height * 0.35, + width: double.infinity, + child: Align( + alignment: Alignment.bottomCenter, + child: Text( + "TESO", + style: TextStyle( + fontSize: 55, + color: Colors.white, + fontFamily: "WickedGrit", + //foreground: Paint()..shader = linearGradient + //fontWeight: FontWeight.w900, + ), + ), + ), + ), + // ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.only( + bottom: 60, + left: SizeConfig.blockSizeHorizontal * 12, + right: SizeConfig.blockSizeHorizontal * 12, + ), + child: RichText( + textAlign: TextAlign.center, + textWidthBasis: TextWidthBasis.longestLine, + text: TextSpan( + text: 'By using this application you agree to the', + style: TextStyle( + color: Colors.black, + fontSize: SizeConfig.blockSizeHorizontal * 3.9, + height: 1.8), + children: [ + TextSpan( + text: ' End User License Agreement', + style: TextStyle( + color: Colors.blueAccent, + fontSize: SizeConfig.blockSizeHorizontal * 4, + fontWeight: FontWeight.w600, + ), + recognizer: TapGestureRecognizer() + ..onTap = () => Navigator.push( + context, + PageTransition( + child: TermsUse(), + type: + PageTransitionType.leftToRight))), + TextSpan( + text: ' and the ', + style: TextStyle( + color: Colors.black, + fontSize: SizeConfig.blockSizeHorizontal * 3.9, + ), + ), + TextSpan( + text: 'Privacy Statement', + style: TextStyle( + color: Colors.blueAccent, + fontSize: SizeConfig.blockSizeHorizontal * 4, + fontWeight: FontWeight.w600, + ), + recognizer: TapGestureRecognizer() + ..onTap = () => Navigator.push( + context, + PageTransition( + child: Privacy(), + type: + PageTransitionType.leftToRight))), + ]), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: RaisedGradientButton( + child: Text( + "Accept & Continue", + style: TextStyle(color: Colors.white, fontSize: 18), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + // Colors.green[400], + // Colors.green[600], + accentMain, + tesoGold + ], + ), + onPressed: () => Navigator.pushAndRemoveUntil( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: LoginPage(), + ), + (Route route) => false), + width: 200, + height: 50, + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/PageWidgets/Alerts/AlertTile.dart b/lib/Pages/PageWidgets/Alerts/AlertTile.dart new file mode 100644 index 0000000..44c04a0 --- /dev/null +++ b/lib/Pages/PageWidgets/Alerts/AlertTile.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +buildAlert( + {BuildContext context, + DateTime timestamp, + String description, + Icon icons, + String thumbnail, + String username}) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 25.0), + child: Material( + elevation: 10, + child: Container( + width: MediaQuery.of(context).size.width, + color: Theme.of(context).primaryColor, + child: Row( + children: [ + Container( + constraints: BoxConstraints(minHeight: 80, maxHeight: 150), + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width) * 0.85, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: icons, + ), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( + direction: Axis.horizontal, + runSpacing: 10, + children: [ + Icon( + Icons.timer, + size: 15, + ), + Text( + TimeElapsed.fromDateTime(timestamp), + style: (TextStyle( + color: Colors.grey, + )), + ), + ], + ), + Container( + width: MediaQuery.of(context).size.width * 0.527, + child: Text( + description, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + ), + Container(), + ], + ), + ), + ), + Container( + width: (MediaQuery.of(context).size.width) * 0.10, + color: Theme.of(context).primaryColor, + child: Align( + alignment: Alignment.topCenter, + child: thumbnail == null + ? Container( + height: 40, + width: 40, + color: Color.fromRGBO(0, 0, 0, 0.4), + child: Center( + child: Text( + username.characters + .characterAt(0) + .toString() + .toUpperCase(), + style: TextStyle(color: Colors.white), + ), + ), + ) + : FadeInImage( + height: 40, + width: 40, + fit: BoxFit.fill, + image: NetworkImage(userdpURL + thumbnail), + placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Alerts/Redeemable.dart b/lib/Pages/PageWidgets/Alerts/Redeemable.dart new file mode 100644 index 0000000..18f790d --- /dev/null +++ b/lib/Pages/PageWidgets/Alerts/Redeemable.dart @@ -0,0 +1,106 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +import 'package:time_elapsed/time_elapsed.dart'; + +buildRedeemableAlert( + {BuildContext context, + DateTime timestamp, + Icon icons, + String thumbnail, + String message}) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 30.0), + child: Material( + elevation: 10, + child: Container( + width: MediaQuery.of(context).size.width, + color: Theme.of(context).primaryColor, + // height: 50, + child: Row( + children: [ + Container( + constraints: BoxConstraints(minHeight: 80, maxHeight: 150), + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width) * 0.88, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: icons, + ), + Expanded( + flex: 1, + // color: Theme.of(context).primaryColor, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( + direction: Axis.horizontal, + runSpacing: 10, + children: [ + Icon( + Icons.timer, + size: 15, + ), + Text( + TimeElapsed.fromDateTime(timestamp), + style: (TextStyle( + color: Colors.grey, + )), + ), + ], + ), + Container( + width: MediaQuery.of(context).size.width * 0.527, + child: Text( + message, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + ), + Container(), + ], + ), + ), + ), + Container( + width: (MediaQuery.of(context).size.width) * 0.10, + color: Theme.of(context).primaryColor, + child: Align( + alignment: Alignment.topCenter, + child: CachedNetworkImage( + imageUrl: productURL + thumbnail, + imageBuilder: (context, imageProvider) => Image( + fit: BoxFit.fill, + width: 40, + height: 40, + image: imageProvider, + ), + ), + // FadeInImage( + // height: 40, + // width: 40, + // fit: BoxFit.fill, + // image: NetworkImage(), + // placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Alerts/Refund.dart b/lib/Pages/PageWidgets/Alerts/Refund.dart new file mode 100644 index 0000000..c7b8a6b --- /dev/null +++ b/lib/Pages/PageWidgets/Alerts/Refund.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +import 'package:time_elapsed/time_elapsed.dart'; + +buildRefundAlert( + {BuildContext context, DateTime timestamp, Icon icons, String message}) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 30.0), + child: Material( + elevation: 10, + child: Container( + width: MediaQuery.of(context).size.width, + color: Theme.of(context).primaryColor, + // height: 50, + child: Row( + children: [ + Container( + constraints: BoxConstraints(minHeight: 80, maxHeight: 150), + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width) * 0.88, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: icons, + ), + Expanded( + flex: 1, + // color: Theme.of(context).primaryColor, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( + direction: Axis.horizontal, + runSpacing: 10, + children: [ + Icon( + Icons.timer, + size: 15, + ), + Text( + TimeElapsed.fromDateTime(timestamp), + style: (TextStyle( + color: Colors.grey, + )), + ), + ], + ), + Container( + width: MediaQuery.of(context).size.width * 0.527, + child: Text( + message, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.2, + ), + ), + ), + Container(), + ], + ), + ), + ), + Container( + width: (MediaQuery.of(context).size.width) * 0.08, + color: Theme.of(context).primaryColor, + child: Align( + alignment: Alignment.topCenter, + child: FadeInImage( + height: 30, + width: 30, + fit: BoxFit.fill, + image: AssetImage("assets/images/silver1.png"), + placeholder: AssetImage("assets/images/silver1.png"), + ), + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Alerts/header.dart b/lib/Pages/PageWidgets/Alerts/header.dart new file mode 100644 index 0000000..a3dd9b4 --- /dev/null +++ b/lib/Pages/PageWidgets/Alerts/header.dart @@ -0,0 +1,138 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/Notifications/newMessage.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; + +buildNotficationHeader( + BuildContext context, + Color fcurrentColor, + Color fcurrentColor1, + Color fTextColor1, + Color fTextColor2, + Function navigationTapped, + bool status, + TesoUser user) { + return Container( + // margin: EdgeInsets.symmetric( + // horizontal: MediaQuery.of(context).size.width / 5, + // ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Visibility( + visible: status, + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + //color: Colors.grey, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(40.0), + topRight: Radius.circular(40.0), + bottomLeft: Radius.circular(40), + bottomRight: Radius.circular(40), + ), + child: user.thumbnail_dp == null + ? Center( + child: Text( + user.username.characters + .characterAt(0) + .toString() + .toUpperCase(), + ), + ) + : CachedNetworkImage( + imageUrl: userdpURL + user.thumbnail_dp, + imageBuilder: (context, imageProvider) => FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + ), + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 5.0), + // padding: EdgeInsets.symmetric( + // horizontal: 5.0, + // ), + child: Material( + color: fcurrentColor == null + ? Theme.of(context).colorScheme.secondary + : fcurrentColor, + elevation: 0, + borderRadius: BorderRadius.circular(20.0), + shadowColor: Theme.of(context).backgroundColor, + child: InkWell( + onTap: () => navigationTapped(0), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Text( + "Updates", + style: TextStyle( + fontSize: 16, + color: fTextColor1, + ), + ), + ), + ), + ), + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 5.0), + // padding: EdgeInsets.symmetric( + // horizontal: 5.0, + // ), + child: Material( + color: fcurrentColor1, + elevation: 0, + borderRadius: BorderRadius.circular(20.0), + shadowColor: Theme.of(context).backgroundColor, + child: InkWell( + onTap: () => navigationTapped(1), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Text( + "Inbox", + style: TextStyle( + fontSize: 16, + color: fTextColor2, + ), + ), + ), + ), + ), + ), + Visibility( + visible: status, + child: GestureDetector( + child: Container( + width: 40, + height: 30, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.secondary, + ), + // padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Icon(Icons.add, size: 25, color: Colors.white), + ), + onTap: () => Navigator.of(context).push( + PageTransition( + child: NewMessage(), + type: PageTransitionType.downToUp, + ), + ), + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Alerts/personalizedCoupon.dart b/lib/Pages/PageWidgets/Alerts/personalizedCoupon.dart new file mode 100644 index 0000000..fa31241 --- /dev/null +++ b/lib/Pages/PageWidgets/Alerts/personalizedCoupon.dart @@ -0,0 +1,157 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/customTesoButton.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +buildPersonalizedAlert( + {BuildContext context, + DateTime timestamp, + Icon icons, + String thumbnail, + String message, + Function accept, + Function decline}) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 30.0), + child: Material( + elevation: 10, + child: Container( + width: MediaQuery.of(context).size.width, + color: Theme.of(context).primaryColor, + // height: 50, + child: Row( + children: [ + Container( + constraints: BoxConstraints(minHeight: 129, maxHeight: 170), + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width) * 0.88, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: icons, + ), + Expanded( + flex: 1, + // color: Theme.of(context).primaryColor, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( + direction: Axis.horizontal, + runSpacing: 10, + children: [ + Icon( + Icons.timer, + size: 15, + ), + Text( + TimeElapsed.fromDateTime(timestamp), + style: (TextStyle( + color: Colors.grey, + )), + ), + ], + ), + Container( + width: MediaQuery.of(context).size.width * 0.527, + child: Text( + message, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + ), + Container(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + Container( + margin: EdgeInsets.all(5), + child: RaisedGradientButton( + child: Text( + "Accept", + style: TextStyle( + color: Colors.white, fontSize: 13.5), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + //darkAccent, + ], + ), + onPressed: accept, + width: 90, + height: 30, + ), + ), + Container( + child: RaisedGradientButton( + child: Text( + "Decline", + style: TextStyle( + color: Colors.white, fontSize: 13.5), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + //darkAccent, + ], + ), + onPressed: decline, + width: 90, + height: 30, + ), + ), + ], + ), + ], + ), + ), + ), + Container( + width: (MediaQuery.of(context).size.width) * 0.10, + color: Theme.of(context).primaryColor, + child: Align( + alignment: Alignment.topCenter, + child: CachedNetworkImage( + imageUrl: productURL + thumbnail, + imageBuilder: (context, imageProvider) => Image( + fit: BoxFit.fill, + width: 40, + height: 40, + image: imageProvider, + ), + ), + // FadeInImage( + // height: 40, + // width: 40, + // fit: BoxFit.fill, + // image: NetworkImage(), + // placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Alerts/post.dart b/lib/Pages/PageWidgets/Alerts/post.dart new file mode 100644 index 0000000..32c65a9 --- /dev/null +++ b/lib/Pages/PageWidgets/Alerts/post.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +buildPostAlert( + {BuildContext context, + DateTime timestamp, + Icon icons, + String thumbnail, + String message}) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 30.0), + child: Material( + elevation: 10, + child: Container( + width: MediaQuery.of(context).size.width, + color: Theme.of(context).primaryColor, + // height: 50, + child: Row( + children: [ + Container( + constraints: BoxConstraints(minHeight: 80, maxHeight: 150), + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width) * 0.88, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: icons, + ), + Expanded( + flex: 1, + // color: Theme.of(context).primaryColor, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( + direction: Axis.horizontal, + runSpacing: 10, + children: [ + Icon( + Icons.timer, + size: 15, + ), + Text( + TimeElapsed.fromDateTime(timestamp), + style: (TextStyle( + color: Colors.grey, + )), + ), + ], + ), + Container( + width: MediaQuery.of(context).size.width * 0.527, + child: Text( + message, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + ), + Container(), + ], + ), + ), + ), + // Container( + // width: (MediaQuery.of(context).size.width) * 0.10, + // color: Theme.of(context).primaryColor, + // child: Align( + // alignment: Alignment.topCenter, + // child: Image( + // fit: BoxFit.fill, + // width: 40, + // height: 40, + // image: MemoryImage(base64Decode(thumbnail)), + // ), + // ), + // ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Alerts/requestTile.dart b/lib/Pages/PageWidgets/Alerts/requestTile.dart new file mode 100644 index 0000000..e94ba22 --- /dev/null +++ b/lib/Pages/PageWidgets/Alerts/requestTile.dart @@ -0,0 +1,184 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +import 'package:teso/Classes/customTesoButton.dart'; + +buildRequest( + {BuildContext context, + DateTime timestamp, + String description, + Icon icons, + String thumbnail, + String username, + Function approve, + Function decline}) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 30.0, vertical: 8.0), + child: Material( + elevation: 10, + child: Container( + width: MediaQuery.of(context).size.width, + color: Theme.of(context).primaryColor, + height: 120, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + height: 120, + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width) * 0.90, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: icons, + ), + ), + Container( + height: 120, + padding: EdgeInsets.symmetric(vertical: 10), + color: Theme.of(context).primaryColor, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( + direction: Axis.horizontal, + runSpacing: 10, + children: [ + Icon( + Icons.timer, + size: 15, + ), + Text( + DateTime.now().difference(timestamp).inMinutes > 60 + ? DateTime.now().difference(timestamp).inHours > 24 + ? DateTime.now() + .difference(timestamp) + .inDays + .toString() + + "d" + : DateTime.now() + .difference(timestamp) + .inHours + .toString() + + "h" + : DateTime.now().difference(timestamp).inMinutes == + 0 + ? "now" + : DateTime.now() + .difference(timestamp) + .inMinutes + .toString() + + "m", + style: (TextStyle( + color: Colors.grey, + )), + ), + ], + ), + Container( + width: MediaQuery.of(context).size.width * 0.45, + child: Text( + description, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + ), + Container(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + Container( + margin: EdgeInsets.all(5), + child: RaisedGradientButton( + child: Text( + "Accept", + style: + TextStyle(color: Colors.white, fontSize: 13.5), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + //darkAccent, + ], + ), + onPressed: approve, + width: 90, + height: 30, + ), + ), + Container( + child: RaisedGradientButton( + child: Text( + "Decline", + style: + TextStyle(color: Colors.white, fontSize: 13.5), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + //darkAccent, + ], + ), + onPressed: decline, + width: 90, + height: 30, + ), + ), + ], + ), + ], + ), + ), + Container( + width: (MediaQuery.of(context).size.width) * 0.10, + color: Theme.of(context).primaryColor, + child: Align( + alignment: Alignment.topCenter, + child: thumbnail == null + ? Container( + height: 40, + width: 40, + color: Color.fromRGBO(0, 0, 0, 0.4), + child: Center( + child: Text( + username.characters + .characterAt(0) + .toString() + .toUpperCase(), + style: TextStyle(color: Colors.white), + ), + ), + ) + : FadeInImage( + height: 40, + width: 40, + fit: BoxFit.fill, + image: NetworkImage(userdpURL + thumbnail), + placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Alerts/timeline_description.dart b/lib/Pages/PageWidgets/Alerts/timeline_description.dart new file mode 100644 index 0000000..024d31e --- /dev/null +++ b/lib/Pages/PageWidgets/Alerts/timeline_description.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +builddescription(BuildContext context, String title, String details) { + return Container( + margin: EdgeInsets.all(10), + padding: EdgeInsets.all(10), + child: Column( + children: [ + Container( + child: Align( + alignment: Alignment.topLeft, + child: Text( + title, + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + ), + ), + ), + Container( + child: Align( + alignment: Alignment.bottomLeft, + child: Text(details), + ), + ), + Divider( + color: accentMain, + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/BusinessProfile/BusinessHead.dart b/lib/Pages/PageWidgets/BusinessProfile/BusinessHead.dart new file mode 100644 index 0000000..98b3745 --- /dev/null +++ b/lib/Pages/PageWidgets/BusinessProfile/BusinessHead.dart @@ -0,0 +1,181 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:numeral/numeral.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/TesoShop.dart'; + +buildHead( + {BuildContext context, + TesoShop shopSelected, + int products, + int subscribers, + int coupons}) { + return Container( + // height: MediaQuery.of(context).size.width - + // (MediaQuery.of(context).size.width * 0.40), + width: MediaQuery.of(context).size.width, + child: Material( + elevation: 2.0, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(5), + bottomRight: Radius.circular(5), + ), + // shadowColor: Theme.of(context).backgroundColor, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + margin: EdgeInsets.only( + top: 10, + ), + width: 80, + height: 80, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(50.0), + topRight: Radius.circular(50.0), + bottomLeft: Radius.circular(50), + bottomRight: Radius.circular(50), + ), + child: shopSelected.logo != "null" + ? CachedNetworkImage( + imageUrl: businessLogoURL + shopSelected.logo, + imageBuilder: (context, imageProvider) => + FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: imageProvider, + placeholder: + AssetImage("assets/images/store.png"), + ), + ) + : Image( + height: 90, + width: 90, + fit: BoxFit.fill, + image: AssetImage("assets/images/store.png"), + ), + ), + ), + new Wrap( + direction: Axis.vertical, + children: [ + Container( + child: Center( + child: Text( + Numeral(products).value().toString(), + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ), + Container( + child: Text( + "Products", + style: TextStyle(fontWeight: FontWeight.w400), + ), + ), + ], + ), + new Wrap( + direction: Axis.vertical, + children: [ + Container( + child: Center( + child: Text( + Numeral(subscribers).value().toString(), + textAlign: TextAlign.center, + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ), + Container( + child: Text( + "Subscribers", + style: TextStyle(fontWeight: FontWeight.w400), + ), + ), + ], + ), + new Wrap( + direction: Axis.vertical, + children: [ + Container( + child: Center( + child: Text( + Numeral(coupons).value().toString(), + textAlign: TextAlign.center, + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ), + Container( + child: Text( + "Coupons", + style: TextStyle(fontWeight: FontWeight.w400), + ), + ), + ], + ), + ], + ), + Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.025, + left: MediaQuery.of(context).size.width * 0.09, + ), + child: Text( + shopSelected.shopAddress, + style: TextStyle( + fontWeight: FontWeight.w400, + //color: Colors.grey, + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.01, + left: MediaQuery.of(context).size.width * 0.09, + ), + child: Text( + shopSelected.categoryShop, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + color: Colors.grey, + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.025, + left: MediaQuery.of(context).size.width * 0.09, + ), + child: Text( + shopSelected.shopDescription.toLowerCase() != "null" + ? shopSelected.shopDescription + : "", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w400, + //color: Colors.grey, + ), + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/BusinessProfile/BusinessItems.dart b/lib/Pages/PageWidgets/BusinessProfile/BusinessItems.dart new file mode 100644 index 0000000..5372b7a --- /dev/null +++ b/lib/Pages/PageWidgets/BusinessProfile/BusinessItems.dart @@ -0,0 +1,69 @@ +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/productDetails.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +buildItems(BuildContext context, Product product, double height) { + return Container( + margin: EdgeInsets.all(1), + width: MediaQuery.of(context).size.width * 0.525, + height: MediaQuery.of(context).size.width * height, + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(30.0), + // ), + child: Hero( + tag: product.productID, + child: GestureDetector( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: ProductDetails( + product: product, + ), + ), + ), + child: Container( + width: double.infinity, + height: double.infinity, + child: Stack( + children: [ + FadeInImage( + width: double.infinity, + height: double.infinity, + fit: BoxFit.fill, + image: NetworkImage(productURL + product.productImage), + placeholder: AssetImage("assets/images/loading.png"), + ), + Container( + height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + child: Align( + alignment: Alignment.bottomCenter, + child: Container( + width: double.infinity, + margin: EdgeInsets.only(bottom: 40), + child: Text( + product.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Campaigns/campaignTile.dart b/lib/Pages/PageWidgets/Campaigns/campaignTile.dart new file mode 100644 index 0000000..074afb1 --- /dev/null +++ b/lib/Pages/PageWidgets/Campaigns/campaignTile.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/Campaign.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/consts.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/AuditionPage.dart'; + +buildCampaign(BuildContext context, Campaign campaignItem) { + return Container( + width: MediaQuery.of(context).size.width, + //height: 120, + // padding: EdgeInsets.only( + // left: 10, + // right: 10, + // ), + child: Material( + elevation: 2.5, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + width: 90, + height: 95, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Image( + width: MediaQuery.of(context).size.width * 0.28, + height: 110, + fit: BoxFit.fill, + image: NetworkImage(campaignItem.targetProduct != null + ? productURL + campaignItem.targetProduct + : ""), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width - 105, + //height: 110, + padding: EdgeInsets.all(5), + child: Column( + children: [ + Container( + width: double.infinity, + child: Wrap( + direction: Axis.horizontal, + children: [ + Text( + "Campaign Title : ", + style: TextStyle(fontWeight: FontWeight.bold), + ), + Text(campaignItem.title) + ], + ), + ), + Container( + width: double.infinity, + child: Wrap( + direction: Axis.horizontal, + children: [ + Text( + "Description : ", + style: TextStyle(fontWeight: FontWeight.bold), + ), + Text( + campaignItem.description.length > 90 + ? campaignItem.description.substring(0, 90) + + "...." + : campaignItem.description, + ) + ], + ), + ), + Container( + width: double.infinity, + child: Wrap( + direction: Axis.horizontal, + children: [ + Text( + "Start Date : ", + style: TextStyle(fontWeight: FontWeight.bold), + ), + Text(Jiffy(campaignItem.startDate).yMMMMd), + ], + ), + ), + Container( + width: double.infinity, + child: Align( + alignment: Alignment.centerRight, + child: Container( + width: 90, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + primary: accentMain, + ), + onPressed: () => Navigator.push( + context, + PageTransition( + child: Audition( + campaign: campaignItem, + ), + type: PageTransitionType.rightToLeft, + ), + ), + child: Text("Join"), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Campaigns/header.dart b/lib/Pages/PageWidgets/Campaigns/header.dart new file mode 100644 index 0000000..c9f8ede --- /dev/null +++ b/lib/Pages/PageWidgets/Campaigns/header.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +buildCampaignHead( + BuildContext context, TextEditingController searchkey, Function filter) { + return Container( + height: 60.0, + //margin: EdgeInsets.all(10.0), + padding: EdgeInsets.all(10.0), + child: Material( + elevation: 4.0, + borderRadius: BorderRadius.circular(12.0), + shadowColor: Theme.of(context).backgroundColor, + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + //buildSmartSearch(context), + new Expanded( + child: InkWell( + onTap: () => print("Searching"), + child: TextField( + autofocus: false, + enabled: true, + textAlign: TextAlign.start, + controller: searchkey, + onChanged: (String v) => filter(v), + style: TextStyle( + color: Theme.of(context).primaryColorLight, + ), + decoration: InputDecoration( + border: InputBorder.none, + prefixIcon: Icon( + Icons.search, + color: Theme.of(context).primaryColorLight, + ), + hintText: "Search", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/ChatScreen/bottomBar.dart b/lib/Pages/PageWidgets/ChatScreen/bottomBar.dart new file mode 100644 index 0000000..7c4bf58 --- /dev/null +++ b/lib/Pages/PageWidgets/ChatScreen/bottomBar.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; + +buildBottom( + BuildContext context, TextEditingController controller, Function send) { + return Container( + height: 70.0, + //margin: EdgeInsets.all(10.0), + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(10.0), + child: Material( + color: Theme.of(context).primaryColor, + elevation: 4.0, + borderRadius: BorderRadius.circular(12.0), + shadowColor: Theme.of(context).backgroundColor, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + //buildSmartSearch(context), + // InkWell( + // child: Container( + // color: Theme.of(context).primaryColor, + // padding: EdgeInsets.symmetric(horizontal: 10.0), + // child: Icon( + // Ionicons.ios_happy, + // size: 30, + // color: Theme.of(context).accentColor, + // ), + // ), + // onTap: () => print(":)"), + // ), + Container( + width: MediaQuery.of(context).size.width * 0.78, + color: Theme.of(context).primaryColor, + child: TextField( + autofocus: false, + maxLines: null, + textAlign: TextAlign.start, + controller: controller, + style: TextStyle( + color: Theme.of(context).primaryColorLight, + ), + decoration: InputDecoration( + border: InputBorder.none, + //contentPadding: EdgeInsets.only(top: 14.0), + hintText: "Type a message", + hintStyle: TextStyle(color: Colors.grey), + fillColor: Theme.of(context).primaryColor, + filled: true, + ), + ), + ), + InkWell( + child: Container( + color: Theme.of(context).primaryColor, + padding: EdgeInsets.symmetric(horizontal: 10.0), + child: Icon( + Icons.send, + size: 30, + color: Theme.of(context).colorScheme.secondary, + ), + ), + onTap: () => send(controller.text, 0), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/ChatScreen/header.dart b/lib/Pages/PageWidgets/ChatScreen/header.dart new file mode 100644 index 0000000..903e6bc --- /dev/null +++ b/lib/Pages/PageWidgets/ChatScreen/header.dart @@ -0,0 +1,106 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/userProfile3P.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; + +buildChatHead(BuildContext context, TesoUser user) { + return Material( + elevation: 10, + child: Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only(top: 15), + padding: EdgeInsets.all(15), + height: 70, + child: Row( + children: [ + InkWell( + child: Container( + width: 25, + height: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.secondary, + ), + // padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Icon(Icons.arrow_back_ios, size: 20, color: Colors.white), + ), + onTap: () => Navigator.pop(context), + ), + InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: UserProfileThirdPerson(user: user), + type: PageTransitionType.fade), + ), + child: Container( + width: 35, + height: 35, + margin: EdgeInsets.only(left: 20, right: 15), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey[400], + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(40.0), + topRight: Radius.circular(40.0), + bottomLeft: Radius.circular(40), + bottomRight: Radius.circular(40), + ), + child: user.thumbnail_dp == null + ? Center( + child: Text( + user.firstname.characters + .characterAt(0) + .toString() + .toUpperCase(), + ), + ) + : CachedNetworkImage( + imageUrl: userdpURL + user.thumbnail_dp, + imageBuilder: (context, imageProvider) => FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: imageProvider, + placeholder: + AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + ), + ), + InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: UserProfileThirdPerson(user: user), + type: PageTransitionType.fade), + ), + child: Container( + margin: EdgeInsets.only(top: 05), + child: Wrap( + direction: Axis.vertical, + children: [ + Text( + user.firstname + " " + user.lastname, + style: TextStyle(fontSize: 15), + textAlign: TextAlign.left, + ), + // Text( + // "typing....", + // style: TextStyle(fontSize: 11.5), + // textAlign: TextAlign.left, + // ), + ], + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/ChatScreen/recipient.dart b/lib/Pages/PageWidgets/ChatScreen/recipient.dart new file mode 100644 index 0000000..b14315b --- /dev/null +++ b/lib/Pages/PageWidgets/ChatScreen/recipient.dart @@ -0,0 +1,46 @@ +import 'package:teso/Classes/ChatMessage.dart'; +import 'package:flutter/material.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +buildRecipient(BuildContext context, ChatMessage message) { + return Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + left: 10, top: 20, right: MediaQuery.of(context).size.width * 0.3), + decoration: BoxDecoration(), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(0.0), + bottomRight: Radius.circular(10), + ), + child: Align( + alignment: Alignment.centerRight, + child: Container( + padding: EdgeInsets.all(10), + color: Theme.of(context).primaryColor, + child: Wrap( + alignment: WrapAlignment.spaceBetween, + spacing: 10.0, + direction: Axis.horizontal, + children: [ + Text(message.content), + Container( + child: Align( + alignment: Alignment.bottomRight, + child: Text( + TimeElapsed.fromDateTime(message.timestamp), + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 10, + ), + ), + ), + ), + ]), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/ChatScreen/sender.dart b/lib/Pages/PageWidgets/ChatScreen/sender.dart new file mode 100644 index 0000000..f46a26d --- /dev/null +++ b/lib/Pages/PageWidgets/ChatScreen/sender.dart @@ -0,0 +1,46 @@ +import 'package:teso/Classes/ChatMessage.dart'; +import 'package:flutter/material.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +buildSender(BuildContext context, ChatMessage document) { + return Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.3, top: 20, right: 10), + decoration: BoxDecoration(), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.0), + topRight: Radius.circular(20.0), + bottomLeft: Radius.circular(10.0), + bottomRight: Radius.circular(0), + ), + child: Align( + alignment: Alignment.centerRight, + child: Container( + padding: EdgeInsets.all(10), + color: Theme.of(context).colorScheme.secondary, + child: Wrap( + alignment: WrapAlignment.spaceBetween, + spacing: 10.0, + direction: Axis.horizontal, + children: [ + Text(document.content), + Container( + child: Align( + alignment: Alignment.bottomRight, + child: Text( + TimeElapsed.fromDateTime(document.timestamp), + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 10, + ), + ), + ), + ), + ]), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/CoinPurchase/AmountInput.dart b/lib/Pages/PageWidgets/CoinPurchase/AmountInput.dart new file mode 100644 index 0000000..c623b15 --- /dev/null +++ b/lib/Pages/PageWidgets/CoinPurchase/AmountInput.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +buildAmount(BuildContext context, TextEditingController user) { + return Container( + width: double.infinity, + height: 45, + child: TextField( + autofocus: true, + keyboardType: TextInputType.number, + textAlign: TextAlign.left, + controller: user, + decoration: InputDecoration( + prefix: Container( + margin: EdgeInsets.only(right: 6), + padding: EdgeInsets.only(right: 5), + child: Text( + "GH¢ ", + style: TextStyle(color: Colors.grey), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 1.0), + ), + //contentPadding: EdgeInsets.only(top: 8.0), + hintText: "00.00", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/CoinPurchase/MomoType.dart b/lib/Pages/PageWidgets/CoinPurchase/MomoType.dart new file mode 100644 index 0000000..fe26dcd --- /dev/null +++ b/lib/Pages/PageWidgets/CoinPurchase/MomoType.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +buildType(BuildContext context, String image, String type, Function selected, + String tapped) { + return Container( + margin: EdgeInsets.all(5), + width: 100, + height: 60, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: InkWell( + onTap: () => selected(type), + child: Container( + width: 120, + height: 50, + decoration: BoxDecoration( + border: type == "airteltigo" + ? Border( + right: BorderSide( + color: accentMain, + width: 2, + ), + left: BorderSide( + color: darkAccent, + width: 2, + ), + ) + : null, + ), + child: Stack( + children: [ + Image( + width: double.infinity, + height: double.infinity, + fit: BoxFit.fill, + image: AssetImage(image), + ), + Visibility( + visible: type == tapped ? false : true, + child: Container( + width: double.infinity, + height: double.infinity, + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/CoinPurchase/PayGold.dart b/lib/Pages/PageWidgets/CoinPurchase/PayGold.dart new file mode 100644 index 0000000..0baf728 --- /dev/null +++ b/lib/Pages/PageWidgets/CoinPurchase/PayGold.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +payWithGold( + BuildContext context, String type, Function selected, String tapped) { + return Container( + margin: EdgeInsets.all(5), + width: 90, + height: 90, + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Colors.transparent, + ), + child: Container( + width: 90, + height: 90, + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Colors.transparent, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(120.0), + child: InkWell( + onTap: () => selected(type), + child: Stack( + children: [ + Image( + width: 90, + height: 90, + fit: BoxFit.fill, + image: AssetImage("assets/images/gold1.png"), + ), + Visibility( + visible: type == tapped ? false : true, + child: Container( + width: double.infinity, + height: double.infinity, + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/CoinPurchase/SilverAmountInput.dart b/lib/Pages/PageWidgets/CoinPurchase/SilverAmountInput.dart new file mode 100644 index 0000000..68731a4 --- /dev/null +++ b/lib/Pages/PageWidgets/CoinPurchase/SilverAmountInput.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:teso/util/consts.dart'; + +buildAmount(BuildContext context, TextEditingController user) { + return Container( + width: 100, + height: 65, + child: TextField( + autofocus: true, + maxLength: 3, + maxLengthEnforcement: MaxLengthEnforcement.enforced, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + controller: user, + style: TextStyle( + fontSize: 18, + ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 1.0), + ), + //contentPadding: EdgeInsets.only(top: 8.0), + hintText: "0000", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/CoinPurchase/SilverPurchase.dart b/lib/Pages/PageWidgets/CoinPurchase/SilverPurchase.dart new file mode 100644 index 0000000..1afcb45 --- /dev/null +++ b/lib/Pages/PageWidgets/CoinPurchase/SilverPurchase.dart @@ -0,0 +1,186 @@ +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/Sub_Pages/CoinsPurchase/SilverPurchaseFixed.dart'; + +buildSilverCard({ + BuildContext context, + String goldEquivalent, + String cashEquivalent, + String funds, + TesoUser user, +}) { + return Container( + margin: EdgeInsets.all(10), + width: 150, + height: 150, + child: Material( + elevation: 5, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + topLeft: Radius.circular(25), + topRight: Radius.circular(25), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(25.0), + child: InkWell( + onTap: () => showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), + ), + builder: (BuildContext bc) { + return SilverPurchaseFixed( + amount: funds, + cashCost: cashEquivalent, + goldCost: goldEquivalent, + user: user, + ); + }, + ), + child: Container( + // color: accentMain, + width: 150, + height: 150, + child: Stack( + children: [ + Container( + margin: EdgeInsets.all(10), + width: 120, + height: 120, + child: Padding( + padding: EdgeInsets.all(5), + child: Image( + width: 90, + height: 90, + image: AssetImage("assets/images/silverAnimated.png"), + fit: BoxFit.fill, + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: Text( + "x " + funds, + style: TextStyle( + color: tesoBlue, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: MediaQuery.of(context).size.width, + height: 25, + color: Color.fromRGBO(0, 0, 0, 0.4), + padding: EdgeInsets.symmetric(horizontal: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + new Wrap( + direction: Axis.horizontal, + children: [ + Container( + child: Container( + height: 20, + width: 20, + decoration: new BoxDecoration( + shape: BoxShape.circle, + ), + child: Container( + height: 20, + width: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(60.0), + topRight: Radius.circular(60.0), + bottomLeft: Radius.circular(60), + bottomRight: Radius.circular(60), + ), + child: Image( + height: 20, + width: 20, + fit: BoxFit.fill, + image: + AssetImage("assets/images/gold1.png"), + ), + ), + ), + ), + ), + Container( + width: 20, + height: 20, + child: Center( + child: Text( + goldEquivalent, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Container( + child: Container( + height: 20, + //width: 20, + decoration: new BoxDecoration( + shape: BoxShape.circle, + ), + child: Center( + child: Text( + "GH¢", + style: TextStyle( + color: Colors.grey[300], + fontSize: 13, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + Container( + width: 20, + height: 20, + child: Center( + child: Text( + cashEquivalent, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/CoinPurchase/phonenumber.dart b/lib/Pages/PageWidgets/CoinPurchase/phonenumber.dart new file mode 100644 index 0000000..3548def --- /dev/null +++ b/lib/Pages/PageWidgets/CoinPurchase/phonenumber.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:teso/util/consts.dart'; + +inputTelNumber(BuildContext context, TextEditingController user) { + return Container( + width: double.infinity, + height: 45, + child: TextField( + maxLengthEnforcement: MaxLengthEnforcement.enforced, + autofocus: true, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + controller: user, + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 1.0), + ), + hintStyle: TextStyle(color: Colors.grey), + ), + inputFormatters: []), + ); +} diff --git a/lib/Pages/PageWidgets/CoinPurchase/selector.dart b/lib/Pages/PageWidgets/CoinPurchase/selector.dart new file mode 100644 index 0000000..cfbe216 --- /dev/null +++ b/lib/Pages/PageWidgets/CoinPurchase/selector.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +buildSelector(BuildContext context, String title, double size, + List color, Function tapped, Color textColor) { + return InkWell( + onTap: tapped, + child: Container( + width: size - size * 0.55, + height: 42.5, + child: Center( + child: Text( + title, + style: TextStyle( + color: textColor, + ), + ), + ), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: color, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(25), + topLeft: Radius.circular(25), + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Coupons/GiftFriend.dart b/lib/Pages/PageWidgets/Coupons/GiftFriend.dart new file mode 100644 index 0000000..01da598 --- /dev/null +++ b/lib/Pages/PageWidgets/Coupons/GiftFriend.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/TesoUser.dart'; + +buildGiftRecipient( + {BuildContext context, + TextEditingController searchkey, + TesoUser friend, + Function loadFriend}) { + return Container( + height: 60.0, + padding: EdgeInsets.all(10.0), + child: Material( + color: Colors.grey[300], + elevation: 4.0, + borderRadius: BorderRadius.circular(12.0), + shadowColor: Theme.of(context).backgroundColor, + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: 5), + height: 40.0, + width: 40.0, + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey[300], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(35.0), + child: friend == null || friend.username == null + ? Image( + image: AssetImage("assets/images/tesoDP/dp1.png"), + ) + : friend.thumbnail_dp == null + ? Center( + child: Text( + friend.username.characters + .characterAt(0) + .toString() + .toUpperCase(), + ), + ) + : FadeInImage( + height: 45, + width: 45, + fit: BoxFit.fill, + image: NetworkImage(userdpURL + friend.thumbnail_dp), + placeholder: + AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + new Expanded( + child: InkWell( + onTap: loadFriend, + child: TextField( + autofocus: false, + enabled: false, + textAlign: TextAlign.start, + controller: searchkey, + style: TextStyle( + color: Colors.black, + ), + decoration: InputDecoration( + border: InputBorder.none, + hintText: "Enter Recipient", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Coupons/mycoupons.dart b/lib/Pages/PageWidgets/Coupons/mycoupons.dart new file mode 100644 index 0000000..f448adb --- /dev/null +++ b/lib/Pages/PageWidgets/Coupons/mycoupons.dart @@ -0,0 +1,223 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:location/location.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/prominentDisclosure.dart'; +import 'package:teso/Pages/Sub_Pages/Coupons/CouponLocation.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; + +buildMyCoupons(BuildContext context, CouponDetails coupon, Function dialog, + Function scan) { + SizeConfig().init(context); + return InkWell( + onTap: () => dialog(context, coupon), + child: Container( + width: MediaQuery.of(context).size.width, + height: 100, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + width: 80, + height: 80, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: CachedNetworkImage( + imageUrl: productURL + coupon.targetProduct.productImage, + imageBuilder: (context, imageProvider) => Image( + width: MediaQuery.of(context).size.width * 0.28, + height: 110, + fit: BoxFit.fill, + image: imageProvider, + ), + ), + ), + ), + Container( + width: SizeConfig.safeBlockHorizontal * 50, + height: 80, + padding: EdgeInsets.all(5), + child: Center( + child: Column( + children: [ + Container( + width: double.infinity, + child: Text( + "Item Name : " + coupon.targetProduct.productName, + style: TextStyle( + fontSize: SizeConfig.safeBlockHorizontal * 2.8, + ), + ), + ), + Container( + width: double.infinity, + child: Text( + "Shop Name : " + coupon.issuer.businessName, + style: TextStyle( + fontSize: SizeConfig.safeBlockHorizontal * 2.8, + ), + ), + ), + Container( + width: double.infinity, + child: CountdownTimer( + endTime: coupon.expiration.millisecondsSinceEpoch, + widgetBuilder: (context, time) { + int day = time.days; + int hours = time.hours; + int min = time.min; + int sec = time.sec; + if (time.min == null) min = 0; + if (time.days == null) day = 0; + if (time.hours == null) hours = 0; + if (time.sec == null) sec = 0; + if (time == null) { + return Text('Calculating..'); + } else if (time.days == null || time.days < 1) { + return Text( + "Expires in : ${hours}h : ${min}m : ${sec}s", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + fontSize: SizeConfig.safeBlockHorizontal * 2.8, + ), + ); + } else if (time.days == 1) { + return Text( + "Expires in : ${day}day, ${hours}h : ${min}m : ${sec}s", + style: TextStyle( + color: Colors.green, + fontWeight: FontWeight.bold, + fontSize: SizeConfig.safeBlockHorizontal * 2.8, + ), + ); + } else { + return Text( + "Expires in : ${day}days, ${hours}h : ${min}m : ${sec}s", + style: TextStyle( + color: Colors.green, + fontWeight: FontWeight.bold, + fontSize: SizeConfig.safeBlockHorizontal * 2.8, + ), + ); + } + }, + ), + //Text("Expires : " + "00:00.0"), + ), + Container( + width: double.infinity, + child: Text( + "Pay only : GH¢ " + + (coupon.targetProduct.unitPrice - + (coupon.targetProduct.unitPrice * + (coupon.lowerLimit / 100))) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.red, + fontSize: SizeConfig.safeBlockHorizontal * 2.8, + ), + ), + ), + ], + ), + ), + ), + new Wrap( + direction: Axis.vertical, + children: [ + Container( + width: SizeConfig.safeBlockHorizontal * 25, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.all( + // Radius.circular(20.0), + // ), + // ), + primary: tesoBlue, + ), + onPressed: () async { + PermissionStatus alreadyConsent = + await Location.instance.hasPermission(); + if (alreadyConsent != PermissionStatus.granted) { + bool results = await Navigator.push( + context, + PageTransition( + child: ProminentDisclosure(), + type: PageTransitionType.leftToRight)); + if (results) { + await Location.instance.requestPermission(); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: CouponLocator( + shop: coupon.issuer, + ), + ), + ); + } + } else { + Navigator.push( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: CouponLocator( + shop: coupon.issuer, + ), + ), + ); + } + }, + child: Text( + "Location", + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + Container( + width: SizeConfig.safeBlockHorizontal * 25, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + primary: accentMain, + ), + onPressed: () => scan(context, coupon), + child: Text("Redeem"), + ), + ), + ], + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Coupons/personalizedDiscount.dart b/lib/Pages/PageWidgets/Coupons/personalizedDiscount.dart new file mode 100644 index 0000000..d46285a --- /dev/null +++ b/lib/Pages/PageWidgets/Coupons/personalizedDiscount.dart @@ -0,0 +1,297 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/util/consts.dart'; + +buildPersonalizedDiscountCoupon( + BuildContext context, CouponDetails coupon, Function acquiring) { + return Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.75, + //color: Colors.black, + + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.1, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30), + topRight: Radius.circular(30), + ), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.68, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topLeft: Radius.circular(30), + topRight: Radius.circular(30), + ), + color: Colors.white, + ), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: coupon.upperLimit < 50.1 + ? DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/blue.png"), + colorFilter: new ColorFilter.mode( + Color(0xFF0031ed).withOpacity(1.0), + BlendMode.multiply), + ) + : DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/redBack.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(1.0), + BlendMode.multiply), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: + TextStyle(color: Colors.white, fontSize: 15), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + coupon.issuer.businessName.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "VOUCHER", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.18, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + border: Border.all(color: Colors.white)), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + coupon.targetProduct.unitPrice.toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ " + + (coupon.targetProduct.unitPrice- (coupon.targetProduct.unitPrice * + (coupon.lowerLimit / 100))) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: + MediaQuery.of(context).size.width * 0.15), + child: Text( + coupon.targetProduct.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.025, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Pay : GH¢ " + + (coupon.targetProduct.unitPrice- (coupon.targetProduct.unitPrice * + (coupon.lowerLimit / 100))) + .toStringAsFixed(2) + + " only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + coupon.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + coupon.condition, + ), + ), + ], + ), + ), + ), + Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Center( + child: Wrap( + children: [ + Text( + coupon.lowerLimit.toString() + "%", + style: TextStyle( + color: Colors.white, + fontSize: 18.5, + fontWeight: FontWeight.bold, + ), + ), + Container( + margin: EdgeInsets.only( + left: 22, + ), + child: Text( + "OFF", + textAlign: TextAlign.end, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w900, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.03, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: coupon.upperLimit < 50.1 ? tesoBlue : Colors.red, + ), + onPressed: acquiring, + child: Text( + "Accept Coupon", + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Coupons/personalizedFreebie.dart b/lib/Pages/PageWidgets/Coupons/personalizedFreebie.dart new file mode 100644 index 0000000..5589788 --- /dev/null +++ b/lib/Pages/PageWidgets/Coupons/personalizedFreebie.dart @@ -0,0 +1,280 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:math' as math; + +buildPersonalizedFreebieCoupon( + BuildContext context, CouponDetails coupon, Function acquiring) { + return Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.75, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.1, + ), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.68, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + color: Colors.white, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/grey.png"), + colorFilter: new ColorFilter.mode( + tesoAsh.withOpacity(1.0), BlendMode.overlay), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + coupon.issuer.businessName.toUpperCase(), + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "FREEBIE", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.18, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 2), + ), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + coupon.targetProduct.unitPrice + .toStringAsFixed(2), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ 0.00", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: + MediaQuery.of(context).size.width * 0.15), + child: Text( + coupon.targetProduct.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.025, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Pay : GH¢ 0.00 only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + coupon.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + coupon.condition + .replaceAll('"', ""), + style: TextStyle( + color: Colors.black, + ), + ), + ), + ], + ), + ), + ), + Container( + width: 70, + height: 70, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 70, + height: 70, + //padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Transform.rotate( + angle: -math.pi / 4, + child: Container( + margin: EdgeInsets.symmetric( + vertical: 20, horizontal: 5), + height: 10, + child: Text( + "FREE!!!", + style: TextStyle( + color: Colors.white, + fontSize: 16.2, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.03, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: accentMain, + ), + onPressed: acquiring, + child: Text( + "Accept Coupon", + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/CouponsList/activeCoupon.dart b/lib/Pages/PageWidgets/CouponsList/activeCoupon.dart new file mode 100644 index 0000000..1bda217 --- /dev/null +++ b/lib/Pages/PageWidgets/CouponsList/activeCoupon.dart @@ -0,0 +1,433 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API%20Clasess/CouponHead.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +buildActiveDiscountCoupon(BuildContext context, CouponDetails coupon, + Function acquiring, Function calWorth) { + SizeConfig().init(context); + return Wrap( + direction: Axis.horizontal, + children: [ + Container( + width: SizeConfig.safeBlockHorizontal * 70, + height: SizeConfig.safeBlockVertical * 85, + //color: Colors.black, + + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.1, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30), + topRight: Radius.circular(30), + ), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.68, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topLeft: Radius.circular(30), + topRight: Radius.circular(30), + ), + color: Colors.white, + ), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: coupon.upperLimit < 50.1 + ? DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/blue.png"), + colorFilter: new ColorFilter.mode( + Color(0xFF0031ed).withOpacity(1.0), + BlendMode.multiply), + ) + : DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/redBack.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(1.0), + BlendMode.multiply), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.white, fontSize: 15), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + coupon.issuer.businessName.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "VOUCHER", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: SizeConfig.safeBlockHorizontal * 16, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all(color: Colors.white)), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + coupon.targetProduct.unitPrice + .toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * 3, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ " + + (coupon.targetProduct.unitPrice - + (coupon.targetProduct + .unitPrice * + (coupon.worth / 100))) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * 3), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Image( + width: + SizeConfig.safeBlockHorizontal * 5, + image: AssetImage( + "assets/images/silver1.png"), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + "SILVER COIN COST : ", + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * + 3, + ), + ), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + CouponRateCalculator.getRate(coupon + .targetProduct.unitPrice * + (coupon.worth / 100)) + .toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * + 3, + ), + ), + ), + ], + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: + MediaQuery.of(context).size.width * 0.15), + child: Text( + coupon.targetProduct.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.025, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Pay : GH¢ " + + (coupon.targetProduct.unitPrice - + (coupon.targetProduct + .unitPrice * + (coupon.worth / 100))) + .toStringAsFixed(2) + + " only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + coupon.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + coupon.condition + .replaceAll('"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: SizeConfig.safeBlockHorizontal * 22, + height: SizeConfig.safeBlockHorizontal * 22, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: SizeConfig.safeBlockHorizontal * 22, + height: SizeConfig.safeBlockHorizontal * 22, + padding: EdgeInsets.all(2), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all( + color: Colors.red, + ), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + coupon.worth.toStringAsFixed(1) + "%", + style: TextStyle( + color: Colors.white, + fontSize: + SizeConfig.safeBlockHorizontal * 6, + fontWeight: FontWeight.w900, + ), + ), + Container( + margin: EdgeInsets.only( + left: 22, + ), + child: Text( + "OFF", + textAlign: TextAlign.end, + style: TextStyle( + color: Colors.white, + fontSize: + SizeConfig.safeBlockHorizontal * + 4, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.03, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: coupon.upperLimit < 50.1 ? tesoBlue : Colors.red, + ), + onPressed: () async { + CouponsHead couponsHead = new CouponsHead(); + couponsHead.businessId = coupon.issuer.businessId; + couponsHead.expiration = coupon.expiration; + couponsHead.couponId = coupon.couponId; + couponsHead.quantity = 1; + couponsHead.state = "active"; + couponsHead.lower = coupon.worth; + couponsHead.upper = coupon.upperLimit; + couponsHead.targetProduct = coupon.targetProduct.productID; + double price = + coupon.targetProduct.unitPrice * (coupon.worth / 100); + + int cost = CouponRateCalculator.getRate(price); + acquiring(couponsHead, cost); + }, + child: Text( + "Accept Coupon", + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ), + ), + ), + Container( + // width: SizeConfig.safeBlockHorizontal * 0.1, + height: SizeConfig.safeBlockVertical * 60, + padding: EdgeInsets.all(SizeConfig.safeBlockVertical * 2), + child: Center( + child: new Column( + children: [ + Container( + child: Text( + coupon.upperLimit.toString() + " %", + textAlign: TextAlign.center, + style: TextStyle( + foreground: Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = Colors.blue, + ), + ), + ), + Container( + height: SizeConfig.safeBlockVertical * 50, + child: new RotatedBox( + quarterTurns: 3, + child: Slider( + value: coupon.worth, + min: coupon.lowerLimit, + max: coupon.upperLimit, + divisions: 20, + activeColor: accentMain, + inactiveColor: darkAccent, + label: coupon.worth.toString() + "%", + onChanged: (double newValue) => calWorth(newValue, coupon), + ), + ), + ), + Container( + child: Text( + coupon.lowerLimit.toString() + " %", + textAlign: TextAlign.center, + style: TextStyle( + foreground: Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = Colors.blue, + ), + ), + ), + ], + ), + ), + ), + ], + ); +} diff --git a/lib/Pages/PageWidgets/CouponsList/activeFreebieCoupon.dart b/lib/Pages/PageWidgets/CouponsList/activeFreebieCoupon.dart new file mode 100644 index 0000000..185f7da --- /dev/null +++ b/lib/Pages/PageWidgets/CouponsList/activeFreebieCoupon.dart @@ -0,0 +1,347 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:math' as math; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; + +buildFreebieCoupon( + BuildContext context, CouponDetails coupon, Function acquiring) { + SizeConfig().init(context); + return Container( + width: SizeConfig.safeBlockHorizontal * 70, + height: SizeConfig.safeBlockVertical * 85, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.1, + ), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.68, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + color: Colors.white, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.70, + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/grey.png"), + colorFilter: new ColorFilter.mode( + tesoAsh.withOpacity(1.0), BlendMode.overlay), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + coupon.issuer.businessName.toUpperCase(), + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "FREEBIE", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: SizeConfig.safeBlockHorizontal * 16, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 2), + ), + child: Center( + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + coupon.targetProduct.unitPrice + .toStringAsFixed(2), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * 3, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ 0.00", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * 3, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Image( + width: SizeConfig.safeBlockHorizontal * 5, + image: AssetImage( + "assets/images/silver1.png"), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + "SILVER COIN COST : ", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * + 3, + ), + ), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + CouponRateCalculator.getRate( + coupon.targetProduct.unitPrice * + (coupon.worth / 100)) + .toString(), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * + 3, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: + MediaQuery.of(context).size.width * 0.15), + child: Text( + coupon.targetProduct.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.025, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Pay : GH¢ 0.00 only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + coupon.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + coupon.condition + .replaceAll('"', ""), + style: TextStyle( + color: Colors.black, + ), + ), + ), + ], + ), + ), + ), + Container( + width: 70, + height: 70, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 70, + height: 70, + //padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Transform.rotate( + angle: -math.pi / 4, + child: Container( + margin: EdgeInsets.symmetric( + vertical: 20, horizontal: 5), + height: 10, + child: Text( + "FREE!!!", + style: TextStyle( + color: Colors.white, + fontSize: 16.2, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.03, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: accentMain, + ), + onPressed: () async { + CouponsHead couponsHead = new CouponsHead(); + couponsHead.businessId = coupon.issuer.businessId; + couponsHead.expiration = coupon.expiration; + couponsHead.couponId = coupon.couponId; + couponsHead.quantity = 1; + couponsHead.state = "active"; + couponsHead.lower = 100; + couponsHead.upper = 100; + couponsHead.targetProduct = coupon.targetProduct.productID; + double price = + coupon.targetProduct.unitPrice * (couponsHead.lower / 100); + int cost = CouponRateCalculator.getRate(price); + acquiring(couponsHead, cost); + }, + child: Text( + "Accept Coupon", + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/DesireComeTrue/DesireTile.dart b/lib/Pages/PageWidgets/DesireComeTrue/DesireTile.dart new file mode 100644 index 0000000..5f88400 --- /dev/null +++ b/lib/Pages/PageWidgets/DesireComeTrue/DesireTile.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/Desire.dart'; +import 'package:teso/util/consts.dart'; + +buildProductDesire(BuildContext context, Desire item, Function addItem) { + return Container( + width: MediaQuery.of(context).size.width, + height: 90, + padding: EdgeInsets.symmetric(horizontal: 10), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + Container( + width: 70, + height: 70, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Image( + width: MediaQuery.of(context).size.width * 0.28, + fit: BoxFit.fill, + //controller: controller, + image: NetworkImage(productURL + item.productImage), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width / 1.7, + padding: EdgeInsets.symmetric(vertical: 20, horizontal: 5), + child: Column( + children: [ + Container( + width: double.infinity, + child: Text("Item Name : " + item.productName), + ), + Container( + width: double.infinity, + child: Text("Price : GH¢ " + item.price.toString()), + ), + ], + ), + ), + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.green, + ), + child: Center( + child: IconButton( + icon: Icon( + Icons.add, + color: Colors.white, + ), + onPressed: () => addItem(item), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/DesireComeTrue/DesiredItem.dart b/lib/Pages/PageWidgets/DesireComeTrue/DesiredItem.dart new file mode 100644 index 0000000..79a906b --- /dev/null +++ b/lib/Pages/PageWidgets/DesireComeTrue/DesiredItem.dart @@ -0,0 +1,190 @@ +import 'package:teso/Classes/API%20Clasess/Desire.dart'; +import 'package:flutter/material.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class DesiredItem extends StatefulWidget { + final Desire item; + final int number; + final List selected; + const DesiredItem({Key key, this.item, this.number, this.selected}) + : super(key: key); + @override + _DesiredItemState createState() => _DesiredItemState(); +} + +class _DesiredItemState extends State + with TickerProviderStateMixin { + Animation _spaceWidth; + AnimationController _controller; + AnimationController _strikeController; + Animation _strikePercent; + + bool cancelled = false; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 150), + ); + + _strikeController = AnimationController( + vsync: this, duration: const Duration(milliseconds: 300)); + + _spaceWidth = Tween(begin: 8, end: 12) + .animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut)); + + _strikePercent = Tween(begin: 0, end: 1).animate( + CurvedAnimation(parent: _strikeController, curve: Curves.easeOut)); + + _spaceWidth.addListener(() { + setState(() {}); + }); + + _strikePercent.addListener(() { + setState(() {}); + }); + } + + cancel() { + setState(() { + cancelled = !cancelled; + }); + if (cancelled) { + _playAnimation(true); + } else { + _playAnimation(false); + } + } + + Future _playAnimation(bool strikeIn) async { + try { + if (strikeIn) { + _strikeController.forward().orCancel; + } else { + _strikeController.reverse().orCancel; + } + + await _controller.forward().orCancel; + await _controller.reverse().orCancel; + } on TickerCanceled { + // the animation got canceled, probably because we were disposed + } + } + + removeElement(item) { + cancel(); + if (cancelled) { + widget.selected.remove(item); + SharedPreferences.getInstance().then((value) { + var jiffy = Jiffy()..add(months: 1); + value.setString( + "desire" + jiffy.format("MMMM, yyyy"), widget.selected.toString()); + }); + } else { + widget.selected.insert(widget.number - 1, item); + SharedPreferences.getInstance().then((value) { + var jiffy = Jiffy()..add(months: 1); + value.setString( + "desire" + jiffy.format("MMMM, yyyy"), widget.selected.toString()); + }); + } + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), + child: IntrinsicHeight( + child: Stack( + children: [ + Row( + children: [ + SizedBox( + width: 40, + child: Center( + child: Text(widget.number.toString() + ")", + style: _getValidateStyle(true)), + ), + ), + Container( + width: 1, + decoration: BoxDecoration(color: Colors.red), + ), + SizedBox( + width: _spaceWidth.value, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: CustomPaint( + foregroundPainter: + StrikeThroughPainter(_strikePercent.value), + child: Text( + widget.item.productName.length > 20 + ? widget.item.productName.substring(0, 16) + + "......" + : widget.item.productName, + style: _getValidateStyle(true)), + ), + ), + SizedBox( + width: 16, + ), + ], + ), + Align( + alignment: Alignment.centerRight, + child: InkWell( + onTap: () => removeElement(widget.item), + child: Container( + margin: EdgeInsets.only( + right: 10, + ), + width: 22.5, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: cancelled == false ? Colors.red : Colors.green, + ), + child: Center( + child: Text( + cancelled == false ? "-" : "+", + style: TextStyle(fontSize: 25, color: Colors.white), + ), + )), + ), + ) + ], + ), + ), + ); + } + + TextStyle _getValidateStyle(bool validation) { + return TextStyle( + fontWeight: FontWeight.bold, + color: (validation) ? Colors.black54 : Colors.black87, + fontSize: 18, + fontFamily: 'WickedGrit', + decoration: null); + } +} + +class StrikeThroughPainter extends CustomPainter { + StrikeThroughPainter(this.percent); + + double percent; + + @override + void paint(Canvas canvas, Size size) { + canvas.drawRect( + Rect.fromLTWH(0, (size.height / 2) - 2, size.width * percent, 4), + Paint()..color = Colors.redAccent); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return false; + } +} diff --git a/lib/Pages/PageWidgets/Editors/SampleThumbnail.dart b/lib/Pages/PageWidgets/Editors/SampleThumbnail.dart new file mode 100644 index 0000000..0cb800c --- /dev/null +++ b/lib/Pages/PageWidgets/Editors/SampleThumbnail.dart @@ -0,0 +1,52 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:teso/Classes/ColorFilters.dart'; +import 'package:teso/util/SizeConfig.dart'; + +buildFilterThumb(BuildContext context, ColorFilter color, Uint8List thumb) { + SizeConfig().init(context); + return Container( + margin: EdgeInsets.symmetric( + horizontal: 5, + ), + width: SizeConfig.safeBlockHorizontal * 25, + height: 45, + color: Colors.white, + child: Column( + children: [ + Container( + width: SizeConfig.safeBlockHorizontal * 25, + color: Color.fromRGBO(0, 0, 0, 0.8), + child: Text( + color.name, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + color: Colors.white, + ), + ), + ), + Container( + width: SizeConfig.safeBlockHorizontal * 25, + child: Stack( + children: [ + Image.memory( + thumb, + height: 93.5, + fit: BoxFit.fill, + ), + Container( + width: SizeConfig.safeBlockHorizontal * 25, + height: 93, + color: color.name.toLowerCase() == "original" + ? color.code.withOpacity(0) + : color.code.withOpacity(0.5), + ), + ], + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Explore/business.dart b/lib/Pages/PageWidgets/Explore/business.dart new file mode 100644 index 0000000..18457eb --- /dev/null +++ b/lib/Pages/PageWidgets/Explore/business.dart @@ -0,0 +1,90 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/TesoShop.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/Pages/Sub_Pages/Business/BusinessProfile.dart'; + +buildBusiness(BuildContext context, TesoShop shop) { + return Container( + margin: EdgeInsets.all(2.5), + width: MediaQuery.of(context).size.width * 0.525, + height: MediaQuery.of(context).size.width * 0.60, + child: InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: BusinessProfile( + shop: shop, + ), + type: PageTransitionType.fade, + ), + ), + child: Material( + elevation: 5, + child: Container( + width: MediaQuery.of(context).size.width * 0.525, + height: MediaQuery.of(context).size.width * 0.60, + child: Wrap( + children: [ + shop.logo.toLowerCase() == "null" || shop.logo == null + ? Image( + width: double.infinity, + height: MediaQuery.of(context).size.width * 0.45, + fit: BoxFit.fill, + image: AssetImage("assets/images/store.png"), + ) + : CachedNetworkImage( + imageUrl: businessLogoURL + shop.logo, + imageBuilder: (context, imageProvider) => FadeInImage( + width: double.infinity, + height: MediaQuery.of(context).size.width * 0.45, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/store.png"), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Colors.white10, + ), + child: Text( + shop.shopName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + // color: Colors.black45, + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + color: Colors.white10, + child: Text( + shop.categoryShop, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.normal, + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + color: Colors.white10, + child: Text( + shop.shopAddress, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.normal, + ), + ), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Explore/categoriesTile.dart b/lib/Pages/PageWidgets/Explore/categoriesTile.dart new file mode 100644 index 0000000..1fbaf6f --- /dev/null +++ b/lib/Pages/PageWidgets/Explore/categoriesTile.dart @@ -0,0 +1,48 @@ +import 'package:teso/Classes/categories.dart'; +import 'package:flutter/material.dart'; + +buildCategory(BuildContext context, Category category) { + return Container( + // height: 60, + margin: EdgeInsets.all(5), + + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).primaryColor, + ), + //padding: EdgeInsets.all(8), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(10.0), + bottomRight: Radius.circular(10), + ), + child: Material( + color: Theme.of(context).primaryColor, + elevation: 4.0, + borderRadius: BorderRadius.circular(10.0), + shadowColor: Theme.of(context).primaryColor, + child: Column( + children: [ + Container( + margin: EdgeInsets.all(4.5), + child: Image( + image: AssetImage(category.image), + width: 24, + height: 24, + ), + ), + SizedBox( + height: 5, + ), + Container( + margin: EdgeInsets.all(4.5), + child: Text(category.name), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Explore/header.dart b/lib/Pages/PageWidgets/Explore/header.dart new file mode 100644 index 0000000..18329b5 --- /dev/null +++ b/lib/Pages/PageWidgets/Explore/header.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +import 'package:teso/Pages/Sub_Pages/Explore/search.dart'; + +buildSearch( + BuildContext context, TextEditingController searchkey, Function select) { + return Container( + height: 60.0, + //margin: EdgeInsets.all(10.0), + padding: EdgeInsets.all(10.0), + child: Material( + color: Colors.grey[300], + elevation: 4.0, + borderRadius: BorderRadius.circular(12.0), + shadowColor: Theme.of(context).backgroundColor, + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + //buildSmartSearch(context), + new Expanded( + child: InkWell( + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => Lookup(), + ), + ), + child: TextField( + autofocus: false, + enabled: false, + textAlign: TextAlign.start, + controller: searchkey, + style: TextStyle( + color: Colors.white, + ), + decoration: InputDecoration( + border: InputBorder.none, + //contentPadding: EdgeInsets.only(top: 14.0), + prefixIcon: Icon( + Icons.search, + color: Colors.white, + ), + hintText: "Search", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + ), + // GestureDetector( + // child: Container( + // padding: EdgeInsets.symmetric(horizontal: 20.0), + // child: Icon(MaterialIcons.camera_alt, size: 30, color: tesoBlue), + // ), + // onTap: () => select(), + // ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Explore/popular.dart b/lib/Pages/PageWidgets/Explore/popular.dart new file mode 100644 index 0000000..6321fb0 --- /dev/null +++ b/lib/Pages/PageWidgets/Explore/popular.dart @@ -0,0 +1,75 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/productDetails.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; + +buildPopularItem(BuildContext context, Product product) { + return SizedBox( + child: Container( + height: 200, + margin: EdgeInsets.only(bottom: 05), + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: ProductDetails( + product: product, + ), + ), + ), + child: Stack( + fit: StackFit.passthrough, + children: [ + CachedNetworkImage( + imageUrl: tesoProductThumbnail( + productLogo: product.productImage, + width: 640, + height: 640, + ), + imageBuilder: (context, imageProvider) => FadeInImage( + width: double.infinity, + fit: BoxFit.contain, + image: imageProvider, + placeholder: AssetImage("assets/images/loading.png"), + ), + ), + Container( + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + Center( + child: Text( + product.productName.toUpperCase(), + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ) + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Explore/products.dart b/lib/Pages/PageWidgets/Explore/products.dart new file mode 100644 index 0000000..aa1c9bc --- /dev/null +++ b/lib/Pages/PageWidgets/Explore/products.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/productDetails.dart'; + +buildProducts(BuildContext context, Product product) { + return Container( + margin: EdgeInsets.all(2), + width: MediaQuery.of(context).size.width * 0.525, + height: MediaQuery.of(context).size.width * 0.60, + child: InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: ProductDetails( + product: product, + ), + ), + ), + child: Container( + width: MediaQuery.of(context).size.width * 0.525, + height: MediaQuery.of(context).size.width * 0.60, + child: Wrap( + children: [ + FadeInImage( + width: double.infinity, + height: MediaQuery.of(context).size.width * 0.45, + fit: BoxFit.fill, + image: NetworkImage( + tesoProductThumbnail( + productLogo: product.productImage, + width: 640, + height: 640, + ), + ), + placeholder: AssetImage("assets/images/loading.png"), + ), + Container( + height: 50, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + child: Center( + child: Text( + product.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Explore/trending.dart b/lib/Pages/PageWidgets/Explore/trending.dart new file mode 100644 index 0000000..b9202b0 --- /dev/null +++ b/lib/Pages/PageWidgets/Explore/trending.dart @@ -0,0 +1,139 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; + +buildTrend(BuildContext context, Product product) { + return Container( + margin: EdgeInsets.all(10), + width: MediaQuery.of(context).size.width * 0.525, + height: MediaQuery.of(context).size.width * 0.65, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(30.0), + child: Container( + width: double.infinity, + height: double.infinity, + child: Stack( + children: [ + CachedNetworkImage( + imageUrl: tesoProductThumbnail( + productLogo: product.productImage, + width: 640, + height: 640, + ), + imageBuilder: (context, imageProvider) => FadeInImage( + width: double.infinity, + fit: BoxFit.contain, + image: imageProvider, + placeholder: AssetImage("assets/images/loading.png"), + ), + ), + Container( + height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + child: Align( + alignment: Alignment.bottomCenter, + child: Container( + width: double.infinity, + margin: EdgeInsets.only(bottom: 40), + child: Text( + product.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + Container( + height: double.infinity, + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 10.0), + child: Align( + alignment: Alignment.bottomCenter, + child: Container( + width: double.infinity, + margin: EdgeInsets.only( + bottom: 15, + ), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + Icon( + Icons.location_on, + color: Colors.white, + size: 14, + ), + Text( + " " + product.categoryID.toString(), + textAlign: TextAlign.left, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + ], + ), + ), + ), + ), + ), + // Align( + // alignment: Alignment.bottomRight, + // child: GestureDetector( + // onTap: () => print("Hello"), + // child: Container( + // margin: EdgeInsets.all(10), + // padding: EdgeInsets.all(5), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.all( + // Radius.circular(20), + // ), + // color: Colors.white, + // ), + // child: Icon( + // Feather.heart, + // color: Theme.of(context).accentColor, + // ), + // ), + // ), + // ), + // Align( + // alignment: Alignment.topLeft, + // child: Container( + // margin: EdgeInsets.all(10), + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 5, + // ), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.all( + // Radius.circular(20), + // ), + // color: Colors.white, + // ), + // child: Text( + // product.duration, + // style: TextStyle( + // color: Colors.black, + // ), + // ), + // ), + // ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Friends/friendTile.dart b/lib/Pages/PageWidgets/Friends/friendTile.dart new file mode 100644 index 0000000..324e35d --- /dev/null +++ b/lib/Pages/PageWidgets/Friends/friendTile.dart @@ -0,0 +1,82 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +buildFriend(BuildContext context, TesoUser friend) { + return Container( + width: MediaQuery.of(context).size.width, + height: 65, + child: Row( + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: 10), + height: 45.0, + width: 45.0, + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey[400], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(30.0), + child: friend.thumbnail_dp == null + ? Center( + child: Text( + friend.username.characters + .characterAt(0) + .toString() + .toUpperCase(), + ), + ) + : CachedNetworkImage( + imageUrl: userdpURL + friend.thumbnail_dp, + imageBuilder: (context, imageProvider) => Image( + height: 90, + width: 90, + fit: BoxFit.fill, + image: imageProvider, + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + height: 30, + child: Container( + width: double.infinity, + height: double.infinity, + child: Align( + alignment: Alignment.centerLeft, + child: Text( + friend.username, + style: TextStyle( + fontSize: 12.5, + color: Colors.grey, + ), + textAlign: TextAlign.left, + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + child: Text( + friend.firstname + " " + friend.lastname, + textAlign: TextAlign.left, + ), + ), + // Divider(), + ], + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Friends/header.dart b/lib/Pages/PageWidgets/Friends/header.dart new file mode 100644 index 0000000..12aa981 --- /dev/null +++ b/lib/Pages/PageWidgets/Friends/header.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +buildFriendsHeader( + BuildContext context, TextEditingController searchkey, Function filter) { + return Container( + height: 60.0, + //margin: EdgeInsets.all(10.0), + padding: EdgeInsets.all(10.0), + child: Material( + elevation: 4.0, + borderRadius: BorderRadius.circular(12.0), + shadowColor: Theme.of(context).backgroundColor, + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + //buildSmartSearch(context), + new Expanded( + child: InkWell( + onTap: () => print("Searching"), + child: TextField( + autofocus: false, + enabled: true, + textAlign: TextAlign.start, + controller: searchkey, + onChanged: (String v) => filter(v), + style: TextStyle( + color: Theme.of(context).primaryColorLight, + ), + decoration: InputDecoration( + border: InputBorder.none, + //contentPadding: EdgeInsets.only(top: 14.0), + prefixIcon: Icon( + Icons.search, + color: Theme.of(context).primaryColorLight, + ), + hintText: "Search", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Home/homeTile.dart b/lib/Pages/PageWidgets/Home/homeTile.dart new file mode 100644 index 0000000..0b9ec53 --- /dev/null +++ b/lib/Pages/PageWidgets/Home/homeTile.dart @@ -0,0 +1,27 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/util/consts.dart'; + +buildTile(BuildContext context, Post advert, double height, Function push) { + return Container( + margin: EdgeInsets.all(0.5), + width: MediaQuery.of(context).size.width * 0.525, + height: MediaQuery.of(context).size.width * height, + child: InkWell( + onTap: () => push(advert), + child: CachedNetworkImage( + imageUrl: tesoPostThumb(advert.playbackID), + imageBuilder: (context, imageProvider) => FadeInImage( + width: double.infinity, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/blank.jpg"), + ), + errorWidget: (context, url, error) => Container( + color: Colors.grey[800], + )), + ), + ); +} diff --git a/lib/Pages/PageWidgets/HomeProximity/ActiveDiscount.dart b/lib/Pages/PageWidgets/HomeProximity/ActiveDiscount.dart new file mode 100644 index 0000000..30d1332 --- /dev/null +++ b/lib/Pages/PageWidgets/HomeProximity/ActiveDiscount.dart @@ -0,0 +1,575 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:teso/Pages/productView.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +Positioned buildActiveDiscountCoupon( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + int flag, + Function swipeRight, + Function swipeLeft, + double worth, + Function calWorth) { + SizeConfig().init(context); + if (worth < img.lowerLimit) { + worth = img.lowerLimit; + } + return new Positioned( + bottom: bottom, + right: flag == 0 + ? right != 0.0 + ? right + : null + : null, + left: flag == 1 + ? right != 0.0 + ? right + : null + : null, + child: new Transform( + alignment: flag == 0 ? Alignment.bottomRight : Alignment.bottomLeft, + transform: new Matrix4.skewX(skew), + child: new RotationTransition( + turns: new AlwaysStoppedAnimation( + flag == 0 ? rotation / 360 : -rotation / 360), + child: Column( + children: [ + new Row( + children: [ + InkWell( + onTap: () async { + CouponsHead head = new CouponsHead(); + head.businessId = img.business.businessId; + head.couponId = img.couponId; + head.expiration = img.expiration; + head.lower = img.lowerLimit; + head.upper = img.upperLimit; + head.quantity = img.quantity; + head.state = img.state; + head.targetProduct = img.targetID; + head.type = img.type; + + CouponDetails details = new CouponDetails(); + details.businessId = img.business.businessId; + details.countID = "null"; + details.couponId = img.couponId; + details.expiration = img.expiration; + details.issuer = img.business; + details.lowerLimit = img.lowerLimit; + details.upperLimit = img.upperLimit; + details.quantity = img.quantity; + details.state = img.state; + details.type = img.type; + details.targetProduct = new Product(); + details.targetProduct.businessID = details.businessId; + details.targetProduct.productImage = img.targetImage; + details.targetProduct.productName = img.targetName; + details.targetProduct.unitPrice = img.targetCost; + details.targetProduct.productDesc = img.tagretDescription; + + Provider.of(context, listen: false) + .viewCoupon(head); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: ProductView( + couponDetails: details, + ), + ), + ); + }, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.65, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.12, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + color: Colors.white, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Material( + elevation: 5.0, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.68, + color: Colors.white, + child: Column( + children: [ + Container( + width: + (MediaQuery.of(context).size.width * 0.75), + height: + MediaQuery.of(context).size.height * 0.50, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: img.upperLimit < 50.1 + ? DecorationImage( + fit: BoxFit.fill, + image: AssetImage( + "assets/images/blue.png"), + colorFilter: new ColorFilter.mode( + Color(0xFF0031ed) + .withOpacity(1.0), + BlendMode.multiply), + ) + : DecorationImage( + fit: BoxFit.fill, + image: AssetImage( + "assets/images/redBack.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(1.0), + BlendMode.multiply), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.white, + fontSize: 15), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context) + .size + .width * + 0.125, + ), + child: Column( + children: [ + Text( + img.business.businessName, + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "VOUCHER", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: + SizeConfig.safeBlockHorizontal * 16, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.white)), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetCost + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ " + + (img.targetCost - + (img.targetCost * + (worth / 100))) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Image( + width: SizeConfig + .safeBlockHorizontal * + 5, + image: AssetImage( + "assets/images/silver1.png"), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + "SILVER COIN COST : ", + style: TextStyle( + color: Colors.white, + fontWeight: + FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + CouponRateCalculator + .getRate( + img.targetCost * + (worth / + 100)) + .toString(), + style: TextStyle( + color: Colors.white, + fontWeight: + FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + ), + ], + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context) + .size + .width * + 0.15), + child: Text( + img.targetName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * 0.010, + ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * + 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Pay : GH¢ " + + (img.targetCost - + (img.targetCost * + (worth / 100))) + .toStringAsFixed(2) + + " only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + img.condition + .replaceAll( + '"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: + SizeConfig.safeBlockHorizontal * 22, + height: + SizeConfig.safeBlockHorizontal * 22, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: + SizeConfig.safeBlockHorizontal * 22, + height: + SizeConfig.safeBlockHorizontal * 22, + padding: EdgeInsets.all(2), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage( + "assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Center( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + worth.toStringAsFixed(1) + "%", + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig + .safeBlockHorizontal * + 6, + fontWeight: FontWeight.w900, + ), + ), + Container( + margin: EdgeInsets.only( + left: 22, + ), + child: Text( + "OFF", + textAlign: TextAlign.end, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig + .safeBlockHorizontal * + 4, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ), + Container( + // width: SizeConfig.safeBlockHorizontal * 30, + height: SizeConfig.safeBlockVertical * 60, + // padding: EdgeInsets.all(SizeConfig.safeBlockVertical * 2), + child: new Column( + children: [ + Container( + child: Text( + img.upperLimit.toStringAsFixed(1) + " %", + textAlign: TextAlign.center, + style: TextStyle( + foreground: Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = Colors.blue, + ), + ), + ), + Container( + height: SizeConfig.safeBlockVertical * 50, + child: new RotatedBox( + quarterTurns: 3, + child: Slider( + value: worth, + min: img.lowerLimit, + max: img.upperLimit, + divisions: 20, + activeColor: accentMain, + inactiveColor: darkAccent, + label: worth.toStringAsFixed(1) + "%", + onChanged: (double newValue) => calWorth(newValue), + ), + ), + ), + Container( + child: Text( + img.lowerLimit.toString() + " %", + textAlign: TextAlign.center, + style: TextStyle( + foreground: Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = Colors.blue, + ), + ), + ), + ], + ), + ) + ], + ), + SizedBox( + height: 20, + ), + new Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.red[200], + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => swipeLeft(img), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.red[200], + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Not Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green, + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => swipeRight(img), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ], + ) + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/HomeProximity/ActiveFreebie.dart b/lib/Pages/PageWidgets/HomeProximity/ActiveFreebie.dart new file mode 100644 index 0000000..71fd715 --- /dev/null +++ b/lib/Pages/PageWidgets/HomeProximity/ActiveFreebie.dart @@ -0,0 +1,453 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'dart:math' as math; +import 'package:teso/util/consts.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:teso/Pages/productView.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; + +Positioned buildActiveFreebieCoupon( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + int flag, + Function swipeRight, + Function swipeLeft, +) { + SizeConfig().init(context); + return new Positioned( + bottom: bottom, + right: flag == 0 + ? right != 0.0 + ? right + : null + : null, + left: flag == 1 + ? right != 0.0 + ? right + : null + : null, + child: new Transform( + alignment: flag == 0 ? Alignment.bottomRight : Alignment.bottomLeft, + transform: new Matrix4.skewX(skew), + child: new RotationTransition( + turns: new AlwaysStoppedAnimation( + flag == 0 ? rotation / 360 : -rotation / 360), + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.65, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.1, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + color: Colors.white, + ), + child: InkWell( + onTap: () async { + CouponsHead head = new CouponsHead(); + head.businessId = img.business.businessId; + head.couponId = img.couponId; + head.expiration = img.expiration; + head.lower = img.lowerLimit; + head.upper = img.upperLimit; + head.quantity = img.quantity; + head.state = img.state; + head.targetProduct = img.targetID; + head.type = img.type; + + CouponDetails details = new CouponDetails(); + details.businessId = img.business.businessId; + details.countID = "null"; + details.couponId = img.couponId; + details.expiration = img.expiration; + details.issuer = img.business; + details.lowerLimit = img.lowerLimit; + details.upperLimit = img.upperLimit; + details.quantity = img.quantity; + details.state = img.state; + details.type = img.type; + details.targetProduct = new Product(); + details.targetProduct.businessID = details.businessId; + details.targetProduct.productImage = img.targetImage; + details.targetProduct.productName = img.targetName; + details.targetProduct.unitPrice = img.targetCost; + details.targetProduct.productDesc = img.tagretDescription; + + Provider.of(context, listen: false) + .viewCoupon(head); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: ProductView( + couponDetails: details, + ), + ), + ); + }, + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.50, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/grey.png"), + colorFilter: new ColorFilter.mode( + tesoAsh.withOpacity(1.0), BlendMode.overlay), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: + MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.business.businessName, + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "FREEBIE", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: SizeConfig.safeBlockHorizontal * 16, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: + Border.all(color: Colors.black, width: 2), + ), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetCost.toStringAsFixed(2), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * 3, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ 0.00", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * 3, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Image( + width: + SizeConfig.safeBlockHorizontal * + 5, + image: AssetImage( + "assets/images/silver1.png"), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + "Silver Coin Cost : ", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + CouponRateCalculator.getRate( + img.targetCost * + (img.lowerLimit / 100)) + .toString(), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + ), + ], + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: + MediaQuery.of(context).size.width * + 0.15), + child: Text( + img.targetName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.025, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Pay : GH¢ 0.00 only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + img.condition + .replaceAll('"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: SizeConfig.safeBlockHorizontal * 22, + height: SizeConfig.safeBlockHorizontal * 22, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: SizeConfig.safeBlockHorizontal * 22, + height: SizeConfig.safeBlockHorizontal * 22, + + //padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Transform.rotate( + angle: -math.pi / 4, + child: Container( + margin: EdgeInsets.symmetric( + vertical: 20, horizontal: 5), + height: 10, + child: Text( + "FREE!!!", + style: TextStyle( + color: Colors.white, + fontSize: 16.2, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + SizedBox( + height: 20, + ), + new Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.red, + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => swipeLeft(img), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.red, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Not Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green, + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => swipeRight(img), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ], + ) + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/HomeProximity/DummyDiscount.dart b/lib/Pages/PageWidgets/HomeProximity/DummyDiscount.dart new file mode 100644 index 0000000..e8edf53 --- /dev/null +++ b/lib/Pages/PageWidgets/HomeProximity/DummyDiscount.dart @@ -0,0 +1,314 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +Positioned buildDummyDiscountCoupon( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + String discount, + double selectedDiscount) { + return new Positioned( + bottom: 60 + bottom, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + decoration: BoxDecoration( + border: Border.all(color: tesoGold, width: 0.5), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + color: Colors.white, + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: img.upperLimit < 50.1 + ? DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/blue.png"), + colorFilter: new ColorFilter.mode( + Color(0xFF0031ed).withOpacity(1.0), + BlendMode.multiply), + ) + : DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/redBack.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(1.0), BlendMode.multiply), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle(color: Colors.white, fontSize: 15), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.business.businessName, + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "VOUCHER", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.2, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all(color: Colors.white)), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetCost.toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ " + + (img.targetCost - + (img.targetCost * + (img.lowerLimit / 100))) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Text( + "SILVER COIN COST : ", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Image( + width: 20, + image: + AssetImage("assets/images/silver1.png"), + ), + Text( + CouponRateCalculator.getRate(img.targetCost * + (img.lowerLimit / 100)) + .toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.15), + child: Text( + img.targetName, + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.02, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Price : GH¢ " + + (img.targetCost * (img.lowerLimit / 100)) + .toStringAsFixed(2) + + " only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + img.condition.replaceAll('"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: SizeConfig.safeBlockHorizontal * 22, + height: SizeConfig.safeBlockHorizontal * 22, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: SizeConfig.safeBlockHorizontal * 22, + height: SizeConfig.safeBlockHorizontal * 22, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), BlendMode.overlay), + ), + ), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + img.lowerLimit.toStringAsFixed(1) + "%", + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.safeBlockHorizontal * 6, + fontWeight: FontWeight.w900, + ), + ), + Container( + margin: EdgeInsets.only( + left: 22, + ), + child: Text( + "OFF", + textAlign: TextAlign.end, + style: TextStyle( + color: Colors.white, + fontSize: + SizeConfig.safeBlockHorizontal * 4, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/HomeProximity/DummyFreebie.dart b/lib/Pages/PageWidgets/HomeProximity/DummyFreebie.dart new file mode 100644 index 0000000..43288fe --- /dev/null +++ b/lib/Pages/PageWidgets/HomeProximity/DummyFreebie.dart @@ -0,0 +1,291 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'dart:math' as math; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; + +Positioned buildDummyFreebieCoupon( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + String discount, + double selectedDiscount) { + SizeConfig().init(context); + return new Positioned( + bottom: 60.0 + bottom, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + decoration: BoxDecoration( + border: Border.all(color: tesoBlue, width: 0.5), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + color: Colors.white, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/grey.png"), + colorFilter: new ColorFilter.mode( + tesoAsh.withOpacity(1.0), BlendMode.overlay), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.business.businessName, + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "FREEBIE", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.2, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 2), + ), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetCost.toStringAsFixed(2), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ 0.00", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Text( + "Silver Coin Cost : ", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Image( + width: 20, + image: + AssetImage("assets/images/silver1.png"), + ), + Text( + CouponRateCalculator.getRate(img.targetCost * + (img.lowerLimit / 100)) + .toString(), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.15), + child: Text( + img.targetName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.023, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Price : GH¢ 0.00 only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + img.condition.replaceAll('"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: 70, + height: 70, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: SizeConfig.safeBlockHorizontal * 22, + height: SizeConfig.safeBlockHorizontal * 22, + //padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), BlendMode.overlay), + ), + ), + child: Transform.rotate( + angle: -math.pi / 4, + child: Container( + margin: + EdgeInsets.symmetric(vertical: 20, horizontal: 5), + height: 10, + child: Text( + "FREE!!!", + style: TextStyle( + color: Colors.white, + fontSize: 16.2, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Inbox/header.dart b/lib/Pages/PageWidgets/Inbox/header.dart new file mode 100644 index 0000000..a0f78e0 --- /dev/null +++ b/lib/Pages/PageWidgets/Inbox/header.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; + +buildInboxHead( + BuildContext context, TextEditingController controller, Function filter) { + return Container( + height: 50.0, + width: MediaQuery.of(context).size.width, + // padding: EdgeInsets.all(10.0), + child: Container( + height: 50, + width: MediaQuery.of(context).size.width - 10, + padding: EdgeInsets.symmetric( + horizontal: 15, + ), + child: Container( + width: MediaQuery.of(context).size.width - 10, + height: 40, + margin: EdgeInsets.only(top: 10), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).backgroundColor, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Material( + // color: Theme.of(context).backgroundColor, + elevation: 10.0, + borderRadius: BorderRadius.circular(30.0), + shadowColor: Theme.of(context).backgroundColor, + child: TextField( + autofocus: false, + textAlign: TextAlign.start, + style: TextStyle( + color: Colors.black, + ), + controller: controller, + onChanged: (String value) { + filter(value); + }, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(10), + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: const BorderRadius.all( + Radius.circular( + 30, + ), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: const BorderRadius.all( + Radius.circular( + 30, + ), + ), + ), + //fillColor: Theme.of(context).primaryColor, + prefixIcon: Icon( + Icons.search, + color: Theme.of(context).primaryColorLight, + ), + hintText: "Search", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Inbox/inboxTile.dart b/lib/Pages/PageWidgets/Inbox/inboxTile.dart new file mode 100644 index 0000000..520a178 --- /dev/null +++ b/lib/Pages/PageWidgets/Inbox/inboxTile.dart @@ -0,0 +1,149 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:teso/Classes/inbox.dart'; +import 'package:flutter/material.dart'; +import 'package:time_elapsed/time_elapsed.dart'; +import 'package:teso/util/consts.dart'; + +buildInboxTile(BuildContext context, InboxMessage message, bool read) { + return Container( + width: MediaQuery.of(context).size.width, + height: 80, + child: Row( + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: 10), + height: 50.0, + width: 50.0, + decoration: new BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(30.0), + child: message.thumbnail == null + ? Center( + child: Text( + message.firstname.characters + .characterAt(0) + .toString() + .toUpperCase(), + ), + ) + : CachedNetworkImage( + imageUrl: userdpURL + message.thumbnail, + imageBuilder: (context, imageProvider) => FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width - 80, + margin: EdgeInsets.only( + top: 10, + ), + child: Column( + children: [ + Row( + children: [ + Column( + children: [ + Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + height: 30, + child: Container( + width: double.infinity, + height: double.infinity, + child: Align( + alignment: Alignment.centerLeft, + child: Text( + message.firstname + " " + message.surname, + style: TextStyle( + fontSize: 12.5, + color: Theme.of(context).primaryColorLight, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.left, + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + child: Text( + message.message.length > 73 + ? message.message.substring(0, 73).toString() + + "...." + : message.message, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 11, + ), + ), + ), + ], + ), + Visibility( + visible: !read, + child: Container( + child: Column( + children: [ + Container( + width: 30, + height: 30, + // padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Center( + child: Text( + TimeElapsed.fromDateTime(message.timestamp), + style: TextStyle( + color: + Theme.of(context).colorScheme.secondary, + ), + ), + ), + ), + Material( + color: Colors.grey[300], + elevation: 10.0, + borderRadius: BorderRadius.circular(30.0), + shadowColor: Colors.grey, + child: Container( + width: 10, + height: 10, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + color: Theme.of(context).colorScheme.secondary, + ), + // padding: EdgeInsets.symmetric(horizontal: 20.0), + ), + ), + ], + ), + ), + ), + Visibility( + visible: read, + child: Container( + height: 30, + child: Center( + child: Text( + TimeElapsed.fromDateTime(message.timestamp), + ), + ), + ), + ), + ], + ), + Divider(), + ], + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Inbox/newMessageHeader.dart b/lib/Pages/PageWidgets/Inbox/newMessageHeader.dart new file mode 100644 index 0000000..0574356 --- /dev/null +++ b/lib/Pages/PageWidgets/Inbox/newMessageHeader.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; + +buildNewHead(BuildContext context, Function clear, String title) { + return Container( + width: MediaQuery.of(context).size.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () => Navigator.pop(context), + child: Container( + margin: EdgeInsets.all(20), + height: 30, + width: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + color: Theme.of(context).colorScheme.secondary, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Align( + alignment: Alignment.center, + child: Icon( + Icons.arrow_back_ios, + color: Colors.white, + )), + ), + ), + ), + Text(title), + InkWell( + onTap: () => clear(), + child: Container( + margin: EdgeInsets.all(20), + padding: EdgeInsets.all(05), + height: 40, + width: 80, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + color: Theme.of(context).colorScheme.secondary, + ), + child: Center( + child: Text( + "Clear", + style: TextStyle(color: Colors.white), + ), + ), + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Login/bottomCurve.dart b/lib/Pages/PageWidgets/Login/bottomCurve.dart new file mode 100644 index 0000000..761d33e --- /dev/null +++ b/lib/Pages/PageWidgets/Login/bottomCurve.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/painting.dart'; + +class CurvePainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { +// final Shader linearGradient = LinearGradient( +// colors: [Color(0xffDA44bb), Color(0xff8921aa)], +// ).createShader(Rect.fromLTWH(0.0, 0.0, 200.0, 70.0)); +// var paint = Paint()..shader = linearGradient + var paint = Paint(); + paint.color = Colors.white; + paint.style = PaintingStyle.fill; // Change this to fill + + var path = Path(); + + path.moveTo(0.2, size.height * 0.55); + path.quadraticBezierTo(size.width * 0.4, size.height * 0.8, + size.width * 0.75, size.height * 0.40); + path.quadraticBezierTo(size.width * 0.85, size.height * 0.30, + size.width * 1.0, size.height * 0.3468); + path.lineTo(size.width, size.height); + path.lineTo(0, size.height); + + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} diff --git a/lib/Pages/PageWidgets/Login/email.dart b/lib/Pages/PageWidgets/Login/email.dart new file mode 100644 index 0000000..b4d6cfa --- /dev/null +++ b/lib/Pages/PageWidgets/Login/email.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +import 'package:teso/util/consts.dart'; + +email(BuildContext context, String first, TextEditingController user) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + first, + style: TextStyle( + color: Colors.white, + fontSize: 14.0, + ), + ), + SizedBox( + height: 10.0, + ), + Container( + alignment: Alignment.centerLeft, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10.0), + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 6.0, + offset: Offset(0, 2), + ) + ]), + height: 40.0, + child: TextField( + textAlign: TextAlign.center, + controller: user, + style: TextStyle( + color: Colors.black87, + ), + decoration: InputDecoration( + border: InputBorder.none, + // contentPadding: EdgeInsets.only(top: 14.0), + prefixIcon: Icon( + Icons.mail, + color: accentMain, + ), + hintText: "Enter your email address here", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + ], + ); +} diff --git a/lib/Pages/PageWidgets/Login/emailSignup.dart b/lib/Pages/PageWidgets/Login/emailSignup.dart new file mode 100644 index 0000000..67e5023 --- /dev/null +++ b/lib/Pages/PageWidgets/Login/emailSignup.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +import 'package:teso/util/consts.dart'; + +inputEmail(BuildContext context, TextEditingController user) { + return Container( + width: double.infinity, + height: 50, + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.10), + child: TextField( + autofocus: true, + textAlign: TextAlign.left, + controller: user, + style: TextStyle( + fontSize: 18, + color: Colors.black, + ), + decoration: InputDecoration( + suffix: InkWell( + onTap: () => user.clear(), + child: Icon( + Icons.close_outlined, + color: Colors.grey, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 1.0), + ), + //contentPadding: EdgeInsets.only(top: 8.0), + hintText: "Email Address", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Login/forgotPassword.dart b/lib/Pages/PageWidgets/Login/forgotPassword.dart new file mode 100644 index 0000000..301780a --- /dev/null +++ b/lib/Pages/PageWidgets/Login/forgotPassword.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/ResetPassword.dart'; + +buildForgotPasswordBtn(BuildContext context) { + return Container( + alignment: Alignment.centerRight, + child: TextButton( + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) => ResetPassword(), + ), + style: TextButton.styleFrom( + + padding: EdgeInsets.only(right: 0.0), + ), + child: Text( + 'Forgot Password?', + style: TextStyle( + color: Colors.blue, + // fontWeight: FontWeight.bold, + fontFamily: 'OpenSans', + fontSize: 12, + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Login/password.dart b/lib/Pages/PageWidgets/Login/password.dart new file mode 100644 index 0000000..60081aa --- /dev/null +++ b/lib/Pages/PageWidgets/Login/password.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +passwordBuilder( + BuildContext context, String hint, TextEditingController password) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Password', + style: TextStyle( + color: Colors.black, + fontSize: 13.0, + ), + ), + TextField( + controller: password, + textAlign: TextAlign.left, + obscureText: true, + style: TextStyle( + color: Colors.black, + ), + decoration: InputDecoration( + border: UnderlineInputBorder( + borderSide: BorderSide( + color: Colors.black87, + ), + ), + hintText: hint, + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ], + ); +} diff --git a/lib/Pages/PageWidgets/Login/passwordAlter.dart b/lib/Pages/PageWidgets/Login/passwordAlter.dart new file mode 100644 index 0000000..ff9fbdb --- /dev/null +++ b/lib/Pages/PageWidgets/Login/passwordAlter.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +changePassword( + BuildContext context, String hint, TextEditingController password) { + return Container( + width: double.infinity, + height: 40, + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.10), + child: TextField( + textAlign: TextAlign.left, + controller: password, + obscureText: true, + style: TextStyle( + color: Colors.black, + ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 1.0), + ), + hintText: hint, + hintStyle: TextStyle(color: Colors.grey), + //contentPadding: EdgeInsets.only(top: 8.0), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Login/passwordSignUP.dart b/lib/Pages/PageWidgets/Login/passwordSignUP.dart new file mode 100644 index 0000000..81c0b99 --- /dev/null +++ b/lib/Pages/PageWidgets/Login/passwordSignUP.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +createPassword(BuildContext context, TextEditingController password) { + return Container( + width: double.infinity, + height: 50, + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.10), + child: TextField( + textAlign: TextAlign.left, + controller: password, + obscureText: true, + style: TextStyle( + color: Theme.of(context).primaryColorLight, + ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.blueGrey[400], width: 1.0), + ), + //contentPadding: EdgeInsets.only(top: 8.0), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Login/phonenumber.dart b/lib/Pages/PageWidgets/Login/phonenumber.dart new file mode 100644 index 0000000..10d6b70 --- /dev/null +++ b/lib/Pages/PageWidgets/Login/phonenumber.dart @@ -0,0 +1,61 @@ +import 'package:country_list_pick/country_list_pick.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:teso/util/consts.dart'; + +inputTelNumber( + BuildContext context, Function countryPrefix, TextEditingController user) { + return Container( + width: double.infinity, + height: 80, + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.10), + child: TextField( + maxLength: 9, + maxLengthEnforcement: MaxLengthEnforcement.enforced, + autofocus: true, + keyboardType: TextInputType.number, + textAlign: TextAlign.left, + controller: user, + style: TextStyle( + fontSize: 18, + color: Colors.black, + ), + decoration: InputDecoration( + prefix: Container( + margin: EdgeInsets.only(right: 6), + padding: EdgeInsets.only(right: 5), + decoration: BoxDecoration( + border: Border( + right: BorderSide( + color: accentMain, + width: 2, + ), + ), + ), + child: CountryListPick( + theme: CountryTheme( + isShowFlag: true, isShowCode: true, isShowTitle: false), + //initialSelection: user.currentUser.country, + onChanged: (CountryCode code) => countryPrefix(code)), + ), + // suffix: InkWell( + // onTap: () => user.clear(), + // child: Icon( + // Ionicons.ios_close_circle, + // color: Colors.grey, + // ), + // ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 1.0), + ), + //contentPadding: EdgeInsets.only(top: 8.0), + hintText: "Phone number", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Login/signupusername.dart b/lib/Pages/PageWidgets/Login/signupusername.dart new file mode 100644 index 0000000..121b393 --- /dev/null +++ b/lib/Pages/PageWidgets/Login/signupusername.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/services.dart'; + +usersignup(BuildContext context, TextEditingController user) { + return Container( + width: double.infinity, + height: 50, + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.10), + child: TextField( + textAlign: TextAlign.center, + controller: user, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r"[a-zA-Z0-9]+")), + ], + style: TextStyle( + color: Colors.black, + ), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black12, width: 1.0), + ), + //contentPadding: EdgeInsets.only(top: 8.0), + hintText: "Enter new username here", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Login/username.dart b/lib/Pages/PageWidgets/Login/username.dart new file mode 100644 index 0000000..80289ea --- /dev/null +++ b/lib/Pages/PageWidgets/Login/username.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +username(BuildContext context, String first, TextEditingController user) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + first, + style: TextStyle( + color: Colors.black, + fontSize: 13.0, + ), + ), + TextField( + textAlign: TextAlign.left, + controller: user, + style: TextStyle( + color: Colors.black, + ), + decoration: InputDecoration( + border: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.black87), + ), + //contentPadding: EdgeInsets.only(top: 8.0), + hintText: "Enter your " + first.toLowerCase() + " here", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ], + ); +} diff --git a/lib/Pages/PageWidgets/Login/validation.dart b/lib/Pages/PageWidgets/Login/validation.dart new file mode 100644 index 0000000..b24ca27 --- /dev/null +++ b/lib/Pages/PageWidgets/Login/validation.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; + +class ValidationItem extends StatefulWidget { + ValidationItem(this.title, this.valid); + + final String title; + final bool valid; + + @override + _ValidationItemState createState() => _ValidationItemState(); +} + +class _ValidationItemState extends State + with TickerProviderStateMixin { + AnimationController _controller; + AnimationController _strikeController; + Animation _spaceWidth; + Animation _strikePercent; + + @override + void didUpdateWidget(ValidationItem oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.valid != widget.valid) { + if (widget.valid) { + _playAnimation(true); + } else { + _playAnimation(false); + } + } + } + + @override + void initState() { + super.initState(); + //debugPrint("Init State"); + _controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 150), + ); + + _strikeController = AnimationController( + vsync: this, duration: const Duration(milliseconds: 300)); + + _spaceWidth = Tween(begin: 8, end: 12) + .animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut)); + + _strikePercent = Tween(begin: 0, end: 1).animate( + CurvedAnimation(parent: _strikeController, curve: Curves.easeOut)); + + _spaceWidth.addListener(() { + setState(() {}); + }); + + _strikePercent.addListener(() { + setState(() {}); + }); + } + + Future _playAnimation(bool strikeIn) async { + try { + if (strikeIn) { + _strikeController.forward().orCancel; + } else { + _strikeController.reverse().orCancel; + } + + await _controller.forward().orCancel; + await _controller.reverse().orCancel; + } on TickerCanceled { + // the animation got canceled, probably because we were disposed + } + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), + child: IntrinsicHeight( + child: Row( + children: [ + SizedBox( + width: 40, + ), + Container( + width: 1, + decoration: BoxDecoration(color: Colors.red), + ), + SizedBox( + width: _spaceWidth.value, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: CustomPaint( + foregroundPainter: StrikeThroughPainter(_strikePercent.value), + child: Text(widget.title, + style: _getValidateStyle(widget.valid))), + ), + SizedBox( + width: 16, + ) + ], + ), + ), + ); + } + + TextStyle _getValidateStyle(bool validation) { + return TextStyle( + fontWeight: FontWeight.bold, + color: (validation) ? Colors.black54 : Colors.black87, + fontSize: 18, + fontFamily: 'UbuntuMono', + decoration: null); + } +} + +class StrikeThroughPainter extends CustomPainter { + StrikeThroughPainter(this.percent); + + double percent; + + @override + void paint(Canvas canvas, Size size) { + canvas.drawRect( + Rect.fromLTWH(0, (size.height / 2) - 2, size.width * percent, 4), + Paint()..color = Colors.green); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return false; + } +} diff --git a/lib/Pages/PageWidgets/Login/verificationCode.dart b/lib/Pages/PageWidgets/Login/verificationCode.dart new file mode 100644 index 0000000..26305a2 --- /dev/null +++ b/lib/Pages/PageWidgets/Login/verificationCode.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +import 'package:teso/util/consts.dart'; + +inputCode(BuildContext context, TextEditingController user) { + return Container( + width: double.infinity, + height: 50, + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.10), + child: TextField( + autofocus: true, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + controller: user, + style: TextStyle( + fontSize: 18, + color: Colors.black, + ), + decoration: InputDecoration( + suffix: InkWell( + onTap: () => user.clear(), + child: Icon( + Icons.close_outlined, + color: Colors.grey, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: accentMain, width: 1.0), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 1.0), + ), + //contentPadding: EdgeInsets.only(top: 8.0), + hintText: "Verification Code", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Personal/Empty.dart b/lib/Pages/PageWidgets/Personal/Empty.dart new file mode 100644 index 0000000..a8d55b8 --- /dev/null +++ b/lib/Pages/PageWidgets/Personal/Empty.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +buildEmpty(BuildContext context, Function share) { + return SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height + 40, + child: Column( + children: [ + SizedBox( + height: 50, + ), + Container( + width: 100, + height: 100, + padding: EdgeInsets.all(15), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Theme.of(context).primaryColorLight, + width: 1, + ), + ), + child: ImageIcon( + AssetImage("assets/images/rawLogo.png"), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + top: 10, + ), + child: Center( + child: Text( + "Share contents", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 23, + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width * 0.5, + margin: EdgeInsets.only( + top: 10, + ), + child: Center( + child: Text( + "When you share videos they'll appear on your profile.", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 13, + color: Colors.grey, + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width * 0.5, + margin: EdgeInsets.only( + top: 10, + ), + child: Center( + child: GestureDetector( + onTap: () => share(context), + child: Text( + "Share your first content", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 13, + color: tesoBlue, + ), + ), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Personal/header.dart b/lib/Pages/PageWidgets/Personal/header.dart new file mode 100644 index 0000000..0a14cdb --- /dev/null +++ b/lib/Pages/PageWidgets/Personal/header.dart @@ -0,0 +1,192 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; + +buildProfileHeader( + BuildContext context, + String gold, + String silver, + String username, + String bytes, + String friends, + String fullname, + double tall) { + return Container( + child: Column( + children: [ + SizedBox( + height: 5, + ), + Container( + //color: Theme.of(context).accentColor, + //transform: Matrix4.translationValues(0.0, -40.0, 0.0), + height: SizeConfig.blockSizeHorizontal * 20, + width: SizeConfig.blockSizeHorizontal * 20, + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey[400], + ), + child: bytes == null + ? Center( + child: Text( + username.characters.characterAt(0).toString().toUpperCase(), + ), + ) + : bytes == "null" + ? Center( + child: Text( + username.characters + .characterAt(0) + .toString() + .toUpperCase(), + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ) + : Container( + height: SizeConfig.blockSizeHorizontal * 18, + width: SizeConfig.blockSizeHorizontal * 18, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: CachedNetworkImage( + imageUrl: bytes, + imageBuilder: (context, imageProvider) => FadeInImage( + height: SizeConfig.blockSizeHorizontal * 18, + width: SizeConfig.blockSizeHorizontal * 18, + fit: BoxFit.fill, + image: imageProvider, + placeholder: + AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + //transform: Matrix4.translationValues(0.0, -30.0, 0.0), + child: Center( + child: Text( + fullname, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.8, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + // transform: Matrix4.translationValues(0.0, -25.0, 0.0), + child: Center( + child: Text( + "Friends : " + friends, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.8, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + height: 18, + width: double.infinity, + // transform: Matrix4.translationValues(0.0, -25.0, 0.0), + margin: + EdgeInsets.only(left: MediaQuery.of(context).size.width / 3.5), + child: Center( + child: Row( + children: [ + Image( + image: AssetImage("assets/images/gold1.png"), + ), + Container( + child: Center( + child: Text( + "Gold Coins : ", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.8, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + Container( + margin: EdgeInsets.only(left: 5), + child: Center( + child: Text( + gold, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.8, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + height: 18, + width: double.infinity, + // transform: Matrix4.translationValues(0.0, -25.0, 0.0), + margin: + EdgeInsets.only(left: MediaQuery.of(context).size.width / 3.5), + child: Center( + child: Row( + children: [ + Image( + image: AssetImage("assets/images/silver1.png"), + ), + Container( + child: Center( + child: Text( + "Silver Coins : ", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.8, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + Container( + margin: EdgeInsets.only(left: 5), + child: Center( + child: Text( + silver, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.8, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Personal/scrolls.dart b/lib/Pages/PageWidgets/Personal/scrolls.dart new file mode 100644 index 0000000..21cedd4 --- /dev/null +++ b/lib/Pages/PageWidgets/Personal/scrolls.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; + +buildScrolls(BuildContext context, Function navigation, Color couponsSurround, + Color friendsSurround, Color recentlySurround, Color postsSurround) { + return Container( + height: 30, + width: MediaQuery.of(context).size.width, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: 5.0), + // padding: EdgeInsets.symmetric( + // horizontal: 5.0, + // ), + width: 90, + child: Material( + color: postsSurround, + elevation: 0, + borderRadius: BorderRadius.circular(20.0), + shadowColor: Theme.of(context).backgroundColor, + child: InkWell( + onTap: () => navigation(0), + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Center( + child: Text( + "Posts", + style: TextStyle( + fontSize: 15, + // color: fTextColor1, + ), + ), + ), + ), + ), + ), + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 5.0), + // padding: EdgeInsets.symmetric( + // horizontal: 5.0, + // ), + width: 90, + child: Material( + color: friendsSurround, + elevation: 0, + borderRadius: BorderRadius.circular(20.0), + shadowColor: Theme.of(context).backgroundColor, + child: InkWell( + onTap: () => navigation(1), + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Center( + child: Text( + "Friends", + style: TextStyle( + fontSize: 15, + // color: fTextColor1, + ), + ), + ), + ), + ), + ), + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 5.0), + // padding: EdgeInsets.symmetric( + // horizontal: 5.0, + // ), + child: Material( + color: recentlySurround, + elevation: 0, + borderRadius: BorderRadius.circular(20.0), + shadowColor: Theme.of(context).backgroundColor, + child: InkWell( + onTap: () => navigation(2), + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Center( + child: Text( + "Recently Viewed", + style: TextStyle( + fontSize: 15, + // color: fTextColor1, + ), + ), + ), + ), + ), + ), + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 5.0), + // padding: EdgeInsets.symmetric( + // horizontal: 5.0, + // ), + width: 90, + child: Material( + color: couponsSurround, + elevation: 0, + borderRadius: BorderRadius.circular(20.0), + shadowColor: Theme.of(context).backgroundColor, + child: InkWell( + onTap: () => navigation(3), + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Center( + child: Text( + "Coupons", + style: TextStyle( + fontSize: 15, + // color: fTextColor1, + ), + ), + ), + ), + ), + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Posts/comment.dart b/lib/Pages/PageWidgets/Posts/comment.dart new file mode 100644 index 0000000..4880de3 --- /dev/null +++ b/lib/Pages/PageWidgets/Posts/comment.dart @@ -0,0 +1,137 @@ +import 'dart:typed_data'; +import 'package:flutter/material.dart'; + +buildCommentTile(BuildContext context, bool available, Uint8List bytes, + TextEditingController controller) { + return Container( + width: MediaQuery.of(context).size.width, + height: 170, + margin: EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Material( + elevation: 50.0, + borderRadius: BorderRadius.circular(12.0), + child: InkWell( + onTap: () { + // Navigator.push( + // context, + // PageTransition( + // type: PageTransitionType.rightToLeft, + // child: CommentSection(), + // ), + // ); + }, + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: 10), + height: 30, + width: double.infinity, + child: Center( + child: Text( + "Comments", + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + Divider(), + Container( + padding: EdgeInsets.symmetric(horizontal: 10), + width: double.infinity, + child: Text("Love this post ? Say something!")), + GestureDetector( + onTap: () => print("hello"), + child: Container( + padding: EdgeInsets.symmetric( + vertical: 08, + horizontal: 6, + ), + width: MediaQuery.of(context).size.width, + child: Row( + children: [ + Container( + height: 45.0, + width: 50.0, + margin: EdgeInsets.only(right: 8), + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey, + ), + child: !available + ? Center( + child: Text("B"), + ) + : Image( + fit: BoxFit.fill, + image: MemoryImage(bytes), + ), + ), + Container( + width: MediaQuery.of(context).size.width * 0.55, + height: 50, + child: TextField( + maxLines: 2, + autofocus: false, + enabled: true, + textAlign: TextAlign.start, + controller: controller, + style: TextStyle( + color: Colors.white, + ), + decoration: InputDecoration( + border: InputBorder.none, + //contentPadding: EdgeInsets.only(top: 14.0), + hintText: "Add a comment", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + GestureDetector( + onTap: () { + print("send comment"); + }, + child: Container( + margin: EdgeInsets.all(20), + height: 30, + width: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Align( + alignment: Alignment.center, + child: Icon( + Icons.send, + )), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Posts/posted.dart b/lib/Pages/PageWidgets/Posts/posted.dart new file mode 100644 index 0000000..07ebc6c --- /dev/null +++ b/lib/Pages/PageWidgets/Posts/posted.dart @@ -0,0 +1,40 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/Posts/SpecialPosts.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/consts.dart'; + +buildPosted(BuildContext context, Post post, double height, TesoUser user, + bool addable) { + return Container( + margin: EdgeInsets.all(3), + width: MediaQuery.of(context).size.width * 0.5, + height: MediaQuery.of(context).size.width * height, + color: Colors.black, + child: GestureDetector( + onTap: () { + Navigator.push( + context, + PageTransition( + child: new ViewPost( + postedAd: post, + play: true, + ), + type: PageTransitionType.fade)); + }, + child: CachedNetworkImage( + imageUrl: tesoPostThumb(post.playbackID), + imageBuilder: (context, imageProvider) => FadeInImage( + width: double.infinity, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/blank.jpg"), + ), + errorWidget: (context, url, error) => + Image.asset("assets/images/blank.jpg"), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Posts/user3P_commentTitle.dart b/lib/Pages/PageWidgets/Posts/user3P_commentTitle.dart new file mode 100644 index 0000000..e49f04b --- /dev/null +++ b/lib/Pages/PageWidgets/Posts/user3P_commentTitle.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/util/consts.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +buildPostTile3P(BuildContext context, TesoUser user, Post postedAd) { + return ListTile( + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + )), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: NetworkImage( + serverLocation + "api/pulldp/" + postedAd.publisherID, + ), + placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + title: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: user.username + " ", + style: new TextStyle(fontWeight: FontWeight.bold)), + new TextSpan( + text: postedAd.title, + ), + ], + ), + ), + subtitle: Text( + TimeElapsed.fromDateTime(postedAd.timestamp), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Posts/user_commentTitle.dart b/lib/Pages/PageWidgets/Posts/user_commentTitle.dart new file mode 100644 index 0000000..5f08bdb --- /dev/null +++ b/lib/Pages/PageWidgets/Posts/user_commentTitle.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API%20Clasess/PostedAd.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +buildPostTile(BuildContext context, PostedAd postedAd) { + return Consumer( + builder: (context, value, child) { + return ListTile( + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + )), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: value.currentUser.thumbnail_dp == null + ? Center( + child: Text( + value.currentUser.username.characters + .characterAt(0) + .toString() + .toUpperCase(), + ), + ) + : FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: NetworkImage( + userdpURL + value.currentUser.thumbnail_dp), + placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + title: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: value.currentUser.username + " ", + style: new TextStyle(fontWeight: FontWeight.bold)), + new TextSpan( + text: postedAd.post.title, + ), + ], + ), + ), + subtitle: Text( + TimeElapsed.fromDateTime(postedAd.post.timestamp), + ), + ); + }, + ); +} diff --git a/lib/Pages/PageWidgets/Posts/user_posted.dart b/lib/Pages/PageWidgets/Posts/user_posted.dart new file mode 100644 index 0000000..30797c7 --- /dev/null +++ b/lib/Pages/PageWidgets/Posts/user_posted.dart @@ -0,0 +1,35 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; +import 'package:teso/Pages/Sub_Pages/Posts/UserPosts.dart'; +import 'package:teso/util/consts.dart'; + +buildPosted(BuildContext context, FBPosts post, double height) { + return Container( + margin: EdgeInsets.all(3), + width: MediaQuery.of(context).size.width * 0.5, + height: MediaQuery.of(context).size.width * height, + color: Colors.black, + child: GestureDetector( + onTap: () { + Navigator.of(context).push(new PageRouteBuilder( + pageBuilder: (_, __, ___) => new UserPosts(postedAd: post), + )); + }, + child: CachedNetworkImage( + imageUrl: tesoPostThumb( + post.playbackID, + ), + imageBuilder: (context, imageProvider) => FadeInImage( + width: double.infinity, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/blank.jpg"), + ), + errorWidget: (context, url, error) => Container( + color: Colors.grey[800], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/ProximityList/ActiveDiscount.dart b/lib/Pages/PageWidgets/ProximityList/ActiveDiscount.dart new file mode 100644 index 0000000..b399621 --- /dev/null +++ b/lib/Pages/PageWidgets/ProximityList/ActiveDiscount.dart @@ -0,0 +1,587 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:teso/Pages/productView.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +Positioned buildActiveDiscountCoupon( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + Function dismissImg, + int flag, + Function addImg, + Function swipeRight, + Function swipeLeft, + double worth, + Function calWorth) { + SizeConfig().init(context); + if (worth < img.lowerLimit) { + worth = img.lowerLimit; + } + return new Positioned( + bottom: bottom, + right: flag == 0 + ? right != 0.0 + ? right + : null + : null, + left: flag == 1 + ? right != 0.0 + ? right + : null + : null, + child: new Dismissible( + key: new Key(new Random().toString()), + crossAxisEndOffset: -0.3, + onResize: () {}, + onDismissed: (DismissDirection direction) { + if (direction == DismissDirection.endToStart) + dismissImg(img); + else + addImg(img); + }, + child: new Transform( + alignment: flag == 0 ? Alignment.bottomRight : Alignment.bottomLeft, + transform: new Matrix4.skewX(skew), + child: new RotationTransition( + turns: new AlwaysStoppedAnimation( + flag == 0 ? rotation / 360 : -rotation / 360), + child: Column( + children: [ + new Wrap( + children: [ + InkWell( + onTap: () async { + CouponsHead head = new CouponsHead(); + head.businessId = img.business.businessId; + head.couponId = img.couponId; + head.expiration = img.expiration; + head.lower = img.lowerLimit; + head.upper = img.upperLimit; + head.quantity = img.quantity; + head.state = img.state; + head.targetProduct = img.targetID; + head.type = img.type; + + CouponDetails details = new CouponDetails(); + details.businessId = img.business.businessId; + details.countID = "null"; + details.couponId = img.couponId; + details.expiration = img.expiration; + details.issuer = img.business; + details.lowerLimit = img.lowerLimit; + details.upperLimit = img.upperLimit; + details.quantity = img.quantity; + details.state = img.state; + details.type = img.type; + details.targetProduct = new Product(); + details.targetProduct.businessID = details.businessId; + details.targetProduct.productImage = img.targetImage; + details.targetProduct.productName = img.targetName; + details.targetProduct.unitPrice = img.targetCost; + details.targetProduct.productDesc = img.tagretDescription; + + await Provider.of(context, listen: false) + .viewCoupon(head); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: ProductView( + couponDetails: details, + ), + ), + ); + }, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.65, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.12, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + color: Colors.white, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Material( + elevation: 5.0, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.68, + color: Colors.white, + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * + 0.75), + height: + MediaQuery.of(context).size.height * 0.50, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: img.upperLimit < 50.1 + ? DecorationImage( + fit: BoxFit.fill, + image: AssetImage( + "assets/images/blue.png"), + colorFilter: new ColorFilter.mode( + Color(0xFF0031ed) + .withOpacity(1.0), + BlendMode.multiply), + ) + : DecorationImage( + fit: BoxFit.fill, + image: AssetImage( + "assets/images/redBack.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(1.0), + BlendMode.multiply), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.white, + fontSize: 15), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context) + .size + .width * + 0.125, + ), + child: Column( + children: [ + Text( + img.business.businessName, + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "VOUCHER", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 25, + fontWeight: + FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: + SizeConfig.safeBlockVertical * 10, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.white)), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetCost + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ " + + (img.targetCost - + (img.targetCost * + (worth / 100))) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Image( + width: SizeConfig + .safeBlockHorizontal * + 5, + image: AssetImage( + "assets/images/silver1.png"), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + "SILVER COIN COST : ", + style: TextStyle( + color: Colors.white, + fontWeight: + FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + CouponRateCalculator + .getRate( + img.targetCost * + (worth / + 100)) + .toString(), + style: TextStyle( + color: Colors.white, + fontWeight: + FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + ), + ], + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context) + .size + .width * + 0.15), + child: Text( + img.targetName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * + 0.010, + ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Container( + width: + MediaQuery.of(context).size.width * + 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Pay : GH¢ " + + (img.targetCost - + (img.targetCost * + (worth / + 100))) + .toStringAsFixed(2) + + " only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + img.condition + .replaceAll( + '"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: + Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage( + "assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Center( + child: Wrap( + children: [ + Text( + worth.toString() + "%", + style: TextStyle( + color: Colors.white, + fontSize: 18.5, + fontWeight: FontWeight.bold, + ), + ), + Container( + margin: EdgeInsets.only( + left: 22, + ), + child: Text( + "OFF", + textAlign: TextAlign.end, + style: TextStyle( + color: Colors.white, + fontWeight: + FontWeight.w900, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ), + Container( + // width: SizeConfig.safeBlockHorizontal * 0.1, + height: SizeConfig.safeBlockVertical * 60, + padding: EdgeInsets.all(SizeConfig.safeBlockVertical * 2), + child: Center( + child: new Column( + children: [ + Container( + child: Text( + img.upperLimit.toString() + " %", + textAlign: TextAlign.center, + style: TextStyle( + foreground: Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = Colors.blue, + ), + ), + ), + Container( + height: SizeConfig.safeBlockVertical * 50, + child: new RotatedBox( + quarterTurns: 3, + child: Slider( + value: worth, + min: img.lowerLimit, + max: img.upperLimit, + divisions: 20, + activeColor: accentMain, + inactiveColor: darkAccent, + label: worth.toString() + "%", + onChanged: (double newValue) => + calWorth(newValue), + ), + ), + ), + Container( + child: Text( + img.lowerLimit.toString() + " %", + textAlign: TextAlign.center, + style: TextStyle( + foreground: Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = Colors.blue, + ), + ), + ), + ], + ), + ), + ), + ], + ), + SizedBox( + height: 20, + ), + new Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.red[200], + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => swipeLeft(img), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.red[200], + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Not Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green, + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => swipeRight(img), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ], + ) + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/ProximityList/ActiveFreebie.dart b/lib/Pages/PageWidgets/ProximityList/ActiveFreebie.dart new file mode 100644 index 0000000..71cbd50 --- /dev/null +++ b/lib/Pages/PageWidgets/ProximityList/ActiveFreebie.dart @@ -0,0 +1,470 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'dart:math' as math; +import 'package:teso/util/consts.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:teso/Pages/productView.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; + +Positioned buildActiveFreebieCoupon( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + Function dismissImg, + int flag, + Function addImg, + Function swipeRight, + Function swipeLeft, +) { + SizeConfig().init(context); + return new Positioned( + bottom: bottom, + right: flag == 0 + ? right != 0.0 + ? right + : null + : null, + left: flag == 1 + ? right != 0.0 + ? right + : null + : null, + child: new Dismissible( + key: new Key(new Random().toString()), + crossAxisEndOffset: -0.3, + onResize: () {}, + onDismissed: (DismissDirection direction) { + if (direction == DismissDirection.endToStart) + dismissImg(img); + else + addImg(img); + }, + child: new Transform( + alignment: flag == 0 ? Alignment.bottomRight : Alignment.bottomLeft, + transform: new Matrix4.skewX(skew), + child: new RotationTransition( + turns: new AlwaysStoppedAnimation( + flag == 0 ? rotation / 360 : -rotation / 360), + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.65, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.1, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + color: Colors.white, + ), + child: InkWell( + onTap: () async { + CouponsHead head = new CouponsHead(); + head.businessId = img.business.businessId; + head.couponId = img.couponId; + head.expiration = img.expiration; + head.lower = img.lowerLimit; + head.upper = img.upperLimit; + head.quantity = img.quantity; + head.state = img.state; + head.targetProduct = img.targetID; + head.type = img.type; + + CouponDetails details = new CouponDetails(); + details.businessId = img.business.businessId; + details.countID = "null"; + details.couponId = img.couponId; + details.expiration = img.expiration; + details.issuer = img.business; + details.lowerLimit = img.lowerLimit; + details.upperLimit = img.upperLimit; + details.quantity = img.quantity; + details.state = img.state; + details.type = img.type; + details.targetProduct = new Product(); + details.targetProduct.businessID = details.businessId; + details.targetProduct.productImage = img.targetImage; + details.targetProduct.productName = img.targetName; + details.targetProduct.unitPrice = img.targetCost; + details.targetProduct.productDesc = img.tagretDescription; + + await Provider.of(context, listen: false) + .viewCoupon(head); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: ProductView( + couponDetails: details, + ), + ), + ); + }, + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.50, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/grey.png"), + colorFilter: new ColorFilter.mode( + tesoAsh.withOpacity(1.0), BlendMode.overlay), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * + 0.125, + ), + child: Column( + children: [ + Text( + img.business.businessName, + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "FREEBIE", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: SizeConfig.safeBlockVertical * 10, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.black, width: 2), + ), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetCost.toStringAsFixed(2), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * + 3, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ 0.00", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + SizeConfig.safeBlockHorizontal * + 3, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Image( + width: + SizeConfig.safeBlockHorizontal * + 5, + image: AssetImage( + "assets/images/silver1.png"), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + "Silver Coin Cost : ", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + ), + Container( + margin: EdgeInsets.only( + top: 5, + ), + child: Text( + CouponRateCalculator.getRate(img + .targetCost * + (img.lowerLimit / 100)) + .toString(), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .safeBlockHorizontal * + 3, + ), + ), + ), + ], + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: + MediaQuery.of(context).size.width * + 0.15), + child: Text( + img.targetName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * 0.025, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Pay : GH¢ 0.00 only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + img.condition + .replaceAll('"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: 70, + height: 70, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 70, + height: 70, + //padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: + AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Transform.rotate( + angle: -math.pi / 4, + child: Container( + margin: EdgeInsets.symmetric( + vertical: 20, horizontal: 5), + height: 10, + child: Text( + "FREE!!!", + style: TextStyle( + color: Colors.white, + fontSize: 16.2, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + SizedBox( + height: 20, + ), + new Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.red, + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => swipeLeft(img), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.red, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Not Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green, + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => swipeRight(img), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ], + ) + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/ProximityList/DummyDiscount.dart b/lib/Pages/PageWidgets/ProximityList/DummyDiscount.dart new file mode 100644 index 0000000..1ea7bb2 --- /dev/null +++ b/lib/Pages/PageWidgets/ProximityList/DummyDiscount.dart @@ -0,0 +1,310 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/util/consts.dart'; + +Positioned buildDummyDiscountCoupon( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + String discount, + double selectedDiscount) { + return new Positioned( + bottom: 60 + bottom, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + decoration: BoxDecoration( + border: Border.all(color: tesoGold, width: 0.5), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + color: Colors.white, + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: img.upperLimit < 50.1 + ? DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/blue.png"), + colorFilter: new ColorFilter.mode( + Color(0xFF0031ed).withOpacity(1.0), + BlendMode.multiply), + ) + : DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/redBack.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(1.0), BlendMode.multiply), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle(color: Colors.white, fontSize: 15), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.business.businessName, + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "VOUCHER", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.2, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all(color: Colors.white)), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetCost.toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ " + + (img.targetCost - + (img.targetCost * + (img.lowerLimit / 100))) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Text( + "SILVER COIN COST : ", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Image( + width: 20, + image: + AssetImage("assets/images/silver1.png"), + ), + Text( + CouponRateCalculator.getRate(img.targetCost * + (img.lowerLimit / 100)) + .toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.15), + child: Text( + img.targetName, + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.02, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Price : GH¢ " + + (img.targetCost * (img.lowerLimit / 100)) + .toStringAsFixed(2) + + " only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + img.condition.replaceAll('"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), BlendMode.overlay), + ), + ), + child: Center( + child: Wrap( + children: [ + Text( + img.lowerLimit.toString() + "%", + style: TextStyle( + color: Colors.white, + fontSize: 18.5, + fontWeight: FontWeight.bold, + ), + ), + Container( + margin: EdgeInsets.only( + left: 22, + ), + child: Text( + "OFF", + textAlign: TextAlign.end, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w900, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/ProximityList/DummyFreebie.dart b/lib/Pages/PageWidgets/ProximityList/DummyFreebie.dart new file mode 100644 index 0000000..8dd01f7 --- /dev/null +++ b/lib/Pages/PageWidgets/ProximityList/DummyFreebie.dart @@ -0,0 +1,289 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'dart:math' as math; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; + +Positioned buildDummyFreebieCoupon( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + String discount, + double selectedDiscount) { + return new Positioned( + bottom: 60.0 + bottom, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75), + decoration: BoxDecoration( + border: Border.all(color: tesoBlue, width: 0.5), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + color: Colors.white, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20), + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75), + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/grey.png"), + colorFilter: new ColorFilter.mode( + tesoAsh.withOpacity(1.0), BlendMode.overlay), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.business.businessName, + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "FREEBIE", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.2, + padding: EdgeInsets.all(4.5), + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 2), + ), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetCost.toStringAsFixed(2), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ 0.00", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + new Wrap( + direction: Axis.horizontal, + children: [ + Text( + "Silver Coin Cost : ", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Image( + width: 20, + image: + AssetImage("assets/images/silver1.png"), + ), + Text( + CouponRateCalculator.getRate(img.targetCost * + (img.lowerLimit / 100)) + .toString(), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.15), + child: Text( + img.targetName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.023, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Price : GH¢ 0.00 only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + + img.condition.replaceAll('"', ""), + ), + ), + ], + ), + ), + ), + Container( + width: 70, + height: 70, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 70, + height: 70, + //padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), BlendMode.overlay), + ), + ), + child: Transform.rotate( + angle: -math.pi / 4, + child: Container( + margin: + EdgeInsets.symmetric(vertical: 20, horizontal: 5), + height: 10, + child: Text( + "FREE!!!", + style: TextStyle( + color: Colors.white, + fontSize: 16.2, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Recently Viewed/viewedItem.dart b/lib/Pages/PageWidgets/Recently Viewed/viewedItem.dart new file mode 100644 index 0000000..90f2d5a --- /dev/null +++ b/lib/Pages/PageWidgets/Recently Viewed/viewedItem.dart @@ -0,0 +1,176 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; + +buildRecentItem( + BuildContext context, CouponDetails couponDetails, Function purchase) { + bool active; + if (couponDetails.quantity > 0) { + active = true; + } else { + active = false; + } + return Container( + width: MediaQuery.of(context).size.width, + height: 150, + child: Row( + children: [ + Container( + width: 95, + height: 95, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: CachedNetworkImage( + imageUrl: productURL + couponDetails.targetProduct.productImage, + imageBuilder: (context, imageProvider) => Image( + width: MediaQuery.of(context).size.width * 0.28, + height: 110, + fit: BoxFit.fill, + image: imageProvider, + ), + ), + ), + ), + Container( + padding: EdgeInsets.only(top: 10, left: 10), + height: 150, + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + height: 30, + child: Stack( + children: [ + Container( + width: double.infinity, + height: double.infinity, + child: Align( + alignment: Alignment.centerLeft, + child: Text( + couponDetails.issuer.businessName, + style: TextStyle( + fontSize: 12.5, + color: Colors.grey, + ), + textAlign: TextAlign.left, + ), + ), + ), + Container( + width: double.infinity, + height: double.infinity, + child: Align( + alignment: Alignment.centerRight, + child: Text( + couponDetails.lowerLimit.toString() + + " - " + + couponDetails.upperLimit.toString() + + "%", + textAlign: TextAlign.left, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + child: Text( + couponDetails.targetProduct.productName, + textAlign: TextAlign.left, + ), + ), + Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.35), + child: Text( + "Cost : GH¢ " + + couponDetails.targetProduct.unitPrice.toString(), + textAlign: TextAlign.left, + ), + ), + Visibility( + visible: active, + child: Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.25), + child: Align( + alignment: Alignment.centerRight, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: accentMain, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + ), + onPressed: () async { + CouponsHead couponHeads = new CouponsHead(); + couponHeads.businessId = couponDetails.businessId; + couponHeads.couponId = couponDetails.couponId; + couponHeads.expiration = couponDetails.expiration; + couponHeads.lower = couponDetails.lowerLimit; + couponHeads.upper = couponDetails.upperLimit; + couponHeads.type = couponDetails.type; + couponHeads.quantity = 1; + couponHeads.targetProduct = + couponDetails.targetProduct.productID; + purchase(context, couponHeads, + couponDetails.targetProduct.unitPrice); + }, + child: Text("Acquire Coupon"), + ), + ), + ), + ), + Visibility( + visible: !active, + child: Container( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.25), + child: Align( + alignment: Alignment.centerRight, + child: Text( + "Sold Out", + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + ), + ), + Divider(), + ], + ), + ), + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Redeem/activeCard.dart b/lib/Pages/PageWidgets/Redeem/activeCard.dart new file mode 100644 index 0000000..42f8bb3 --- /dev/null +++ b/lib/Pages/PageWidgets/Redeem/activeCard.dart @@ -0,0 +1,275 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:teso/util/consts.dart'; + +Positioned activeCard( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + Function dismissImg, + int flag, + Function addImg, + Function swipeRight, + Function swipeLeft, + String discount, + double selectedDiscount) { + Size screenSize = MediaQuery.of(context).size; + return new Positioned( + bottom: 45.0 + bottom, + right: flag == 0 + ? right != 0.0 + ? right + : null + : null, + left: flag == 1 + ? right != 0.0 + ? right + : null + : null, + child: new Dismissible( + key: new Key(new Random().toString()), + crossAxisEndOffset: -0.3, + onResize: () { + //print("here"); + // setState(() { + // var i = data.removeLast(); + + // data.insert(0, i); + // }); + }, + onDismissed: (DismissDirection direction) { +// _swipeAnimation(); + if (direction == DismissDirection.endToStart) + dismissImg(img); + else + addImg(img); + }, + child: new Transform( + alignment: flag == 0 ? Alignment.bottomRight : Alignment.bottomLeft, + //transform: null, + transform: new Matrix4.skewX(skew), + //..rotateX(-math.pi / rotation), + child: new RotationTransition( + turns: new AlwaysStoppedAnimation( + flag == 0 ? rotation / 360 : -rotation / 360), + child: new Hero( + tag: img.couponId, + child: new GestureDetector( + // onTap: () { + // // Navigator.push( + // // context, + // // new MaterialPageRoute( + // // builder: (context) => new DetailPage(type: img))); + // Navigator.of(context).push(new PageRouteBuilder( + // pageBuilder: (_, __, ___) => new DetailPage(type: img), + // )); + // }, + child: Column( + children: [ + new Container( + margin: EdgeInsets.all(40), + width: screenSize.width * 0.93, + height: MediaQuery.of(context).size.height / 2, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Material( + elevation: 10.0, + borderRadius: BorderRadius.circular(12.0), + // shadowColor: Theme.of(context).backgroundColor, + child: Container( + height: MediaQuery.of(context).size.height / 2, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [accentMain, darkAccent], + //stops: [0.1, 0.4, 0.7, 0.8], + ), + ), + child: Column( + children: [ + Stack( + children: [ + Align( + alignment: Alignment.topCenter, + child: Container( + width: double.infinity, + padding: + EdgeInsets.symmetric(horizontal: 15), + margin: + EdgeInsets.symmetric(vertical: 15), + child: Center( + child: Text( + "Teso Discount Coupon", + style: TextStyle( + fontSize: 22, + color: Colors.white, + fontFamily: 'WickedGrit', + ), + ), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + decoration: BoxDecoration( + color: Colors.white38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + padding: EdgeInsets.all(10), + margin: + EdgeInsets.only(right: 20, top: 35), + child: Text( + selectedDiscount.toString() + "% off", + style: TextStyle(fontSize: 18), + ), + ), + ), + ], + ), + Container( + margin: EdgeInsets.all(8), + width: double.infinity, + child: Center( + child: Text( + img.targetName, + style: TextStyle( + fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + margin: EdgeInsets.all(8), + width: double.infinity, + child: Center( + child: Text( + "Item Original Price : GH¢ " + + img.targetCost.toString(), + style: TextStyle( + fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + margin: EdgeInsets.all(10), + width: double.infinity, + child: Center( + child: Text( + "Item Discounted Price : GH¢ " + discount, + style: TextStyle( + fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + width: double.infinity, + child: new Wrap( + direction: Axis.horizontal, + children: [ + Align( + alignment: Alignment.bottomCenter, + child: Container( + child: Image( + height: 80, + image: AssetImage( + "assets/images/tesoCouponInsignia.png"), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + new Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + new ElevatedButton(style:ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.red, + padding: new EdgeInsets.all(10.0),), + onPressed: swipeLeft, + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.red, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Not Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + new ElevatedButton(style:ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green[200], + padding: new EdgeInsets.all(10.0),), + onPressed: swipeRight, + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green[200], + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ], + ) + ], + ), + ), + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Redeem/detail.dart b/lib/Pages/PageWidgets/Redeem/detail.dart new file mode 100644 index 0000000..09f7e67 --- /dev/null +++ b/lib/Pages/PageWidgets/Redeem/detail.dart @@ -0,0 +1,308 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; + +class DetailPage extends StatefulWidget { + final CouponDetails type; + const DetailPage({Key key, this.type}) : super(key: key); + @override + _DetailPageState createState() => new _DetailPageState(type: type); +} + +enum AppBarBehavior { normal, pinned, floating, snapping } + +class _DetailPageState extends State with TickerProviderStateMixin { + AnimationController _containerController; + Animation width; + Animation heigth; + CouponDetails type; + _DetailPageState({this.type}); + double _appBarHeight = 256.0; + AppBarBehavior _appBarBehavior = AppBarBehavior.pinned; + + @override + void initState() { + _containerController = new AnimationController( + duration: new Duration(milliseconds: 2000), vsync: this); + super.initState(); + width = new Tween( + begin: 200.0, + end: 220.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth = new Tween( + begin: 400.0, + end: 400.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth.addListener(() { + setState(() { + if (heigth.isCompleted) {} + }); + }); + _containerController.forward(); + } + + @override + void dispose() { + _containerController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + timeDilation = 0.7; + return new Theme( + data: new ThemeData( + brightness: Brightness.light, + // primaryColor: const Color.fromRGBO(106, 94, 175, 1.0), + platform: Theme.of(context).platform, + ), + child: new Container( + width: width.value, + height: heigth.value, + //color: const Color.fromRGBO(106, 94, 175, 1.0), + child: new Hero( + tag: type.couponId, + child: new Card( + color: Colors.transparent, + child: new Container( + alignment: Alignment.center, + width: width.value, + height: heigth.value, + decoration: new BoxDecoration( + color: Colors.white, + borderRadius: new BorderRadius.circular(10.0), + ), + child: new Stack( + alignment: AlignmentDirectional.bottomCenter, + children: [ + new CustomScrollView( + shrinkWrap: false, + slivers: [ + new SliverAppBar( + elevation: 0.0, + forceElevated: true, + leading: new IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: new Icon( + Icons.arrow_back, + color: Theme.of(context).colorScheme.secondary, + size: 30.0, + ), + ), + expandedHeight: _appBarHeight, + pinned: _appBarBehavior == AppBarBehavior.pinned, + floating: _appBarBehavior == AppBarBehavior.floating || + _appBarBehavior == AppBarBehavior.snapping, + snap: _appBarBehavior == AppBarBehavior.snapping, + backgroundColor: Theme.of(context).backgroundColor, + flexibleSpace: new FlexibleSpaceBar( + title: Container( + // padding: EdgeInsets.all(5), + width: MediaQuery.of(context).size.width, + height: 40, + decoration: new BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + child: Align( + alignment: Alignment.bottomCenter, + child: new Text( + type.targetProduct.productName, + // style: TextStyle( + // // color: Theme.of(context).accentColor), + ), + ), + ), + //), + background: new Stack( + fit: StackFit.expand, + children: [ + new Container( + width: width.value, + height: _appBarHeight, + decoration: new BoxDecoration( + image: DecorationImage( + image: NetworkImage(productURL + + type.targetProduct.productImage), + fit: BoxFit.fill, + ), + ), + ), + ], + ), + ), + ), + new SliverList( + delegate: new SliverChildListDelegate([ + new Container( + color: Colors.white, + child: new Padding( + padding: const EdgeInsets.all(35.0), + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Container( + padding: new EdgeInsets.only(bottom: 20.0), + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.white, + border: new Border( + bottom: new BorderSide( + color: Colors.black12))), + child: new Wrap( + direction: Axis.horizontal, + children: [ + new Row( + children: [ + new Icon( + Icons.price_change_sharp, + color: Theme.of(context) + .colorScheme + .secondary, + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text("GH¢ " + + type.targetProduct.unitPrice + .toString()), + ) + ], + ), + new Padding( + padding: const EdgeInsets.all(8.0), + child: new Text( + type.worth.toString() + "% off"), + ), + new Row( + children: [ + new Icon( + Icons.store, + color: Theme.of(context) + .colorScheme + .secondary, + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text( + type.issuer.businessName), + ) + ], + ), + new Row( + children: [ + new Icon( + Icons.map, + color: Theme.of(context) + .colorScheme + .secondary, + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text("15 MILES"), + ) + ], + ), + ], + ), + ), + new Padding( + padding: const EdgeInsets.only( + top: 16.0, bottom: 8.0), + child: new Text( + "Product Description", + style: new TextStyle( + fontWeight: FontWeight.bold), + ), + ), + new Text(type.targetProduct.productDesc), + new Container( + margin: new EdgeInsets.only(top: 25.0), + padding: new EdgeInsets.only( + top: 5.0, bottom: 10.0), + height: 120.0, + decoration: new BoxDecoration( + color: Colors.white, + border: new Border( + top: new BorderSide( + color: Colors.black12), + ), + ), + ), + new Container( + height: 100.0, + ) + ], + ), + ), + ), + ]), + ), + ], + ), + new Container( + width: 600.0, + height: 80.0, + decoration: new BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + alignment: Alignment.center, + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + new TextButton( + onPressed: () {}, + child: new Container( + height: 60.0, + width: 130.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.red, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Not Interested", + style: new TextStyle(color: Colors.white), + ), + )), + new TextButton( + onPressed: () {}, + child: new Container( + height: 60.0, + width: 130.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: accentMain, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Interested", + style: new TextStyle(color: Colors.white), + ), + )) + ], + )) + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/PageWidgets/Redeem/discountCoupon.dart b/lib/Pages/PageWidgets/Redeem/discountCoupon.dart new file mode 100644 index 0000000..4f13547 --- /dev/null +++ b/lib/Pages/PageWidgets/Redeem/discountCoupon.dart @@ -0,0 +1,274 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; + +Positioned buildCoupon( + CouponDetails img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + Function dismissImg, + int flag, + Function addImg, + Function swipeRight, + Function swipeLeft, + String discount, +) { + img.productCost = img.targetProduct.unitPrice; + Size screenSize = MediaQuery.of(context).size; + return new Positioned( + bottom: 45.0 + bottom, + right: flag == 0 + ? right != 0.0 + ? right + : null + : null, + left: flag == 1 + ? right != 0.0 + ? right + : null + : null, + child: new Dismissible( + key: new Key(new Random().toString()), + crossAxisEndOffset: -0.3, + onResize: () {}, + onDismissed: (DismissDirection direction) { +// _swipeAnimation(); + if (direction == DismissDirection.endToStart) + dismissImg(img); + else + addImg(img); + }, + child: new Transform( + alignment: flag == 0 ? Alignment.bottomRight : Alignment.bottomLeft, + //transform: null, + transform: new Matrix4.skewX(skew), + //..rotateX(-math.pi / rotation), + child: new RotationTransition( + turns: new AlwaysStoppedAnimation( + flag == 0 ? rotation / 360 : -rotation / 360), + child: Column( + children: [ + InkWell( + onTap: () async { + CouponsHead head = new CouponsHead(); + head.businessId = img.issuer.businessId; + head.couponId = img.couponId; + head.expiration = img.expiration; + head.lower = img.lowerLimit; + head.upper = img.upperLimit; + head.quantity = img.quantity; + head.state = img.state; + head.targetProduct = img.targetProduct.productID; + head.type = img.type; + await Provider.of(context, listen: false) + .viewCoupon(head); + }, + child: new Container( + margin: EdgeInsets.all(40), + width: screenSize.width * 0.95, + height: MediaQuery.of(context).size.height / 2, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Material( + elevation: 10.0, + borderRadius: BorderRadius.circular(12.0), + // shadowColor: Theme.of(context).backgroundColor, + child: Container( + height: MediaQuery.of(context).size.height / 2, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFfea404), + Color(0xFFfd0a35), + ], + //stops: [0.1, 0.4, 0.7, 0.8], + ), + ), + child: Column( + children: [ + Stack( + children: [ + Align( + alignment: Alignment.topCenter, + child: Container( + width: double.infinity, + padding: + EdgeInsets.symmetric(horizontal: 15), + margin: EdgeInsets.symmetric(vertical: 15), + child: Center( + child: Text( + "Teso Discount Coupon", + style: TextStyle( + fontSize: 22, + color: Colors.white, + fontFamily: 'WickedGrit', + ), + ), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + decoration: BoxDecoration( + color: Colors.white38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + padding: EdgeInsets.all(10), + margin: EdgeInsets.only(right: 20, top: 35), + child: Text( + img.worth.toString() + "% off", + style: TextStyle(fontSize: 18), + ), + ), + ), + ], + ), + Container( + margin: EdgeInsets.all(8), + width: double.infinity, + child: Center( + child: Text( + img.targetProduct.productName, + style: TextStyle( + fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + margin: EdgeInsets.all(8), + width: double.infinity, + child: Center( + child: Text( + "Item Original Price : " + + img.targetProduct.unitPrice.toString(), + style: TextStyle( + fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + margin: EdgeInsets.all(10), + width: double.infinity, + child: Center( + child: Text( + "Item Discounted Price : " + discount, + style: TextStyle( + fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + width: double.infinity, + child: new Wrap( + direction: Axis.horizontal, + children: [ + Align( + alignment: Alignment.bottomCenter, + child: Container( + child: Image( + height: 80, + image: AssetImage( + "assets/images/tesoCouponInsignia.png"), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ), + new Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + new ElevatedButton(style:ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.red, + padding: new EdgeInsets.all(10.0),), + onPressed: swipeLeft, + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.red, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Not Interested", + style: new TextStyle(color: Colors.white), + ), + ), + ), + new ElevatedButton(style:ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green[200], + padding: new EdgeInsets.all(10.0),), + onPressed: swipeRight, + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green[200], + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Redeem", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ], + ) + ], + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Redeem/dummyCard.dart b/lib/Pages/PageWidgets/Redeem/dummyCard.dart new file mode 100644 index 0000000..4087b30 --- /dev/null +++ b/lib/Pages/PageWidgets/Redeem/dummyCard.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; + +Positioned cardDemoDummy( + ProximityCoupon img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + String discount, + double selectedDiscount) { + Size screenSize = MediaQuery.of(context).size; + return new Positioned( + bottom: 100.0 + bottom, + child: new Container( + margin: EdgeInsets.all(40), + width: screenSize.width * 0.95, + height: MediaQuery.of(context).size.height / 2, + decoration: BoxDecoration( + border: Border.all( + color: Colors.white, + width: 1, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Material( + elevation: 4.0, + borderRadius: BorderRadius.circular(12.0), + child: new Container( + alignment: Alignment.center, + width: screenSize.width / 1.2 + cardWidth, + height: screenSize.height / 1.7, + decoration: new BoxDecoration( + color: new Color.fromRGBO(121, 114, 173, 1.0), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFfd0a35), + Color(0xFFfea404), + ], + //stops: [0.1, 0.4, 0.7, 0.8], + ), + borderRadius: new BorderRadius.circular(30.0), + ), + child: new Container( + width: screenSize.width / 1.2 + cardWidth, + height: screenSize.height / 2.1, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFfd0a35), + Color(0xFFfea404), + ], + //stops: [0.1, 0.4, 0.7, 0.8], + ), + ), + child: Column( + children: [ + Stack( + children: [ + Align( + alignment: Alignment.topCenter, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 15), + margin: EdgeInsets.symmetric(vertical: 15), + child: Center( + child: Text( + "Teso Discount Coupon", + style: TextStyle( + fontSize: 22, + color: Colors.white, + fontFamily: 'WickedGrit', + ), + ), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + decoration: BoxDecoration( + color: Colors.white38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + padding: EdgeInsets.all(10), + margin: EdgeInsets.only(right: 20, top: 35), + child: Text( + selectedDiscount.toString() + "% off", + style: TextStyle(fontSize: 18), + ), + ), + ), + ], + ), + Container( + margin: EdgeInsets.all(8), + width: double.infinity, + child: Center( + child: Text( + img.targetName, + style: TextStyle(fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + margin: EdgeInsets.all(8), + width: double.infinity, + child: Center( + child: Text( + "Item Original Price : " + + "GH¢ " + + img.targetCost.toString(), + style: TextStyle(fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + margin: EdgeInsets.all(10), + width: double.infinity, + child: Center( + child: Text( + "Item Discounted Price : " + "GH¢ " + discount, + style: TextStyle(fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + width: double.infinity, + child: new Wrap( + direction: Axis.horizontal, + children: [ + Align( + alignment: Alignment.bottomCenter, + child: Container( + child: Image( + height: 80, + image: AssetImage( + "assets/images/tesoCouponInsignia.png"), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Redeem/scannedDummy.dart b/lib/Pages/PageWidgets/Redeem/scannedDummy.dart new file mode 100644 index 0000000..b4ce0c3 --- /dev/null +++ b/lib/Pages/PageWidgets/Redeem/scannedDummy.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; + +Positioned cardDemoDummy( + CouponDetails img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + String discount, + double selectedDiscount) { + Size screenSize = MediaQuery.of(context).size; + return new Positioned( + bottom: 100.0 + bottom, + child: new Container( + margin: EdgeInsets.all(40), + width: screenSize.width * 0.95, + height: MediaQuery.of(context).size.height / 2, + decoration: BoxDecoration( + border: Border.all( + color: Colors.white, + width: 1, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Material( + elevation: 4.0, + borderRadius: BorderRadius.circular(12.0), + child: new Container( + alignment: Alignment.center, + width: screenSize.width / 1.2 + cardWidth, + height: screenSize.height / 1.7, + decoration: new BoxDecoration( + color: new Color.fromRGBO(121, 114, 173, 1.0), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFfd0a35), + Color(0xFFfea404), + ], + //stops: [0.1, 0.4, 0.7, 0.8], + ), + borderRadius: new BorderRadius.circular(30.0), + ), + child: new Container( + width: screenSize.width / 1.2 + cardWidth, + height: screenSize.height / 2.1, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFfd0a35), + Color(0xFFfea404), + ], + //stops: [0.1, 0.4, 0.7, 0.8], + ), + ), + child: Column( + children: [ + Stack( + children: [ + Align( + alignment: Alignment.topCenter, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 15), + margin: EdgeInsets.symmetric(vertical: 15), + child: Center( + child: Text( + "Teso Discount Coupon", + style: TextStyle( + fontSize: 22, + color: Colors.white, + fontFamily: 'WickedGrit', + ), + ), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + decoration: BoxDecoration( + color: Colors.white38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + padding: EdgeInsets.all(10), + margin: EdgeInsets.only(right: 20, top: 35), + child: Text( + selectedDiscount.toString() + "% off", + style: TextStyle(fontSize: 18), + ), + ), + ), + ], + ), + Container( + margin: EdgeInsets.all(8), + width: double.infinity, + child: Center( + child: Text( + img.targetProduct.productName, + style: TextStyle(fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + margin: EdgeInsets.all(8), + width: double.infinity, + child: Center( + child: Text( + "Item Original Price : " + + "GH¢ " + + img.targetProduct.unitPrice.toString(), + style: TextStyle(fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + margin: EdgeInsets.all(10), + width: double.infinity, + child: Center( + child: Text( + "Item Discounted Price : " + "GH¢ " + discount, + style: TextStyle(fontSize: 18, color: Colors.white), + ), + ), + ), + Container( + width: double.infinity, + child: new Wrap( + direction: Axis.horizontal, + children: [ + Align( + alignment: Alignment.bottomCenter, + child: Container( + child: Image( + height: 80, + image: AssetImage( + "assets/images/tesoCouponInsignia.png"), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Settings/AccountSettings.dart b/lib/Pages/PageWidgets/Settings/AccountSettings.dart new file mode 100644 index 0000000..8296dd7 --- /dev/null +++ b/lib/Pages/PageWidgets/Settings/AccountSettings.dart @@ -0,0 +1,153 @@ +import 'package:country_list_pick/country_list_pick.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/AccountSettings/changePassword.dart'; +import 'package:teso/Pages/Sub_Pages/AccountSettings/genderPicker.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/providers/user_provider.dart'; + +class AccountSettings extends StatefulWidget { + @override + _AccountSettingsState createState() => _AccountSettingsState(); +} + +class _AccountSettingsState extends State { + bool show = false; + + @override + void initState() { + SharedPreferences.getInstance().then((value) { + setState(() { + show = value.getBool("password"); + }); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + UserProvider userProvider = Provider.of(context); + return Consumer( + builder: (BuildContext context, UserProvider user, Widget child) { + return Container( + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Wrap( + children: [ + new Container( + margin: EdgeInsets.only( + left: 20.0, + top: 20.0, + bottom: 12.0, + ), + child: Row( + children: [ + Container( + child: InkWell( + onTap: () => Navigator.pop(context), + child: Icon(Icons.arrow_back_ios), + ), + ), + Expanded( + child: Container( + width: double.infinity, + child: Center( + child: Text( + "Account Settings", + style: TextStyle( + fontSize: 18.0, + ), + ), + ), + ), + ), + ], + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + trailing: CountryListPick( + theme: CountryTheme( + isShowFlag: true, isShowCode: true, isShowTitle: true), + initialSelection: user.currentUser.country, + onChanged: (CountryCode code) { + TesoUser newuser = user.currentUser; + newuser.country = code.dialCode; + Provider.of(context, listen: false) + .updateUser(newuser); + }, + ), + title: new Text('Country'), + ), + new ListTile( + trailing: new Wrap( + spacing: 10, + children: [ + new Text( + userProvider.currentUser.gender, + style: TextStyle( + fontSize: 16, + ), + ), + Icon(Icons.arrow_forward_ios) + ], + ), + title: new Text('Gender'), + onTap: () async { + int orientation = 5; + switch (userProvider.currentUser.gender) { + case "Male": + orientation = 0; + break; + case "Female": + orientation = 1; + break; + case "Other": + orientation = 2; + break; + } + orientation = await Navigator.push( + context, + PageTransition( + child: Gender( + value: orientation, + ), + type: PageTransitionType.leftToRight, + ), + ); + TesoUser newuser = user.currentUser; + Provider.of(context, listen: false) + .updateUser(newuser); + }), + Visibility( + visible: show, + child: new ListTile( + trailing: new Icon(Icons.arrow_forward_ios), + title: new Text('Change password'), + onTap: () => Navigator.push( + context, + PageTransition( + child: ChangePassword(), + type: PageTransitionType.leftToRight, + ), + )), + ), + // new ListTile( + // trailing: new Icon(Icons.arrow_forward_ios), + // title: new Text('Delete account'), + // subtitle: new Text( + // "Delete your account and account data not reversible"), + // onTap: () => {}, + // ), + ], + ), + ), + ); + }); + } +} diff --git a/lib/Pages/PageWidgets/Settings/EditProfile.dart b/lib/Pages/PageWidgets/Settings/EditProfile.dart new file mode 100644 index 0000000..df09fbd --- /dev/null +++ b/lib/Pages/PageWidgets/Settings/EditProfile.dart @@ -0,0 +1,440 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:camera/camera.dart'; +import 'package:teso/GeneralWidgets/inputTextLimited.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Picture/TakeDP.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:image_cropper/image_cropper.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/GeneralWidgets/inputText.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; + +class EditProfile extends StatefulWidget { + final TesoUser userProfile; + final List connectedCameras; + const EditProfile({Key key, this.userProfile, this.connectedCameras}) + : super(key: key); + @override + _EditProfileState createState() => + _EditProfileState(userProfile: this.userProfile); +} + +enum AppState { + free, + picked, + cropped, +} + +class _EditProfileState extends State { + TesoUser userProfile; + _EditProfileState({this.userProfile}); + AppState state; + bool imageAvailable = false; + File _image; + String thumb; + bool passed = false; + bool failed = false; + final picker = ImagePicker(); + TextEditingController username = new TextEditingController(); + TextEditingController firstname = new TextEditingController(); + TextEditingController surname = new TextEditingController(); + TextEditingController telephone = new TextEditingController(); + TextEditingController email = new TextEditingController(); + TextEditingController address = new TextEditingController(); + TextEditingController description = new TextEditingController(); + + _imageSelection(context) async { + final pickedFile = await Navigator.push( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: TakeDP(connectedCameras: widget.connectedCameras), + )); + + if (pickedFile != null) { + _image = File(pickedFile.path); + setState(() { + _cropImage(); + imageAvailable = true; + }); + } + } + + Future _cropImage() async { + File croppedFile = await ImageCropper.cropImage( + sourcePath: _image.path, + cropStyle: CropStyle.circle, + aspectRatioPresets: Platform.isAndroid + ? [CropAspectRatioPreset.original] + : [CropAspectRatioPreset.original], + androidUiSettings: AndroidUiSettings( + toolbarTitle: 'Teso Profile Picture', + toolbarColor: Theme.of(context).colorScheme.secondary, + toolbarWidgetColor: Colors.white, + initAspectRatio: CropAspectRatioPreset.original, + lockAspectRatio: false), + iosUiSettings: IOSUiSettings( + title: 'Teso Profile Picture', + )); + if (croppedFile != null) { + setState(() { + _image = croppedFile; + state = AppState.cropped; + imageAvailable = true; + }); + } else { + _clearImage(); + } + } + + void _clearImage() { + setState(() { + _image = null; + state = AppState.free; + imageAvailable = false; + }); + } + + Future _updateUser() async { + try { + String thumbnail; + if (_image != null) { + List imageBytes = await _image.readAsBytes(); + thumbnail = base64Encode(imageBytes); + } else if (thumb != null) { + thumbnail = thumb; + } + TesoUser updateuser = new TesoUser(); + updateuser.userGUID = userProfile.userGUID; + updateuser.username = username.text; + updateuser.firstname = firstname.text; + updateuser.lastname = surname.text; + updateuser.address = address.text; + updateuser.description = description.text; + updateuser.email = email.text; + updateuser.phonenumber = telephone.text; + updateuser.gold = userProfile.gold; + updateuser.silver = userProfile.silver; + updateuser.friends = userProfile.friends; + updateuser.displaypicture = thumbnail; + updateuser.thumbnail_dp = userProfile.thumbnail_dp; + updateuser.country = userProfile.country; + updateuser.gender = userProfile.gender; + updateuser.dateOfBirth = userProfile.dateOfBirth; + print(updateuser.toString()); + UserProvider userProvider = + Provider.of(context, listen: false); + userProvider.updateUser(updateuser); + + if (mounted) { + setState(() { + passed = true; + }); + } + + Future.delayed(const Duration(seconds: 5), () { + if (passed) { + if (mounted) { + setState(() { + passed = false; + }); + } + } + }); + } catch (e) { + print(e); + if (mounted) { + setState(() { + failed = true; + }); + } + + Future.delayed(const Duration(seconds: 5), () { + if (failed) { + if (mounted) { + setState(() { + failed = false; + }); + } + } + }); + } + } + + @override + void initState() { + username.text = userProfile.username.toString(); + firstname.text = userProfile.firstname; + surname.text = userProfile.lastname; + address.text = userProfile.address; + email.text = userProfile.email; + telephone.text = userProfile.phonenumber; + description.text = userProfile.description; + thumb = userProfile.thumbnail_dp; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + height: MediaQuery.of(context).size.height * 0.9, + child: Scaffold( + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Wrap( + children: [ + new Container( + margin: EdgeInsets.only( + left: 20.0, + top: 10.0, + bottom: 10.0, + ), + child: Row( + children: [ + Container( + child: InkWell( + onTap: () => Navigator.pop(context), + child: Icon(Icons.close), + ), + ), + Expanded( + child: Container( + width: double.infinity, + child: Center( + child: Text( + "Edit Profile", + style: TextStyle( + fontSize: 17.0, + ), + ), + ), + ), + ), + new Container( + margin: EdgeInsets.symmetric( + vertical: 0.0, horizontal: 20), + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: + Theme.of(context).colorScheme.secondary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + ), + onPressed: () => _updateUser(), + child: Text("Done"), + ), + ), + ) + ], + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + Visibility( + visible: passed, + child: Container( + margin: EdgeInsets.only(top: 10, bottom: 10), + color: Colors.green, + width: double.infinity, + height: 20, + child: Center( + child: Text( + 'Profile updated successfully', + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + ), + Visibility( + visible: failed, + child: Container( + margin: EdgeInsets.only(top: 10, bottom: 10), + color: Colors.red, + width: double.infinity, + height: 20, + child: Center( + child: Text( + 'An error occurred', + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + ), + Center( + child: InkWell( + onTap: () => _imageSelection(context), + child: Container( + //color: Theme.of(context).accentColor, + height: 130, + width: 130, + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey[400], + ), + child: _image == null && thumb == null + ? Center( + child: Text( + username.text.characters + .characterAt(0) + .toString() + .toUpperCase(), + ), + ) + : _image == null && thumb == "null" + ? Center( + child: Text( + username.text.characters + .characterAt(0) + .toString() + .toUpperCase(), + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ) + : _image != null + ? Container( + height: 130, + width: 130, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + placeholder: AssetImage( + "assets/images/tesoDP/dp1.png"), + height: 130, + width: 130, + fit: BoxFit.fill, + image: FileImage( + File( + _image.path, + ), + ), + ), + ), + ) + : Container( + height: 130, + width: 130, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + height: 130, + width: 130, + fit: BoxFit.fill, + image: + NetworkImage(userdpURL + thumb), + placeholder: AssetImage( + "assets/images/tesoDP/dp1.png"), + ), + ), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20.0, vertical: 20.0), + child: Divider(), + ), + new ListTile( + enabled: false, + title: buildInputContainer( + context, username, "Username", false), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + title: buildInputContainer( + context, firstname, "Firstname", true), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + title: + buildInputContainer(context, surname, "Lastname", true), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + title: buildInputContainerLimit( + context, description, "About You", true, 120), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + title: buildInputContainer( + context, email, "Contact (email)", true), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + title: buildInputContainer( + context, telephone, "Contact (phone number)", true), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + title: + buildInputContainer(context, address, "Address", true), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/PageWidgets/Settings/privacy.dart b/lib/Pages/PageWidgets/Settings/privacy.dart new file mode 100644 index 0000000..4238d4a --- /dev/null +++ b/lib/Pages/PageWidgets/Settings/privacy.dart @@ -0,0 +1,30 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class Privacy extends StatefulWidget { + @override + _PrivacyState createState() => _PrivacyState(); +} + +class _PrivacyState extends State { + @override + void initState() { + super.initState(); + if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text("Teso Terms and Privacy"), + ), + body: WebView( + initialUrl: serverwebPrivacy, + ), + ); + } +} diff --git a/lib/Pages/PageWidgets/Settings/terms.dart b/lib/Pages/PageWidgets/Settings/terms.dart new file mode 100644 index 0000000..5d5e93e --- /dev/null +++ b/lib/Pages/PageWidgets/Settings/terms.dart @@ -0,0 +1,62 @@ +import 'dart:io'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class TermsUse extends StatefulWidget { + @override + _TermsUseState createState() => _TermsUseState(); +} + +class _TermsUseState extends State { + bool loading = true; + @override + void initState() { + super.initState(); + if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); + } + + loaded() { + setState(() { + loading = false; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text( + "End User Agreement", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + ), + body: Stack( + children: [ + WebView( + // javascriptMode: JavascriptMode.unrestricted, + initialUrl: serverwebTerms, + onPageFinished: (url) => loaded(), + ), + loading + ? Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 20, + ), + ), + ) + : Container(), + ], + ), + ); + } +} diff --git a/lib/Pages/PageWidgets/Settings/webViews.dart b/lib/Pages/PageWidgets/Settings/webViews.dart new file mode 100644 index 0000000..9ed7457 --- /dev/null +++ b/lib/Pages/PageWidgets/Settings/webViews.dart @@ -0,0 +1,30 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class WebViews extends StatefulWidget { + @override + _WebViewsState createState() => _WebViewsState(); +} + +class _WebViewsState extends State { + @override + void initState() { + super.initState(); + if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text("Teso Support"), + ), + body: WebView( + initialUrl: serverwebHelp, + ), + ); + } +} diff --git a/lib/Pages/PageWidgets/Third Person Profile/Empty.dart b/lib/Pages/PageWidgets/Third Person Profile/Empty.dart new file mode 100644 index 0000000..e5d2b66 --- /dev/null +++ b/lib/Pages/PageWidgets/Third Person Profile/Empty.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; + +buildEmpty3P(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + SizedBox( + height: 50, + ), + Container( + width: 100, + height: 100, + padding: EdgeInsets.all(15), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Theme.of(context).primaryColorLight, + width: 1, + ), + ), + child: ImageIcon( + AssetImage("assets/images/rawLogo.png"), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + top: 10, + ), + child: Center( + child: Text( + "No content", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 23, + ), + ), + ), + ), + SizedBox( + height: 10, + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/Third Person Profile/header.dart b/lib/Pages/PageWidgets/Third Person Profile/header.dart new file mode 100644 index 0000000..79bbe94 --- /dev/null +++ b/lib/Pages/PageWidgets/Third Person Profile/header.dart @@ -0,0 +1,305 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; + +buildHead( + {BuildContext context, + TesoUser user, + String posts, + List friends, + List following, + bool relation, + bool pending, + bool approveRequest, + bool loading, + Function unFriend, + Function approve, + Function cancelRequest, + Function decline}) { + return Container( + color: Theme.of(context).backgroundColor, + width: MediaQuery.of(context).size.width, + child: Column( + children: [ + Container( + child: Container( + height: 70.0, + width: 70.0, + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey, + ), + child: user.thumbnail_dp == null + ? Center( + child: Text( + user.username.characters + .characterAt(0) + .toString() + .toUpperCase(), + ), + ) + : Container( + width: 70, + height: 70, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(40.0), + topRight: Radius.circular(40.0), + bottomLeft: Radius.circular(40), + bottomRight: Radius.circular(40), + ), + child: CachedNetworkImage( + imageUrl: userdpURL + user.thumbnail_dp, + imageBuilder: (context, imageProvider) => Image( + height: 70, + width: 70, + fit: BoxFit.fill, + image: imageProvider, + ), + ), + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + bottom: 10, + top: 10, + ), + child: Center( + child: Text( + user.firstname + " " + user.lastname, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ), + user.description != null + ? Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + bottom: 10, + ), + child: Text( + user.description != null ? user.description : "", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + color: Colors.grey, + ), + ), + ) + : Container(), + Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.symmetric(horizontal: 5, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + new Wrap( + direction: Axis.vertical, + children: [ + Container( + child: Text( + "Posts", + style: TextStyle(color: Colors.grey, fontSize: 12), + ), + ), + Container( + child: Text( + posts != null ? posts.toString() : "", + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + // new Wrap( + // direction: Axis.vertical, + // children: [ + // Container( + // child: Text( + // "Following", + // style: TextStyle(color: Colors.grey, fontSize: 12), + // ), + // ), + // Container( + // child: Text( + // following != null ? following.length.toString() : "", + // style: TextStyle( + // fontWeight: FontWeight.bold, + // ), + // ), + // ), + // ], + // ), + new Wrap( + direction: Axis.vertical, + children: [ + Container( + child: Text( + "Friends", + style: TextStyle(color: Colors.grey, fontSize: 12), + ), + ), + Container( + child: Text( + friends != null ? friends.length.toString() : "", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ], + ), + ), + // Container( + // width: MediaQuery.of(context).size.width, + // height: 45, + // margin: EdgeInsets.only(bottom: 10), + // child: Wrap( + // spacing: 10, + // alignment: WrapAlignment.center, + // direction: Axis.horizontal, + // children: [ + // Visibility( + // visible: loading, + // child: Center( + // child: CupertinoActivityIndicator( + // animating: true, + // radius: 15, + // ), + // ), + // ), + // Visibility( + // visible: approveRequest, + // child: RaisedGradientButton( + // child: Text( + // "Accept Request", + // style: TextStyle(color: Colors.white, fontSize: 14), + // ), + // gradient: LinearGradient( + // begin: Alignment.topCenter, + // end: Alignment.bottomCenter, + // colors: [ + // accentMain, + // darkAccent, + // //darkAccent, + // ], + // ), + // onPressed: approve, + // width: 120, + // height: 45, + // ), + // ), + // Visibility( + // visible: approveRequest, + // child: RaisedGradientButton( + // child: Text( + // "Decline Request", + // style: TextStyle(color: Colors.white, fontSize: 14), + // ), + // gradient: LinearGradient( + // begin: Alignment.topCenter, + // end: Alignment.bottomCenter, + // colors: [ + // accentMain, + // darkAccent, + // //darkAccent, + // ], + // ), + // onPressed: decline, + // width: 120, + // height: 45, + // ), + // ), + // Visibility( + // visible: relation, + // child: Container( + // width: 95, + // height: 40, + // //padding: EdgeInsets.all(10), + // //margin: EdgeInsets.all(7), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.all(Radius.circular(30.0)), + // border: Border.all( + // color: accentMain, + // )), + // child: InkWell( + // child: Center( + // child: Text( + // "Unfriend", + // style: TextStyle(color: accentMain, fontSize: 15), + // ), + // ), + // onTap: unFriend, + // ), + // ), + // ), + // Visibility( + // visible: pending, + // child: RaisedGradientButton( + // child: Text( + // "Cancel Request", + // style: TextStyle(color: Colors.white, fontSize: 14), + // ), + // gradient: LinearGradient( + // begin: Alignment.topCenter, + // end: Alignment.bottomCenter, + // colors: [ + // accentMain, + // darkAccent, + // //darkAccent, + // ], + // ), + // onPressed: cancelRequest, + // width: 120, + // height: 45, + // ), + // ), + // Visibility( + // visible: relation, + // child: RaisedGradientButton( + // child: Text( + // "Message", + // style: TextStyle(color: Colors.white, fontSize: 14), + // ), + // gradient: LinearGradient( + // begin: Alignment.topCenter, + // end: Alignment.bottomCenter, + // colors: [ + // accentMain, + // darkAccent, + // ], + // ), + // onPressed: () => Navigator.pushReplacement( + // context, + // PageTransition( + // child: ChatScreen( + // user: user, + // ), + // type: PageTransitionType.fade, + // ), + // ), + // width: 100, + // height: 45, + // ), + // ), + // ], + // ), + // ) + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/Uploads/Pending.dart b/lib/Pages/PageWidgets/Uploads/Pending.dart new file mode 100644 index 0000000..6cf1acf --- /dev/null +++ b/lib/Pages/PageWidgets/Uploads/Pending.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:loading_indicator/loading_indicator.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/Uploading.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; + +uploadTile(BuildContext context, Uploading pendingUpload) { + return Card( + child: Container( + width: MediaQuery.of(context).size.width, + height: 70, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + //constraints: BoxConstraints(minHeight: 80, maxHeight: 150), + width: 60, + height: 60, + child: pendingUpload != null && pendingUpload.thumbnail != null + ? Image( + width: double.infinity, + height: double.infinity, + fit: BoxFit.cover, + image: MemoryImage(base64Decode(pendingUpload.thumbnail)), + gaplessPlayback: true, + ) + : Image( + width: double.infinity, + height: double.infinity, + fit: BoxFit.cover, + image: AssetImage( + "assets/images/blank.jpg", + ), + gaplessPlayback: true, + ), + ), + pendingUpload.isProcessing + ? new Wrap( + direction: Axis.vertical, + children: [ + Text("Preparing post.."), + LoadingIndicator( + indicatorType: Indicator.ballPulse, + + /// Required, The loading type of the widget + colors: [tesoAsh, tesoBlue, tesoGold], + + /// Optional, The color collections + strokeWidth: 2, + + /// Optional, The stroke of the line, only applicable to widget which contains line + ) + ], + ) + : new Wrap( + direction: Axis.vertical, + children: [ + Text("Processing.."), + Container( + width: MediaQuery.of(context).size.width * 0.65, + height: 10, + child: LinearProgressIndicator( + value: pendingUpload.pending, + backgroundColor: tesoAsh, + valueColor: new AlwaysStoppedAnimation(tesoBlue), + ), + ), + ], + ), + pendingUpload.isProcessing + ? Container() + : Container( + width: 40, + height: 40, + child: InkWell( + onTap: () => + Provider.of(context, listen: false) + .cancelUpload(pendingUpload), + child: Icon(Icons.close), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/WalkIn Coupons/ActiveDiscount.dart b/lib/Pages/PageWidgets/WalkIn Coupons/ActiveDiscount.dart new file mode 100644 index 0000000..44e1482 --- /dev/null +++ b/lib/Pages/PageWidgets/WalkIn Coupons/ActiveDiscount.dart @@ -0,0 +1,359 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:teso/Pages/productView.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/RedeemingCoupon.dart'; + +buildActiveDiscountCoupon( + CouponDetails img, + double cardWidth, + BuildContext context, +) { + img.productCost = img.targetProduct.unitPrice; + return Container( + width: (MediaQuery.of(context).size.width * 0.80) - cardWidth, + height: MediaQuery.of(context).size.height * 0.68, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topLeft: Radius.circular(30), + topRight: Radius.circular(30), + ), + ), + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.10), + child: Column( + children: [ + InkWell( + onTap: () async { + CouponsHead head = new CouponsHead(); + head.businessId = img.issuer.businessId; + head.couponId = img.couponId; + head.expiration = img.expiration; + head.lower = img.lowerLimit; + head.upper = img.upperLimit; + head.quantity = img.quantity; + head.state = img.state; + head.targetProduct = img.targetProduct.productID; + head.type = img.type; + await Provider.of(context, listen: false) + .viewCoupon(head); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: ProductView( + couponDetails: img, + ), + ), + ); + }, + child: Container( + width: (MediaQuery.of(context).size.width * 0.80) - cardWidth, + height: MediaQuery.of(context).size.height * 0.65, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.1, + ), + child: Material( + elevation: 5.0, + child: Container( + width: (MediaQuery.of(context).size.width * 0.80) - cardWidth, + height: MediaQuery.of(context).size.height * 0.68, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topLeft: Radius.circular(30), + topRight: Radius.circular(30), + ), + color: Colors.white, + ), + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.80) - + cardWidth, + height: MediaQuery.of(context).size.height * 0.50, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: img.upperLimit < 50.1 + ? DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/blue.png"), + colorFilter: new ColorFilter.mode( + Color(0xFF0031ed).withOpacity(1.0), + BlendMode.multiply), + ) + : DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/redBack.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(1.0), + BlendMode.multiply), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.white, fontSize: 15), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.issuer.businessName.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "VOUCHER", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.18, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + border: Border.all(color: Colors.white)), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetProduct.unitPrice.toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ " + + (img.targetProduct.unitPrice * + (img.lowerLimit / 100)) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: + MediaQuery.of(context).size.width * 0.15), + child: Text( + img.targetProduct.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.025, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Price : GH¢ " + + (img.targetProduct.unitPrice * + (img.lowerLimit / 100)) + .toStringAsFixed(2) + + " only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + img.condition, + ), + ), + ], + ), + ), + ), + Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Center( + child: Wrap( + children: [ + Text( + img.lowerLimit.toString() + "%", + style: TextStyle( + color: Colors.white, + fontSize: 18.5, + fontWeight: FontWeight.bold, + ), + ), + Container( + margin: EdgeInsets.only( + left: 22, + ), + child: Text( + "OFF", + textAlign: TextAlign.end, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w900, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + SizedBox( + height: 20, + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: tesoGold, + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => Navigator.pushReplacement( + context, + PageTransition( + child: RedeemingCoupon( + couponDetails: img, + ), + type: PageTransitionType.fade)), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: tesoGold, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Redeem", + style: new TextStyle(color: Colors.white), + ), + ), + ) + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/WalkIn Coupons/ActiveFreebie.dart b/lib/Pages/PageWidgets/WalkIn Coupons/ActiveFreebie.dart new file mode 100644 index 0000000..aba4676 --- /dev/null +++ b/lib/Pages/PageWidgets/WalkIn Coupons/ActiveFreebie.dart @@ -0,0 +1,318 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'dart:math' as math; +import 'package:teso/util/consts.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:teso/Pages/productView.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/RedeemingCoupon.dart'; + +buildActiveFreebieCoupon( + CouponDetails img, double cardWidth, BuildContext context) { + img.productCost = img.targetProduct.unitPrice; + return Container( + width: (MediaQuery.of(context).size.width * 0.80) - cardWidth, + height: MediaQuery.of(context).size.height * 0.68, + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.10), + child: Column( + children: [ + InkWell( + onTap: () async { + CouponsHead head = new CouponsHead(); + head.businessId = img.issuer.businessId; + head.couponId = img.couponId; + head.expiration = img.expiration; + head.lower = img.lowerLimit; + head.upper = img.upperLimit; + head.quantity = img.quantity; + head.state = img.state; + head.targetProduct = img.targetProduct.productID; + head.type = img.type; + await Provider.of(context, listen: false) + .viewCoupon(head); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: ProductView( + couponDetails: img, + ), + ), + ); + }, + child: Container( + width: (MediaQuery.of(context).size.width * 0.80) - cardWidth, + height: MediaQuery.of(context).size.height * 0.65, + margin: EdgeInsets.only( + left: MediaQuery.of(context).size.width * 0.1, + ), + child: Material( + elevation: 5.0, + child: Container( + width: (MediaQuery.of(context).size.width * 0.80) - cardWidth, + height: MediaQuery.of(context).size.height * 0.65, + color: Colors.white, + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.80) - + cardWidth, + height: MediaQuery.of(context).size.height * 0.50, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/grey.png"), + colorFilter: new ColorFilter.mode( + tesoAsh.withOpacity(1.0), BlendMode.overlay), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.issuer.businessName.toUpperCase(), + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "FREEBIE", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.18, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + border: + Border.all(color: Colors.black, width: 2), + ), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetProduct.unitPrice + .toStringAsFixed(2), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ 0.00", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: + MediaQuery.of(context).size.width * 0.15), + child: Text( + img.targetProduct.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.025, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Price : GH¢ 0.00 only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + img.condition, + ), + ), + ], + ), + ), + ), + Container( + width: 70, + height: 70, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 70, + height: 70, + //padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), + BlendMode.overlay), + ), + ), + child: Transform.rotate( + angle: -math.pi / 4, + child: Container( + margin: EdgeInsets.symmetric( + vertical: 20, horizontal: 5), + height: 10, + child: Text( + "FREE!!!", + style: TextStyle( + color: Colors.white, + fontSize: 16.2, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + SizedBox( + height: 20, + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: tesoGold, + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => Navigator.pushReplacement( + context, + PageTransition( + child: RedeemingCoupon( + couponDetails: img, + ), + type: PageTransitionType.fade)), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Redeem", + style: new TextStyle(color: Colors.white), + ), + ), + ) + ], + ), + ); +} diff --git a/lib/Pages/PageWidgets/WalkIn Coupons/DummyDiscount.dart b/lib/Pages/PageWidgets/WalkIn Coupons/DummyDiscount.dart new file mode 100644 index 0000000..fc3c9fe --- /dev/null +++ b/lib/Pages/PageWidgets/WalkIn Coupons/DummyDiscount.dart @@ -0,0 +1,271 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/util/consts.dart'; + +Positioned buildDummyDiscountCoupon( + CouponDetails img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + String discount, + double selectedDiscount) { + img.productCost = img.targetProduct.unitPrice; + return new Positioned( + bottom: 100.0 + bottom, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75) - cardWidth, + // height: MediaQuery.of(context).size.height * 0.70, + decoration: BoxDecoration( + border: Border.all(color: tesoGold, width: 0.5), + ), + child: Container( + width: (MediaQuery.of(context).size.width * 0.75) - cardWidth, + // height: MediaQuery.of(context).size.height * 0.68, + color: Colors.white, + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75) - cardWidth, + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: img.upperLimit < 50.1 + ? DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/blue.png"), + colorFilter: new ColorFilter.mode( + Color(0xFF0031ed).withOpacity(1.0), + BlendMode.multiply), + ) + : DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/redBack.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(1.0), BlendMode.multiply), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle(color: Colors.white, fontSize: 15), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.issuer.businessName.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "VOUCHER", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.18, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + border: Border.all(color: Colors.white)), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetProduct.unitPrice.toString(), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ " + + (img.targetProduct.unitPrice * + (img.lowerLimit / 100)) + .toStringAsFixed(2), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.15), + child: Text( + img.targetProduct.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.02, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Price : GH¢ " + + (img.targetProduct.unitPrice * + (img.lowerLimit / 100)) + .toStringAsFixed(2) + + " only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + img.condition, + ), + ), + ], + ), + ), + ), + Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 65, + height: 65, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), BlendMode.overlay), + ), + ), + child: Center( + child: Wrap( + children: [ + Text( + img.lowerLimit.toString() + "%", + style: TextStyle( + color: Colors.white, + fontSize: 18.5, + fontWeight: FontWeight.bold, + ), + ), + Container( + margin: EdgeInsets.only( + left: 22, + ), + child: Text( + "OFF", + textAlign: TextAlign.end, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w900, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/Pages/PageWidgets/WalkIn Coupons/DummyFreebie.dart b/lib/Pages/PageWidgets/WalkIn Coupons/DummyFreebie.dart new file mode 100644 index 0000000..f084969 --- /dev/null +++ b/lib/Pages/PageWidgets/WalkIn Coupons/DummyFreebie.dart @@ -0,0 +1,245 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:math' as math; + +Positioned buildDummyFreebieCoupon( + CouponDetails img, + double bottom, + double right, + double left, + double cardWidth, + double rotation, + double skew, + BuildContext context, + String discount, + double selectedDiscount) { + img.productCost = img.targetProduct.unitPrice; + return new Positioned( + bottom: 80.0 + bottom, + child: Container( + width: (MediaQuery.of(context).size.width * 0.75) - cardWidth, + decoration: BoxDecoration( + border: Border.all(color: tesoBlue, width: 0.5), + color: Colors.white, + ), + child: Column( + children: [ + Container( + width: (MediaQuery.of(context).size.width * 0.75) - cardWidth, + height: MediaQuery.of(context).size.height * 0.55, + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/grey.png"), + colorFilter: new ColorFilter.mode( + tesoAsh.withOpacity(1.0), BlendMode.overlay), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Image( + width: 30, + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + SizedBox( + width: 5, + ), + Text( + "TESO", + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.125, + ), + child: Column( + children: [ + Text( + img.issuer.businessName.toUpperCase(), + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + vertical: 10, + ), + child: Center( + child: Text( + "FREEBIE", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 25, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + height: MediaQuery.of(context).size.width * 0.18, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 2), + ), + child: Column( + children: [ + Text( + "ORIGINAL PRICE : GH¢ " + + img.targetProduct.unitPrice.toStringAsFixed(2), + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + Text( + "DISCOUNTED PRICE : GH¢ 0.00", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.15), + child: Text( + img.targetProduct.productName.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.023, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.4, + height: 70, + padding: EdgeInsets.only( + top: 5, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + child: Text( + "Price : GH¢ 0.00 only", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ), + img.condition == 'none' + ? Container() + : Container( + child: Text( + "Condition : " + img.condition, + ), + ), + ], + ), + ), + ), + Container( + width: 70, + height: 70, + padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + border: Border.all( + color: Colors.red, + ), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(80), + topRight: Radius.circular(80), + bottomLeft: Radius.circular(80), + bottomRight: Radius.circular(80), + ), + ), + child: Container( + width: 70, + height: 70, + //padding: EdgeInsets.all(2.5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + border: Border.all(color: Colors.red), + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage("assets/images/red.png"), + colorFilter: new ColorFilter.mode( + Colors.red.withOpacity(0.5), BlendMode.overlay), + ), + ), + child: Transform.rotate( + angle: -math.pi / 4, + child: Container( + margin: + EdgeInsets.symmetric(vertical: 20, horizontal: 5), + height: 10, + child: Text( + "FREE!!!", + style: TextStyle( + color: Colors.white, + fontSize: 16.2, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/Personnal.dart b/lib/Pages/Personnal.dart new file mode 100644 index 0000000..bc78797 --- /dev/null +++ b/lib/Pages/Personnal.dart @@ -0,0 +1,184 @@ +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Personal/header.dart'; +import 'package:teso/Pages/Sub_Pages/PersonalSub/Friends.dart'; +import 'package:teso/Pages/Sub_Pages/PersonalSub/Posts.dart'; +import 'package:teso/Pages/Sub_Pages/PersonalSub/Recently.dart'; + +import 'package:teso/providers/user_provider.dart'; +import 'package:provider/provider.dart'; + +class Personnal extends StatefulWidget { + final Function showSettings; + const Personnal({Key key, this.showSettings}) : super(key: key); + @override + _PersonnalState createState() => + _PersonnalState(showSettings: this.showSettings); +} + +enum AppBarBehavior { normal, pinned, floating, snapping } + +class _PersonnalState extends State with TickerProviderStateMixin { + TesoUser currentUser; + Function showSettings; + double scale; + _PersonnalState({this.showSettings}); + double _appBarHeightSpecial = 275.0; + RefreshController _refreshController = + RefreshController(initialRefresh: false); + TabController tabsController; + + Widget _randomHeightWidgets(BuildContext context) { + return Consumer( + builder: (BuildContext context, UserProvider user, Widget child) { + user.getCurrentUser(); + currentUser = user.currentUser; + if (user.currentUser == null || currentUser == null) { + return Container(); + } else { + String dp = userdpURL + currentUser.thumbnail_dp; + + return buildProfileHeader( + context, + currentUser.gold, + currentUser.silver, + currentUser.username, + dp, + user.friends != null + ? user.friends.length.toString() + : currentUser.friends, + currentUser.firstname + " " + currentUser.lastname, + _appBarHeightSpecial); + } + }); + } + + void _onRefresh() async { + Provider.of(context, listen: false).getUserInformation(); + + switch (tabsController.index) { + case 0: + await Provider.of(context, listen: false).pullAds(); + Provider.of(context, listen: false).loadFriends(); + Provider.of(context, listen: false).getCoupons(); + break; + case 1: + await Provider.of(context, listen: false).loadFriends(); + Provider.of(context, listen: false).getCoupons(); + Provider.of(context, listen: false).pullAds(); + break; + default: + break; + } + _refreshController.refreshCompleted(); + } + + @override + void initState() { + super.initState(); + tabsController = new TabController(length: 3, initialIndex: 0, vsync: this); + } + + @override + void dispose() { + tabsController.dispose(); + _refreshController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + extendBody: true, + appBar: PreferredSize( + preferredSize: Size.fromHeight(SizeConfig.blockSizeHorizontal * 73.1), + child: Column( + children: [ + AppBar( + backgroundColor: Colors.transparent, + toolbarHeight: 50, + automaticallyImplyLeading: false, + title: Consumer( + builder: + (BuildContext context, UserProvider user, Widget child) { + currentUser = user.currentUser; + if (user.currentUser == null || currentUser == null) { + return Container(); + } else { + return Text(user.currentUser.username, + style: TextStyle( + color: Theme.of(context).primaryColorLight, + )); + } + }, + ), + actions: [ + Container( + width: 50, + child: Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.only(top: 10), + height: 30.0, + width: 45.0, + //color: Color.fromRGBO(0, 0, 0, 0.4), + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: Color.fromRGBO(0, 0, 0, 0.4), + //border: Border.all(color: Colors.black, width: 2.0), + ), + child: InkWell( + onTap: () => showSettings(context), + child: Icon( + Icons.settings, + color: Colors.white, + ), + ), + ), + ), + ) + ], + ), + _randomHeightWidgets(context), + TabBar( + labelStyle: TextStyle( + fontSize: SizeConfig.safeBlockHorizontal * 3, + ), + controller: tabsController, + tabs: [ + Tab( + text: "Posts", + ), + Tab( + text: "Friends", + ), + Tab( + text: "Recently Viewed", + ), + ], + ), + ], + ), + ), + body: SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: ClassicHeader(), + controller: _refreshController, + onRefresh: _onRefresh, + child: TabBarView( + controller: tabsController, + children: [ + Posts(), + Friends(), + Recently(), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Picture/PictureReview.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Picture/PictureReview.dart new file mode 100644 index 0000000..7b27dfc --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Picture/PictureReview.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'dart:io'; + +class PictureReview extends StatefulWidget { + final image; + + const PictureReview({Key key, this.image}) : super(key: key); + @override + _PictureReviewState createState() => _PictureReviewState(); +} + +class _PictureReviewState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack(children: [ + Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Image.file( + File( + widget.image, + ), + fit: BoxFit.fill, + ), + ), + Align( + alignment: Alignment.topLeft, + child: InkWell( + onTap: () { + Navigator.pop(context, false); + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.08, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 50, + width: 50, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), shape: BoxShape.circle), + child: Icon( + Icons.close, + color: Colors.red, + size: 40, + ), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: InkWell( + onTap: () { + Navigator.pop(context, true); + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.08, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 50, + width: 50, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), shape: BoxShape.circle), + child: Icon( + Icons.check, + color: Colors.green, + size: 40, + ), + ), + ), + ), + ]), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Picture/TakeDP.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Picture/TakeDP.dart new file mode 100644 index 0000000..973cd92 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Picture/TakeDP.dart @@ -0,0 +1,314 @@ +import 'package:flutter/material.dart'; +import 'package:camera/camera.dart'; + +import 'package:teso/util/consts.dart'; +import 'package:image_picker/image_picker.dart'; + +// ignore: must_be_immutable +class TakeDP extends StatefulWidget { + List connectedCameras; + + TakeDP({Key key, this.connectedCameras}) : super(key: key); + + @override + _TakeDPState createState() => _TakeDPState(); +} + +class _TakeDPState extends State with TickerProviderStateMixin { + CameraController _controller; + int selectedCamera = 0; + bool flash = false; + bool frontFlash = false; + double _currentScale = 1.0; + double _baseScale = 1.0; + double _minAvailableZoom; + double _maxAvailableZoom; + int _pointers = 0; + final picker = ImagePicker(); + + setCamera(int camera) async { + _controller = + CameraController(widget.connectedCameras[camera], ResolutionPreset.max); + _controller.initialize().then((_) { + _controller.setFocusMode(FocusMode.auto); + if (!mounted) { + return; + } + setState(() {}); + }); + } + + void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + final offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + _controller.setExposurePoint(offset); + _controller.setFocusPoint(offset); + } + + @override + void initState() { + if (widget.connectedCameras == null || + widget.connectedCameras.length == 0) { + availableCameras().then((value) { + widget.connectedCameras = value; + setCamera(0); + }); + } else { + setCamera(0); + } + + super.initState(); + } + + sayCheese() async { + try { + final image = await _controller.takePicture(); + + if (image != null) Navigator.pop(context, image); + } catch (e) { + print(e); + } + } + + void _handleScaleStart(ScaleStartDetails details) { + _baseScale = _currentScale; + } + + Future _handleScaleUpdate(ScaleUpdateDetails details) async { + // When there are not exactly two fingers on screen don't scale + if (_pointers != 2) { + return; + } + + _currentScale = (_baseScale * details.scale) + .clamp(_minAvailableZoom, _maxAvailableZoom); + + await _controller.setZoomLevel(_currentScale); + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (_controller == null || !_controller.value.isInitialized) { + return Container(); + } else { + return Scaffold( + body: Stack( + children: [ + cameraWidget(context), + flashWidget(context), + Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.06, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + child: InkWell( + onTap: () { + if (flash && + _controller.description.lensDirection == + CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.off); + setState(() { + flash = false; + }); + } else { + setState(() { + flash = false; + frontFlash = false; + }); + } + selectedCamera++; + if (selectedCamera < widget.connectedCameras.length) { + setCamera(selectedCamera); + } else { + selectedCamera = 0; + setCamera(selectedCamera); + } + }, + child: Icon( + Icons.video_call, + color: Colors.white, + size: 40, + ), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.07, + vertical: MediaQuery.of(context).size.width * 0.25, + ), + child: InkWell( + onTap: () { + try { + if (!flash && + _controller.description.lensDirection == + CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.torch); + setState(() { + flash = true; + }); + } else if (!flash && + _controller.description.lensDirection == + CameraLensDirection.front) { + setState(() { + flash = true; + frontFlash = true; + }); + } else if (flash && + _controller.description.lensDirection == + CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.off); + setState(() { + flash = false; + }); + } else { + setState(() { + flash = false; + frontFlash = false; + }); + } + } catch (e) {} + }, + child: Icon( + flash ? Icons.flash_on : Icons.flash_off, + color: flash ? tesoGold : Colors.white, + size: 30, + ), + ), + ), + ), + Align( + alignment: Alignment.topLeft, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.08, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 35, + width: 35, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + shape: BoxShape.circle), + child: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: InkWell( + onTap: sayCheese, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.white, + width: 3, + ), + ), + child: Icon( + Icons.camera_alt, + color: Colors.white, + size: 40, + ), + ), + ), + ), + Align( + alignment: Alignment.bottomLeft, + child: InkWell( + onTap: imgFromGallery, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.05, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + child: Icon( + Icons.photo, + color: Colors.white, + size: 27, + ), + ), + ), + ), + ], + ), + ); + } + } + + imgFromGallery() async { + final pickedFile = + await picker.pickImage(source: ImageSource.gallery, imageQuality: 70); + + if (pickedFile != null) { + Navigator.pop(context, pickedFile); + } else { + print('No image selected.'); + } + return; + } + + Widget flashWidget(context) { + return Visibility( + visible: frontFlash, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.4), + ), + ), + ); + } + + Widget cameraWidget(context) { + var camera = _controller.value; + final size = MediaQuery.of(context).size; + var scale = size.aspectRatio * camera.aspectRatio; + if (scale < 1) scale = 1 / scale; + + return Transform.scale( + scale: scale, + child: Center( + child: CameraPreview( + _controller, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onScaleStart: _handleScaleStart, + onScaleUpdate: _handleScaleUpdate, + onTapDown: (details) => onViewFinderTap(details, constraints), + ); + }), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Picture/TakePicture.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Picture/TakePicture.dart new file mode 100644 index 0000000..04cff91 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Picture/TakePicture.dart @@ -0,0 +1,335 @@ +import 'package:flutter/material.dart'; +import 'package:camera/camera.dart'; + +import 'package:teso/util/consts.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:page_transition/page_transition.dart'; +import 'PictureReview.dart'; +import 'dart:io'; + +// ignore: must_be_immutable +class TakePicture extends StatefulWidget { + List connectedCameras; + + TakePicture({Key key, this.connectedCameras}) : super(key: key); + + @override + _TakePictureState createState() => _TakePictureState(); +} + +class _TakePictureState extends State + with TickerProviderStateMixin { + CameraController _controller; + int selectedCamera = 0; + bool flash = false; + bool frontFlash = false; + double _currentScale = 1.0; + double _baseScale = 1.0; + double _minAvailableZoom; + double _maxAvailableZoom; + int _pointers = 0; + final picker = ImagePicker(); + + setCamera(int camera) async { + _controller = + CameraController(widget.connectedCameras[camera], ResolutionPreset.max); + _controller.initialize().then((_) { + _controller.setFocusMode(FocusMode.auto); + if (!mounted) { + return; + } + setState(() {}); + }); + } + + void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + final offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + _controller.setExposurePoint(offset); + _controller.setFocusPoint(offset); + } + + @override + void initState() { + if (widget.connectedCameras == null || + widget.connectedCameras.length == 0) { + availableCameras().then((value) { + widget.connectedCameras = value; + }); + setCamera(0); + } else { + setCamera(0); + } + + super.initState(); + } + + sayCheese() async { + try { + final image = await _controller.takePicture(); + + bool result = false; + result = await Navigator.push( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: PictureReview( + image: image.path, + ), + )); + + if (result) Navigator.pop(context, image); + } catch (e) { + print(e); + } + } + + void _handleScaleStart(ScaleStartDetails details) { + _baseScale = _currentScale; + } + + Future _handleScaleUpdate(ScaleUpdateDetails details) async { + // When there are not exactly two fingers on screen don't scale + if (_pointers != 2) { + return; + } + + _currentScale = (_baseScale * details.scale) + .clamp(_minAvailableZoom, _maxAvailableZoom); + + await _controller.setZoomLevel(_currentScale); + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (_controller == null || !_controller.value.isInitialized) { + return Container(); + } else { + return Scaffold( + body: Stack( + children: [ + cameraWidget(context), + Visibility( + visible: frontFlash, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.4), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.06, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + child: InkWell( + onTap: () { + if (flash && + _controller.description.lensDirection == + CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.off); + setState(() { + flash = false; + }); + } else { + setState(() { + flash = false; + frontFlash = false; + }); + } + selectedCamera++; + if (selectedCamera < widget.connectedCameras.length) { + setCamera(selectedCamera); + } else { + selectedCamera = 0; + setCamera(selectedCamera); + } + }, + child: Icon( + Icons.camera_alt, + color: Colors.white, + size: 40, + ), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.07, + vertical: MediaQuery.of(context).size.width * 0.25, + ), + child: InkWell( + onTap: () { + try { + if (!flash && + _controller.description.lensDirection == + CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.torch); + setState(() { + flash = true; + }); + } else if (!flash && + _controller.description.lensDirection == + CameraLensDirection.front) { + setState(() { + flash = true; + frontFlash = true; + }); + } else if (flash && + _controller.description.lensDirection == + CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.off); + setState(() { + flash = false; + }); + } else { + setState(() { + flash = false; + frontFlash = false; + }); + } + } catch (e) {} + }, + child: Icon( + flash ? Icons.flash_on : Icons.flash_off, + color: flash ? tesoGold : Colors.white, + size: 30, + ), + ), + ), + ), + Align( + alignment: Alignment.topLeft, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.08, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 35, + width: 35, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + shape: BoxShape.circle), + child: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: InkWell( + onTap: sayCheese, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.white, + width: 3, + ), + ), + child: Icon( + Icons.camera_alt, + color: Colors.white, + size: 40, + ), + ), + ), + ), + Align( + alignment: Alignment.bottomLeft, + child: InkWell( + onTap: imgFromGallery, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.05, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + child: Icon( + Icons.photo, + color: Colors.white, + size: 27, + ), + ), + ), + ), + ], + ), + ); + } + } + + imgFromGallery() async { + final pickedFile = + await picker.pickImage(source: ImageSource.gallery, imageQuality: 70); + + if (pickedFile != null) { + final image = File(pickedFile.path); + bool result; + result = await Navigator.push( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: PictureReview( + image: image.path, + ), + )); + + if (result) Navigator.pop(context, image); + } else { + print('No image selected.'); + } + return; + } + + Widget cameraWidget(context) { + var camera = _controller.value; + final size = MediaQuery.of(context).size; + var scale = size.aspectRatio * camera.aspectRatio; + if (scale < 1) scale = 1 / scale; + + return Transform.scale( + scale: scale, + child: Center( + child: CameraPreview( + _controller, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onScaleStart: _handleScaleStart, + onScaleUpdate: _handleScaleUpdate, + onTapDown: (details) => onViewFinderTap(details, constraints), + ); + }), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/TextEditor.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/TextEditor.dart new file mode 100644 index 0000000..dcbbe8a --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/TextEditor.dart @@ -0,0 +1,212 @@ +// import 'package:flutter/material.dart'; + +// import 'package:teso/Classes/TextE.dart'; +// import 'package:teso/util/SizeConfig.dart'; +// import 'textstyler/src/toolbar_action.dart'; +// import 'textstyler/text_style_editor.dart'; + +// // ignore: must_be_immutable +// class TextEdit extends StatefulWidget { +// Textted content; +// TextEdit({Key key, this.content}) : super(key: key); + +// @override +// _TextEditState createState() => _TextEditState(); +// } + +// class _TextEditState extends State { +// TextStyle textStyle; +// TextAlign textAlign; +// List fonts = [ +// 'Billabong', +// 'AlexBrush', +// 'Allura', +// 'Arizonia', +// 'ChunkFive', +// 'GrandHotel', +// 'GreatVibes', +// 'Lobster', +// 'OpenSans', +// 'OstrichSans', +// 'Oswald', +// 'Pacifico', +// 'Quicksand', +// 'Roboto', +// 'SEASRN', +// 'Windsong', +// ]; +// List paletteColors = [ +// Colors.black, +// Colors.white, +// Color(int.parse('0xffEA2027')), +// Color(int.parse('0xff006266')), +// Color(int.parse('0xff1B1464')), +// Color(int.parse('0xff5758BB')), +// Color(int.parse('0xff6F1E51')), +// Color(int.parse('0xffB53471')), +// Color(int.parse('0xffEE5A24')), +// Color(int.parse('0xff009432')), +// Color(int.parse('0xff0652DD')), +// Color(int.parse('0xff9980FA')), +// Color(int.parse('0xff833471')), +// Color(int.parse('0xff112CBC4')), +// Color(int.parse('0xffFDA7DF')), +// Color(int.parse('0xffED4C67')), +// Color(int.parse('0xffF79F1F')), +// Color(int.parse('0xffA3CB38')), +// Color(int.parse('0xff1289A7')), +// Color(int.parse('0xffD980FA')) +// ]; +// FocusNode _focus = new FocusNode(); +// TextEditingController controller; +// @override +// void initState() { +// controller = new TextEditingController(); +// textStyle = TextStyle( +// fontSize: 15, +// color: Colors.white, +// fontFamily: 'OpenSans', +// ); +// textAlign = TextAlign.left; +// _focus.addListener(_onFocusChange); + +// controller.text = widget.content.text != null ? widget.content.text : ""; +// textStyle = +// widget.content.textStyle != null ? widget.content.textStyle : null; +// textAlign = widget.content.textAlign != null +// ? widget.content.textAlign +// : TextAlign.center; +// super.initState(); +// } + +// @override +// void dispose() { +// _focus.removeListener(_onFocusChange); +// _focus.dispose(); +// super.dispose(); +// } + +// void _onFocusChange() { +// debugPrint("Focus: " + _focus.hasFocus.toString()); +// } + +// void verify() { +// if (_focus.hasFocus) { +// _focus.unfocus(); +// } else { +// Navigator.pop( +// context, +// new Textted( +// text: controller.text, +// textAlign: textAlign, +// textStyle: textStyle, +// )); +// } +// } + +// @override +// Widget build(BuildContext context) { +// SizeConfig().init(context); +// return Scaffold( +// resizeToAvoidBottomInset: false, +// backgroundColor: Color.fromRGBO(0, 0, 0, 0.8), +// appBar: AppBar( +// backgroundColor: Colors.transparent, +// leading: IconButton( +// onPressed: () => Navigator.pop(context, widget.content), +// icon: Icon( +// Feather.x, +// color: Colors.white, +// ), +// ), +// actions: [ +// IconButton( +// onPressed: verify, +// icon: Icon( +// AntDesign.check, +// color: Colors.white, +// ), +// ), +// ], +// ), +// body: Container( +// height: SizeConfig.safeBlockVertical * 40, +// child: Center( +// child: TextField( +// controller: controller, +// // enabled: false, +// focusNode: _focus, +// style: textStyle, +// textAlign: textAlign, +// // maxLines: 4, +// decoration: new InputDecoration( +// filled: true, +// enabledBorder: OutlineInputBorder( +// borderRadius: BorderRadius.all( +// Radius.circular(10.0), +// ), +// borderSide: BorderSide( +// color: Colors.grey.shade400, +// width: 2, +// ), +// ), +// focusedBorder: OutlineInputBorder( +// borderRadius: BorderRadius.all( +// Radius.circular(10.0), +// ), +// borderSide: BorderSide( +// color: Colors.blue.shade300, +// width: 0, +// ), +// ), +// contentPadding: EdgeInsets.all(15), +// ), +// ), +// ), +// ), +// extendBody: false, +// extendBodyBehindAppBar: false, +// bottomSheet: Container( +// height: SizeConfig.safeBlockVertical * 60, +// child: Container( +// padding: EdgeInsets.all(10), +// decoration: BoxDecoration( +// color: Theme.of(context).backgroundColor, +// border: Border.symmetric( +// horizontal: BorderSide( +// color: Theme.of(context).backgroundColor, +// ), +// ), +// ), +// child: Align( +// alignment: Alignment.topCenter, +// child: SingleChildScrollView( +// scrollDirection: Axis.vertical, +// child: TextStyleEditor( +// fonts: fonts, +// paletteColors: paletteColors, +// textStyle: textStyle, +// textAlign: textAlign, +// initialTool: EditorToolbarAction.fontFamilyTool, +// onTextAlignEdited: (align) { +// setState(() { +// textAlign = align; +// }); +// }, +// onTextStyleEdited: (style) { +// setState(() { +// textStyle = textStyle.merge(style); +// }); +// }, +// onCpasLockTaggle: (caps) { +// print(caps); +// }, +// //onToolbarActionChanged: (fu) => , +// ), +// ), +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/VideoReview copy.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/VideoReview copy.dart new file mode 100644 index 0000000..e71e0d5 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/VideoReview copy.dart @@ -0,0 +1,840 @@ +// import 'dart:typed_data'; +// import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter/rendering.dart'; +// import 'package:flutter/services.dart'; +// import 'dart:io'; +// +// import 'package:image_gallery_saver/image_gallery_saver.dart'; +// import 'package:page_transition/page_transition.dart'; +// import 'package:share_plus/share_plus.dart'; +// import 'package:teso/Classes/TextE.dart'; +// import 'package:teso/Pages/PageWidgets/Editors/SampleThumbnail.dart'; +// import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Editor/TextEditor.dart'; +// import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart'; +// import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart'; +// import 'package:teso/Pages/Sub_Pages/Posts/CreatePost.dart'; +// import 'package:teso/util/SizeConfig.dart'; +// import 'package:video_player/video_player.dart'; +// import 'package:teso/util/consts.dart'; +// import 'dart:async'; +// import 'package:path_provider/path_provider.dart'; +// import 'package:teso/Classes/TesoUser.dart'; +// import 'package:provider/provider.dart'; +// import 'package:teso/providers/user_provider.dart'; +// import 'package:flutter/cupertino.dart'; +// import 'package:image/image.dart' as IMG; +// import 'package:video_thumbnail/video_thumbnail.dart'; +// import 'package:teso/Classes/ColorFilters.dart'; + +// class VideoReview extends StatefulWidget { +// final video; +// final bool recorded; + +// const VideoReview({Key key, @required this.video, @required this.recorded}) +// : super(key: key); +// @override +// _VideoReviewState createState() => _VideoReviewState(); +// } + +// class _VideoReviewState extends State +// with TickerProviderStateMixin { +// Trimmer _trimmer = new Trimmer(); +// VideoPlayerController videoController; +// VoidCallback videoPlayerListener; +// bool muted = false; +// String readyVideo; +// Color textColor = Colors.white; +// double _startValue = 0.15; +// double _endValue = 60000.0; +// var _future; +// bool _isPlaying = false; +// Duration _duration; +// Duration _position; +// ByteData bytes; +// Uint8List imageBitmap; +// Uint8List thumbnail; +// Directory tempDirectory; +// TesoUser user; +// bool processing = false; +// bool downloaded = false; +// bool processed = false; +// final key = new GlobalKey(); +// double currentOffset = 0; + +// // ScreenshotController screenshotController = ScreenshotController(); +// Offset offset = Offset(0, SizeConfig.safeBlockVertical * 50); +// var indexFilter = 0; +// ScrollController controller; +// TextStyle textStyle; +// TextAlign textAlign; +// bool showFilter = false; +// Textted editting = new Textted(); +// List fonts = [ +// 'Billabong', +// 'AlexBrush', +// 'Allura', +// 'Arizonia', +// 'ChunkFive', +// 'GrandHotel', +// 'GreatVibes', +// 'Lobster', +// 'OpenSans', +// 'OstrichSans', +// 'Oswald', +// 'Pacifico', +// 'Quicksand', +// 'Roboto', +// 'SEASRN', +// 'Windsong', +// ]; +// List paletteColors = [ +// Colors.black, +// Colors.white, +// Color(int.parse('0xffEA2027')), +// Color(int.parse('0xff006266')), +// Color(int.parse('0xff1B1464')), +// Color(int.parse('0xff5758BB')), +// Color(int.parse('0xff6F1E51')), +// Color(int.parse('0xffB53471')), +// Color(int.parse('0xffEE5A24')), +// Color(int.parse('0xff009432')), +// Color(int.parse('0xff0652DD')), +// Color(int.parse('0xff9980FA')), +// Color(int.parse('0xff833471')), +// Color(int.parse('0xff112CBC4')), +// Color(int.parse('0xffFDA7DF')), +// Color(int.parse('0xffED4C67')), +// Color(int.parse('0xffF79F1F')), +// Color(int.parse('0xffA3CB38')), +// Color(int.parse('0xff1289A7')), +// Color(int.parse('0xffD980FA')) +// ]; +// List colorFilters = [ +// new ColorFilter(code: Color(0xFFffffff), name: "Original"), +// new ColorFilter(code: Color(0xFFffffff), name: "White"), +// new ColorFilter(code: Color(0xFF5E2612), name: "Sepia"), +// new ColorFilter(code: Color(0xFF8BA446), name: "Martini Olive"), +// new ColorFilter(code: Color(0xFFFFF8DC), name: "Cornsilk"), +// new ColorFilter(code: Color(0xFFCDB7B5), name: "Mistyrose"), +// new ColorFilter(code: Color(0xFFEEE9E9), name: "Snow"), +// new ColorFilter(code: Color(0xFF856363), name: "Dusty"), +// new ColorFilter(code: Color(0xFF8C1717), name: "Scarlet"), +// new ColorFilter(code: Color(0xFF615E3F), name: "Tank"), +// ]; + +// void _scrollListener() { +// setState(() { +// indexFilter = +// (controller.offset / MediaQuery.of(context).size.width).round() + 1; +// if (controller.offset > currentOffset) { +// controller.animateTo(currentOffset + MediaQuery.of(context).size.width, +// duration: Duration(microseconds: 2), curve: Curves.easeIn); +// currentOffset = controller.offset; +// } else { +// controller.animateTo(currentOffset - MediaQuery.of(context).size.width, +// duration: Duration(microseconds: 2), curve: Curves.easeIn); +// currentOffset = controller.offset; +// } +// }); +// print(indexFilter); +// } + +// Future _startVideoPlayer() async { +// await videoController.play(); +// } + +// Future initializeController(String fileLocation) async { +// videoController = VideoPlayerController.file(File(fileLocation)); + +// videoPlayerListener = () async { +// Timer.run(() { +// this.setState(() { +// _position = videoController.value.position; +// }); +// setState(() { +// _duration = Duration(milliseconds: _endValue.round()); +// }); +// if (_duration?.compareTo(_position) == 0 || +// _duration?.compareTo(_position) == -1) { +// this.setState(() { +// _isPlaying = false; +// }); +// videoController.pause(); +// videoController.seekTo(Duration(milliseconds: _startValue.round())); +// } else {} +// }); +// }; +// videoController.addListener(videoPlayerListener); +// await videoController.setLooping(true); +// await videoController.initialize(); +// await _trimmer.loadVideo(videoFile: File(fileLocation)); +// } + +// @override +// void initState() { +// // textStyle = TextStyle( +// // fontSize: 15, +// // color: Colors.white, +// // fontFamily: 'OpenSans', +// // ); +// controller = new ScrollController(); +// controller.addListener(_scrollListener); +// readyVideo = widget.video; +// if (readyVideo != null) _future = initializeController(readyVideo); +// rootBundle.load("assets/images/rawLogo.png").then((value) => setState(() { +// imageBitmap = value.buffer.asUint8List(); +// IMG.Image img = IMG.decodeImage(imageBitmap); +// IMG.Image resized = IMG.copyResize(img, width: 50, height: 60); +// imageBitmap = IMG.encodePng(resized); +// })); +// super.initState(); +// editting = +// new Textted(text: "", textAlign: textAlign, textStyle: textStyle); +// } + +// Future saveTextOverlay() async { +// return await screenshotController.capture( +// pixelRatio: videoController.value.aspectRatio); +// } + +// @override +// void dispose() { +// videoController?.dispose(); +// controller.dispose(); +// super.dispose(); +// } + +// void postVideo(context) async { +// setState(() { +// processing = true; +// }); +// if (processed) { +// await Navigator.pushReplacement( +// context, +// PageTransition( +// type: PageTransitionType.leftToRight, +// child: CreatePost( +// video: readyVideo, +// aspectRatio: videoController.value.aspectRatio.toString(), +// thumbnail: this.thumbnail, +// ), +// )); +// } else { +// readyVideo = await processVideo(context, false); +// await Navigator.pushReplacement( +// context, +// PageTransition( +// type: PageTransitionType.leftToRight, +// child: CreatePost( +// video: readyVideo, +// aspectRatio: videoController.value.aspectRatio.toString(), +// thumbnail: this.thumbnail, +// ), +// )); +// } +// } + +// Future downloadVideo(context) async { +// try { +// setState(() { +// processing = true; +// }); +// String output = await processVideo(context, true); +// await ImageGallerySaver.saveFile(output).catchError((error, stackTrace) { +// setState(() { +// processing = false; +// downloaded = false; +// }); +// }).then((value) { +// setState(() { +// processing = false; +// downloaded = true; +// }); +// }); +// } catch (e) { +// print(e); +// } +// } + +// Future processVideo(context, bool watermark) async { +// user = Provider.of(context, listen: false).currentUser; +// String location = await getTemporaryDirectory().then((value) => +// value.path + +// "/" + +// DateTime.now().millisecondsSinceEpoch.toString() + +// ".mp4"); +// String initial = await _trimmer.saveTrimmedVideo( +// applyVideoEncoding: false, +// startValue: _startValue, +// endValue: videoController.value.duration.inMilliseconds > 5900 && +// videoController.value.duration.inMilliseconds >= _endValue +// ? _endValue +// : double.parse( +// videoController.value.duration.inMilliseconds.toString()), +// ); +// this.thumbnail = await generateThumbnail(); +// if (widget.recorded) { +// try { +// // Uint8List textBytes; +// // if (editting.text.isNotEmpty) textBytes = await saveTextOverlay(); +// // int xposition = ScaledPosition.getWidth( +// // SizeConfig.safeBlockHorizontal * 100, +// // videoController.value.size.width, +// // offset.dx); +// // int yposition = ScaledPosition.getHeight( +// // SizeConfig.safeBlockVertical * 100, +// // videoController.value.size.height, +// // offset.dy); +// // if (watermark) { +// // final tapiocaBalls = [ +// // if (indexFilter > 1) +// // TapiocaBall.filterFromColor(colorFilters[indexFilter - 1].code), +// // TapiocaBall.imageOverlay(imageBitmap, 0, 0), +// // if (textBytes != null) +// // TapiocaBall.imageOverlay(textBytes, xposition, yposition), +// // ]; + +// // final cup = Cup(Content(initial), tapiocaBalls); +// // await cup.suckUp(location); + +// // setState(() { +// // processed = true; +// // }); +// // } else if (!watermark && indexFilter == 1) { +// // setState(() { +// // processed = true; +// // }); +// // final tapiocaBalls = [ +// // if (textBytes != null) +// // TapiocaBall.imageOverlay(textBytes, xposition, yposition), +// // ]; +// // final cup = Cup(Content(initial), tapiocaBalls); +// // await cup.suckUp(location); +// // } else { +// // final tapiocaBalls = [ +// // if (indexFilter != 1 && indexFilter != 0) +// // TapiocaBall.filterFromColor(colorFilters[indexFilter - 1].code), +// // if (textBytes != null) +// // TapiocaBall.imageOverlay(textBytes, xposition, yposition), +// // ]; +// // final cup = Cup(Content(initial), tapiocaBalls); +// // await cup.suckUp(location); + +// // setState(() { +// // processed = true; +// // }); +// // } +// } catch (e) { +// print(e); +// FirebaseCrashlytics.instance.recordError( +// e, +// e, +// reason: "Video Editor", +// ); +// setState(() { +// processed = true; +// }); +// } +// } else { +// location = initial; +// } +// return location; +// } + +// Future generateThumbnail() async { +// try { +// Uint8List thumbnail; + +// thumbnail = await VideoThumbnail.thumbnailData( +// video: widget.video, +// imageFormat: ImageFormat.JPEG, +// maxWidth: 0, +// maxHeight: 0, +// timeMs: 100, +// quality: 100, +// ); +// return thumbnail; +// } catch (e) { +// print("Error :::: " + e); +// return null; +// } +// } + +// Future shareVideo(context) async { +// setState(() { +// processing = true; +// }); +// if (readyVideo == widget.video) { +// readyVideo = await processVideo(context, true); +// Share.shareFiles([readyVideo]); +// } else { +// Share.shareFiles([readyVideo]); +// } +// setState(() { +// processing = false; +// }); +// } + +// void setText() async { +// Textted ed = await Navigator.push( +// context, +// PageRouteBuilder( +// opaque: false, +// pageBuilder: (_, __, ___) => TextEdit( +// content: editting, +// ), +// ), +// ); +// if (ed != null) { +// setState(() { +// editting = ed; +// }); +// } +// } + +// void showFilters() async { +// if (thumbnail == null) this.thumbnail = await generateThumbnail(); +// setState(() { +// showFilter = !showFilter; +// }); +// } + +// void setFilter(index) { +// setState(() { +// indexFilter = index; +// }); +// } + +// @override +// Widget build(BuildContext context) { +// SizeConfig().init(context); +// return Scaffold( +// body: FutureBuilder( +// future: _future, +// builder: (context, snapshot) { +// if (snapshot.connectionState == ConnectionState.waiting) { +// return Container( +// color: Colors.black, +// width: MediaQuery.of(context).size.width, +// height: MediaQuery.of(context).size.height, +// child: Center( +// child: CircularProgressIndicator( +// backgroundColor: Colors.red, +// ), +// ), +// ); +// } else { +// return Stack( +// children: [ +// videoContent(context), +// // //Filters +// filterWidget(context), +// textWidget(context), +// // Video trimmer +// trimmerWidget(context), +// // Pop button +// Align( +// alignment: Alignment.topLeft, +// child: InkWell( +// onTap: () => Navigator.pop(context), +// child: Container( +// margin: EdgeInsets.symmetric( +// horizontal: MediaQuery.of(context).size.width * 0.07, +// vertical: MediaQuery.of(context).size.width * 0.1, +// ), +// height: 35, +// width: 35, +// decoration: BoxDecoration( +// color: Color.fromRGBO(0, 0, 0, 0.4), +// shape: BoxShape.circle), +// child: Icon( +// EvilIcons.close, +// color: Colors.white, +// size: 20, +// ), +// ), +// ), +// ), +// //Filter Buttons +// showFilter ? listFilters(context) : Container(), +// // Bottom buttons +// bottomButtons(context), +// Visibility( +// visible: processing, +// child: Container( +// width: MediaQuery.of(context).size.width, +// height: MediaQuery.of(context).size.height, +// color: Color.fromRGBO(0, 0, 0, 0.6), +// padding: EdgeInsets.only( +// top: MediaQuery.of(context).size.width * 0.7), +// child: Center( +// child: Column( +// children: [ +// Image.asset(cupertinoActivityIndicatorSmall), +// Text( +// "Processing.....", +// style: TextStyle( +// color: Colors.white, +// ), +// ), +// ], +// ), +// ), +// ), +// ), +// ], +// ); +// } +// }), +// ); +// } + +// Widget trimmerWidget(context) { +// return Container( +// margin: EdgeInsets.symmetric( +// horizontal: MediaQuery.of(context).size.width * 0.01, +// vertical: MediaQuery.of(context).size.width * 0.20, +// ), +// width: MediaQuery.of(context).size.width, +// child: TrimEditor( +// borderPaintColor: tesoGold, +// circlePaintColor: tesoBlue, +// thumbnailQuality: 100, +// showDuration: true, +// viewerHeight: 50.0, +// maxVideoLength: Duration(seconds: 60), +// viewerWidth: MediaQuery.of(context).size.width, +// onChangeStart: (value) { +// if (!mounted) { +// setState(() { +// _startValue = value; +// }); +// } else { +// _startValue = value; +// } +// videoController.seekTo(Duration(milliseconds: value.round())); +// }, +// onChangeEnd: (value) { +// if (!mounted) { +// setState(() { +// _endValue = value; +// }); +// } else { +// _endValue = value; +// } +// }, +// onChangePlaybackState: (isPlaying) { +// if (mounted) +// setState(() { +// _isPlaying = isPlaying; +// }); +// }, +// )); +// } + +// Widget filterWidget(context) { +// return GestureDetector( +// onTap: () { +// !_isPlaying ? _startVideoPlayer() : videoController.pause(); +// setState(() { +// _isPlaying = !_isPlaying; +// }); +// }, +// child: Container( +// width: MediaQuery.of(context).size.width, +// height: MediaQuery.of(context).size.height, +// color: +// colorFilters.elementAt(indexFilter).name.toLowerCase() == "original" +// ? colorFilters.elementAt(indexFilter).code.withOpacity(0) +// : colorFilters.elementAt(indexFilter).code.withOpacity(0.5), +// ), +// ); +// } + +// Widget videoContent(context) { +// print(videoController.value.size.width); + +// return Container( +// width: MediaQuery.of(context).size.width, +// height: MediaQuery.of(context).size.height, +// color: Colors.black, +// child: Center( +// child: AspectRatio( +// aspectRatio: videoController.value.size != null +// ? videoController.value.aspectRatio +// : 1.0, +// child: Stack( +// children: [ +// InkWell( +// onTap: () { +// !_isPlaying ? _startVideoPlayer() : videoController.pause(); +// setState(() { +// _isPlaying = !_isPlaying; +// }); +// }, +// child: VideoPlayer( +// videoController, +// ), +// ), +// Container( +// width: double.infinity, +// height: double.infinity, +// child: GestureDetector( +// child: !_isPlaying +// ? Icon( +// Ionicons.md_play_circle, +// size: 60, +// color: Colors.white, +// ) +// : Container(), +// onTap: () { +// !_isPlaying +// ? _startVideoPlayer() +// : videoController.pause(); +// setState(() { +// _isPlaying = !_isPlaying; +// }); +// }, +// ), +// ), +// ], +// )), +// ), +// ); +// } + +// Widget bottomButtons(context) { +// if (widget.recorded) { +// return Align( +// alignment: Alignment.bottomLeft, +// child: Container( +// margin: EdgeInsets.symmetric( +// horizontal: MediaQuery.of(context).size.width * 0.05, +// vertical: SizeConfig.safeBlockVertical * 2.5, +// ), +// width: MediaQuery.of(context).size.width, +// child: Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// mainAxisSize: MainAxisSize.min, +// children: [ +// Container( +// width: 55, +// height: 40, +// padding: EdgeInsets.all(5), +// decoration: BoxDecoration( +// color: Color.fromRGBO(0, 0, 0, 0.6), +// borderRadius: BorderRadius.only( +// bottomLeft: Radius.circular(30), +// bottomRight: Radius.circular(30), +// topRight: Radius.circular(30), +// topLeft: Radius.circular(30), +// ), +// border: Border.all(color: Colors.white, width: 0.5)), +// child: InkWell( +// onTap: () async => +// !downloaded ? await downloadVideo(context) : null, +// child: Icon( +// !downloaded ? Feather.download : MaterialIcons.check, +// color: !downloaded ? Colors.white : Colors.green, +// ), +// ), +// ), +// // InkWell( +// // onTap: setText, +// // child: Container( +// // width: 50, +// // padding: EdgeInsets.symmetric(horizontal: 18), +// // decoration: BoxDecoration( +// // color: Color.fromRGBO(0, 0, 0, 0.6), +// // borderRadius: BorderRadius.only( +// // bottomLeft: Radius.circular(30), +// // bottomRight: Radius.circular(30), +// // topRight: Radius.circular(30), +// // topLeft: Radius.circular(30), +// // ), +// // border: Border.all(color: Colors.white, width: 0.5)), +// // child: Text( +// // "T", +// // style: TextStyle( +// // color: Colors.white, +// // fontWeight: FontWeight.bold, +// // fontSize: SizeConfig.safeBlockHorizontal * 8, +// // fontFamily: 'DeadheadScript', +// // ), +// // ), +// // ), +// // ), +// // Container( +// // width: 55, +// // height: 40, +// // padding: EdgeInsets.all(5), +// // decoration: BoxDecoration( +// // color: Color.fromRGBO(0, 0, 0, 0.6), +// // borderRadius: BorderRadius.only( +// // bottomLeft: Radius.circular(30), +// // bottomRight: Radius.circular(30), +// // topRight: Radius.circular(30), +// // topLeft: Radius.circular(30), +// // ), +// // border: Border.all(color: Colors.white, width: 0.5)), +// // child: InkWell( +// // onTap: showFilters, +// // child: Image( +// // image: AssetImage("assets/images/color-filters.png"), +// // ), +// // ), +// // ), + +// Container( +// width: 55, +// height: 40, +// padding: EdgeInsets.all(5), +// decoration: BoxDecoration( +// color: Color.fromRGBO(0, 0, 0, 0.6), +// borderRadius: BorderRadius.only( +// bottomLeft: Radius.circular(30), +// bottomRight: Radius.circular(30), +// topRight: Radius.circular(30), +// topLeft: Radius.circular(30), +// ), +// border: Border.all(color: Colors.white, width: 0.5)), +// child: InkWell( +// onTap: () async => await shareVideo(context), +// child: Icon( +// Entypo.share, +// color: Colors.white, +// ), +// ), +// ), +// Container( +// padding: EdgeInsets.all(5), +// width: 100, +// height: 40, +// decoration: BoxDecoration( +// color: tesoGold, +// borderRadius: BorderRadius.only( +// bottomLeft: Radius.circular(30), +// bottomRight: Radius.circular(30), +// topRight: Radius.circular(30), +// topLeft: Radius.circular(30), +// ), +// ), +// child: InkWell( +// onTap: () => postVideo(context), +// child: Row( +// mainAxisAlignment: MainAxisAlignment.spaceEvenly, +// children: [ +// Text( +// "Post", +// style: TextStyle( +// fontWeight: FontWeight.bold, +// ), +// ), +// Icon( +// Ionicons.md_send, +// color: tesoBlue, +// ), +// ], +// ), +// ), +// ), +// ], +// ), +// ), +// ); +// } else { +// return Align( +// alignment: Alignment.bottomRight, +// child: Container( +// padding: EdgeInsets.all(5), +// width: 100, +// height: 40, +// margin: EdgeInsets.symmetric( +// vertical: 10, +// horizontal: 20, +// ), +// decoration: BoxDecoration( +// color: tesoGold, +// borderRadius: BorderRadius.only( +// bottomLeft: Radius.circular(30), +// bottomRight: Radius.circular(30), +// topRight: Radius.circular(30), +// topLeft: Radius.circular(30), +// ), +// ), +// child: InkWell( +// onTap: () => postVideo(context), +// child: Row( +// mainAxisAlignment: MainAxisAlignment.spaceEvenly, +// children: [ +// Text( +// "Post", +// style: TextStyle( +// fontWeight: FontWeight.bold, +// ), +// ), +// Icon( +// Ionicons.md_send, +// color: tesoBlue, +// ), +// ], +// ), +// ), +// ), +// ); +// } +// } + +// Widget listFilters(context) { +// return Align( +// alignment: Alignment.bottomLeft, +// child: Container( +// margin: EdgeInsets.symmetric( +// horizontal: MediaQuery.of(context).size.width * 0.05, +// vertical: SizeConfig.safeBlockVertical * 10, +// ), +// width: MediaQuery.of(context).size.width, +// height: SizeConfig.safeBlockVertical * 17, +// child: ListView.builder( +// scrollDirection: Axis.horizontal, +// itemCount: colorFilters.length, +// //controller: controller, +// itemBuilder: (context, index) { +// return InkWell( +// onTap: () => setFilter(index), +// child: buildFilterThumb( +// context, colorFilters[index], thumbnail)); +// }), +// ), +// ); +// } + +// Widget textWidget(context) { +// return Container( +// child: Positioned( +// left: offset.dx, +// top: offset.dy, +// child: GestureDetector( +// onTap: setText, +// onPanUpdate: (details) { +// setState(() { +// offset = Offset( +// offset.dx + details.delta.dx, offset.dy + details.delta.dy); +// }); +// }, +// child: Screenshot( +// controller: screenshotController, +// child: editting.text != null +// ? Text( +// editting.text, +// style: editting.textStyle, +// textAlign: editting.textAlign, +// ) +// : Container(), +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/VideoReview.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/VideoReview.dart new file mode 100644 index 0000000..1a157a6 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/VideoReview.dart @@ -0,0 +1,554 @@ +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'dart:io'; + +import 'package:image_gallery_saver/image_gallery_saver.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart'; +import 'package:teso/Pages/Sub_Pages/Posts/CreatePost.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:video_player/video_player.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:async'; +import 'package:path_provider/path_provider.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:image/image.dart' as IMG; +import 'package:video_thumbnail/video_thumbnail.dart'; + +class VideoReview extends StatefulWidget { + final video; + final bool recorded; + final double aspect; + + const VideoReview( + {Key key, @required this.video, @required this.recorded, this.aspect}) + : super(key: key); + @override + _VideoReviewState createState() => _VideoReviewState(); +} + +class _VideoReviewState extends State + with TickerProviderStateMixin { + Trimmer _trimmer = new Trimmer(); + VideoPlayerController videoController; + VoidCallback videoPlayerListener; + bool muted = false; + String readyVideo; + Color textColor = Colors.white; + double _startValue = 0.15; + double _endValue = 60000.0; + var _future; + bool _isPlaying = false; + Duration _duration; + Duration _position; + ByteData bytes; + Uint8List imageBitmap; + Uint8List thumbnail; + Directory tempDirectory; + TesoUser user; + bool processing = false; + bool downloaded = false; + bool processed = false; + final key = new GlobalKey(); + double currentOffset = 0; + + Future _startVideoPlayer() async { + await videoController.play(); + } + + Future initializeController(String fileLocation) async { + videoController = VideoPlayerController.file(File(fileLocation)); + + videoPlayerListener = () async { + Timer.run(() { + this.setState(() { + _position = videoController.value.position; + }); + setState(() { + _duration = Duration(milliseconds: _endValue.round()); + }); + if (_duration?.compareTo(_position) == 0 || + _duration?.compareTo(_position) == -1) { + this.setState(() { + _isPlaying = false; + }); + videoController.pause(); + videoController.seekTo(Duration(milliseconds: _startValue.round())); + } else {} + }); + }; + videoController.addListener(videoPlayerListener); + await videoController.setLooping(true); + await videoController.initialize(); + await _trimmer.loadVideo(videoFile: File(fileLocation)); + } + + @override + void initState() { + readyVideo = widget.video; + if (readyVideo != null) _future = initializeController(readyVideo); + rootBundle.load("assets/images/rawLogo.png").then((value) => setState(() { + imageBitmap = value.buffer.asUint8List(); + IMG.Image img = IMG.decodeImage(imageBitmap); + IMG.Image resized = IMG.copyResize(img, width: 50, height: 60); + imageBitmap = IMG.encodePng(resized); + })); + super.initState(); + } + + @override + void dispose() { + super.dispose(); + videoController.dispose(); + } + + void postVideo(context) async { + setState(() { + processing = true; + }); + if (processed) { + await Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: CreatePost( + video: readyVideo, + aspectRatio: widget.recorded + ? "0.5625" + : videoController.value.aspectRatio.toString(), + thumbnail: this.thumbnail, + ), + )); + } else { + readyVideo = await processVideo(context, false); + await Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: CreatePost( + video: readyVideo, + aspectRatio: widget.recorded + ? "0.5625" + : videoController.value.aspectRatio.toString(), + thumbnail: this.thumbnail, + ), + )); + } + } + + Future downloadVideo(context) async { + try { + setState(() { + processing = true; + }); + String output = await processVideo(context, true); + await ImageGallerySaver.saveFile(output).catchError((error, stackTrace) { + setState(() { + processing = false; + downloaded = false; + }); + }).then((value) { + setState(() { + processing = false; + downloaded = true; + }); + }); + } catch (e) { + print(e); + } + } + + Future processVideo(context, bool watermark) async { + user = Provider.of(context, listen: false).currentUser; + String location = await getTemporaryDirectory().then((value) => + value.path + + "/" + + DateTime.now().millisecondsSinceEpoch.toString() + + ".mp4"); + if (widget.recorded) { + String initial = await _trimmer.saveTrimmedVideo( + applyVideoEncoding: false, + ffmpegCommand: "-vf setsar=1:1 -aspect 9:16", + customVideoFormat: ".mp4", + startValue: _startValue, + endValue: videoController.value.duration.inMilliseconds > 5900 && + videoController.value.duration.inMilliseconds >= _endValue + ? _endValue + : double.parse( + videoController.value.duration.inMilliseconds.toString()), + ); + this.thumbnail = await generateThumbnail(); + + location = initial; + } else { + String initial = await _trimmer.saveTrimmedVideo( + startValue: _startValue, + endValue: videoController.value.duration.inMilliseconds > 5900 && + videoController.value.duration.inMilliseconds >= _endValue + ? _endValue + : double.parse( + videoController.value.duration.inMilliseconds.toString()), + outputFormat: FileFormat.mp4, + ); + this.thumbnail = await generateThumbnail(); + + location = initial; + } + return location; + } + + Future generateThumbnail() async { + try { + Uint8List thumbnail; + + thumbnail = await VideoThumbnail.thumbnailData( + video: widget.video, + imageFormat: ImageFormat.JPEG, + maxWidth: 0, + maxHeight: 0, + timeMs: 100, + quality: 100, + ); + return thumbnail; + } catch (e) { + print("Error :::: " + e); + return null; + } + } + + Future shareVideo(context) async { + setState(() { + processing = true; + }); + if (readyVideo == widget.video) { + readyVideo = await processVideo(context, true); + Share.shareFiles([readyVideo]); + } else { + Share.shareFiles([readyVideo]); + } + setState(() { + processing = false; + }); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + body: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Container( + color: Colors.black, + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: CircularProgressIndicator( + backgroundColor: Colors.red, + ), + ), + ); + } else { + return Stack( + children: [ + videoContent(context), + // Video trimmer + trimmerWidget(context), + // Pop button + Align( + alignment: Alignment.topLeft, + child: InkWell( + onTap: () => Navigator.pop(context), + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.07, + vertical: MediaQuery.of(context).size.width * 0.1, + ), + height: 35, + width: 35, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + shape: BoxShape.circle), + child: Icon( + Icons.close, + color: Colors.white, + size: 20, + ), + ), + ), + ), + // Bottom buttons + bottomButtons(context), + Visibility( + visible: processing, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Color.fromRGBO(0, 0, 0, 0.6), + padding: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.7), + child: Center( + child: Column( + children: [ + CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + Text( + "Processing.....", + style: TextStyle( + color: Colors.white, + ), + ), + ], + ), + ), + ), + ), + ], + ); + } + }), + ); + } + + Widget trimmerWidget(context) { + return Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.01, + vertical: MediaQuery.of(context).size.width * 0.20, + ), + width: MediaQuery.of(context).size.width, + child: TrimEditor( + borderPaintColor: tesoGold, + circlePaintColor: tesoBlue, + thumbnailQuality: 100, + showDuration: true, + viewerHeight: 50.0, + maxVideoLength: Duration(seconds: 60), + viewerWidth: MediaQuery.of(context).size.width, + onChangeStart: (value) { + if (!mounted) { + setState(() { + _startValue = value; + }); + } else { + _startValue = value; + } + videoController.seekTo(Duration(milliseconds: value.round())); + }, + onChangeEnd: (value) { + if (!mounted) { + setState(() { + _endValue = value; + }); + } else { + _endValue = value; + } + }, + onChangePlaybackState: (isPlaying) { + if (mounted) + setState(() { + _isPlaying = isPlaying; + }); + }, + )); + } + + Widget videoContent(context) { + print(videoController.value.size.width); + + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Colors.black, + child: Center( + child: AspectRatio( + aspectRatio: videoController.value.size != null + ? videoController.value.aspectRatio + : 1.0, + child: Stack( + children: [ + InkWell( + onTap: () { + !_isPlaying ? _startVideoPlayer() : videoController.pause(); + setState(() { + _isPlaying = !_isPlaying; + }); + }, + child: VideoPlayer( + videoController, + ), + ), + Container( + width: double.infinity, + height: double.infinity, + child: GestureDetector( + child: !_isPlaying + ? Icon( + Icons.play_circle, + size: 60, + color: Colors.white, + ) + : Container(), + onTap: () { + !_isPlaying + ? _startVideoPlayer() + : videoController.pause(); + setState(() { + _isPlaying = !_isPlaying; + }); + }, + ), + ), + ], + )), + ), + ); + } + + Widget bottomButtons(context) { + if (widget.recorded) { + return Align( + alignment: Alignment.bottomLeft, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.05, + vertical: SizeConfig.safeBlockVertical * 2.5, + ), + width: MediaQuery.of(context).size.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 55, + height: 40, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.6), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + ), + border: Border.all(color: Colors.white, width: 0.5)), + child: InkWell( + onTap: () async => + !downloaded ? await downloadVideo(context) : null, + child: Icon( + !downloaded ? Icons.download : Icons.check, + color: !downloaded ? Colors.white : Colors.green, + ), + ), + ), + Container( + width: 55, + height: 40, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.6), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + ), + border: Border.all(color: Colors.white, width: 0.5)), + child: InkWell( + onTap: () async => await shareVideo(context), + child: Icon( + Icons.share, + color: Colors.white, + ), + ), + ), + Container( + padding: EdgeInsets.all(5), + width: 100, + height: 40, + decoration: BoxDecoration( + color: tesoGold, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + ), + ), + child: InkWell( + onTap: () => postVideo(context), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + "Post", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + Icon( + Icons.send, + color: tesoBlue, + ), + ], + ), + ), + ), + ], + ), + ), + ); + } else { + return Align( + alignment: Alignment.bottomRight, + child: Container( + padding: EdgeInsets.all(5), + width: 100, + height: 40, + margin: EdgeInsets.symmetric( + vertical: 10, + horizontal: 20, + ), + decoration: BoxDecoration( + color: tesoGold, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + ), + ), + child: InkWell( + onTap: () => postVideo(context), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + "Post", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + Icon( + Icons.send, + color: tesoBlue, + ), + ], + ), + ), + ), + ); + } + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/color_palette.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/color_palette.dart new file mode 100644 index 0000000..130d0f2 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/color_palette.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; + +class ColorPalette extends StatefulWidget { + final Color activeColor; + final List colors; + final Function(Color) onColorPicked; + + ColorPalette({ + this.activeColor, + this.onColorPicked, + this.colors, + }); + + @override + _ColorPaletteState createState() => _ColorPaletteState(); +} + +class _ColorPaletteState extends State { + Color _activeColor; + + @override + void initState() { + _activeColor = widget.activeColor ?? widget.colors[0]; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.all(16), + child: Wrap( + spacing: 16, + runSpacing: 16, + children: widget.colors + .map( + (color) => _ColorHolder( + color: color, + active: color == _activeColor, + onTap: (color) { + setState(() => _activeColor = color); + widget.onColorPicked(color); + }, + ), + ) + .toList(), + ), + ); + } +} + +class _ColorHolder extends StatelessWidget { + final Color color; + final Function(Color) onTap; + final bool active; + + _ColorHolder({ + this.color, + this.onTap, + this.active = false, + }); + + @override + Widget build(BuildContext context) { + return Container( + height: 40, + width: 40, + decoration: BoxDecoration( + border: active + ? Border.fromBorderSide( + BorderSide(color: Theme.of(context).colorScheme.onSurface)) + : null, + borderRadius: BorderRadius.circular(50), + ), + child: Center( + child: GestureDetector( + onTap: () => onTap(color), + child: Container( + height: 35, + width: 35, + decoration: BoxDecoration( + border: Border.fromBorderSide( + BorderSide(color: Theme.of(context).colorScheme.onSurface)), + borderRadius: BorderRadius.circular(50), + color: color, + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/option_button.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/option_button.dart new file mode 100644 index 0000000..ad31cf2 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/option_button.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class OptionButton extends StatelessWidget { + final bool isActive; + final Function() onPressed; + final Widget child; + final Size size; + + OptionButton({ + this.onPressed, + this.child, + this.isActive = false, + this.size, + }); + @override + Widget build(BuildContext context) { + return RawMaterialButton( + constraints: BoxConstraints.tight(size ?? Size(45, 45)), + highlightColor: Theme.of(context).colorScheme.background, + splashColor: Theme.of(context).colorScheme.background, + fillColor: isActive ? Theme.of(context).colorScheme.background : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: BorderSide(color: Theme.of(context).colorScheme.surface), + ), + child: child, + onPressed: onPressed, + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/toolbar.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/toolbar.dart new file mode 100644 index 0000000..48dec72 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/toolbar.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; + +import 'option_button.dart'; +import 'toolbar_action.dart'; + +class Toolbar extends StatefulWidget { + final EditorToolbarAction initialTool; + final Function(EditorToolbarAction) onToolSelect; + + Toolbar({ + this.initialTool = EditorToolbarAction.editor, + this.onToolSelect, + }); + + @override + _ToolbarState createState() => _ToolbarState(); +} + +class _ToolbarState extends State { + EditorToolbarAction _selectedAction; + @override + void initState() { + _selectedAction = widget.initialTool; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + // OptionButton( + // isActive: _selectedAction == EditorToolbarAction.editor, + // child: Icon(Icons.keyboard), + // onPressed: () { + // setState(() => _selectedAction = EditorToolbarAction.editor); + // widget.onToolSelect(EditorToolbarAction.editor); + // }, + // ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.fontFamilyTool, + child: Icon(Icons.title), + onPressed: () { + setState( + () => _selectedAction = EditorToolbarAction.fontFamilyTool); + widget.onToolSelect(EditorToolbarAction.fontFamilyTool); + }, + ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.fontOptionTool, + child: Icon(Icons.strikethrough_s), + onPressed: () { + setState( + () => _selectedAction = EditorToolbarAction.fontOptionTool); + widget.onToolSelect(EditorToolbarAction.fontOptionTool); + }, + ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.fontSizeTool, + child: Icon(Icons.format_size), + onPressed: () { + setState(() => _selectedAction = EditorToolbarAction.fontSizeTool); + widget.onToolSelect(EditorToolbarAction.fontSizeTool); + }, + ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.fontColorTool, + child: Icon(Icons.format_color_text), + onPressed: () { + setState(() => _selectedAction = EditorToolbarAction.fontColorTool); + widget.onToolSelect(EditorToolbarAction.fontColorTool); + }, + ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.backgroundColorTool, + child: Icon(Icons.format_color_fill), + onPressed: () { + setState(() => + _selectedAction = EditorToolbarAction.backgroundColorTool); + widget.onToolSelect(EditorToolbarAction.backgroundColorTool); + }, + ), + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/toolbar_action.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/toolbar_action.dart new file mode 100644 index 0000000..07382cc --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/toolbar_action.dart @@ -0,0 +1,8 @@ +enum EditorToolbarAction { + editor, + fontFamilyTool, + fontOptionTool, + fontSizeTool, + fontColorTool, + backgroundColorTool, +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/background_color_tool.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/background_color_tool.dart new file mode 100644 index 0000000..340f9a4 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/background_color_tool.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import '../color_palette.dart'; + +class BackgroundColorTool extends StatelessWidget { + final List colors; + final Color activeColor; + final Function(Color) onColorPicked; + + BackgroundColorTool({ + this.colors, + this.onColorPicked, + this.activeColor, + }); + + @override + Widget build(BuildContext context) { + return ColorPalette( + activeColor: activeColor, + onColorPicked: onColorPicked, + colors: colors, + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_color_tool.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_color_tool.dart new file mode 100644 index 0000000..8da558d --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_color_tool.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import '../color_palette.dart'; + +class FontColorTool extends StatelessWidget { + final List colors; + final Color activeColor; + final Function(Color) onColorPicked; + + FontColorTool({ + this.colors, + this.onColorPicked, + this.activeColor, + }); + + @override + Widget build(BuildContext context) { + return ColorPalette( + activeColor: activeColor, + onColorPicked: onColorPicked, + colors: colors, + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_family_tool.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_family_tool.dart new file mode 100644 index 0000000..1b7ab48 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_family_tool.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; + +import '../option_button.dart'; + +class FontFamilyTool extends StatefulWidget { + final List fonts; + final Function(String) onSelectFont; + final String selectedFont; + + FontFamilyTool({ + this.fonts, + this.onSelectFont, + this.selectedFont, + }); + + @override + _FontFamilyToolState createState() => _FontFamilyToolState(); +} + +class _FontFamilyToolState extends State { + String _selectedFont; + + @override + void initState() { + _selectedFont = widget.selectedFont ?? widget.fonts[0]; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Wrap( + spacing: 12, + runSpacing: 12, + children: widget.fonts + .map<_FontFamily>( + (font) => _FontFamily( + font, + isSelected: _selectedFont == font, + onSelect: (selectedFont) { + setState(() => _selectedFont = selectedFont); + widget.onSelectFont(selectedFont); + }, + ), + ) + .toList(), + ); + } +} + +class _FontFamily extends StatelessWidget { + final String font; + final bool isSelected; + final Function(String) onSelect; + + _FontFamily(this.font, {this.onSelect, this.isSelected = false}); + @override + Widget build(BuildContext context) { + return OptionButton( + isActive: isSelected, + size: Size(90, 45), + onPressed: () => onSelect(font), + child: Center(child: Text(font, style: TextStyle(fontFamily: font))), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_size_tool.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_size_tool.dart new file mode 100644 index 0000000..53915f1 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/font_size_tool.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; + +class FontSizeTool extends StatelessWidget { + final double fontSize; + final double letterSpacing; + final double letterHeight; + final Function( + double fontSize, + double letterSpacing, + double letterHeight, + ) onFontSizeEdited; + + FontSizeTool({ + this.onFontSizeEdited, + this.fontSize = 0, + this.letterSpacing = 0, + this.letterHeight = 0, + }); + + @override + Widget build(BuildContext context) { + double _fontSize = fontSize; + double _letterSpacing = letterSpacing; + double _letterHeight = letterHeight; + + return Padding( + padding: EdgeInsets.all(16), + child: Column( + children: [ + _ResizeSlider( + value: _fontSize, + icon: Icons.format_size, + max: 45, + onChange: (value) { + _fontSize = value; + onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); + }, + ), + _ResizeSlider( + value: _letterHeight, + icon: Icons.format_line_spacing, + max: 10, + onChange: (value) { + _letterHeight = value; + onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); + }, + ), + _ResizeSlider( + value: _letterSpacing, + icon: Icons.settings_ethernet, + max: 10, + onChange: (value) { + _letterSpacing = value; + onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); + }, + ), + ], + ), + ); + } +} + +class _ResizeSlider extends StatefulWidget { + final double value; + final double min; + final double max; + final IconData icon; + final Function(double) onChange; + + _ResizeSlider({ + this.value, + this.icon, + this.onChange, + this.min = 0, + this.max = 100, + }); + + @override + _ResizeSliderState createState() => _ResizeSliderState(); +} + +class _ResizeSliderState extends State<_ResizeSlider> { + double _value; + + @override + void initState() { + _value = widget.value; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Icon(widget.icon), + Expanded( + child: SliderTheme( + data: SliderThemeData( + activeTrackColor: Theme.of(context).colorScheme.background, + inactiveTrackColor: Theme.of(context).colorScheme.background, + thumbColor: Theme.of(context).colorScheme.background, + overlayColor: + Theme.of(context).colorScheme.background.withOpacity(0.2), + trackHeight: 2, + ), + child: Slider( + value: _value, + onChanged: (value) { + setState(() => _value = value); + + widget.onChange(value); + }, + min: widget.min, + max: widget.max, + ), + ), + ), + Text(_value.toStringAsFixed(1)), + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/text_format_tool.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/text_format_tool.dart new file mode 100644 index 0000000..5365741 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/src/tools/text_format_tool.dart @@ -0,0 +1,237 @@ +import 'package:flutter/material.dart'; + +import '../option_button.dart'; + +class TextFormatTool extends StatelessWidget { + final Function( + bool bold, + bool italic, + ) onTextFormatEdited; + final Function(bool caps) onCpasLockTaggle; + final Function(TextAlign textAlign) onTextAlignEdited; + final TextAlign textAlign; + final bool bold; + final bool italic; + final bool caps; + + TextFormatTool({ + this.onTextFormatEdited, + this.onTextAlignEdited, + this.onCpasLockTaggle, + this.textAlign = TextAlign.left, + this.bold = false, + this.italic = false, + this.caps = false, + }); + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only(top: 36), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _TextFormatEditor( + bold: bold, + italic: italic, + caps: caps, + onFormatEdited: onTextFormatEdited, + onCpasLockTaggle: onCpasLockTaggle, + ), + SizedBox(height: 36), + _TextAlignEditor( + textAlign: textAlign, + onTextAlignEdited: onTextAlignEdited, + ), + ], + ), + ); + } +} + +class _TextAlignEditor extends StatefulWidget { + final TextAlign textAlign; + final Function(TextAlign textAlign) onTextAlignEdited; + + _TextAlignEditor({ + this.onTextAlignEdited, + this.textAlign = TextAlign.left, + }); + + @override + _TextAlignEditorState createState() => _TextAlignEditorState(); +} + +class _TextAlignEditorState extends State<_TextAlignEditor> { + TextAlign _textAlign; + + @override + void initState() { + _textAlign = widget.textAlign; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _TextAlignOption( + icon: Icons.format_align_left, + isActive: _textAlign == TextAlign.left, + onPressed: () { + setState(() => _textAlign = TextAlign.left); + widget.onTextAlignEdited(_textAlign); + }, + ), + _TextAlignOption( + icon: Icons.format_align_center, + isActive: _textAlign == TextAlign.center, + onPressed: () { + setState(() => _textAlign = TextAlign.center); + widget.onTextAlignEdited(_textAlign); + }, + ), + _TextAlignOption( + icon: Icons.format_align_right, + isActive: _textAlign == TextAlign.right, + onPressed: () { + setState(() => _textAlign = TextAlign.right); + widget.onTextAlignEdited(_textAlign); + }, + ), + _TextAlignOption( + icon: Icons.format_align_justify, + isActive: _textAlign == TextAlign.justify, + onPressed: () { + setState(() => _textAlign = TextAlign.justify); + widget.onTextAlignEdited(_textAlign); + }, + ), + ], + ); + } +} + +class _TextAlignOption extends StatelessWidget { + final IconData icon; + final Function() onPressed; + final bool isActive; + + _TextAlignOption({ + this.icon, + this.onPressed, + this.isActive = false, + }); + + @override + Widget build(BuildContext context) { + return IconButton( + iconSize: 32, + icon: Icon(icon), + color: isActive + ? Theme.of(context).iconTheme.color + : Theme.of(context).disabledColor, + onPressed: onPressed, + ); + } +} + +class _TextFormatEditor extends StatefulWidget { + final Function(bool bold, bool italic) onFormatEdited; + final Function(bool caps) onCpasLockTaggle; + final bool bold; + final bool italic; + final bool caps; + + _TextFormatEditor({ + this.onFormatEdited, + this.onCpasLockTaggle, + this.bold = false, + this.italic = false, + this.caps = false, + }); + + @override + _TextFormatEditorState createState() => _TextFormatEditorState(); +} + +class _TextFormatEditorState extends State<_TextFormatEditor> { + bool _bold; + bool _italic; + bool _caps; + + @override + void initState() { + _bold = widget.bold; + _italic = widget.italic; + _caps = widget.caps; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _TextFormatOption( + title: 'BOLD', + icon: Icons.format_bold, + isActive: _bold, + onPressed: () { + setState(() => _bold = !_bold); + widget.onFormatEdited(_bold, _italic); + }, + ), + _TextFormatOption( + title: 'ITALIC', + icon: Icons.format_italic, + isActive: _italic, + onPressed: () { + setState(() => _italic = !_italic); + widget.onFormatEdited(_bold, _italic); + }, + ), + _TextFormatOption( + title: 'CAPS', + icon: Icons.keyboard_capslock, + isActive: _caps, + onPressed: () { + setState(() => _caps = !_caps); + widget.onCpasLockTaggle(_caps); + }, + ), + ], + ); + } +} + +class _TextFormatOption extends StatelessWidget { + final String title; + final IconData icon; + final Function() onPressed; + final bool isActive; + + _TextFormatOption({ + this.title, + this.icon, + this.onPressed, + this.isActive = false, + }); + @override + Widget build(BuildContext context) { + return Column( + children: [ + OptionButton( + isActive: isActive, + onPressed: onPressed, + child: Icon(icon), + ), + SizedBox(height: 12), + Text(title), + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/text_style_editor.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/text_style_editor.dart new file mode 100644 index 0000000..a921e11 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Editor/textstyler/text_style_editor.dart @@ -0,0 +1,223 @@ +library text_style_editor; + +export 'src/toolbar_action.dart'; + +import 'package:flutter/material.dart'; +import 'src/toolbar_action.dart'; +import 'src/tools/background_color_tool.dart'; +import 'src/color_palette.dart'; +import 'src/tools/font_family_tool.dart'; +import 'src/tools/font_size_tool.dart'; +import 'src/tools/text_format_tool.dart'; +import 'src/toolbar.dart'; + +/// Text style editor +/// A flutter widget that edit text style and text alignment +/// +/// You can pass your text style or alignment to the widget +/// and then get the edited text style +class TextStyleEditor extends StatefulWidget { + /// Editor's font families + final List fonts; + + /// The text style + final TextStyle textStyle; + + /// The text alignment + final TextAlign textAlign; + + /// The inithial editor tool + final EditorToolbarAction initialTool; + + /// Editor's palette colors + final List paletteColors; + + /// [onTextStyleEdited] will be called after [textStyle] prop has changed + final Function(TextStyle) onTextStyleEdited; + + /// [onTextAlignEdited] will be called after [textAlingment] prop has changed + final Function(TextAlign) onTextAlignEdited; + + /// [onCpasLockTaggle] will be called after caps lock has changed + final Function(bool) onCpasLockTaggle; + + /// [onToolbarActionChanged] will be called after editor's tool has changed + final Function(EditorToolbarAction) onToolbarActionChanged; + + /// Create a [TextStyleEditor] widget + /// + /// [fonts] list of font families that you want to use in editor. + /// [textStyle] initiate text style. + /// [textAlign] initiate text alignment. + /// + /// [onTextStyleEdited] callback will be called every time [textStyle] has changed. + /// [onTextAlignEdited] callback will be called every time [textAlign] has changed. + /// [onCpasLockTaggle] callback will be called every time caps lock has changed to off or on. + /// [onToolbarActionChanged] callback will be called every time editor's tool has changed. + TextStyleEditor({ + this.fonts, + this.textStyle, + this.textAlign, + this.paletteColors, + this.initialTool = EditorToolbarAction.editor, + this.onTextStyleEdited, + this.onTextAlignEdited, + this.onCpasLockTaggle, + this.onToolbarActionChanged, + }); + + @override + _TextStyleEditorState createState() => _TextStyleEditorState(); +} + +class _TextStyleEditorState extends State { + EditorToolbarAction _currentTool; + TextStyle _textStyle; + TextAlign _textAlign; + List _paletteColors; + + @override + void initState() { + _currentTool = widget.initialTool; + _textStyle = widget.textStyle; + _textAlign = widget.textAlign; + + // Set default palette's colors + _paletteColors = widget.paletteColors ?? + [ + Colors.black, + Colors.white, + Colors.red, + Colors.blue, + Colors.blueAccent, + Colors.brown, + Colors.green, + Colors.indigoAccent, + Colors.lime, + ]; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Toolbar( + initialTool: _currentTool, + onToolSelect: (action) { + setState(() => _currentTool = action); + if (widget.onToolbarActionChanged != null) { + widget.onToolbarActionChanged(action); + } + }, + ), + Divider(), + Container( + child: SingleChildScrollView( + child: () { + // Choice tools + switch (_currentTool) { + case EditorToolbarAction.fontFamilyTool: + return FontFamilyTool( + fonts: widget.fonts, + selectedFont: _textStyle.fontFamily, + onSelectFont: (fontFamily) { + setState(() => _textStyle = + _textStyle.copyWith(fontFamily: fontFamily)); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + ); + case EditorToolbarAction.fontOptionTool: + return TextFormatTool( + bold: _textStyle.fontWeight == FontWeight.bold, + italic: _textStyle.fontStyle == FontStyle.italic, + textAlign: _textAlign, + onTextFormatEdited: (bold, italic) { + setState(() => _textStyle = _textStyle.copyWith( + fontWeight: + bold ? FontWeight.bold : FontWeight.normal, + fontStyle: + italic ? FontStyle.italic : FontStyle.normal, + )); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + onTextAlignEdited: (align) { + setState(() => _textAlign = align); + + if (widget.onTextAlignEdited != null) { + widget.onTextAlignEdited(align); + } + }, + onCpasLockTaggle: (caps) { + if (widget.onCpasLockTaggle != null) { + widget.onCpasLockTaggle(caps); + } + }, + ); + case EditorToolbarAction.fontSizeTool: + return FontSizeTool( + fontSize: _textStyle.fontSize ?? 0, + letterHeight: _textStyle.height ?? 1.2, + letterSpacing: _textStyle.letterSpacing ?? 1, + onFontSizeEdited: ( + fontSize, + letterSpacing, + letterHeight, + ) { + setState(() => _textStyle = _textStyle.copyWith( + fontSize: fontSize, + height: letterHeight, + letterSpacing: letterSpacing, + )); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + ); + case EditorToolbarAction.fontColorTool: + return BackgroundColorTool( + activeColor: _textStyle.color, + colors: _paletteColors, + onColorPicked: (color) { + setState( + () => _textStyle = _textStyle.copyWith(color: color)); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + ); + case EditorToolbarAction.backgroundColorTool: + return ColorPalette( + activeColor: _textStyle.backgroundColor, + colors: _paletteColors, + onColorPicked: (color) { + setState(() => _textStyle = + _textStyle.copyWith(backgroundColor: color)); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + ); + case EditorToolbarAction.editor: + return Container(); + default: + return Container(); + } + }(), + ), + ), + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/RecordVideo.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/RecordVideo.dart new file mode 100644 index 0000000..8180c9c --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/RecordVideo.dart @@ -0,0 +1,499 @@ +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:camera/camera.dart'; +import 'package:flutter/services.dart'; + +import 'package:image_picker/image_picker.dart'; +import 'package:teso/util/consts.dart'; +import 'package:page_transition/page_transition.dart'; +import 'dart:async'; +import 'package:circular_countdown_timer/circular_countdown_timer.dart'; +import 'package:video_thumbnail/video_thumbnail.dart' as thumb; + +import 'Editor/VideoReview.dart'; + +// ignore: must_be_immutable +class RecordVideo extends StatefulWidget { + List connectedCameras; + + RecordVideo({Key key, this.connectedCameras}) : super(key: key); + @override + _RecordVideoState createState() => _RecordVideoState(); +} + +class _RecordVideoState extends State + with TickerProviderStateMixin { + CameraController _controller; + int selectedCamera = 0; + bool flash = false; + bool frontFlash = false; + bool recording = false; + AnimationController _recordingAnimationController; + XFile video; + String filePath; + int recordEnd = 60; + CountDownController _controllerCountDown = CountDownController(); + final interval = const Duration(seconds: 1); + final picker = ImagePicker(); + bool gallery = false; + + final int timerMaxSeconds = 60; + + int currentSeconds = 0; + + flipCamera() { + selectedCamera++; + if (selectedCamera < widget.connectedCameras.length) { + onNewCameraSelected(widget.connectedCameras.elementAt(selectedCamera)); + } else { + selectedCamera = 0; + onNewCameraSelected(widget.connectedCameras.elementAt(selectedCamera)); + } + } + + flashCamera() { + try { + if (!flash && + _controller.description.lensDirection == CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.torch); + setState(() { + flash = true; + frontFlash = false; + }); + } else if (!flash && + _controller.description.lensDirection == CameraLensDirection.front) { + setState(() { + flash = true; + frontFlash = true; + }); + } else if (flash && + _controller.description.lensDirection == CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.off); + setState(() { + flash = false; + }); + } else { + setState(() { + flash = false; + frontFlash = false; + }); + } + } catch (e) {} + } + + haltRecord() async { + double aspect = _controller.value.aspectRatio; + XFile recorded = await stopVideoRecording(); + if (recorded != null) + Navigator.of(context).pushReplacement( + PageRouteBuilder( + opaque: false, + pageBuilder: (_, __, ___) => VideoReview( + video: recorded.path, + aspect: aspect, + // campaignID: widget.campaignID, + recorded: true, + ), + ), + ); + } + + Future generateThumbnail(video) async { + try { + Uint8List thumbnail; + + thumbnail = await thumb.VideoThumbnail.thumbnailData( + video: video, + imageFormat: thumb.ImageFormat.JPEG, + maxWidth: 0, + maxHeight: 0, + timeMs: 1, + quality: 100, + ); + return thumbnail; + } catch (e) { + print("Error :::: " + e); + return null; + } + } + + @override + void initState() { + if (widget.connectedCameras == null || + widget.connectedCameras.length == 0) { + availableCameras().then((value) { + widget.connectedCameras = value; + onNewCameraSelected(widget.connectedCameras.first); + }); + } else { + onNewCameraSelected(widget.connectedCameras.first); + } + _recordingAnimationController = + new AnimationController(vsync: this, duration: Duration(seconds: 1)); + + _recordingAnimationController.repeat(reverse: true); + super.initState(); + } + + sayCheese() async { + try { + if (flash && !frontFlash) + await _controller.setFlashMode(FlashMode.always); + await _controller.startVideoRecording(); + } catch (e) { + print(e); + } + } + + Future stopVideoRecording() async { + if (!_controller.value.isRecordingVideo) { + return null; + } + + try { + return _controller.stopVideoRecording(); + } on CameraException catch (e) { + print(e); + return null; + } + } + + void onNewCameraSelected(CameraDescription cameraDescription) async { + if (_controller != null) { + await _controller.dispose(); + } + _controller = CameraController( + cameraDescription, + ResolutionPreset.max, + enableAudio: true, + imageFormatGroup: ImageFormatGroup.jpeg, + ); + + // If the controller is updated then update the UI. + _controller.addListener(() { + if (mounted) setState(() {}); + if (_controller.value.hasError) { + print('Camera error ${_controller.value.errorDescription}'); + } + }); + + try { + await _controller.initialize(); + _controller.lockCaptureOrientation(DeviceOrientation.portraitUp); + _controller.setFocusMode(FocusMode.auto); + } on CameraException catch (e) { + print(e); + } + + if (mounted) { + setState(() {}); + } + } + + void onHocusFocus(TapDownDetails details, BoxConstraints constraints) { + final offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + _controller.setExposurePoint(offset); + _controller.setFocusPoint(offset); + } + + @override + void dispose() { + _controller?.dispose(); + _recordingAnimationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (_controller == null || !_controller.value.isInitialized) { + return Container( + color: Colors.black, + ); + } else { + return Scaffold( + body: !gallery + ? Stack( + children: [ + cameraWidget(context), + flashWidget(context), + cameraFlip(context), + cameraFlash(context), + recordingAnimation(context), + recordingCircle(context), + recorderWidget(context), + galleryPicker(context), + ], + ) + : Container(), + ); + } + } + + imgFromGallery() async { + try { + setState(() { + gallery = true; + }); + final pickedFile = await picker.pickVideo( + source: ImageSource.gallery, + maxDuration: Duration(minutes: 1), + ); + + if (pickedFile != null) { + return pickedFile.path; + } else { + onNewCameraSelected(widget.connectedCameras.first); + print('No image selected.'); + } + } catch (e) { + print(e); + } + setState(() { + gallery = false; + }); + return; + } + + Widget recordingCircle(context) { + return Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + child: CircularCountDownTimer( + duration: recordEnd, + initialDuration: 0, + controller: _controllerCountDown, + width: MediaQuery.of(context).size.width / 2, + height: MediaQuery.of(context).size.height / 2, + ringColor: Colors.grey[300], + fillColor: Colors.red, + backgroundColor: Colors.transparent, + autoStart: false, + strokeWidth: 5.5, + isTimerTextShown: false, + strokeCap: StrokeCap.round, + //onStart: startTimeout, + onComplete: haltRecord, + ), + ), + ); + } + + Widget recorderWidget(context) { + return Align( + alignment: Alignment.bottomCenter, + child: InkWell( + onTap: recording + ? haltRecord + : () async { + await _controller.startVideoRecording(); + setState(() { + _controllerCountDown.start(); + recording = !recording; + }); + }, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + child: Icon( + recording ? Icons.stop : Icons.video_camera_back, + color: Colors.white, + size: 25, + ), + ), + ), + ); + } + + Widget galleryPicker(context) { + return Align( + alignment: Alignment.bottomLeft, + child: recording + ? Container() + : InkWell( + onTap: () async { + String result = await imgFromGallery(); + if (result != null) { + // _controller.dispose(); + Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: VideoReview( + video: result, + recorded: false, + // campaignID: widget.campaignID, + ), + )); + } + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.05, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + child: Icon( + Icons.photo, + color: Colors.white, + size: 27, + ), + ), + ), + ); + } + + Widget recordingAnimation(context) { + if (!recording) + return Align( + alignment: Alignment.topLeft, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.08, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 35, + width: 35, + decoration: BoxDecoration( + //color: ColorFilterEngineLayer (0, 0, 0, 0.4), + shape: BoxShape.circle), + child: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + ), + )); + else + return Align( + alignment: Alignment.topLeft, + child: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.symmetric( + horizontal: 5, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + padding: EdgeInsets.all(2.5), + height: 20, + width: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.red, + width: 2, + )), + child: FadeTransition( + opacity: _recordingAnimationController, + child: Container( + width: 20, + height: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget cameraFlash(context) { + return !recording + ? Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.07, + vertical: MediaQuery.of(context).size.width * 0.25, + ), + child: InkWell( + onTap: flashCamera, + child: Icon( + flash ? Icons.flash_on : Icons.flash_off, + color: flash ? tesoGold : Colors.white, + size: 30, + ), + ), + ), + ) + : Container(); + } + + Widget cameraFlip(context) { + return !recording + ? Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.06, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + child: InkWell( + onTap: flipCamera, + child: Icon( + Icons.cameraswitch_outlined, + color: Colors.white, + size: 40, + ), + ), + ), + ) + : Container(); + } + + Widget flashWidget(context) { + return Visibility( + visible: frontFlash, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.4), + ), + ), + ); + } + + Widget cameraWidget(context) { + var camera = _controller.value; + final size = MediaQuery.of(context).size; + var scale = size.aspectRatio * camera.aspectRatio; + if (scale < 1) scale = 1 / scale; + + return Transform.scale( + scale: scale, + child: Center( + child: CameraPreview( + _controller, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTapDown: (details) => onHocusFocus(details, constraints), + ); + }), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart new file mode 100644 index 0000000..c5ec37f --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart @@ -0,0 +1,45 @@ +/// The video file formats available for +/// generating the output trimmed video. +/// +/// The available formats are `mp4`, `mkv`, +/// `mov`, `flv`, `avi`, `wmv`& `gif`. +/// +/// If you define a custom `FFmpeg` command +/// then this will be ignored. +/// +class FileFormat { + const FileFormat._(this.index); + + final int index; + + static const FileFormat mp4 = FileFormat._(0); + static const FileFormat mkv = FileFormat._(1); + static const FileFormat mov = FileFormat._(2); + static const FileFormat flv = FileFormat._(3); + static const FileFormat avi = FileFormat._(4); + static const FileFormat wmv = FileFormat._(5); + static const FileFormat gif = FileFormat._(6); + + static const List values = [ + mp4, + mkv, + mov, + flv, + avi, + wmv, + gif, + ]; + + @override + String toString() { + return const { + 0: '.mp4', + 1: '.mkv', + 2: '.mov', + 3: '.flv', + 4: '.avi', + 5: '.wmv', + 6: '.gif', + }[index]; + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/storage_dir.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/storage_dir.dart new file mode 100644 index 0000000..3c15be3 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/storage_dir.dart @@ -0,0 +1,32 @@ +/// Supported storage locations. +/// +/// * [temporaryDirectory] +/// +/// * [applicationDocumentsDirectory] +/// +/// * [externalStorageDirectory] +/// +class StorageDir { + const StorageDir._(this.index); + + final int index; + + static const StorageDir temporaryDirectory = StorageDir._(0); + static const StorageDir applicationDocumentsDirectory = StorageDir._(1); + static const StorageDir externalStorageDirectory = StorageDir._(2); + + static const List values = [ + temporaryDirectory, + applicationDocumentsDirectory, + externalStorageDirectory, + ]; + + @override + String toString() { + return const { + 0: 'temporaryDirectory', + 1: 'applicationDocumentsDirectory', + 2: 'externalStorageDirectory', + }[index]; + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/thumbnail_viewer.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/thumbnail_viewer.dart new file mode 100644 index 0000000..c31a705 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/thumbnail_viewer.dart @@ -0,0 +1,81 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:video_thumbnail/video_thumbnail.dart'; + +class ThumbnailViewer extends StatelessWidget { + final videoFile; + final videoDuration; + final thumbnailHeight; + final fit; + final int numberOfThumbnails; + final int quality; + + /// For showing the thumbnails generated from the video, + /// like a frame by frame preview + ThumbnailViewer({ + @required this.videoFile, + @required this.videoDuration, + @required this.thumbnailHeight, + @required this.numberOfThumbnails, + @required this.fit, + this.quality = 75, + }) : assert(videoFile != null), + assert(videoDuration != null), + assert(thumbnailHeight != null), + assert(numberOfThumbnails != null), + assert(quality != null); + + Stream> generateThumbnail() async* { + final String _videoPath = videoFile.path; + + double _eachPart = videoDuration / numberOfThumbnails; + + List _byteList = []; + + for (int i = 1; i <= numberOfThumbnails; i++) { + Uint8List _bytes; + _bytes = await VideoThumbnail.thumbnailData( + video: _videoPath, + imageFormat: ImageFormat.JPEG, + timeMs: (_eachPart * i).toInt(), + quality: quality, + ); + + _byteList.add(_bytes); + + yield _byteList; + } + } + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: generateThumbnail(), + builder: (context, snapshot) { + if (snapshot.hasData) { + List _imageBytes = snapshot.data; + return ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: snapshot.data.length, + itemBuilder: (context, index) { + return Container( + height: thumbnailHeight, + width: thumbnailHeight, + child: Image( + image: MemoryImage(_imageBytes[index]), + fit: fit, + ), + ); + }); + } else { + return Container( + color: Colors.grey[900], + height: thumbnailHeight, + width: double.maxFinite, + ); + } + }, + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart new file mode 100644 index 0000000..bb5f459 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart @@ -0,0 +1,537 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/thumbnail_viewer.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor_painter.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart'; +import 'package:video_player/video_player.dart'; + +VideoPlayerController videoPlayerController; + +class TrimEditor extends StatefulWidget { + /// For defining the total trimmer area width + final double viewerWidth; + + /// For defining the total trimmer area height + final double viewerHeight; + + /// For defining the image fit type of each thumbnail image. + /// + /// By default it is set to `BoxFit.fitHeight`. + final BoxFit fit; + + /// For defining the maximum length of the output video. + final Duration maxVideoLength; + + /// For specifying a size to the holder at the + /// two ends of the video trimmer area, while it is `idle`. + /// + /// By default it is set to `5.0`. + final double circleSize; + + /// For specifying a size to the holder at + /// the two ends of the video trimmer area, while it is being + /// `dragged`. + /// + /// By default it is set to `8.0`. + final double circleSizeOnDrag; + + /// For specifying a color to the circle. + /// + /// By default it is set to `Colors.white`. + final Color circlePaintColor; + + /// For specifying a color to the border of + /// the trim area. + /// + /// By default it is set to `Colors.white`. + final Color borderPaintColor; + + /// For specifying a color to the video + /// scrubber inside the trim area. + /// + /// By default it is set to `Colors.white`. + final Color scrubberPaintColor; + + /// For specifying the quality of each + /// generated image thumbnail, to be displayed in the trimmer + /// area. + final int thumbnailQuality; + + /// For showing the start and the end point of the + /// video on top of the trimmer area. + /// + /// By default it is set to `true`. + final bool showDuration; + + /// For providing a `TextStyle` to the + /// duration text. + /// + /// By default it is set to `TextStyle(color: Colors.white)` + final TextStyle durationTextStyle; + + /// Callback to the video start position + /// + /// Returns the selected video start position in `milliseconds`. + final Function(double startValue) onChangeStart; + + /// Callback to the video end position. + /// + /// Returns the selected video end position in `milliseconds`. + final Function(double endValue) onChangeEnd; + + /// Callback to the video playback + /// state to know whether it is currently playing or paused. + /// + /// Returns a `boolean` value. If `true`, video is currently + /// playing, otherwise paused. + final Function(bool isPlaying) onChangePlaybackState; + + /// Widget for displaying the video trimmer. + /// + /// This has frame wise preview of the video with a + /// slider for selecting the part of the video to be + /// trimmed. + /// + /// The required parameters are [viewerWidth] & [viewerHeight] + /// + /// * [viewerWidth] to define the total trimmer area width. + /// + /// + /// * [viewerHeight] to define the total trimmer area height. + /// + /// + /// The optional parameters are: + /// + /// * [fit] for specifying the image fit type of each thumbnail image. + /// By default it is set to `BoxFit.fitHeight`. + /// + /// + /// * [maxVideoLength] for specifying the maximum length of the + /// output video. + /// + /// + /// * [circleSize] for specifying a size to the holder at the + /// two ends of the video trimmer area, while it is `idle`. + /// By default it is set to `5.0`. + /// + /// + /// * [circleSizeOnDrag] for specifying a size to the holder at + /// the two ends of the video trimmer area, while it is being + /// `dragged`. By default it is set to `8.0`. + /// + /// + /// * [circlePaintColor] for specifying a color to the circle. + /// By default it is set to `Colors.white`. + /// + /// + /// * [borderPaintColor] for specifying a color to the border of + /// the trim area. By default it is set to `Colors.white`. + /// + /// + /// * [scrubberPaintColor] for specifying a color to the video + /// scrubber inside the trim area. By default it is set to + /// `Colors.white`. + /// + /// + /// * [thumbnailQuality] for specifying the quality of each + /// generated image thumbnail, to be displayed in the trimmer + /// area. + /// + /// + /// * [showDuration] for showing the start and the end point of the + /// video on top of the trimmer area. By default it is set to `true`. + /// + /// + /// * [durationTextStyle] is for providing a `TextStyle` to the + /// duration text. By default it is set to + /// `TextStyle(color: Colors.white)` + /// + /// + /// * [onChangeStart] is a callback to the video start position. + /// + /// + /// * [onChangeEnd] is a callback to the video end position. + /// + /// + /// * [onChangePlaybackState] is a callback to the video playback + /// state to know whether it is currently playing or paused. + /// + TrimEditor({ + @required this.viewerWidth, + @required this.viewerHeight, + this.fit = BoxFit.fitHeight, + this.maxVideoLength = const Duration(milliseconds: 0), + this.circleSize = 5.0, + this.circleSizeOnDrag = 8.0, + this.circlePaintColor = Colors.white, + this.borderPaintColor = Colors.white, + this.scrubberPaintColor = Colors.white, + this.thumbnailQuality = 75, + this.showDuration = true, + this.durationTextStyle = const TextStyle( + color: Colors.white, + ), + this.onChangeStart, + this.onChangeEnd, + this.onChangePlaybackState, + }) : assert(viewerWidth != null), + assert(viewerHeight != null), + assert(fit != null), + assert(maxVideoLength != null), + assert(circleSize != null), + assert(circleSizeOnDrag != null), + assert(circlePaintColor != null), + assert(borderPaintColor != null), + assert(scrubberPaintColor != null), + assert(thumbnailQuality != null), + assert(showDuration != null), + assert(durationTextStyle != null); + + @override + _TrimEditorState createState() => _TrimEditorState(); +} + +class _TrimEditorState extends State with TickerProviderStateMixin { + File _videoFile; + + double _videoStartPos = 0.0; + double _videoEndPos = 0.0; + + bool _canUpdateStart = true; + bool _isLeftDrag = true; + + Offset _startPos = Offset(0, 0); + Offset _endPos = Offset(0, 0); + + double _startFraction = 0.0; + double _endFraction = 1.0; + + int _videoDuration = 0; + int _currentPosition = 0; + + double _thumbnailViewerW = 0.0; + double _thumbnailViewerH = 0.0; + + int _numberOfThumbnails = 0; + + double _circleSize; + + double fraction; + double maxLengthPixels; + + ThumbnailViewer thumbnailWidget; + + Animation _scrubberAnimation; + AnimationController _animationController; + Tween _linearTween; + + Future _initializeVideoController() async { + if (_videoFile != null) { + videoPlayerController.addListener(() { + final bool isPlaying = videoPlayerController.value.isPlaying; + + if (isPlaying) { + widget.onChangePlaybackState(true); + setState(() { + _currentPosition = + videoPlayerController.value.position.inMilliseconds; + + if (_currentPosition > _videoEndPos.toInt()) { + widget.onChangePlaybackState(false); + videoPlayerController.pause(); + _animationController.stop(); + } else { + if (!_animationController.isAnimating) { + widget.onChangePlaybackState(true); + _animationController.forward(); + } + } + }); + } else { + if (videoPlayerController.value.isInitialized) { + if (_animationController != null) { + if ((_scrubberAnimation.value).toInt() == (_endPos.dx).toInt()) { + _animationController.reset(); + } + _animationController.stop(); + widget.onChangePlaybackState(false); + } + } + } + }); + + videoPlayerController.setVolume(1.0); + _videoDuration = videoPlayerController.value.duration.inMilliseconds; + print(_videoFile.path); + + _videoEndPos = fraction != null + ? _videoDuration.toDouble() * fraction + : _videoDuration.toDouble(); + + widget.onChangeEnd(_videoEndPos); + + final ThumbnailViewer _thumbnailWidget = ThumbnailViewer( + videoFile: _videoFile, + videoDuration: _videoDuration, + fit: widget.fit, + thumbnailHeight: _thumbnailViewerH, + numberOfThumbnails: _numberOfThumbnails, + quality: widget.thumbnailQuality, + ); + thumbnailWidget = _thumbnailWidget; + } + } + + void _setVideoStartPosition(DragUpdateDetails details) async { + if (!(_startPos.dx + details.delta.dx < 0) && + !(_startPos.dx + details.delta.dx > _thumbnailViewerW) && + !(_startPos.dx + details.delta.dx > _endPos.dx)) { + if (maxLengthPixels != null) { + if (!(_endPos.dx - _startPos.dx - details.delta.dx > maxLengthPixels)) { + setState(() { + if (!(_startPos.dx + details.delta.dx < 0)) + _startPos += details.delta; + + _startFraction = (_startPos.dx / _thumbnailViewerW); + + _videoStartPos = _videoDuration * _startFraction; + widget.onChangeStart(_videoStartPos); + }); + await videoPlayerController.pause(); + await videoPlayerController + .seekTo(Duration(milliseconds: _videoStartPos.toInt())); + _linearTween.begin = _startPos.dx; + _animationController.duration = + Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); + _animationController.reset(); + } + } else { + setState(() { + if (!(_startPos.dx + details.delta.dx < 0)) + _startPos += details.delta; + + _startFraction = (_startPos.dx / _thumbnailViewerW); + + _videoStartPos = _videoDuration * _startFraction; + widget.onChangeStart(_videoStartPos); + }); + await videoPlayerController.pause(); + await videoPlayerController + .seekTo(Duration(milliseconds: _videoStartPos.toInt())); + _linearTween.begin = _startPos.dx; + _animationController.duration = + Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); + _animationController.reset(); + } + } + } + + void _setVideoEndPosition(DragUpdateDetails details) async { + if (!(_endPos.dx + details.delta.dx > _thumbnailViewerW) && + !(_endPos.dx + details.delta.dx < 0) && + !(_endPos.dx + details.delta.dx < _startPos.dx)) { + if (maxLengthPixels != null) { + if (!(_endPos.dx - _startPos.dx + details.delta.dx > maxLengthPixels)) { + setState(() { + _endPos += details.delta; + _endFraction = _endPos.dx / _thumbnailViewerW; + + _videoEndPos = _videoDuration * _endFraction; + widget.onChangeEnd(_videoEndPos); + }); + await videoPlayerController.pause(); + await videoPlayerController + .seekTo(Duration(milliseconds: _videoEndPos.toInt())); + _linearTween.end = _endPos.dx; + _animationController.duration = + Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); + _animationController.reset(); + } + } else { + setState(() { + _endPos += details.delta; + _endFraction = _endPos.dx / _thumbnailViewerW; + + _videoEndPos = _videoDuration * _endFraction; + widget.onChangeEnd(_videoEndPos); + }); + await videoPlayerController.pause(); + await videoPlayerController + .seekTo(Duration(milliseconds: _videoEndPos.toInt())); + _linearTween.end = _endPos.dx; + _animationController.duration = + Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); + _animationController.reset(); + } + } + } + + @override + void initState() { + super.initState(); + _circleSize = widget.circleSize; + + _videoFile = Trimmer.currentVideoFile; + _thumbnailViewerH = widget.viewerHeight; + + _numberOfThumbnails = widget.viewerWidth ~/ _thumbnailViewerH; + + _thumbnailViewerW = _numberOfThumbnails * _thumbnailViewerH; + + Duration totalDuration = videoPlayerController.value.duration; + + if (widget.maxVideoLength > Duration(milliseconds: 0) && + widget.maxVideoLength < totalDuration) { + if (widget.maxVideoLength < totalDuration) { + fraction = + widget.maxVideoLength.inMilliseconds / totalDuration.inMilliseconds; + + maxLengthPixels = _thumbnailViewerW * fraction; + } + } + + _initializeVideoController(); + _endPos = Offset( + maxLengthPixels != null ? maxLengthPixels : _thumbnailViewerW, + _thumbnailViewerH, + ); + + // Defining the tween points + _linearTween = Tween(begin: _startPos.dx, end: _endPos.dx); + + _animationController = AnimationController( + vsync: this, + duration: Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()), + ); + + _scrubberAnimation = _linearTween.animate(_animationController) + ..addListener(() { + setState(() {}); + }) + ..addStatusListener((status) { + if (status == AnimationStatus.completed) { + _animationController.stop(); + } + }); + } + + @override + void dispose() { + videoPlayerController.pause(); + widget.onChangePlaybackState(false); + if (_videoFile != null) { + videoPlayerController.setVolume(0.0); + videoPlayerController.pause(); + videoPlayerController.dispose(); + widget.onChangePlaybackState(false); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onHorizontalDragStart: (DragStartDetails details) { + print("START"); + print(details.localPosition); + print((_startPos.dx - details.localPosition.dx).abs()); + print((_endPos.dx - details.localPosition.dx).abs()); + + if (_endPos.dx >= _startPos.dx) { + if ((_startPos.dx - details.localPosition.dx).abs() > + (_endPos.dx - details.localPosition.dx).abs()) { + setState(() { + _canUpdateStart = false; + }); + } else { + setState(() { + _canUpdateStart = true; + }); + } + } else { + if (_startPos.dx > details.localPosition.dx) { + _isLeftDrag = true; + } else { + _isLeftDrag = false; + } + } + }, + onHorizontalDragEnd: (DragEndDetails details) { + setState(() { + _circleSize = widget.circleSize; + }); + }, + onHorizontalDragUpdate: (DragUpdateDetails details) { + _circleSize = widget.circleSizeOnDrag; + + if (_endPos.dx >= _startPos.dx) { + _isLeftDrag = false; + if (_canUpdateStart && _startPos.dx + details.delta.dx > 0) { + _isLeftDrag = false; // To prevent from scrolling over + _setVideoStartPosition(details); + } else if (!_canUpdateStart && + _endPos.dx + details.delta.dx < _thumbnailViewerW) { + _isLeftDrag = true; // To prevent from scrolling over + _setVideoEndPosition(details); + } + } else { + if (_isLeftDrag && _startPos.dx + details.delta.dx > 0) { + _setVideoStartPosition(details); + } else if (!_isLeftDrag && + _endPos.dx + details.delta.dx < _thumbnailViewerW) { + _setVideoEndPosition(details); + } + } + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + widget.showDuration + ? Container( + width: _thumbnailViewerW, + child: Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + Duration(milliseconds: _videoStartPos.toInt()) + .toString() + .split('.')[0], + style: widget.durationTextStyle, + ), + Text( + Duration(milliseconds: _videoEndPos.toInt()) + .toString() + .split('.')[0], + style: widget.durationTextStyle, + ), + ], + ), + ), + ) + : Container(), + CustomPaint( + foregroundPainter: TrimEditorPainter( + startPos: _startPos, + endPos: _endPos, + scrubberAnimationDx: _scrubberAnimation.value, + circleSize: _circleSize, + circlePaintColor: widget.circlePaintColor, + borderPaintColor: widget.borderPaintColor, + scrubberPaintColor: widget.scrubberPaintColor, + ), + child: Container( + color: Colors.grey[900], + height: _thumbnailViewerH, + width: _thumbnailViewerW, + child: thumbnailWidget == null ? Column() : thumbnailWidget, + ), + ), + ], + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor_painter.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor_painter.dart new file mode 100644 index 0000000..91df007 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor_painter.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; + +class TrimEditorPainter extends CustomPainter { + /// To define the start offset + final Offset startPos; + + /// To define the end offset + final Offset endPos; + + /// To define the horizontal length of the selected video area + final double scrubberAnimationDx; + + /// For specifying a size to the holder at the + /// two ends of the video trimmer area, while it is `idle`. + /// By default it is set to `0.5`. + final double circleSize; + + /// For specifying the width of the border around + /// the trim area. By default it is set to `3`. + final double borderWidth; + + /// For specifying the width of the video scrubber + final double scrubberWidth; + + /// For specifying whether to show the scrubber + final bool showScrubber; + + /// For specifying a color to the border of + /// the trim area. By default it is set to `Colors.white`. + final Color borderPaintColor; + + /// For specifying a color to the circle. + /// By default it is set to `Colors.white` + final Color circlePaintColor; + + /// For specifying a color to the video + /// scrubber inside the trim area. By default it is set to + /// `Colors.white`. + final Color scrubberPaintColor; + + /// For drawing the trim editor slider + /// + /// The required parameters are [startPos], [endPos] + /// & [scrubberAnimationDx] + /// + /// * [startPos] to define the start offset + /// + /// + /// * [endPos] to define the end offset + /// + /// + /// * [scrubberAnimationDx] to define the horizontal length of the + /// selected video area + /// + /// + /// The optional parameters are: + /// + /// * [circleSize] for specifying a size to the holder at the + /// two ends of the video trimmer area, while it is `idle`. + /// By default it is set to `0.5`. + /// + /// + /// * [borderWidth] for specifying the width of the border around + /// the trim area. By default it is set to `3`. + /// + /// + /// * [scrubberWidth] for specifying the width of the video scrubber + /// + /// + /// * [showScrubber] for specifying whether to show the scrubber + /// + /// + /// * [borderPaintColor] for specifying a color to the border of + /// the trim area. By default it is set to `Colors.white`. + /// + /// + /// * [circlePaintColor] for specifying a color to the circle. + /// By default it is set to `Colors.white`. + /// + /// + /// * [scrubberPaintColor] for specifying a color to the video + /// scrubber inside the trim area. By default it is set to + /// `Colors.white`. + /// + TrimEditorPainter({ + @required this.startPos, + @required this.endPos, + @required this.scrubberAnimationDx, + this.circleSize = 0.5, + this.borderWidth = 3, + this.scrubberWidth = 1, + this.showScrubber = true, + this.borderPaintColor = Colors.white, + this.circlePaintColor = Colors.white, + this.scrubberPaintColor = Colors.white, + }) : assert(startPos != null), + assert(endPos != null), + assert(scrubberAnimationDx != null), + assert(circleSize != null), + assert(borderWidth != null), + assert(scrubberWidth != null), + assert(showScrubber != null), + assert(borderPaintColor != null), + assert(circlePaintColor != null), + assert(scrubberPaintColor != null); + + @override + void paint(Canvas canvas, Size size) { + var borderPaint = Paint() + ..color = borderPaintColor + ..strokeWidth = borderWidth + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + var circlePaint = Paint() + ..color = circlePaintColor + ..strokeWidth = 1 + ..style = PaintingStyle.fill + ..strokeCap = StrokeCap.round; + + var scrubberPaint = Paint() + ..color = scrubberPaintColor + ..strokeWidth = scrubberWidth + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + final rect = Rect.fromPoints(startPos, endPos); + + if (showScrubber) { + if (scrubberAnimationDx.toInt() > startPos.dx.toInt()) { + canvas.drawLine( + Offset(scrubberAnimationDx, 0), + Offset(scrubberAnimationDx, 0) + Offset(0, endPos.dy), + scrubberPaint, + ); + } + } + + canvas.drawRect(rect, borderPaint); + canvas.drawCircle( + startPos + Offset(0, endPos.dy / 2), circleSize, circlePaint); + canvas.drawCircle( + endPos + Offset(0, -endPos.dy / 2), circleSize, circlePaint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart new file mode 100644 index 0000000..dfdf121 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart @@ -0,0 +1,300 @@ +import 'dart:io'; +import 'package:path/path.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; +import 'package:intl/intl.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/storage_dir.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart'; +import 'package:video_player/video_player.dart'; + +/// Helps in loading video from file, saving trimmed video to a file +/// and gives video playback controls. Some of the helpful methods +/// are: +/// * [loadVideo()] +/// * [saveTrimmedVideo()] +/// * [videPlaybackControl()] +class Trimmer { + static File currentVideoFile; + + final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg(); + + /// Loads a video using the path provided. + /// + /// Returns the loaded video file. + Future loadVideo({@required File videoFile}) async { + currentVideoFile = videoFile; + if (currentVideoFile != null) { + videoPlayerController = VideoPlayerController.file(currentVideoFile); + await videoPlayerController.initialize().then((_) { + TrimEditor( + viewerHeight: 50, + viewerWidth: 50.0 * 8, + // currentVideoFile: currentVideoFile, + ); + }); + // TrimEditor( + // viewerHeight: 50, + // viewerWidth: 50.0 * 8, + // // currentVideoFile: currentVideoFile, + // ); + } + } + + Future _createFolderInAppDocDir( + String folderName, + StorageDir storageDir, + ) async { + Directory _directory; + + if (storageDir == null) { + _directory = await getApplicationDocumentsDirectory(); + } else { + switch (storageDir.toString()) { + case 'temporaryDirectory': + _directory = await getTemporaryDirectory(); + break; + + case 'applicationDocumentsDirectory': + _directory = await getApplicationDocumentsDirectory(); + break; + + case 'externalStorageDirectory': + _directory = await getExternalStorageDirectory(); + break; + } + } + + // Directory + folder name + final Directory _directoryFolder = + Directory('${_directory.path}/$folderName/'); + + if (await _directoryFolder.exists()) { + // If folder already exists return path + print('Exists'); + return _directoryFolder.path; + } else { + print('Creating'); + // If folder does not exists create folder and then return its path + final Directory _directoryNewFolder = + await _directoryFolder.create(recursive: true); + return _directoryNewFolder.path; + } + } + + /// Saves the trimmed video to file system. + /// + /// Returns the output video path + /// + /// The required parameters are [startValue] & [endValue]. + /// + /// The optional parameters are [videoFolderName], [videoFileName], + /// [outputFormat], [fpsGIF], [scaleGIF], [applyVideoEncoding]. + /// + /// The `@required` parameter [startValue] is for providing a starting point + /// to the trimmed video. To be specified in `milliseconds`. + /// + /// The `@required` parameter [endValue] is for providing an ending point + /// to the trimmed video. To be specified in `milliseconds`. + /// + /// The parameter [videoFolderName] is used to + /// pass a folder name which will be used for creating a new + /// folder in the selected directory. The default value for + /// it is `Trimmer`. + /// + /// The parameter [videoFileName] is used for giving + /// a new name to the trimmed video file. By default the + /// trimmed video is named as `_trimmed.mp4`. + /// + /// The parameter [outputFormat] is used for providing a + /// file format to the trimmed video. This only accepts value + /// of [FileFormat] type. By default it is set to `FileFormat.mp4`, + /// which is for `mp4` files. + /// + /// The parameter [storageDir] can be used for providing a storage + /// location option. It accepts only [StorageDir] values. By default + /// it is set to [applicationDocumentsDirectory]. Some of the + /// storage types are: + /// + /// * [temporaryDirectory] (Only accessible from inside the app, can be + /// cleared at anytime) + /// + /// * [applicationDocumentsDirectory] (Only accessible from inside the app) + /// + /// * [externalStorageDirectory] (Supports only `Android`, accessible externally) + /// + /// The parameters [fpsGIF] & [scaleGIF] are used only if the + /// selected output format is `FileFormat.gif`. + /// + /// * [fpsGIF] for providing a FPS value (by default it is set + /// to `10`) + /// + /// + /// * [scaleGIF] for proving a width to output GIF, the height + /// is selected by maintaining the aspect ratio automatically (by + /// default it is set to `480`) + /// + /// + /// * [applyVideoEncoding] for specifying whether to apply video + /// encoding (by default it is set to `false`). + /// + /// + /// ADVANCED OPTION: + /// + /// If you want to give custom `FFmpeg` command, then define + /// [ffmpegCommand] & [customVideoFormat] strings. The `input path`, + /// `output path`, `start` and `end` position is already define. + /// + /// NOTE: The advanced option does not provide any safety check, so if wrong + /// video format is passed in [customVideoFormat], then the app may + /// crash. + /// + Future saveTrimmedVideo({ + @required double startValue, + @required double endValue, + bool applyVideoEncoding = false, + FileFormat outputFormat, + String ffmpegCommand, + String customVideoFormat, + int fpsGIF, + int scaleGIF, + String videoFolderName, + String videoFileName, + StorageDir storageDir, + }) async { + final String _videoPath = currentVideoFile.path; + final String _videoName = basename(_videoPath).split('.')[0]; + + String _command; + + // Formatting Date and Time + String dateTime = DateFormat.yMMMd() + .addPattern('-') + .add_Hms() + .format(DateTime.now()) + .toString(); + + // String _resultString; + String _outputPath; + String _outputFormatString; + String formattedDateTime = dateTime.replaceAll(' ', ''); + + print("DateTime: $dateTime"); + print("Formatted: $formattedDateTime"); + + if (videoFolderName == null) { + videoFolderName = "Trimmer"; + } + + if (videoFileName == null) { + videoFileName = "${_videoName}_trimmed:$formattedDateTime"; + } + + videoFileName = videoFileName.replaceAll(' ', '_'); + + String path = await _createFolderInAppDocDir( + videoFolderName, + storageDir, + ).whenComplete( + () => print("Retrieved Trimmer folder"), + ); + + Duration startPoint = Duration(milliseconds: startValue.toInt()); + Duration endPoint = Duration(milliseconds: endValue.toInt()); + + // Checking the start and end point strings + print("Start: ${startPoint.toString()} & End: ${endPoint.toString()}"); + + print(path); + + if (outputFormat == null) { + if (Platform.isIOS) { + outputFormat = FileFormat.mp4; + } else { + outputFormat = FileFormat.mkv; + } + _outputFormatString = outputFormat.toString(); + print('OUTPUT: $_outputFormatString'); + } else { + _outputFormatString = outputFormat.toString(); + } + + String _trimLengthCommand = + ' -ss $startPoint -i "$_videoPath" -t ${endPoint - startPoint} -avoid_negative_ts make_zero '; + + if (ffmpegCommand == null) { + _command = '$_trimLengthCommand -c:a copy '; + + if (!applyVideoEncoding) { + _command += '-c:v copy '; + } + + if (outputFormat == FileFormat.gif) { + if (fpsGIF == null) { + fpsGIF = 10; + } + if (scaleGIF == null) { + scaleGIF = 480; + } + _command = + '$_trimLengthCommand -vf "fps=$fpsGIF,scale=$scaleGIF:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 '; + } + } else { + _command = '$_trimLengthCommand $ffmpegCommand '; + _outputFormatString = customVideoFormat; + } + + _outputPath = '$path$videoFileName$_outputFormatString'; + + _command += '"$_outputPath"'; + print(_command); + await _flutterFFmpeg.execute(_command).whenComplete(() { + print('Got value'); + debugPrint('Video successfuly saved'); + // _resultString = 'Video successfuly saved'; + }).catchError((error) { + print('Error'); + // _resultString = 'Couldn\'t save the video'; + debugPrint('Couldn\'t save the video'); + }); + + return _outputPath; + } + + /// For getting the video controller state, to know whether the + /// video is playing or paused currently. + /// + /// The two required parameters are [startValue] & [endValue] + /// + /// * [startValue] is the current starting point of the video. + /// * [endValue] is the current ending point of the video. + /// + /// Returns a `Future`, if `true` then video is playing + /// otherwise paused. + Future videPlaybackControl({ + @required double startValue, + @required double endValue, + }) async { + if (videoPlayerController.value.isPlaying) { + await videoPlayerController.pause(); + return false; + } else { + if (videoPlayerController.value.position.inMilliseconds >= + endValue.toInt()) { + await videoPlayerController + .seekTo(Duration(milliseconds: startValue.toInt())); + await videoPlayerController.play(); + return true; + } else { + await videoPlayerController.play(); + return true; + } + } + } + + File getVideoFile() { + return currentVideoFile; + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/Error.dart b/lib/Pages/Sub_Pages/@Generic/Error.dart new file mode 100644 index 0000000..b9c584e --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/Error.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class ErrorPage extends StatefulWidget { + final String error; + + const ErrorPage({Key key, this.error}) : super(key: key); + @override + _ErrorPageState createState() => _ErrorPageState(); +} + +class _ErrorPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Text( + widget.error, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/ErrorRedeem.dart b/lib/Pages/Sub_Pages/@Generic/ErrorRedeem.dart new file mode 100644 index 0000000..8a5ff55 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/ErrorRedeem.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +import 'package:teso/util/consts.dart'; + +class ErrorRedeem extends StatefulWidget { + @override + _ErrorRedeemState createState() => _ErrorRedeemState(); +} + +class _ErrorRedeemState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Container( + width: 300, + height: 200, + child: Column( + children: [ + Container( + child: Icon( + Icons.error, + color: Colors.red, + ), + ), + Container( + child: Text( + "An error occurred while trying to redeem coupon please try again !!!"), + ), + SizedBox( + height: 30, + ), + Container( + width: double.infinity, + child: Align( + alignment: Alignment.center, + child: Container( + width: 100, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + primary: accentMain, + ), + onPressed: () => Navigator.pop(context), + child: Text("Try again"), + ), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/ProductImage.dart b/lib/Pages/Sub_Pages/@Generic/ProductImage.dart new file mode 100644 index 0000000..12ed3f8 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/ProductImage.dart @@ -0,0 +1,85 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +class ProductImage extends StatefulWidget { + final productTag; + final productImageSRC; + const ProductImage( + {Key key, @required this.productTag, @required this.productImageSRC}) + : super(key: key); + + @override + _ProductImageState createState() => _ProductImageState(); +} + +class _ProductImageState extends State { + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + body: Stack( + children: [ + Container( + margin: EdgeInsets.symmetric( + vertical: SizeConfig.safeBlockHorizontal * 8), + child: new IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Container( + width: 35, + height: 35, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + child: new Icon( + Icons.arrow_back, + color: Colors.white, + size: 20.0, + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + onVerticalDragUpdate: (details) { + int sensitivity = 8; + if (details.delta.dy < -sensitivity) { + Navigator.pop(context); + } + }, + child: Center( + child: Hero( + tag: widget.productTag, + child: CachedNetworkImage( + imageUrl: tesoProductThumbnail( + productLogo: widget.productImageSRC), + imageBuilder: (context, imageProvider) => Image( + fit: BoxFit.fill, + image: imageProvider, + ), + placeholder: (context, url) => Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/RedeemingCoupon.dart b/lib/Pages/Sub_Pages/@Generic/RedeemingCoupon.dart new file mode 100644 index 0000000..964007b --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/RedeemingCoupon.dart @@ -0,0 +1,49 @@ +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; + +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/providers/user_provider.dart'; + +class RedeemingCoupon extends StatefulWidget { + final CouponDetails couponDetails; + + const RedeemingCoupon({Key key, @required this.couponDetails}) + : super(key: key); + @override + _RedeemingCouponState createState() => _RedeemingCouponState(); +} + +class _RedeemingCouponState extends State { + @override + void initState() { + super.initState(); + Provider.of(context, listen: false) + .redeemCoupon(widget.couponDetails, context); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Container( + width: 200, + height: 200, + child: Column( + children: [ + Container( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + Container( + child: Text("Please wait, redeeming coupon!!"), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/SuccessRedeem.dart b/lib/Pages/Sub_Pages/@Generic/SuccessRedeem.dart new file mode 100644 index 0000000..0546a47 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/SuccessRedeem.dart @@ -0,0 +1,376 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +import 'package:jiffy/jiffy.dart'; +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/util/consts.dart'; + +class SuccessfullyRedeemed extends StatefulWidget { + final CouponDetails couponDetails; + final int returns; + + const SuccessfullyRedeemed({ + Key key, + @required this.couponDetails, + this.returns, + }) : super(key: key); + @override + _SuccessfullyRedeemedState createState() => _SuccessfullyRedeemedState(); +} + +class _SuccessfullyRedeemedState extends State { + var timestamp; + @override + void initState() { + super.initState(); + timestamp = Jiffy(DateTime.now().toString()).yMMMMEEEEdjm; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: ListView( + scrollDirection: Axis.vertical, + children: [ + Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + top: 20, + ), + height: 200, + child: Center( + child: Container( + width: 200, + height: 200, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: CachedNetworkImage( + imageUrl: widget.couponDetails.targetProduct != null + ? productURL + + widget.couponDetails.targetProduct.productImage + : "", + imageBuilder: (context, imageProvider) => Image( + width: MediaQuery.of(context).size.width * 0.28, + height: 110, + fit: BoxFit.fill, + image: imageProvider, + ), + ), + ), + ), + ), + ), + SizedBox( + height: 20, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Product ID : ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan( + text: widget.couponDetails.targetProduct.productID), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Product Name : ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan( + text: widget.couponDetails.targetProduct.productName), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Shop Name : ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan( + text: widget.couponDetails.issuer.businessName), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Coupon ID : ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan(text: widget.couponDetails.couponId), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Timestamp : ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan(text: timestamp), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Product Cost : GH¢ ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan( + text: (widget.couponDetails.productCost) + .toStringAsFixed(2)), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Discount : GH¢ ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan( + text: ((widget.couponDetails.productCost * + (widget.couponDetails.worth / 100))) + .toStringAsFixed(2), + style: new TextStyle( + color: Colors.green, + )), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Coin Refund : ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan(text: widget.returns.toString()), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Amount Due : GH¢ ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan( + text: (widget.couponDetails.productCost - + (widget.couponDetails.productCost * + (widget.couponDetails.worth / 100))) + .toStringAsFixed(2), + style: new TextStyle( + color: Colors.red, + )), + ], + ), + ), + ), + ), + Container( + child: Icon( + Icons.check_circle, + color: Colors.green, + size: 30, + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "verified", + style: TextStyle( + color: Colors.green, + ), + ), + ), + ), + SizedBox( + height: 20, + ), + Container( + width: double.infinity, + child: Align( + alignment: Alignment.center, + child: Container( + width: 100, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + primary: accentMain, + ), + onPressed: () => Navigator.pop(context), + child: Text("Close"), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/productDetails.dart b/lib/Pages/Sub_Pages/@Generic/productDetails.dart new file mode 100644 index 0000000..02fd7a3 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/productDetails.dart @@ -0,0 +1,384 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:teso/util/SizeConfig.dart'; + +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/Pages/Sub_Pages/ProductDetails/CouponList.dart'; +import 'dart:convert'; + +import 'ProductImage.dart'; + +class ProductDetails extends StatefulWidget { + final Product product; + + const ProductDetails({Key key, this.product}) : super(key: key); + @override + _ProductDetailsState createState() => _ProductDetailsState(); +} + +enum AppBarBehavior { normal, pinned, floating, snapping } + +class _ProductDetailsState extends State + with TickerProviderStateMixin { + AnimationController _containerController; + Animation width; + Animation heigth; + double _appBarHeight = 256.0; + AppBarBehavior _appBarBehavior = AppBarBehavior.pinned; + List coupons; + int number = 0; + var _future; + + Future> getProductCoupons() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + var register2 = serverLocation + 'coupons/productCoupon'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(widget.product.productID), headers: requestHeaders); + + if (client1.statusCode == 200) { + try { + var details = jsonDecode(client1.body); + coupons = List.from( + details.map((model) => CouponDetails.fromJSON(model)).toList()); + setState(() { + number = coupons.length; + }); + return coupons; + } catch (e) { + print(e); + return null; + } + } else { + return null; + } + } + + @override + void initState() { + _future = getProductCoupons(); + _containerController = new AnimationController( + duration: new Duration(milliseconds: 2000), vsync: this); + super.initState(); + width = new Tween( + begin: 200.0, + end: 220.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth = new Tween( + begin: 400.0, + end: 400.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth.addListener(() { + setState(() { + if (heigth.isCompleted) {} + }); + }); + _containerController.forward(); + } + + @override + void dispose() { + _containerController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return new Theme( + data: new ThemeData( + brightness: Brightness.light, + platform: Theme.of(context).platform, + ), + child: new Container( + width: width.value, + height: heigth.value, + child: new Card( + color: Colors.transparent, + child: new Container( + alignment: Alignment.center, + width: width.value, + height: heigth.value, + decoration: new BoxDecoration( + color: Colors.white, + borderRadius: new BorderRadius.circular(10.0), + ), + child: new Stack( + alignment: AlignmentDirectional.bottomCenter, + children: [ + new CustomScrollView( + shrinkWrap: false, + slivers: [ + new SliverAppBar( + elevation: 0.0, + forceElevated: true, + leading: new IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + child: new Icon( + Icons.arrow_back, + color: Colors.white, + size: 25.0, + ), + ), + ), + expandedHeight: _appBarHeight, + pinned: _appBarBehavior == AppBarBehavior.pinned, + floating: _appBarBehavior == AppBarBehavior.floating || + _appBarBehavior == AppBarBehavior.snapping, + snap: _appBarBehavior == AppBarBehavior.snapping, + backgroundColor: Theme.of(context).backgroundColor, + flexibleSpace: new FlexibleSpaceBar( + background: new Stack( + fit: StackFit.expand, + children: [ + GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) { + return ProductImage( + productTag: widget.product.productID, + productImageSRC: + widget.product.productImage, + ); + })); + }, + child: new Container( + width: width.value, + height: _appBarHeight, + child: Hero( + tag: widget.product.productID, + child: CachedNetworkImage( + imageUrl: tesoProductThumbnail( + productLogo: widget.product.productImage, + width: 640, + height: 640, + ), + placeholder: (context, url) => Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 10, + ), + ), + imageBuilder: (context, imageProvider) => + Image( + fit: BoxFit.contain, + image: imageProvider, + ), + ), + ), + ), + ), + ], + ), + ), + ), + new SliverList( + delegate: new SliverChildListDelegate([ + new Container( + color: Colors.white, + child: new Padding( + padding: const EdgeInsets.all(35.0), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Container( + padding: new EdgeInsets.only(bottom: 20.0), + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.white, + border: new Border( + bottom: new BorderSide( + color: Colors.black12))), + child: new Wrap( + children: [ + new Center( + child: new Text( + widget.product.productName + .toUpperCase(), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: SizeConfig + .blockSizeHorizontal * + 4.0, + ), + ), + ), + new Row( + children: [ + new Icon(Icons.shopping_bag), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text( + "Price : ", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text("GH¢ " + + widget.product.unitPrice + .toString()), + ) + ], + ), + new Row( + children: [ + new Icon( + Icons.shopping_basket, + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text( + "Coupons Available : ", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: + new Text(number.toString()), + ) + ], + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: new Row( + children: [ + new Icon( + Icons.store, + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text( + "Shop Name : ", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text(widget.product + .businessID != + null + ? widget.product.businessID + .toUpperCase() + : ""), + ) + ], + ), + ), + ], + ), + ), + new Padding( + padding: const EdgeInsets.only( + top: 16.0, bottom: 8.0), + child: new Text( + "Product Description", + style: new TextStyle( + fontWeight: FontWeight.bold), + ), + ), + new Text( + widget.product.productDesc != null + ? widget.product.productDesc + : "", + style: TextStyle( + fontSize: + SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + SizedBox( + height: 50, + ), + ], + ), + ), + ), + ), + ]), + ), + ], + ), + FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (coupons == null || number == 0) { + return Container(); + } else { + return Container( + height: 100.0, + padding: EdgeInsets.all(20), + child: new TextButton( + onPressed: () => Navigator.of(context).push( + PageRouteBuilder( + opaque: false, + pageBuilder: (_, __, ___) => CouponList( + couponsList: coupons, + ), + ), + ), + child: new Container( + height: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: tesoGold, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "View Coupons", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ); + } + }), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/prominentDisclosure.dart b/lib/Pages/Sub_Pages/@Generic/prominentDisclosure.dart new file mode 100644 index 0000000..9558af6 --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/prominentDisclosure.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; + +class ProminentDisclosure extends StatefulWidget { + const ProminentDisclosure({Key key}) : super(key: key); + + @override + _ProminentDisclosureState createState() => _ProminentDisclosureState(); +} + +class _ProminentDisclosureState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + margin: EdgeInsets.only(top: 40), + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + alignment: Alignment.center, + child: Column( + children: [ + Icon( + Icons.location_on_sharp, + color: Colors.blue, + size: 50, + ), + SizedBox( + height: 10, + ), + Text( + "Use your location", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 4, + fontWeight: FontWeight.w800, + ), + ), + Container( + padding: EdgeInsets.all( + SizeConfig.blockSizeHorizontal * 3, + ), + child: Text( + "Turn on location and give Teso App access to help you find shops on the map", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.w400, + ), + ), + ), + Container( + width: SizeConfig.blockSizeHorizontal * 50, + height: SizeConfig.blockSizeHorizontal * 70, + child: Image( + image: AssetImage("assets/images/prominent-disclosure.jpg"), + ), + ), + ], + ), + ), + bottomNavigationBar: Padding( + padding: EdgeInsets.all( + SizeConfig.blockSizeHorizontal * 4, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: Text( + "No Thanks", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 4.2, + fontWeight: FontWeight.w800, + ), + ), + ), + TextButton( + onPressed: () => Navigator.pop(context, true), + child: Text( + "Turn on", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 4.2, + fontWeight: FontWeight.w800, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/@Generic/prominentDisclosureBackground.dart b/lib/Pages/Sub_Pages/@Generic/prominentDisclosureBackground.dart new file mode 100644 index 0000000..d4adbad --- /dev/null +++ b/lib/Pages/Sub_Pages/@Generic/prominentDisclosureBackground.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; + +class ProminentDisclosure extends StatefulWidget { + const ProminentDisclosure({Key key}) : super(key: key); + + @override + _ProminentDisclosureState createState() => _ProminentDisclosureState(); +} + +class _ProminentDisclosureState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + margin: EdgeInsets.only(top: 40), + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + alignment: Alignment.center, + child: Column( + children: [ + Icon( + Icons.location_on_sharp, + color: Colors.blue, + size: 50, + ), + SizedBox( + height: 10, + ), + Text( + "Use your location", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 4, + fontWeight: FontWeight.w800, + ), + ), + Container( + padding: EdgeInsets.all( + SizeConfig.blockSizeHorizontal * 3, + ), + child: Text( + "To get proximity coupons from shops, allow Teso App to access your location all the time ", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.w400, + ), + ), + ), + Container( + padding: EdgeInsets.all( + SizeConfig.blockSizeHorizontal * 3, + ), + child: Text( + "Teso App collects location data to enable Proximity Coupons feature to get you available coupons in your area even when the app is closed or not in use.”", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.w400, + ), + ), + ), + Container( + width: SizeConfig.blockSizeHorizontal * 50, + height: SizeConfig.blockSizeHorizontal * 70, + child: Image( + image: AssetImage("assets/images/prominent-disclosure.jpg"), + ), + ), + ], + ), + ), + bottomNavigationBar: Padding( + padding: EdgeInsets.all( + SizeConfig.blockSizeHorizontal * 4, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: Text( + "No Thanks", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 4.2, + fontWeight: FontWeight.w800, + ), + ), + ), + TextButton( + onPressed: () => Navigator.pop(context, true), + child: Text( + "Turn on", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 4.2, + fontWeight: FontWeight.w800, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/AccountSettings/BlockedUser.dart b/lib/Pages/Sub_Pages/AccountSettings/BlockedUser.dart new file mode 100644 index 0000000..c2a31e3 --- /dev/null +++ b/lib/Pages/Sub_Pages/AccountSettings/BlockedUser.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Classes/customTesoButton.dart'; +import 'package:teso/Pages/PageWidgets/Friends/friendTile.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +class BlockAccounts extends StatefulWidget { + const BlockAccounts({Key key}) : super(key: key); + + @override + _BlockAccountsState createState() => _BlockAccountsState(); +} + +class _BlockAccountsState extends State { + RefreshController _refreshController = + RefreshController(initialRefresh: false); + void _onRefresh() async { + try { + await Provider.of(context, listen: false) + .checkBlockedUsers(); + } catch (e) { + print(e); + } + _refreshController.refreshCompleted(); + } + + @override + void dispose() { + _refreshController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).backgroundColor, + elevation: 5, + centerTitle: true, + title: Text( + "Blocked Users", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + color: Colors.red[800], + ), + ), + ), + body: Consumer( + builder: (context, value, child) { + if (value.blockUserList == null) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else { + return SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: ClassicHeader(), + controller: _refreshController, + onRefresh: _onRefresh, + child: ListView.builder( + itemCount: value.blockUserList.length, + itemBuilder: (context, int index) { + return InkWell( + onTap: () => reportDialog( + context, + value.blockUserList.elementAt(index), + ), + child: Column( + children: [ + buildFriend( + context, + value.blockUserList.elementAt(index), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 25), + child: Divider(), + ), + ], + ), + ); + }, + ), + ); + } + }, + ), + ); + } + + void reportDialog(BuildContext context, TesoUser user) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return Container( + height: MediaQuery.of(context).size.height * 0.3, + child: Column( + children: [ + Container( + margin: EdgeInsets.only( + top: 15, + ), + child: Center( + child: Text( + "Unblock ${user.username}?", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 4.5, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: 15, + vertical: 15, + ), + child: Center( + child: Text( + "${user.username} will now be able to request to follow and message you Instagram. " + "They won't be notified that you unblocked them.", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Divider(), + Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width - 40, + height: 40, + child: RaisedGradientButton( + child: Text( + "Unblock", + style: TextStyle(color: Colors.white, fontSize: 18), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + tesoBlue, + Colors.blueAccent, + ], + ), + onPressed: () { + Provider.of(context, listen: false) + .unblockUser(user); + Navigator.pop(context); + }, + width: MediaQuery.of(context).size.width - 40, + height: 40, + ), + ) + ], + ), + ); + }, + ); + } +} diff --git a/lib/Pages/Sub_Pages/AccountSettings/changePassword.dart b/lib/Pages/Sub_Pages/AccountSettings/changePassword.dart new file mode 100644 index 0000000..7d21c94 --- /dev/null +++ b/lib/Pages/Sub_Pages/AccountSettings/changePassword.dart @@ -0,0 +1,200 @@ +import 'package:flutter/material.dart'; + +import 'package:teso/Pages/PageWidgets/Login/passwordAlter.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/ResetPassword.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'newPassword.dart'; +import 'dart:convert'; + +class ChangePassword extends StatefulWidget { + @override + _ChangePasswordState createState() => _ChangePasswordState(); +} + +class _ChangePasswordState extends State { + TextEditingController oldPassword = new TextEditingController(); + bool verifyiing = false; + bool incorrentpassword = false; + + verify(context) async { + setState(() { + verifyiing = true; + incorrentpassword = false; + }); + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'users/verifypassword'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(oldPassword.text), headers: requestHeaders); + + if (client1.statusCode == 200) { + setState(() { + verifyiing = false; + }); + + switch (client1.body) { + case "matched": + Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: CreateNewPassword(), + ), + ); + break; + case "mismatched": + setState(() { + incorrentpassword = true; + }); + break; + default: + tesoDialog(context, client1.body); + break; + } + } else { + error(context); + } + } + + void error(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + actions: [ + TextButton( + child: Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Sorry an error occurred please try again!", + style: TextStyle(color: Colors.redAccent[100]), + ), + ); + }); + } + + void tesoDialog(context, value) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + actions: [ + TextButton( + child: Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Sorry you cannot change for password since you are logged in with your " + + value + + " account, please change your password on the " + + value + + " platform", + style: TextStyle(color: Colors.redAccent[100]), + ), + ); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Change Password"), + leading: IconButton( + icon: Icon(Icons.close), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + actions: [ + new Container( + margin: EdgeInsets.symmetric(vertical: 0.0, horizontal: 20), + child: new Center( + child: IconButton( + color: Theme.of(context).colorScheme.secondary, + icon: Icon(Icons.check), + onPressed: () => verify(context), + ), + ), + ) + ], + ), + body: Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Column( + children: [ + SizedBox( + height: 10.0, + ), + Center( + child: changePassword(context, "Current Password", oldPassword), + ), + Visibility( + visible: incorrentpassword, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20), + alignment: Alignment.centerLeft, + child: Text( + 'The password you entered does not match your current password, if you forgot your current password click on forgot password to continue!!', + style: TextStyle( + color: Colors.red, + // fontWeight: FontWeight.bold, + fontFamily: 'OpenSans', + fontSize: 14, + ), + ), + ), + ), + Container( + alignment: Alignment.centerLeft, + child: TextButton( + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) => ResetPassword(), + ), + //padding: EdgeInsets.only(left: 20.0), + child: Text( + 'Forgot Password?', + style: TextStyle( + color: Colors.black87, + // fontWeight: FontWeight.bold, + fontFamily: 'OpenSans', + fontSize: 14, + ), + ), + ), + ), + Visibility( + visible: verifyiing, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/AccountSettings/genderPicker.dart b/lib/Pages/Sub_Pages/AccountSettings/genderPicker.dart new file mode 100644 index 0000000..b643ac7 --- /dev/null +++ b/lib/Pages/Sub_Pages/AccountSettings/genderPicker.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:provider/provider.dart'; + +class Gender extends StatefulWidget { + final int value; + + const Gender({Key key, this.value}) : super(key: key); + @override + _GenderState createState() => _GenderState(value: this.value); +} + +class _GenderState extends State { + int value; + _GenderState({this.value}); + List genders = ["Male", "Female", "Other"]; + bool light = true; + + @override + void initState() { + SharedPreferences.getInstance().then((prefs) { + setState(() { + light = prefs.getString("theme") == "dark" ? false : true; + }); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Gender"), + leading: IconButton( + icon: Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + actions: [ + new Container( + margin: EdgeInsets.symmetric(vertical: 0.0, horizontal: 20), + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Theme.of(context).colorScheme.secondary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + ), + onPressed: () { + Provider.of(context, listen: false) + .currentUser + .gender = genders[value]; + Navigator.of(context).pop(value); + }, + child: Text("Done"), + ), + ), + ) + ], + ), + body: Container( + child: Column( + children: [ + for (int i = 0; i < genders.length; i++) + Column( + children: [ + ListTile( + title: Text( + genders[i], + style: Theme.of(context).textTheme.subtitle1.copyWith( + color: light + ? i == 3 + ? Colors.black38 + : Colors.black + : i == 3 + ? Colors.white10 + : Colors.white, + ), + ), + trailing: Radio( + value: i, + groupValue: value, + activeColor: accentMain, + onChanged: i == 3 + ? null + : (int _value) { + setState(() { + value = _value; + }); + }, + ), + ), + Divider(), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/AccountSettings/newPassword.dart b/lib/Pages/Sub_Pages/AccountSettings/newPassword.dart new file mode 100644 index 0000000..f814091 --- /dev/null +++ b/lib/Pages/Sub_Pages/AccountSettings/newPassword.dart @@ -0,0 +1,410 @@ +import 'package:http/http.dart' as http; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Login/passwordSignUP.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:math' as math; +import 'package:teso/Pages/PageWidgets/Login/validation.dart'; +import 'dart:convert'; +import 'dart:ui'; + +class CreateNewPassword extends StatefulWidget { + @override + _CreateNewPasswordState createState() => _CreateNewPasswordState(); +} + +class _CreateNewPasswordState extends State + with TickerProviderStateMixin { + TextEditingController password = new TextEditingController(); + AnimationController _controller; + Animation _fabScale; + bool eightChars = false; + bool specialChar = false; + bool upperCaseChar = false; + bool lowerCaseChar = false; + bool number = false; + bool changing = false; + + @override + void dispose() { + super.dispose(); + } + + changePassword(context) async { + setState(() { + changing = true; + }); + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'users/changepassword'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(password.text), headers: requestHeaders); + + if (client1.statusCode == 200) { + await tesoSuccessDialog(context); + setState(() { + changing = false; + }); + Future.delayed(const Duration(seconds: 5), () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + } else { + tesoErrorDialog(context); + } + } + + tesoSuccessDialog(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Success", + style: TextStyle(color: Colors.green[400]), + ), + actions: [ + TextButton( + child: Text( + 'OK', + style: TextStyle(color: Colors.green[400]), + ), + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Password changed successfully", + style: TextStyle(color: Colors.green[400]), + ), + ); + }); + } + + tesoErrorDialog(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Error Occurred", + style: TextStyle(color: Colors.red[400]), + ), + actions: [ + TextButton( + child: Text( + 'OK', + style: TextStyle(color: Colors.red[400]), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "An error occurred while changing passwor, please try again!", + style: TextStyle(color: Colors.red[400]), + ), + ); + }); + } + + bool _allValid() { + return eightChars && + number && + specialChar && + upperCaseChar && + lowerCaseChar; + } + + @override + void initState() { + super.initState(); + password.addListener(() { + setState(() { + eightChars = password.text.length >= 8; + number = password.text.contains(RegExp(r'\d'), 0); + upperCaseChar = password.text.contains(new RegExp(r'[A-Z]'), 0); + lowerCaseChar = password.text.contains(new RegExp(r'[a-z]'), 0); + specialChar = password.text.isNotEmpty && + !password.text.contains(RegExp(r'^[\w&.-]+$'), 0); + }); + + if (_allValid()) { + _controller.forward(); + } else { + _controller.reverse(); + } + }); + _controller = AnimationController( + vsync: this, duration: const Duration(milliseconds: 500)); + + _fabScale = Tween(begin: 0, end: 1) + .animate(CurvedAnimation(parent: _controller, curve: Curves.bounceOut)); + + _fabScale.addListener(() { + setState(() {}); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text("Create new a password"), + ), + body: Container( + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + children: [ + Container( + padding: EdgeInsets.all(10), + width: double.infinity, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.02, + top: MediaQuery.of(context).size.height * 0.02, + ), + child: Center( + child: Text( + "Enter a password with a minimum of 8 characters and the password must have at least an uppercase letter, a lowercase letter, " + + "a digit and a non-alphanumeric character", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width) * 0.01), + child: _validationStack()), + SizedBox( + height: 50, + ), + createPassword(context, password), + SizedBox(height: MediaQuery.of(context).size.height * 0.002), + Visibility( + visible: !changing, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.6, + height: 40.0, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: () async { + if (_allValid()) { + await changePassword(context); + } + }, + child: Center( + child: Text( + "Change Password", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + )), + ), + ), + ), + Visibility( + visible: changing, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _separator() { + return Container( + height: 1, + decoration: BoxDecoration(color: Colors.blue.withAlpha(100)), + ); + } + + Stack _validationStack() { + return Stack( + alignment: Alignment.bottomLeft, + children: [ + Card( + shape: CircleBorder(), + color: Colors.black12, + child: Container( + height: 150, + width: 150, + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 32.0, left: 10), + child: Transform.rotate( + angle: -math.pi / 20, + child: Icon( + Icons.lock, + color: Colors.pink, + size: 60, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 50.0, right: 60), + child: Transform.rotate( + angle: -math.pi / -60, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + elevation: 4, + color: Colors.yellow.shade800, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(8, 8, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 8), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 74), + child: Transform.rotate( + angle: math.pi / -45, + child: Card( + elevation: 6, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + child: Stack( + alignment: Alignment.bottomRight, + children: [ + IntrinsicWidth( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ValidationItem("8 or more Characters", eightChars), + _separator(), + ValidationItem("1 Special character", specialChar), + _separator(), + ValidationItem("1 Upper case", upperCaseChar), + _separator(), + ValidationItem("1 Lower case", lowerCaseChar), + _separator(), + ValidationItem("1 Number", number) + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Transform.scale( + scale: _fabScale.value, + child: Card( + shape: CircleBorder(), + color: Colors.green, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.check, + color: Colors.white, + ), + ), + ), + ), + ) + ], + ), + ), + ), + ) + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/Business/BusinessProducts.dart b/lib/Pages/Sub_Pages/Business/BusinessProducts.dart new file mode 100644 index 0000000..f6a496b --- /dev/null +++ b/lib/Pages/Sub_Pages/Business/BusinessProducts.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:teso/Pages/PageWidgets/BusinessProfile/BusinessItems.dart'; + +class BusinessProducts extends StatefulWidget { + final String shopName; + final List product; + const BusinessProducts({Key key, this.shopName, this.product}) + : super(key: key); + + @override + _BusinessProductsState createState() => + _BusinessProductsState(shopName: this.shopName, product: this.product); +} + +class _BusinessProductsState extends State { + final String shopName; + ScrollController _controller; + SharedPreferences prefs; + final List product; + List show = []; + _BusinessProductsState({this.shopName, this.product}); + int count; + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + fetchImages(); + } + } + + Future fetchImages() async { + try { + count = show.length; + for (int i = 1; i <= 5; i++) { + setState(() { + show.add(product.elementAt(count)); + count++; + imageCache.clear(); + }); + } + } catch (e) { + print(e); + } + } + + @override + void initState() { + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + fetchImages(); + + super.initState(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + child: StaggeredGridView.count( + controller: _controller, + crossAxisCount: 2, + children: List.generate(show.length, (int index) { + show.elementAt(index).businessID = widget.shopName; + return index % 2 == 0 + ? buildItems(context, show.elementAt(index), 0.5) + : buildItems(context, show.elementAt(index), 0.35); + }), + staggeredTiles: List.generate( + show.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Business/BusinessProfile.dart b/lib/Pages/Sub_Pages/Business/BusinessProfile.dart new file mode 100644 index 0000000..68dd4e9 --- /dev/null +++ b/lib/Pages/Sub_Pages/Business/BusinessProfile.dart @@ -0,0 +1,260 @@ +import 'package:teso/Classes/API%20Clasess/BusinessProfile.dart'; +import 'package:teso/Classes/TesoShop.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/BusinessProfile/BusinessHead.dart'; +import 'package:teso/Pages/Sub_Pages/Business/BusinessProducts.dart'; +import 'package:flutter/material.dart'; + +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'dart:convert'; +import 'package:flutter/cupertino.dart'; + +class BusinessProfile extends StatefulWidget { + final TesoShop shop; + const BusinessProfile({Key key, this.shop}) : super(key: key); + @override + _BusinessProfileState createState() => new _BusinessProfileState(shop: shop); +} + +enum AppBarBehavior { normal, pinned, floating, snapping } + +class _BusinessProfileState extends State + with TickerProviderStateMixin { + AnimationController _containerController; + Animation width; + Animation heigth; + final TesoShop shop; + _BusinessProfileState({this.shop}); + bool subscribed = false; + SharedPreferences prefs; + var stats; + int productTotal = 0; + int adsTotal = 0; + int couponsTotal = 0; + List products; + List coupons; + BusinessProfileClass profile; + List subscribers; + + List _randomChildren; + + Future subscribe() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + var register = serverLocation + 'relationships/business-subscription'; + var client = await http.post(Uri.parse(register), + headers: requestHeaders, body: json.encode(shop.shopID)); + if (client.statusCode == 200) { + setState(() { + subscribed = true; + }); + } else { + return null; + } + } + + Future unsubscribe() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + var register = serverLocation + 'relationships/business-unsubscribe'; + var client = await http.post(Uri.parse(register), + headers: requestHeaders, body: json.encode(shop.shopID)); + if (client.statusCode == 200) { + setState(() { + subscribed = false; + }); + } else { + return null; + } + } + + fetchProfile() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + var register = serverLocation + 'tesobusiness/profile'; + var client = await http.post(Uri.parse(register), + headers: requestHeaders, body: json.encode(shop.shopID)); + if (client.statusCode == 200) { + var details = jsonDecode(client.body); + setState(() { + profile = BusinessProfileClass.fromJSON(details); + products = profile.products; + coupons = profile.coupons; + couponsTotal = coupons.length; + subscribed = profile.subscribed; + subscribers = profile.subscribers; + productTotal = products.length; + }); + } else { + return null; + } + } + + Future fetched() async { + try { + await fetchProfile(); + return true; + } catch (e) { + return false; + } + } + + @override + void initState() { + stats = fetched(); + _containerController = new AnimationController( + duration: new Duration(milliseconds: 2000), vsync: this); + super.initState(); + width = new Tween( + begin: 200.0, + end: 220.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth = new Tween( + begin: 400.0, + end: 400.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth.addListener(() { + setState(() { + if (heigth.isCompleted) {} + }); + }); + _containerController.forward(); + } + + @override + void dispose() { + _containerController.dispose(); + super.dispose(); + } + + List _randomHeightWidgets(BuildContext context) { + _randomChildren ??= List.generate(1, (index) { + return buildHead( + context: context, + shopSelected: shop, + subscribers: subscribers.length, + coupons: couponsTotal, + products: productTotal); + }); + return _randomChildren; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + toolbarHeight: 50, + automaticallyImplyLeading: false, + title: Text(shop.shopName.toUpperCase()), + leadingWidth: 40, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios), + onPressed: () => Navigator.pop(context), + ), + actions: [ + FutureBuilder( + future: stats, + builder: (context, snapshot) { + if (snapshot.data == null && + snapshot.connectionState == ConnectionState.waiting) { + return Container(); + } else if (snapshot.data == null && + snapshot.connectionState == ConnectionState.done) { + return IconButton( + icon: Icon( + Icons.notifications_off, + ), + onPressed: null, + ); + } else { + return IconButton( + icon: Icon( + subscribed + ? Icons.notifications_active + : Icons.notifications_none, + ), + onPressed: () async { + if (subscribed) { + await unsubscribe(); + } else { + await subscribe(); + } + }, + ); + } + }, + ), + ], + ), + body: FutureBuilder( + future: stats, + builder: (context, snapshot) { + if (snapshot.data == null) { + return Container( + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } else { + return DefaultTabController( + length: 1, + child: NestedScrollView( + headerSliverBuilder: (context, _) { + return [ + SliverList( + delegate: SliverChildListDelegate( + _randomHeightWidgets(context), + ), + ), + ]; + }, + body: TabBarView( + children: [ + BusinessProducts( + shopName: shop.shopName, + product: products, + ), + // BusinessPosts(), + ], + ), + ), + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Business/Posts.dart b/lib/Pages/Sub_Pages/Business/Posts.dart new file mode 100644 index 0000000..fa3b5d7 --- /dev/null +++ b/lib/Pages/Sub_Pages/Business/Posts.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class BusinessPosts extends StatefulWidget { + @override + _BusinessPostsState createState() => _BusinessPostsState(); +} + +class _BusinessPostsState extends State { + @override + Widget build(BuildContext context) { + return Container( + child: Center( + child: Text("fgfgfghjfhgfjhgfjjjjjjgfgh"), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/BusinessDetails.dart b/lib/Pages/Sub_Pages/BusinessDetails.dart new file mode 100644 index 0000000..2f0abd4 --- /dev/null +++ b/lib/Pages/Sub_Pages/BusinessDetails.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/TesoShop.dart'; + +import 'package:url_launcher/url_launcher.dart'; +import 'package:page_transition/page_transition.dart'; +import 'Business/BusinessProfile.dart'; + +buildShopDetails(BuildContext context, TesoShop shop, Function navigate) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(20), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + children: [ + new Container( + width: double.infinity, + margin: EdgeInsets.symmetric(vertical: 2), + child: Text( + shop.shopName, + style: TextStyle( + fontSize: 25.0, + fontWeight: FontWeight.w900, + ), + )), + Container( + width: double.infinity, + child: new Text( + shop.categoryShop, + ), + ), + Container( + width: double.infinity, + child: new Text( + shop.shopAddress, + ), + ), + new InkWell( + onTap: () async { + if (await canLaunch("tel:" + shop.shopPhone)) { + await launch("tel:" + shop.shopPhone); + } else { + throw 'call not possible'; + } + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.all(5), + child: Center( + child: new Wrap( + children: [ + Container( + child: Icon( + Icons.call, + color: Colors.blue, + ), + ), + Container( + child: Text( + shop.shopPhone, + style: TextStyle( + color: Colors.blue, + ), + ), + ), + ], + ), + ), + ), + ), + Container( + height: 50, + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: Theme.of(context).backgroundColor, + borderRadius: BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: Theme.of(context).colorScheme.secondary, + )), + child: InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: BusinessProfile( + shop: shop, + ), + type: PageTransitionType.bottomToTop), + ), + child: Container( + child: ListTile( + leading: Icon( + Icons.shop, + size: 19, + ), + title: Text( + "View Business Profile", + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontSize: 15), + ), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 05.0), + child: Divider(), + ), + Container( + width: double.infinity, + child: new Text( + shop.shopDescription != null + ? shop.shopDescription.toLowerCase() != "null" + ? shop.shopDescription + : "Registered Teso Business" + : "Registered Teso Business", + ), + ), + SizedBox( + height: 15, + ), + new Container( + width: MediaQuery.of(context).size.width, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Theme.of(context).colorScheme.secondary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + ), + onPressed: () async { + Navigator.pop(context); + await navigate(shop); + }, + child: Text( + "Navigate to " + shop.shopName, + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/Pages/Sub_Pages/Campaign/AuditionPage.dart b/lib/Pages/Sub_Pages/Campaign/AuditionPage.dart new file mode 100644 index 0000000..7e4ce07 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/AuditionPage.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API%20Clasess/Campaign.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/Video/RecordVideo.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/consts.dart'; + +class Audition extends StatefulWidget { + final Campaign campaign; + + const Audition({Key key, this.campaign}) : super(key: key); + @override + _AuditionState createState() => _AuditionState(); +} + +class _AuditionState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + widget.campaign.title, + ), + centerTitle: true, + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: ListView( + scrollDirection: Axis.vertical, + children: [ + Container( + width: MediaQuery.of(context).size.width, + height: 150, + child: Center( + child: Container( + width: 150, + height: 150, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Image( + width: MediaQuery.of(context).size.width * 0.28, + height: 110, + fit: BoxFit.fill, + image: NetworkImage(widget.campaign.targetProduct != null + ? productURL + widget.campaign.targetProduct + : ""), + ), + ), + ), + ), + ), + SizedBox( + height: 20, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Publisher : ", + style: new TextStyle( + fontWeight: FontWeight.bold, + ), + ), + new TextSpan( + text: widget.campaign.businessID, + ), + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "Description", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.5, + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Text(widget.campaign.description), + ), + ), + SizedBox( + height: 30, + ), + Container( + width: double.infinity, + child: Align( + alignment: Alignment.center, + child: Container( + width: 100, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + primary: accentMain, + ), + onPressed: () => Navigator.pushReplacement( + context, + PageTransition( + child: RecordVideo( + campaignID: widget.campaign.campaignID, + ), + type: PageTransitionType.fade, + ), + ), + child: Text("Submit Ad"), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/CreatePost.dart b/lib/Pages/Sub_Pages/Campaign/CreatePost.dart new file mode 100644 index 0000000..dc2754c --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/CreatePost.dart @@ -0,0 +1,200 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/Uploading.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; + +class CreateCampaignPost extends StatefulWidget { + final String video; + final Uint8List thumbnail; + final String campaignID; + final String aspectRatio; + + const CreateCampaignPost({ + Key key, + this.video, + this.thumbnail, + this.campaignID, + this.aspectRatio, + }) : super(key: key); + @override + _CreateCampaignPostState createState() => _CreateCampaignPostState(); +} + +class _CreateCampaignPostState extends State { + String aspectRatio; + TextEditingController controller; + SharedPreferences prefs; + bool sending = false; + + void postVideo(context) async { + setState(() { + sending = true; + }); + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = {'Authorization': token}; + String urlLocation = tesoStreaming + "api/mobile/upload/authurl"; + var client = + await http.get(Uri.parse(urlLocation), headers: requestHeaders); + if (client.statusCode == 200) { + var details = jsonDecode(client.body); + String muxuploadsID = details["data"]["id"]; + String muxuploadsURL = details["data"]["url"]; + + Provider.of(context, listen: false).uploadPost(Uploading( + id: DateTime.now().toString() + + widget.video.replaceAll("file://", ""), + aspect: widget.aspectRatio, + path: widget.video.replaceAll("file://", ""), + thumbnail: + widget.thumbnail != null ? base64Encode(widget.thumbnail) : null, + title: controller.text, + pending: 0, + campaignID: widget.campaignID, + muxuploadID: muxuploadsID, + muxuploadURL: muxuploadsURL, + )); + Navigator.pop(context); + } + } catch (e) { + print(e); + } + setState(() { + sending = false; + }); + } + + @override + void initState() { + super.initState(); + controller = new TextEditingController(); + } + + @override + void dispose() { + super.dispose(); + controller.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: AppBar( + automaticallyImplyLeading: true, + title: Text("Post"), + centerTitle: true, + ), + preferredSize: Size.fromHeight(70.0), + ), + body: SingleChildScrollView( + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + padding: EdgeInsets.all(MediaQuery.of(context).size.width * 0.025), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.25, + height: MediaQuery.of(context).size.width * 0.35, + color: Colors.black, + child: widget.thumbnail != null + ? Image.memory(widget.thumbnail) + : CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + Container( + width: (MediaQuery.of(context).size.width) - + (MediaQuery.of(context).size.width * 0.35), + height: MediaQuery.of(context).size.width * 0.35, + child: TextField( + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide.none, + ), + filled: true, + isDense: true, + labelText: "Say Something..", + labelStyle: TextStyle( + color: Colors.black54, + ), + fillColor: Colors.white70, + ), + controller: controller, + maxLines: null, + keyboardType: TextInputType.text, + textInputAction: TextInputAction.done, + ), + ), + ], + ), + ), + SizedBox( + height: 20, + ), + Divider(), + Container( + margin: EdgeInsets.only( + top: 10, + ), + child: Text( + "Teso businesses and other Teso users can see your post in their feeds and on your profile.", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ], + ), + ), + ), + floatingActionButton: !sending + ? Container( + margin: EdgeInsets.all(20), + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + color: tesoBlue, + ), + child: InkWell( + onTap: () => postVideo(context), + child: Center( + child: Text( + "NEXT", + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + height: 50, + ) + : Container( + width: MediaQuery.of(context).size.width, + height: 50, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/SubmitAdvert.dart b/lib/Pages/Sub_Pages/Campaign/SubmitAdvert.dart new file mode 100644 index 0000000..1ef9780 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/SubmitAdvert.dart @@ -0,0 +1,195 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/Uploading.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; + +class SubmitAdvert extends StatefulWidget { + final String video; + final String aspectRatio; + final Uint8List thumbnail; + final String campaignID; + + const SubmitAdvert( + {Key key, this.video, this.aspectRatio, this.thumbnail, this.campaignID}) + : super(key: key); + @override + _SubmitAdvertState createState() => _SubmitAdvertState(); +} + +class _SubmitAdvertState extends State { + TextEditingController controller; + SharedPreferences prefs; + bool sending = false; + + void postVideo(context) async { + setState(() { + sending = true; + }); + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = {'Authorization': token}; + String urlLocation = tesoStreaming + "api/mobile/upload/authurl"; + var client = + await http.get(Uri.parse(urlLocation), headers: requestHeaders); + if (client.statusCode == 200) { + var details = jsonDecode(client.body); + String muxuploadsID = details["data"]["id"]; + String muxuploadsURL = details["data"]["url"]; + + Provider.of(context, listen: false).uploadPost(Uploading( + id: DateTime.now().toString() + + widget.video.replaceAll("file://", ""), + aspect: widget.aspectRatio, + path: widget.video.replaceAll("file://", ""), + thumbnail: + widget.thumbnail != null ? base64Encode(widget.thumbnail) : null, + title: controller.text, + pending: 0, + campaignID: widget.campaignID, + muxuploadID: muxuploadsID, + muxuploadURL: muxuploadsURL, + )); + Navigator.pop(context); + } + } catch (e) { + print("Something is " + e.toString()); + } + setState(() { + sending = false; + }); + } + + @override + void initState() { + super.initState(); + controller = new TextEditingController(); + } + + @override + void dispose() { + super.dispose(); + controller.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: AppBar( + automaticallyImplyLeading: true, + title: Text("Post"), + centerTitle: true, + ), + preferredSize: Size.fromHeight(70.0), + ), + body: SingleChildScrollView( + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + padding: EdgeInsets.all(MediaQuery.of(context).size.width * 0.025), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.25, + height: MediaQuery.of(context).size.width * 0.35, + color: Colors.black, + child: widget.thumbnail != null + ? Image.memory(widget.thumbnail) + : CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + Container( + width: (MediaQuery.of(context).size.width) - + (MediaQuery.of(context).size.width * 0.35), + height: MediaQuery.of(context).size.width * 0.35, + child: TextField( + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide.none, + ), + filled: true, + isDense: true, + labelText: "Say Something..", + labelStyle: TextStyle( + color: Colors.black54, + ), + fillColor: Colors.white70, + ), + controller: controller, + maxLines: null, + keyboardType: TextInputType.url, + textInputAction: TextInputAction.done, + ), + ), + ], + ), + ), + SizedBox( + height: 20, + ), + Divider(), + Container( + margin: EdgeInsets.only( + top: 10, + ), + child: Text( + "Teso businesses and other Teso users can only see campaign post on your profile after they have been approved by the campaign owners.", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ], + ), + ), + ), + floatingActionButton: !sending + ? Container( + margin: EdgeInsets.all(20), + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + color: tesoBlue, + ), + child: InkWell( + onTap: () => postVideo(context), + child: Center( + child: Text( + "NEXT", + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + height: 50, + ) + : Container( + width: MediaQuery.of(context).size.width, + height: 50, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/TextEditor.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/TextEditor.dart new file mode 100644 index 0000000..5c3c0eb --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/TextEditor.dart @@ -0,0 +1,212 @@ +// import 'package:flutter/material.dart'; + +// import 'package:teso/Classes/TextE.dart'; +// import 'package:teso/util/SizeConfig.dart'; +// import 'textstyler/src/toolbar_action.dart'; +// import 'textstyler/text_style_editor.dart'; + +// // ignore: must_be_immutable +// class TextEdit extends StatefulWidget { +// Textted content; +// TextEdit({Key key, this.content}) : super(key: key); + +// @override +// _TextEditState createState() => _TextEditState(); +// } + +// class _TextEditState extends State { +// TextStyle textStyle; +// TextAlign textAlign; +// List fonts = [ +// 'Billabong', +// 'AlexBrush', +// 'Allura', +// 'Arizonia', +// 'ChunkFive', +// 'GrandHotel', +// 'GreatVibes', +// 'Lobster', +// 'OpenSans', +// 'OstrichSans', +// 'Oswald', +// 'Pacifico', +// 'Quicksand', +// 'Roboto', +// 'SEASRN', +// 'Windsong', +// ]; +// List paletteColors = [ +// Colors.black, +// Colors.white, +// Color(int.parse('0xffEA2027')), +// Color(int.parse('0xff006266')), +// Color(int.parse('0xff1B1464')), +// Color(int.parse('0xff5758BB')), +// Color(int.parse('0xff6F1E51')), +// Color(int.parse('0xffB53471')), +// Color(int.parse('0xffEE5A24')), +// Color(int.parse('0xff009432')), +// Color(int.parse('0xff0652DD')), +// Color(int.parse('0xff9980FA')), +// Color(int.parse('0xff833471')), +// Color(int.parse('0xff112CBC4')), +// Color(int.parse('0xffFDA7DF')), +// Color(int.parse('0xffED4C67')), +// Color(int.parse('0xffF79F1F')), +// Color(int.parse('0xffA3CB38')), +// Color(int.parse('0xff1289A7')), +// Color(int.parse('0xffD980FA')) +// ]; +// FocusNode _focus = new FocusNode(); +// TextEditingController controller; +// @override +// void initState() { +// controller = new TextEditingController(); +// textStyle = TextStyle( +// fontSize: 15, +// color: Colors.white, +// fontFamily: 'OpenSans', +// ); +// textAlign = TextAlign.left; +// _focus.addListener(_onFocusChange); + +// controller.text = widget.content.text != null ? widget.content.text : ""; +// textStyle = +// widget.content.textStyle != null ? widget.content.textStyle : null; +// textAlign = widget.content.textAlign != null +// ? widget.content.textAlign +// : TextAlign.center; +// super.initState(); +// } + +// @override +// void dispose() { +// _focus.removeListener(_onFocusChange); +// _focus.dispose(); +// super.dispose(); +// } + +// void _onFocusChange() { +// debugPrint("Focus: " + _focus.hasFocus.toString()); +// } + +// void verify() { +// if (_focus.hasFocus) { +// _focus.unfocus(); +// } else { +// Navigator.pop( +// context, +// new Textted( +// text: controller.text, +// textAlign: textAlign, +// textStyle: textStyle, +// )); +// } +// } + +// @override +// Widget build(BuildContext context) { +// SizeConfig().init(context); +// return Scaffold( +// resizeToAvoidBottomInset: false, +// backgroundColor: Color.fromRGBO(0, 0, 0, 0.8), +// appBar: AppBar( +// backgroundColor: Colors.transparent, +// leading: IconButton( +// onPressed: () => Navigator.pop(context, widget.content), +// icon: Icon( +// Icons.x, +// color: Colors.white, +// ), +// ), +// actions: [ +// IconButton( +// onPressed: verify, +// icon: Icon( +// AntDesign.check, +// color: Colors.white, +// ), +// ), +// ], +// ), +// body: Container( +// height: SizeConfig.safeBlockVertical * 40, +// child: Center( +// child: TextField( +// controller: controller, +// // enabled: false, +// focusNode: _focus, +// style: textStyle, +// textAlign: textAlign, +// // maxLines: 4, +// decoration: new InputDecoration( +// filled: true, +// enabledBorder: OutlineInputBorder( +// borderRadius: BorderRadius.all( +// Radius.circular(10.0), +// ), +// borderSide: BorderSide( +// color: Colors.grey.shade400, +// width: 2, +// ), +// ), +// focusedBorder: OutlineInputBorder( +// borderRadius: BorderRadius.all( +// Radius.circular(10.0), +// ), +// borderSide: BorderSide( +// color: Colors.blue.shade300, +// width: 0, +// ), +// ), +// contentPadding: EdgeInsets.all(15), +// ), +// ), +// ), +// ), +// extendBody: false, +// extendBodyBehindAppBar: false, +// bottomSheet: Container( +// height: SizeConfig.safeBlockVertical * 60, +// child: Container( +// padding: EdgeInsets.all(10), +// decoration: BoxDecoration( +// color: Theme.of(context).backgroundColor, +// border: Border.symmetric( +// horizontal: BorderSide( +// color: Theme.of(context).backgroundColor, +// ), +// ), +// ), +// child: Align( +// alignment: Alignment.topCenter, +// child: SingleChildScrollView( +// scrollDirection: Axis.vertical, +// child: TextStyleEditor( +// fonts: fonts, +// paletteColors: paletteColors, +// textStyle: textStyle, +// textAlign: textAlign, +// initialTool: EditorToolbarAction.fontFamilyTool, +// onTextAlignEdited: (align) { +// setState(() { +// textAlign = align; +// }); +// }, +// onTextStyleEdited: (style) { +// setState(() { +// textStyle = textStyle.merge(style); +// }); +// }, +// onCpasLockTaggle: (caps) { +// print(caps); +// }, +// //onToolbarActionChanged: (fu) => , +// ), +// ), +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/VideoReview.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/VideoReview.dart new file mode 100644 index 0000000..867f2aa --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/VideoReview.dart @@ -0,0 +1,560 @@ +import 'dart:typed_data'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'dart:io'; + +import 'package:image_gallery_saver/image_gallery_saver.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/SubmitAdvert.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:video_player/video_player.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:async'; +import 'package:path_provider/path_provider.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:image/image.dart' as IMG; +import 'package:video_thumbnail/video_thumbnail.dart'; + +class VideoReview extends StatefulWidget { + final video; + final bool recorded; + final String campaignID; + final double aspect; + + @override + const VideoReview( + {Key key, + this.video, + @required this.campaignID, + @required this.recorded, + this.aspect}) + : super(key: key); + @override + _VideoReviewState createState() => _VideoReviewState(); +} + +class _VideoReviewState extends State + with TickerProviderStateMixin { + Trimmer _trimmer = new Trimmer(); + VideoPlayerController videoController; + VoidCallback videoPlayerListener; + bool muted = false; + String readyVideo; + Color textColor = Colors.white; + double _startValue = 0.15; + double _endValue = 60000.0; + var _future; + bool _isPlaying = false; + Duration _duration; + Duration _position; + ByteData bytes; + Uint8List imageBitmap; + Uint8List thumbnail; + Directory tempDirectory; + TesoUser user; + bool processing = false; + bool downloaded = false; + bool processed = false; + final key = new GlobalKey(); + double currentOffset = 0; + + Future _startVideoPlayer() async { + await videoController.play(); + } + + Future initializeController(String fileLocation) async { + videoController = VideoPlayerController.file(File(fileLocation)); + + videoPlayerListener = () async { + Timer.run(() { + this.setState(() { + _position = videoController.value.position; + }); + setState(() { + _duration = Duration(milliseconds: _endValue.round()); + }); + if (_duration?.compareTo(_position) == 0 || + _duration?.compareTo(_position) == -1) { + this.setState(() { + _isPlaying = false; + }); + videoController.pause(); + videoController.seekTo(Duration(milliseconds: _startValue.round())); + } else {} + }); + }; + videoController.addListener(videoPlayerListener); + await videoController.setLooping(true); + await videoController.initialize(); + await _trimmer.loadVideo(videoFile: File(fileLocation)); + } + + @override + void initState() { + readyVideo = widget.video; + if (readyVideo != null) _future = initializeController(readyVideo); + rootBundle.load("assets/images/rawLogo.png").then((value) => setState(() { + imageBitmap = value.buffer.asUint8List(); + IMG.Image img = IMG.decodeImage(imageBitmap); + IMG.Image resized = IMG.copyResize(img, width: 50, height: 60); + imageBitmap = IMG.encodePng(resized); + })); + super.initState(); + } + + @override + void dispose() { + videoController?.dispose(); + super.dispose(); + } + + void postVideo(context) async { + setState(() { + processing = true; + }); + if (processed) { + await Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: SubmitAdvert( + video: readyVideo, + aspectRatio: widget.recorded + ? "0.5625" + : videoController.value.aspectRatio.toString(), + thumbnail: this.thumbnail, + campaignID: widget.campaignID, + ), + )); + } else { + readyVideo = await processVideo(context, false); + await Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: SubmitAdvert( + video: readyVideo, + aspectRatio: widget.recorded + ? "0.5625" + : videoController.value.aspectRatio.toString(), + thumbnail: this.thumbnail, + campaignID: widget.campaignID, + ), + )); + } + } + + Future downloadVideo(context) async { + try { + setState(() { + processing = true; + }); + String output = await processVideo(context, true); + await ImageGallerySaver.saveFile(output).catchError((error, stackTrace) { + setState(() { + processing = false; + downloaded = false; + }); + }).then((value) { + setState(() { + processing = false; + downloaded = true; + }); + }); + } catch (e) { + print(e); + } + } + + Future processVideo(context, bool watermark) async { + user = Provider.of(context, listen: false).currentUser; + String location = await getTemporaryDirectory().then((value) => + value.path + + "/" + + DateTime.now().millisecondsSinceEpoch.toString() + + ".mp4"); + if (widget.recorded) { + String initial = await _trimmer.saveTrimmedVideo( + applyVideoEncoding: false, + ffmpegCommand: "-vf setsar=1:1 -aspect 9:16", + customVideoFormat: ".mp4", + startValue: _startValue, + endValue: videoController.value.duration.inMilliseconds > 5900 && + videoController.value.duration.inMilliseconds >= _endValue + ? _endValue + : double.parse( + videoController.value.duration.inMilliseconds.toString()), + ); + this.thumbnail = await generateThumbnail(); + + location = initial; + } else { + String initial = await _trimmer.saveTrimmedVideo( + startValue: _startValue, + endValue: videoController.value.duration.inMilliseconds > 5900 && + videoController.value.duration.inMilliseconds >= _endValue + ? _endValue + : double.parse( + videoController.value.duration.inMilliseconds.toString()), + outputFormat: FileFormat.mp4, + ); + this.thumbnail = await generateThumbnail(); + + location = initial; + } + return location; + } + + Future generateThumbnail() async { + try { + Uint8List thumbnail; + + thumbnail = await VideoThumbnail.thumbnailData( + video: widget.video, + imageFormat: ImageFormat.JPEG, + maxWidth: 0, + maxHeight: 0, + timeMs: 100, + quality: 100, + ); + return thumbnail; + } catch (e) { + print("Error :::: " + e); + return null; + } + } + + Future shareVideo(context) async { + setState(() { + processing = true; + }); + if (readyVideo == widget.video) { + readyVideo = await processVideo(context, true); + Share.shareFiles([readyVideo]); + } else { + Share.shareFiles([readyVideo]); + } + setState(() { + processing = false; + }); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + body: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Container( + color: Colors.black, + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: CircularProgressIndicator( + backgroundColor: Colors.red, + ), + ), + ); + } else { + return Stack( + children: [ + videoContent(context), + // Video trimmer + trimmerWidget(context), + // Pop button + Align( + alignment: Alignment.topLeft, + child: InkWell( + onTap: () => Navigator.pop(context), + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.07, + vertical: MediaQuery.of(context).size.width * 0.1, + ), + height: 35, + width: 35, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + shape: BoxShape.circle), + child: Icon( + Icons.close, + color: Colors.white, + size: 20, + ), + ), + ), + ), + // Bottom buttons + bottomButtons(context), + Visibility( + visible: processing, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Color.fromRGBO(0, 0, 0, 0.6), + padding: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.7), + child: Center( + child: Column( + children: [ + CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + Text( + "Processing.....", + style: TextStyle( + color: Colors.white, + ), + ), + ], + ), + ), + ), + ), + ], + ); + } + }), + ); + } + + Widget trimmerWidget(context) { + return Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.01, + vertical: MediaQuery.of(context).size.width * 0.20, + ), + width: MediaQuery.of(context).size.width, + child: TrimEditor( + borderPaintColor: tesoGold, + circlePaintColor: tesoBlue, + thumbnailQuality: 100, + showDuration: true, + viewerHeight: 50.0, + maxVideoLength: Duration(seconds: 60), + viewerWidth: MediaQuery.of(context).size.width, + onChangeStart: (value) { + if (!mounted) { + setState(() { + _startValue = value; + }); + } else { + _startValue = value; + } + videoController.seekTo(Duration(milliseconds: value.round())); + }, + onChangeEnd: (value) { + if (!mounted) { + setState(() { + _endValue = value; + }); + } else { + _endValue = value; + } + }, + onChangePlaybackState: (isPlaying) { + if (mounted) + setState(() { + _isPlaying = isPlaying; + }); + }, + )); + } + + Widget videoContent(context) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Colors.black, + child: Center( + child: AspectRatio( + aspectRatio: videoController.value.size != null + ? videoController.value.aspectRatio + : 1.0, + child: Stack( + children: [ + InkWell( + onTap: () { + !_isPlaying ? _startVideoPlayer() : videoController.pause(); + setState(() { + _isPlaying = !_isPlaying; + }); + }, + child: VideoPlayer( + videoController, + ), + ), + Container( + width: double.infinity, + height: double.infinity, + child: GestureDetector( + child: !_isPlaying + ? Icon( + Icons.play_circle, + size: 60, + color: Colors.white, + ) + : Container(), + onTap: () { + !_isPlaying + ? _startVideoPlayer() + : videoController.pause(); + setState(() { + _isPlaying = !_isPlaying; + }); + }, + ), + ), + ], + )), + ), + ); + } + + Widget bottomButtons(context) { + if (widget.recorded) { + return Align( + alignment: Alignment.bottomLeft, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.05, + vertical: SizeConfig.safeBlockVertical * 2.5, + ), + width: MediaQuery.of(context).size.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 55, + height: 40, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.6), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + ), + border: Border.all(color: Colors.white, width: 0.5)), + child: InkWell( + onTap: () async => + !downloaded ? await downloadVideo(context) : null, + child: Icon( + !downloaded ? Icons.download : Icons.check, + color: !downloaded ? Colors.white : Colors.green, + ), + ), + ), + Container( + width: 55, + height: 40, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.6), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + ), + border: Border.all(color: Colors.white, width: 0.5)), + child: InkWell( + onTap: () async => await shareVideo(context), + child: Icon( + Icons.share, + color: Colors.white, + ), + ), + ), + Container( + padding: EdgeInsets.all(5), + width: 100, + height: 40, + decoration: BoxDecoration( + color: tesoGold, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + ), + ), + child: InkWell( + onTap: () => postVideo(context), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + "Post", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + Icon( + Icons.send, + color: tesoBlue, + ), + ], + ), + ), + ), + ], + ), + ), + ); + } else { + return Align( + alignment: Alignment.bottomRight, + child: Container( + padding: EdgeInsets.all(5), + width: 100, + height: 40, + margin: EdgeInsets.symmetric( + vertical: 10, + horizontal: 20, + ), + decoration: BoxDecoration( + color: tesoGold, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + ), + ), + child: InkWell( + onTap: () => postVideo(context), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + "Post", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + Icon( + Icons.send, + color: tesoBlue, + ), + ], + ), + ), + ), + ); + } + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/color_palette.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/color_palette.dart new file mode 100644 index 0000000..130d0f2 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/color_palette.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; + +class ColorPalette extends StatefulWidget { + final Color activeColor; + final List colors; + final Function(Color) onColorPicked; + + ColorPalette({ + this.activeColor, + this.onColorPicked, + this.colors, + }); + + @override + _ColorPaletteState createState() => _ColorPaletteState(); +} + +class _ColorPaletteState extends State { + Color _activeColor; + + @override + void initState() { + _activeColor = widget.activeColor ?? widget.colors[0]; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.all(16), + child: Wrap( + spacing: 16, + runSpacing: 16, + children: widget.colors + .map( + (color) => _ColorHolder( + color: color, + active: color == _activeColor, + onTap: (color) { + setState(() => _activeColor = color); + widget.onColorPicked(color); + }, + ), + ) + .toList(), + ), + ); + } +} + +class _ColorHolder extends StatelessWidget { + final Color color; + final Function(Color) onTap; + final bool active; + + _ColorHolder({ + this.color, + this.onTap, + this.active = false, + }); + + @override + Widget build(BuildContext context) { + return Container( + height: 40, + width: 40, + decoration: BoxDecoration( + border: active + ? Border.fromBorderSide( + BorderSide(color: Theme.of(context).colorScheme.onSurface)) + : null, + borderRadius: BorderRadius.circular(50), + ), + child: Center( + child: GestureDetector( + onTap: () => onTap(color), + child: Container( + height: 35, + width: 35, + decoration: BoxDecoration( + border: Border.fromBorderSide( + BorderSide(color: Theme.of(context).colorScheme.onSurface)), + borderRadius: BorderRadius.circular(50), + color: color, + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/option_button.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/option_button.dart new file mode 100644 index 0000000..ad31cf2 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/option_button.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class OptionButton extends StatelessWidget { + final bool isActive; + final Function() onPressed; + final Widget child; + final Size size; + + OptionButton({ + this.onPressed, + this.child, + this.isActive = false, + this.size, + }); + @override + Widget build(BuildContext context) { + return RawMaterialButton( + constraints: BoxConstraints.tight(size ?? Size(45, 45)), + highlightColor: Theme.of(context).colorScheme.background, + splashColor: Theme.of(context).colorScheme.background, + fillColor: isActive ? Theme.of(context).colorScheme.background : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: BorderSide(color: Theme.of(context).colorScheme.surface), + ), + child: child, + onPressed: onPressed, + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/toolbar.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/toolbar.dart new file mode 100644 index 0000000..48dec72 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/toolbar.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; + +import 'option_button.dart'; +import 'toolbar_action.dart'; + +class Toolbar extends StatefulWidget { + final EditorToolbarAction initialTool; + final Function(EditorToolbarAction) onToolSelect; + + Toolbar({ + this.initialTool = EditorToolbarAction.editor, + this.onToolSelect, + }); + + @override + _ToolbarState createState() => _ToolbarState(); +} + +class _ToolbarState extends State { + EditorToolbarAction _selectedAction; + @override + void initState() { + _selectedAction = widget.initialTool; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + // OptionButton( + // isActive: _selectedAction == EditorToolbarAction.editor, + // child: Icon(Icons.keyboard), + // onPressed: () { + // setState(() => _selectedAction = EditorToolbarAction.editor); + // widget.onToolSelect(EditorToolbarAction.editor); + // }, + // ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.fontFamilyTool, + child: Icon(Icons.title), + onPressed: () { + setState( + () => _selectedAction = EditorToolbarAction.fontFamilyTool); + widget.onToolSelect(EditorToolbarAction.fontFamilyTool); + }, + ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.fontOptionTool, + child: Icon(Icons.strikethrough_s), + onPressed: () { + setState( + () => _selectedAction = EditorToolbarAction.fontOptionTool); + widget.onToolSelect(EditorToolbarAction.fontOptionTool); + }, + ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.fontSizeTool, + child: Icon(Icons.format_size), + onPressed: () { + setState(() => _selectedAction = EditorToolbarAction.fontSizeTool); + widget.onToolSelect(EditorToolbarAction.fontSizeTool); + }, + ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.fontColorTool, + child: Icon(Icons.format_color_text), + onPressed: () { + setState(() => _selectedAction = EditorToolbarAction.fontColorTool); + widget.onToolSelect(EditorToolbarAction.fontColorTool); + }, + ), + OptionButton( + isActive: _selectedAction == EditorToolbarAction.backgroundColorTool, + child: Icon(Icons.format_color_fill), + onPressed: () { + setState(() => + _selectedAction = EditorToolbarAction.backgroundColorTool); + widget.onToolSelect(EditorToolbarAction.backgroundColorTool); + }, + ), + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/toolbar_action.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/toolbar_action.dart new file mode 100644 index 0000000..07382cc --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/toolbar_action.dart @@ -0,0 +1,8 @@ +enum EditorToolbarAction { + editor, + fontFamilyTool, + fontOptionTool, + fontSizeTool, + fontColorTool, + backgroundColorTool, +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/background_color_tool.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/background_color_tool.dart new file mode 100644 index 0000000..340f9a4 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/background_color_tool.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import '../color_palette.dart'; + +class BackgroundColorTool extends StatelessWidget { + final List colors; + final Color activeColor; + final Function(Color) onColorPicked; + + BackgroundColorTool({ + this.colors, + this.onColorPicked, + this.activeColor, + }); + + @override + Widget build(BuildContext context) { + return ColorPalette( + activeColor: activeColor, + onColorPicked: onColorPicked, + colors: colors, + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_color_tool.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_color_tool.dart new file mode 100644 index 0000000..8da558d --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_color_tool.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import '../color_palette.dart'; + +class FontColorTool extends StatelessWidget { + final List colors; + final Color activeColor; + final Function(Color) onColorPicked; + + FontColorTool({ + this.colors, + this.onColorPicked, + this.activeColor, + }); + + @override + Widget build(BuildContext context) { + return ColorPalette( + activeColor: activeColor, + onColorPicked: onColorPicked, + colors: colors, + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_family_tool.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_family_tool.dart new file mode 100644 index 0000000..1b7ab48 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_family_tool.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; + +import '../option_button.dart'; + +class FontFamilyTool extends StatefulWidget { + final List fonts; + final Function(String) onSelectFont; + final String selectedFont; + + FontFamilyTool({ + this.fonts, + this.onSelectFont, + this.selectedFont, + }); + + @override + _FontFamilyToolState createState() => _FontFamilyToolState(); +} + +class _FontFamilyToolState extends State { + String _selectedFont; + + @override + void initState() { + _selectedFont = widget.selectedFont ?? widget.fonts[0]; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Wrap( + spacing: 12, + runSpacing: 12, + children: widget.fonts + .map<_FontFamily>( + (font) => _FontFamily( + font, + isSelected: _selectedFont == font, + onSelect: (selectedFont) { + setState(() => _selectedFont = selectedFont); + widget.onSelectFont(selectedFont); + }, + ), + ) + .toList(), + ); + } +} + +class _FontFamily extends StatelessWidget { + final String font; + final bool isSelected; + final Function(String) onSelect; + + _FontFamily(this.font, {this.onSelect, this.isSelected = false}); + @override + Widget build(BuildContext context) { + return OptionButton( + isActive: isSelected, + size: Size(90, 45), + onPressed: () => onSelect(font), + child: Center(child: Text(font, style: TextStyle(fontFamily: font))), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_size_tool.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_size_tool.dart new file mode 100644 index 0000000..55d7478 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/font_size_tool.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; + +class FontSizeTool extends StatelessWidget { + final double fontSize; + final double letterSpacing; + final double letterHeight; + final Function( + double fontSize, + double letterSpacing, + double letterHeight, + ) onFontSizeEdited; + + FontSizeTool({ + this.onFontSizeEdited, + this.fontSize = 0, + this.letterSpacing = 0, + this.letterHeight = 0, + }); + + @override + Widget build(BuildContext context) { + double _fontSize = fontSize; + double _letterSpacing = letterSpacing; + double _letterHeight = letterHeight; + + return Padding( + padding: EdgeInsets.all(16), + child: Column( + children: [ + _ResizeSlider( + value: _fontSize, + max: 45, + icon: Icons.format_size, + onChange: (value) { + _fontSize = value; + onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); + }, + ), + _ResizeSlider( + value: _letterHeight, + icon: Icons.format_line_spacing, + max: 10, + onChange: (value) { + _letterHeight = value; + onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); + }, + ), + _ResizeSlider( + value: _letterSpacing, + icon: Icons.settings_ethernet, + max: 10, + onChange: (value) { + _letterSpacing = value; + onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); + }, + ), + ], + ), + ); + } +} + +class _ResizeSlider extends StatefulWidget { + final double value; + final double min; + final double max; + final IconData icon; + final Function(double) onChange; + + _ResizeSlider({ + this.value, + this.icon, + this.onChange, + this.min = 0, + this.max = 100, + }); + + @override + _ResizeSliderState createState() => _ResizeSliderState(); +} + +class _ResizeSliderState extends State<_ResizeSlider> { + double _value; + + @override + void initState() { + _value = widget.value; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Icon(widget.icon), + Expanded( + child: SliderTheme( + data: SliderThemeData( + activeTrackColor: Theme.of(context).colorScheme.background, + inactiveTrackColor: Theme.of(context).colorScheme.background, + thumbColor: Theme.of(context).colorScheme.background, + overlayColor: + Theme.of(context).colorScheme.background.withOpacity(0.2), + trackHeight: 2, + ), + child: Slider( + value: _value, + onChanged: (value) { + setState(() => _value = value); + + widget.onChange(value); + }, + min: widget.min, + max: widget.max, + ), + ), + ), + Text(_value.toStringAsFixed(1)), + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/text_format_tool.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/text_format_tool.dart new file mode 100644 index 0000000..5365741 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/src/tools/text_format_tool.dart @@ -0,0 +1,237 @@ +import 'package:flutter/material.dart'; + +import '../option_button.dart'; + +class TextFormatTool extends StatelessWidget { + final Function( + bool bold, + bool italic, + ) onTextFormatEdited; + final Function(bool caps) onCpasLockTaggle; + final Function(TextAlign textAlign) onTextAlignEdited; + final TextAlign textAlign; + final bool bold; + final bool italic; + final bool caps; + + TextFormatTool({ + this.onTextFormatEdited, + this.onTextAlignEdited, + this.onCpasLockTaggle, + this.textAlign = TextAlign.left, + this.bold = false, + this.italic = false, + this.caps = false, + }); + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only(top: 36), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _TextFormatEditor( + bold: bold, + italic: italic, + caps: caps, + onFormatEdited: onTextFormatEdited, + onCpasLockTaggle: onCpasLockTaggle, + ), + SizedBox(height: 36), + _TextAlignEditor( + textAlign: textAlign, + onTextAlignEdited: onTextAlignEdited, + ), + ], + ), + ); + } +} + +class _TextAlignEditor extends StatefulWidget { + final TextAlign textAlign; + final Function(TextAlign textAlign) onTextAlignEdited; + + _TextAlignEditor({ + this.onTextAlignEdited, + this.textAlign = TextAlign.left, + }); + + @override + _TextAlignEditorState createState() => _TextAlignEditorState(); +} + +class _TextAlignEditorState extends State<_TextAlignEditor> { + TextAlign _textAlign; + + @override + void initState() { + _textAlign = widget.textAlign; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _TextAlignOption( + icon: Icons.format_align_left, + isActive: _textAlign == TextAlign.left, + onPressed: () { + setState(() => _textAlign = TextAlign.left); + widget.onTextAlignEdited(_textAlign); + }, + ), + _TextAlignOption( + icon: Icons.format_align_center, + isActive: _textAlign == TextAlign.center, + onPressed: () { + setState(() => _textAlign = TextAlign.center); + widget.onTextAlignEdited(_textAlign); + }, + ), + _TextAlignOption( + icon: Icons.format_align_right, + isActive: _textAlign == TextAlign.right, + onPressed: () { + setState(() => _textAlign = TextAlign.right); + widget.onTextAlignEdited(_textAlign); + }, + ), + _TextAlignOption( + icon: Icons.format_align_justify, + isActive: _textAlign == TextAlign.justify, + onPressed: () { + setState(() => _textAlign = TextAlign.justify); + widget.onTextAlignEdited(_textAlign); + }, + ), + ], + ); + } +} + +class _TextAlignOption extends StatelessWidget { + final IconData icon; + final Function() onPressed; + final bool isActive; + + _TextAlignOption({ + this.icon, + this.onPressed, + this.isActive = false, + }); + + @override + Widget build(BuildContext context) { + return IconButton( + iconSize: 32, + icon: Icon(icon), + color: isActive + ? Theme.of(context).iconTheme.color + : Theme.of(context).disabledColor, + onPressed: onPressed, + ); + } +} + +class _TextFormatEditor extends StatefulWidget { + final Function(bool bold, bool italic) onFormatEdited; + final Function(bool caps) onCpasLockTaggle; + final bool bold; + final bool italic; + final bool caps; + + _TextFormatEditor({ + this.onFormatEdited, + this.onCpasLockTaggle, + this.bold = false, + this.italic = false, + this.caps = false, + }); + + @override + _TextFormatEditorState createState() => _TextFormatEditorState(); +} + +class _TextFormatEditorState extends State<_TextFormatEditor> { + bool _bold; + bool _italic; + bool _caps; + + @override + void initState() { + _bold = widget.bold; + _italic = widget.italic; + _caps = widget.caps; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _TextFormatOption( + title: 'BOLD', + icon: Icons.format_bold, + isActive: _bold, + onPressed: () { + setState(() => _bold = !_bold); + widget.onFormatEdited(_bold, _italic); + }, + ), + _TextFormatOption( + title: 'ITALIC', + icon: Icons.format_italic, + isActive: _italic, + onPressed: () { + setState(() => _italic = !_italic); + widget.onFormatEdited(_bold, _italic); + }, + ), + _TextFormatOption( + title: 'CAPS', + icon: Icons.keyboard_capslock, + isActive: _caps, + onPressed: () { + setState(() => _caps = !_caps); + widget.onCpasLockTaggle(_caps); + }, + ), + ], + ); + } +} + +class _TextFormatOption extends StatelessWidget { + final String title; + final IconData icon; + final Function() onPressed; + final bool isActive; + + _TextFormatOption({ + this.title, + this.icon, + this.onPressed, + this.isActive = false, + }); + @override + Widget build(BuildContext context) { + return Column( + children: [ + OptionButton( + isActive: isActive, + onPressed: onPressed, + child: Icon(icon), + ), + SizedBox(height: 12), + Text(title), + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/text_style_editor.dart b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/text_style_editor.dart new file mode 100644 index 0000000..a3a8378 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Editor/textstyler/text_style_editor.dart @@ -0,0 +1,226 @@ +library text_style_editor; + +export 'src/toolbar_action.dart'; + +import 'package:flutter/material.dart'; +import 'src/toolbar_action.dart'; +import 'src/tools/background_color_tool.dart'; +import 'src/color_palette.dart'; +import 'src/tools/font_family_tool.dart'; +import 'src/tools/font_size_tool.dart'; +import 'src/tools/text_format_tool.dart'; +import 'src/toolbar.dart'; + +/// Text style editor +/// A flutter widget that edit text style and text alignment +/// +/// You can pass your text style or alignment to the widget +/// and then get the edited text style +class TextStyleEditor extends StatefulWidget { + /// Editor's font families + final List fonts; + + /// The text style + final TextStyle textStyle; + + /// The text alignment + final TextAlign textAlign; + + /// The inithial editor tool + final EditorToolbarAction initialTool; + + /// Editor's palette colors + final List paletteColors; + + /// [onTextStyleEdited] will be called after [textStyle] prop has changed + final Function(TextStyle) onTextStyleEdited; + + /// [onTextAlignEdited] will be called after [textAlingment] prop has changed + final Function(TextAlign) onTextAlignEdited; + + /// [onCpasLockTaggle] will be called after caps lock has changed + final Function(bool) onCpasLockTaggle; + + /// [onToolbarActionChanged] will be called after editor's tool has changed + final Function(EditorToolbarAction) onToolbarActionChanged; + + /// Create a [TextStyleEditor] widget + /// + /// [fonts] list of font families that you want to use in editor. + /// [textStyle] initiate text style. + /// [textAlign] initiate text alignment. + /// + /// [onTextStyleEdited] callback will be called every time [textStyle] has changed. + /// [onTextAlignEdited] callback will be called every time [textAlign] has changed. + /// [onCpasLockTaggle] callback will be called every time caps lock has changed to off or on. + /// [onToolbarActionChanged] callback will be called every time editor's tool has changed. + TextStyleEditor({ + this.fonts, + this.textStyle, + this.textAlign, + this.paletteColors, + this.initialTool = EditorToolbarAction.editor, + this.onTextStyleEdited, + this.onTextAlignEdited, + this.onCpasLockTaggle, + this.onToolbarActionChanged, + }); + + @override + _TextStyleEditorState createState() => _TextStyleEditorState(); +} + +class _TextStyleEditorState extends State { + EditorToolbarAction _currentTool; + TextStyle _textStyle; + TextAlign _textAlign; + List _paletteColors; + + @override + void initState() { + _currentTool = widget.initialTool; + _textStyle = widget.textStyle; + _textAlign = widget.textAlign; + + // Set default palette's colors + _paletteColors = widget.paletteColors ?? + [ + Colors.black, + Colors.white, + Colors.red, + Colors.blue, + Colors.blueAccent, + Colors.brown, + Colors.green, + Colors.indigoAccent, + Colors.lime, + ]; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + color: Theme.of(context).backgroundColor, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Toolbar( + initialTool: _currentTool, + onToolSelect: (action) { + setState(() => _currentTool = action); + if (widget.onToolbarActionChanged != null) { + widget.onToolbarActionChanged(action); + } + }, + ), + Divider(), + Container( + child: SingleChildScrollView( + child: () { + // Choice tools + switch (_currentTool) { + case EditorToolbarAction.fontFamilyTool: + return FontFamilyTool( + fonts: widget.fonts, + selectedFont: _textStyle.fontFamily, + onSelectFont: (fontFamily) { + setState(() => _textStyle = + _textStyle.copyWith(fontFamily: fontFamily)); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + ); + case EditorToolbarAction.fontOptionTool: + return TextFormatTool( + bold: _textStyle.fontWeight == FontWeight.bold, + italic: _textStyle.fontStyle == FontStyle.italic, + textAlign: _textAlign, + onTextFormatEdited: (bold, italic) { + setState(() => _textStyle = _textStyle.copyWith( + fontWeight: + bold ? FontWeight.bold : FontWeight.normal, + fontStyle: + italic ? FontStyle.italic : FontStyle.normal, + )); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + onTextAlignEdited: (align) { + setState(() => _textAlign = align); + + if (widget.onTextAlignEdited != null) { + widget.onTextAlignEdited(align); + } + }, + onCpasLockTaggle: (caps) { + if (widget.onCpasLockTaggle != null) { + widget.onCpasLockTaggle(caps); + } + }, + ); + case EditorToolbarAction.fontSizeTool: + return FontSizeTool( + fontSize: _textStyle.fontSize ?? 0, + letterHeight: _textStyle.height ?? 1.2, + letterSpacing: _textStyle.letterSpacing ?? 1, + onFontSizeEdited: ( + fontSize, + letterSpacing, + letterHeight, + ) { + setState(() => _textStyle = _textStyle.copyWith( + fontSize: fontSize, + height: letterHeight, + letterSpacing: letterSpacing, + )); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + ); + case EditorToolbarAction.fontColorTool: + return BackgroundColorTool( + activeColor: _textStyle.color, + colors: _paletteColors, + onColorPicked: (color) { + setState(() => + _textStyle = _textStyle.copyWith(color: color)); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + ); + case EditorToolbarAction.backgroundColorTool: + return ColorPalette( + activeColor: _textStyle.backgroundColor, + colors: _paletteColors, + onColorPicked: (color) { + setState(() => _textStyle = + _textStyle.copyWith(backgroundColor: color)); + + if (widget.onTextStyleEdited != null) { + widget.onTextStyleEdited(_textStyle); + } + }, + ); + case EditorToolbarAction.editor: + return Container(); + default: + return Container(); + } + }(), + ), + ), + ], + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/RecordVideo.dart b/lib/Pages/Sub_Pages/Campaign/Video/RecordVideo.dart new file mode 100644 index 0000000..7a7cb47 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/RecordVideo.dart @@ -0,0 +1,500 @@ +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:camera/camera.dart'; +import 'package:flutter/services.dart'; + +import 'package:image_picker/image_picker.dart'; +import 'package:teso/util/consts.dart'; +import 'package:page_transition/page_transition.dart'; +import 'dart:async'; +import 'package:circular_countdown_timer/circular_countdown_timer.dart'; +import 'package:video_thumbnail/video_thumbnail.dart' as thumb; + +import 'Editor/VideoReview.dart'; + +// ignore: must_be_immutable +class RecordVideo extends StatefulWidget { + final String campaignID; + List connectedCameras; + + RecordVideo({Key key, this.connectedCameras, @required this.campaignID}) + : super(key: key); + @override + _RecordVideoState createState() => _RecordVideoState(); +} + +class _RecordVideoState extends State + with TickerProviderStateMixin { + CameraController _controller; + int selectedCamera = 0; + bool flash = false; + bool frontFlash = false; + bool recording = false; + AnimationController _recordingAnimationController; + XFile video; + String filePath; + int recordEnd = 60; + CountDownController _controllerCountDown = CountDownController(); + final interval = const Duration(seconds: 1); + final picker = ImagePicker(); + bool gallery = false; + + final int timerMaxSeconds = 60; + + int currentSeconds = 0; + + flipCamera() { + selectedCamera++; + if (selectedCamera < widget.connectedCameras.length) { + onNewCameraSelected(widget.connectedCameras.elementAt(selectedCamera)); + } else { + selectedCamera = 0; + onNewCameraSelected(widget.connectedCameras.elementAt(selectedCamera)); + } + } + + flashCamera() { + try { + if (!flash && + _controller.description.lensDirection == CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.torch); + setState(() { + flash = true; + frontFlash = false; + }); + } else if (!flash && + _controller.description.lensDirection == CameraLensDirection.front) { + setState(() { + flash = true; + frontFlash = true; + }); + } else if (flash && + _controller.description.lensDirection == CameraLensDirection.back) { + _controller.setFlashMode(FlashMode.off); + setState(() { + flash = false; + }); + } else { + setState(() { + flash = false; + frontFlash = false; + }); + } + } catch (e) {} + } + + haltRecord() async { + XFile recorded = await stopVideoRecording(); + if (recorded != null) + Navigator.of(context).pushReplacement( + PageRouteBuilder( + opaque: false, + pageBuilder: (_, __, ___) => VideoReview( + video: recorded.path, + campaignID: widget.campaignID, + recorded: true, + ), + ), + ); + } + + Future generateThumbnail(video) async { + try { + Uint8List thumbnail; + + thumbnail = await thumb.VideoThumbnail.thumbnailData( + video: video, + imageFormat: thumb.ImageFormat.JPEG, + maxWidth: 0, + maxHeight: 0, + timeMs: 1, + quality: 100, + ); + return thumbnail; + } catch (e) { + print("Error :::: " + e); + return null; + } + } + + @override + void initState() { + if (widget.connectedCameras == null || + widget.connectedCameras.length == 0) { + availableCameras().then((value) { + widget.connectedCameras = value; + onNewCameraSelected(widget.connectedCameras.first); + }); + } else { + onNewCameraSelected(widget.connectedCameras.first); + } + _recordingAnimationController = + new AnimationController(vsync: this, duration: Duration(seconds: 1)); + + _recordingAnimationController.repeat(reverse: true); + super.initState(); + } + + sayCheese() async { + try { + if (flash && !frontFlash) + await _controller.setFlashMode(FlashMode.always); + await _controller.startVideoRecording(); + } catch (e) { + print(e); + } + } + + Future stopVideoRecording() async { + if (!_controller.value.isRecordingVideo) { + return null; + } + + try { + return _controller.stopVideoRecording(); + } on CameraException catch (e) { + print(e); + return null; + } + } + + void onNewCameraSelected(CameraDescription cameraDescription) async { + if (_controller != null) { + await _controller.dispose(); + } + _controller = CameraController( + cameraDescription, + ResolutionPreset.high, + enableAudio: true, + imageFormatGroup: ImageFormatGroup.jpeg, + ); + + // If the controller is updated then update the UI. + _controller.addListener(() { + if (mounted) setState(() {}); + if (_controller.value.hasError) { + print('Camera error ${_controller.value.errorDescription}'); + } + }); + + try { + await _controller.initialize(); + _controller.lockCaptureOrientation(DeviceOrientation.portraitUp); + _controller.setFocusMode(FocusMode.auto); + } on CameraException catch (e) { + print(e); + } + + if (mounted) { + setState(() {}); + } + } + + void onHocusFocus(TapDownDetails details, BoxConstraints constraints) { + final offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + _controller.setExposurePoint(offset); + _controller.setFocusPoint(offset); + } + + @override + void dispose() { + _controller?.dispose(); + _recordingAnimationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (_controller == null || !_controller.value.isInitialized) { + return Container( + color: Colors.black, + ); + } else { + return Scaffold( + body: !gallery + ? Stack( + children: [ + cameraWidget(context), + flashWidget(context), + cameraFlip(context), + cameraFlash(context), + recordingAnimation(context), + recordingCircle(context), + recorderWidget(context), + galleryPicker(context), + ], + ) + : Container(), + ); + } + } + + imgFromGallery() async { + try { + _controller?.dispose(); + setState(() { + gallery = true; + }); + final pickedFile = await picker.pickVideo( + source: ImageSource.gallery, + maxDuration: Duration(minutes: 1), + ); + + if (pickedFile != null) { + return pickedFile.path; + } else { + onNewCameraSelected(widget.connectedCameras.first); + print('No image selected.'); + } + } catch (e) { + print(e); + } + setState(() { + gallery = false; + }); + return; + } + + Widget recordingCircle(context) { + return Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + child: CircularCountDownTimer( + duration: recordEnd, + initialDuration: 0, + controller: _controllerCountDown, + width: MediaQuery.of(context).size.width / 2, + height: MediaQuery.of(context).size.height / 2, + ringColor: Colors.grey[300], + fillColor: Colors.red, + backgroundColor: Colors.transparent, + autoStart: false, + strokeWidth: 5.5, + isTimerTextShown: false, + strokeCap: StrokeCap.round, + //onStart: startTimeout, + onComplete: haltRecord, + ), + ), + ); + } + + Widget recorderWidget(context) { + return Align( + alignment: Alignment.bottomCenter, + child: InkWell( + onTap: recording + ? haltRecord + : () async { + await _controller.startVideoRecording(); + setState(() { + _controllerCountDown.start(); + recording = !recording; + }); + }, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + child: Icon( + recording ? Icons.stop : Icons.video_camera_back, + color: Colors.white, + size: 25, + ), + ), + ), + ); + } + + Widget galleryPicker(context) { + return Align( + alignment: Alignment.bottomLeft, + child: recording + ? Container() + : InkWell( + onTap: () async { + String result = await imgFromGallery(); + if (result != null) { + // _controller.dispose(); + Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: VideoReview( + video: result, + recorded: false, + campaignID: widget.campaignID, + ), + )); + } + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.05, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 70, + width: 70, + child: Icon( + Icons.photo, + color: Colors.white, + size: 27, + ), + ), + ), + ); + } + + Widget recordingAnimation(context) { + if (!recording) + return Align( + alignment: Alignment.topLeft, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.08, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + height: 35, + width: 35, + decoration: BoxDecoration( + //color: ColorFilterEngineLayer (0, 0, 0, 0.4), + shape: BoxShape.circle), + child: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + ), + )); + else + return Align( + alignment: Alignment.topLeft, + child: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.symmetric( + horizontal: 5, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + padding: EdgeInsets.all(2.5), + height: 20, + width: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.red, + width: 2, + )), + child: FadeTransition( + opacity: _recordingAnimationController, + child: Container( + width: 20, + height: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget cameraFlash(context) { + return !recording + ? Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.07, + vertical: MediaQuery.of(context).size.width * 0.25, + ), + child: InkWell( + onTap: flashCamera, + child: Icon( + flash ? Icons.flash_on : Icons.flash_off, + color: flash ? tesoGold : Colors.white, + size: 30, + ), + ), + ), + ) + : Container(); + } + + Widget cameraFlip(context) { + return !recording + ? Align( + alignment: Alignment.topRight, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.06, + vertical: MediaQuery.of(context).size.width * 0.11, + ), + child: InkWell( + onTap: flipCamera, + child: Icon( + Icons.cameraswitch_outlined, + color: Colors.white, + size: 40, + ), + ), + ), + ) + : Container(); + } + + Widget flashWidget(context) { + return Visibility( + visible: frontFlash, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.4), + ), + ), + ); + } + + Widget cameraWidget(context) { + var camera = _controller.value; + final size = MediaQuery.of(context).size; + var scale = size.aspectRatio * camera.aspectRatio; + if (scale < 1) scale = 1 / scale; + + return Transform.scale( + scale: scale, + child: Center( + child: CameraPreview( + _controller, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTapDown: (details) => onHocusFocus(details, constraints), + ); + }), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/file_formats.dart b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/file_formats.dart new file mode 100644 index 0000000..c5ec37f --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/file_formats.dart @@ -0,0 +1,45 @@ +/// The video file formats available for +/// generating the output trimmed video. +/// +/// The available formats are `mp4`, `mkv`, +/// `mov`, `flv`, `avi`, `wmv`& `gif`. +/// +/// If you define a custom `FFmpeg` command +/// then this will be ignored. +/// +class FileFormat { + const FileFormat._(this.index); + + final int index; + + static const FileFormat mp4 = FileFormat._(0); + static const FileFormat mkv = FileFormat._(1); + static const FileFormat mov = FileFormat._(2); + static const FileFormat flv = FileFormat._(3); + static const FileFormat avi = FileFormat._(4); + static const FileFormat wmv = FileFormat._(5); + static const FileFormat gif = FileFormat._(6); + + static const List values = [ + mp4, + mkv, + mov, + flv, + avi, + wmv, + gif, + ]; + + @override + String toString() { + return const { + 0: '.mp4', + 1: '.mkv', + 2: '.mov', + 3: '.flv', + 4: '.avi', + 5: '.wmv', + 6: '.gif', + }[index]; + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/storage_dir.dart b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/storage_dir.dart new file mode 100644 index 0000000..3c15be3 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/storage_dir.dart @@ -0,0 +1,32 @@ +/// Supported storage locations. +/// +/// * [temporaryDirectory] +/// +/// * [applicationDocumentsDirectory] +/// +/// * [externalStorageDirectory] +/// +class StorageDir { + const StorageDir._(this.index); + + final int index; + + static const StorageDir temporaryDirectory = StorageDir._(0); + static const StorageDir applicationDocumentsDirectory = StorageDir._(1); + static const StorageDir externalStorageDirectory = StorageDir._(2); + + static const List values = [ + temporaryDirectory, + applicationDocumentsDirectory, + externalStorageDirectory, + ]; + + @override + String toString() { + return const { + 0: 'temporaryDirectory', + 1: 'applicationDocumentsDirectory', + 2: 'externalStorageDirectory', + }[index]; + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/thumbnail_viewer.dart b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/thumbnail_viewer.dart new file mode 100644 index 0000000..c31a705 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/thumbnail_viewer.dart @@ -0,0 +1,81 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:video_thumbnail/video_thumbnail.dart'; + +class ThumbnailViewer extends StatelessWidget { + final videoFile; + final videoDuration; + final thumbnailHeight; + final fit; + final int numberOfThumbnails; + final int quality; + + /// For showing the thumbnails generated from the video, + /// like a frame by frame preview + ThumbnailViewer({ + @required this.videoFile, + @required this.videoDuration, + @required this.thumbnailHeight, + @required this.numberOfThumbnails, + @required this.fit, + this.quality = 75, + }) : assert(videoFile != null), + assert(videoDuration != null), + assert(thumbnailHeight != null), + assert(numberOfThumbnails != null), + assert(quality != null); + + Stream> generateThumbnail() async* { + final String _videoPath = videoFile.path; + + double _eachPart = videoDuration / numberOfThumbnails; + + List _byteList = []; + + for (int i = 1; i <= numberOfThumbnails; i++) { + Uint8List _bytes; + _bytes = await VideoThumbnail.thumbnailData( + video: _videoPath, + imageFormat: ImageFormat.JPEG, + timeMs: (_eachPart * i).toInt(), + quality: quality, + ); + + _byteList.add(_bytes); + + yield _byteList; + } + } + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: generateThumbnail(), + builder: (context, snapshot) { + if (snapshot.hasData) { + List _imageBytes = snapshot.data; + return ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: snapshot.data.length, + itemBuilder: (context, index) { + return Container( + height: thumbnailHeight, + width: thumbnailHeight, + child: Image( + image: MemoryImage(_imageBytes[index]), + fit: fit, + ), + ); + }); + } else { + return Container( + color: Colors.grey[900], + height: thumbnailHeight, + width: double.maxFinite, + ); + } + }, + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor.dart b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor.dart new file mode 100644 index 0000000..20780b6 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor.dart @@ -0,0 +1,537 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/thumbnail_viewer.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor_painter.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/trimmer.dart'; +import 'package:video_player/video_player.dart'; + +VideoPlayerController videoPlayerController; + +class TrimEditor extends StatefulWidget { + /// For defining the total trimmer area width + final double viewerWidth; + + /// For defining the total trimmer area height + final double viewerHeight; + + /// For defining the image fit type of each thumbnail image. + /// + /// By default it is set to `BoxFit.fitHeight`. + final BoxFit fit; + + /// For defining the maximum length of the output video. + final Duration maxVideoLength; + + /// For specifying a size to the holder at the + /// two ends of the video trimmer area, while it is `idle`. + /// + /// By default it is set to `5.0`. + final double circleSize; + + /// For specifying a size to the holder at + /// the two ends of the video trimmer area, while it is being + /// `dragged`. + /// + /// By default it is set to `8.0`. + final double circleSizeOnDrag; + + /// For specifying a color to the circle. + /// + /// By default it is set to `Colors.white`. + final Color circlePaintColor; + + /// For specifying a color to the border of + /// the trim area. + /// + /// By default it is set to `Colors.white`. + final Color borderPaintColor; + + /// For specifying a color to the video + /// scrubber inside the trim area. + /// + /// By default it is set to `Colors.white`. + final Color scrubberPaintColor; + + /// For specifying the quality of each + /// generated image thumbnail, to be displayed in the trimmer + /// area. + final int thumbnailQuality; + + /// For showing the start and the end point of the + /// video on top of the trimmer area. + /// + /// By default it is set to `true`. + final bool showDuration; + + /// For providing a `TextStyle` to the + /// duration text. + /// + /// By default it is set to `TextStyle(color: Colors.white)` + final TextStyle durationTextStyle; + + /// Callback to the video start position + /// + /// Returns the selected video start position in `milliseconds`. + final Function(double startValue) onChangeStart; + + /// Callback to the video end position. + /// + /// Returns the selected video end position in `milliseconds`. + final Function(double endValue) onChangeEnd; + + /// Callback to the video playback + /// state to know whether it is currently playing or paused. + /// + /// Returns a `boolean` value. If `true`, video is currently + /// playing, otherwise paused. + final Function(bool isPlaying) onChangePlaybackState; + + /// Widget for displaying the video trimmer. + /// + /// This has frame wise preview of the video with a + /// slider for selecting the part of the video to be + /// trimmed. + /// + /// The required parameters are [viewerWidth] & [viewerHeight] + /// + /// * [viewerWidth] to define the total trimmer area width. + /// + /// + /// * [viewerHeight] to define the total trimmer area height. + /// + /// + /// The optional parameters are: + /// + /// * [fit] for specifying the image fit type of each thumbnail image. + /// By default it is set to `BoxFit.fitHeight`. + /// + /// + /// * [maxVideoLength] for specifying the maximum length of the + /// output video. + /// + /// + /// * [circleSize] for specifying a size to the holder at the + /// two ends of the video trimmer area, while it is `idle`. + /// By default it is set to `5.0`. + /// + /// + /// * [circleSizeOnDrag] for specifying a size to the holder at + /// the two ends of the video trimmer area, while it is being + /// `dragged`. By default it is set to `8.0`. + /// + /// + /// * [circlePaintColor] for specifying a color to the circle. + /// By default it is set to `Colors.white`. + /// + /// + /// * [borderPaintColor] for specifying a color to the border of + /// the trim area. By default it is set to `Colors.white`. + /// + /// + /// * [scrubberPaintColor] for specifying a color to the video + /// scrubber inside the trim area. By default it is set to + /// `Colors.white`. + /// + /// + /// * [thumbnailQuality] for specifying the quality of each + /// generated image thumbnail, to be displayed in the trimmer + /// area. + /// + /// + /// * [showDuration] for showing the start and the end point of the + /// video on top of the trimmer area. By default it is set to `true`. + /// + /// + /// * [durationTextStyle] is for providing a `TextStyle` to the + /// duration text. By default it is set to + /// `TextStyle(color: Colors.white)` + /// + /// + /// * [onChangeStart] is a callback to the video start position. + /// + /// + /// * [onChangeEnd] is a callback to the video end position. + /// + /// + /// * [onChangePlaybackState] is a callback to the video playback + /// state to know whether it is currently playing or paused. + /// + TrimEditor({ + @required this.viewerWidth, + @required this.viewerHeight, + this.fit = BoxFit.fitHeight, + this.maxVideoLength = const Duration(milliseconds: 0), + this.circleSize = 5.0, + this.circleSizeOnDrag = 8.0, + this.circlePaintColor = Colors.white, + this.borderPaintColor = Colors.white, + this.scrubberPaintColor = Colors.white, + this.thumbnailQuality = 75, + this.showDuration = true, + this.durationTextStyle = const TextStyle( + color: Colors.white, + ), + this.onChangeStart, + this.onChangeEnd, + this.onChangePlaybackState, + }) : assert(viewerWidth != null), + assert(viewerHeight != null), + assert(fit != null), + assert(maxVideoLength != null), + assert(circleSize != null), + assert(circleSizeOnDrag != null), + assert(circlePaintColor != null), + assert(borderPaintColor != null), + assert(scrubberPaintColor != null), + assert(thumbnailQuality != null), + assert(showDuration != null), + assert(durationTextStyle != null); + + @override + _TrimEditorState createState() => _TrimEditorState(); +} + +class _TrimEditorState extends State with TickerProviderStateMixin { + File _videoFile; + + double _videoStartPos = 0.0; + double _videoEndPos = 0.0; + + bool _canUpdateStart = true; + bool _isLeftDrag = true; + + Offset _startPos = Offset(0, 0); + Offset _endPos = Offset(0, 0); + + double _startFraction = 0.0; + double _endFraction = 1.0; + + int _videoDuration = 0; + int _currentPosition = 0; + + double _thumbnailViewerW = 0.0; + double _thumbnailViewerH = 0.0; + + int _numberOfThumbnails = 0; + + double _circleSize; + + double fraction; + double maxLengthPixels; + + ThumbnailViewer thumbnailWidget; + + Animation _scrubberAnimation; + AnimationController _animationController; + Tween _linearTween; + + Future _initializeVideoController() async { + if (_videoFile != null) { + videoPlayerController.addListener(() { + final bool isPlaying = videoPlayerController.value.isPlaying; + + if (isPlaying) { + widget.onChangePlaybackState(true); + setState(() { + _currentPosition = + videoPlayerController.value.position.inMilliseconds; + + if (_currentPosition > _videoEndPos.toInt()) { + widget.onChangePlaybackState(false); + videoPlayerController.pause(); + _animationController.stop(); + } else { + if (!_animationController.isAnimating) { + widget.onChangePlaybackState(true); + _animationController.forward(); + } + } + }); + } else { + if (videoPlayerController.value.isInitialized) { + if (_animationController != null) { + if ((_scrubberAnimation.value).toInt() == (_endPos.dx).toInt()) { + _animationController.reset(); + } + _animationController.stop(); + widget.onChangePlaybackState(false); + } + } + } + }); + + videoPlayerController.setVolume(1.0); + _videoDuration = videoPlayerController.value.duration.inMilliseconds; + print(_videoFile.path); + + _videoEndPos = fraction != null + ? _videoDuration.toDouble() * fraction + : _videoDuration.toDouble(); + + widget.onChangeEnd(_videoEndPos); + + final ThumbnailViewer _thumbnailWidget = ThumbnailViewer( + videoFile: _videoFile, + videoDuration: _videoDuration, + fit: widget.fit, + thumbnailHeight: _thumbnailViewerH, + numberOfThumbnails: _numberOfThumbnails, + quality: widget.thumbnailQuality, + ); + thumbnailWidget = _thumbnailWidget; + } + } + + void _setVideoStartPosition(DragUpdateDetails details) async { + if (!(_startPos.dx + details.delta.dx < 0) && + !(_startPos.dx + details.delta.dx > _thumbnailViewerW) && + !(_startPos.dx + details.delta.dx > _endPos.dx)) { + if (maxLengthPixels != null) { + if (!(_endPos.dx - _startPos.dx - details.delta.dx > maxLengthPixels)) { + setState(() { + if (!(_startPos.dx + details.delta.dx < 0)) + _startPos += details.delta; + + _startFraction = (_startPos.dx / _thumbnailViewerW); + + _videoStartPos = _videoDuration * _startFraction; + widget.onChangeStart(_videoStartPos); + }); + await videoPlayerController.pause(); + await videoPlayerController + .seekTo(Duration(milliseconds: _videoStartPos.toInt())); + _linearTween.begin = _startPos.dx; + _animationController.duration = + Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); + _animationController.reset(); + } + } else { + setState(() { + if (!(_startPos.dx + details.delta.dx < 0)) + _startPos += details.delta; + + _startFraction = (_startPos.dx / _thumbnailViewerW); + + _videoStartPos = _videoDuration * _startFraction; + widget.onChangeStart(_videoStartPos); + }); + await videoPlayerController.pause(); + await videoPlayerController + .seekTo(Duration(milliseconds: _videoStartPos.toInt())); + _linearTween.begin = _startPos.dx; + _animationController.duration = + Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); + _animationController.reset(); + } + } + } + + void _setVideoEndPosition(DragUpdateDetails details) async { + if (!(_endPos.dx + details.delta.dx > _thumbnailViewerW) && + !(_endPos.dx + details.delta.dx < 0) && + !(_endPos.dx + details.delta.dx < _startPos.dx)) { + if (maxLengthPixels != null) { + if (!(_endPos.dx - _startPos.dx + details.delta.dx > maxLengthPixels)) { + setState(() { + _endPos += details.delta; + _endFraction = _endPos.dx / _thumbnailViewerW; + + _videoEndPos = _videoDuration * _endFraction; + widget.onChangeEnd(_videoEndPos); + }); + await videoPlayerController.pause(); + await videoPlayerController + .seekTo(Duration(milliseconds: _videoEndPos.toInt())); + _linearTween.end = _endPos.dx; + _animationController.duration = + Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); + _animationController.reset(); + } + } else { + setState(() { + _endPos += details.delta; + _endFraction = _endPos.dx / _thumbnailViewerW; + + _videoEndPos = _videoDuration * _endFraction; + widget.onChangeEnd(_videoEndPos); + }); + await videoPlayerController.pause(); + await videoPlayerController + .seekTo(Duration(milliseconds: _videoEndPos.toInt())); + _linearTween.end = _endPos.dx; + _animationController.duration = + Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); + _animationController.reset(); + } + } + } + + @override + void initState() { + super.initState(); + _circleSize = widget.circleSize; + + _videoFile = Trimmer.currentVideoFile; + _thumbnailViewerH = widget.viewerHeight; + + _numberOfThumbnails = widget.viewerWidth ~/ _thumbnailViewerH; + + _thumbnailViewerW = _numberOfThumbnails * _thumbnailViewerH; + + Duration totalDuration = videoPlayerController.value.duration; + + if (widget.maxVideoLength > Duration(milliseconds: 0) && + widget.maxVideoLength < totalDuration) { + if (widget.maxVideoLength < totalDuration) { + fraction = + widget.maxVideoLength.inMilliseconds / totalDuration.inMilliseconds; + + maxLengthPixels = _thumbnailViewerW * fraction; + } + } + + _initializeVideoController(); + _endPos = Offset( + maxLengthPixels != null ? maxLengthPixels : _thumbnailViewerW, + _thumbnailViewerH, + ); + + // Defining the tween points + _linearTween = Tween(begin: _startPos.dx, end: _endPos.dx); + + _animationController = AnimationController( + vsync: this, + duration: Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()), + ); + + _scrubberAnimation = _linearTween.animate(_animationController) + ..addListener(() { + setState(() {}); + }) + ..addStatusListener((status) { + if (status == AnimationStatus.completed) { + _animationController.stop(); + } + }); + } + + @override + void dispose() { + videoPlayerController.pause(); + widget.onChangePlaybackState(false); + if (_videoFile != null) { + videoPlayerController.setVolume(0.0); + videoPlayerController.pause(); + videoPlayerController.dispose(); + widget.onChangePlaybackState(false); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onHorizontalDragStart: (DragStartDetails details) { + print("START"); + print(details.localPosition); + print((_startPos.dx - details.localPosition.dx).abs()); + print((_endPos.dx - details.localPosition.dx).abs()); + + if (_endPos.dx >= _startPos.dx) { + if ((_startPos.dx - details.localPosition.dx).abs() > + (_endPos.dx - details.localPosition.dx).abs()) { + setState(() { + _canUpdateStart = false; + }); + } else { + setState(() { + _canUpdateStart = true; + }); + } + } else { + if (_startPos.dx > details.localPosition.dx) { + _isLeftDrag = true; + } else { + _isLeftDrag = false; + } + } + }, + onHorizontalDragEnd: (DragEndDetails details) { + setState(() { + _circleSize = widget.circleSize; + }); + }, + onHorizontalDragUpdate: (DragUpdateDetails details) { + _circleSize = widget.circleSizeOnDrag; + + if (_endPos.dx >= _startPos.dx) { + _isLeftDrag = false; + if (_canUpdateStart && _startPos.dx + details.delta.dx > 0) { + _isLeftDrag = false; // To prevent from scrolling over + _setVideoStartPosition(details); + } else if (!_canUpdateStart && + _endPos.dx + details.delta.dx < _thumbnailViewerW) { + _isLeftDrag = true; // To prevent from scrolling over + _setVideoEndPosition(details); + } + } else { + if (_isLeftDrag && _startPos.dx + details.delta.dx > 0) { + _setVideoStartPosition(details); + } else if (!_isLeftDrag && + _endPos.dx + details.delta.dx < _thumbnailViewerW) { + _setVideoEndPosition(details); + } + } + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + widget.showDuration + ? Container( + width: _thumbnailViewerW, + child: Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + Duration(milliseconds: _videoStartPos.toInt()) + .toString() + .split('.')[0], + style: widget.durationTextStyle, + ), + Text( + Duration(milliseconds: _videoEndPos.toInt()) + .toString() + .split('.')[0], + style: widget.durationTextStyle, + ), + ], + ), + ), + ) + : Container(), + CustomPaint( + foregroundPainter: TrimEditorPainter( + startPos: _startPos, + endPos: _endPos, + scrubberAnimationDx: _scrubberAnimation.value, + circleSize: _circleSize, + circlePaintColor: widget.circlePaintColor, + borderPaintColor: widget.borderPaintColor, + scrubberPaintColor: widget.scrubberPaintColor, + ), + child: Container( + color: Colors.grey[900], + height: _thumbnailViewerH, + width: _thumbnailViewerW, + child: thumbnailWidget == null ? Column() : thumbnailWidget, + ), + ), + ], + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor_painter.dart b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor_painter.dart new file mode 100644 index 0000000..91df007 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor_painter.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; + +class TrimEditorPainter extends CustomPainter { + /// To define the start offset + final Offset startPos; + + /// To define the end offset + final Offset endPos; + + /// To define the horizontal length of the selected video area + final double scrubberAnimationDx; + + /// For specifying a size to the holder at the + /// two ends of the video trimmer area, while it is `idle`. + /// By default it is set to `0.5`. + final double circleSize; + + /// For specifying the width of the border around + /// the trim area. By default it is set to `3`. + final double borderWidth; + + /// For specifying the width of the video scrubber + final double scrubberWidth; + + /// For specifying whether to show the scrubber + final bool showScrubber; + + /// For specifying a color to the border of + /// the trim area. By default it is set to `Colors.white`. + final Color borderPaintColor; + + /// For specifying a color to the circle. + /// By default it is set to `Colors.white` + final Color circlePaintColor; + + /// For specifying a color to the video + /// scrubber inside the trim area. By default it is set to + /// `Colors.white`. + final Color scrubberPaintColor; + + /// For drawing the trim editor slider + /// + /// The required parameters are [startPos], [endPos] + /// & [scrubberAnimationDx] + /// + /// * [startPos] to define the start offset + /// + /// + /// * [endPos] to define the end offset + /// + /// + /// * [scrubberAnimationDx] to define the horizontal length of the + /// selected video area + /// + /// + /// The optional parameters are: + /// + /// * [circleSize] for specifying a size to the holder at the + /// two ends of the video trimmer area, while it is `idle`. + /// By default it is set to `0.5`. + /// + /// + /// * [borderWidth] for specifying the width of the border around + /// the trim area. By default it is set to `3`. + /// + /// + /// * [scrubberWidth] for specifying the width of the video scrubber + /// + /// + /// * [showScrubber] for specifying whether to show the scrubber + /// + /// + /// * [borderPaintColor] for specifying a color to the border of + /// the trim area. By default it is set to `Colors.white`. + /// + /// + /// * [circlePaintColor] for specifying a color to the circle. + /// By default it is set to `Colors.white`. + /// + /// + /// * [scrubberPaintColor] for specifying a color to the video + /// scrubber inside the trim area. By default it is set to + /// `Colors.white`. + /// + TrimEditorPainter({ + @required this.startPos, + @required this.endPos, + @required this.scrubberAnimationDx, + this.circleSize = 0.5, + this.borderWidth = 3, + this.scrubberWidth = 1, + this.showScrubber = true, + this.borderPaintColor = Colors.white, + this.circlePaintColor = Colors.white, + this.scrubberPaintColor = Colors.white, + }) : assert(startPos != null), + assert(endPos != null), + assert(scrubberAnimationDx != null), + assert(circleSize != null), + assert(borderWidth != null), + assert(scrubberWidth != null), + assert(showScrubber != null), + assert(borderPaintColor != null), + assert(circlePaintColor != null), + assert(scrubberPaintColor != null); + + @override + void paint(Canvas canvas, Size size) { + var borderPaint = Paint() + ..color = borderPaintColor + ..strokeWidth = borderWidth + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + var circlePaint = Paint() + ..color = circlePaintColor + ..strokeWidth = 1 + ..style = PaintingStyle.fill + ..strokeCap = StrokeCap.round; + + var scrubberPaint = Paint() + ..color = scrubberPaintColor + ..strokeWidth = scrubberWidth + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + final rect = Rect.fromPoints(startPos, endPos); + + if (showScrubber) { + if (scrubberAnimationDx.toInt() > startPos.dx.toInt()) { + canvas.drawLine( + Offset(scrubberAnimationDx, 0), + Offset(scrubberAnimationDx, 0) + Offset(0, endPos.dy), + scrubberPaint, + ); + } + } + + canvas.drawRect(rect, borderPaint); + canvas.drawCircle( + startPos + Offset(0, endPos.dy / 2), circleSize, circlePaint); + canvas.drawCircle( + endPos + Offset(0, -endPos.dy / 2), circleSize, circlePaint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} diff --git a/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trimmer.dart b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trimmer.dart new file mode 100644 index 0000000..3973e70 --- /dev/null +++ b/lib/Pages/Sub_Pages/Campaign/Video/Trimmer/trimmer.dart @@ -0,0 +1,300 @@ +import 'dart:io'; +import 'package:path/path.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; +import 'package:intl/intl.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/file_formats.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/storage_dir.dart'; +import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor.dart'; +import 'package:video_player/video_player.dart'; + +/// Helps in loading video from file, saving trimmed video to a file +/// and gives video playback controls. Some of the helpful methods +/// are: +/// * [loadVideo()] +/// * [saveTrimmedVideo()] +/// * [videPlaybackControl()] +class Trimmer { + static File currentVideoFile; + + final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg(); + + /// Loads a video using the path provided. + /// + /// Returns the loaded video file. + Future loadVideo({@required File videoFile}) async { + currentVideoFile = videoFile; + if (currentVideoFile != null) { + videoPlayerController = VideoPlayerController.file(currentVideoFile); + await videoPlayerController.initialize().then((_) { + TrimEditor( + viewerHeight: 50, + viewerWidth: 50.0 * 8, + // currentVideoFile: currentVideoFile, + ); + }); + // TrimEditor( + // viewerHeight: 50, + // viewerWidth: 50.0 * 8, + // // currentVideoFile: currentVideoFile, + // ); + } + } + + Future _createFolderInAppDocDir( + String folderName, + StorageDir storageDir, + ) async { + Directory _directory; + + if (storageDir == null) { + _directory = await getApplicationDocumentsDirectory(); + } else { + switch (storageDir.toString()) { + case 'temporaryDirectory': + _directory = await getTemporaryDirectory(); + break; + + case 'applicationDocumentsDirectory': + _directory = await getApplicationDocumentsDirectory(); + break; + + case 'externalStorageDirectory': + _directory = await getExternalStorageDirectory(); + break; + } + } + + // Directory + folder name + final Directory _directoryFolder = + Directory('${_directory.path}/$folderName/'); + + if (await _directoryFolder.exists()) { + // If folder already exists return path + print('Exists'); + return _directoryFolder.path; + } else { + print('Creating'); + // If folder does not exists create folder and then return its path + final Directory _directoryNewFolder = + await _directoryFolder.create(recursive: true); + return _directoryNewFolder.path; + } + } + + /// Saves the trimmed video to file system. + /// + /// Returns the output video path + /// + /// The required parameters are [startValue] & [endValue]. + /// + /// The optional parameters are [videoFolderName], [videoFileName], + /// [outputFormat], [fpsGIF], [scaleGIF], [applyVideoEncoding]. + /// + /// The `@required` parameter [startValue] is for providing a starting point + /// to the trimmed video. To be specified in `milliseconds`. + /// + /// The `@required` parameter [endValue] is for providing an ending point + /// to the trimmed video. To be specified in `milliseconds`. + /// + /// The parameter [videoFolderName] is used to + /// pass a folder name which will be used for creating a new + /// folder in the selected directory. The default value for + /// it is `Trimmer`. + /// + /// The parameter [videoFileName] is used for giving + /// a new name to the trimmed video file. By default the + /// trimmed video is named as `_trimmed.mp4`. + /// + /// The parameter [outputFormat] is used for providing a + /// file format to the trimmed video. This only accepts value + /// of [FileFormat] type. By default it is set to `FileFormat.mp4`, + /// which is for `mp4` files. + /// + /// The parameter [storageDir] can be used for providing a storage + /// location option. It accepts only [StorageDir] values. By default + /// it is set to [applicationDocumentsDirectory]. Some of the + /// storage types are: + /// + /// * [temporaryDirectory] (Only accessible from inside the app, can be + /// cleared at anytime) + /// + /// * [applicationDocumentsDirectory] (Only accessible from inside the app) + /// + /// * [externalStorageDirectory] (Supports only `Android`, accessible externally) + /// + /// The parameters [fpsGIF] & [scaleGIF] are used only if the + /// selected output format is `FileFormat.gif`. + /// + /// * [fpsGIF] for providing a FPS value (by default it is set + /// to `10`) + /// + /// + /// * [scaleGIF] for proving a width to output GIF, the height + /// is selected by maintaining the aspect ratio automatically (by + /// default it is set to `480`) + /// + /// + /// * [applyVideoEncoding] for specifying whether to apply video + /// encoding (by default it is set to `false`). + /// + /// + /// ADVANCED OPTION: + /// + /// If you want to give custom `FFmpeg` command, then define + /// [ffmpegCommand] & [customVideoFormat] strings. The `input path`, + /// `output path`, `start` and `end` position is already define. + /// + /// NOTE: The advanced option does not provide any safety check, so if wrong + /// video format is passed in [customVideoFormat], then the app may + /// crash. + /// + Future saveTrimmedVideo({ + @required double startValue, + @required double endValue, + bool applyVideoEncoding = false, + FileFormat outputFormat, + String ffmpegCommand, + String customVideoFormat, + int fpsGIF, + int scaleGIF, + String videoFolderName, + String videoFileName, + StorageDir storageDir, + }) async { + final String _videoPath = currentVideoFile.path; + final String _videoName = basename(_videoPath).split('.')[0]; + + String _command; + + // Formatting Date and Time + String dateTime = DateFormat.yMMMd() + .addPattern('-') + .add_Hms() + .format(DateTime.now()) + .toString(); + + // String _resultString; + String _outputPath; + String _outputFormatString; + String formattedDateTime = dateTime.replaceAll(' ', ''); + + print("DateTime: $dateTime"); + print("Formatted: $formattedDateTime"); + + if (videoFolderName == null) { + videoFolderName = "Trimmer"; + } + + if (videoFileName == null) { + videoFileName = "${_videoName}_trimmed:$formattedDateTime"; + } + + videoFileName = videoFileName.replaceAll(' ', '_'); + + String path = await _createFolderInAppDocDir( + videoFolderName, + storageDir, + ).whenComplete( + () => print("Retrieved Trimmer folder"), + ); + + Duration startPoint = Duration(milliseconds: startValue.toInt()); + Duration endPoint = Duration(milliseconds: endValue.toInt()); + + // Checking the start and end point strings + print("Start: ${startPoint.toString()} & End: ${endPoint.toString()}"); + + print(path); + + if (outputFormat == null) { + if (Platform.isIOS) { + outputFormat = FileFormat.mp4; + } else { + outputFormat = FileFormat.mkv; + } + _outputFormatString = outputFormat.toString(); + print('OUTPUT: $_outputFormatString'); + } else { + _outputFormatString = outputFormat.toString(); + } + + String _trimLengthCommand = + ' -ss $startPoint -i "$_videoPath" -t ${endPoint - startPoint} -avoid_negative_ts make_zero '; + + if (ffmpegCommand == null) { + _command = '$_trimLengthCommand -c:a copy '; + + if (!applyVideoEncoding) { + _command += '-c:v copy '; + } + + if (outputFormat == FileFormat.gif) { + if (fpsGIF == null) { + fpsGIF = 10; + } + if (scaleGIF == null) { + scaleGIF = 480; + } + _command = + '$_trimLengthCommand -vf "fps=$fpsGIF,scale=$scaleGIF:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 '; + } + } else { + _command = '$_trimLengthCommand $ffmpegCommand '; + _outputFormatString = customVideoFormat; + } + + _outputPath = '$path$videoFileName$_outputFormatString'; + + _command += '"$_outputPath"'; + + await _flutterFFmpeg.execute(_command).whenComplete(() { + print('Got value'); + debugPrint('Video successfuly saved'); + // _resultString = 'Video successfuly saved'; + }).catchError((error) { + print('Error'); + // _resultString = 'Couldn\'t save the video'; + debugPrint('Couldn\'t save the video'); + }); + + return _outputPath; + } + + /// For getting the video controller state, to know whether the + /// video is playing or paused currently. + /// + /// The two required parameters are [startValue] & [endValue] + /// + /// * [startValue] is the current starting point of the video. + /// * [endValue] is the current ending point of the video. + /// + /// Returns a `Future`, if `true` then video is playing + /// otherwise paused. + Future videPlaybackControl({ + @required double startValue, + @required double endValue, + }) async { + if (videoPlayerController.value.isPlaying) { + await videoPlayerController.pause(); + return false; + } else { + if (videoPlayerController.value.position.inMilliseconds >= + endValue.toInt()) { + await videoPlayerController + .seekTo(Duration(milliseconds: startValue.toInt())); + await videoPlayerController.play(); + return true; + } else { + await videoPlayerController.play(); + return true; + } + } + } + + File getVideoFile() { + return currentVideoFile; + } +} diff --git a/lib/Pages/Sub_Pages/CoinPurchase.dart b/lib/Pages/Sub_Pages/CoinPurchase.dart new file mode 100644 index 0000000..104ae06 --- /dev/null +++ b/lib/Pages/Sub_Pages/CoinPurchase.dart @@ -0,0 +1,173 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/consts.dart'; + +import 'package:teso/Pages/PageWidgets/CoinPurchase/selector.dart'; +import 'CoinsPurchase/GoldTransactions.dart'; +import 'CoinsPurchase/SilverTransaction.dart'; + +class Coins extends StatefulWidget { + final int initalPage; + + const Coins({Key key, @required this.initalPage}) : super(key: key); + @override + _CoinsState createState() => _CoinsState(); +} + +class _CoinsState extends State { + PageController _pageController; + Color goldText = Colors.white; + Color silverText = tesoBlue; + List gold = [ + tesoBlue, + tesoBlue, + ]; + List silver = [ + Colors.white, + Colors.white, + ]; + + void selectGold() { + setState(() { + gold = [ + tesoBlue, + tesoBlue, + ]; + silver = [ + Colors.white, + Colors.white, + ]; + goldText = Colors.white; + silverText = tesoBlue; + }); + } + + void selectSilver() { + setState(() { + silver = [ + tesoBlue, + tesoBlue, + ]; + gold = [ + Colors.white, + Colors.white, + ]; + silverText = Colors.white; + goldText = tesoBlue; + }); + } + + void selectedPage(int pages) { + if (pages == 0) { + selectGold(); + _pageController.jumpToPage(0); + } else { + selectSilver(); + _pageController.jumpToPage(1); + } + } + + @override + void initState() { + super.initState(); + _pageController = + PageController(initialPage: widget.initalPage, keepPage: false); + widget.initalPage == 0 ? selectGold() : selectSilver(); + } + + @override + void dispose() { + super.dispose(); + _pageController.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: Container( + height: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.5), + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [tesoGold, tesoAsh, tesoBlue], + ), + ), + child: Column( + children: [ + AppBar( + backgroundColor: Colors.transparent, + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + onPressed: () => Navigator.pop(context), + ), + title: Center( + child: Text( + "Teso Coins", + style: TextStyle(color: Colors.white), + )), + ), + SizedBox(height: 20.0), + Container( + padding: EdgeInsets.all(5), + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.28), + height: 52.5, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topRight: Radius.circular(25), + topLeft: Radius.circular(25), + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildSelector( + context, + "Gold Coins", + MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.25), + gold, + () => selectedPage(0), + goldText), + buildSelector( + context, + "Silver Coins", + MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.25), + silver, + () => selectedPage(1), + silverText), + ], + ), + ), + ], + ), + ), + preferredSize: Size.fromHeight( + MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width * 0.5), + ), + ), + body: PageView( + controller: _pageController, + onPageChanged: (int i) { + selectedPage(i); + }, + //physics: NeverScrollableScrollPhysics(), + children: [ + GoldTransactions(), + SilverTransaction(), + ], + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/CoinsPurchase/GoldTransactions.dart b/lib/Pages/Sub_Pages/CoinsPurchase/GoldTransactions.dart new file mode 100644 index 0000000..0c968c7 --- /dev/null +++ b/lib/Pages/Sub_Pages/CoinsPurchase/GoldTransactions.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'GoldWithdrawal.dart'; + +class GoldTransactions extends StatefulWidget { + @override + _GoldTransactionsState createState() => _GoldTransactionsState(); +} + +class _GoldTransactionsState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(65), + child: Container( + child: Material( + elevation: 5.0, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Padding( + padding: const EdgeInsets.all(7.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( + direction: Axis.horizontal, + children: [ + Container( + child: Container( + height: 50.0, + width: 50.0, + decoration: new BoxDecoration( + shape: BoxShape.circle, + ), + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(50.0), + topRight: Radius.circular(50.0), + bottomLeft: Radius.circular(50), + bottomRight: Radius.circular(50), + ), + child: Image( + height: 50, + width: 50, + fit: BoxFit.fill, + image: + AssetImage("assets/images/gold1.png"), + ), + ), + ), + ), + ), + Consumer( + builder: (context, value, child) => Container( + width: 40, + height: 40, + child: Center( + child: Text( + value.currentUser != null + ? value.currentUser.gold + : "00", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ], + ), + Container( + height: 35, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: Theme.of(context).backgroundColor, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: Theme.of(context).colorScheme.secondary, + )), + child: InkWell( + onTap: () => showDialog( + context: context, + builder: (BuildContext context) => + GoldCoinWithdrawal(), + ), + child: Text( + "CASH OUT", + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + margin: EdgeInsets.symmetric(vertical: 15), + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width) * 0.3, + child: Center( + child: Image( + width: MediaQuery.of(context).size.width - + (MediaQuery.of(context).size.width) * 0.50, + image: AssetImage("assets/images/background.png"), + fit: BoxFit.fill, + ), + ), + ), + SizedBox(height: 20.0), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "Earn more gold coins and redeem them as real cash . \n" + + " Would you like to earn some more gold coins ?", + style: TextStyle( + color: Theme.of(context).primaryColorLight, + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/CoinsPurchase/GoldWithdrawal.dart b/lib/Pages/Sub_Pages/CoinsPurchase/GoldWithdrawal.dart new file mode 100644 index 0000000..7b3b5b0 --- /dev/null +++ b/lib/Pages/Sub_Pages/CoinsPurchase/GoldWithdrawal.dart @@ -0,0 +1,171 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/AmountInput.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/MomoType.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/phonenumber.dart'; +import 'package:teso/Pages/Sub_Pages/Payments/Withdrawal.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/SizeConfig.dart'; + +class GoldCoinWithdrawal extends StatefulWidget { + @override + _GoldCoinWithdrawalState createState() => _GoldCoinWithdrawalState(); +} + +class _GoldCoinWithdrawalState extends State { + bool loading = false; + TextEditingController amount; + TextEditingController momoNumber; + String tapped; + + @override + void initState() { + super.initState(); + amount = new TextEditingController(); + momoNumber = new TextEditingController(); + selectTransfer("MTN"); + } + + void selectTransfer(String type) { + setState(() { + tapped = type; + }); + print(tapped); + } + + submit() async { + await Navigator.push( + context, + PageTransition( + child: WithdrawalPage( + amount: amount.text, + momonumber: momoNumber.text, + provider: tapped, + ), + type: PageTransitionType.fade, + ), + ); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(Consts.padding), + ), + elevation: 0.0, + backgroundColor: Colors.transparent, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + child: Stack( + children: [ + Container( + padding: EdgeInsets.only( + top: Consts.padding, + bottom: Consts.padding, + left: Consts.padding, + right: Consts.padding, + ), + margin: EdgeInsets.only(top: Consts.avatarRadius), + decoration: new BoxDecoration( + color: Theme.of(context).primaryColor, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(Consts.padding), + ), + child: Column( + // mainAxisSize: MainAxisSize.min, // To make the card compact + children: [ + SizedBox(height: 14.0), + Container( + width: double.infinity, + child: Text( + "Amount", + style: TextStyle( + color: Theme.of(context).primaryColorLight, + fontSize: 17, + ), + ), + ), + SizedBox(height: 10.0), + buildAmount(context, amount), + SizedBox(height: 14.0), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + buildType(context, "assets/images/MTN.png", "MTN", + selectTransfer, tapped), + buildType(context, "assets/images/Vodafone.png", + "VODAFONE", selectTransfer, tapped), + buildType(context, "assets/images/AirtelTigo.png", + "AIRTEL", selectTransfer, tapped), + ], + ), + ), + SizedBox(height: 14.0), + Container( + width: double.infinity, + child: Text( + "Phone number", + style: TextStyle( + color: Theme.of(context).primaryColorLight, + fontSize: 17, + ), + ), + ), + SizedBox(height: 10.0), + inputTelNumber(context, momoNumber), + SizedBox(height: 14.0), + Align( + alignment: Alignment.bottomCenter, + child: Container( + height: 35, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: Theme.of(context).backgroundColor, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: Theme.of(context).colorScheme.secondary, + )), + child: InkWell( + onTap: submit, + child: Text( + "SUBMIT", + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + ), + ), + Visibility( + visible: loading, + child: Align( + alignment: Alignment.bottomCenter, + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} + +class Consts { + Consts._(); + + static const double padding = 16.0; + static const double avatarRadius = 55.0; +} diff --git a/lib/Pages/Sub_Pages/CoinsPurchase/PurchaseingSilver.dart b/lib/Pages/Sub_Pages/CoinsPurchase/PurchaseingSilver.dart new file mode 100644 index 0000000..19f3c8c --- /dev/null +++ b/lib/Pages/Sub_Pages/CoinsPurchase/PurchaseingSilver.dart @@ -0,0 +1,149 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API Clasess/UserFinance.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/SilverPurchaseRequest.dart'; +import 'package:teso/Classes/Payload.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Notifications/NotificationPlugin.dart'; +import 'package:teso/Pages/Sub_Pages/Payments/PaymentView.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; + +class ProcessSilverPurchase extends StatefulWidget { + final int silverAmount; + final String method; + final double cost; + final TesoUser user; + + const ProcessSilverPurchase({ + Key key, + @required this.user, + @required this.cost, + @required this.method, + @required this.silverAmount, + }) : super(key: key); + @override + _ProcessSilverPurchaseState createState() => _ProcessSilverPurchaseState(); +} + +class _ProcessSilverPurchaseState extends State { + String processing = "Processing silver coin purchase"; + Future purchaseCoin() async { + try { + if (widget.method != "goldcoins") { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PaymentView( + selectedUrl: + 'https://expresspaygh.com/payment_api_auto.php?token=99356106a8bbaa43c9.217546426106a8bbaa4417.9361359096566106a8bbaa&orderid=213', + ), + maintainState: true), + ); + } else { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + SilverPurchaseRequest request = new SilverPurchaseRequest(); + request.coinamount = widget.silverAmount; + request.amount = widget.cost; + request.method = widget.method == "goldcoins" ? "gold" : "realcash"; + + var register = serverLocation + 'coins/purchase_silver'; + var client = await http.post(Uri.parse(register), + body: json.encode(request), headers: requestHeaders); + Payload payload = new Payload(); + payload.loadID = "TESN003"; + payload.load1 = "SilverCoin Purchase"; + if (client.statusCode == 200) { + var posts = jsonDecode(client.body); + UserFinance finance = UserFinance.fromJSON(posts); + TesoUser user = widget.user; + + user.gold = finance.gold.toString(); + user.silver = finance.silver.toString(); + setState(() { + processing = + "${request.coinamount} silver coins successfully purchased !!!"; + }); + Provider.of(context, listen: false).setUser(user); + await notificationPlugin.showNotification( + "Funds purchased", + "You have successfully purchased ${request.coinamount} silver coins", + payload.toString(), + ); + } else if (client.statusCode == 300) { + await notificationPlugin.showNotification( + "Insufficient Funds", + "Unable to purchase silver coins due to insufficient funds", + payload.toString(), + ); + setState(() { + processing = "Insufficient funds to complete transaction"; + }); + } else { + setState(() { + processing = + "An error occurred while processing purchase, please try again later"; + }); + } + } + } catch (e) { + setState(() { + processing = + "An error occurred while processing purchase, please try again later"; + }); + } + Future.delayed(Duration(seconds: 5), () => Navigator.pop(context)); + } + + @override + void initState() { + super.initState(); + purchaseCoin(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Container( + width: 200, + height: 200, + child: Column( + children: [ + Container( + child: processing == "Processing silver coin purchase" + ? CupertinoActivityIndicator( + animating: true, + radius: 15, + ) + : processing.contains("successfully") + ? Icon( + Icons.check_circle, + color: Colors.green, + ) + : Icon( + Icons.error, + color: Colors.red, + ), + ), + Container( + child: Text(processing), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/CoinsPurchase/SilverPurchase.dart b/lib/Pages/Sub_Pages/CoinsPurchase/SilverPurchase.dart new file mode 100644 index 0000000..e2ef59f --- /dev/null +++ b/lib/Pages/Sub_Pages/CoinsPurchase/SilverPurchase.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/SilverAmountInput.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/MomoType.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/PayGold.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/phonenumber.dart'; + +class SilverPurchase extends StatefulWidget { + @override + _SilverPurchaseState createState() => _SilverPurchaseState(); +} + +class _SilverPurchaseState extends State { + TextEditingController amount; + TextEditingController momoNumber; + String tapped; + + @override + void initState() { + amount = new TextEditingController(); + momoNumber = new TextEditingController(); + super.initState(); + amount.addListener(() { + setState(() {}); + }); + } + + void selectTransfer(String type) { + setState(() { + tapped = type; + }); + } + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width) * 0.030, + vertical: (MediaQuery.of(context).size.width) * 0.040, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + child: new Wrap( + children: [ + new Container( + width: double.infinity, + margin: EdgeInsets.only( + top: 5.0, + bottom: 10.0, + ), + child: Center( + child: Text( + "Purchase Silver Coins", + style: TextStyle( + fontSize: 18.0, + ), + ), + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Container( + width: double.infinity, + margin: EdgeInsets.symmetric(vertical: 10), + child: new Wrap( + spacing: 10, + direction: Axis.horizontal, + children: [ + Container( + height: 70, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Amount", + style: TextStyle( + fontSize: 18, + ), + ), + ), + ), + buildAmount(context, amount), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Container( + width: double.infinity, + margin: EdgeInsets.symmetric(vertical: 5), + child: Text( + "Payment Method", + style: TextStyle( + fontSize: 18, + ), + ), + ), + ), + SizedBox(height: 14.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + payWithGold(context, "goldcoins", selectTransfer, tapped), + buildType(context, "assets/images/MTN.png", "mtn", + selectTransfer, tapped), + buildType(context, "assets/images/Vodafone.png", + "vodafone", selectTransfer, tapped), + buildType(context, "assets/images/AirtelTigo.png", + "airteltigo", selectTransfer, tapped), + ], + ), + ), + ), + SizedBox(height: 10.0), + Visibility( + visible: tapped != null && + tapped != "goldcoins" && + amount.text.isNotEmpty + ? true + : false, + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Text( + "Phone number", + style: TextStyle( + fontSize: 17, + ), + ), + ), + ), + Visibility( + visible: tapped != null && + tapped != "goldcoins" && + amount.text.isNotEmpty + ? true + : false, + child: SizedBox(height: 10.0)), + Visibility( + visible: tapped != null && + tapped != "goldcoins" && + amount.text.isNotEmpty + ? true + : false, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: inputTelNumber(context, momoNumber), + ), + ), + Visibility( + visible: + tapped != null && amount.text.isNotEmpty ? true : false, + child: new Container( + margin: EdgeInsets.symmetric( + vertical: 20.0, + ), + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Theme.of(context).colorScheme.secondary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + ), + onPressed: () => Navigator.pop(context), + child: Text( + "Confirm", + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/CoinsPurchase/SilverPurchaseFixed.dart b/lib/Pages/Sub_Pages/CoinsPurchase/SilverPurchaseFixed.dart new file mode 100644 index 0000000..5c6a1a5 --- /dev/null +++ b/lib/Pages/Sub_Pages/CoinsPurchase/SilverPurchaseFixed.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/MomoType.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/PayGold.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/consts.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import 'PurchaseingSilver.dart'; + +class SilverPurchaseFixed extends StatefulWidget { + final String amount; + final String goldCost; + final String cashCost; + final TesoUser user; + + const SilverPurchaseFixed( + {Key key, this.amount, this.goldCost, this.cashCost, this.user}) + : super(key: key); + @override + _SilverPurchaseFixedState createState() => _SilverPurchaseFixedState(); +} + +class _SilverPurchaseFixedState extends State { + TextEditingController momoNumber; + String tapped = "goldcoins"; + + @override + void initState() { + momoNumber = new TextEditingController(); + super.initState(); + } + + void selectTransfer(String type) { + setState(() { + tapped = type; + }); + } + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width) * 0.030, + vertical: (MediaQuery.of(context).size.width) * 0.040, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + child: new Wrap( + children: [ + new Container( + width: double.infinity, + margin: EdgeInsets.only( + top: 5.0, + bottom: 10.0, + ), + child: Center( + child: Text( + "Purchase Silver Coins", + style: TextStyle( + fontSize: 18.0, + ), + ), + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.symmetric(vertical: 5), + child: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 15.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: "Purchasing ", + ), + new TextSpan( + text: widget.amount, + style: new TextStyle(fontWeight: FontWeight.bold), + ), + new TextSpan( + text: " Silver coins", + ), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Container( + width: double.infinity, + margin: EdgeInsets.symmetric(vertical: 5), + child: Text( + "Payment Method", + style: TextStyle( + fontSize: 18, + ), + ), + ), + ), + SizedBox(height: 14.0), + Container( + color: Colors.transparent, + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + payWithGold(context, "goldcoins", selectTransfer, tapped), + buildType(context, "assets/images/MTN.png", "mtn", + selectTransfer, tapped), + buildType(context, "assets/images/Vodafone.png", + "vodafone", selectTransfer, tapped), + buildType(context, "assets/images/AirtelTigo.png", + "airteltigo", selectTransfer, tapped), + ], + ), + ), + ), + SizedBox(height: 10.0), + Visibility( + visible: tapped != null ? true : false, + child: new Container( + margin: EdgeInsets.symmetric( + vertical: 20.0, + ), + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Theme.of(context).colorScheme.secondary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + ), + onPressed: () async { + if (tapped == "goldcoins") { + Navigator.push( + context, + PageTransition( + child: ProcessSilverPurchase( + cost: double.parse(widget.goldCost), + method: tapped, + silverAmount: int.parse(widget.amount), + user: widget.user, + ), + type: PageTransitionType.leftToRight, + ), + ); + } else { + String _url = paymentServer + + "purchasesilver/user=${widget.user.userGUID}/amount=${widget.amount}/cointype=TESCNS01"; + await canLaunch(_url) + ? await launch( + _url, + enableJavaScript: true, + forceWebView: true, + ) + : throw 'Could not launch $_url'; + } + }, + child: Text( + "Confirm", + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/CoinsPurchase/SilverTransaction.dart b/lib/Pages/Sub_Pages/CoinsPurchase/SilverTransaction.dart new file mode 100644 index 0000000..63eb378 --- /dev/null +++ b/lib/Pages/Sub_Pages/CoinsPurchase/SilverTransaction.dart @@ -0,0 +1,241 @@ +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/SilverPurchase.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:teso/Pages/Sub_Pages/PersonalSub/Referral.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; + +class SilverTransaction extends StatefulWidget { + @override + _SilverTransactionState createState() => _SilverTransactionState(); +} + +class _SilverTransactionState extends State { + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, value, child) { + if (value.currentUser == null) { + return Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ); + } else { + return Scaffold( + resizeToAvoidBottomInset: true, + appBar: PreferredSize( + preferredSize: Size.fromHeight(65), + child: Container( + child: Material( + elevation: 5.0, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: Padding( + padding: const EdgeInsets.all(7.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( + direction: Axis.horizontal, + children: [ + Container( + child: Container( + height: 50.0, + width: 50.0, + decoration: new BoxDecoration( + shape: BoxShape.circle, + ), + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(50.0), + topRight: Radius.circular(50.0), + bottomLeft: Radius.circular(50), + bottomRight: Radius.circular(50), + ), + child: Image( + height: 50, + width: 50, + fit: BoxFit.fill, + image: AssetImage( + "assets/images/silver1.png"), + ), + ), + ), + ), + ), + Container( + // width: 40, + height: 40, + child: Center( + child: Text( + value.currentUser != null + ? value.currentUser.silver + : "00", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + // InkWell( + // onTap: () => showModalBottomSheet( + // context: context, + // isScrollControlled: true, + // enableDrag: true, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.vertical( + // top: Radius.circular(20.0)), + // ), + // builder: (BuildContext bc) { + // return SilverPurchase(); + // }, + // ), + // child: Container( + // height: 35, + // padding: EdgeInsets.all(10), + // margin: EdgeInsets.all(7), + // decoration: BoxDecoration( + // color: Theme.of(context).backgroundColor, + // borderRadius: + // BorderRadius.all(Radius.circular(20.0)), + // border: Border.all( + // color: Theme.of(context).accentColor, + // )), + // child: Text( + // "PURCHASE COINS", + // style: TextStyle( + // color: Theme.of(context).accentColor, + // ), + // ), + // ), + // ), + ], + ), + ], + ), + ), + ), + ), + ), + body: AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.only( + top: 10, + ), + //height: MediaQuery.of(context).size.height - 120, + child: Center( + child: Column( + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + buildSilverCard( + context: context, + goldEquivalent: "1", + cashEquivalent: "1", + funds: "100", + user: value.currentUser, + ), + buildSilverCard( + context: context, + goldEquivalent: "2", + cashEquivalent: "2", + funds: "240", + user: value.currentUser, + ), + buildSilverCard( + context: context, + goldEquivalent: "3", + cashEquivalent: "3", + funds: "380", + user: value.currentUser, + ), + buildSilverCard( + context: context, + goldEquivalent: "4", + cashEquivalent: "4", + funds: "520", + user: value.currentUser, + ), + buildSilverCard( + context: context, + goldEquivalent: "5", + cashEquivalent: "5", + funds: "660", + user: value.currentUser, + ) + ], + ), + ), + SizedBox(height: 14.0), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Earn more silver coins and use them for in-app purchases.", + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).primaryColorLight, + ), + ), + ), + ), + ), + Container( + height: 35, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: tesoBlue, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: tesoBlue, + )), + child: InkWell( + onTap: () => showDialog( + context: context, + builder: (BuildContext context) => Referrals(), + ), + child: Text( + "REFER AND EARN", + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); + } + }); + } +} diff --git a/lib/Pages/Sub_Pages/Coupons/Acquire.dart b/lib/Pages/Sub_Pages/Coupons/Acquire.dart new file mode 100644 index 0000000..818dc97 --- /dev/null +++ b/lib/Pages/Sub_Pages/Coupons/Acquire.dart @@ -0,0 +1,226 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/providers/device_provider.dart'; +import 'package:teso/util/consts.dart'; + +class AcquireCoupon extends StatefulWidget { + final CouponsHead head; + final double price; + + const AcquireCoupon({Key key, this.head, this.price}) : super(key: key); + @override + _AcquireCouponState createState() => _AcquireCouponState(head: this.head); +} + +class _AcquireCouponState extends State { + final CouponsHead head; + _AcquireCouponState({this.head}); + double _value; + int _n = 1; + int coinCost = 0; + + changeValue(val) { + setState(() { + _value = val; + }); + calcCost(); + } + + calcCost() { + double price = widget.price * (_value / 100); + + coinCost = CouponRateCalculator.getRate(price); + + coinCost = coinCost * _n; + } + + @override + void initState() { + _value = head.lower; + calcCost(); + super.initState(); + } + + void add() { + setState(() { + _n++; + }); + calcCost(); + } + + void minus() { + setState(() { + if (_n != 1) _n--; + }); + calcCost(); + } + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width) * 0.030, + vertical: (MediaQuery.of(context).size.width) * 0.040, + ), + child: Material( + child: new Wrap(children: [ + new Container( + width: double.infinity, + margin: EdgeInsets.only( + top: 20.0, + bottom: 12.0, + ), + child: Center( + child: Text( + "ACQUIRE COUPON", + style: TextStyle( + fontSize: 20.0, + ), + ), + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + title: new Container( + child: new Center( + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text("Quantity"), + new Container( + width: 30, + height: 30, + child: new FloatingActionButton( + onPressed: minus, + child: Text( + "-", + style: TextStyle( + fontSize: 25, + color: Colors.black, + ), + ), + backgroundColor: Colors.white, + ), + ), + new Text('$_n', style: new TextStyle(fontSize: 20.0)), + new Container( + width: 30, + height: 30, + child: new FloatingActionButton( + onPressed: add, + child: new Icon( + Icons.add, + color: Colors.black, + size: 20, + ), + backgroundColor: Colors.white, + ), + ), + ], + ), + ), + ), + ), + Container( + child: head.type.toLowerCase().contains("freebie") + ? Center( + child: Text("Coupon Type : FREEBIE"), + ) + : Row( + children: [ + Text(head.lower.toString() + " %"), + Slider( + value: _value, + min: head.lower, + max: head.upper, + divisions: 20, + activeColor: accentMain, + inactiveColor: darkAccent, + label: _value.toString() + "%", + onChanged: (double newValue) => changeValue(newValue), + ), + Text(head.upper.toString() + " %"), + ], + ), + ), + Center( + child: Wrap( + direction: Axis.horizontal, + children: [ + Container( + child: Container( + height: 40.0, + width: 40.0, + decoration: new BoxDecoration( + shape: BoxShape.circle, + ), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(50.0), + topRight: Radius.circular(50.0), + bottomLeft: Radius.circular(50), + bottomRight: Radius.circular(50), + ), + child: Image( + height: 40, + width: 40, + fit: BoxFit.fill, + image: AssetImage("assets/images/silver1.png"), + ), + ), + ), + ), + ), + Container( + width: 40, + height: 40, + child: Center( + child: Text( + coinCost.toString(), + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + new ListTile( + title: new ElevatedButton( + style: ElevatedButton.styleFrom( + primary: accentMain, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + ), + onPressed: coinCost == 0 + ? null + : () async { + head.lower = _value; + head.quantity = _n; + await Provider.of(context, listen: false) + .acceptCoupon(head, coinCost, context); + }, + child: Text("Confirm"), + ), + onTap: () => {}, + ), + ]), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Coupons/CouponLocation.dart b/lib/Pages/Sub_Pages/Coupons/CouponLocation.dart new file mode 100644 index 0000000..103d3cf --- /dev/null +++ b/lib/Pages/Sub_Pages/Coupons/CouponLocation.dart @@ -0,0 +1,338 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'dart:async'; +import 'package:flutter/services.dart' show PlatformException, rootBundle; +import 'package:flutter/cupertino.dart'; +import 'package:location/location.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:geolocator/geolocator.dart'; + +import 'package:teso/Classes/API%20Clasess/TesoBusinessDetail.dart'; +import 'package:teso/Classes/TesoShop.dart'; +import 'package:teso/Pages/Sub_Pages/BusinessDetails.dart'; +import 'package:flutter_polyline_points/flutter_polyline_points.dart'; +import 'package:teso/util/consts.dart'; + +class CouponLocator extends StatefulWidget { + final TesoBusinessDetail shop; + + const CouponLocator({Key key, this.shop}) : super(key: key); + @override + _CouponLocatorState createState() => _CouponLocatorState( + shop: new TesoShop( + categoryShop: this.shop.businessCategory, + dateEst: this.shop.dateOfEst, + email: this.shop.businessEmail, + handle: this.shop.handle, + latitude: double.parse(this.shop.businessLat), + logo: this.shop.businessLogo, + longitude: double.parse(this.shop.businessLng), + shopAddress: this.shop.businessAddress, + shopDescription: this.shop.businessDescription, + shopID: this.shop.businessId, + shopName: this.shop.businessName, + shopPhone: this.shop.businessContact, + shopTin: this.shop.businessTin, + )); +} + +class _CouponLocatorState extends State { + String mapstyle; + var _future; + static LatLng _initialPosition; + Map markers = {}; + GoogleMapController mapController; + static const double CAMERA_ZOOM = 14.4746; + static const double CAMERA_TILT = 80; + static const double CAMERA_BEARING = 30; + TesoShop shop; + _CouponLocatorState({this.shop}); + Map polylines = {}; + String selectedshop = ""; + Location location = Location(); + LocationData _location; + bool ios = false; + + Future _determinePosition() async { + try { + final LocationData _locationResult = await location.getLocation(); + setState(() { + _location = _locationResult; + _initialPosition = LatLng(_location.latitude, _location.longitude); + }); + await getLocations(); + return await navigateToShop(shop); + } on PlatformException catch (err) { + setState(() { + print(err.code); + }); + return false; + } + } + + getLocations() async { + MarkerId markerId = MarkerId(widget.shop.businessId); + Marker marker = Marker( + markerId: markerId, + position: LatLng(double.parse(widget.shop.businessLat), + double.parse(widget.shop.businessLng)), + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan), + infoWindow: InfoWindow( + title: widget.shop.businessName, + snippet: widget.shop.businessAddress, + ), + onTap: () => showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return buildShopDetails(bc, shop, navigateToShop); + }, + ), + ); + if (mounted) { + setState(() { + markers[markerId] = marker; + }); + } + } + + @override + void initState() { + super.initState(); + ios = Platform.isIOS; + SharedPreferences.getInstance().then((prefs) { + String currentTheme = prefs.getString("theme"); + if (currentTheme == "light") { + rootBundle.loadString('assets/styles/light.txt').then((string) { + mapstyle = string; + }); + } else { + rootBundle.loadString('assets/styles/dark.txt').then((string) { + mapstyle = string; + }); + } + }); + _future = _determinePosition(); + + location.onLocationChanged.listen((LocationData cLoc) { + _initialPosition = LatLng(cLoc.latitude, cLoc.longitude); + }); + } + + navigateToShop(TesoShop tesoShop) async { + MarkerId markerId = MarkerId(tesoShop.shopName + " Location"); + Marker marker = Marker( + markerId: markerId, + position: LatLng(tesoShop.latitude, tesoShop.longitude), + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), + infoWindow: InfoWindow( + title: tesoShop.shopName, + snippet: tesoShop.shopAddress, + ), + ); + + setState(() { + markers.clear(); + markers[markerId] = marker; + selectedshop = tesoShop.shopName; + }); + + Position user = Position( + latitude: _initialPosition.latitude, + longitude: _initialPosition.longitude, + accuracy: 100, + altitude: 100, + heading: 100, + speed: 100, + speedAccuracy: 100, + timestamp: DateTime.now()); + Position shopLoc = Position( + latitude: tesoShop.latitude, + longitude: tesoShop.longitude, + accuracy: 100, + altitude: 100, + heading: 100, + speed: 100, + speedAccuracy: 100, + timestamp: DateTime.now()); + + if (await createPolylines(user, shopLoc)) { + return true; + } else { + return false; + } + } + + createPolylines(Position start, Position destination) async { + try { + PolylinePoints polylinePoints; + List polylineCoordinates = []; + polylinePoints = PolylinePoints(); + PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( + mapsKey, + PointLatLng(start.latitude, start.longitude), + PointLatLng(destination.latitude, destination.longitude), + travelMode: TravelMode.driving, + ); + + if (result.points.isNotEmpty) { + result.points.forEach((PointLatLng point) { + polylineCoordinates.add(LatLng(point.latitude, point.longitude)); + }); + } + + PolylineId id = PolylineId('poly'); + + Polyline polyline = Polyline( + polylineId: id, + color: Theme.of(context).colorScheme.secondary, + points: polylineCoordinates, + width: 5, + ); + setState(() { + polylines[id] = polyline; + }); + return true; + } catch (e) { + print(e); + return false; + } + } + + @override + void dispose() { + mapController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + body: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Stack( + children: [ + ios + ? Container( + margin: EdgeInsets.only( + top: (MediaQuery.of(context).size.height) - + (MediaQuery.of(context).size.height * 0.935), + left: 10), + child: Material( + elevation: 5, + color: Color.fromRGBO(0, 0, 0, 0.4), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + topLeft: Radius.circular(25), + topRight: Radius.circular(25), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(25.0), + child: IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 20, + ), + color: Colors.white, + onPressed: () => Navigator.pop(context), + ), + ), + ), + ) + : Container(), + Container( + padding: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.7), + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Center( + child: Column( + children: [ + CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + Text("Routing to shop....."), + ], + ), + ), + ), + ], + ); + } else if (snapshot.data == false && + snapshot.connectionState == ConnectionState.done) { + Navigator.of(context).pop(); + return Container(); + } else { + return Stack( + children: [ + GoogleMap( + padding: EdgeInsets.only( + top: 70.0, + ), + zoomGesturesEnabled: true, + zoomControlsEnabled: false, + compassEnabled: true, + myLocationButtonEnabled: true, + myLocationEnabled: true, + markers: Set.of(markers.values), + initialCameraPosition: CameraPosition( + target: _initialPosition, + zoom: CAMERA_ZOOM, + tilt: CAMERA_TILT, + bearing: CAMERA_BEARING, + ), + onMapCreated: (GoogleMapController controller) { + controller.setMapStyle(mapstyle); + mapController = controller; + }, + onCameraMove: (position) { + setState(() { + _initialPosition = LatLng( + position.target.latitude, position.target.longitude); + }); + }, + polylines: Set.of(polylines.values), + ), + Container( + margin: EdgeInsets.only( + top: (MediaQuery.of(context).size.height) - + (MediaQuery.of(context).size.height * 0.935), + left: 10), + child: Material( + elevation: 5, + color: Color.fromRGBO(0, 0, 0, 0.4), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + topLeft: Radius.circular(25), + topRight: Radius.circular(25), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(25.0), + child: IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 20, + ), + color: Colors.white, + onPressed: () => Navigator.pop(context), + ), + ), + ), + ), + ], + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Coupons/LoadGiftRecipient.dart b/lib/Pages/Sub_Pages/Coupons/LoadGiftRecipient.dart new file mode 100644 index 0000000..2ac6693 --- /dev/null +++ b/lib/Pages/Sub_Pages/Coupons/LoadGiftRecipient.dart @@ -0,0 +1,88 @@ +import 'package:provider/provider.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/Friends/friendTile.dart'; +import 'package:teso/Pages/PageWidgets/Inbox/newMessageHeader.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Friends/header.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/providers/user_provider.dart'; + +class LoadGiftRecipient extends StatefulWidget { + @override + _LoadGiftRecipientState createState() => _LoadGiftRecipientState(); +} + +class _LoadGiftRecipientState extends State { + TextEditingController searchkey; + List recipientMain= []; + List recipient; + SharedPreferences prefs; + + void clearText() { + setState(() {}); + } + + @override + void initState() { + searchkey = new TextEditingController(); + super.initState(); + } + + updateList(String name, recipientMain) { + setState(() { + recipient = recipientMain + .where((element) => + element.username.toLowerCase().contains(name.toLowerCase())) + .toList(); + recipient.sort((a, b) { + return b.firstname.compareTo(a.firstname); + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(150), + child: Container( + // margin: EdgeInsets.only(top: 30), + child: Column(children: [ + buildNewHead(context, clearText, "Look up recipient"), + buildFriendsHeader(context, searchkey, updateList), + ]), + ), + ), + body: Consumer( + builder: (context, value, child) { + if (value.friends == null) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor), + ), + ); + } else { + if (recipientMain.length == 0) { + recipientMain = value.friends; + recipient = value.friends; + } + recipient.sort((a, b) { + return b.firstname.compareTo(a.firstname); + }); + return ListView.builder( + itemCount: recipient.length, + itemBuilder: (context, int index) { + return InkWell( + onTap: () => + Navigator.pop(context, recipient.elementAt(index)), + child: buildFriend(context, recipient.elementAt(index)), + ); + }, + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Coupons/MyCouponOptions.dart b/lib/Pages/Sub_Pages/Coupons/MyCouponOptions.dart new file mode 100644 index 0000000..c0a2676 --- /dev/null +++ b/lib/Pages/Sub_Pages/Coupons/MyCouponOptions.dart @@ -0,0 +1,408 @@ +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:provider/provider.dart'; +import 'package:teso/Pages/codeQR.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:convert'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/Pages/PageWidgets/Coupons/GiftFriend.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'LoadGiftRecipient.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:flutter/cupertino.dart'; + +class AcquiredOptions extends StatefulWidget { + final CouponDetails head; + + const AcquiredOptions({Key key, this.head}) : super(key: key); + @override + _AcquiredOptionsState createState() => _AcquiredOptionsState(head: this.head); +} + +class _AcquiredOptionsState extends State { + final CouponDetails head; + _AcquiredOptionsState({this.head}); + double _value; + String result = ""; + TextEditingController friendName; + TesoUser selectedFriend = new TesoUser(); + bool loading = false; + double _n; + int coinCost = 0; + int _time = 0; + bool freebie = false; + + changeValue(val) { + if (mounted) + setState(() { + _value = val; + }); + } + + calcCost() { + if (mounted) + setState(() { + _n = head.worth - (_value / 100) * head.worth; + _n.roundToDouble(); + }); + } + + // ignore: unused_element + Future _scanQRCode() async { + result = ""; + result = await Navigator.push( + context, + PageTransition( + type: PageTransitionType.downToUp, + child: QRCodeScanner(), + ), + ); + if (result != null && result.isNotEmpty) { + print("confirm purchase"); + } + } + + Future loadFriend(context) async { + selectedFriend = await showModalBottomSheet( + context: context, + // isScrollControlled: true, + // enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), + ), + builder: (BuildContext bc) { + return LoadGiftRecipient(); + }); + if (selectedFriend != null) { + setState(() { + selectedFriend = selectedFriend; + }); + friendName.text = + selectedFriend.firstname + " " + selectedFriend.lastname; + if (friendName.text.length > 15) + friendName.text = friendName.text.substring(0, 15) + "..."; + } else { + friendName.text = ""; + setState(() { + selectedFriend = new TesoUser(); + }); + } + } + + Future giftCoupon() async { + setState(() { + loading = true; + }); + if (head.worth > 0 && selectedFriend.userGUID != null) { + CouponsHead gift = new CouponsHead(); + gift.businessId = head.issuer.businessName; + gift.targetProduct = head.targetProduct.productID; + gift.couponId = head.couponId; + if (freebie) { + gift.lower = 100; + } else { + gift.lower = _value; + } + gift.state = head.countID; + gift.upper = 0; + gift.type = selectedFriend.userGUID; + gift.expiration = head.expiration; + + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + try { + var register2 = serverLocation + 'coupons/giftCoupon'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(gift), headers: requestHeaders); + if (client1.statusCode == 200) { + setState(() { + loading = false; + }); + Navigator.pop(context, true); + Provider.of(context, listen: false).getCoupons(); + } + } catch (e) { + setState(() { + loading = false; + }); + print(e); + } + } + } + + @override + void initState() { + _value = head.worth; + friendName = new TextEditingController(); + _n = head.worth; + if (widget.head.type.toLowerCase().contains("freebie")) { + setState(() { + freebie = true; + }); + } + + super.initState(); + } + + void add() { + setState(() { + _time++; + coinCost = _time * 2; + }); + } + + void minus() { + setState(() { + if (_time != 0) { + _time--; + coinCost = (_time / 2).round(); + } + }); + } + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width) * 0.030, + vertical: (MediaQuery.of(context).size.width) * 0.040, + ), + child: Material( + child: Stack( + children: [ + new Wrap( + children: [ + new Container( + width: double.infinity, + margin: EdgeInsets.only( + top: 15.0, + bottom: 12.0, + ), + child: Center( + child: Text( + "COUPON OPTIONS", + style: TextStyle( + fontSize: 18.0, + ), + ), + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + leading: new Text( + "Gift To : ", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + title: buildGiftRecipient( + context: context, + searchkey: friendName, + loadFriend: () => loadFriend(context), + friend: selectedFriend), + ), + if (!freebie) + Row( + children: [ + Text( + "Discount ", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + Text("0 %"), + Slider( + value: _value, + min: 0, + max: 100, + divisions: 20, + activeColor: accentMain, + inactiveColor: darkAccent, + label: _value.toString() + "%", + onChanged: (double newValue) { + setState(() { + _value = newValue; + }); + calcCost(); + }, + ), + Text("100 %"), + ], + ), + Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: accentMain, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + ), + onPressed: () async => await giftCoupon(), + child: Text("Gift"), + ), + ), + Center( + child: Wrap( + direction: Axis.horizontal, + children: [ + Container( + child: Text("Current Coupon Worth : "), + ), + Container( + child: Text( + _n.toString() + " % ", + style: TextStyle( + // fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ), + // Divider(), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // children: [ + // Text( + // "Additional Time (hours) ", + // style: TextStyle( + // fontWeight: FontWeight.bold, + // ), + // ), + // Container( + // width: 30, + // child: new FloatingActionButton( + // onPressed: minus, + // child: new Text( + // "-", + // style: TextStyle( + // fontSize: 30, + // color: Colors.black, + // ), + // ), + // backgroundColor: Colors.white, + // ), + // ), + // new Text( + // '$_time', + // style: TextStyle( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // ), + // ), + // Container( + // width: 30, + // child: new FloatingActionButton( + // onPressed: add, + // child: new Icon( + // Icons.add, + // color: Colors.black, + // size: 20, + // ), + // backgroundColor: Colors.white, + // ), + // ), + // ], + // ), + // Center( + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // children: [ + // Container( + // child: Text( + // "Cost", + // style: TextStyle( + // fontWeight: FontWeight.bold, + // ), + // ), + // ), + // Wrap( + // direction: Axis.horizontal, + // children: [ + // Container( + // child: Container( + // height: 30.0, + // width: 30.0, + // decoration: new BoxDecoration( + // shape: BoxShape.circle, + // ), + // child: ClipRRect( + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(50.0), + // topRight: Radius.circular(50.0), + // bottomLeft: Radius.circular(50), + // bottomRight: Radius.circular(50), + // ), + // child: Image( + // height: 30, + // width: 30, + // fit: BoxFit.fill, + // image: + // AssetImage("assets/images/silver1.png"), + // ), + // ), + // ), + // ), + // Container( + // width: 30, + // height: 30, + // child: Center( + // child: Text( + // "$_time", + // style: TextStyle( + // fontSize: 16.5, + // fontWeight: FontWeight.bold, + // ), + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), + // Center( + // child: ElevatedButton( + // style: ElevatedButton.styleFrom( + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(20.0), + // ), + // primary: accentMain, + // ), + // onPressed: () async => await giftCoupon(), + // child: Text("Extend Time"), + // ), + // ), + ], + ), + Visibility( + visible: loading, + child: Container( + color: Color.fromRGBO(0, 0, 0, 0.2), + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.width, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Coupons/ProximityCoupons.dart b/lib/Pages/Sub_Pages/Coupons/ProximityCoupons.dart new file mode 100644 index 0000000..d179f74 --- /dev/null +++ b/lib/Pages/Sub_Pages/Coupons/ProximityCoupons.dart @@ -0,0 +1,404 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart' show timeDilation; +import 'package:page_transition/page_transition.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/Pages/Sub_Pages/CoinPurchase.dart'; +import 'package:teso/providers/device_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:teso/Pages/PageWidgets/ProximityList/ActiveDiscount.dart'; +import 'package:teso/Pages/PageWidgets/ProximityList/ActiveFreebie.dart'; +import 'package:teso/Pages/PageWidgets/ProximityList/DummyDiscount.dart'; +import 'package:teso/Pages/PageWidgets/ProximityList/DummyFreebie.dart'; +import 'package:teso/providers/user_provider.dart'; + +class ProximityCoupons extends StatefulWidget { + @override + _ProximityCouponsState createState() => _ProximityCouponsState(); +} + +class _ProximityCouponsState extends State + with TickerProviderStateMixin { + AnimationController _buttonController; + Animation rotate; + Animation right; + Animation bottom; + Animation width; + int flag = 0; + List data = []; + List selectedData = []; + double selectedDiscount; + double price = 0; + + @override + void initState() { + super.initState(); + + _buttonController = new AnimationController( + duration: new Duration(milliseconds: 1000), vsync: this); + + rotate = new Tween( + begin: -0.0, + end: -40.0, + ).animate( + new CurvedAnimation( + parent: _buttonController, + curve: Curves.ease, + ), + ); + rotate.addListener(() { + setState(() { + if (rotate.isCompleted) { + var i = data.removeLast(); + data.insert(0, i); + + _buttonController.reset(); + } + }); + }); + + right = new Tween( + begin: 0.0, + end: 400.0, + ).animate( + new CurvedAnimation( + parent: _buttonController, + curve: Curves.ease, + ), + ); + bottom = new Tween( + begin: 15.0, + end: 100.0, + ).animate( + new CurvedAnimation( + parent: _buttonController, + curve: Curves.ease, + ), + ); + width = new Tween( + begin: 20.0, + end: 25.0, + ).animate( + new CurvedAnimation( + parent: _buttonController, + curve: Curves.bounceOut, + ), + ); + } + + @override + void dispose() { + _buttonController.dispose(); + super.dispose(); + } + + Future _swipeAnimation() async { + try { + await _buttonController.forward(); + } on TickerCanceled {} + } + + dismissImg(ProximityCoupon img) async { + CouponsHead couponsHead = new CouponsHead(); + couponsHead.businessId = img.business.businessId; + couponsHead.expiration = img.expiration; + couponsHead.couponId = img.couponId; + couponsHead.quantity = img.quantity; + couponsHead.state = "active"; + couponsHead.lower = double.parse(img.lowerLimit.toString()); + couponsHead.upper = double.parse(img.upperLimit.toString()); + + Provider.of(context, listen: false) + .declineCoupon(couponsHead); + + setState(() { + data.remove(img); + }); + } + + addImg(ProximityCoupon img) async { + CouponsHead couponsHead = new CouponsHead(); + couponsHead.businessId = img.business.businessId; + couponsHead.expiration = img.expiration; + couponsHead.couponId = img.couponId; + couponsHead.quantity = 1; + couponsHead.state = "active"; + couponsHead.lower = double.parse(img.lowerLimit.toString()); + couponsHead.upper = double.parse(img.upperLimit.toString()); + couponsHead.targetProduct = img.targetID; + double price = (img.targetCost * (img.lowerLimit / 100)); + + //Calculations + int cost = CouponRateCalculator.getRate(price); + Provider.of(context, listen: false) + .acceptCoupon(couponsHead, cost.ceil(), context); + + setState(() { + data.remove(img); + selectedData.add(img); + }); + } + + swipeRight(img) { + if (flag == 0) + setState(() { + flag = 1; + }); + _swipeAnimation(); + addImg(img); + } + + swipeLeft(img) { + if (flag == 1) + setState(() { + flag = 0; + }); + _swipeAnimation(); + dismissImg(img); + } + + void calculateWorth(worth) { + setState(() { + price = worth; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Proximity Coupons"), + actions: [ + Consumer(builder: + (BuildContext context, UserProvider value, Widget child) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + height: 30, + child: Row( + children: [ + InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.bottomToTop, + child: Coins(initalPage: 0), + ), + ), + child: Image( + image: AssetImage("assets/images/gold1.png"), + ), + ), + InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.bottomToTop, + child: Coins(initalPage: 0), + ), + ), + child: Container( + margin: EdgeInsets.only(left: 2), + child: Center( + child: Text( + value.currentUser.gold, + style: TextStyle(fontSize: 13.5), + ), + ), + ), + ), + InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.bottomToTop, + child: Coins(initalPage: 1), + ), + ), + child: Image( + image: AssetImage("assets/images/silver1.png"), + ), + ), + InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.bottomToTop, + child: Coins(initalPage: 1), + ), + ), + child: Container( + margin: EdgeInsets.only( + left: 2, + right: 2, + ), + child: Center( + child: Text( + value.currentUser.silver, + style: TextStyle(fontSize: 13.5), + ), + ), + ), + ), + ], + ), + ), + ], + ); + }), + ], + ), + body: Consumer( + builder: (context, value, child) { + if (!value.serviceEnabled) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + child: Text( + "Proximity Coupon Alerts Disabled", + textAlign: TextAlign.center, + style: TextStyle( + color: Color(0xFF003445), + fontFamily: 'WickedGrit', + fontSize: 30, + ), + ), + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green[200], + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => + Provider.of(context, listen: false) + .toggleBackgroundMode(context), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green[200], + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Enable", + style: new TextStyle( + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ); + } else if (value.proximityCoupons == null || + value.proximityCoupons.length == 0) { + return Center( + child: Text("No coupons available in your location"), + ); + } else { + data = value.proximityCoupons; + timeDilation = 0.4; + double initialBottom = 15.0; + var dataLength = data.length; + double backCardPosition = + initialBottom + (dataLength - 1) * 10 + 10; + double backCardWidth = -10.0; + + return Container( + alignment: Alignment.center, + child: dataLength > 0 + ? new Stack( + alignment: AlignmentDirectional.center, + children: data.map((item) { + if (data.indexOf(item) == dataLength - 1 && + item.type.toLowerCase().contains("discount")) { + selectedDiscount = item.lowerLimit; + return buildActiveDiscountCoupon( + item, + bottom.value, + right.value, + 0.0, + backCardWidth + 10, + rotate.value, + rotate.value < -10 ? 0.1 : 0.0, + context, + dismissImg, + flag, + addImg, + swipeRight, + swipeLeft, + price, + calculateWorth); + } else if (data.indexOf(item) == dataLength - 1 && + item.type.toLowerCase().contains("freebie")) { + return buildActiveFreebieCoupon( + item, + bottom.value, + right.value, + 0.0, + backCardWidth + 10, + rotate.value, + rotate.value < -10 ? 0.1 : 0.0, + context, + dismissImg, + flag, + addImg, + swipeRight, + swipeLeft, + ); + } else if (data.indexOf(item) < dataLength - 1 && + item.type.toLowerCase().contains("discount")) { + backCardPosition = backCardPosition - 10; + backCardWidth = backCardWidth + 10; + double price = item.targetCost - + (item.targetCost * item.lowerLimit / 100); + return buildDummyDiscountCoupon( + item, + backCardPosition, + 0.0, + 0.0, + backCardWidth, + 0.0, + 0.0, + context, + price.toString(), + selectedDiscount); + } else { + backCardPosition = backCardPosition - 10; + backCardWidth = backCardWidth + 10; + double price = item.targetCost - + (item.targetCost * item.lowerLimit / 100); + return buildDummyFreebieCoupon( + item, + backCardPosition, + 0.0, + 0.0, + backCardWidth, + 0.0, + 0.0, + context, + price.toString(), + selectedDiscount); + } + }).toList()) + : new Center( + child: Text("No coupons available in your location")), + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Desires Come True/AddDesire.dart b/lib/Pages/Sub_Pages/Desires Come True/AddDesire.dart new file mode 100644 index 0000000..7c6607d --- /dev/null +++ b/lib/Pages/Sub_Pages/Desires Come True/AddDesire.dart @@ -0,0 +1,236 @@ +import 'package:teso/Classes/API%20Clasess/Desire.dart'; +import 'package:flutter/material.dart'; + +import 'package:teso/Classes/customTesoButton.dart'; +import 'package:teso/util/consts.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:teso/Pages/PageWidgets/DesireComeTrue/DesireTile.dart'; +import 'NotListed.dart'; +import 'package:jiffy/jiffy.dart'; + +class NewDesire extends StatefulWidget { + final List selected; + + const NewDesire({Key key, this.selected}) : super(key: key); + @override + _NewDesireState createState() => _NewDesireState(); +} + +class _NewDesireState extends State { + List newdesire = []; + List newdesireMain = []; + var search = new TextEditingController(); + SharedPreferences prefs; + List selectList = []; + + @override + void initState() { + super.initState(); + search.addListener(() async { + if (search.text.isNotEmpty) { + lookItem(); + } else { + setState(() { + newdesire.clear(); + }); + } + }); + selectList = widget.selected.map((e) => e.productID).toList(); + } + + lookItem() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = search.text.toString(); + var register = serverLocation + 'products/desire-product'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + var items = jsonDecode(client.body); + + newdesireMain = List.from( + items.map((model) => Desire.fromJSON(model)).toList()); + updateList(); + } else { + if (mounted) { + setState(() { + newdesireMain.clear(); + }); + } + } + } + + updateList() { + if (mounted) { + setState(() { + newdesireMain + .removeWhere((element) => selectList.contains(element.productID)); + newdesire = newdesireMain; + newdesire.sort((a, b) { + return b.productName.compareTo(a.productName); + }); + }); + } + } + + addItem(Desire item) { + if (mounted) + setState(() { + widget.selected.add(item); + selectList.add(item.productID); + newdesire.remove(item); + }); + } + + @override + void dispose() { + search.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10), + margin: EdgeInsets.only(top: 30), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Material( + color: Colors.black54, + elevation: 0, + borderRadius: BorderRadius.circular(30.0), + child: Row( + children: [ + new Expanded( + child: TextField( + autofocus: false, + textAlign: TextAlign.start, + controller: search, + style: TextStyle( + color: Colors.white, + ), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 14.0), + hintText: "Search", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + Container( + child: InkWell( + onTap: () => search.clear(), + child: Icon( + Icons.close_outlined, + color: Colors.white, + ), + ), + margin: EdgeInsets.symmetric(horizontal: 20), + ), + ], + ), + ), + ), + SizedBox( + width: 15, + ), + Container( + child: RaisedGradientButton( + child: Text( + "Done", + style: TextStyle(color: Colors.white, fontSize: 15), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + tesoGold, + tesoGold, + ], + ), + onPressed: () { + SharedPreferences.getInstance().then((value) { + var jiffy = Jiffy()..add(months: 1); + value.setString("desire" + jiffy.format("MMMM, yyyy"), + widget.selected.toString()); + }); + Navigator.pop(context, true); + }, + width: 70, + height: 50, + ), + ), + ], + ), + ), + preferredSize: Size.fromHeight(50.0)), + body: newdesire.length == 0 && search.text.length != 0 + ? InkWell( + onTap: () async { + Desire unlistedDesire = await showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + content: NotListed( + productName: search.text, + ), + ); + }); + if (unlistedDesire != null) widget.selected.add(unlistedDesire); + search.clear(); + }, + child: Container( + padding: EdgeInsets.all(20), + width: MediaQuery.of(context).size.width, + child: Center( + child: Container( + child: Wrap( + //direction: Axis.vertical, + children: [ + Container( + child: Text( + "Sorry, the product you entered has not been posted by any business. ", + textAlign: TextAlign.center, + style: TextStyle(fontSize: 16), + ), + ), + Text( + "Click here to have the product enlisted and sent to the appropriate businesses.", + textAlign: TextAlign.center, + style: TextStyle(color: tesoBlue, fontSize: 16.5), + ), + ], + ), + ), + ), + ), + ) + : ListView.builder( + itemCount: newdesire.length, + itemBuilder: (context, int index) { + if (newdesire.length == 0 && search.text.length == 0) { + return Container(); + } else { + if (!widget.selected.contains(newdesire.elementAt(index))) { + return buildProductDesire( + context, newdesire.elementAt(index), addItem); + } else { + return Container(); + } + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Desires Come True/NotListed.dart b/lib/Pages/Sub_Pages/Desires Come True/NotListed.dart new file mode 100644 index 0000000..7a24673 --- /dev/null +++ b/lib/Pages/Sub_Pages/Desires Come True/NotListed.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/categories.dart'; +import 'package:teso/Classes/customTesoButton.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/Desire.dart'; + +class NotListed extends StatefulWidget { + final String productName; + + const NotListed({Key key, this.productName}) : super(key: key); + @override + _NotListedState createState() => _NotListedState(); +} + +class _NotListedState extends State { + List categories = Category.category; + Category selectedCategory = new Category(); + + @override + void initState() { + super.initState(); + selectedCategory = categories.elementAt(0); + } + + @override + Widget build(BuildContext context) { + return Container( + height: MediaQuery.of(context).size.width * 0.33, + width: MediaQuery.of(context).size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + child: Text( + widget.productName, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: DropdownButton( + hint: Center( + child: Text( + "Product Category", + ), + ), + value: selectedCategory, + items: categories + .map( + (category) => DropdownMenuItem( + value: category, + child: Text( + category.name, + style: TextStyle(color: Colors.grey, fontSize: 17), + ), + ), + ) + .toList(), + onChanged: (value) { + setState(() { + selectedCategory = value; + }); + }), + ), + Container( + child: RaisedGradientButton( + child: Text( + "Add to List", + style: TextStyle(color: Colors.white, fontSize: 15), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + tesoGold, + tesoGold, + ], + ), + onPressed: () { + Desire item = new Desire(); + item.category = selectedCategory.id; + item.enlisted = "false"; + item.price = 0.0; + item.productID = widget.productName; + item.productImage = ""; + item.productName = widget.productName; + Navigator.pop(context, item); + }, + width: 120, + height: 50, + ), + ), + ], + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/Categories/AllCategories.dart b/lib/Pages/Sub_Pages/Explore/Categories/AllCategories.dart new file mode 100644 index 0000000..8c40a22 --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/Categories/AllCategories.dart @@ -0,0 +1,45 @@ +import 'package:teso/Classes/categories.dart'; +import 'package:teso/Pages/PageWidgets/Explore/categoriesTile.dart'; +import 'package:teso/Pages/Sub_Pages/Explore/Categories/ExploreCategory.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:page_transition/page_transition.dart'; + +class CategoriesAll extends StatefulWidget { + @override + _CategoriesAllState createState() => _CategoriesAllState(); +} + +class _CategoriesAllState extends State { + List productCategories = Category.category; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: true, + title: Text("Categories"), + ), + body: StaggeredGridView.count( + crossAxisCount: 2, + children: List.generate(productCategories.length, (int index) { + return InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: ExploreCategory( + selectedCategory: productCategories.elementAt(index), + ), + type: PageTransitionType.leftToRight)), + child: buildCategory(context, productCategories.elementAt(index)), + ); + }), + staggeredTiles: List.generate( + productCategories.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/Categories/ExploreCategory.dart b/lib/Pages/Sub_Pages/Explore/Categories/ExploreCategory.dart new file mode 100644 index 0000000..80d2ffb --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/Categories/ExploreCategory.dart @@ -0,0 +1,139 @@ +import 'dart:convert'; +import 'package:teso/Classes/categories.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Pages/PageWidgets/Explore/products.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter/cupertino.dart'; + +class ExploreCategory extends StatefulWidget { + final Category selectedCategory; + + const ExploreCategory({Key key, this.selectedCategory}) : super(key: key); + @override + _ExploreCategoryState createState() => _ExploreCategoryState(); +} + +class _ExploreCategoryState extends State { + ScrollController _controller; + List products; + List show = []; + int count = 0; + var _future; + + Future fetchImages() async { + try { + if (products != null && show.length <= products.length) { + count = show.length; + for (int i = 0; i <= 9; i++) { + if (products.length > count) + setState(() { + show.add(products.elementAt(count)); + count++; + imageCache.clear(); + }); + } + } else { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'search/category-products'; + var client = await http.post(Uri.parse(register), + body: json.encode(widget.selectedCategory.id), + headers: requestHeaders); + if (client.statusCode == 200) { + var productItem = jsonDecode(client.body); + this.products = List.from( + productItem.map((model) => Product.fromJson(model)).toList()); + + count = show.length; + for (int i = 0; i <= 9; i++) { + if (products.length > count) + setState(() { + show.add(products.elementAt(count)); + count++; + imageCache.clear(); + }); + } + } + } + } catch (e) { + print(e); + } + } + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + fetchImages(); + } + } + + @override + void initState() { + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + super.initState(); + _future = fetchImages(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: true, + title: Text(widget.selectedCategory.name), + actions: [ + Image( + image: AssetImage(widget.selectedCategory.image), + ), + ], + ), + body: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (products == null && + snapshot.connectionState == ConnectionState.waiting) { + return Container( + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } else if (products == null && + snapshot.connectionState == ConnectionState.done) { + return Container( + child: Center( + child: Text("No products under this category posted!"), + ), + ); + } else { + return StaggeredGridView.count( + controller: _controller, + crossAxisCount: 2, + children: List.generate(show.length, (int index) { + return index % 2 == 0 + ? buildProducts(context, show.elementAt(index)) + : buildProducts(context, show.elementAt(index)); + }), + staggeredTiles: List.generate( + show.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ); + } + }), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/ExploreBusiness.dart b/lib/Pages/Sub_Pages/Explore/ExploreBusiness.dart new file mode 100644 index 0000000..ed62574 --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/ExploreBusiness.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Classes/TesoShop.dart'; + +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:teso/Pages/PageWidgets/Explore/business.dart'; + +class ExploreBusiness extends StatefulWidget { + final List businesses; + + const ExploreBusiness({Key key, this.businesses}) : super(key: key); + @override + _ExploreBusinessState createState() => _ExploreBusinessState(); +} + +class _ExploreBusinessState extends State { + ScrollController _controller; + List show = []; + int count = 0; + + Future fetchImages() async { + try { + count = show.length; + for (int i = 0; i <= 9; i++) { + if (widget.businesses.length > count) + setState(() { + show.add(widget.businesses.elementAt(count)); + count++; + imageCache.clear(); + }); + } + } catch (e) { + print(e); + } + } + + @override + void didUpdateWidget(covariant ExploreBusiness oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.businesses != widget.businesses || + widget.businesses.length == 0) { + show = []; + fetchImages(); + } + } + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + fetchImages(); + } + } + + @override + void initState() { + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + fetchImages(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + child: StaggeredGridView.count( + controller: _controller, + crossAxisCount: 2, + children: List.generate(show.length, (int index) { + return buildBusiness(context, show.elementAt(index)); + }), + staggeredTiles: List.generate( + show.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/ExplorePeople.dart b/lib/Pages/Sub_Pages/Explore/ExplorePeople.dart new file mode 100644 index 0000000..3f7b950 --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/ExplorePeople.dart @@ -0,0 +1,46 @@ +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/Friends/friendTile.dart'; +import 'package:teso/Pages/Sub_Pages/userProfile3P.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:flutter/material.dart'; + +class ExplorePeople extends StatefulWidget { + final List people; + + const ExplorePeople({Key key, this.people}) : super(key: key); + @override + _ExplorePeopleState createState() => _ExplorePeopleState(); +} + +class _ExplorePeopleState extends State { + @override + Widget build(BuildContext context) { + return Container( + child: ListView.builder( + itemCount: widget.people.length, + itemBuilder: (context, int index) { + return InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: UserProfileThirdPerson( + user: widget.people.elementAt(index), + ), + type: PageTransitionType.fade, + ), + ), + child: Column( + children: [ + buildFriend(context, widget.people.elementAt(index)), + Padding( + padding: const EdgeInsets.all(8.0), + child: Divider(), + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/ExploreProduct.dart b/lib/Pages/Sub_Pages/Explore/ExploreProduct.dart new file mode 100644 index 0000000..bec06eb --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/ExploreProduct.dart @@ -0,0 +1,80 @@ +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:teso/Pages/PageWidgets/Explore/products.dart'; + +class ExploreProduct extends StatefulWidget { + final List products; + + const ExploreProduct({Key key, this.products}) : super(key: key); + @override + _ExploreProductState createState() => _ExploreProductState(); +} + +class _ExploreProductState extends State { + ScrollController _controller; + List show = []; + int count = 0; + + Future fetchImages() async { + try { + count = show.length; + for (int i = 0; i <= 9; i++) { + if (widget.products.length > count) + setState(() { + show.add(widget.products.elementAt(count)); + count++; + imageCache.clear(); + }); + } + } catch (e) { + print(e); + } + } + + @override + void didUpdateWidget(covariant ExploreProduct oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.products != widget.products || widget.products.length == 0) { + show = []; + fetchImages(); + } + } + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + fetchImages(); + } + } + + @override + void initState() { + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + fetchImages(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + child: StaggeredGridView.count( + controller: _controller, + crossAxisCount: 2, + children: List.generate(show.length, (int index) { + return index % 2 == 0 + ? buildProducts(context, show.elementAt(index)) + : buildProducts(context, show.elementAt(index)); + }), + staggeredTiles: List.generate( + show.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/Latest/AllLatest.dart b/lib/Pages/Sub_Pages/Explore/Latest/AllLatest.dart new file mode 100644 index 0000000..c237ced --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/Latest/AllLatest.dart @@ -0,0 +1,75 @@ +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:teso/Pages/PageWidgets/Explore/products.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; + +class NewArrivals extends StatefulWidget { + final List products; + + const NewArrivals({Key key, this.products}) : super(key: key); + @override + _NewArrivalsState createState() => _NewArrivalsState(); +} + +class _NewArrivalsState extends State { + ScrollController _controller; + List show = []; + int count = 0; + + Future fetchImages() async { + try { + count = show.length; + for (int i = 0; i <= 9; i++) { + if (widget.products.length > count) + setState(() { + show.add(widget.products.elementAt(count)); + count++; + imageCache.clear(); + }); + } + } catch (e) { + print(e); + } + } + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + fetchImages(); + } + } + + @override + void initState() { + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + fetchImages(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: true, + title: Text("New Arrivals"), + ), + body: StaggeredGridView.count( + controller: _controller, + crossAxisCount: 2, + children: List.generate(show.length, (int index) { + return index % 2 == 0 + ? buildProducts(context, show.elementAt(index)) + : buildProducts(context, show.elementAt(index)); + }), + staggeredTiles: List.generate( + show.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/ML/FindProduct.dart b/lib/Pages/Sub_Pages/Explore/ML/FindProduct.dart new file mode 100644 index 0000000..72190cb --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/ML/FindProduct.dart @@ -0,0 +1,220 @@ +import 'dart:io'; + +import 'package:google_ml_vision/google_ml_vision.dart'; +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:teso/Pages/PageWidgets/Explore/products.dart'; +import 'package:teso/Pages/Sub_Pages/Explore/search.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/util/consts.dart'; +import 'dart:convert'; + +class MLFindProduct extends StatefulWidget { + final File searchImage; + + const MLFindProduct({Key key, this.searchImage}) : super(key: key); + @override + _MLFindProductState createState() => _MLFindProductState(); +} + +class _MLFindProductState extends State { + List products = []; + List show = []; + var searchkey = new TextEditingController(); + var _future; + ScrollController _controller; + int count = 0; + List searchList = []; + SharedPreferences prefs; + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + fetchImages(); + } + } + + Future fetchImages() async { + try { + final GoogleVisionImage visionImage = + GoogleVisionImage.fromFile(widget.searchImage); + + final ImageLabeler labeler = GoogleVision.instance.imageLabeler(); + final List labels = await labeler.processImage(visionImage); + final TextRecognizer textRecognizer = + GoogleVision.instance.textRecognizer(); + VisionText visionText = await textRecognizer.processImage(visionImage); + textRecognizer.close(); + + for (ImageLabel label in labels) { + final String name = label.text; + if (!searchList.contains(name) && name.length > 2) searchList.add(name); + } + + for (TextBlock blocks in visionText.blocks) { + if (!searchList.contains(blocks.text) && blocks.text.length > 2) + searchList.add(blocks.text); + } + + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'search/artificial-intel-products'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchList), headers: requestHeaders); + if (client.statusCode == 200) { + var productItem = jsonDecode(client.body); + this.products = List.from( + productItem.map((model) => Product.fromJson(model)).toList()); + } + + count = show.length; + for (int i = 0; i <= 9; i++) { + if (products != null && products.length > count) + setState(() { + show.add(products.elementAt(count)); + count++; + imageCache.clear(); + }); + } + } catch (e) { + print(e); + } + return 1; + } + + @override + void initState() { + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + _future = fetchImages(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: Container( + padding: EdgeInsets.only(top: 25.0), + child: Container( + height: 60.0, + //margin: EdgeInsets.all(10.0), + padding: EdgeInsets.all(10.0), + child: Material( + color: Colors.grey[300], + elevation: 4.0, + borderRadius: BorderRadius.circular(12.0), + shadowColor: Theme.of(context).backgroundColor, + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + new Expanded( + child: InkWell( + onTap: () => Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) => Lookup(), + ), + ), + child: TextField( + autofocus: false, + enabled: false, + textAlign: TextAlign.start, + controller: searchkey, + style: TextStyle( + color: Colors.white, + ), + decoration: InputDecoration( + border: InputBorder.none, + //contentPadding: EdgeInsets.only(top: 14.0), + prefixIcon: Icon( + Icons.search, + color: Colors.white, + ), + hintText: "Search", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + ), + ], + ), + ), + ), + ), + preferredSize: Size.fromHeight(150.0), + ), + body: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.data == null && + snapshot.connectionState == ConnectionState.waiting) { + return Container( + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } else if (products.length <= 0 && + snapshot.connectionState == ConnectionState.done) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + padding: EdgeInsets.all( + 10, + ), + child: Center( + child: Column( + children: [ + Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.width * 0.3), + child: Image( + image: AssetImage("assets/images/emptyBox.png"), + ), + ), + Text( + "Sorry, there are no products listed matching the image provided", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.grey), + ), + ], + ), + ), + ); + } else { + return StaggeredGridView.count( + controller: _controller, + crossAxisCount: 2, + children: List.generate(show.length, (int index) { + return index % 2 == 0 + ? buildProducts(context, show.elementAt(index)) + : buildProducts(context, show.elementAt(index)); + }), + staggeredTiles: List.generate( + show.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/Trending/AllTrending.dart b/lib/Pages/Sub_Pages/Explore/Trending/AllTrending.dart new file mode 100644 index 0000000..2307cd1 --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/Trending/AllTrending.dart @@ -0,0 +1,75 @@ +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:teso/Pages/PageWidgets/Explore/products.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; + +class TrendingAll extends StatefulWidget { + final List products; + + const TrendingAll({Key key, this.products}) : super(key: key); + @override + _TrendingAllState createState() => _TrendingAllState(); +} + +class _TrendingAllState extends State { + ScrollController _controller; + List show = []; + int count = 0; + + Future fetchImages() async { + try { + count = show.length; + for (int i = 0; i <= 9; i++) { + if (widget.products.length > count) + setState(() { + show.add(widget.products.elementAt(count)); + count++; + imageCache.clear(); + }); + } + } catch (e) { + print(e); + } + } + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + fetchImages(); + } + } + + @override + void initState() { + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + fetchImages(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: true, + title: Text("Trending Products"), + ), + body: StaggeredGridView.count( + controller: _controller, + crossAxisCount: 2, + children: List.generate(show.length, (int index) { + return index % 2 == 0 + ? buildProducts(context, show.elementAt(index)) + : buildProducts(context, show.elementAt(index)); + }), + staggeredTiles: List.generate( + show.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Explore/search.dart b/lib/Pages/Sub_Pages/Explore/search.dart new file mode 100644 index 0000000..2c215d8 --- /dev/null +++ b/lib/Pages/Sub_Pages/Explore/search.dart @@ -0,0 +1,290 @@ +import 'package:teso/Pages/Sub_Pages/Explore/ExploreBusiness.dart'; +import 'package:teso/Pages/Sub_Pages/Explore/ExplorePeople.dart'; +import 'package:teso/Pages/Sub_Pages/Explore/ExploreProduct.dart'; +import 'package:flutter/material.dart'; + +import 'package:teso/Classes/TesoUser.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/util/consts.dart'; +import 'dart:convert'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:teso/Classes/TesoShop.dart'; + +class Lookup extends StatefulWidget { + @override + _LookupState createState() => _LookupState(); +} + +class _LookupState extends State with TickerProviderStateMixin { + List people = []; + List peopleAlert = []; + List product = []; + List productAlert = []; + List businessAlert = []; + List business = []; + TabController tabController; + var search = new TextEditingController(); + SharedPreferences prefs; + var future; + + @override + void initState() { + super.initState(); + tabController = new TabController(length: 3, vsync: this); + tabController.addListener(() async { + if (search.text.isNotEmpty) { + switch (tabController.index) { + case 0: + await lookupUser(); + break; + case 1: + await lookupProducts(); + break; + case 2: + await lookupBusiness(); + break; + default: + await lookupUser(); + break; + } + } + }); + + search.addListener(() async { + if (search.text.isNotEmpty) { + switch (tabController.index) { + case 0: + await lookupUser(); + break; + case 1: + await lookupProducts(); + break; + case 2: + await lookupBusiness(); + break; + default: + await lookupUser(); + break; + } + } else { + setState(() { + people.clear(); + product.clear(); + business.clear(); + }); + } + }); + } + + lookupUser() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = search.text; + var register = serverLocation + 'search/people'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + var people = jsonDecode(client.body); + this.people = List.from( + people.map((model) => TesoUser.fromJSON(model)).toList()); + + updateList(); + } else { + if (mounted) { + setState(() { + people.clear(); + }); + } + } + } + + lookupProducts() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = search.text; + var register = serverLocation + 'search/products'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + var productItem = jsonDecode(client.body); + this.product = List.from( + productItem.map((model) => Product.fromJson(model)).toList()); + + orderProductsList(); + } else { + if (mounted) { + setState(() { + product.clear(); + }); + } + } + } + + lookupBusiness() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = search.text; + var register = serverLocation + 'search/business'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + var businessItem = jsonDecode(client.body); + this.business = List.from( + businessItem.map((model) => TesoShop.fromJSON(model)).toList()); + + orderBusinessList(); + } else { + if (mounted) { + setState(() { + product.clear(); + }); + } + } + } + + updateList() { + if (mounted) { + setState(() { + peopleAlert = people; + }); + } + } + + orderProductsList() { + if (mounted) { + setState(() { + productAlert = product; + }); + } + } + + orderBusinessList() { + if (mounted) { + setState(() { + businessAlert = business; + }); + } + } + + @override + void dispose() { + search.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10), + margin: EdgeInsets.only(top: 30), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Material( + color: Colors.black54, + elevation: 0, + borderRadius: BorderRadius.circular(30.0), + child: Row( + children: [ + new Expanded( + child: TextField( + autofocus: false, + textAlign: TextAlign.start, + controller: search, + style: TextStyle( + color: Colors.white, + ), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 14.0), + hintText: "Search", + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + Container( + child: InkWell( + onTap: () => search.clear(), + child: Icon( + Icons.close_outlined, + color: Colors.white, + ), + ), + margin: EdgeInsets.symmetric(horizontal: 20), + ), + ], + ), + ), + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 20), + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: Text( + "Cancel", + style: TextStyle(fontSize: 15), + ), + ), + ), + ) + ], + ), + ), + preferredSize: Size.fromHeight(50.0)), + body: Column( + children: [ + TabBar( + controller: tabController, + tabs: [ + Tab( + text: "People", + ), + Tab( + text: "Products", + ), + Tab( + text: "Business", + ), + ], + ), + Expanded( + child: TabBarView( + controller: tabController, + children: [ + ExplorePeople( + people: peopleAlert, + ), + ExploreProduct( + products: productAlert, + ), + ExploreBusiness( + businesses: businessAlert, + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/FinalProcess.dart b/lib/Pages/Sub_Pages/LandingPage/FinalProcess.dart new file mode 100644 index 0000000..39860ca --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/FinalProcess.dart @@ -0,0 +1,280 @@ +import 'package:country_list_pick/country_list_pick.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/createPassword.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/GeneralWidgets/generalInput.dart'; +import 'package:http/http.dart' as http; + +class FinalProcess extends StatefulWidget { + final TesoUser newuser; + const FinalProcess({Key key, this.newuser}) : super(key: key); + @override + _FinalProcessState createState() => _FinalProcessState(newuser: this.newuser); +} + +class _FinalProcessState extends State { + TesoUser newuser; + _FinalProcessState({this.newuser}); + double phoneborder = 1.0; + bool phone = true; + bool email = false; + double mailborder = 0.5; + String countryPrefix = ""; + TextEditingController firstname = new TextEditingController(); + TextEditingController surname = new TextEditingController(); + TextEditingController address = new TextEditingController(); + TextEditingController emailaddress = new TextEditingController(); + bool loading = false; + + void _countryPrefix(CountryCode code) { + setState(() { + countryPrefix = code.dialCode; + newuser.country = code.dialCode; + }); + } + + @override + void initState() { + super.initState(); + emailaddress.addListener(() { + setState(() { + email = false; + }); + }); + } + + @override + void dispose() { + super.dispose(); + } + + void startProcessing() async { + try { + setState(() { + loading = true; + }); + + var client = await http.get(Uri.parse( + serverLocation + "api/email/" + emailaddress.text.toString().trim())); + print(client.body); + if (client.statusCode == 200) { + newuser.firstname = firstname.text.trim(); + newuser.lastname = surname.text.trim(); + newuser.address = address.text.trim(); + newuser.country = countryPrefix; + newuser.email = emailaddress.text.toString().trim(); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: CreatePassword( + newuser: newuser, + ), + ), + ); + setState(() { + loading = false; + }); + } else { + setState(() { + email = true; + loading = false; + }); + } + } catch (e) { + print(e); + setState(() { + email = true; + loading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).primaryColor, + appBar: AppBar( + backgroundColor: Theme.of(context).primaryColorDark, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: Container( + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + width: double.infinity, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.02, + ), + child: Center( + child: Text( + "Personnal Information", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.grey, fontSize: 18), + ), + ), + ), + Container( + child: new Wrap( + direction: Axis.horizontal, + children: [ + fineText(context, firstname, "Firstname"), + fineText(context, surname, "Lastname"), + ], + ), + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.02), + Container( + child: new Wrap( + direction: Axis.horizontal, + children: [ + fineText(context, address, "Residential Address"), + fineText(context, emailaddress, "Email"), + ], + ), + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.02), + Container( + width: double.infinity, + margin: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.03), + padding: EdgeInsets.only(left: 5, right: 5), + decoration: BoxDecoration( + border: Border( + right: BorderSide( + color: Colors.grey, + width: 1, + ), + left: BorderSide( + color: Colors.grey, + width: 1, + ), + top: BorderSide( + color: Colors.grey, + width: 1, + ), + bottom: BorderSide( + color: Colors.grey, + width: 1, + ), + ), + ), + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Country", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.grey, fontSize: 15), + ), + ), + Container( + child: CountryListPick( + theme: CountryTheme( + initialSelection: "Ghana", + isShowFlag: true, + isShowCode: false, + isShowTitle: true), + + //initialSelection: user.currentUser.country, + onChanged: (CountryCode code) => + _countryPrefix(code)), + ), + ], + ), + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.001), + Visibility( + visible: loading, + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.025), + ), + Visibility( + visible: loading, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + Visibility( + visible: email, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.9, + child: Center( + child: Text( + "Email already exists, if you forgot your password please try to reset your password from the login page!!!", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.red, + ), + ), + ), + ), + ), + Visibility( + visible: !loading, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.6, + height: 40.0, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: startProcessing, + child: Center( + child: Text( + "NEXT", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + )), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/Login.dart b/lib/Pages/Sub_Pages/LandingPage/Login.dart new file mode 100644 index 0000000..0955613 --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/Login.dart @@ -0,0 +1,773 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'dart:ui'; +import 'package:crypto/crypto.dart'; +import 'package:provider/provider.dart'; +import 'package:sign_in_with_apple/sign_in_with_apple.dart'; +import 'package:teso/Classes/API%20Clasess/FacebookUser.dart'; +import 'package:teso/Classes/API%20Clasess/GoogleUser.dart'; +import 'package:teso/Classes/API%20Clasess/TokenHandler.dart'; +import 'package:teso/Classes/API%20Clasess/UserAuth.dart'; +import 'package:camera/camera.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:teso/Classes/customLoginButton.dart'; +import 'package:teso/Classes/customTesoButton.dart'; +import 'package:teso/Pages/PageWidgets/Login/forgotPassword.dart'; +import 'package:teso/Pages/PageWidgets/Login/password.dart'; +import 'package:teso/Pages/PageWidgets/Login/username.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/SignUp.dart'; +import 'package:teso/main_screen.dart'; +import 'package:teso/providers/referral_provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:google_sign_in/google_sign_in.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/util/consts.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'facebookRedirect.dart'; + +class LoginPage extends StatefulWidget { + final List connectedCameras; + final String referrer; + + const LoginPage({Key key, this.connectedCameras, this.referrer}) + : super(key: key); + @override + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + TextEditingController usern = new TextEditingController(); + TextEditingController password = new TextEditingController(); + UserProvider user = UserProvider(); + User _user; + bool loading = false; + bool gloading = false; + bool ios = false; + bool error = false; + String errorMessage = "An error occurred while verifying account try again!!"; + String deviceToken; + FirebaseAuth _auth; + GoogleSignIn _googleSignIn; + FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; + Locale myLocale; + + void signIn() async { + if (deviceToken == null) firebaseCloudMessaging_Listeners(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + setState(() { + loading = true; + }); + + Map requestHeaders = { + 'Content-type': 'application/json', + }; + UserAuth auth = new UserAuth(); + auth.username = usern.text; + auth.password = password.text; + auth.deviceToken = deviceToken; + + var register2 = serverLocation + 'api/tokens'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(auth), headers: requestHeaders); + + if (client1.statusCode == 200) { + Map handler = jsonDecode(client1.body); + TokenHandler tokenHandler = TokenHandler.fromJSON(handler); + + _auth = FirebaseAuth.instance; + await _auth.signInWithCustomToken(tokenHandler.tokenFirebase); + + prefs.setString("tokensTeso", "Bearer " + tokenHandler.tokenTeso); + prefs.setString("tokensFirebase", tokenHandler.tokenFirebase); + prefs.setString("accountType", "email"); + prefs.setString("id", _auth.currentUser.uid); + prefs.setString("currentUser", tokenHandler.user.toString()); + prefs.setBool("password", true); + + user.setUser(tokenHandler.user); + + final QuerySnapshot result = await FirebaseFirestore.instance + .collection('users') + .where('id', isEqualTo: _auth.currentUser.uid) + .get(); + final List documents = result.docs; + if (documents.length == 0) { + // Update data to server if new user + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .set({ + 'firstname': tokenHandler.user.firstname, + 'surname': tokenHandler.user.lastname, + 'id': _auth.currentUser.uid + }); + } + + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .update({'deviceToken': deviceToken}); + + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => MainScreens( + // connectedCameras: widget.connectedCameras + )), + (Route route) => false); + } else { + setState(() { + loading = false; + error = true; + }); + } + + setState(() { + loading = false; + }); + } + + void signup(context) { + Provider.of(context, listen: false) + .setReferral(widget.referrer); + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return SignUpPage(); + }); + } + + signInWithGoogle() async { + try { + setState(() { + gloading = true; + error = false; + }); + + if (deviceToken == null) firebaseCloudMessaging_Listeners(); + _auth = FirebaseAuth.instance; + _googleSignIn = GoogleSignIn(); + GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn(); + GoogleSignInAuthentication googleSignInAuthentication = + await googleSignInAccount.authentication; + AuthCredential credential = GoogleAuthProvider.credential( + accessToken: googleSignInAuthentication.accessToken, + idToken: googleSignInAuthentication.idToken, + ); + var authResult = await _auth.signInWithCredential(credential); + _user = authResult.user; + assert(!_user.isAnonymous); + assert(await _user.getIdToken() != null); + User currentUser = _auth.currentUser; + assert(_user.uid == currentUser.uid); + + var names = googleSignInAccount.displayName.split(' '); + + GoogleUser googleUser = new GoogleUser(); + googleUser.userGUID = currentUser.uid; + googleUser.firstname = names[0]; + googleUser.surname = names[1]; + googleUser.email = _user.email; + googleUser.pictureUri = _user.photoURL; + googleUser.deviceToken = deviceToken; + if (widget.referrer != null) googleUser.referralCode = widget.referrer; + + final QuerySnapshot result = await FirebaseFirestore.instance + .collection('users') + .where('id', isEqualTo: _auth.currentUser.uid) + .orderBy('surname', descending: true) + .get(); + final List documents = result.docs; + if (documents.length == 0) { + // Update data to server if new user + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .set({ + 'firstname': googleUser.firstname, + 'surname': googleUser.surname, + 'id': _auth.currentUser.uid + }); + } + + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .update({'deviceToken': deviceToken}); + + googleSignInAccount = await _googleSignIn.signOut(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + }; + + var register2 = serverLocation + 'api/googleauth'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(googleUser), headers: requestHeaders); + if (client1.statusCode == 200) { + Map handler = jsonDecode(client1.body); + TokenHandler tokenHandler = TokenHandler.fromJSON(handler); + prefs.setString("tokensTeso", "Bearer " + tokenHandler.tokenTeso); + prefs.setString("id", _auth.currentUser.uid); + _user + .getIdToken() + .then((value) => prefs.setString("tokensFirebase", value)); + prefs.setString("currentUser", tokenHandler.user.toString()); + prefs.setBool("password", false); + + user.setUser(tokenHandler.user); + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => MainScreens( + // connectedCameras: widget.connectedCameras + )), + (Route route) => false); + } else { + setState(() { + gloading = false; + error = true; + }); + } + + setState(() { + gloading = false; + }); + } catch (e) { + setState(() { + gloading = false; + error = true; + }); + } + } + + String generateNonce([int length = 32]) { + final charset = + '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; + final random = Random.secure(); + return List.generate(length, (_) => charset[random.nextInt(charset.length)]) + .join(); + } + + /// Returns the sha256 hash of [input] in hex notation. + String sha256ofString(String input) { + final bytes = utf8.encode(input); + final digest = sha256.convert(bytes); + return digest.toString(); + } + + signInWithApple() async { + try { + setState(() { + gloading = true; + error = false; + }); + + final scopes = [ + AppleIDAuthorizationScopes.email, + AppleIDAuthorizationScopes.fullName, + ]; + final rawNonce = generateNonce(); + final nonce = sha256ofString(rawNonce); + + final appleCredential = await SignInWithApple.getAppleIDCredential( + scopes: scopes, + nonce: nonce, + webAuthenticationOptions: WebAuthenticationOptions( + clientId: "com.tesoapp.teso", + redirectUri: Uri.parse( + "https://tesoappleauth.glitch.me/callbacks/sign_in_with_apple")), + ); + + final oauthCredential = OAuthProvider("apple.com").credential( + idToken: appleCredential.identityToken, + accessToken: appleCredential.authorizationCode, + rawNonce: rawNonce, + ); + _auth = FirebaseAuth.instance; + + final authResult = await _auth.signInWithCredential(oauthCredential); + _user = authResult.user; + assert(!_user.isAnonymous); + assert(await _user.getIdToken() != null); + User currentUser = _auth.currentUser; + assert(_user.uid == currentUser.uid); + + GoogleUser googleUser = new GoogleUser(); + googleUser.userGUID = currentUser.uid; + googleUser.firstname = + appleCredential.givenName != null ? appleCredential.givenName : ""; + googleUser.surname = + appleCredential.familyName != null ? appleCredential.familyName : ""; + googleUser.email = _user.email; + googleUser.deviceToken = deviceToken; + if (widget.referrer != null) googleUser.referralCode = widget.referrer; + + final QuerySnapshot result = await FirebaseFirestore.instance + .collection('users') + .where('id', isEqualTo: _auth.currentUser.uid) + .get(); + final List documents = result.docs; + if (documents.length == 0) { + // Update data to server if new user + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .set({ + 'firstname': googleUser.firstname, + 'surname': googleUser.surname, + 'id': _auth.currentUser.uid + }); + } + + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .update({'deviceToken': deviceToken}); + + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + }; + + var register2 = serverLocation + 'api/apple'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(googleUser), headers: requestHeaders); + if (client1.statusCode == 200) { + Map handler = jsonDecode(client1.body); + TokenHandler tokenHandler = TokenHandler.fromJSON(handler); + prefs.setString("tokensTeso", "Bearer " + tokenHandler.tokenTeso); + prefs.setString("id", _auth.currentUser.uid); + _user + .getIdToken() + .then((value) => prefs.setString("tokensFirebase", value)); + prefs.setString("currentUser", tokenHandler.user.toString()); + prefs.setBool("password", false); + + user.setUser(tokenHandler.user); + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => MainScreens( + // connectedCameras: widget.connectedCameras + )), + (Route route) => false); + } else { + setState(() { + gloading = false; + error = true; + }); + } + // } else { + // // errorMessage = "Unable to " + // } + setState(() { + gloading = false; + }); + } catch (e) { + print(e); + setState(() { + gloading = false; + error = true; + }); + } + } + + loginWithFacebook() async { + setState(() { + gloading = true; + error = false; + }); + String results = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CustomWebView( + selectedUrl: + 'https://www.facebook.com/dialog/oauth?client_id=$your_client_id&redirect_uri=$your_redirect_url&response_type=token&scope=email,public_profile,', + ), + maintainState: true), + ); + if (results != null) { + try { + _auth = FirebaseAuth.instance; + final facebookAuthCred = FacebookAuthProvider.credential(results); + UserCredential user = + await _auth.signInWithCredential(facebookAuthCred); + + FacebookUser facebookUser = new FacebookUser(); + facebookUser.userGUID = _auth.currentUser.uid; + facebookUser.firstname = user.user.displayName; + facebookUser.surname = ""; + facebookUser.email = user.user.email; + facebookUser.pictureUri = user.user.photoURL; + facebookUser.deviceToken = deviceToken; + facebookUser.username = user.user.displayName.replaceAll(' ', ''); + if (widget.referrer != null) + facebookUser.referralCode = widget.referrer; + + _user = user.user; + assert(!_user.isAnonymous); + assert(await _user.getIdToken() != null); + User currentUser = _auth.currentUser; + assert(_user.uid == currentUser.uid); + + final QuerySnapshot result = await FirebaseFirestore.instance + .collection('users') + .where('id', isEqualTo: _auth.currentUser.uid) + .get(); + final List documents = result.docs; + if (documents.length == 0) { + // Update data to server if new user + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .set({ + 'firstname': facebookUser.firstname, + 'surname': facebookUser.surname, + 'id': _auth.currentUser.uid + }); + } + + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .update({'deviceToken': deviceToken}); + + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + }; + + var register2 = serverLocation + 'api/facebookauth'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(facebookUser), headers: requestHeaders); + + if (client1.statusCode == 200) { + Map handler = jsonDecode(client1.body); + TokenHandler tokenHandler = TokenHandler.fromJSON(handler); + _user + .getIdToken() + .then((value) => prefs.setString("tokensFirebase", value)); + prefs.setString("id", _auth.currentUser.uid); + prefs.setString("currentUser", tokenHandler.user.toString()); + prefs.setString("tokensTeso", "Bearer " + tokenHandler.tokenTeso); + prefs.setBool("password", false); + + this.user.setUser(tokenHandler.user); + + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => + MainScreens(connectedCameras: widget.connectedCameras)), + (Route route) => false); + } else { + setState(() { + gloading = false; + error = true; + }); + } + + setState(() { + gloading = false; + }); + } catch (e) { + print(e); + setState(() { + gloading = false; + error = true; + }); + } + } else { + gloading = false; + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + void initState() { + ios = Platform.isIOS; + SharedPreferences.getInstance().then((value) { + String current = value.getString("api-version"); + value.clear(); + if (current != null) value.setString("api-version", current); + }); + super.initState(); + firebaseCloudMessaging_Listeners(); + } + + // ignore: non_constant_identifier_names + void firebaseCloudMessaging_Listeners() async { + try { + if (Platform.isIOS) iOS_Permission(); + + await _firebaseMessaging.getToken().then((token) { + deviceToken = token; + }); + } catch (e) { + print(e); + } + } + + // ignore: non_constant_identifier_names + void iOS_Permission() { + _firebaseMessaging.requestPermission( + sound: true, badge: true, alert: true, provisional: false); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Stack( + children: [ + Container( + height: double.infinity, + child: SingleChildScrollView( + // physics: AlwaysScrollableScrollPhysics(), + padding: + EdgeInsets.symmetric(horizontal: 40.00, vertical: 50), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + height: MediaQuery.of(context).size.height * 0.10, + width: double.infinity, + child: Align( + alignment: Alignment.topCenter, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/images/tesoCouponInsignia.png")), + ), + ), + ), + ), + Container( + width: double.infinity, + child: Center( + child: Text( + "Sign In", + style: TextStyle( + color: Colors.black87, + fontFamily: 'OpenSans', + fontWeight: FontWeight.bold, + fontSize: 22.0, + //fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox(height: 20.0), + username(context, "Username", usern), + SizedBox(height: 5.0), + passwordBuilder( + context, "Enter your password here", password), + buildForgotPasswordBtn(context), + Visibility( + visible: loading, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + Visibility( + visible: error, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.9, + child: Center( + child: Text( + errorMessage, + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.red, + ), + ), + ), + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.001), + RaisedGradientButton( + onPressed: signIn, + child: Text( + "Sign In", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + ), + SizedBox(height: 20.0), + Container( + width: double.infinity, + child: Center( + child: Text( + "or", + style: TextStyle( + fontSize: 15, + color: Colors.black87, + ), + ), + ), + ), + SizedBox(height: 20.0), + // !ios + // ? buildThirdAuth(context) + buildIOSThirdAuth(context), + SizedBox( + height: MediaQuery.of(context).size.height * 0.05), + InkWell( + onTap: () => signup(context), + child: new Wrap( + direction: Axis.horizontal, + spacing: 5, + children: [ + Text( + "Don't have an account ?", + style: TextStyle( + fontSize: 14.5, + color: Colors.black87, + ), + ), + Text("Sign Up", + style: TextStyle( + fontSize: 16, + color: Colors.blue, + )), + ], + ), + ) + ], + ), + ), + ), + Visibility( + visible: gloading, + child: Container( + color: Theme.of(context).backgroundColor, + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ], + ), + ), + ), + ); + } + + buildThirdAuth(BuildContext context) { + return Wrap( + direction: Axis.horizontal, + children: [ + MaterialButton( + onPressed: signInWithGoogle, + color: Colors.white, + padding: EdgeInsets.all(10), + shape: CircleBorder(), + child: Image( + image: AssetImage("assets/images/google.png"), + height: MediaQuery.of(context).size.height * 0.045, + ), + ), + MaterialButton( + onPressed: loginWithFacebook, + color: Colors.white, + padding: EdgeInsets.all(10), + shape: CircleBorder(), + child: Image( + image: AssetImage("assets/images/facebook.png"), + height: MediaQuery.of(context).size.height * 0.045, + ), + ), + ], + ); + } + + buildIOSThirdAuth(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SignInWithAppleButton( + style: SignInWithAppleButtonStyle.black, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + iconAlignment: IconAlignment.left, + onPressed: signInWithApple, + ), + SizedBox( + height: 10, + ), + CustomLoginButton( + child: Text( + "Sign in with Google ", + style: TextStyle( + fontSize: 44 * 0.43, + color: Colors.black, + ), + ), + color: Colors.white, + icon: "assets/images/google.png", + onPressed: signInWithGoogle, + ), + SizedBox( + height: 10, + ), + CustomLoginButton( + child: Text( + "Sign in with Facebook", + style: TextStyle( + fontSize: 44 * 0.43, + color: Colors.white, + ), + ), + color: Color(0XFF4267B2), + icon: "assets/images/facebook_new.png", + onPressed: signInWithGoogle, + ), + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/NewProfile.dart b/lib/Pages/Sub_Pages/LandingPage/NewProfile.dart new file mode 100644 index 0000000..173e6b3 --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/NewProfile.dart @@ -0,0 +1,212 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_datetime_picker/flutter_datetime_picker.dart'; +import 'package:intl/intl.dart'; +import 'package:teso/Classes/API%20Clasess/TesoUserDetail.dart'; +import 'package:teso/Classes/customTesoButton.dart'; +import 'package:teso/util/consts.dart'; + +class CompleteNewProfile extends StatefulWidget { + @override + _CompleteNewProfileState createState() => _CompleteNewProfileState(); +} + +class _CompleteNewProfileState extends State { + DateFormat dateFormat = DateFormat("EEEE dd-MM-yyyy"); + DateTime selectedDate = DateTime.now(); + String selectedGender; + List gender = ["Male", "Female", "Other"]; + bool error = false; + String message = ""; + TesoUserDetail olduser; + + void changeDate(v) { + setState(() { + selectedDate = v; + }); + } + + completeNewProfile() async { + if (selectedDate.year != DateTime.now().year && selectedGender != null) { + olduser = new TesoUserDetail(); + olduser.dateOfBirth = selectedDate; + olduser.gender = selectedGender; + Navigator.pop(context, olduser); + } else { + setState(() { + error = true; + message = + "Sorry, an error occurred please make sure to select the right date and select a gender !!!"; + }); + + Future.delayed(const Duration(seconds: 5), () { + if (error) { + if (mounted) { + setState(() { + error = false; + }); + } + } + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(20), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + height: 120, + padding: EdgeInsets.symmetric(vertical: 20), + child: Image( + image: AssetImage("assets/images/tesoCouponInsignia.png"), + ), + ), + SizedBox( + height: 5, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "Complete your profile", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + ), + ), + ), + SizedBox( + height: 40, + ), + Container( + width: MediaQuery.of(context).size.width, + // height: 10, + child: Text( + "When is your birthday ?", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + alignment: Alignment.centerLeft, + child: TextButton( + onPressed: () { + DatePicker.showDatePicker( + context, + showTitleActions: true, + maxTime: DateTime.now(), + onConfirm: (date) { + changeDate(date); + }, + currentTime: selectedDate, + locale: LocaleType.en, + ); + }, + child: Text( + dateFormat.format(selectedDate).toString(), + style: TextStyle( + fontWeight: FontWeight.normal, + ), + ), + ), + ), + Divider(), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Text( + "What is your gender ?", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + Container( + alignment: Alignment.centerLeft, + child: DropdownButton( + hint: Text("Select Gender"), + value: selectedGender, + items: gender + .map( + (gender) => DropdownMenuItem( + value: gender, + child: Text( + gender, + style: TextStyle( + // color: Colors.grey, + ), + ), + ), + ) + .toList(), + onChanged: (v) { + setState(() { + selectedGender = v; + }); + }, + ), + ), + Divider(), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Text( + "Teso uses this data to personalize your experiences, to help business understand their customers, and more. " + + "We will always keep your personal data private.", + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: 20, + ), + Visibility( + visible: error, + child: Container( + width: MediaQuery.of(context).size.width, + child: Text( + message, + style: TextStyle( + color: Colors.red, + ), + ), + ), + ), + RaisedGradientButton( + onPressed: completeNewProfile, + child: Text( + "Submit", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/ReferPage.dart b/lib/Pages/Sub_Pages/LandingPage/ReferPage.dart new file mode 100644 index 0000000..807be00 --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/ReferPage.dart @@ -0,0 +1,143 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter/cupertino.dart'; + +class ReferPage extends StatefulWidget { + final String accountType; + const ReferPage({Key key, this.accountType}) : super(key: key); + @override + _ReferPageState createState() => + _ReferPageState(accountType: this.accountType); +} + +class _ReferPageState extends State { + String accountType; + _ReferPageState({this.accountType}); + var usern = new TextEditingController(); + bool visibleT = true; + var password = new TextEditingController(); + bool loading = false; + bool error = false; + bool rememberMe = false; + bool success = false; + String usernameFor; + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(Consts.padding), + ), + elevation: 0.0, + backgroundColor: Colors.transparent, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Stack( + children: [ + Container( + padding: EdgeInsets.only( + top: Consts.avatarRadius + Consts.padding, + bottom: Consts.padding, + left: Consts.padding, + right: Consts.padding, + ), + margin: EdgeInsets.only(top: Consts.avatarRadius), + decoration: new BoxDecoration( + color: Colors.white, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(Consts.padding), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10.0, + offset: const Offset(0.0, 10.0), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, // To make the card compact + children: [ + Visibility( + visible: visibleT, + child: Text( + "Error", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.w700, + color: Colors.red, + ), + ), + ), + SizedBox(height: 16.0), + Text( + "Sorry your account is connected to " + + accountType + + ", login using the " + + accountType + + " button ", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12.0, + color: Colors.black87, + ), + ), + SizedBox(height: 14.0), + Align( + alignment: Alignment.bottomRight, + child: TextButton( + onPressed: () { + Navigator.of(context).pop(); // To close the dialog + }, + child: Text( + "OK", + style: TextStyle( + color: Colors.black87, + ), + ), + ), + ), + Visibility( + visible: loading, + child: Align( + alignment: Alignment.bottomCenter, + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ], + ), + ), + Positioned( + left: Consts.padding, + right: Consts.padding, + child: CircleAvatar( + child: Icon( + Icons.cancel, + color: Colors.red, + size: 100, + ), + backgroundColor: Colors.white, + radius: Consts.avatarRadius, + ), + ), + ], + ), + ), + ); + } +} + +class Consts { + Consts._(); + + static const double padding = 16.0; + static const double avatarRadius = 55.0; +} diff --git a/lib/Pages/Sub_Pages/LandingPage/ResetPassword.dart b/lib/Pages/Sub_Pages/LandingPage/ResetPassword.dart new file mode 100644 index 0000000..9c7a23b --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/ResetPassword.dart @@ -0,0 +1,244 @@ +import 'package:teso/Pages/Sub_Pages/LandingPage/Success.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/ReferPage.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/Pages/PageWidgets/Login/email.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; + +class ResetPassword extends StatefulWidget { + @override + _ResetPasswordState createState() => _ResetPasswordState(); +} + +class _ResetPasswordState extends State { + var usern = new TextEditingController(); + bool visibleT = true; + var password = new TextEditingController(); + bool loading = false; + bool error = false; + bool rememberMe = false; + bool success = false; + String usernameFor; + + @override + void dispose() { + super.dispose(); + } + + void requestReset() async { + setState(() { + error = false; + }); + Map requestHeaders = { + 'Content-type': 'application/json', + }; + String email = usern.text; + var register2 = serverLocation + 'resetpassword/request'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(email), headers: requestHeaders); + + if (client1.statusCode == 200) { + String responseBody = client1.body.trim(); + switch (responseBody) { + case "sent": + await showDialog( + context: context, + builder: (BuildContext context) => SuccessPage(), + ); + Navigator.pop(context); + break; + case "twitter": + await showDialog( + context: context, + builder: (BuildContext context) => ReferPage( + accountType: responseBody, + ), + ); + Navigator.pop(context); + break; + case "google": + await showDialog( + context: context, + builder: (BuildContext context) => + ReferPage(accountType: responseBody), + ); + Navigator.pop(context); + break; + case "facebook": + await showDialog( + context: context, + builder: (BuildContext context) => + ReferPage(accountType: responseBody), + ); + Navigator.pop(context); + break; + } + } else { + setState(() { + error = true; + }); + } + } + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(Consts.padding), + ), + elevation: 0.0, + backgroundColor: Colors.transparent, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Stack( + children: [ + Container( + padding: EdgeInsets.only( + top: Consts.avatarRadius + Consts.padding, + bottom: Consts.padding, + left: Consts.padding, + right: Consts.padding, + ), + margin: EdgeInsets.only(top: Consts.avatarRadius), + decoration: new BoxDecoration( + color: Colors.white, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(Consts.padding), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10.0, + offset: const Offset(0.0, 10.0), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, // To make the card compact + children: [ + Visibility( + visible: success, + child: Text( + "A confirmation link has been sent to your mail open the link to continue with the process !!!", + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.w700, + color: Colors.green, + ), + ), + ), + Visibility( + visible: visibleT, + child: Text( + "Reset your Teso account password", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.w700, + color: Colors.white, + ), + ), + ), + SizedBox(height: 16.0), + Visibility( + visible: visibleT, + child: Text( + "Please enter the registered email address for your Teso account", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12.0, + color: Colors.white, + ), + ), + ), + SizedBox(height: 14.0), + Visibility( + visible: visibleT, + child: email(context, "Email address", usern), + ), + SizedBox(height: 20.0), + Visibility( + visible: visibleT, + child: Align( + alignment: Alignment.bottomCenter, + child: TextButton( + onPressed: requestReset, + child: Text( + "RESET PASSWORD", + style: TextStyle( + color: Colors.white, + fontSize: 13, + ), + ), + ), + ), + ), + Visibility( + visible: error, + child: Align( + alignment: Alignment.bottomCenter, + child: Text( + "Oops! An error occurred while trying to reset password, please make sure your email is correct and try again", + style: TextStyle(fontSize: 16, color: Colors.red), + )), + ), + Visibility( + visible: !visibleT, + child: Align( + alignment: Alignment.bottomRight, + child: TextButton( + onPressed: () { + Navigator.of(context).pop(); // To close the dialog + }, + child: Text("OK"), + ), + ), + ), + Visibility( + visible: loading, + child: Align( + alignment: Alignment.bottomCenter, + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ], + ), + ), + Positioned( + left: Consts.padding, + right: Consts.padding, + child: CircleAvatar( + child: Image.asset( + "assets/images/tesoCouponInsignia.png", + width: 100, + ), + backgroundColor: Colors.white, + radius: Consts.avatarRadius, + ), + ), + ], + ), + ), + ); + } +} + +class Consts { + Consts._(); + + static const double padding = 16.0; + static const double avatarRadius = 55.0; +} diff --git a/lib/Pages/Sub_Pages/LandingPage/SignUp.dart b/lib/Pages/Sub_Pages/LandingPage/SignUp.dart new file mode 100644 index 0000000..9c82445 --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/SignUp.dart @@ -0,0 +1,207 @@ +import 'package:flutter/material.dart'; + +import 'package:teso/Pages/PageWidgets/Login/signupusername.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/FinalProcess.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:http/http.dart' as http; + +class SignUpPage extends StatefulWidget { + @override + _SignUpPageState createState() => _SignUpPageState(); +} + +class _SignUpPageState extends State { + TesoUser newuser = new TesoUser(); + TextEditingController user = new TextEditingController(); + bool loading = false; + bool taken = false; + + @override + void dispose() { + super.dispose(); + } + + @override + void initState() { + super.initState(); + user.addListener(() { + taken = false; + }); + } + + void startProcessing() async { + setState(() { + loading = true; + }); + var client = await http.get(Uri.parse( + serverLocation + "api/username/" + user.text.toString().trim())); + if (client.statusCode == 200) { + newuser.username = user.text; + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: FinalProcess( + newuser: this.newuser, + )), + ); + setState(() { + loading = false; + }); + } else { + setState(() { + loading = false; + taken = true; + }); + } + } + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only( + top: MediaQuery.of(context).size.height * 0.05, + ), + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + children: [ + new Container( + // color: Colors.white, + margin: EdgeInsets.only( + left: 20.0, + top: 20.0, + bottom: 12.0, + ), + child: Row( + children: [ + Container( + child: InkWell( + onTap: () => Navigator.pop(context), + child: Icon(Icons.close), + ), + ), + Expanded( + child: Container( + width: double.infinity, + child: Center( + child: Text( + "Create username", + style: TextStyle( + fontSize: 20.0, + color: Colors.black, + ), + ), + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Divider( + thickness: 0.8, + ), + ), + Container( + width: double.infinity, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.02, + ), + child: Center( + child: Text( + "Choose a username for your new account. You can always change it later.", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + SizedBox( + height: 20, + ), + usersignup(context, user), + Visibility( + visible: loading, + child: + SizedBox(height: MediaQuery.of(context).size.height * 0.025), + ), + Visibility( + visible: loading, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + Visibility( + visible: taken, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.6, + // height: 40.0, + child: Center( + child: Text( + "Username has already been taken...", + style: TextStyle( + color: Colors.red, + ), + ), + ), + ), + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.001), + Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.6, + height: 40.0, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: startProcessing, + child: Center( + child: Text( + "NEXT", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + )), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/Success.dart b/lib/Pages/Sub_Pages/LandingPage/Success.dart new file mode 100644 index 0000000..7e3d205 --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/Success.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter/cupertino.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/resetVerification.dart'; + +class SuccessPage extends StatefulWidget { + @override + _SuccessPageState createState() => _SuccessPageState(); +} + +class _SuccessPageState extends State { + var usern = new TextEditingController(); + bool visibleT = true; + var password = new TextEditingController(); + bool loading = false; + bool error = false; + bool rememberMe = false; + bool success = false; + String usernameFor; + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(Consts.padding), + ), + elevation: 0.0, + backgroundColor: Colors.transparent, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Stack( + children: [ + Container( + padding: EdgeInsets.only( + top: Consts.avatarRadius + Consts.padding, + bottom: Consts.padding, + left: Consts.padding, + right: Consts.padding, + ), + margin: EdgeInsets.only(top: Consts.avatarRadius), + decoration: new BoxDecoration( + color: Colors.white, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(Consts.padding), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10.0, + offset: const Offset(0.0, 10.0), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, // To make the card compact + children: [ + Visibility( + visible: visibleT, + child: Text( + "Success", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.w700, + color: Colors.green, + ), + ), + ), + SizedBox(height: 16.0), + Text( + "A password reset link has been sent to your email !!!", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12.0, + color: Colors.black87, + ), + ), + SizedBox(height: 14.0), + Align( + alignment: Alignment.bottomRight, + child: TextButton( + onPressed: () async { + await Navigator.push(context, MaterialPageRoute( + builder: (context) { + return ResetVerificationCode(); + }, + )); + Navigator.pop(context); + }, + child: Text( + "OK", + style: TextStyle( + color: Colors.black87, + ), + ), + ), + ), + Visibility( + visible: loading, + child: Align( + alignment: Alignment.bottomCenter, + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ], + ), + ), + Positioned( + left: Consts.padding, + right: Consts.padding, + child: CircleAvatar( + child: Icon( + Icons.check_circle, + color: Colors.green, + size: 100, + ), + backgroundColor: Colors.white, + radius: Consts.avatarRadius, + ), + ), + ], + ), + ), + ); + } +} + +class Consts { + Consts._(); + + static const double padding = 16.0; + static const double avatarRadius = 55.0; +} diff --git a/lib/Pages/Sub_Pages/LandingPage/createPassword.dart b/lib/Pages/Sub_Pages/LandingPage/createPassword.dart new file mode 100644 index 0000000..f1642a9 --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/createPassword.dart @@ -0,0 +1,356 @@ +import 'package:teso/Classes/API%20Clasess/Registrar.dart'; +import 'package:teso/Classes/API%20Clasess/TesoUserDetail.dart'; +import 'package:teso/Classes/API%20Clasess/UserAuth.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/registeringProcess.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Login/passwordSignUP.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:math' as math; +import 'package:teso/Pages/PageWidgets/Login/validation.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'dart:io'; +import 'dart:ui'; + +class CreatePassword extends StatefulWidget { + final TesoUser newuser; + const CreatePassword({Key key, this.newuser}) : super(key: key); + @override + _CreatePasswordState createState() => + _CreatePasswordState(newuser: this.newuser); +} + +class _CreatePasswordState extends State + with TickerProviderStateMixin { + TesoUser newuser; + _CreatePasswordState({this.newuser}); + TextEditingController password = new TextEditingController(); + AnimationController _controller; + Animation _fabScale; + bool eightChars = false; + bool specialChar = false; + bool upperCaseChar = false; + bool lowerCaseChar = false; + bool number = false; + FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; + String deviceToken = ""; + + @override + void dispose() { + super.dispose(); + } + + bool _allValid() { + return eightChars && + number && + specialChar && + upperCaseChar && + lowerCaseChar; + } + + @override + void initState() { + super.initState(); + firebaseCloudMessaging_Listeners(); + password.addListener(() { + setState(() { + eightChars = password.text.length >= 8; + number = password.text.contains(RegExp(r'\d'), 0); + upperCaseChar = password.text.contains(new RegExp(r'[A-Z]'), 0); + lowerCaseChar = password.text.contains(new RegExp(r'[a-z]'), 0); + specialChar = password.text.isNotEmpty && + !password.text.contains(RegExp(r'^[\w&.-]+$'), 0); + }); + + if (_allValid()) { + _controller.forward(); + } else { + _controller.reverse(); + } + }); + _controller = AnimationController( + vsync: this, duration: const Duration(milliseconds: 500)); + + _fabScale = Tween(begin: 0, end: 1) + .animate(CurvedAnimation(parent: _controller, curve: Curves.bounceOut)); + + _fabScale.addListener(() { + setState(() {}); + }); + } + + // ignore: non_constant_identifier_names + void firebaseCloudMessaging_Listeners() async { + try { + if (Platform.isIOS) iOS_Permission(); + + await _firebaseMessaging.getToken().then((token) { + deviceToken = token; + }); + } catch (e) { + print(e); + } + } + + // ignore: non_constant_identifier_names + void iOS_Permission() { + _firebaseMessaging.requestPermission( + sound: true, badge: true, alert: true, provisional: false); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text("Create a password"), + ), + body: Container( + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + children: [ + Container( + padding: EdgeInsets.all(10), + width: double.infinity, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.02, + top: MediaQuery.of(context).size.height * 0.02, + ), + child: Center( + child: Text( + "Enter a password with a minimum of 8 characters and the password must have at least an uppercase letter, a lowercase letter, " + + "a digit and a non-alphanumeric character", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width) * 0.01), + child: _validationStack()), + SizedBox( + height: 50, + ), + createPassword(context, password), + SizedBox(height: MediaQuery.of(context).size.height * 0.002), + Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.6, + height: 40.0, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: () { + if (_allValid()) { + UserAuth auth = new UserAuth(); + auth.username = newuser.username; + auth.password = password.text; + auth.status = "awaiting"; + auth.accountType = "TSUAC001"; + auth.deviceToken = deviceToken; + + Registrar registration = new Registrar(); + registration.authentication = auth; + registration.user = TesoUserDetail.fromUSER(newuser); + + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: RegisteringProcess( + newuser: registration, + ), + ), + ); + } + }, + child: Center( + child: Text( + "Continue", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + )), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _separator() { + return Container( + height: 1, + decoration: BoxDecoration(color: Colors.blue.withAlpha(100)), + ); + } + + Stack _validationStack() { + return Stack( + alignment: Alignment.bottomLeft, + children: [ + Card( + shape: CircleBorder(), + color: Colors.black12, + child: Container( + height: 150, + width: 150, + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 32.0, left: 10), + child: Transform.rotate( + angle: -math.pi / 20, + child: Icon( + Icons.lock, + color: Colors.pink, + size: 60, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 50.0, right: 60), + child: Transform.rotate( + angle: -math.pi / -60, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + elevation: 4, + color: Colors.yellow.shade800, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(8, 8, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 8), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 74), + child: Transform.rotate( + angle: math.pi / -45, + child: Card( + elevation: 6, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + child: Stack( + alignment: Alignment.bottomRight, + children: [ + IntrinsicWidth( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ValidationItem("8 or more Characters", eightChars), + _separator(), + ValidationItem("1 Special character", specialChar), + _separator(), + ValidationItem("1 Upper case", upperCaseChar), + _separator(), + ValidationItem("1 Lower case", lowerCaseChar), + _separator(), + ValidationItem("1 Number", number) + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Transform.scale( + scale: _fabScale.value, + child: Card( + shape: CircleBorder(), + color: Colors.green, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.check, + color: Colors.white, + ), + ), + ), + ), + ) + ], + ), + ), + ), + ) + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/facebookRedirect.dart b/lib/Pages/Sub_Pages/LandingPage/facebookRedirect.dart new file mode 100644 index 0000000..05d3c9d --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/facebookRedirect.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; + +class CustomWebView extends StatefulWidget { + final String selectedUrl; + + CustomWebView({this.selectedUrl}); + + @override + _CustomWebViewState createState() => _CustomWebViewState(); +} + +class _CustomWebViewState extends State { + final flutterWebviewPlugin = new FlutterWebviewPlugin(); + + @override + void initState() { + super.initState(); + + flutterWebviewPlugin.onUrlChanged.listen((String url) { + if (url.contains("#access_token")) { + succeed(url); + } + + if (url.contains( + "https://www.facebook.com/connect/login_success.html?error=access_denied&error_code=200&error_description=Permissions+error&error_reason=user_denied")) { + denied(); + } + }); + } + + denied() { + Navigator.pop(context); + } + + succeed(String url) { + var params = url.split("access_token="); + + var endparam = params[1].split("&"); + flutterWebviewPlugin.clearCache(); + flutterWebviewPlugin.cleanCookies(); + flutterWebviewPlugin.dispose(); + Navigator.pop(context, endparam[0]); + } + + @override + Widget build(BuildContext context) { + return WebviewScaffold( + url: widget.selectedUrl, + appBar: new AppBar( + automaticallyImplyLeading: false, + backgroundColor: Color.fromRGBO(66, 103, 178, 1), + title: new Text( + "Facebook login", + style: TextStyle(color: Colors.white), + ), + )); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/registeringProcess.dart b/lib/Pages/Sub_Pages/LandingPage/registeringProcess.dart new file mode 100644 index 0000000..753dc12 --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/registeringProcess.dart @@ -0,0 +1,80 @@ +import 'dart:convert'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API%20Clasess/Registrar.dart'; +import 'package:teso/providers/referral_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:flutter/cupertino.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/verification.dart'; + +class RegisteringProcess extends StatefulWidget { + final Registrar newuser; + const RegisteringProcess({Key key, this.newuser}) : super(key: key); + @override + _RegisteringProcessState createState() => + _RegisteringProcessState(newuser: this.newuser); +} + +class _RegisteringProcessState extends State { + Registrar newuser; + _RegisteringProcessState({this.newuser}); + void registerUser() async { + try { + Map requestHeaders = { + 'Content-type': 'application/json', + }; + + String referral = + Provider.of(context, listen: false).getReferral(); + + Registrar registrar = newuser; + registrar.user.dateOfBirth = DateTime.now(); + if (referral != null) registrar.referral = referral; + var register = serverLocation + 'api/userauths'; + var client = await http.post(Uri.parse(register), + body: json.encode(registrar), headers: requestHeaders); + if (client.statusCode == 200) { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => Verification( + user: registrar, + )), + (Route route) => false); + } else { + Navigator.of(context).pop(); + } + } catch (e) { + print(e); + } + } + + @override + void initState() { + super.initState(); + + registerUser(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + padding: EdgeInsets.only(top: MediaQuery.of(context).size.width * 0.7), + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Center( + child: Column( + children: [ + CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + Text("Please wait, setting up account...."), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/resetVerification.dart b/lib/Pages/Sub_Pages/LandingPage/resetVerification.dart new file mode 100644 index 0000000..2ab037a --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/resetVerification.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Login/verificationCode.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/resetpasswordCode.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +class ResetVerificationCode extends StatefulWidget { + @override + _ResetVerificationStateCode createState() => _ResetVerificationStateCode(); +} + +class _ResetVerificationStateCode extends State { + TextEditingController code = new TextEditingController(); + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + extendBodyBehindAppBar: false, + appBar: AppBar( + iconTheme: IconThemeData( + color: Colors.white, + ), + backgroundColor: tesoGold, + foregroundColor: Colors.white, + toolbarTextStyle: TextStyle( + color: Colors.white, + ), + automaticallyImplyLeading: true, + title: Text( + "Password Reset", + style: TextStyle( + color: Colors.white, + ), + ), + ), + body: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + padding: + EdgeInsets.symmetric(vertical: SizeConfig.safeBlockHorizontal * 20), + child: Center( + child: Column( + children: [ + Container( + width: SizeConfig.safeBlockHorizontal * 80, + child: Text( + "Enter the verification code we sent below", + textAlign: TextAlign.center, + style: + TextStyle(fontSize: SizeConfig.safeBlockHorizontal * 7), + ), + ), + SizedBox( + height: 10, + ), + inputCode(context, code), + SizedBox( + height: 10, + ), + Container( + width: SizeConfig.safeBlockHorizontal * 50, + height: SizeConfig.safeBlockHorizontal * 10, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: () => Navigator.push(context, MaterialPageRoute( + builder: (context) { + return ResetPasswordCode( + code: int.parse(code.value.text), + ); + }, + )), + child: Center( + child: Text( + "Continue", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + )), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/resetpasswordCode.dart b/lib/Pages/Sub_Pages/LandingPage/resetpasswordCode.dart new file mode 100644 index 0000000..2122d73 --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/resetpasswordCode.dart @@ -0,0 +1,446 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/Classes/API%20Clasess/ResetClass.dart'; +import 'package:teso/Pages/PageWidgets/Login/passwordSignUP.dart'; +import 'package:teso/Pages/PageWidgets/Login/validation.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:math' as math; +import 'package:http/http.dart' as http; + +class ResetPasswordCode extends StatefulWidget { + final code; + const ResetPasswordCode({Key key, @required this.code}) : super(key: key); + @override + _ResetPasswordCodeState createState() => _ResetPasswordCodeState(); +} + +class _ResetPasswordCodeState extends State + with TickerProviderStateMixin { + TextEditingController password = new TextEditingController(); + AnimationController _controller; + Animation _fabScale; + bool eightChars = false; + bool specialChar = false; + bool upperCaseChar = false; + bool lowerCaseChar = false; + bool number = false; + bool changing = false; + + @override + void dispose() { + super.dispose(); + } + + changePassword(context) async { + setState(() { + changing = true; + }); + Map requestHeaders = { + 'Content-type': 'application/json', + }; + var register2 = serverLocation + 'resetpassword/reset'; + ResetClass resetClass = ResetClass( + password: password.text, + resetcode: widget.code.toString(), + ); + var client1 = await http.post( + Uri.parse(register2), + body: json.encode(resetClass), + headers: requestHeaders, + ); + + if (client1.statusCode == 200) { + await tesoSuccessDialog(context); + setState(() { + changing = false; + }); + Future.delayed(const Duration(seconds: 5), () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + } else if (client1.statusCode == 400 && client1.body == "expired") { + setState(() { + changing = false; + }); + tesoErrorDialog(context); + } else { + setState(() { + changing = false; + }); + tesoErrorDialog(context); + } + } + + tesoSuccessDialog(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Success", + style: TextStyle(color: Colors.green[400]), + ), + actions: [ + TextButton( + child: Text( + 'OK', + style: TextStyle(color: Colors.green[400]), + ), + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Password changed successfully", + style: TextStyle(color: Colors.green[400]), + ), + ); + }); + } + + tesoErrorDialog(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Error Occurred", + style: TextStyle(color: Colors.red[400]), + ), + actions: [ + TextButton( + child: Text( + 'OK', + style: TextStyle(color: Colors.red[400]), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "An error occurred while changing password, please try again!", + style: TextStyle(color: Colors.red[400]), + ), + ); + }); + } + + tesoExpiredDialog(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Code Expired", + style: TextStyle(color: Colors.red[400]), + ), + actions: [ + TextButton( + child: Text( + 'OK', + style: TextStyle(color: Colors.red[400]), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Password reset code expired, please try again with a new code!", + style: TextStyle(color: Colors.red[400]), + ), + ); + }); + } + + bool _allValid() { + return eightChars && + number && + specialChar && + upperCaseChar && + lowerCaseChar; + } + + @override + void initState() { + super.initState(); + password.addListener(() { + setState(() { + eightChars = password.text.length >= 8; + number = password.text.contains(RegExp(r'\d'), 0); + upperCaseChar = password.text.contains(new RegExp(r'[A-Z]'), 0); + lowerCaseChar = password.text.contains(new RegExp(r'[a-z]'), 0); + specialChar = password.text.isNotEmpty && + !password.text.contains(RegExp(r'^[\w&.-]+$'), 0); + }); + + if (_allValid()) { + _controller.forward(); + } else { + _controller.reverse(); + } + }); + _controller = AnimationController( + vsync: this, duration: const Duration(milliseconds: 500)); + + _fabScale = Tween(begin: 0, end: 1) + .animate(CurvedAnimation(parent: _controller, curve: Curves.bounceOut)); + + _fabScale.addListener(() { + setState(() {}); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + title: Text("Reset your Teso password"), + automaticallyImplyLeading: false, + ), + body: Container( + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + children: [ + Container( + padding: EdgeInsets.all(10), + width: double.infinity, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.02, + top: MediaQuery.of(context).size.height * 0.02, + ), + child: Center( + child: Text( + "Enter a password with a minimum of 8 characters and the password must have at least an uppercase letter, a lowercase letter, " + + "a digit and a non-alphanumeric character", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width) * 0.01), + child: _validationStack()), + SizedBox( + height: 50, + ), + createPassword(context, password), + SizedBox(height: MediaQuery.of(context).size.height * 0.002), + Visibility( + visible: !changing, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.6, + height: 40.0, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: () async { + if (_allValid()) { + await changePassword(context); + } + }, + child: Center( + child: Text( + "Reset Password", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + )), + ), + ), + ), + Visibility( + visible: changing, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _separator() { + return Container( + height: 1, + decoration: BoxDecoration(color: Colors.blue.withAlpha(100)), + ); + } + + Stack _validationStack() { + return Stack( + alignment: Alignment.bottomLeft, + children: [ + Card( + shape: CircleBorder(), + color: Colors.black12, + child: Container( + height: 150, + width: 150, + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 32.0, left: 10), + child: Transform.rotate( + angle: -math.pi / 20, + child: Icon( + Icons.lock, + color: Colors.pink, + size: 60, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 50.0, right: 60), + child: Transform.rotate( + angle: -math.pi / -60, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + elevation: 4, + color: Colors.yellow.shade800, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(8, 8, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 8), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 74), + child: Transform.rotate( + angle: math.pi / -45, + child: Card( + elevation: 6, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + child: Stack( + alignment: Alignment.bottomRight, + children: [ + IntrinsicWidth( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ValidationItem("8 or more Characters", eightChars), + _separator(), + ValidationItem("1 Special character", specialChar), + _separator(), + ValidationItem("1 Upper case", upperCaseChar), + _separator(), + ValidationItem("1 Lower case", lowerCaseChar), + _separator(), + ValidationItem("1 Number", number) + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Transform.scale( + scale: _fabScale.value, + child: Card( + shape: CircleBorder(), + color: Colors.green, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.check, + color: Colors.white, + ), + ), + ), + ), + ) + ], + ), + ), + ), + ) + ], + ); + } +} diff --git a/lib/Pages/Sub_Pages/LandingPage/verification.dart b/lib/Pages/Sub_Pages/LandingPage/verification.dart new file mode 100644 index 0000000..dcd886f --- /dev/null +++ b/lib/Pages/Sub_Pages/LandingPage/verification.dart @@ -0,0 +1,279 @@ +import 'package:camera/camera.dart'; +import 'package:teso/Classes/API%20Clasess/TokenHandler.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Login/verificationCode.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/Classes/API%20Clasess/Registrar.dart'; +import 'package:teso/main_screen.dart'; +import 'package:teso/Classes/API%20Clasess/UserAuth.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/Login.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:firebase_auth/firebase_auth.dart'; + +class Verification extends StatefulWidget { + final Registrar user; + const Verification({Key key, this.user}) : super(key: key); + @override + _VerificationState createState() => _VerificationState(user: this.user); +} + +class _VerificationState extends State { + List connectedCameras; + Registrar user; + _VerificationState({this.user}); + TextEditingController code = new TextEditingController(); + bool loading = false; + bool error = false; + FirebaseAuth _auth; + + @override + void initState() { + availableCameras().then((value) { + connectedCameras = value; + }); + super.initState(); + } + + void verifyAccount() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + setState(() { + loading = true; + }); + + Map requestHeaders = { + 'Content-type': 'application/json', + }; + int codeEntered = int.parse(code.text.toString()); + var register = serverLocation + 'api/activationhandler'; + var client = await http.post(Uri.parse(register), + body: json.encode(codeEntered), headers: requestHeaders); + + if (client.statusCode == 200) { + UserAuth auth = user.authentication; + var register2 = serverLocation + 'api/tokens'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(auth), headers: requestHeaders); + if (client1.statusCode == 200) { + Map handler = jsonDecode(client1.body); + TokenHandler tokenHandler = TokenHandler.fromJSON(handler); + + _auth = FirebaseAuth.instance; + await _auth.signInWithCustomToken(tokenHandler.tokenFirebase); + + final QuerySnapshot result = await FirebaseFirestore.instance + .collection('users') + .where('id', isEqualTo: tokenHandler.user.userGUID) + .get(); + final List documents = result.docs; + if (documents.length == 0) { + // Update data to server if new user + FirebaseFirestore.instance + .collection('users') + .doc(tokenHandler.user.userGUID) + .set({ + 'nickname': tokenHandler.user.username, + 'id': tokenHandler.user.userGUID + }); + } + + FirebaseFirestore.instance + .collection('users') + .doc(_auth.currentUser.uid) + .update({'deviceToken': auth.deviceToken}); + + prefs.setString("tokensTeso", "Bearer " + tokenHandler.tokenTeso); + prefs.setString("tokensFirebase", tokenHandler.tokenFirebase); + prefs.setString("accountType", "email"); + prefs.setString("id", tokenHandler.user.userGUID); + prefs.setString("currentUser", tokenHandler.user.toString()); + prefs.setBool("password", true); + + UserProvider userProvider = UserProvider(); + userProvider.setUser(tokenHandler.user); + + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => MainScreens( + connectedCameras: connectedCameras, + )), + (Route route) => false); + } else { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => LoginPage( + connectedCameras: connectedCameras, + )), + (Route route) => false); + } + } else { + setState(() { + loading = false; + error = true; + }); + } + + setState(() { + loading = false; + }); + } + + void reGenerateActivation() async { + setState(() { + loading = true; + }); + + Map requestHeaders = { + 'Content-type': 'application/json', + }; + + var register = serverLocation + 'api/activationgenerator'; + await http.post(Uri.parse(register), + body: json.encode(user.user), headers: requestHeaders); + setState(() { + loading = false; + }); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text("Account Verification"), + ), + body: Container( + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + children: [ + Container( + padding: EdgeInsets.all(10), + width: double.infinity, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.02, + top: MediaQuery.of(context).size.height * 0.02, + ), + child: Center( + child: Text( + "Enter the verification code we sent to your email " + + user.user.email + + " to activate your account.", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + SizedBox( + height: 10, + ), + inputCode(context, code), + SizedBox(height: MediaQuery.of(context).size.height * 0.001), + Visibility( + visible: loading, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + Visibility( + visible: error, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.9, + child: Center( + child: Text( + "An error occurred while verifying account try again!!", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.red, + ), + ), + ), + ), + ), + Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.6, + height: 40.0, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: () => verifyAccount(), + child: Center( + child: Text( + "Continue", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + )), + ), + ), + SizedBox(height: MediaQuery.of(context).size.height * 0.001), + InkWell( + onTap: reGenerateActivation, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.9, + child: Center( + child: Text( + "Didn't receive the code ? Click to resend", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.blueAccent, + ), + ), + ), + ), + ), + ], + ), + ), + )); + } +} diff --git a/lib/Pages/Sub_Pages/Notifications/Alerts.dart b/lib/Pages/Sub_Pages/Notifications/Alerts.dart new file mode 100644 index 0000000..ba7f76f --- /dev/null +++ b/lib/Pages/Sub_Pages/Notifications/Alerts.dart @@ -0,0 +1,868 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; + +import 'package:flutter/cupertino.dart'; +import 'package:teso/Classes/API%20Clasess/CouponHead.dart'; +import 'package:teso/Classes/Payload.dart'; +import 'package:teso/Pages/PageWidgets/Alerts/AlertTile.dart'; +import 'package:teso/Pages/PageWidgets/Alerts/Redeemable.dart'; +import 'package:teso/Pages/PageWidgets/Alerts/Refund.dart'; +import 'package:teso/Pages/PageWidgets/Alerts/personalizedCoupon.dart'; +import 'package:teso/Pages/PageWidgets/Alerts/post.dart'; +import 'package:teso/Pages/PageWidgets/Alerts/requestTile.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/util/consts.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'dart:convert'; +import 'package:teso/Notifications/NotificationPlugin.dart'; + +class Alerts extends StatefulWidget { + @override + _AlertsState createState() => _AlertsState(); +} + +class _AlertsState extends State { + List listMessage = new List.from([]); + String id = ""; + int _limit = 20; + final int _limitIncrement = 20; + final ScrollController listScrollController = ScrollController(); + SharedPreferences prefs; + bool loading = false; + + Icon iconIndicator(String type) { + switch (type) { + case "alerts": + return Icon( + Icons.notifications, + color: Colors.grey[50], + ); + break; + case "funds": + return Icon( + Icons.wallet_giftcard_outlined, + color: Colors.grey[50], + ); + break; + case "redeemable": + return Icon( + Icons.redeem, + color: Colors.grey[50], + ); + break; + case "gifted": + return Icon( + Icons.redeem, + color: Colors.grey[50], + ); + break; + case "warning": + return Icon( + Icons.warning, + color: Colors.grey[50], + ); + break; + case "friendapproval": + return Icon( + Icons.people_alt, + color: Colors.grey[50], + ); + break; + default: + return Icon( + Icons.notifications, + color: Colors.grey[50], + ); + break; + } + } + + setTitle(value) { + switch (value) { + case "friendrequest": + return "Friend Request"; + default: + return ""; + } + } + + setDescription(value) { + switch (value) { + case "friendrequest": + return " sent you a friend request"; + case "friendapproval": + return " accepted your friend request !!!"; + default: + return ""; + } + } + + _scrollListener() { + if (listScrollController.offset >= + listScrollController.position.maxScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the bottom"); + setState(() { + print("reach the bottom"); + _limit += _limitIncrement; + }); + } + if (listScrollController.offset <= + listScrollController.position.minScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the top"); + setState(() { + print("reach the top"); + }); + } + } + + void initState() { + listScrollController.addListener(_scrollListener); + SharedPreferences.getInstance().then((value) => id = value.getString("id")); + super.initState(); + } + + void approveRelationship(userGUID) async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = userGUID; + var register = serverLocation + 'relationships/friendapproval'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + setState(() {}); + Provider.of(context, listen: false).loadFriends(); + } else { + setState(() {}); + } + } + + void declineRelationship(userGUID) async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = userGUID; + var register = serverLocation + 'relationships/frienddecline'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + setState(() {}); + Provider.of(context, listen: false).loadFriends(); + } else { + setState(() {}); + } + } + + void acceptCoupon(CouponsHead head) async { + setState(() { + loading = true; + }); + try { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'coupons/acceptPersonalized'; + var client = await http.post(Uri.parse(register), + body: json.encode(head), headers: requestHeaders); + if (client.statusCode == 200) { + } else if (client.statusCode == 400) { + Payload payload = new Payload(); + payload.loadID = "TESN000"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Offer Expired", + "Unable to acquire coupon as this offer has expired!!", + payload.toString(), + ); + } else { + Payload payload = new Payload(); + payload.loadID = "TESN000"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Error Occurred", + "Unable to acquire coupon as this moment", + payload.toString(), + ); + } + } catch (e) { + print(e); + Payload payload = new Payload(); + payload.loadID = "TESN000"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Error Occurred", + "Unable to acquire coupon as this moment", + payload.toString(), + ); + } + setState(() { + loading = false; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: !loading + ? Consumer( + builder: + (BuildContext context, UserProvider value, Widget child) { + if (value != null) { + id = value.currentUser.userGUID; + return StreamBuilder( + stream: FirebaseFirestore.instance + .collection('notifications') + .doc(id != null ? id : "") + .collection(id != null ? id : "") + .orderBy('timestamp', descending: true) + .limit(_limit) + .snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else if (snapshot.data == null) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else { + listMessage = snapshot.data.docs; + return ListView.builder( + // controller: listScrollController, + itemCount: listMessage.length, + itemBuilder: (BuildContext context, int index) { + int timeInMillis = int.parse(snapshot + .data.docs[index]['timestamp'] + .toString()); + DateTime date = DateTime.fromMillisecondsSinceEpoch( + timeInMillis); + + switch (listMessage[index]["notificationType"]) { + case "friendrequest": + return buildRequest( + context: context, + approve: () => approveRelationship( + listMessage[index]["initiatorID"]), + decline: () => declineRelationship( + listMessage[index]["initiatorID"]), + timestamp: date, + description: listMessage[index] + ["initiatorUsername"] + + setDescription(listMessage[index] + ["notificationType"]), + thumbnail: listMessage[index] + ["initiatorThumbnail"], + username: listMessage[index] + ["initiatorUsername"], + icons: iconIndicator( + listMessage[index]["notificationType"], + ), + ); + break; + case "redeemable": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index]["couponID"]), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildRedeemableAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + thumbnail: listMessage[index] + ["productImage"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "expired": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index]["couponID"]), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildRedeemableAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + thumbnail: listMessage[index] + ["productImage"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "gifted": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index]["couponID"]), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildRedeemableAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + thumbnail: listMessage[index] + ["productImage"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "likes": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index]["timestamp"] + .toString()), + background: Container( + //height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildAlert( + context: context, + timestamp: date, + username: listMessage[index]["message"], + description: listMessage[index] + ["message"] + + setDescription(listMessage[index] + ["notificationType"]), + thumbnail: listMessage[index] + ["thumbnail"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "comments": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index]["timestamp"] + .toString()), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildAlert( + context: context, + timestamp: date, + username: listMessage[index]["message"], + description: listMessage[index] + ["message"] + + setDescription(listMessage[index] + ["notificationType"]), + thumbnail: listMessage[index] + ["thumbnail"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "personalized": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: buildPersonalizedAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + thumbnail: listMessage[index] + ["productImage"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + decline: () => FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }), + accept: () { + var coup = jsonDecode( + listMessage[index]["coupon"]); + CouponsHead head = new CouponsHead(); + head.businessId = coup["BusinessId"]; + head.couponId = + listMessage[index].reference.id; + head.expiration = DateTime.parse( + coup["Expiration"].toString()); + head.lower = coup["LowerLimit"]; + head.quantity = 1; + head.state = listMessage[index] + ["couponCondition"]; + head.targetProduct = + coup["TargetProduct"]; + head.type = coup["Type"]; + head.upper = coup["UpperLimit"]; + head.generated = DateTime.parse( + coup["Generated"].toString()); + acceptCoupon(head); + }), + ); + break; + case "cancellation": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index].reference.id), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildRedeemableAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + thumbnail: listMessage[index] + ["productImage"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "refund": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index].reference.id), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildRefundAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "welcome": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index].reference.id), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildRefundAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "adStatus": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index].reference.id), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildPostAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + thumbnail: listMessage[index] + ["assetID"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + case "desire": + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key(listMessage[index]["couponID"]), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildRedeemableAlert( + context: context, + timestamp: date, + message: listMessage[index]["message"], + thumbnail: listMessage[index] + ["productImage"], + icons: iconIndicator(listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + break; + default: + try { + return Padding( + padding: + EdgeInsets.symmetric(vertical: 8.0), + child: Dismissible( + key: Key( + listMessage[index]["relationID"]), + background: Container( + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + darkAccent, + accentMain, + ], + ), + ), + child: Center( + child: Text( + "Cleared!!!!", + style: TextStyle( + color: Colors.white, + fontSize: 25), + ), + ), + ), + child: buildAlert( + context: context, + timestamp: date, + username: listMessage[index] + ["initiatorUsername"], + description: listMessage[index] + ["initiatorUsername"] + + setDescription(listMessage[index] + ["notificationType"]), + thumbnail: listMessage[index] + ["initiatorThumbnail"], + icons: iconIndicator( + listMessage[index] + ["notificationType"]), + ), + onDismissed: (direction) { + FirebaseFirestore.instance + .runTransaction( + (transaction) async { + transaction.delete( + listMessage[index].reference); + }); + }), + ); + } catch (e) { + print(e); + return Container(); + } + break; + } + }, + ); + } + }, + ); + } else { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } + }, + ) + : Container( + padding: + EdgeInsets.only(top: MediaQuery.of(context).size.width * 0.7), + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Center( + child: Column( + children: [ + CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + Text("Processing request....."), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Notifications/ChatScreen.dart b/lib/Pages/Sub_Pages/Notifications/ChatScreen.dart new file mode 100644 index 0000000..bcdc4a5 --- /dev/null +++ b/lib/Pages/Sub_Pages/Notifications/ChatScreen.dart @@ -0,0 +1,345 @@ +import 'dart:async'; + +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Classes/ChatMessage.dart'; +import 'package:teso/Pages/PageWidgets/ChatScreen/bottomBar.dart'; +import 'package:teso/Pages/PageWidgets/ChatScreen/header.dart'; +import 'package:teso/Pages/PageWidgets/ChatScreen/recipient.dart'; +import 'package:teso/Pages/PageWidgets/ChatScreen/sender.dart'; +import 'package:flutter/material.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'dart:io'; +import 'dart:convert'; +import 'dart:ui'; +import 'package:intl/intl.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; + +class ChatScreen extends StatefulWidget { + final TesoUser user; + + const ChatScreen({Key key, this.user}) : super(key: key); + @override + _ChatScreenState createState() => _ChatScreenState(user: this.user); +} + +class _ChatScreenState extends State { + final datakey = new GlobalKey(); + TesoUser user; + _ChatScreenState({this.user}); + TextEditingController controller = new TextEditingController(); + bool first = true; + String id; + + List listMessage = new List.from([]); + int _limit = 20; + final int _limitIncrement = 20; + String groupChatId; + SharedPreferences prefs; + final ScrollController listScrollController = ScrollController(); + File imageFile; + bool isLoading; + bool isShowSticker; + String imageUrl; + TesoUser currentUser; + Future _future; + Timer timer; + int counter = 0; + + _scrollListener() { + if (listScrollController.offset >= + listScrollController.position.maxScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the bottom"); + setState(() { + print("reach the bottom"); + _limit += _limitIncrement; + }); + } + if (listScrollController.offset <= + listScrollController.position.minScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the top"); + setState(() { + print("reach the top"); + }); + } + } + + void sendMessage(String content, int type) { + if (content.trim() != '') { + controller.clear(); + + var documentReference = FirebaseFirestore.instance + .collection('messages') + .doc(groupChatId) + .collection(groupChatId) + .doc(DateTime.now().millisecondsSinceEpoch.toString()); + + FirebaseFirestore.instance.runTransaction((transaction) async { + transaction.set( + documentReference, + { + 'idFrom': id, + 'idTo': user.userGUID, + 'timestamp': DateTime.now().millisecondsSinceEpoch.toString(), + 'content': content.trim(), + 'type': type, + 'read': false + }, + ); + }); + + var inboxReference1 = FirebaseFirestore.instance + .collection('inbox') + .doc(id) + .collection("lastMessage") + .doc(groupChatId); + + FirebaseFirestore.instance.runTransaction((transaction) async { + transaction.set( + inboxReference1, + { + 'senderID': id, + 'peerID': user.userGUID, + 'timestamp': DateTime.now().millisecondsSinceEpoch.toString(), + 'content': content.trim(), + 'firstname': user.firstname, + 'surname': user.lastname, + 'thumbnail': user.thumbnail_dp, + 'username': user.username, + 'read': false + }, + ); + }); + var inboxReference2 = FirebaseFirestore.instance + .collection('inbox') + .doc(user.userGUID) + .collection("lastMessage") + .doc(groupChatId); + + FirebaseFirestore.instance.runTransaction((transaction) async { + transaction.set( + inboxReference2, + { + 'senderID': id, + 'peerID': id, + 'timestamp': DateTime.now().millisecondsSinceEpoch.toString(), + 'content': content.trim(), + 'firstname': currentUser.firstname, + 'surname': currentUser.lastname, + 'thumbnail': currentUser.thumbnail_dp, + 'username': currentUser.username, + 'read': false + }, + ); + }); + listScrollController.animateTo(0.0, + duration: Duration(milliseconds: 300), curve: Curves.easeOut); + } else { + // Fluttertoast.showToast( + // msg: 'Nothing to send', + // backgroundColor: Colors.black, + // textColor: Colors.red); + } + } + + void readMessage(DocumentReference reference) { + reference.update({"read": true}); + + FirebaseFirestore.instance + .collection('inbox') + .doc(id) + .collection("lastMessage") + .doc(groupChatId) + .update({"read": true}); + + FirebaseFirestore.instance + .collection('inbox') + .doc(user.userGUID) + .collection("lastMessage") + .doc(groupChatId) + .update({"read": true}); + } + + @override + void initState() { + super.initState(); + readLocal(); + listScrollController.addListener(_scrollListener); + controller.addListener(() { + if (controller.text.isNotEmpty) { + isTyping(true); + } else { + isTyping(false); + } + }); + currentUser = Provider.of(context, listen: false).currentUser; + _future = findUser(); + timer = Timer.periodic(Duration(seconds: 30), (Timer t) => addValue()); + } + + void addValue() { + setState(() { + counter++; + }); + } + + readLocal() async { + prefs = await SharedPreferences.getInstance(); + id = prefs.getString('id') ?? ''; + if (id.hashCode <= user.userGUID.hashCode) { + groupChatId = id + user.userGUID; + } else { + groupChatId = user.userGUID + id; + } + FirebaseFirestore.instance + .collection('users') + .doc(id) + .update({'chattingWith': user.userGUID, 'typing': false}); + + setState(() {}); + } + + void isTyping(bool value) { + FirebaseFirestore.instance + .collection('users') + .doc(id) + .update({'chattingWith': user.userGUID, 'typing': value}); + } + + Future findUser() async { + TesoUser loadedUser; + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + String auth = user.userGUID; + var register2 = serverLocation + 'users/finduser'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(auth), headers: requestHeaders); + + if (client1.statusCode == 200) { + Map handler = jsonDecode(client1.body); + TesoUser tokenHandler = TesoUser.fromJSON(handler); + setState(() { + user = tokenHandler; + loadedUser = user; + }); + } + + return loadedUser; + } + + @override + void dispose() { + listScrollController.dispose(); + controller.dispose(); + timer?.cancel(); + FirebaseFirestore.instance + .collection('users') + .doc(id) + .update({'chattingWith': "", 'typing': false}); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(90), + child: FutureBuilder( + future: _future, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData == null || + snapshot.connectionState == ConnectionState.waiting) { + return buildChatHead(context, user); + } else { + return buildChatHead(context, user); + } + }, + ), + ), + body: Container( + height: MediaQuery.of(context).size.height, + child: Stack( + children: [ + Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only(bottom: 60), + child: StreamBuilder( + stream: FirebaseFirestore.instance + .collection('messages') + .doc(groupChatId) + .collection(groupChatId) + .orderBy('timestamp', descending: true) + .limit(_limit) + .snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else if (snapshot.data == null || listMessage == null) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else { + listMessage = snapshot.data.docs; + return ListView.builder( + padding: EdgeInsets.all(10.0), + itemCount: listMessage.length, + itemBuilder: (context, index) { + if (snapshot.data.docs[index].data()['idTo'] == id && + snapshot.data.docs[index].data()['read'] == false) { + readMessage(listMessage[index].reference); + } + int timeInMillis = int.parse( + snapshot.data.docs[index].data()['timestamp']); + var date = + DateTime.fromMillisecondsSinceEpoch(timeInMillis); + var formattedDate = + DateFormat("yyyy-MM-dd HH:mm:ss").format(date); + ChatMessage message = new ChatMessage(); + message.idFrom = + snapshot.data.docs[index].data()['idFrom']; + message.content = + snapshot.data.docs[index].data()['content']; + message.idTo = snapshot.data.docs[index].data()['idTo']; + message.timestamp = DateTime.parse(formattedDate); + message.type = int.parse(snapshot.data.docs[index] + .data()['type'] + .toString()); + if (snapshot.data.docs[index].data()['idFrom'] == id) { + return buildSender(context, message); + } else { + return buildRecipient(context, message); + } + }, + reverse: true, + controller: listScrollController, + ); + } + }, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: buildBottom(context, controller, sendMessage), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Notifications/Inbox.dart b/lib/Pages/Sub_Pages/Notifications/Inbox.dart new file mode 100644 index 0000000..89d5fe2 --- /dev/null +++ b/lib/Pages/Sub_Pages/Notifications/Inbox.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:typed_data'; +import 'package:intl/intl.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Classes/inbox.dart'; +import 'package:teso/Pages/PageWidgets/Inbox/inboxTile.dart'; +import 'package:teso/Pages/Sub_Pages/Notifications/ChatScreen.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:flutter/material.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter/cupertino.dart'; + +class Inbox extends StatefulWidget { + final TesoUser user; + + const Inbox({Key key, this.user}) : super(key: key); + @override + _InboxState createState() => _InboxState(user: this.user); +} + +class _InboxState extends State { + final TesoUser user; + TesoUser currentUser = new TesoUser(); + TextEditingController controller = new TextEditingController(); + Uint8List bytes; + List listMessage = new List.from([]); + String id; + SharedPreferences prefs; + Timer timer; + int counter = 0; + + _InboxState({this.user}); + + @override + void initState() { + readLocal(); + super.initState(); + timer = Timer.periodic(Duration(seconds: 30), (Timer t) => addValue()); + } + + void addValue() { + setState(() { + counter++; + }); + } + + @override + void dispose() { + controller.dispose(); + timer?.cancel(); + super.dispose(); + } + + readLocal() async { + prefs = await SharedPreferences.getInstance(); + id = prefs.getString('id') ?? ''; + setState(() {}); + } + + @override + Widget build(BuildContext context) { + currentUser = Provider.of(context, listen: false).currentUser; + return Scaffold( + body: StreamBuilder( + stream: FirebaseFirestore.instance + .collection('inbox') + .doc(id) + .collection("lastMessage") + .orderBy("timestamp", descending: false) + .snapshots(), + builder: (context, snapshot) { + if (snapshot.data == null) { + return Container( + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } else { + listMessage = snapshot.data.docs; + return ListView.builder( + padding: EdgeInsets.all(5.0), + itemCount: listMessage.length, + itemBuilder: (context, index) { + int timeInMillis = + int.parse(snapshot.data.docs[index].data()['timestamp']); + var date = DateTime.fromMillisecondsSinceEpoch(timeInMillis); + var formattedDate = + DateFormat("yyyy-MM-dd HH:mm:ss").format(date); + TesoUser username = new TesoUser(); + username.userGUID = + snapshot.data.docs[index].data()['peerID']; + username.firstname = + snapshot.data.docs[index].data()['firstname']; + username.lastname = + snapshot.data.docs[index].data()['surname']; + username.thumbnail_dp = + snapshot.data.docs[index].data()['thumbnail']; + username.username = + snapshot.data.docs[index].data()['username']; + + InboxMessage message = new InboxMessage(); + message.bio = ""; + message.userID = snapshot.data.docs[index].data()['peerID']; + message.firstname = + snapshot.data.docs[index].data()['firstname']; + message.surname = snapshot.data.docs[index].data()['surname']; + message.thumbnail = + snapshot.data.docs[index].data()['thumbnail']; + message.message = snapshot.data.docs[index].data()['content']; + message.timestamp = DateTime.parse(formattedDate); + message.messageID = listMessage[index].reference.toString(); + bool status; + if (snapshot.data.docs[index].data()['senderID'] == id) { + status = true; + } else if (snapshot.data.docs[index].data()['senderID'] != + id && + !listMessage[index]['read']) { + status = false; + } else { + status = true; + } + return InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: ChatScreen( + user: username, + ), + type: PageTransitionType.leftToRightWithFade), + ), + child: buildInboxTile(context, message, status), + ); + }, + ); + } + }), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Notifications/newMessage.dart b/lib/Pages/Sub_Pages/Notifications/newMessage.dart new file mode 100644 index 0000000..c9fba86 --- /dev/null +++ b/lib/Pages/Sub_Pages/Notifications/newMessage.dart @@ -0,0 +1,126 @@ +import 'package:provider/provider.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/Friends/friendTile.dart'; +import 'package:teso/Pages/PageWidgets/Inbox/newMessageHeader.dart'; +import 'package:teso/Pages/Sub_Pages/Notifications/ChatScreen.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Friends/header.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:convert'; + +class NewMessage extends StatefulWidget { + @override + _NewMessageState createState() => _NewMessageState(); +} + +class _NewMessageState extends State { + TextEditingController searchkey; + List recipientMain = []; + List recipient; + SharedPreferences prefs; + + void clearText() { + setState(() { + // searchkey.clear(); + // recipient = recipientMain; + // recipient.sort((a, b) { + // return b.userID.compareTo(a.userID); + // }); + }); + } + + @override + void initState() { + searchkey = new TextEditingController(); + Provider.of(context, listen: false).loadFriends(); + super.initState(); + } + + Future loadFriends() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'relationships/friends'; + var client = await http.get(Uri.parse(register), headers: requestHeaders); + if (client.statusCode == 200) { + var people = jsonDecode(client.body); + recipientMain = List.from( + people.map((model) => TesoUser.fromJSON(model)).toList()); + } + + recipient = recipientMain; + recipient.sort((a, b) { + return b.firstname.compareTo(a.firstname); + }); + } + + updateList(String name) { + setState(() { + recipient = recipientMain + .where((element) => + element.username.toLowerCase().contains(name.toLowerCase())) + .toList(); + recipient.sort((a, b) { + return b.firstname.compareTo(a.firstname); + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(150), + child: Container( + margin: EdgeInsets.only(top: 30), + child: Column(children: [ + buildNewHead(context, clearText, "New Message"), + buildFriendsHeader(context, searchkey, updateList), + ]), + ), + ), + body: Consumer( + builder: (context, value, child) { + if (value.friends == null) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor), + ), + ); + } else { + if (recipientMain.length == 0) { + recipientMain = value.friends; + recipient = value.friends; + } + recipient.sort((a, b) { + return b.firstname.compareTo(a.firstname); + }); + return ListView.builder( + itemCount: recipient.length, + itemBuilder: (context, int index) { + return InkWell( + onTap: () => Navigator.pushReplacement( + context, + PageTransition( + child: ChatScreen(user: recipient.elementAt(index)), + type: PageTransitionType.fade, + ), + ), + child: buildFriend(context, recipient.elementAt(index)), + ); + }, + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Payments/PaymentView.dart b/lib/Pages/Sub_Pages/Payments/PaymentView.dart new file mode 100644 index 0000000..3868ca5 --- /dev/null +++ b/lib/Pages/Sub_Pages/Payments/PaymentView.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; + +class PaymentView extends StatefulWidget { + final String selectedUrl; + + PaymentView({this.selectedUrl}); + + @override + _PaymentViewState createState() => _PaymentViewState(); +} + +class _PaymentViewState extends State { + final flutterWebviewPlugin = new FlutterWebviewPlugin(); + + @override + void initState() { + super.initState(); + flutterWebviewPlugin.onBack.listen((event) { + Navigator.pop(context); + }); + flutterWebviewPlugin.onUrlChanged.listen((String url) { + print(url); + if (url.contains("#access_token")) { + succeed(url); + } + + if (url.contains( + "https://www.facebook.com/connect/login_success.html?error=access_denied&error_code=200&error_description=Permissions+error&error_reason=user_denied")) { + denied(); + } + if (url.contains("https://expresspaygh.com/retry_later.php")) { + denied(); + } + }); + } + + denied() { + Navigator.pop(context); + } + + succeed(String url) { + var params = url.split("access_token="); + + var endparam = params[1].split("&"); + flutterWebviewPlugin.clearCache(); + flutterWebviewPlugin.cleanCookies(); + flutterWebviewPlugin.dispose(); + Navigator.pop(context, endparam[0]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + height: MediaQuery.of(context).size.height, + margin: EdgeInsets.only(top: 10), + child: WebviewScaffold( + url: widget.selectedUrl, + scrollBar: true, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Payments/Withdrawal.dart b/lib/Pages/Sub_Pages/Payments/Withdrawal.dart new file mode 100644 index 0000000..27cfc0d --- /dev/null +++ b/lib/Pages/Sub_Pages/Payments/Withdrawal.dart @@ -0,0 +1,292 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Pages/PageWidgets/CoinPurchase/MomoType.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; + +class WithdrawalPage extends StatefulWidget { + final String momonumber; + final String amount; + final String provider; + const WithdrawalPage({Key key, this.momonumber, this.amount, this.provider}) + : super(key: key); + + @override + _WithdrawalPageState createState() => _WithdrawalPageState(); +} + +class _WithdrawalPageState extends State { + String processing = "Processing request, please wait"; + bool confirmBox = false; + String recipientName = ""; + String currency; + String image; + + submit() async { + String num = widget.momonumber; + String provider = widget.provider; + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register2 = + paymentServer + "withdrawals/getAccountInfo/$provider/account/$num"; + var client1 = + await http.get(Uri.parse(register2), headers: requestHeaders); + if (client1.statusCode == 200) { + var parsedJson = json.decode(client1.body); + if (parsedJson["status-text"] == "Success") { + setState(() { + confirmBox = true; + recipientName = parsedJson["account-info"]; + currency = parsedJson["options"][0]["currency"]; + }); + } else if (parsedJson["status"] == 4) { + setState(() { + confirmBox = false; + processing = parsedJson["status-text"]; + }); + } else { + setState(() { + confirmBox = false; + processing = "Request Failed, please try again later !!!"; + }); + } + } + } catch (e) {} + if (!confirmBox) + Future.delayed(Duration(seconds: 5), () => Navigator.pop(context)); + } + + withdraw() async { + setState(() { + confirmBox = false; + }); + String num = widget.momonumber; + String withdraw = widget.amount; + String provider = widget.provider; + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + Map body = { + "provider": provider, + "number": num, + "amount": withdraw, + }; + var register2 = paymentServer + "withdrawals/cashout"; + var client1 = await http.post( + Uri.parse(register2), + headers: requestHeaders, + body: json.encode(body), + ); + if (client1.statusCode == 200) { + if (client1.body == "Payment Made") { + setState(() { + confirmBox = false; + processing = "Payment made successfully"; + }); + } else if (client1.body == "Payment Pending") { + setState(() { + confirmBox = false; + processing = "Payment submitted successfully, " + + "funds maybe transferred to the specified as soon as possible"; + }); + } + } else { + if (client1.body == "Insufficient Gold") { + setState(() { + confirmBox = false; + processing = + "Insufficient Gold coins, you need to have more than 9 gold coins to make a withdrawal"; + }); + } else { + setState(() { + confirmBox = false; + processing = "Request Failed, please try again later !!!"; + }); + } + } + } catch (e) {} + Provider.of(context, listen: false).getUserInformation(); + if (!confirmBox) + Future.delayed(Duration(seconds: 5), () => Navigator.pop(context)); + } + + @override + void initState() { + switch (widget.provider) { + case "VODAFONE": + setState(() { + image = "assets/images/Vodafone.png"; + }); + break; + case "MTN": + setState(() { + image = "assets/images/MTN.png"; + }); + break; + case "AIRTEL": + setState(() { + image = "assets/images/AirtelTigo.png"; + }); + break; + } + submit(); + super.initState(); + } + + select(s) {} + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: !confirmBox ? pendingCenter(context) : confirmCenter(context), + ), + ); + } + + Widget pendingCenter(BuildContext context) { + return Container( + width: 200, + height: 200, + child: Column( + children: [ + Container( + child: processing == "Processing request, please wait" + ? CupertinoActivityIndicator( + animating: true, + radius: 15, + ) + : processing.contains("successfully") + ? Icon( + Icons.check_circle, + color: Colors.green, + size: SizeConfig.safeBlockHorizontal * 20, + ) + : Icon( + Icons.error, + color: Colors.red, + size: SizeConfig.safeBlockHorizontal * 30, + ), + ), + SizedBox( + height: 10, + ), + Container( + child: Text( + processing, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: SizeConfig.safeBlockHorizontal * 3.8, + ), + ), + ), + ], + ), + ); + } + + Widget confirmCenter(BuildContext context) { + SizeConfig().init(context); + return Container( + width: SizeConfig.safeBlockHorizontal * 72, + height: SizeConfig.safeBlockVertical * 42, + child: Container( + width: SizeConfig.safeBlockHorizontal * 72, + height: SizeConfig.safeBlockVertical * 42, + child: Column( + children: [ + SizedBox( + height: 20, + ), + buildType(context, image, widget.provider, select, widget.provider), + SizedBox( + height: 20, + ), + Container( + child: Text( + "You requested to withdraw $currency ${widget.amount} to $recipientName on ${widget.momonumber}", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: SizeConfig.safeBlockHorizontal * 3.8, + ), + ), + ), + SizedBox( + height: 20, + ), + new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Color(0xFFb90222), + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => Navigator.pop(context), + child: new Container( + height: 20.0, + width: 70.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Color(0xFFb90222), + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Cancel", + style: new TextStyle(color: Colors.white), + ), + ), + ), + SizedBox( + width: 10, + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green, + padding: new EdgeInsets.all(10.0), + ), + onPressed: withdraw, + child: new Container( + height: 20.0, + width: 70.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Confirm", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/PersonalSub/Categories.dart b/lib/Pages/Sub_Pages/PersonalSub/Categories.dart new file mode 100644 index 0000000..95868a0 --- /dev/null +++ b/lib/Pages/Sub_Pages/PersonalSub/Categories.dart @@ -0,0 +1,602 @@ +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; + +import 'package:provider/provider.dart'; +import 'package:flutter/cupertino.dart'; + +class Categories extends StatefulWidget { + @override + _CategoriesState createState() => _CategoriesState(); +} + +class _CategoriesState extends State { + Color antiques = Colors.white; + Color agric = Colors.white; + Color auto = Colors.white; + Color bags = Colors.white; + Color electronics = Colors.white; + Color clothing = Colors.white; + Color gifts = Colors.white; + Color food = Colors.white; + Color home = Colors.white; + Color pet = Colors.white; + Color machinery = Colors.white; + Color health = Colors.white; + List favorites = []; + bool fresh = true; + + @override + void initState() { + super.initState(); + } + + void setFavorites(String id) { + switch (id) { + case "TECAT001": + setState(() { + if (!favorites.contains("TECAT001") && fresh) { + antiques = accentMain; + favorites.add("TECAT001"); + } else if (!favorites.contains("TECAT001")) { + antiques = accentMain; + favorites.add("TECAT001"); + } else { + favorites.remove("TECAT001"); + antiques = Colors.white; + } + }); + break; + case "TECAT002": + setState(() { + if (!favorites.contains("TECAT002") && fresh) { + agric = accentMain; + favorites.add("TECAT002"); + } else if (!favorites.contains("TECAT002")) { + agric = accentMain; + favorites.add("TECAT002"); + } else { + favorites.remove("TECAT002"); + agric = Colors.white; + } + }); + break; + case "TECAT003": + setState(() { + if (!favorites.contains("TECAT003") && fresh) { + auto = accentMain; + favorites.add("TECAT003"); + } else if (!favorites.contains("TECAT003")) { + auto = accentMain; + favorites.add("TECAT003"); + } else { + favorites.remove("TECAT003"); + auto = Colors.white; + } + }); + break; + case "TECAT004": + setState(() { + if (!favorites.contains("TECAT004") && fresh) { + bags = accentMain; + favorites.add("TECAT004"); + } else if (!favorites.contains("TECAT004")) { + bags = accentMain; + favorites.add("TECAT004"); + } else { + favorites.remove("TECAT004"); + bags = Colors.white; + } + }); + break; + case "TECAT005": + setState(() { + if (!favorites.contains("TECAT005") && fresh) { + clothing = accentMain; + favorites.add("TECAT005"); + } else if (!favorites.contains("TECAT005")) { + clothing = accentMain; + favorites.add("TECAT005"); + } else { + favorites.remove("TECAT005"); + clothing = Colors.white; + } + }); + break; + case "TECAT006": + setState(() { + if (!favorites.contains("TECAT006") && fresh) { + electronics = accentMain; + favorites.add("TECAT006"); + } else if (!favorites.contains("TECAT006")) { + electronics = accentMain; + favorites.add("TECAT006"); + } else { + favorites.remove("TECAT006"); + electronics = Colors.white; + } + }); + break; + case "TECAT007": + setState(() { + if (!favorites.contains("TECAT007") && fresh) { + gifts = accentMain; + favorites.add("TECAT007"); + } else if (!favorites.contains("TECAT007")) { + gifts = accentMain; + favorites.add("TECAT007"); + } else { + favorites.remove("TECAT007"); + gifts = Colors.white; + } + }); + break; + case "TECAT008": + setState(() { + if (!favorites.contains("TECAT008") && fresh) { + food = accentMain; + favorites.add("TECAT008"); + } else if (!favorites.contains("TECAT008")) { + food = accentMain; + favorites.add("TECAT008"); + } else { + favorites.remove("TECAT008"); + food = Colors.white; + } + }); + break; + case "TECAT009": + setState(() { + if (!favorites.contains("TECAT009") && fresh) { + home = accentMain; + favorites.add("TECAT009"); + } else if (!favorites.contains("TECAT009")) { + home = accentMain; + favorites.add("TECAT009"); + } else { + favorites.remove("TECAT009"); + home = Colors.white; + } + }); + break; + case "TECAT010": + setState(() { + if (!favorites.contains("TECAT010") && fresh) { + pet = accentMain; + favorites.add("TECAT010"); + } else if (!favorites.contains("TECAT010")) { + pet = accentMain; + favorites.add("TECAT010"); + } else { + favorites.remove("TECAT010"); + pet = Colors.white; + } + }); + break; + case "TECAT011": + setState(() { + if (!favorites.contains("TECAT011") && fresh) { + machinery = accentMain; + favorites.add("TECAT011"); + } else if (!favorites.contains("TECAT011")) { + machinery = accentMain; + favorites.add("TECAT011"); + } else { + favorites.remove("TECAT011"); + machinery = Colors.white; + } + }); + break; + case "TECAT012": + setState(() { + if (!favorites.contains("TECAT012") && fresh) { + health = accentMain; + favorites.add("TECAT012"); + } else if (!favorites.contains("TECAT012")) { + health = accentMain; + favorites.add("TECAT012"); + } else { + favorites.remove("TECAT012"); + health = Colors.white; + } + }); + break; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: Container( + width: double.infinity, + padding: EdgeInsets.only(top: 40, left: 10), + child: Column( + children: [ + Align( + alignment: Alignment.topLeft, + child: Container( + height: 40, + width: 40, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + color: accentMain, + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: IconButton( + onPressed: () => Navigator.pop(context), + icon: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + ), + ), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + width: 60, + height: 80, + decoration: new BoxDecoration( + shape: BoxShape.circle, + //color: Colors.grey, + ), + child: Image( + height: 60, + width: 100, + fit: BoxFit.fill, + image: AssetImage("assets/images/tesoCouponInsignia.png"), + ), + ), + ), + Container( + margin: EdgeInsets.only(top: 10), + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "Your Interests", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 21, + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "What are you interested in ?", + style: TextStyle( + fontSize: 12, + ), + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "Choose one or more categories you are interested in !!", + style: TextStyle( + fontSize: 12, + ), + ), + ), + ), + ], + ), + ), + preferredSize: Size.fromHeight(218)), + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Consumer( + builder: (context, value, child) { + if (value == null) { + return Container( + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } else { + if (fresh) { + if (value.interest != null) + value.interest.forEach((element) { + Future.delayed(Duration.zero, () async { + setFavorites(element); + }); + }); + fresh = false; + } + return Container( + width: double.infinity, + padding: EdgeInsets.only(top: 0, left: 10), + margin: EdgeInsets.only( + bottom: 100, + ), + child: Column( + children: [ + new Wrap( + children: [ + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: agric, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT002"), + child: Text( + "Agriculture", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: auto, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT003"), + child: Text( + "Auto & Transportation", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: bags, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT004"), + child: Text( + "Bags, Shoes & Accessories", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: clothing, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT005"), + child: Text( + "Clothing & Accessories", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: electronics, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT006"), + child: Text( + "Electronics", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: gifts, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT007"), + child: Text( + "Gifts, Sports & Toys", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: health, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT012"), + child: Text( + "Health & Beauty", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: food, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT008"), + child: Text( + "Food", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: home, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT009"), + child: Text( + "Home & Garden", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: pet, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT010"), + child: Text( + "Pet Supplies", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: antiques, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT001"), + child: Text( + "Antiques", + style: TextStyle(color: Colors.black), + ), + ), + ), + Container( + height: 40, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(7), + decoration: BoxDecoration( + color: machinery, + borderRadius: + BorderRadius.all(Radius.circular(20.0)), + border: Border.all( + color: accentMain, + )), + child: InkWell( + onTap: () => setFavorites("TECAT011"), + child: Text( + "Machinery, Industrial Parts & Tools", + style: TextStyle(color: Colors.black), + ), + ), + ), + ], + ), + ], + ), + ); + } + }, + ), + ), + floatingActionButton: Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.only( + top: 10, + ), + width: MediaQuery.of(context).size.width / 1.4, + height: 40, + decoration: new BoxDecoration( + shape: BoxShape.circle, + //color: Colors.grey, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: accentMain, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20.0), + ), + ), + ), + onPressed: () { + Provider.of(context, listen: false) + .updateFavoriteCategories(favorites); + }, + child: Text( + "Save", + style: TextStyle(color: Colors.white), + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/PersonalSub/CompleteProfile.dart b/lib/Pages/Sub_Pages/PersonalSub/CompleteProfile.dart new file mode 100644 index 0000000..7af3259 --- /dev/null +++ b/lib/Pages/Sub_Pages/PersonalSub/CompleteProfile.dart @@ -0,0 +1,262 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_datetime_picker/flutter_datetime_picker.dart'; +import 'package:intl/intl.dart'; +import 'package:teso/Classes/customTesoButton.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'dart:convert'; + +class CompleteProfile extends StatefulWidget { + @override + _CompleteProfileState createState() => _CompleteProfileState(); +} + +class _CompleteProfileState extends State { + DateFormat dateFormat = DateFormat("EEEE dd-MM-yyyy"); + DateTime selectedDate = DateTime.now(); + String selectedGender; + List gender = ["Male", "Female", "Other"]; + bool error = false; + String message = ""; + String olduser; + + void changeDate(v) { + setState(() { + selectedDate = v; + }); + } + + @override + void initState() { + super.initState(); + SharedPreferences.getInstance().then((prefs) async { + olduser = prefs.getString("currentUser"); + }); + } + + completeProfile() async { + if (selectedDate.year != DateTime.now().year || + selectedGender == null || + selectedGender.isEmpty) { + SharedPreferences.getInstance().then((prefs) async { + Map user = + jsonDecode(prefs.getString("currentUser")) as Map; + TesoUser currentuser = TesoUser.fromJSON(user); + currentuser.dateOfBirth = selectedDate; + currentuser.gender = selectedGender; + Provider.of(context, listen: false) + .updateUser(currentuser); + }).whenComplete(() { + SharedPreferences.getInstance().then((prefs) async { + if (prefs.getString("currentUser") == null) { + prefs.setString("currentUser", olduser); + setState(() { + error = true; + message = + "Sorry, an error occurred while updating your profile please try again later !!!"; + }); + + Future.delayed(const Duration(seconds: 5), () { + if (error) { + if (mounted) { + setState(() { + error = false; + }); + } + } + }); + Navigator.pop(context); + } else { + Map user = + jsonDecode(olduser) as Map; + TesoUser currentuser = TesoUser.fromJSON(user); + currentuser.dateOfBirth = selectedDate; + currentuser.gender = selectedGender; + Provider.of(context, listen: false) + .setUser(currentuser); + Navigator.pop(context); + } + }); + }); + } else { + setState(() { + error = true; + message = + "Sorry, an error occurred please make sure to select the right date and select a gender !!!"; + }); + + Future.delayed(const Duration(seconds: 5), () { + if (error) { + if (mounted) { + setState(() { + error = false; + }); + } + } + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(20), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + height: 120, + padding: EdgeInsets.symmetric(vertical: 20), + child: Image( + image: AssetImage("assets/images/tesoCouponInsignia.png"), + ), + ), + SizedBox( + height: 5, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Center( + child: Text( + "Complete your profile", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + ), + ), + ), + SizedBox( + height: 40, + ), + Container( + width: MediaQuery.of(context).size.width, + // height: 10, + child: Text( + "When is your birthday ?", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + alignment: Alignment.centerLeft, + child: TextButton( + onPressed: () { + DatePicker.showDatePicker( + context, + showTitleActions: true, + maxTime: DateTime.now(), + onConfirm: (date) { + changeDate(date); + }, + currentTime: selectedDate, + locale: LocaleType.en, + ); + }, + child: Text( + dateFormat.format(selectedDate).toString(), + style: TextStyle( + fontWeight: FontWeight.normal, + ), + ), + ), + ), + Divider(), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Text( + "What is your gender ?", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + Container( + alignment: Alignment.centerLeft, + child: DropdownButton( + hint: Text("Select Gender"), + value: selectedGender, + items: gender + .map( + (gender) => DropdownMenuItem( + value: gender, + child: Text( + gender, + style: TextStyle( + // color: Colors.grey, + ), + ), + ), + ) + .toList(), + onChanged: (v) { + setState(() { + selectedGender = v; + }); + }, + ), + ), + Divider(), + SizedBox( + height: 10, + ), + Container( + width: MediaQuery.of(context).size.width, + child: Text( + "Teso uses this data to personalize your experiences, to help business understand their customers, and more. " + + "We will always keep your personal data private.", + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: 20, + ), + Visibility( + visible: error, + child: Container( + width: MediaQuery.of(context).size.width, + child: Text( + message, + style: TextStyle( + color: Colors.red, + ), + ), + ), + ), + RaisedGradientButton( + onPressed: completeProfile, + child: Text( + "Submit", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/PersonalSub/Friends.dart b/lib/Pages/Sub_Pages/PersonalSub/Friends.dart new file mode 100644 index 0000000..caead32 --- /dev/null +++ b/lib/Pages/Sub_Pages/PersonalSub/Friends.dart @@ -0,0 +1,91 @@ +import 'package:provider/provider.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/Friends/friendTile.dart'; +import 'package:teso/Pages/Sub_Pages/userProfile3P.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Friends/header.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class Friends extends StatefulWidget { + @override + _FriendsState createState() => _FriendsState(); +} + +class _FriendsState extends State { + TextEditingController searchkey; + List friendsMain = []; + List friends; + SharedPreferences prefs; + + void clearText() { + setState(() { + searchkey.clear(); + }); + } + + @override + void initState() { + searchkey = new TextEditingController(); + super.initState(); + } + + updateList(String name) { + setState(() { + friends = friendsMain + .where((element) => + element.username.toLowerCase().contains(name.toLowerCase())) + .toList(); + friends.sort((a, b) { + return b.firstname.compareTo(a.firstname); + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + // extendBody: true, + appBar: PreferredSize( + preferredSize: Size.fromHeight(90), + child: buildFriendsHeader(context, searchkey, updateList), + ), + body: Consumer( + builder: (context, value, child) { + if (value.friends == null) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else { + if (friendsMain.length == 0) { + friendsMain = value.friends; + friends = value.friends; + } + friends.sort((a, b) { + return b.firstname.compareTo(a.firstname); + }); + return ListView.builder( + itemCount: friends.length, + itemBuilder: (context, int index) { + return InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: UserProfileThirdPerson( + user: friends.elementAt(index), + ), + type: PageTransitionType.fade, + ), + ), + child: buildFriend(context, friends.elementAt(index)), + ); + }, + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/PersonalSub/Posts.dart b/lib/Pages/Sub_Pages/PersonalSub/Posts.dart new file mode 100644 index 0000000..d6d8c7c --- /dev/null +++ b/lib/Pages/Sub_Pages/PersonalSub/Posts.dart @@ -0,0 +1,92 @@ +import 'dart:typed_data'; +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; +import 'package:teso/Classes/Uploading.dart'; +import 'package:teso/Pages/PageWidgets/Posts/user_posted.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:teso/Pages/PageWidgets/Uploads/Pending.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/RecordVideo.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Pages/PageWidgets/Personal/Empty.dart'; + +class Posts extends StatefulWidget { + @override + _PostsState createState() => _PostsState(); +} + +class _PostsState extends State { + // ScrollController _controller; + List trends = []; + List show = []; + int count; + Uint8List thumbnail; + SharedPreferences prefs; + bool loading = false; + + void postContent(context) async { + await Navigator.of(context).push( + PageRouteBuilder( + opaque: false, + pageBuilder: (_, __, ___) => RecordVideo(), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, UserProvider value, child) { + if (value.posts == null || value.posts.isEmpty) { + return buildEmpty(context, postContent); + } else { + return StaggeredGridView.count( + crossAxisCount: 3, + children: List.generate(value.posts.length, (int index) { + // if (index == 0 && provider.isNotEmpty) { + return buildPosted(context, value.posts.elementAt(index), 0.325); + // }else{ + // return buildPosted( + // context, value.posts.elementAt(index), 0.325); + // } + }), + staggeredTiles: List.generate( + value.posts.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ); + } + }, + ); + // : Center( + // child: CupertinoActivityIndicator( + // animating: true, + // radius: 15, + // ), + // ); + } + + Widget getTiles(BuildContext context, value) { + try { + List provider = value.getPending(); + if (value.pending != null) { + return SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: ListView( + children: provider + .map((item) => uploadTile(context, item)) + .toList()), + ); + } else { + return Container(); + } + } catch (e) { + return Container(); + } + } +} diff --git a/lib/Pages/Sub_Pages/PersonalSub/Recently.dart b/lib/Pages/Sub_Pages/PersonalSub/Recently.dart new file mode 100644 index 0000000..dce4e47 --- /dev/null +++ b/lib/Pages/Sub_Pages/PersonalSub/Recently.dart @@ -0,0 +1,175 @@ +import 'package:teso/Classes/TesoUser.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Recently%20Viewed/viewedItem.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/Classes/API Clasess/Product.dart'; +import 'package:teso/Classes/API Clasess/TesoBusinessDetail.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:teso/Pages/Sub_Pages/Coupons/Acquire.dart'; + +class Recently extends StatefulWidget { + @override + _RecentlyState createState() => _RecentlyState(); +} + +class _RecentlyState extends State { + TesoUser currentUser; + int _limit = 20; + final int _limitIncrement = 20; + List coupons = new List.from([]); + final ScrollController listScrollController = ScrollController(); + + @override + void initState() { + listScrollController.addListener(_scrollListener); + super.initState(); + } + + _scrollListener() { + if (listScrollController.offset >= + listScrollController.position.maxScrollExtent && + !listScrollController.position.outOfRange) { + setState(() { + _limit += _limitIncrement; + }); + } + if (listScrollController.offset <= + listScrollController.position.minScrollExtent && + !listScrollController.position.outOfRange) { + setState(() {}); + } + } + + tesoCouponDialog(context, CouponsHead head, double price) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), + ), + builder: (BuildContext bc) { + return AcquireCoupon( + head: head, + price: price, + ); + }); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (BuildContext context, UserProvider user, Widget child) { + if (user == null) { + return Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ); + } else { + currentUser = user.currentUser; + return StreamBuilder( + stream: FirebaseFirestore.instance + .collection('recentlyViewed') + .doc("users") + .collection(currentUser.userGUID) + .orderBy('dateViewed', descending: true) + .limit(_limit) + .snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else if (snapshot.data == null || coupons == null) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else { + coupons = snapshot.data.docs; + return ListView.builder( + itemCount: coupons.length, + controller: listScrollController, + itemBuilder: (context, int index) { + if (coupons.length <= 0) { + return Container(); + } else { + CouponDetails couponDetails = new CouponDetails(); + couponDetails.issuer = new TesoBusinessDetail(); + couponDetails.targetProduct = new Product(); + couponDetails.businessId = + snapshot.data.docs[index].data()['businessId']; + couponDetails.couponId = + snapshot.data.docs[index].data()['couponId']; + couponDetails.expiration = DateTime.parse(snapshot + .data.docs[index] + .data()['expiration'] + .toString()); + couponDetails.type = + snapshot.data.docs[index].data()['type']; + couponDetails.quantity = int.parse(snapshot + .data.docs[index] + .data()['quantity'] + .toString()); + couponDetails.worth = double.parse(snapshot + .data.docs[index] + .data()['worth'] + .toString()); + couponDetails.state = + snapshot.data.docs[index].data()['state']; + couponDetails.productCost = double.parse(snapshot + .data.docs[index] + .data()['unitPrice'] + .toString()); + couponDetails.issuer.businessAddress = + snapshot.data.docs[index].data()['businessAddress']; + couponDetails.issuer.businessContact = + snapshot.data.docs[index].data()['businessContact']; + couponDetails.issuer.businessDescription = snapshot + .data.docs[index] + .data()['businessDescription']; + couponDetails.issuer.businessLat = + snapshot.data.docs[index].data()['businessLat']; + couponDetails.issuer.businessLng = + snapshot.data.docs[index].data()['businessLng']; + couponDetails.issuer.businessLogo = + snapshot.data.docs[index].data()['businessLogo']; + couponDetails.issuer.businessName = + snapshot.data.docs[index].data()['businessName']; + couponDetails.targetProduct.productID = + snapshot.data.docs[index].data()['productId']; + couponDetails.targetProduct.productName = + snapshot.data.docs[index].data()['name']; + couponDetails.targetProduct.productImage = + snapshot.data.docs[index].data()['productImage']; + couponDetails.targetProduct.unitPrice = double.parse( + snapshot.data.docs[index] + .data()['unitPrice'] + .toString()); + couponDetails.lowerLimit = double.parse(snapshot + .data.docs[index] + .data()['lowerLimit'] + .toString()); + couponDetails.upperLimit = double.parse(snapshot + .data.docs[index] + .data()['upperLimit'] + .toString()); + return buildRecentItem( + context, couponDetails, tesoCouponDialog); + } + }, + ); + } + }); + } + }, + ); + } +} diff --git a/lib/Pages/Sub_Pages/PersonalSub/Referral.dart b/lib/Pages/Sub_Pages/PersonalSub/Referral.dart new file mode 100644 index 0000000..3d42f5b --- /dev/null +++ b/lib/Pages/Sub_Pages/PersonalSub/Referral.dart @@ -0,0 +1,241 @@ +import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; +import 'package:flutter/material.dart'; + +import 'package:flutter/cupertino.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +class Referrals extends StatefulWidget { + const Referrals({Key key}) : super(key: key); + + @override + _ReferralsState createState() => _ReferralsState(); +} + +class _ReferralsState extends State { + String refLink = ""; + @override + void initState() { + SharedPreferences.getInstance().then((value) async { + setState(() { + refLink = value.getString("referral"); + }); + if (refLink == null) { + String uid = value.getString("id"); + final ShortDynamicLink shortenedLink = + await DynamicLinkParameters.shortenUrl( + Uri.parse(dynamiclinkprefix + + 'https://mobile.tesoapp.com/referral?referrer=' + + uid + + dynamiclinktrailing), + DynamicLinkParametersOptions( + shortDynamicLinkPathLength: + ShortDynamicLinkPathLength.unguessable), + ); + setState(() { + refLink = shortenedLink.shortUrl.toString(); + }); + value.setString("referral", refLink); + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + body: refLink != null && refLink.isNotEmpty + ? Stack( + children: [ + Container( + height: MediaQuery.of(context).size.height * 0.5, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [tesoGold, tesoAsh, tesoBlue], + ), + ), + child: AppBar( + backgroundColor: Colors.transparent, + leading: Container( + margin: EdgeInsets.all(5), + height: 35, + width: 35, + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.4), + shape: BoxShape.circle), + child: IconButton( + icon: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + onPressed: () => Navigator.pop(context), + ), + ), + ), + ), + Align( + alignment: Alignment.center, + child: new Column( + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.2, + ), + margin: EdgeInsets.only( + top: SizeConfig.safeBlockHorizontal * 5), + child: Text( + "Invite Your Friend And Earn Silver Coin", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w900, + fontSize: 20, + ), + ), + ), + SizedBox( + height: 20, + ), + Container( + width: SizeConfig.safeBlockHorizontal * 50, + height: SizeConfig.safeBlockHorizontal * 50, + child: Image.asset("assets/images/refer.png"), + ), + ], + ), + ), + new Container( + alignment: Alignment.topCenter, + padding: new EdgeInsets.only( + top: MediaQuery.of(context).size.height * .44, + right: 10.0, + left: 10.0), + child: new Container( + // height: 80.0, + width: MediaQuery.of(context).size.width, + child: new Card( + //color: Colors.black, + elevation: 4.0, + child: new ListTile( + leading: Container( + child: Image.asset("assets/images/wallet.png"), + ), + title: Text( + "Share your referral link and invite your friends via SMS, Email or Whatsapp and Earn up to 250 Silver Coins", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15, + ), + ), + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + margin: EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.width * 0.1), + height: MediaQuery.of(context).size.height * 0.28, + child: new Column( + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * 0.2, + ), + child: Text( + "YOUR REFERRAL LINK", + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.normal, + fontSize: 18, + ), + ), + ), + SizedBox( + height: 10, + ), + Container( + decoration: BoxDecoration( + border: Border.all( + width: 2, + style: BorderStyle.solid, + ), + ), + padding: EdgeInsets.symmetric( + vertical: 10, + horizontal: + MediaQuery.of(context).size.width * 0.1, + ), + width: MediaQuery.of(context).size.width * 0.8, + child: Text( + refLink, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.normal, + fontSize: 15, + ), + ), + ), + Container( + width: MediaQuery.of(context).size.width * 0.4, + margin: EdgeInsets.symmetric(vertical: 15), + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + ), + ), + child: TextButton.icon( + onPressed: () => Share.share( + 'Join Teso App using my referral link and earn 5 silver coins ' + + refLink, + subject: 'Teso App!'), + icon: Icon( + Icons.share, + color: Colors.white, + ), + label: Text( + "Share", + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ), + ) + ], + ) + : Container( + width: double.infinity, + height: double.infinity, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/PersonalSub/Settings.dart b/lib/Pages/Sub_Pages/PersonalSub/Settings.dart new file mode 100644 index 0000000..76db822 --- /dev/null +++ b/lib/Pages/Sub_Pages/PersonalSub/Settings.dart @@ -0,0 +1,246 @@ +import 'package:camera/camera.dart'; +import 'package:teso/Pages/PageWidgets/Settings/privacy.dart'; +import 'package:flutter/material.dart'; + +import 'package:provider/provider.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/Settings/AccountSettings.dart'; +import 'package:teso/Pages/PageWidgets/Settings/EditProfile.dart'; +import 'package:teso/Pages/PageWidgets/Settings/terms.dart'; +import 'package:teso/Pages/Sub_Pages/AccountSettings/BlockedUser.dart'; +import 'package:teso/Pages/Sub_Pages/PersonalSub/Referral.dart'; +import 'package:teso/providers/app_provider.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/providers/device_provider.dart'; +import 'package:teso/util/consts.dart'; + +class Setting extends StatefulWidget { + final Function logOut; + final List connectedCameras; + const Setting({Key key, this.logOut, this.connectedCameras}) + : super(key: key); + + @override + _SettingState createState() => _SettingState(logOut: this.logOut); +} + +class _SettingState extends State { + Function logOut; + _SettingState({this.logOut}); + bool enabled = true; + TesoUser currentUser; + + @override + void initState() { + super.initState(); + } + + void editProfileDialog(context, TesoUser user) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return EditProfile( + userProfile: user, + // connectedCameras: widget.connectedCameras, + ); + }); + } + + void accountSettingsDialog(context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return AccountSettings(); + }); + } + + @override + Widget build(BuildContext context) { + return Container( + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Wrap( + children: [ + new Container( + margin: EdgeInsets.only( + left: 20.0, + top: 20.0, + bottom: 12.0, + ), + child: Row( + children: [ + Container( + child: InkWell( + onTap: () => Navigator.pop(context), + child: Icon(Icons.close), + ), + ), + Expanded( + child: Container( + width: double.infinity, + child: Center( + child: Text( + "Settings", + style: TextStyle( + fontSize: 20.0, + ), + ), + ), + ), + ), + ], + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Text( + "Personnal Information", + style: TextStyle(fontSize: 15), + ), + ), + new Consumer( + builder: (BuildContext context, UserProvider user, Widget child) { + if (user.currentUser == null) { + return Container(); + } else { + return new ListTile( + trailing: new Icon(Icons.arrow_forward_ios), + title: new Text('Edit Profile'), + onTap: () => editProfileDialog(context, user.currentUser), + ); + } + }, + ), + new ListTile( + trailing: new Icon(Icons.arrow_forward_ios), + title: new Text('Account settings'), + onTap: () => accountSettingsDialog(context), + ), + new ListTile( + trailing: new Icon(Icons.arrow_forward_ios), + title: new Text('Refer and Earn'), + onTap: () { + Navigator.push( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: Referrals(), + ), + ); + }, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Text("Connections"), + ), + new ListTile( + trailing: new Icon(Icons.arrow_forward_ios), + title: new Text('Blocked accounts'), + onTap: () => Navigator.push( + context, + PageTransition( + child: BlockAccounts(), + type: PageTransitionType.leftToRight)), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Text("Support"), + ), + new ListTile( + trailing: new Icon(Icons.arrow_forward_ios), + title: new Text('Terms of use'), + onTap: () => Navigator.push( + context, + PageTransition( + child: TermsUse(), type: PageTransitionType.leftToRight)), + ), + new ListTile( + trailing: new Icon(Icons.arrow_forward_ios), + title: new Text('See Privacy Policy'), + onTap: () => Navigator.push( + context, + PageTransition( + child: Privacy(), type: PageTransitionType.leftToRight)), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Text("Actions"), + ), + new Consumer(builder: + (BuildContext context, DeviceProvider value, Widget child) { + return SwitchListTile( + secondary: Icon(Icons.camera), + title: Text( + "Coupon Alerts", + ), + subtitle: Text( + "Enabling coupon alerts would you allow receive proximity coupons"), + value: + value.serviceEnabled == null ? false : value.serviceEnabled, + onChanged: (v) { + Provider.of(context, listen: false) + .toggleBackgroundMode(context); + }, + ); + }), + new Consumer(builder: + (BuildContext context, UserProvider value, Widget child) { + return SwitchListTile( + secondary: Icon( + Icons.dark_mode_outlined, + ), + title: Text( + "Dark Mode", + ), + value: Provider.of(context).theme == + Constants.lightTheme + ? false + : true, + onChanged: (v) { + if (v) { + Provider.of(context, listen: false) + .setTheme(Constants.darkTheme, "dark"); + } else { + Provider.of(context, listen: false) + .setTheme(Constants.lightTheme, "light"); + } + }, + ); + }), + new ListTile( + title: new Text('Log out'), + onTap: () => logOut(), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/PopUp/Personalized.dart b/lib/Pages/Sub_Pages/PopUp/Personalized.dart new file mode 100644 index 0000000..c2c2a15 --- /dev/null +++ b/lib/Pages/Sub_Pages/PopUp/Personalized.dart @@ -0,0 +1,154 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +import 'package:http/http.dart' as http; +import 'package:flutter/cupertino.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API%20Clasess/CouponHead.dart'; +import 'package:teso/Classes/Payload.dart'; +import 'package:teso/Notifications/NotificationPlugin.dart'; +import 'package:teso/Pages/PageWidgets/Coupons/personalizedDiscount.dart'; +import 'package:teso/Pages/PageWidgets/Coupons/personalizedFreebie.dart'; +import 'package:teso/util/consts.dart'; + +class PersonalCoupon extends StatefulWidget { + final CouponDetails details; + final CouponsHead head; + const PersonalCoupon({Key key, this.details, this.head}) : super(key: key); + + @override + _PersonalCouponState createState() => _PersonalCouponState(); +} + +class _PersonalCouponState extends State { + bool acquiring = false; + + void acceptCoupon() async { + setState(() { + acquiring = true; + }); + + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestheaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + var register = serverLocation + 'coupons/acceptPersonalized'; + var client = await http.post(Uri.parse(register), + body: json.encode(widget.head), headers: requestheaders); + if (client.statusCode == 200) { + Payload payload = new Payload(); + payload.loadID = "TESN000"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Coupon Accepted", + "The personalied coupon has been added successfully!!", + payload.toString(), + ); + + Navigator.pop(context); + } else if (client.statusCode == 400) { + Payload payload = new Payload(); + payload.loadID = "TESN000"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Offer Expired", + "Unable to acquire coupon as this offer has expired!!", + payload.toString(), + ); + + Navigator.pop(context); + } else { + Payload payload = new Payload(); + payload.loadID = "TESN000"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Error Occurred", + "Unable to acquire coupon as this moment", + payload.toString(), + ); + setState(() { + acquiring = false; + }); + } + } catch (e) { + print(e); + Payload payload = new Payload(); + payload.loadID = "TESN000"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Error Occurred", + "Unable to acquire coupon as this moment", + payload.toString(), + ); + setState(() { + acquiring = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Color.fromRGBO(0, 0, 0, 0.8), + child: Stack( + children: [ + Align( + alignment: Alignment.topRight, + child: Container( + child: InkWell( + onTap: () => Navigator.pop(context), + child: Icon( + Icons.close_outlined, + color: Colors.white, + size: 35, + ), + ), + margin: EdgeInsets.symmetric(horizontal: 30, vertical: 30), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height * 0.85, + padding: EdgeInsets.only(top: 25), + child: widget.head.type == "TESCP006" + ? buildPersonalizedFreebieCoupon( + context, widget.details, acceptCoupon) + : buildPersonalizedDiscountCoupon( + context, widget.details, acceptCoupon)), + ), + Visibility( + visible: acquiring, + child: Container( + color: Colors.white, + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Posts/CreatePost.dart b/lib/Pages/Sub_Pages/Posts/CreatePost.dart new file mode 100644 index 0000000..38479f2 --- /dev/null +++ b/lib/Pages/Sub_Pages/Posts/CreatePost.dart @@ -0,0 +1,196 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/Uploading.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; + +class CreatePost extends StatefulWidget { + final String video; + final Uint8List thumbnail; + final String aspectRatio; + const CreatePost({ + Key key, + this.video, + this.thumbnail, + this.aspectRatio, + }) : super(key: key); + @override + _CreatePostState createState() => _CreatePostState(); +} + +class _CreatePostState extends State { + String aspectRatio; + TextEditingController controller; + SharedPreferences prefs; + bool sending = false; + + void postVideo(context) async { + setState(() { + sending = true; + }); + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = {'Authorization': token}; + String urlLocation = tesoStreaming + "api/mobile/upload/authurl"; + var client = + await http.get(Uri.parse(urlLocation), headers: requestHeaders); + if (client.statusCode == 200) { + var details = jsonDecode(client.body); + String muxuploadsID = details["data"]["id"]; + String muxuploadsURL = details["data"]["url"]; + + Provider.of(context, listen: false).uploadPost(Uploading( + id: DateTime.now().toString() + + widget.video.replaceAll("file://", ""), + aspect: widget.aspectRatio, + path: widget.video.replaceAll("file://", ""), + thumbnail: + widget.thumbnail != null ? base64Encode(widget.thumbnail) : null, + title: controller.text.isNotEmpty ? controller.text : "", + pending: 0, + muxuploadID: muxuploadsID, + muxuploadURL: muxuploadsURL, + )); + Navigator.pop(context); + } + } catch (e) { + print(e); + } + setState(() { + sending = false; + }); + } + + @override + void initState() { + super.initState(); + controller = new TextEditingController(); + } + + @override + void dispose() { + super.dispose(); + controller.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: AppBar( + automaticallyImplyLeading: true, + title: Text("Post"), + centerTitle: true, + ), + preferredSize: Size.fromHeight(70.0), + ), + body: SingleChildScrollView( + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + padding: EdgeInsets.all(MediaQuery.of(context).size.width * 0.025), + child: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.25, + height: MediaQuery.of(context).size.width * 0.35, + color: Colors.black, + child: widget.thumbnail != null + ? Image.memory(widget.thumbnail) + : CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + Container( + width: (MediaQuery.of(context).size.width) - + (MediaQuery.of(context).size.width * 0.35), + height: MediaQuery.of(context).size.width * 0.35, + child: TextField( + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide.none, + ), + filled: true, + isDense: true, + labelText: "Say Something..", + labelStyle: TextStyle( + color: Colors.black54, + ), + fillColor: Colors.white70, + ), + controller: controller, + maxLines: null, + keyboardType: TextInputType.text, + textInputAction: TextInputAction.done, + ), + ), + ], + ), + ), + SizedBox( + height: 20, + ), + Divider(), + Container( + margin: EdgeInsets.only( + top: 10, + ), + child: Text( + "Teso businesses and other Teso users can see your post in their feeds and on your profile.", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ], + ), + ), + ), + floatingActionButton: !sending + ? Container( + margin: EdgeInsets.all(20), + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + color: tesoBlue, + ), + child: InkWell( + onTap: () => postVideo(context), + child: Center( + child: Text( + "NEXT", + style: TextStyle( + color: Colors.white, + ), + ), + ), + ), + height: 50, + ) + : Container( + width: MediaQuery.of(context).size.width, + height: 50, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + ); + } +} diff --git a/lib/Pages/Sub_Pages/Posts/SpecialPosts.dart b/lib/Pages/Sub_Pages/Posts/SpecialPosts.dart new file mode 100644 index 0000000..c6c38f0 --- /dev/null +++ b/lib/Pages/Sub_Pages/Posts/SpecialPosts.dart @@ -0,0 +1,585 @@ +import 'dart:typed_data'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flare_flutter/flare_actor.dart'; +import 'package:flare_flutter/flare_controls.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:flutter/cupertino.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Classes/API%20Clasess/PostFav.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/Posts/comment.dart'; +import 'package:teso/Pages/Sub_Pages/userProfile3P.dart'; +import 'package:teso/Services/video_controller_service.dart'; +import 'package:teso/blocs/video_player/video_player_bloc.dart'; +import 'package:teso/blocs/video_player/video_player_event.dart'; +import 'package:teso/blocs/video_player/video_player_state.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:numeral/numeral.dart'; +import 'package:teso/GeneralWidgets/widgets/video_player_widget.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; + +// ignore: must_be_immutable +class ViewPost extends StatefulWidget { + Post postedAd; + TesoUser user; + bool friend; + final bool play; + + ViewPost({ + Key key, + this.postedAd, + this.user, + this.friend, + @required this.play, + // this.posts, + }) : super(key: key); + @override + _ViewPostState createState() => _ViewPostState(); +} + +class _ViewPostState extends State { + bool favoured = false; + List coupons = []; + Uint8List imageBitmap; + final FlareControls flareControls = FlareControls(); + bool campaignAd = false; + int likes = 0; + int comments = 0; + var userDoc; + var document; + + void sharing(Post ad) async { + await rootBundle + .load("assets/images/rawLogoOverlay.png") + .then((value) => setState(() { + imageBitmap = value.buffer.asUint8List(); + })); + Provider.of(context, listen: false).downloadVideo( + ad.postID, ad.playbackID, ad.rendition, imageBitmap, context); + } + + @override + void dispose() { + rootBundle.evict("assets/images/rawLogoOverlay.png"); + super.dispose(); + } + + void likePost(Post ad) { + SharedPreferences.getInstance().then((value) { + String cid = value.getString("id"); + PostFav liked = new PostFav(); + liked.admirerId = cid; + liked.countId = DateTime.now().toString() + "$cid"; + liked.timestamp = DateTime.now().toIso8601String(); + liked.postId = ad.postID; + + setState(() { + // ad.likes.add(liked); + likes++; + favoured = true; + }); + + flareControls.play("like"); + Provider.of(context, listen: false).addLike(liked); + }); + } + + void dislikePost(Post ad) { + setState(() { + favoured = false; + likes--; + }); + Provider.of(context, listen: false).deleteLike(ad.postID); + } + + void commentsDialog(BuildContext context) { + if (userDoc == null) { + FirebaseFirestore.instance + .collection("users") + .doc(widget.postedAd.publisherID) + .get() + .then((value) { + setState(() { + userDoc = value.data(); + }); + }); + } else { + showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), + ), + builder: (BuildContext bc) { + return ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + topRight: Radius.circular(20.0), + ), + child: CommentSection( + postedAd: widget.postedAd, + user: TesoUser( + username: userDoc["username"], + userGUID: userDoc["id"], + )), + ); + }, + ); + } + } + + Future getCampaignCoupons(String campaign) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + try { + var register2 = serverLocation + 'coupons/campaign_coupons'; + var client1 = await http.post( + Uri.parse(register2), + headers: requestHeaders, + body: json.encode(campaign), + ); + if (client1.statusCode == 200) { + var details = jsonDecode(client1.body); + setState(() { + coupons = List.from( + details.map((model) => CouponDetails.fromJSON(model)).toList()); + // coupons.removeWhere( + // (element) => element.expiration.isAfter(DateTime.now())); + }); + } + } catch (e) { + print(e); + } + } + + @override + void initState() { + campaignAd = false; + _getDocuments(); + _likedListen(); + _commentsListen(); + FirebaseFirestore.instance + .collection("users") + .doc(widget.postedAd.publisherID) + .get() + .then((value) { + if (mounted) + setState(() { + userDoc = value.data(); + }); + }); + super.initState(); + } + + _getDocuments() { + FirebaseFirestore.instance + .collection("posts") + .doc(widget.postedAd.postID) + .get() + .then((value) { + if (mounted) + setState(() { + document = value.data(); + if (document != null) { + if (document["campaignId"] != null) { + campaignAd = true; + getCampaignCoupons(document["campaignId"]); + } + } + }); + }); + } + + _likedListen() { + SharedPreferences.getInstance().then((value) { + String cid = value.getString("id"); + FirebaseFirestore.instance + .collection("posts") + .doc(widget.postedAd.postID) + .collection("likes") + .snapshots() + .listen((event) { + if (mounted) { + setState(() { + favoured = + event.docs.any((element) => element.data()["admirerID"] == cid); + likes = event.docs.length; + }); + } + }); + }); + } + + _commentsListen() { + FirebaseFirestore.instance + .collection("posts") + .doc(widget.postedAd.postID) + .collection("comments") + .snapshots() + .listen((event) { + if (mounted) { + setState(() { + comments = event.docs.length; + }); + } + }); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + body: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Colors.black, + child: Stack( + children: [ + _buildVideoPlayer(widget.postedAd), + Align( + alignment: Alignment.bottomRight, + child: Container( + margin: EdgeInsets.only( + right: 10, + bottom: 30, + ), + width: 50, + height: MediaQuery.of(context).size.width * 0.73, + child: Column( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: InkWell( + onTap: widget.postedAd.publisherID != null + ? () => Navigator.pushReplacement( + context, + PageTransition( + child: UserProfileThirdPerson( + user: new TesoUser( + username: userDoc["username"], + userGUID: userDoc["id"], + firstname: userDoc["firstname"], + lastname: userDoc["surname"], + ), + ), + type: PageTransitionType.fade, + ), + ) + : null, + child: CachedNetworkImage( + imageUrl: serverLocation + + "api/pulldp/" + + widget.postedAd.publisherID, + imageBuilder: (context, imageProvider) => + FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: imageProvider, + placeholder: + AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + ), + ), + SizedBox( + height: 20, + ), + Container( + height: 50, + child: InkWell( + onTap: () { + if (campaignAd) { + if (favoured) { + return null; + } else { + likePost(widget.postedAd); + } + } else { + if (favoured) { + dislikePost(widget.postedAd); + } else { + likePost(widget.postedAd); + } + } + }, + child: new Wrap( + direction: Axis.vertical, + children: [ + Container( + width: 50, + height: 30, + child: Center( + child: Icon( + Icons.favorite, + size: 30, + color: favoured ? Colors.red : Colors.white, + ), + ), + ), + Container( + height: 20, + width: 50, + child: Center( + child: Text( + Numeral(likes).value().toString(), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ), + SizedBox( + height: 20, + ), + Container( + height: 50, + child: InkWell( + onTap: () => commentsDialog(context), + child: new Wrap( + direction: Axis.vertical, + children: [ + Container( + width: 50, + height: 30, + child: Center( + child: Icon( + Icons.comment, + size: 30, + color: Colors.white, + ), + ), + ), + Container( + height: 20, + width: 50, + child: Center( + child: Text( + Numeral(comments).value().toString(), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ), + SizedBox( + height: 20, + ), + Container( + height: 30, + child: InkWell( + onTap: () => sharing(widget.postedAd), + child: Icon( + Icons.share, + size: 30, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.bottomLeft, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: 10, + vertical: MediaQuery.of(context).size.height * 0.05, + ), + child: new Wrap( + direction: Axis.vertical, + children: [ + new RichText( + maxLines: 5, + text: TextSpan( + text: userDoc != null ? "@" + userDoc["username"] : "", + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.safeBlockHorizontal * 4.3, + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox(height: 5), + Container( + margin: EdgeInsets.only(bottom: 20), + width: MediaQuery.of(context).size.width * 0.7, + child: Text( + document != null + ? document["title"] != null + ? document["title"] + : "" + : "", + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.safeBlockHorizontal * 4.3, + height: 1.5, + ), + maxLines: 4, + overflow: TextOverflow.ellipsis, + textDirection: TextDirection.rtl, + textAlign: TextAlign.left, + ), + ), + ], + ), + ), + ), + Align( + alignment: Alignment.topLeft, + child: Container( + margin: EdgeInsets.only(top: 25), + child: IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: new Icon( + Icons.arrow_back, + color: Colors.white, + size: 25.0, + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildVideoPlayer(Post ad) { + return BlocProvider( + create: (context) => VideoPlayerBloc( + RepositoryProvider.of(context)) + ..add(VideoSelectedEvent(ad)), + child: BlocBuilder( + builder: (context, state) { + return Container(child: _getPlayer(context, state, ad)); + }, + ), + ); + } + + Widget _getPlayer(BuildContext context, VideoPlayerState state, Post ad) { + // final screenWidth = MediaQuery.of(context).size.width; + // final containerHeight = screenWidth / ASPECT_RATIO; + final containerHeight = MediaQuery.of(context).size.height; + if (state is VideoPlayerStateLoaded) { + return GestureDetector( + onDoubleTap: () { + if (campaignAd) { + if (favoured) { + return null; + } else { + likePost(ad); + } + } else { + if (favoured) { + dislikePost(ad); + } else { + likePost(ad); + } + } + }, + child: Stack( + children: [ + VideoPlayerWidget( + key: Key(state.video.playbackID), + controller: state.controller, + ad: ad, + play: widget.play, + details: coupons, + ), + Container( + width: double.infinity, + height: MediaQuery.of(context).size.height, + child: Center( + child: SizedBox( + width: 80, + height: 80, + child: FlareActor( + 'assets/like.flr', + controller: flareControls, + animation: 'idle', + ), + ), + ), + ), + ], + ), + ); + } + + if (state is VideoPlayerStateLoading) { + return Container( + height: containerHeight, + color: Colors.black, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } + + if (state is VideoPlayerStateError) { + return Container( + height: containerHeight, + color: Colors.black, + child: Center( + child: Text(state.message), + ), + ); + } + + return Container( + height: containerHeight, + color: Colors.black, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Posts/UserPosts.dart b/lib/Pages/Sub_Pages/Posts/UserPosts.dart new file mode 100644 index 0000000..a3b23d8 --- /dev/null +++ b/lib/Pages/Sub_Pages/Posts/UserPosts.dart @@ -0,0 +1,515 @@ +import 'dart:typed_data'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Classes/API%20Clasess/PostFav.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/Posts/deletePost.dart'; +import 'package:teso/Services/uservideo_controller_service.dart'; +import 'package:teso/blocs/video_player/uservideo_player_bloc.dart'; +import 'package:teso/blocs/video_player/uservideo_player_event.dart'; +import 'package:teso/blocs/video_player/uservideo_player_state.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:numeral/numeral.dart'; +import 'package:teso/GeneralWidgets/widgets/uservideo_player_widget.dart'; + +import 'comment.dart'; + +// ignore: must_be_immutable +class UserPosts extends StatefulWidget { + FBPosts postedAd; + + UserPosts({Key key, this.postedAd}) : super(key: key); + @override + _UserPostsState createState() => _UserPostsState(); +} + +class _UserPostsState extends State { + bool favoured = false; + Uint8List imageBitmap; + var document; + var userDoc; + bool likeShow = false; + + void sharing() async { + await rootBundle + .load("assets/images/rawLogoOverlay.png") + .then((value) => setState(() { + imageBitmap = value.buffer.asUint8List(); + })); + Provider.of(context, listen: false).downloadVideo( + widget.postedAd.postID, + widget.postedAd.playbackID, + widget.postedAd.rendition, + imageBitmap, + context); + } + + @override + void dispose() { + rootBundle.evict("assets/images/rawLogoOverlay.png"); + super.dispose(); + } + + void commentsDialog(BuildContext context) { + if (userDoc == null) { + FirebaseFirestore.instance + .collection("users") + .doc(widget.postedAd.publisherID) + .get() + .then((value) { + setState(() { + userDoc = value.data(); + }); + }); + } else { + showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), + ), + builder: (BuildContext bc) { + return ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + topRight: Radius.circular(20.0), + ), + child: CommentSection( + postedAd: new Post( + aspect: widget.postedAd.aspect, + assetID: widget.postedAd.assetID, + playbackID: widget.postedAd.playbackID, + postID: widget.postedAd.postID, + publisherID: widget.postedAd.publisherID, + title: widget.postedAd.title, + rendition: widget.postedAd.rendition, + timestamp: widget.postedAd.timestamp, + ), + user: TesoUser( + username: userDoc["username"], + userGUID: userDoc["id"], + )), + ); + }, + ); + } + } + + void likePost() { + setState(() { + likeShow = true; + }); + SharedPreferences.getInstance().then((value) { + String cid = value.getString("id"); + PostFav liked = new PostFav(); + liked.admirerId = cid; + liked.countId = DateTime.now().toString() + "$cid"; + liked.timestamp = DateTime.now().toIso8601String(); + liked.postId = widget.postedAd.postID; + + setState(() { + widget.postedAd.likes++; + favoured = true; + }); + Provider.of(context, listen: false).addLike(liked); + }); + } + + void dislikePost() { + SharedPreferences.getInstance().then((value) { + // String cid = value.getString("id"); + setState(() { + widget.postedAd.likes--; + favoured = false; + }); + Provider.of(context, listen: false) + .deleteLike(widget.postedAd.postID); + }); + } + + @override + void initState() { + super.initState(); + _likedListen(); + FirebaseFirestore.instance + .collection("posts") + .doc(widget.postedAd.postID) + .get() + .then((value) { + setState(() { + document = value.data(); + }); + }); + FirebaseFirestore.instance + .collection("users") + .doc(widget.postedAd.publisherID) + .get() + .then((value) { + setState(() { + userDoc = value.data(); + }); + }); + // SharedPreferences.getInstance().then((value) { + // String cid = value.getString("id"); + // if (widget.postedAd.likes + // .map((e) => e.admirerId) + // .toList() + // .contains(cid)) { + // setState(() { + // favoured = true; + // }); + // } + // }); + // _future = initializeController(); + } + + _likedListen() { + SharedPreferences.getInstance().then((value) { + String cid = value.getString("id"); + FirebaseFirestore.instance + .collection("posts") + .doc(widget.postedAd.postID) + .collection("likes") + .snapshots() + .listen((event) { + setState(() { + favoured = + event.docs.any((element) => element.data()["admirerID"] == cid); + widget.postedAd.likes = event.docs.length; + }); + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: AppBar( + automaticallyImplyLeading: false, + backgroundColor: Colors.transparent, + leading: _backWidget(context), + ), + preferredSize: Size.fromHeight(40)), + extendBodyBehindAppBar: true, + body: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Colors.black, + child: Stack( + children: [ + _buildVideoPlayer(), + Align( + alignment: Alignment.bottomRight, + child: Container( + margin: EdgeInsets.only( + right: 10, + ), + width: 50, + height: MediaQuery.of(context).size.width * 0.7, + child: Column( + children: [ + _favoriteWidget(context), + SizedBox( + height: 20, + ), + _commentWidget(context), + SizedBox( + height: 20, + ), + Container( + height: 30, + child: InkWell( + onTap: () => sharing(), + child: Icon( + Icons.share, + size: 30, + color: Colors.white, + ), + ), + ), + SizedBox( + height: 20, + ), + _deleteWidget(context), + ], + ), + ), + ), + _nameDescription(context), + ], + ), + ), + ); + } + + Widget _buildVideoPlayer() { + return BlocProvider( + create: (context) => VideoPlayerBloc( + RepositoryProvider.of(context)) + ..add(VideoSelectedEvent(widget.postedAd)), + child: BlocBuilder( + builder: (context, state) { + return Container(child: _getPlayer(context, state)); + }, + ), + ); + } + + Widget _getPlayer(BuildContext context, VideoPlayerState state) { + // final screenWidth = MediaQuery.of(context).size.width; + // final containerHeight = screenWidth / ASPECT_RATIO; + if (state is VideoPlayerStateLoaded) { + return GestureDetector( + onDoubleTap: () { + if (widget.postedAd.campaignID != null) { + if (favoured) { + return null; + } else { + likePost(); + } + } else { + if (favoured) { + dislikePost(); + } else { + likePost(); + } + } + }, + child: Stack( + children: [ + VideoPlayerWidget( + key: Key(state.video.playbackID), + controller: state.controller, + ad: widget.postedAd, + ), + AnimatedOpacity( + opacity: likeShow ? 1 : 0, + duration: Duration(seconds: 2), + onEnd: () { + setState(() { + likeShow = false; + }); + }, + child: Container( + width: double.infinity, + height: MediaQuery.of(context).size.height, + child: Center( + child: Image.asset("assets/lovw.gif"), + ), + ), + ), + ], + ), + ); + } + + if (state is VideoPlayerStateLoading) { + return Container(); + } + + if (state is VideoPlayerStateError) { + return Container(); + } + + return Container(); + } + + Widget _favoriteWidget(BuildContext context) { + return Container( + height: 50, + child: InkWell( + onTap: () { + if (widget.postedAd.campaignID != null) { + if (favoured) { + return null; + } else { + likePost(); + } + } else { + if (favoured) { + dislikePost(); + } else { + likePost(); + } + } + }, + child: new Wrap( + direction: Axis.vertical, + children: [ + Container( + width: 50, + height: 30, + child: Center( + child: Icon( + Icons.favorite, + size: 30, + color: favoured ? Colors.red : Colors.white, + ), + ), + ), + Container( + height: 20, + width: 50, + child: Center( + child: Text( + Numeral(widget.postedAd.likes).value().toString(), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _commentWidget(BuildContext context) { + return Container( + height: 50, + child: InkWell( + onTap: () => commentsDialog(context), + child: new Wrap( + direction: Axis.vertical, + children: [ + Container( + width: 50, + height: 30, + child: Center( + child: Icon( + Icons.comment, + size: 30, + color: Colors.white, + ), + ), + ), + Container( + height: 20, + width: 50, + child: Center( + child: Text( + Numeral(widget.postedAd.comments).value().toString(), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _nameDescription(BuildContext context) { + return Align( + alignment: Alignment.bottomLeft, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: 10, + vertical: MediaQuery.of(context).size.height * 0.05, + ), + child: new Wrap( + direction: Axis.vertical, + children: [ + new RichText( + maxLines: 5, + text: TextSpan( + text: userDoc != null + ? userDoc["username"] != null + ? "@" + userDoc["username"] + : "" + : "", + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.safeBlockHorizontal * 4.3, + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox(height: 5), + Container( + margin: EdgeInsets.only(bottom: 20), + width: MediaQuery.of(context).size.width * 0.7, + child: Text( + widget.postedAd.title != null ? widget.postedAd.title : "", + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.safeBlockHorizontal * 4.3, + height: 1.5, + ), + maxLines: 4, + overflow: TextOverflow.ellipsis, + textDirection: TextDirection.rtl, + textAlign: TextAlign.left, + ), + ), + ], + ), + ), + ); + } + + Widget _deleteWidget(BuildContext context) { + return Container( + height: 30, + child: GestureDetector( + onTap: () async { + bool result = await Navigator.push( + context, + PageRouteBuilder( + opaque: false, + pageBuilder: (_, __, ___) => DeletePost(), + ), + ); + if (result) { + Provider.of(context, listen: false).deletePost(Post( + aspect: widget.postedAd.aspect, + playbackID: widget.postedAd.playbackID, + postID: widget.postedAd.postID, + publisherID: widget.postedAd.publisherID, + assetID: widget.postedAd.assetID, + timestamp: widget.postedAd.timestamp, + title: widget.postedAd.title, + )); + Navigator.pop(context); + } + }, + child: Icon( + Icons.delete, + size: 28, + color: Colors.white, + ), + ), + ); + } + + Widget _backWidget(BuildContext context) { + return GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + height: 40, + width: 40, + decoration: BoxDecoration( + color: Colors.transparent, + ), + child: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Posts/ViewPost.dart b/lib/Pages/Sub_Pages/Posts/ViewPost.dart new file mode 100644 index 0000000..b98a610 --- /dev/null +++ b/lib/Pages/Sub_Pages/Posts/ViewPost.dart @@ -0,0 +1,920 @@ +import 'dart:typed_data'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flare_flutter/flare_controls.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:page_transition/page_transition.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Classes/API%20Clasess/PostFav.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/Posts/comment.dart'; +import 'package:teso/Pages/Sub_Pages/userProfile3P.dart'; +import 'package:teso/Services/video_controller_service.dart'; +import 'package:teso/blocs/video_player/video_player_bloc.dart'; +import 'package:teso/blocs/video_player/video_player_event.dart'; +import 'package:teso/blocs/video_player/video_player_state.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:numeral/numeral.dart'; +import 'package:teso/GeneralWidgets/widgets/video_player_widget.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; + +// ignore: must_be_immutable +class ViewPost extends StatefulWidget { + Post postedAd; + TesoUser user; + bool friend; + final bool play; + Function report; + + ViewPost({ + Key key, + this.postedAd, + this.user, + this.friend, + @required this.play, + @required this.report, + // this.posts, + }) : super(key: key); + @override + _ViewPostState createState() => _ViewPostState(); +} + +class _ViewPostState extends State { + bool favoured = false; + List coupons = []; + Uint8List imageBitmap; + final FlareControls flareControls = FlareControls(); + bool campaignAd = false; + int likes = 0; + int comments = 0; + var userDoc; + var document; + bool likeShow = false; + bool dark = false; + + void sharing(Post ad) async { + await rootBundle + .load("assets/images/rawLogoOverlay.png") + .then((value) => setState(() { + imageBitmap = value.buffer.asUint8List(); + })); + Provider.of(context, listen: false).downloadVideo( + ad.postID, ad.playbackID, ad.rendition, imageBitmap, context); + } + + @override + void dispose() { + rootBundle.evict("assets/images/rawLogoOverlay.png"); + super.dispose(); + } + + void likePost(Post ad) { + setState(() { + likeShow = true; + }); + SharedPreferences.getInstance().then((value) { + String cid = value.getString("id"); + PostFav liked = new PostFav(); + liked.admirerId = cid; + liked.countId = DateTime.now().toString() + "$cid"; + liked.timestamp = DateTime.now().toIso8601String(); + liked.postId = ad.postID; + + setState(() { + // ad.likes.add(liked); + likes++; + favoured = true; + }); + + flareControls.play("like"); + Provider.of(context, listen: false).addLike(liked); + }); + } + + void dislikePost(Post ad) { + setState(() { + favoured = false; + likes--; + }); + Provider.of(context, listen: false).deleteLike(ad.postID); + } + + void commentsDialog(BuildContext context) { + if (userDoc == null) { + FirebaseFirestore.instance + .collection("users") + .doc(widget.postedAd.publisherID) + .get() + .then((value) { + setState(() { + userDoc = value.data(); + }); + }); + } else { + showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), + ), + builder: (BuildContext bc) { + return ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + topRight: Radius.circular(20.0), + ), + child: CommentSection( + postedAd: widget.postedAd, + user: TesoUser( + username: userDoc["username"], + userGUID: userDoc["id"], + )), + ); + }, + ); + } + } + + Future getCampaignCoupons(String campaign) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + try { + var register2 = serverLocation + 'coupons/campaign_coupons'; + var client1 = await http.post( + Uri.parse(register2), + headers: requestHeaders, + body: json.encode(campaign), + ); + if (client1.statusCode == 200) { + var details = jsonDecode(client1.body); + setState(() { + coupons = List.from( + details.map((model) => CouponDetails.fromJSON(model)).toList()); + // coupons.removeWhere( + // (element) => element.expiration.isAfter(DateTime.now())); + }); + } + } catch (e) { + print(e); + } + } + + @override + void initState() { + campaignAd = false; + _getDocuments(); + _likedListen(); + _commentsListen(); + FirebaseFirestore.instance + .collection("users") + .doc(widget.postedAd.publisherID) + .get() + .then((value) { + if (mounted) + setState(() { + userDoc = value.data(); + }); + }); + SharedPreferences.getInstance().then((value) => + value.getString("theme") == "light" ? dark = false : dark = true); + super.initState(); + } + + _getDocuments() { + FirebaseFirestore.instance + .collection("posts") + .doc(widget.postedAd.postID) + .get() + .then((value) { + if (mounted) + setState(() { + document = value.data(); + if (document != null) { + if (document["campaignId"] != null) { + campaignAd = true; + getCampaignCoupons(document["campaignId"]); + } + } + }); + }); + } + + _likedListen() { + SharedPreferences.getInstance().then((value) { + String cid = value.getString("id"); + FirebaseFirestore.instance + .collection("posts") + .doc(widget.postedAd.postID) + .collection("likes") + .snapshots() + .listen((event) { + if (mounted) { + setState(() { + favoured = + event.docs.any((element) => element.data()["admirerID"] == cid); + likes = event.docs.length; + }); + } + }); + }); + } + + _commentsListen() { + FirebaseFirestore.instance + .collection("posts") + .doc(widget.postedAd.postID) + .collection("comments") + .snapshots() + .listen((event) { + if (mounted) { + setState(() { + comments = event.docs.length; + }); + } + }); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + body: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Colors.black, + child: Stack( + children: [ + _buildVideoPlayer(widget.postedAd), + Align( + alignment: Alignment.bottomRight, + child: Container( + margin: EdgeInsets.only( + right: 10, + bottom: 30, + ), + width: 50, + height: MediaQuery.of(context).size.width * 0.73, + child: Column( + children: [ + _publisherWidget(context), + SizedBox( + height: 20, + ), + _favoriteWidget(context), + SizedBox( + height: 20, + ), + _commentWidget(context), + SizedBox( + height: 20, + ), + Container( + height: 30, + child: InkWell( + onTap: () => moreDialog(context, widget.postedAd), + child: Icon( + Icons.more_horiz, + size: 30, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ), + _nameDescription(context), + ], + ), + ), + ); + } + + Widget _buildVideoPlayer(Post ad) { + return BlocProvider( + create: (context) => VideoPlayerBloc( + RepositoryProvider.of(context)) + ..add(VideoSelectedEvent(ad)), + child: BlocBuilder( + builder: (context, state) { + return Container(child: _getPlayer(context, state, ad)); + }, + ), + ); + } + + Widget _getPlayer(BuildContext context, VideoPlayerState state, Post ad) { + // final screenWidth = MediaQuery.of(context).size.width; + // final containerHeight = screenWidth / ASPECT_RATIO; + final containerHeight = MediaQuery.of(context).size.height; + if (state is VideoPlayerStateLoaded) { + return GestureDetector( + onDoubleTap: () { + if (campaignAd) { + if (favoured) { + return null; + } else { + likePost(ad); + } + } else { + if (favoured) { + dislikePost(ad); + } else { + likePost(ad); + } + } + }, + child: Stack( + children: [ + VideoPlayerWidget( + key: Key(state.video.playbackID), + controller: state.controller, + ad: ad, + play: widget.play, + details: coupons, + ), + AnimatedOpacity( + opacity: likeShow ? 1 : 0, + duration: Duration(seconds: 2), + onEnd: () { + setState(() { + likeShow = false; + }); + }, + child: Container( + width: double.infinity, + height: MediaQuery.of(context).size.height, + child: Center( + child: Image.asset("assets/lovw.gif"), + ), + ), + ), + ], + ), + ); + } + + if (state is VideoPlayerStateLoading) { + return Container(); + } + + if (state is VideoPlayerStateError) { + return Container( + height: containerHeight, + color: Colors.black, + child: Center( + child: Text(state.message), + ), + ); + } + + return Container(); + } + + Widget _nameDescription(BuildContext context) { + return Align( + alignment: Alignment.bottomLeft, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: 10, + vertical: MediaQuery.of(context).size.height * 0.05, + ), + child: new Wrap( + direction: Axis.vertical, + children: [ + new RichText( + maxLines: 5, + text: TextSpan( + text: userDoc != null ? "@" + userDoc["username"] : "", + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.safeBlockHorizontal * 4.3, + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox(height: 5), + Container( + margin: EdgeInsets.only(bottom: 20), + width: MediaQuery.of(context).size.width * 0.7, + child: Text( + document != null + ? document["title"] != null + ? document["title"] + : "" + : "", + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.safeBlockHorizontal * 4.3, + height: 1.5, + ), + maxLines: 4, + overflow: TextOverflow.ellipsis, + textDirection: TextDirection.rtl, + textAlign: TextAlign.left, + ), + ), + ], + ), + ), + ); + } + + Widget _publisherWidget(BuildContext context) { + return Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: InkWell( + onTap: widget.postedAd.publisherID != null + ? () => Navigator.pushReplacement( + context, + PageTransition( + child: UserProfileThirdPerson( + user: new TesoUser( + username: userDoc["username"], + userGUID: userDoc["id"], + firstname: userDoc["firstname"], + lastname: userDoc["surname"], + ), + ), + type: PageTransitionType.fade, + ), + ) + : null, + child: CachedNetworkImage( + imageUrl: + serverLocation + "api/pulldp/" + widget.postedAd.publisherID, + imageBuilder: (context, imageProvider) => FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + ), + ); + } + + Widget _favoriteWidget(BuildContext context) { + return Container( + height: 50, + child: InkWell( + onTap: () { + if (campaignAd) { + if (favoured) { + return null; + } else { + likePost(widget.postedAd); + } + } else { + if (favoured) { + dislikePost(widget.postedAd); + } else { + likePost(widget.postedAd); + } + } + }, + child: new Wrap( + direction: Axis.vertical, + children: [ + Container( + width: 50, + height: 30, + child: Center( + child: Icon( + Icons.favorite, + size: 30, + color: favoured ? Colors.red : Colors.white, + ), + ), + ), + Container( + height: 20, + width: 50, + child: Center( + child: Text( + Numeral(likes).value().toString(), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _commentWidget(BuildContext context) { + return Container( + height: 50, + child: InkWell( + onTap: () => commentsDialog(context), + child: new Wrap( + direction: Axis.vertical, + children: [ + Container( + width: 50, + height: 30, + child: Center( + child: Icon( + Icons.comment, + size: 30, + color: Colors.white, + ), + ), + ), + Container( + height: 20, + width: 50, + child: Center( + child: Text( + Numeral(comments).value().toString(), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ); + } + + void moreDialog(BuildContext context, Post ad) { + showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return Container( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Wrap( + children: [ + new Container( + margin: EdgeInsets.symmetric( + vertical: 15.0, + ), + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Container( + width: 50, + height: 4, + color: Colors.grey, + ), + ), + ), + ), + buildTop3(context, ad), + new Container( + width: MediaQuery.of(context).size.width, + height: 40, + margin: EdgeInsets.symmetric(vertical: 20.0, horizontal: 15), + decoration: BoxDecoration( + color: !dark ? Colors.grey[200] : Colors.white12, + borderRadius: BorderRadius.circular(5)), + child: new Center( + child: Text( + "Why you're seeing this post", + textAlign: TextAlign.center, + style: TextStyle( + color: !dark ? Colors.black : Colors.white, + ), + ), + ), + ), + // bottomButtons(context, friends), + ], + ), + ), + ); + }, + ); + } + + buildTop3(BuildContext context, Post ad) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + GestureDetector( + onTap: () => sharing(ad), + child: Container( + width: SizeConfig.blockSizeHorizontal * 30, + height: SizeConfig.blockSizeHorizontal * 20, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular( + 10, + ), + ), + color: !dark ? Colors.grey[200] : Colors.white12, + ), + alignment: Alignment.center, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Icon(Icons.share), + Text( + "Share", + ), + ], + ), + ), + ), + ), + GestureDetector( + onTap: () => reportDialog(context), + child: Container( + width: SizeConfig.blockSizeHorizontal * 30, + height: SizeConfig.blockSizeHorizontal * 20, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular( + 10, + ), + ), + color: !dark ? Colors.grey[200] : Colors.white12, + ), + alignment: Alignment.center, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Icon( + Icons.report_problem, + color: Colors.red[900], + ), + Text( + "Report", + ), + ], + ), + ), + ), + ), + ], + ); + } + + flagContent(level) { + Provider.of(context, listen: false) + .flagPost(widget.postedAd, level); + Navigator.pop(context); + widget.report(widget.postedAd); + this.dispose(); + } + + bottomButtons(BuildContext context, friends) { + return new Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.symmetric(vertical: 5.0, horizontal: 15), + child: Column( + children: [ + new Container( + width: MediaQuery.of(context).size.width, + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.grey[200], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(5.0), + ), + ), + ), + onPressed: () => Navigator.pop(context), + child: Container( + width: MediaQuery.of(context).size.width, + child: Text( + "Hide", + textAlign: TextAlign.center, + )), + ), + ), + ), + friends + ? new Container( + width: MediaQuery.of(context).size.width, + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.grey[300], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(5.0), + ), + ), + ), + onPressed: () => Navigator.pop(context), + child: Container( + width: MediaQuery.of(context).size.width, + child: Text( + "Unfrend", + textAlign: TextAlign.center, + )), + ), + ), + ) + : new Container( + width: MediaQuery.of(context).size.width, + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.grey[300], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(5.0), + ), + ), + ), + onPressed: () => Navigator.pop(context), + child: Container( + width: MediaQuery.of(context).size.width, + child: Text( + "Add Friend", + textAlign: TextAlign.center, + )), + ), + ), + ) + ], + )); + } + + void reportDialog(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return Container( + height: MediaQuery.of(context).size.height * 0.95, + child: Column( + children: [ + new Container( + margin: EdgeInsets.symmetric( + vertical: 15.0, + ), + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Container( + width: 50, + height: 4, + color: Colors.grey, + ), + ), + ), + ), + Container( + child: Center( + child: Text( + "Report", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.w800, + ), + ), + ), + ), + Divider(), + Container( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Text( + "Why are you reporting this post?", + textAlign: TextAlign.left, + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.w800, + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 15), + child: Center( + child: Text( + "Your report would be handled as soon as possible. However if someone is in immediate danger, call the local emergency services - don't wait.", + style: TextStyle( + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Divider(), + Container( + height: MediaQuery.of(context).size.height * 0.7, + child: new ListView( + scrollDirection: Axis.vertical, + children: [ + ListTile( + title: Text("It's a spam"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(1), + ), + ListTile( + title: Text("Nudity or sexual activity"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(2), + ), + ListTile( + title: Text("I just don't like it"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(3), + ), + ListTile( + title: Text("Hate speech or symbols"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(4), + ), + ListTile( + title: Text("False information"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(5), + ), + ListTile( + title: Text("Bullying harassment"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(6), + ), + ListTile( + title: Text("Violence or dangerous organisations"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(7), + ), + ListTile( + title: Text("Scam or fraud"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(8), + ), + ListTile( + title: Text("Intellectual property violation"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(9), + ), + ListTile( + title: Text("Sale of illegal or regulated goods"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(10), + ), + ListTile( + title: Text("Suicide or self-injury"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(11), + ), + ListTile( + title: Text("Eating disorders"), + trailing: Icon(Icons.arrow_forward_ios), + onTap: () => flagContent(12), + ), + ], + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/Pages/Sub_Pages/Posts/comment.dart b/lib/Pages/Sub_Pages/Posts/comment.dart new file mode 100644 index 0000000..14611b5 --- /dev/null +++ b/lib/Pages/Sub_Pages/Posts/comment.dart @@ -0,0 +1,332 @@ +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Classes/Firebase/Comments.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/PageWidgets/Posts/user3P_commentTitle.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/CommentsPost.dart'; +import 'package:teso/Pages/PageWidgets/ChatScreen/bottomBar.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:intl/intl.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +class CommentSection extends StatefulWidget { + final Post postedAd; + final TesoUser user; + CommentSection({ + Key key, + @required this.postedAd, + @required this.user, + }) : assert(postedAd != null), + assert(user != null), + super(key: key); + @override + _CommentSectionState createState() => _CommentSectionState(); +} + +class _CommentSectionState extends State { + TextEditingController controller = new TextEditingController(); + final ScrollController listScrollController = ScrollController(); + List listMessage = []; + int _limit = 20; + final int _limitIncrement = 20; + int total = 0; + var userDocs; + List latest = []; + + _scrollListener() { + if (listScrollController.offset >= + listScrollController.position.maxScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the bottom"); + setState(() { + print("reach the bottom"); + _limit += _limitIncrement; + }); + } + if (listScrollController.offset <= + listScrollController.position.minScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the top"); + setState(() { + print("reach the top"); + }); + } + } + + @override + void initState() { + super.initState(); + listScrollController.addListener(_scrollListener); + _listenComments(); + FirebaseFirestore.instance + .collection("users") + .doc(widget.postedAd.publisherID) + .get() + .then((value) { + setState(() { + userDocs = value.data(); + }); + }); + } + + @override + void dispose() { + controller.dispose(); + listScrollController.dispose(); + super.dispose(); + } + + void sendComment(String text, int position) async { + TesoUser user = + Provider.of(context, listen: false).currentUser; + if (controller.text.isNotEmpty) { + CommentsPost comment = new CommentsPost(); + SharedPreferences.getInstance().then((value) async { + comment.postId = widget.postedAd.postID; + comment.comment = text.trim(); + comment.timestamp = DateTime.now().toIso8601String(); + comment.commenterId = value.getString("id"); + comment.commentId = + "TESCPCM" + DateTime.now().toString() + value.getString("id"); + + setState(() { + latest.add(FBComments( + comment: comment.comment, + commenterID: comment.commenterId, + commentID: comment.commentId, + commenter: user.username, + postID: comment.postId, + thumbnail: user.thumbnail_dp, + timestamp: DateTime.now(), + )); + total++; + }); + }); + Provider.of(context, listen: false).commentPost(comment); + controller.clear(); + } + } + + _listenComments() { + FirebaseFirestore.instance + .collection('posts') + .doc(widget.postedAd.postID) + .collection("comments") + .orderBy('timestamp') + .snapshots() + .listen((event) { + if (mounted) { + setState(() { + total = event.docs.length; + latest.clear(); + }); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + centerTitle: true, + title: Text( + "$total comments", + style: TextStyle( + fontSize: 14, + ), + ), + actions: [ + IconButton( + onPressed: () => Navigator.pop(context), + icon: Icon( + Icons.close, + ), + ), + ], + ), + body: Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Stack( + children: [ + Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only(bottom: 60), + child: StreamBuilder( + stream: FirebaseFirestore.instance + .collection('posts') + .doc(widget.postedAd.postID) + .collection("comments") + .orderBy('timestamp') + .limit(_limit) + .snapshots(), + builder: (context, snapshot) { + if (snapshot.data == null && + snapshot.connectionState == ConnectionState.waiting) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else if (snapshot.data.docs.length == 0) { + if (widget.postedAd.title != null) { + return Align( + alignment: Alignment.topCenter, + child: buildPostTile3P( + context, widget.user, widget.postedAd), + ); + } else { + return Container(); + } + } else { + QuerySnapshot results = snapshot.data; + listMessage = results.docs + .map((e) => FBComments.fromJSON(e.data())) + .toList(); + if (latest.length != 0) { + listMessage.addAll(latest); + } + + return ListView.builder( + padding: EdgeInsets.all(10.0), + itemCount: listMessage.length, + itemBuilder: (context, index) { + var formattedDate = DateFormat("yyyy-MM-dd HH:mm:ss") + .format(listMessage[index].timestamp); + if (index == 0 && widget.postedAd.title != null) { + return Column(children: [ + buildPostTile3P( + context, widget.user, widget.postedAd), + Divider(), + ListTile( + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + )), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: NetworkImage(serverLocation + + "api/pulldp/" + + snapshot.data.docs[index] + .data()['commenterID']), + placeholder: AssetImage( + "assets/images/tesoDP/dp1.png"), + ), + ), + ), + title: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: snapshot.data.docs[index] + .data()['commenter'] + + " ", + style: new TextStyle( + fontWeight: FontWeight.bold)), + new TextSpan( + text: snapshot.data.docs[index] + .data()['comment']), + ], + ), + ), + subtitle: Text( + TimeElapsed.fromDateTime( + DateTime.parse(formattedDate)), + ), + ), + ]); + } + return ListTile( + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + )), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: NetworkImage(serverLocation + + "api/pulldp/" + + listMessage[index].commenterID), + placeholder: + AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + title: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: listMessage[index].commenter + " ", + style: new TextStyle( + fontWeight: FontWeight.bold)), + new TextSpan(text: listMessage[index].comment), + ], + ), + ), + subtitle: Text( + TimeElapsed.fromDateTime( + DateTime.parse(formattedDate)), + ), + ); + }, + // reverse: true, + controller: listScrollController, + ); + } + }, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: buildBottom( + context, + controller, + sendComment, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Posts/deletePost.dart b/lib/Pages/Sub_Pages/Posts/deletePost.dart new file mode 100644 index 0000000..505320b --- /dev/null +++ b/lib/Pages/Sub_Pages/Posts/deletePost.dart @@ -0,0 +1,155 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; + +class DeletePost extends StatefulWidget { + final Post post; + const DeletePost({Key key, this.post}) : super(key: key); + + @override + _DeletePostState createState() => _DeletePostState(); +} + +class _DeletePostState extends State { + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + backgroundColor: Color.fromRGBO(0, 0, 0, 0.5), + body: Stack( + children: [ + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.symmetric( + vertical: SizeConfig.blockSizeHorizontal * 5, + ), + width: MediaQuery.of(context).size.width, + height: SizeConfig.blockSizeHorizontal * 52.6, + child: Column( + children: [ + _descriptionBox(context), + SizedBox( + height: 5, + ), + _cancelDelete(context), + ], + ), + ), + ), + ], + ), + ); + } + + Widget _descriptionBox(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + topRight: Radius.circular(15), + bottomLeft: Radius.circular(15), + bottomRight: Radius.circular(15), + ), + child: Material( + child: Container( + padding: EdgeInsets.all(10), + child: Column( + children: [ + SizedBox( + height: 5, + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: SizeConfig.safeBlockHorizontal * 2), + padding: EdgeInsets.symmetric( + horizontal: SizeConfig.safeBlockHorizontal * 6), + alignment: Alignment.center, + child: Text( + "Once you proceed you cannot undo your actions . " + + "Are you sure you would like to delete this post ? ", + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary == + Color(0xFFfd0a35) + ? Colors.white24 + : tesoBlue), + ), + ), + SizedBox( + height: 10, + ), + Divider(), + SizedBox( + height: 10, + ), + InkWell( + onTap: () => Navigator.pop(context, true), + child: Container( + alignment: Alignment.center, + width: MediaQuery.of(context).size.width, + child: Text( + "Delete", + style: TextStyle( + color: Colors.red, + fontSize: SizeConfig.safeBlockHorizontal * 4.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox( + height: 10, + ), + ], + ), + ), + ), + ), + ); + } + + Widget _cancelDelete(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + height: SizeConfig.safeBlockVertical * 6.5, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + topRight: Radius.circular(15), + bottomLeft: Radius.circular(15), + bottomRight: Radius.circular(15), + ), + child: ColorFiltered( + colorFilter: ColorFilter.mode( + Colors.white.withOpacity(0.1), BlendMode.lighten), + child: Material( + child: Container( + padding: EdgeInsets.all(10), + child: InkWell( + onTap: () => Navigator.pop(context, false), + child: Text( + "Cancel", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.blueAccent, + fontSize: SizeConfig.safeBlockHorizontal * 4.5, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/Posts/postedDetails.dart b/lib/Pages/Sub_Pages/Posts/postedDetails.dart new file mode 100644 index 0000000..67f8569 --- /dev/null +++ b/lib/Pages/Sub_Pages/Posts/postedDetails.dart @@ -0,0 +1,103 @@ +// import 'dart:typed_data'; + +// import 'package:teso/Classes/API%20Clasess/PostedAd.dart'; +// import 'package:teso/Pages/PageWidgets/Posts/comment.dart'; +// import 'package:teso/Pages/PageWidgets/Posts/expandedPost.dart'; +// import 'package:flutter/material.dart'; +// +// import 'package:teso/util/consts.dart'; +// import 'package:video_player/video_player.dart'; + +// class PostDetails extends StatefulWidget { +// final PostedAd type; +// const PostDetails({Key key, this.type}) : super(key: key); +// @override +// _PostDetailsState createState() => _PostDetailsState(post: type); +// } + +// class _PostDetailsState extends State { +// PostedAd post; +// _PostDetailsState({this.post}); +// bool available = false; +// Uint8List bytes; +// TextEditingController controller; +// VideoPlayerController _videoPlayerController; + +// void deleteDialog(context) { +// showModalBottomSheet( +// context: context, +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), +// ), +// builder: (BuildContext bc) { +// return Container( +// child: new Wrap( +// children: [ +// new Container( +// width: double.infinity, +// margin: EdgeInsets.only( +// top: 20.0, +// bottom: 12.0, +// ), +// child: Center( +// child: Text( +// "Delete post", +// style: TextStyle( +// fontSize: 12.0, +// ), +// ), +// )), +// Padding( +// padding: const EdgeInsets.symmetric(horizontal: 20.0), +// child: Divider(), +// ), +// new ListTile( +// leading: new Icon(MaterialCommunityIcons.trash_can), +// title: new Text('Delete'), +// onTap: () => print("Delete")), +// ], +// ), +// ); +// }, +// ); +// } + +// @override +// void initState() { +// super.initState(); +// _videoPlayerController = +// VideoPlayerController.network(tesoStreaming + post.post.path) +// ..initialize().then((_) { +// _videoPlayerController.play(); +// _videoPlayerController.setLooping(true); +// setState(() {}); +// }); +// } + +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: null, +// body: Container( +// margin: EdgeInsets.only( +// top: 50, +// left: 10, +// right: 10, +// ), +// height: MediaQuery.of(context).size.height, +// width: MediaQuery.of(context).size.width, +// child: SingleChildScrollView( +// scrollDirection: Axis.vertical, +// child: Column( +// children: [ +// buildPostDetails( +// context, post, deleteDialog, _videoPlayerController), +// Padding(padding: EdgeInsets.all(05)), +// buildCommentTile(context, available, bytes, controller), +// ], +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/Pages/Sub_Pages/Posts/user_comment.dart b/lib/Pages/Sub_Pages/Posts/user_comment.dart new file mode 100644 index 0000000..1b9429e --- /dev/null +++ b/lib/Pages/Sub_Pages/Posts/user_comment.dart @@ -0,0 +1,289 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; + +import 'package:page_transition/page_transition.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/CommentsPost.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; +import 'package:teso/Pages/PageWidgets/ChatScreen/bottomBar.dart'; +import 'package:teso/Pages/Sub_Pages/Posts/UserPosts.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:intl/intl.dart'; +import 'package:time_elapsed/time_elapsed.dart'; + +// ignore: must_be_immutable +class CommentSection extends StatefulWidget { + FBPosts postedAd; + CommentSection({ + Key key, + @required this.postedAd, + }) : assert(postedAd != null), + super(key: key); + @override + _CommentSectionState createState() => _CommentSectionState(); +} + +class _CommentSectionState extends State { + TextEditingController controller = new TextEditingController(); + final ScrollController listScrollController = ScrollController(); + List listMessage = new List.from([]); + int _limit = 20; + final int _limitIncrement = 20; + + _scrollListener() { + if (listScrollController.offset >= + listScrollController.position.maxScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the bottom"); + setState(() { + print("reach the bottom"); + _limit += _limitIncrement; + }); + } + if (listScrollController.offset <= + listScrollController.position.minScrollExtent && + !listScrollController.position.outOfRange) { + print("reach the top"); + setState(() { + print("reach the top"); + }); + } + } + + @override + void initState() { + super.initState(); + listScrollController.addListener(_scrollListener); + } + + @override + void dispose() { + controller.dispose(); + listScrollController.dispose(); + super.dispose(); + } + + void sendComment(String text, int position) async { + if (controller.text.isNotEmpty) { + CommentsPost comment = new CommentsPost(); + SharedPreferences.getInstance().then((value) async { + comment.postId = widget.postedAd.postID; + comment.comment = text; + comment.timestamp = DateTime.now().toIso8601String(); + comment.commenterId = value.getString("id"); + comment.commentId = + "TESCPCM" + DateTime.now().toString() + value.getString("id"); + }); + // setState(() { + // widget.postedAd.comments.add(comment); + // }); + + Provider.of(context, listen: false).commentPost(comment); + controller.clear(); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Comments"), + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + ), + onPressed: () => Navigator.pushReplacement( + context, + PageTransition( + child: UserPosts( + postedAd: widget.postedAd, + ), + type: PageTransitionType.rightToLeft, + ), + ), + ), + ), + body: Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Stack( + children: [ + Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only(bottom: 60), + child: StreamBuilder( + stream: FirebaseFirestore.instance + .collection('posts') + .doc('comments') + .collection(widget.postedAd.postID) + .orderBy('timestamp') + .limit(_limit) + .snapshots(), + builder: (context, snapshot) { + if (snapshot.data == null && + snapshot.connectionState == ConnectionState.waiting) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor))); + } else if (snapshot.data.docs.length == 0) { + // if (widget.postedAd.title != null) { + // return Align( + // alignment: Alignment.topCenter, + // child: buildPostTile(context, widget.postedAd), + // ); + // } else { + return Container(); + // } + } else { + listMessage = snapshot.data.docs; + return ListView.builder( + padding: EdgeInsets.all(10.0), + itemCount: listMessage.length, + itemBuilder: (context, index) { + int timeInMillis = int.parse(snapshot.data.docs[index] + .data()['timestamp'] + .toString()); + var date = + DateTime.fromMillisecondsSinceEpoch(timeInMillis); + var formattedDate = + DateFormat("yyyy-MM-dd HH:mm:ss").format(date); + if (index == 0 && widget.postedAd.title != null) { + return Column(children: [ + // buildPostTile(context, widget.postedAd), + Divider(), + ListTile( + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + )), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: NetworkImage(serverLocation + + "api/pulldp/" + + snapshot.data.docs[index] + .data()['commenterID']), + placeholder: AssetImage( + "assets/images/tesoDP/dp1.png"), + ), + ), + ), + title: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: snapshot.data.docs[index] + .data()['commenter'] + + " ", + style: new TextStyle( + fontWeight: FontWeight.bold)), + new TextSpan( + text: snapshot.data.docs[index] + .data()['comment']), + ], + ), + ), + subtitle: Text( + TimeElapsed.fromDateTime( + DateTime.parse(formattedDate)), + ), + ), + ]); + } + return ListTile( + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.black, + width: 1, + )), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(90.0), + topRight: Radius.circular(90.0), + bottomLeft: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + child: FadeInImage( + height: 90, + width: 90, + fit: BoxFit.fill, + image: NetworkImage(serverLocation + + "api/pulldp/" + + snapshot.data.docs[index] + .data()['commenterID']), + placeholder: + AssetImage("assets/images/tesoDP/dp1.png"), + ), + ), + ), + title: new RichText( + text: new TextSpan( + style: new TextStyle( + fontSize: 14.0, + color: Theme.of(context).primaryColorLight, + ), + children: [ + new TextSpan( + text: snapshot.data.docs[index] + .data()['commenter'] + + " ", + style: new TextStyle( + fontWeight: FontWeight.bold)), + new TextSpan( + text: snapshot.data.docs[index] + .data()['comment']), + ], + ), + ), + subtitle: Text( + TimeElapsed.fromDateTime( + DateTime.parse(formattedDate)), + ), + ); + }, + // reverse: true, + controller: listScrollController, + ); + } + }, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: buildBottom( + context, + controller, + sendComment, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/ProductDetails/CouponList.dart b/lib/Pages/Sub_Pages/ProductDetails/CouponList.dart new file mode 100644 index 0000000..8e066d6 --- /dev/null +++ b/lib/Pages/Sub_Pages/ProductDetails/CouponList.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; + +import 'package:teso/Classes/API%20Clasess/CouponHead.dart'; +import 'package:teso/Pages/PageWidgets/CouponsList/activeCoupon.dart'; +import 'package:teso/Pages/PageWidgets/CouponsList/activeFreebieCoupon.dart'; +import 'package:teso/providers/device_provider.dart'; + +class CouponList extends StatefulWidget { + final List couponsList; + + const CouponList({Key key, this.couponsList}) : super(key: key); + @override + _CouponListState createState() => _CouponListState(coupons: this.couponsList); +} + +class _CouponListState extends State { + bool acquiring = false; + List coupons; + _CouponListState({this.coupons}); + Future acquringCoupon(CouponsHead head, int cost) async { + setState(() { + acquiring = true; + }); + await Provider.of(context, listen: false) + .acceptCoupon(head, cost, context); + setState(() { + acquiring = false; + }); + } + + void calculateWorth(worth, CouponDetails coupon) { + setState(() { + final tile = coupons.firstWhere( + (item) => item.couponId == coupon.couponId, + orElse: () => null); + if (tile != null) setState(() => tile.worth = worth); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Color.fromRGBO(0, 0, 0, 0.8), + child: Stack( + children: [ + Align( + alignment: Alignment.topRight, + child: Container( + child: InkWell( + onTap: () => Navigator.pop(context), + child: Icon( + Icons.close_outlined, + color: Colors.white, + size: 35, + ), + ), + margin: EdgeInsets.symmetric(horizontal: 30, vertical: 30), + ), + ), + Align( + alignment: Alignment.center, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height * 0.85, + padding: EdgeInsets.only(top: 25), + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: coupons.length, + itemBuilder: (context, index) { + if (coupons.elementAt(index).type.contains("FREEBIE")) { + return buildFreebieCoupon( + context, coupons.elementAt(index), acquringCoupon); + } else { + return buildActiveDiscountCoupon( + context, + coupons.elementAt(index), + acquringCoupon, + calculateWorth, + ); + } + }, + ), + ), + ), + Visibility( + visible: acquiring, + child: Container( + color: Colors.white, + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/homeSub/HomeFeed.dart b/lib/Pages/Sub_Pages/homeSub/HomeFeed.dart new file mode 100644 index 0000000..07bcda0 --- /dev/null +++ b/lib/Pages/Sub_Pages/homeSub/HomeFeed.dart @@ -0,0 +1,204 @@ +import 'dart:convert'; +import 'dart:math'; +import 'package:http/http.dart' as http; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Pages/PageWidgets/Home/homeTile.dart'; +import 'package:teso/Pages/Sub_Pages/homeSub/VideoList.dart'; +import 'package:teso/util/consts.dart'; + +class HomeFeed extends StatefulWidget { + @override + _HomeFeedState createState() => _HomeFeedState(); +} + +class _HomeFeedState extends State { + ScrollController _controller; + // List trends; + List show; + int count; + var _future; + List deadFeed; + RefreshController _refreshController = + RefreshController(initialRefresh: false); + + Future> pullAds() async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'posts/homefeed'; + var client = + await http.post(Uri.parse(register), headers: requestHeaders); + if (client.statusCode == 200) { + var posts = jsonDecode(client.body); + setState(() { + this.show = List.from( + posts.map((model) => Post.fromJSON(model)).toList()); + // this.show.sort((b, a) => a.timestamp.compareTo(b.timestamp)); + }); + + await prefs.setString("homefeeds", client.body); + // await fetchImages(); + // + return show; + } else { + return null; + } + } catch (e) { + return null; + } + } + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + pullAds(); + } + } + + void _onRefresh() async { + try { + await pullAds(); + } catch (e) { + print(e); + } + _refreshController.refreshCompleted(); + } + + Future watch(advert) async { + List towatch = []; + towatch.add(advert); + if (show.length != 0) { + for (int i = 0; (i < show.length) && (i < 10); i++) { + final _random = new Random(); + towatch.add(show[_random.nextInt(show.length)]); + } + } else if (show.length == 0) { + for (int i = 0; (i < deadFeed.length) && (i < 10); i++) { + final _random = new Random(); + towatch.add(deadFeed[_random.nextInt(show.length)]); + } + } + Post reported = await Navigator.of(context).push( + new PageRouteBuilder( + pageBuilder: (_, __, ___) => new VideoList( + postedAd: towatch, + // posts: towatch, + // user: user, + //friend: addable, + ), + ), + ); + if (reported != null) { + show.remove(reported); + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString("homefeeds", jsonEncode(show)); + + print(prefs.getString("homefeed")); + } + } + + @override + void initState() { + // WidgetsBinding.instance.addPostFrameCallback((_) => widget.toggle(1)); + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + // pullAds(); + SharedPreferences.getInstance().then((value) { + if (value.getString("homefeeds") != null) { + var posts = jsonDecode(value.getString("homefeeds")); + setState(() { + deadFeed = List.from( + posts.map((model) => Post.fromJSON(model)).toList()); + }); + } + }); + _future = pullAds(); + super.initState(); + } + + @override + void dispose() { + _controller.dispose(); + _refreshController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: FutureBuilder( + initialData: deadFeed, + future: _future, + builder: (context, snapshot) { + if (show == null && + deadFeed == null && + (snapshot.connectionState == ConnectionState.none || + snapshot.connectionState == ConnectionState.waiting)) { + return Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ); + } + if (show == null && + deadFeed == null && + snapshot.connectionState == ConnectionState.done) { + return SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: ClassicHeader(), + controller: _refreshController, + onRefresh: _onRefresh, + child: Container( + child: Center( + child: Text("An error occurred"), + ), + ), + ); + } else { + show = show != null ? show : deadFeed; + return SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: ClassicHeader(), + controller: _refreshController, + onRefresh: _onRefresh, + child: StaggeredGridView.count( + controller: _controller, + crossAxisCount: 2, + children: List.generate(show.length, (int index) { + return show.elementAt(index).aspect == null || + show.elementAt(index).aspect == "null" + ? Container() + : double.parse(show.elementAt(index).aspect.toString()) < + 1 + ? buildTile( + context, show.elementAt(index), 0.65, watch) + : buildTile( + context, show.elementAt(index), 0.35, watch); + }), + staggeredTiles: List.generate( + show.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ), + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/homeSub/HomePosts.dart b/lib/Pages/Sub_Pages/homeSub/HomePosts.dart new file mode 100644 index 0000000..366c324 --- /dev/null +++ b/lib/Pages/Sub_Pages/homeSub/HomePosts.dart @@ -0,0 +1,79 @@ +// import 'package:flutter/material.dart'; +// import 'package:video_player/video_player.dart'; + +// class VideoWidget extends StatefulWidget { +// final String url; +// final bool play; + +// const VideoWidget({Key key, @required this.url, @required this.play}) +// : super(key: key); +// @override +// _VideoWidgetState createState() => _VideoWidgetState(); +// } + +// class _VideoWidgetState extends State { +// VideoPlayerController _controller; +// Future _initializeVideoPlayerFuture; + +// @override +// void initState() { +// super.initState(); +// _controller = VideoPlayerController.network(widget.url); +// _controller.addListener(checkVideo); +// _initializeVideoPlayerFuture = _controller.initialize().then((_) { +// setState(() {}); +// }); + +// if (widget.play) { +// _controller.play(); +// _controller.setLooping(true); +// } +// } + +// void checkVideo() { +// // Implement your calls inside these conditions' bodies : +// if (_controller.value.position == +// Duration(seconds: 0, minutes: 0, hours: 0)) { +// print('video Started'); +// } + +// if (_controller.value.position == _controller.value.duration) { +// print('video Ended'); +// } +// } + +// @override +// void didUpdateWidget(VideoWidget oldWidget) { +// if (oldWidget.play != widget.play) { +// if (widget.play) { +// _controller.play(); +// _controller.setLooping(true); +// } else { +// _controller.pause(); +// } +// } +// super.didUpdateWidget(oldWidget); +// } + +// @override +// void dispose() { +// _controller.dispose(); +// super.dispose(); +// } + +// @override +// Widget build(BuildContext context) { +// return FutureBuilder( +// future: _initializeVideoPlayerFuture, +// builder: (context, snapshot) { +// if (snapshot.connectionState == ConnectionState.done) { +// return VideoPlayer(_controller); +// } else { +// return Center( +// child: CircularProgressIndicator(), +// ); +// } +// }, +// ); +// } +// } diff --git a/lib/Pages/Sub_Pages/homeSub/MyCoupons.dart b/lib/Pages/Sub_Pages/homeSub/MyCoupons.dart new file mode 100644 index 0000000..16f2489 --- /dev/null +++ b/lib/Pages/Sub_Pages/homeSub/MyCoupons.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Pages/PageWidgets/Coupons/mycoupons.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/RedeemingCoupon.dart'; +import 'package:teso/Pages/codeQR.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/Pages/Sub_Pages/Coupons/MyCouponOptions.dart'; +import 'package:teso/providers/user_provider.dart'; + +class MyCoupons extends StatefulWidget { + @override + _MyCouponsState createState() => _MyCouponsState(); +} + +class _MyCouponsState extends State { + SharedPreferences prefs; + + @override + void initState() { + super.initState(); + } + + void _scanQRCode(context, CouponDetails img) async { + String result = await Navigator.push( + context, + PageTransition( + type: PageTransitionType.downToUp, + child: QRCodeScanner(), + ), + ); + if (result != null && result.isNotEmpty && result == img.businessId) { + await Navigator.push( + context, + PageTransition( + child: RedeemingCoupon( + couponDetails: img, + ), + type: PageTransitionType.fade, + ), + ); + } + } + + tesoCouponDialog(context, CouponDetails details) async { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), + ), + builder: (BuildContext bc) { + return AcquiredOptions( + head: details, + ); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Consumer( + builder: (context, value, child) { + if (value == null || value.mycoupons == null) { + Provider.of(context, listen: false).getCoupons(); + return Container( + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } else if (value.mycoupons.length == 0) { + Provider.of(context, listen: false).getCoupons(); + return Container( + child: Center( + child: Text("No coupons available"), + ), + ); + } else { + return Container( + // margin: EdgeInsets.only( + // bottom: 80, + // ), + child: ListView.builder( + itemCount: value.mycoupons.length, + itemBuilder: (context, int index) { + return Column( + children: [ + buildMyCoupons(context, value.mycoupons.elementAt(index), + tesoCouponDialog, _scanQRCode), + Divider(), + ], + ); + }, + ), + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/homeSub/ProximityCoupons.dart b/lib/Pages/Sub_Pages/homeSub/ProximityCoupons.dart new file mode 100644 index 0000000..93f5b6a --- /dev/null +++ b/lib/Pages/Sub_Pages/homeSub/ProximityCoupons.dart @@ -0,0 +1,322 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart' show timeDilation; + +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/Pages/PageWidgets/HomeProximity/ActiveDiscount.dart'; +import 'package:teso/Pages/PageWidgets/HomeProximity/ActiveFreebie.dart'; +import 'package:teso/Pages/PageWidgets/HomeProximity/DummyDiscount.dart'; +import 'package:teso/Pages/PageWidgets/HomeProximity/DummyFreebie.dart'; +import 'package:teso/providers/device_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; + +class ProximityCoupons extends StatefulWidget { + @override + _ProximityCouponsState createState() => _ProximityCouponsState(); +} + +class _ProximityCouponsState extends State + with TickerProviderStateMixin { + AnimationController _buttonController; + Animation rotate; + Animation right; + Animation bottom; + Animation width; + int flag = 0; + List data = []; + List selectedData = []; + double selectedDiscount; + double worth = 0; + + @override + void initState() { + super.initState(); + _buttonController = new AnimationController( + duration: new Duration(milliseconds: 1000), vsync: this); + + rotate = new Tween( + begin: -0.0, + end: -40.0, + ).animate( + new CurvedAnimation( + parent: _buttonController, + curve: Curves.ease, + ), + ); + rotate.addListener(() { + setState(() { + if (rotate.isCompleted) { + var i = data.removeLast(); + data.insert(0, i); + + _buttonController.reset(); + } + }); + }); + + right = new Tween( + begin: 0.0, + end: 400.0, + ).animate( + new CurvedAnimation( + parent: _buttonController, + curve: Curves.ease, + ), + ); + bottom = new Tween( + begin: 15.0, + end: 90.0, + ).animate( + new CurvedAnimation( + parent: _buttonController, + curve: Curves.ease, + ), + ); + width = new Tween( + begin: 20.0, + end: 25.0, + ).animate( + new CurvedAnimation( + parent: _buttonController, + curve: Curves.bounceOut, + ), + ); + } + + @override + void dispose() { + _buttonController.dispose(); + super.dispose(); + } + + Future _swipeAnimation() async { + try { + await _buttonController.forward(); + } on TickerCanceled {} + } + + dismissImg(ProximityCoupon img) async { + CouponsHead couponsHead = new CouponsHead(); + couponsHead.businessId = img.business.businessId; + couponsHead.expiration = img.expiration; + couponsHead.couponId = img.couponId; + couponsHead.quantity = img.quantity; + couponsHead.state = "active"; + couponsHead.lower = double.parse(img.lowerLimit.toString()); + couponsHead.upper = double.parse(img.upperLimit.toString()); + + Provider.of(context, listen: false) + .declineCoupon(couponsHead); + + setState(() { + data.remove(img); + }); + } + + addImg(ProximityCoupon img) async { + CouponsHead couponsHead = new CouponsHead(); + couponsHead.businessId = img.business.businessId; + couponsHead.expiration = img.expiration; + couponsHead.couponId = img.couponId; + couponsHead.quantity = 1; + couponsHead.state = "active"; + couponsHead.lower = worth; + couponsHead.upper = double.parse(img.upperLimit.toString()); + couponsHead.targetProduct = img.targetID; + double price = (img.targetCost * (worth / 100)); + + //Calculations + int cost = CouponRateCalculator.getRate(price); + Provider.of(context, listen: false) + .acceptCoupon(couponsHead, cost.ceil(), context); + + setState(() { + data.remove(img); + selectedData.add(img); + }); + } + + swipeRight(img) { + if (flag == 0) + setState(() { + flag = 1; + }); + _swipeAnimation(); + addImg(img); + } + + swipeLeft(img) { + if (flag == 1) + setState(() { + flag = 0; + }); + _swipeAnimation(); + dismissImg(img); + } + + void calculateWorth(val) { + setState(() { + this.worth = val; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBody: false, + body: Consumer( + builder: (context, value, child) { + if (!value.serviceEnabled) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + child: Icon( + Icons.location_off, + size: 120, + color: Color(0xFF003445), + ), + ), + Container( + child: Text( + "Proximity Coupon Alerts Disabled", + textAlign: TextAlign.center, + style: TextStyle( + color: Color(0xFF003445), + fontFamily: 'WickedGrit', + fontSize: 30, + ), + ), + ), + new ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + primary: Colors.green[200], + padding: new EdgeInsets.all(10.0), + ), + onPressed: () => + Provider.of(context, listen: false) + .toggleBackgroundMode(context), + child: new Container( + height: 30.0, + width: 100.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.green[200], + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Enable", + style: new TextStyle( + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + ); + } else if (value.proximityCoupons == null || + value.proximityCoupons.length == 0) { + return Center( + child: Text("No coupons available in your location"), + ); + } else { + data = value.proximityCoupons; + timeDilation = 0.4; + double initialBottom = 20.0; + var dataLength = data.length; + double backCardPosition = + initialBottom + (dataLength - 1) * 10 + 10; + double backCardWidth = -10.0; + + return Container( + alignment: Alignment.center, + child: dataLength > 0 + ? new Stack( + alignment: AlignmentDirectional.center, + children: data.map((item) { + if (data.indexOf(item) == dataLength - 1 && + item.type.toLowerCase().contains("discount")) { + selectedDiscount = item.lowerLimit; + return buildActiveDiscountCoupon( + item, + bottom.value, + right.value, + 0.0, + backCardWidth + 10, + rotate.value, + rotate.value < -10 ? 0.1 : 0.0, + context, + flag, + addImg, + dismissImg, + worth, + calculateWorth); + } else if (data.indexOf(item) == dataLength - 1 && + item.type.toLowerCase().contains("freebie")) { + return buildActiveFreebieCoupon( + item, + bottom.value, + right.value, + 0.0, + backCardWidth + 10, + rotate.value, + rotate.value < -10 ? 0.1 : 0.0, + context, + flag, + addImg, + dismissImg, + ); + } else if (data.indexOf(item) < dataLength - 1 && + item.type.toLowerCase().contains("discount")) { + backCardPosition = backCardPosition - 10; + backCardWidth = backCardWidth + 10; + double price = item.targetCost - + (item.targetCost * item.lowerLimit / 100); + return buildDummyDiscountCoupon( + item, + backCardPosition, + 0.0, + 0.0, + backCardWidth, + 0.0, + 0.0, + context, + price.toString(), + selectedDiscount); + } else { + backCardPosition = backCardPosition - 10; + backCardWidth = backCardWidth + 10; + double price = item.targetCost - + (item.targetCost * item.lowerLimit / 100); + return buildDummyFreebieCoupon( + item, + backCardPosition, + 0.0, + 0.0, + backCardWidth, + 0.0, + 0.0, + context, + price.toString(), + selectedDiscount); + } + }).toList()) + : new Center( + child: Text("No coupons available in your location")), + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/homeSub/VideoList.dart b/lib/Pages/Sub_Pages/homeSub/VideoList.dart new file mode 100644 index 0000000..7c0f823 --- /dev/null +++ b/lib/Pages/Sub_Pages/homeSub/VideoList.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Pages/Sub_Pages/Posts/ViewPost.dart'; +import 'package:teso/providers/user_provider.dart'; + +class VideoList extends StatefulWidget { + final List postedAd; + + VideoList({Key key, this.postedAd}) : super(key: key); + + @override + _VideoListState createState() => _VideoListState(); +} + +class _VideoListState extends State { + @override + void initState() { + Provider.of(context, listen: false) + .predownloadAds(widget.postedAd.map((e) => e.playbackID).toList()); + super.initState(); + } + + report(Post post) { + Navigator.pop(context, post); + Navigator.pop(context, post); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: true, + backgroundColor: Colors.transparent, + leading: InkWell( + onTap: () => Navigator.pop(context), + child: Container( + height: 50, + width: 40, + margin: EdgeInsets.symmetric( + vertical: 30.0, + horizontal: 10, + ), + child: Icon( + Icons.arrow_back_ios, + color: Colors.white, + ), + ), + ), + ), + extendBodyBehindAppBar: true, + body: PageView( + scrollDirection: Axis.vertical, + children: List.generate( + widget.postedAd.length > 10 ? 10 : widget.postedAd.length, + (index) => new ViewPost( + play: true, + postedAd: widget.postedAd[index], + report: report, + )), + ), + ); + } +} diff --git a/lib/Pages/Sub_Pages/userProfile3P.dart b/lib/Pages/Sub_Pages/userProfile3P.dart new file mode 100644 index 0000000..17c45ea --- /dev/null +++ b/lib/Pages/Sub_Pages/userProfile3P.dart @@ -0,0 +1,747 @@ +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:numeral/numeral.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API%20Clasess/ThirdPerson.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/customTesoButton.dart'; + +import 'package:teso/Pages/PageWidgets/Posts/posted.dart'; +import 'package:teso/Pages/PageWidgets/Third Person Profile/header.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Pages/PageWidgets/Third Person Profile/Empty.dart'; +import 'dart:convert'; + +import 'Notifications/ChatScreen.dart'; + +// ignore: must_be_immutable +class UserProfileThirdPerson extends StatefulWidget { + TesoUser user; + final ThirdPerson fullProfile; + + UserProfileThirdPerson({Key key, this.user, this.fullProfile}) + : super(key: key); + @override + _UserProfileThirdPersonState createState() => _UserProfileThirdPersonState(); +} + +enum AppBarBehavior { normal, pinned, floating, snapping } + +class _UserProfileThirdPersonState extends State + with TickerProviderStateMixin { + final datakey = new GlobalKey(); + ThirdPerson userProfile = new ThirdPerson(); + int count; + ScrollController _controller, control, scrollController; + // AppBarBehavior _appBarBehavior = AppBarBehavior.pinned; + Animation width; + Animation heigth; + AnimationController _containerController; + double scale; + bool available = false; + bool scrolled = false; + SharedPreferences prefs; + bool addFriends = false; + bool friends = false; + bool pending = false; + bool approve = false; + bool loading = false; + int request = 0; + var _future; + + Future getProfile() async { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'user_details/pullProfile'; + var client = await http.post(Uri.parse(register), + body: json.encode(widget.user.userGUID), headers: requestHeaders); + if (client.statusCode == 200) { + var profile = jsonDecode(client.body); + userProfile = ThirdPerson.fromJSON(profile); + third(userProfile); + } + } + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + // fetchImages(); + } + } + + @override + void dispose() { + scrollController.dispose(); + _controller.dispose(); + _containerController.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + _controller = ScrollController(); + _controller.addListener(_scrollListener); + count = 0; + + if (widget.fullProfile != null) { + third(widget.fullProfile); + } else { + _future = getProfile(); + } + + scrollController = new ScrollController(); + scrollController.addListener(() => setState(() {})); + _containerController = new AnimationController( + duration: new Duration(milliseconds: 2000), vsync: this); + + width = new Tween( + begin: 200.0, + end: 220.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth = new Tween( + begin: 400.0, + end: 400.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth.addListener(() { + setState(() { + if (heigth.isCompleted) {} + }); + }); + _containerController.forward(); + } + + void third(ThirdPerson fullProfile) async { + switch (fullProfile.relation) { + case "not related": + setState(() { + addFriends = true; + }); + break; + case "sent pending": + setState(() { + pending = true; + }); + break; + case "received pending": + setState(() { + approve = true; + }); + break; + case "friends": + setState(() { + friends = true; + }); + break; + } + if (fullProfile.user != null) widget.user = widget.fullProfile.user; + + // trends = fullProfile.posts; + // await fetchImages(); + } + + void removeRelationship() async { + setState(() { + loading = true; + addFriends = false; + friends = false; + pending = false; + approve = false; + loading = false; + }); + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = widget.user.userGUID; + var register = serverLocation + 'relationships/unfriend'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + setState(() { + friends = false; + pending = false; + addFriends = !friends; + }); + } else { + setState(() { + friends = true; + pending = false; + addFriends = !friends; + }); + } + setState(() { + loading = false; + }); + } + + void addRelationship() async { + setState(() { + loading = true; + addFriends = false; + friends = false; + pending = false; + approve = false; + loading = false; + }); + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = widget.user.userGUID; + var register = serverLocation + 'relationships/friendrequest'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + setState(() { + request = 52; + pending = true; + friends = false; + addFriends = friends; + }); + } else { + setState(() { + friends = false; + pending = false; + addFriends = true; + }); + } + setState(() { + loading = false; + }); + } + + void approveRelationship() async { + setState(() { + loading = true; + addFriends = false; + friends = false; + pending = false; + approve = false; + loading = false; + }); + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = widget.user.userGUID; + var register = serverLocation + 'relationships/friendapproval'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + setState(() { + pending = false; + friends = true; + addFriends = !friends; + approve = false; + }); + } else { + setState(() { + friends = false; + pending = false; + addFriends = false; + approve = true; + }); + } + setState(() { + loading = false; + }); + } + + void declineRelationship() async { + setState(() { + loading = true; + addFriends = false; + friends = false; + pending = false; + approve = false; + loading = false; + }); + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = widget.user.userGUID; + var register = serverLocation + 'relationships/frienddecline'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + setState(() { + pending = false; + friends = false; + addFriends = !friends; + approve = false; + }); + } else { + setState(() { + friends = false; + pending = false; + addFriends = false; + approve = true; + }); + } + setState(() { + loading = false; + }); + } + + void cancelRelationshipRequest() async { + setState(() { + request = 0; + loading = true; + addFriends = false; + friends = false; + pending = false; + approve = false; + loading = false; + }); + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + String searchValue = widget.user.userGUID; + var register = serverLocation + 'relationships/cancelrequest'; + var client = await http.post(Uri.parse(register), + body: json.encode(searchValue), headers: requestHeaders); + if (client.statusCode == 200) { + setState(() { + friends = false; + pending = false; + addFriends = !friends; + }); + } + setState(() { + loading = false; + }); + } + + @override + Widget build(BuildContext context) { + if (scrollController.hasClients) { + scale = scrollController.offset / 250; + scale = scale * 2; + if (scale > 1.90) { + scrolled = true; + } else { + scrolled = false; + } + } else { + scale = 0.0; + scrolled = false; + } + return Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(238), + child: Column( + children: [ + AppBar( + backgroundColor: Theme.of(context).backgroundColor, + elevation: 0.0, + leadingWidth: 50, + centerTitle: true, + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + ), + onPressed: () => Navigator.pop(context), + ), + title: Text( + widget.user.username, + style: TextStyle( + fontSize: 15, + ), + ), + actions: [ + Visibility( + visible: addFriends, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 10), + height: 30.0, + width: 40.0, + decoration: new BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: accentMain, width: 2.0), + ), + child: IconButton( + icon: Icon( + Icons.person_add_alt, + color: accentMain, + ), + onPressed: addRelationship, + ), + ), + ), + IconButton( + onPressed: () => statusDialog(context), + icon: Icon( + Icons.more_horiz, + ), + ) + ], + ), + Container( + width: width.value, + child: buildHead( + context: context, + loading: loading, + user: widget.user, + posts: userProfile.posts != null + ? Numeral(userProfile.posts.length).value().toString() + : "0", + friends: userProfile.friends, + following: userProfile.posts, + relation: friends, + // unFriend: removeRelationship, + // approve: approveRelationship, + pending: pending, + // approveRequest: approve, + //cancelRequest: cancelRelationshipRequest, + // decline: declineRelationship + ), + ), + ], + ), + ), + body: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.data == null && + snapshot.connectionState == ConnectionState.waiting) { + return Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ); + } else if (userProfile.posts.length == 0 && + snapshot.connectionState == ConnectionState.done) { + return buildEmpty3P(context); + } else { + return Container( + height: MediaQuery.of(context).size.height, + child: StaggeredGridView.count( + physics: scrolled + ? NeverScrollableScrollPhysics() + : AlwaysScrollableScrollPhysics(), + controller: _controller, + crossAxisCount: 3, + children: List.generate(userProfile.posts.length, (int index) { + return buildPosted( + context, + userProfile.posts.elementAt(index), + 0.325, + widget.user, + addFriends); + }), + staggeredTiles: List.generate( + userProfile.posts.length, + (int index) { + return StaggeredTile.fit(1); + }, + ), + ), + ); + } + }, + ), + ); + } + + void statusDialog(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return SizedBox( + height: MediaQuery.of(context).size.height * 0.5, + child: Column( + children: [ + new Container( + margin: EdgeInsets.symmetric( + vertical: 15.0, + ), + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Container( + width: 50, + height: 4, + color: Colors.grey, + ), + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + height: MediaQuery.of(context).size.height * 0.4, + padding: EdgeInsets.symmetric(horizontal: 15), + child: new ListView( + scrollDirection: Axis.vertical, + children: [ + ListTile( + title: Center( + child: Text( + "Block User", + style: TextStyle( + color: Colors.red[800], + fontSize: SizeConfig.blockSizeHorizontal * 4.5, + fontWeight: FontWeight.bold), + ), + ), + onTap: () => blockDialog(context), + ), + Visibility(visible: friends, child: Divider()), + Visibility( + visible: friends, + child: ListTile( + title: Center( + child: Text( + "Send Message", + style: TextStyle( + color: Colors.blueAccent, + fontSize: SizeConfig.blockSizeHorizontal * 4.5, + fontWeight: FontWeight.bold, + ), + ), + ), + onTap: () => Navigator.pushReplacement( + context, + PageTransition( + child: ChatScreen( + user: widget.user, + ), + type: PageTransitionType.fade, + ), + ), + ), + ), + Visibility(visible: addFriends, child: Divider()), + Visibility( + visible: addFriends, + child: ListTile( + title: Center( + child: Text( + "Add Friend", + style: TextStyle( + color: Colors.blueAccent, + fontSize: + SizeConfig.blockSizeHorizontal * 4.5, + fontWeight: FontWeight.bold), + ), + ), + onTap: () { + addRelationship(); + + Navigator.pop(context); + }), + ), + Visibility(visible: pending, child: Divider()), + Visibility( + visible: pending, + child: ListTile( + title: Center( + child: Text( + "Cancel Friend Request", + style: TextStyle( + color: Colors.red[800], + fontSize: + SizeConfig.blockSizeHorizontal * 4.5, + fontWeight: FontWeight.bold), + ), + ), + onTap: () { + declineRelationship(); + + Navigator.pop(context); + }), + ), + Visibility(visible: approve, child: Divider()), + Visibility( + visible: approve, + child: ListTile( + title: Center( + child: Text( + "Accept Friend Request", + style: TextStyle( + color: Colors.blueAccent, + fontSize: SizeConfig.blockSizeHorizontal * 4.5, + fontWeight: FontWeight.bold), + ), + ), + onTap: approveRelationship, + ), + ), + Visibility(visible: approve, child: Divider()), + Visibility( + visible: approve, + child: ListTile( + title: Center( + child: Text( + "Decline Friend Request", + style: TextStyle( + color: Colors.red[800], + fontSize: + SizeConfig.blockSizeHorizontal * 4.5, + fontWeight: FontWeight.bold), + ), + ), + onTap: () { + declineRelationship(); + + Navigator.pop(context); + }), + ), + Visibility(visible: friends, child: Divider()), + Visibility( + visible: friends, + child: ListTile( + title: Center( + child: Text( + "Unfriend", + style: TextStyle( + color: Colors.red[800], + fontSize: + SizeConfig.blockSizeHorizontal * 4.5, + fontWeight: FontWeight.bold), + ), + ), + onTap: () { + removeRelationship(); + + Navigator.pop(context); + }), + ), + ], + ), + ), + ], + ), + ); + }, + ); + } + + blockDialog(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return SizedBox( + height: MediaQuery.of(context).size.height * 0.38, + child: Column( + children: [ + new Container( + margin: EdgeInsets.symmetric( + vertical: 15.0, + ), + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Container( + width: 50, + height: 4, + color: Colors.grey, + ), + ), + ), + ), + SizedBox( + height: 5, + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 15), + child: Text( + "Block ${widget.user.username.toUpperCase()} ? ", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + Divider(), + Container( + padding: EdgeInsets.symmetric(horizontal: 15), + child: Text( + "${widget.user.username} won't be able to message you or find your profile or post on Teso. Neither would you, " + "they won't be notified that you have blocked them. You would have to unblock them to be able to interact with them.", + style: TextStyle( + color: Colors.grey, + fontSize: SizeConfig.blockSizeHorizontal * 3.5, + ), + ), + ), + Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width - 20, + height: 40, + child: RaisedGradientButton( + child: Text( + "Continue", + style: TextStyle(color: Colors.white, fontSize: 18), + ), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + // Colors.green[400], + // Colors.green[600], + Colors.red[800], + Colors.redAccent, + ], + ), + onPressed: () { + Provider.of(context, listen: false) + .blockUser(widget.user); + Navigator.pop(context, true); + Navigator.pop(context, true); + }, + width: MediaQuery.of(context).size.width - 20, + height: 40, + ), + ) + ], + ), + ); + }, + ); + } +} diff --git a/lib/Pages/codeQR.dart b/lib/Pages/codeQR.dart new file mode 100644 index 0000000..92845aa --- /dev/null +++ b/lib/Pages/codeQR.dart @@ -0,0 +1,125 @@ +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +// import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:flutter_qrcode_scanner/flutter_qrcode_scanner.dart'; + +class QRCodeScanner extends StatefulWidget { + const QRCodeScanner({ + Key key, + }) : super(key: key); + + @override + State createState() => _QRCodeScannerState(); +} + +class _QRCodeScannerState extends State { + var result; + String resultingCode = ""; + QRViewController controller; + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + + // In order to get hot reload to work we need to pause the camera if the platform + // is android, or resume the camera if the platform is iOS. + @override + void reassemble() { + super.reassemble(); + if (Platform.isAndroid) { + controller.pauseCamera(); + } + controller.resumeCamera(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + _buildQrView(context), + Container( + margin: EdgeInsets.only( + top: (MediaQuery.of(context).size.height) - + (MediaQuery.of(context).size.height * 0.935), + left: 10), + child: Material( + elevation: 5, + color: Color.fromRGBO(0, 0, 0, 0.4), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + topLeft: Radius.circular(25), + topRight: Radius.circular(25), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(25.0), + child: IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 20, + ), + color: Colors.white, + onPressed: () => Navigator.pop(context), + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.18, + ), + child: Text( + "Scan QR Code from vender to redeem", + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildQrView(BuildContext context) { + // For this example we check how width or tall the device is and change the scanArea and overlay accordingly. + var scanArea = (MediaQuery.of(context).size.width < 400 || + MediaQuery.of(context).size.height < 400) + ? 150.0 + : 300.0; + // To ensure the Scanner view is properly sizes after rotation + // we need to listen for Flutter SizeChanged notification and update controller + return QRView( + key: qrKey, + onQRViewCreated: _onQRViewCreated, + overlay: QrScannerOverlayShape( + borderColor: Colors.red, + borderRadius: 10, + borderLength: 30, + borderWidth: 10, + cutOutSize: scanArea), + ); + } + + void _onQRViewCreated(QRViewController controller) { + setState(() { + this.controller = controller; + }); + controller.scannedDataStream.listen((scanData) { + setState(() { + resultingCode = scanData; + }); + if (resultingCode.isNotEmpty) { + this.controller.dispose(); + Navigator.pop(context, resultingCode); + } + }); + } + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } +} diff --git a/lib/Pages/explore.dart b/lib/Pages/explore.dart new file mode 100644 index 0000000..bfdae28 --- /dev/null +++ b/lib/Pages/explore.dart @@ -0,0 +1,369 @@ +import 'dart:convert'; +import 'package:teso/Pages/Sub_Pages/Explore/Latest/AllLatest.dart'; +import 'package:camera/camera.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:teso/Classes/API%20Clasess/ExploreObject.dart'; +import 'package:teso/Classes/categories.dart'; +import 'package:teso/Pages/PageWidgets/Explore/categoriesTile.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/productDetails.dart'; +import 'package:teso/Pages/Sub_Pages/Explore/Categories/AllCategories.dart'; +import 'package:teso/Pages/Sub_Pages/Explore/Categories/ExploreCategory.dart'; +import 'package:teso/Pages/Sub_Pages/Explore/Trending/AllTrending.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Pages/PageWidgets/Explore/header.dart'; +import 'package:teso/Pages/PageWidgets/Explore/popular.dart'; +import 'package:teso/Pages/PageWidgets/Explore/trending.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'dart:io'; +import 'Sub_Pages/Explore/ML/FindProduct.dart'; +import 'Sub_Pages/@Generic/Camera/Picture/TakePicture.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +class Explore extends StatefulWidget { + final List connectedCameras; + + const Explore({Key key, this.connectedCameras}) : super(key: key); + @override + _ExploreState createState() => _ExploreState(); +} + +class _ExploreState extends State with TickerProviderStateMixin { + var search = new TextEditingController(); + // List trends; + List showTrends = []; + List categories = Category.category; + // List arrivals; + List showarrivals = []; + File _image; + String thumb; + SharedPreferences prefs; + var _future; + ExploreClass deadExplore; + RefreshController _refreshController = + RefreshController(initialRefresh: false); + + Future loadPage() async { + try { + prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + var register = serverLocation + 'explore/main'; + var client = await http.get(Uri.parse(register), headers: requestHeaders); + if (client.statusCode == 200) { + ExploreClass exploreClass; + Map products = jsonDecode(client.body); + SharedPreferences.getInstance() + .then((value) => value.setString("explore", client.body)); + setState(() { + exploreClass = ExploreClass.fromJSON(products); + showTrends = exploreClass.trending; + showarrivals = exploreClass.latest; + }); + return exploreClass; + } else { + return null; + } + } catch (e) { + print(e); + return null; + } + } + + void _onRefresh() async { + await loadPage(); + _refreshController.refreshCompleted(); + } + + @override + void initState() { + super.initState(); + SharedPreferences.getInstance().then((value) { + if (value.getString("explore") != null) { + var products = jsonDecode(value.getString("explore")); + setState(() { + deadExplore = ExploreClass.fromJSON(products); + showTrends = deadExplore.trending; + showarrivals = deadExplore.latest; + }); + } + }); + + _future = loadPage(); + } + + pickImage() async { + final pickedFile = await Navigator.push( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: TakePicture( + connectedCameras: widget.connectedCameras, + ), + )); + + if (pickedFile != null) { + setState(() { + _image = File(pickedFile.path); + }); + Navigator.push( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: MLFindProduct( + searchImage: _image, + ), + )); + } else { + print('No image selected.'); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: Container( + padding: EdgeInsets.only(top: 25.0), + child: buildSearch(context, search, pickImage), + ), + preferredSize: Size.fromHeight(150.0), + ), + body: FutureBuilder( + initialData: deadExplore, + future: _future, + builder: (context, snapshot) { + if (snapshot.data == null && + deadExplore == null && + snapshot.connectionState == ConnectionState.waiting) { + return Container( + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ); + } else if (snapshot.data == null && + deadExplore == null && + snapshot.connectionState == ConnectionState.done) { + return SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: ClassicHeader(), + controller: _refreshController, + onRefresh: _onRefresh, + child: Container( + child: Center( + child: Text("An error occurred"), + ), + ), + ); + } else { + // trends = snapshot.data != null + // ? snapshot.data.trending + // : deadExplore.trending; + // showTrends = trends; + // arrivals = snapshot.data != null + // ? snapshot.data.latest + // : deadExplore.latest; + // showarrivals = arrivals; + + return SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: ClassicHeader(), + controller: _refreshController, + onRefresh: _onRefresh, + child: ListView( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Trending', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + letterSpacing: 1.5, + ), + ), + GestureDetector( + onTap: () => Navigator.push( + context, + PageTransition( + child: TrendingAll( + products: showTrends, + ), + type: PageTransitionType.leftToRight), + ), + child: Text( + "See More", + style: TextStyle( + color: slimAccent, + fontWeight: FontWeight.bold, + fontSize: 16.5, + ), + ), + ), + ], + ), + ), + Container( + width: double.infinity, + height: MediaQuery.of(context).size.width * 0.65, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: showTrends.length > 6 ? 6 : showTrends.length, + itemBuilder: (context, index) { + return InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: ProductDetails( + product: showTrends.elementAt(index), + ), + ), + ), + child: buildTrend( + context, + showTrends.elementAt(index), + ), + ); + }, + ), + ), + SizedBox( + height: 20, + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Categories', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + letterSpacing: 1.5, + ), + ), + GestureDetector( + onTap: () => Navigator.push( + context, + PageTransition( + child: CategoriesAll(), + type: PageTransitionType.leftToRight), + ), + child: Text( + "See All", + style: TextStyle( + color: slimAccent, + fontWeight: FontWeight.bold, + fontSize: 16.5, + ), + ), + ), + ], + ), + ), + Container( + width: double.infinity, + height: 80, + padding: EdgeInsets.only(left: 10.0), + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: 10, + itemBuilder: (context, index) { + return InkWell( + onTap: () => Navigator.push( + context, + PageTransition( + child: ExploreCategory( + selectedCategory: categories.elementAt(index), + ), + type: PageTransitionType.leftToRight), + ), + child: buildCategory( + context, + categories.elementAt(index), + ), + ); + }, + ), + ), + SizedBox( + height: 20, + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'New Arrivals', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + letterSpacing: 1.5, + ), + ), + GestureDetector( + onTap: () => Navigator.push( + context, + PageTransition( + child: NewArrivals( + products: showarrivals, + ), + type: PageTransitionType.leftToRight), + ), + child: Text( + "See All", + style: TextStyle( + color: slimAccent, + fontWeight: FontWeight.bold, + fontSize: 16.5, + ), + ), + ), + ], + ), + ), + Container( + height: MediaQuery.of(context).size.height, + child: GridView.count( + physics: NeverScrollableScrollPhysics(), + crossAxisCount: 2, + crossAxisSpacing: 5.0, + mainAxisSpacing: 5.0, + children: List.generate( + showarrivals.length > 6 ? 6 : showarrivals.length, + (index) { + return buildPopularItem( + context, + showarrivals.elementAt(index), + ); + }, + ), + ), + ), + ], + ), + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/firsttimer.dart b/lib/Pages/firsttimer.dart new file mode 100644 index 0000000..9ae230c --- /dev/null +++ b/lib/Pages/firsttimer.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:teso/util/SizeConfig.dart'; + +class FirstTimeIntro extends StatefulWidget { + const FirstTimeIntro({Key key}) : super(key: key); + + @override + _FirstTimeIntroState createState() => _FirstTimeIntroState(); +} + +class _FirstTimeIntroState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + extendBodyBehindAppBar: false, + appBar: AppBar( + automaticallyImplyLeading: false, + toolbarHeight: 05, + backgroundColor: Colors.transparent, + ), + body: GestureDetector( + onPanUpdate: (details) { + // Swiping in right direction. + if (details.delta.dx > 0) { + Navigator.pop(context, 0); + } + + // Swiping in left direction. + if (details.delta.dx < 0) { + Navigator.pop(context, 2); + } + }, + onTap: () => Navigator.pop(context, 1), + child: Container( + width: SizeConfig.blockSizeHorizontal * 100, + height: SizeConfig.blockSizeVertical * 100, + child: Image.asset( + "assets/images/firstTime.png", + fit: BoxFit.fitHeight, + ), + ), + ), + ); + } +} diff --git a/lib/Pages/home.dart b/lib/Pages/home.dart new file mode 100644 index 0000000..92c8b3d --- /dev/null +++ b/lib/Pages/home.dart @@ -0,0 +1,218 @@ +import 'package:flutter/material.dart'; +import 'package:progress_indicators/progress_indicators.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/Uploading.dart'; +import 'package:teso/Pages/Sub_Pages/homeSub/MyCoupons.dart'; +import 'package:teso/Pages/Sub_Pages/homeSub/HomeFeed.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/consts.dart'; +import 'Sub_Pages/CoinPurchase.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:provider/provider.dart'; + +import 'Sub_Pages/homeSub/ProximityCoupons.dart'; +import 'firsttimer.dart'; + +class Home extends StatefulWidget { + final Function showUploads; + final Function toggle; + const Home({Key key, this.showUploads, this.toggle}) : super(key: key); + @override + _HomeState createState() => _HomeState(showUploads: this.showUploads); +} + +class _HomeState extends State with TickerProviderStateMixin { + Function showUploads; + _HomeState({this.showUploads}); + TabController tabsController; + String title = "Home"; + double titleSize = 45.0; + + @override + void initState() { + super.initState(); + tabsController = new TabController(length: 3, initialIndex: 1, vsync: this); + tabsController.addListener(() { + if (tabsController.index == 1) { + setState(() { + title = "Home"; + titleSize = 45.0; + }); + widget.toggle(true); + } else if (tabsController.index == 2) { + setState(() { + title = "Proximity Coupons"; + titleSize = 30.0; + }); + widget.toggle(false); + } else { + setState(() { + title = "Coupons"; + titleSize = 45.0; + }); + widget.toggle(true); + } + }); + SharedPreferences.getInstance().then((prefs) async { + bool first = + prefs.getBool("launched") != null ? prefs.getBool("launched") : false; + if (!first) { + int result = await Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: FirstTimeIntro(), + )); + prefs.setBool("launched", true); + tabsController.animateTo(result); + } + }); + } + + @override + void dispose() { + super.dispose(); + tabsController.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: buildAppBar(context), + body: TabBarView( + controller: tabsController, + children: [ + MyCoupons(), + HomeFeed(), + ProximityCoupons(), + ], + ), + ); + } + + Widget buildAppBar(BuildContext context) { + return PreferredSize( + child: Material( + //elevation: 0.4, + child: AppBar( + backgroundColor: Theme.of(context).primaryColor, + automaticallyImplyLeading: false, + title: Consumer(builder: + (BuildContext context, UserProvider value, Widget child) { + List provider = value.getPending(); + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: TextStyle( + color: Theme.of(context).primaryColorLight, + fontFamily: 'DeadheadScript', + fontSize: titleSize, + letterSpacing: 3.0, + ), + ), + Container( + height: 30, + child: Row( + children: [ + provider == null || provider.length == 0 + ? Container() + : GestureDetector( + onTap: () => showUploads(context), + child: Container( + height: 40, + width: 40, + margin: EdgeInsets.only(right: 15), + child: HeartbeatProgressIndicator( + child: Icon( + Icons.upload, + color: tesoBlue, + size: 18, + ), + ), + ), + ), + GestureDetector( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.downToUp, + child: Coins( + initalPage: 0, + ), + ), + ), + child: Image( + image: AssetImage("assets/images/gold1.png"), + ), + ), + GestureDetector( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.downToUp, + child: Coins(initalPage: 0), + ), + ), + child: Container( + margin: EdgeInsets.only(left: 2), + child: Center( + child: Text( + value.currentUser != null + ? value.currentUser.gold + : "0", + style: TextStyle( + fontSize: 13.5, + color: Theme.of(context).primaryColorLight, + ), + ), + ), + ), + ), + GestureDetector( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.downToUp, + child: Coins(initalPage: 1), + ), + ), + child: Image( + image: AssetImage("assets/images/silver1.png"), + ), + ), + GestureDetector( + onTap: () => Navigator.push( + context, + PageTransition( + type: PageTransitionType.downToUp, + child: Coins(initalPage: 1), + ), + ), + child: Container( + margin: EdgeInsets.only(left: 2), + child: Center( + child: Text( + value.currentUser != null + ? value.currentUser.silver + : "0", + style: TextStyle( + fontSize: 13.5, + color: Theme.of(context).primaryColorLight, + ), + ), + ), + ), + ), + ], + ), + ), + ], + ); + }), + ), + ), + preferredSize: Size.fromHeight(50.0)); + } +} diff --git a/lib/Pages/notifications.dart b/lib/Pages/notifications.dart new file mode 100644 index 0000000..cb332ec --- /dev/null +++ b/lib/Pages/notifications.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; +import 'PageWidgets/Alerts/header.dart'; +import 'Sub_Pages/Notifications/Alerts.dart'; +import 'Sub_Pages/Notifications/Inbox.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/TesoUser.dart'; + +class Notifications extends StatefulWidget { + @override + _NotificationsState createState() => _NotificationsState(); +} + +class _NotificationsState extends State { + // ignore: avoid_init_to_null + Color fcurrentColor = null; + Color fTextColor1 = Colors.white; + Color fcurrentColor1; + Color fTextColor2; + int _page = 0; + PageController _pageController; + bool chats = false; + + @override + void initState() { + super.initState(); + _pageController = PageController(initialPage: _page); + fTextColor1 = Colors.white; + fTextColor2 = Colors.grey; + //setColor(); + } + + void setColor() { + setState(() { + fcurrentColor = Theme.of(context).colorScheme.secondary; + fTextColor1 = Colors.white; + }); + } + + void navigationTapped(int page) { + _pageController.animateToPage( + page, + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + ); + switch (page) { + case 0: + setState(() { + fcurrentColor = Theme.of(context).colorScheme.secondary; + fTextColor1 = Colors.white; + fcurrentColor1 = Theme.of(context).primaryColor; + fTextColor2 = Colors.grey; + chats = false; + }); + break; + case 1: + setState(() { + fcurrentColor1 = Theme.of(context).colorScheme.secondary; + fTextColor1 = Colors.grey; + fcurrentColor = Theme.of(context).primaryColor; + fTextColor2 = Colors.white; + chats = true; + }); + break; + } + } + + void onPageChanged(int page) { + setState(() { + this._page = page; + }); + switch (page) { + case 0: + setState(() { + fcurrentColor = Theme.of(context).colorScheme.secondary; + fTextColor1 = Colors.white; + fcurrentColor1 = Theme.of(context).primaryColor; + fTextColor2 = Colors.grey; + chats = false; + }); + break; + case 1: + setState(() { + fcurrentColor1 = Theme.of(context).colorScheme.secondary; + fTextColor1 = Colors.grey; + fcurrentColor = Theme.of(context).primaryColor; + fTextColor2 = Colors.white; + chats = true; + }); + break; + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + TesoUser user = + Provider.of(context, listen: false).currentUser; + return Scaffold( + appBar: PreferredSize( + child: AppBar( + backgroundColor: Theme.of(context).primaryColor, + title: buildNotficationHeader( + context, + fcurrentColor, + fcurrentColor1, + fTextColor1, + fTextColor2, + navigationTapped, + chats, + user), + ), + preferredSize: Size.fromHeight(50)), + body: PageView( + //physics:, + controller: _pageController, + onPageChanged: onPageChanged, + children: [ + Alerts(), + Inbox( + user: user, + ), + ], + ), + ); + } +} diff --git a/lib/Pages/productView.dart b/lib/Pages/productView.dart new file mode 100644 index 0000000..88a66e5 --- /dev/null +++ b/lib/Pages/productView.dart @@ -0,0 +1,364 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API%20Clasess/CouponHead.dart'; +import 'package:teso/Classes/CouponRateCalculator.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/ProductImage.dart'; +import 'package:teso/providers/device_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; + +class ProductView extends StatefulWidget { + final CouponDetails couponDetails; + + const ProductView({Key key, this.couponDetails}) : super(key: key); + @override + _ProductViewState createState() => _ProductViewState(); +} + +enum AppBarBehavior { normal, pinned, floating, snapping } + +class _ProductViewState extends State + with TickerProviderStateMixin { + AnimationController _containerController; + Animation width; + Animation heigth; + double _appBarHeight = 256.0; + AppBarBehavior _appBarBehavior = AppBarBehavior.pinned; + int number = 0; + bool acquiring = false; + + void acceptCoupon() { + setState(() { + acquiring = true; + }); + CouponsHead couponsHead = new CouponsHead(); + couponsHead.businessId = widget.couponDetails.businessId; + couponsHead.expiration = widget.couponDetails.expiration; + couponsHead.couponId = widget.couponDetails.couponId; + couponsHead.quantity = 1; + couponsHead.state = "active"; + couponsHead.lower = + double.parse(widget.couponDetails.lowerLimit.toString()); + couponsHead.upper = + double.parse(widget.couponDetails.upperLimit.toString()); + couponsHead.targetProduct = widget.couponDetails.targetProduct.productID; + double price = (widget.couponDetails.targetProduct.unitPrice * + (widget.couponDetails.lowerLimit / 100)); + + //Calculations + int cost = CouponRateCalculator.getRate(price); + Provider.of(context, listen: false) + .acceptCoupon(couponsHead, cost.ceil(), context); + setState(() { + acquiring = false; + }); + } + + void redeemCoupon() { + setState(() { + acquiring = true; + }); + + //Calculations + // int cost = CouponRateCalculator.getRate(price); + // Provider.of(context, listen: false) + // .acceptCoupon(couponsHead, cost.ceil(), context); + setState(() { + acquiring = false; + }); + } + + @override + void initState() { + _containerController = new AnimationController( + duration: new Duration(milliseconds: 2000), vsync: this); + super.initState(); + width = new Tween( + begin: 200.0, + end: 220.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth = new Tween( + begin: 400.0, + end: 400.0, + ).animate( + new CurvedAnimation( + parent: _containerController, + curve: Curves.ease, + ), + ); + heigth.addListener(() { + setState(() { + if (heigth.isCompleted) {} + }); + }); + _containerController.forward(); + } + + @override + void dispose() { + _containerController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // timeDilation = 0.7; + return new Theme( + data: new ThemeData( + brightness: Brightness.light, + platform: Theme.of(context).platform, + ), + child: new Container( + width: width.value, + height: heigth.value, + child: new Card( + color: Colors.transparent, + child: new Container( + alignment: Alignment.center, + width: width.value, + height: heigth.value, + decoration: new BoxDecoration( + color: Colors.white, + borderRadius: new BorderRadius.circular(10.0), + ), + child: new Stack( + alignment: AlignmentDirectional.bottomCenter, + children: [ + new CustomScrollView( + shrinkWrap: false, + slivers: [ + new SliverAppBar( + elevation: 0.0, + forceElevated: true, + leading: new IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + child: new Icon( + Icons.arrow_back, + color: Colors.white, + size: 25.0, + ), + ), + ), + expandedHeight: _appBarHeight, + pinned: _appBarBehavior == AppBarBehavior.pinned, + floating: _appBarBehavior == AppBarBehavior.floating || + _appBarBehavior == AppBarBehavior.snapping, + snap: _appBarBehavior == AppBarBehavior.snapping, + backgroundColor: Theme.of(context).backgroundColor, + flexibleSpace: new FlexibleSpaceBar( + background: new Stack( + fit: StackFit.expand, + children: [ + GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) { + return ProductImage( + productTag: widget + .couponDetails.targetProduct.businessID, + productImageSRC: widget.couponDetails + .targetProduct.productImage, + ); + })); + }, + child: new Container( + width: width.value, + height: _appBarHeight, + child: Hero( + tag: widget + .couponDetails.targetProduct.businessID, + child: CachedNetworkImage( + imageUrl: productURL + + widget.couponDetails.targetProduct + .productImage, + imageBuilder: (context, imageProvider) => + Image( + fit: BoxFit.fill, + image: imageProvider, + ), + ), + ), + ), + ), + ], + ), + ), + ), + new SliverList( + delegate: new SliverChildListDelegate([ + new Container( + color: Colors.white, + child: new Padding( + padding: const EdgeInsets.all(35.0), + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Container( + padding: new EdgeInsets.only(bottom: 20.0), + alignment: Alignment.center, + decoration: new BoxDecoration( + color: Colors.white, + border: new Border( + bottom: new BorderSide( + color: Colors.black12))), + child: new Wrap( + children: [ + new Center( + child: new Text( + widget.couponDetails.targetProduct + .productName, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + ), + new Row( + children: [ + new Icon(Icons.shopping_bag), + new Padding( + padding: const EdgeInsets.all(8.0), + child: new Text( + "Price : ", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + new Padding( + padding: const EdgeInsets.all(8.0), + child: new Text("GH¢ " + + widget.couponDetails + .targetProduct.unitPrice + .toString()), + ) + ], + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: new Row( + children: [ + new Icon( + Icons.store, + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text( + "Shop Name : ", + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + new Padding( + padding: + const EdgeInsets.all(8.0), + child: new Text(widget + .couponDetails + .issuer + .businessName), + ) + ], + ), + ), + ], + ), + ), + new Padding( + padding: const EdgeInsets.only( + top: 16.0, bottom: 8.0), + child: new Text( + "Product Description", + style: new TextStyle( + fontWeight: FontWeight.bold), + ), + ), + new Text(widget + .couponDetails.targetProduct.productDesc), + ], + ), + ), + ), + ]), + ), + ], + ), + widget.couponDetails.countID == "null" + ? Container( + height: 80.0, + padding: EdgeInsets.all(20), + child: new TextButton( + onPressed: acceptCoupon, + child: new Container( + height: 50.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: tesoGold, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Accept Coupon", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ) + : Container( + height: 80.0, + padding: EdgeInsets.all(20), + child: new TextButton( + onPressed: () => print(""), + child: new Container( + height: 50.0, + alignment: Alignment.center, + decoration: new BoxDecoration( + color: tesoGold, + borderRadius: new BorderRadius.circular(60.0), + ), + child: new Text( + "Redeem Coupon", + style: new TextStyle(color: Colors.white), + ), + ), + ), + ), + Visibility( + visible: acquiring, + child: Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + color: Colors.transparent, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/Pages/scannerqr.dart b/lib/Pages/scannerqr.dart new file mode 100644 index 0000000..d784bcc --- /dev/null +++ b/lib/Pages/scannerqr.dart @@ -0,0 +1,106 @@ +import 'dart:convert'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:teso/Classes/API Clasess/CouponDetails.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:teso/Pages/PageWidgets/WalkIn Coupons/ActiveDiscount.dart'; +import 'package:teso/Pages/PageWidgets/WalkIn Coupons/ActiveFreebie.dart'; + +class ScanQR extends StatefulWidget { + final String shopID; + const ScanQR({Key key, this.shopID}) : super(key: key); + @override + _ScanQRState createState() => _ScanQRState(shopID: this.shopID); +} + +class _ScanQRState extends State with TickerProviderStateMixin { + String shopID; + _ScanQRState({this.shopID}); + + int flag = 0; + var _future; + List data; + List selectedData = []; + double selectedDiscount = 0; + + void initState() { + super.initState(); + _future = getShopsCoupons(); + } + + @override + void dispose() { + super.dispose(); + } + + Future> getShopsCoupons() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + var register2 = serverLocation + 'coupons/qrCoupon'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(shopID), headers: requestHeaders); + + if (client1.statusCode == 200) { + try { + var details = jsonDecode(client1.body); + data = List.from( + details.map((model) => CouponDetails.fromJSON(model)).toList()); + return data; + } catch (e) { + print(e); + return null; + } + } else { + return null; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Redeem Coupon"), + ), + body: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.data == null && + snapshot.connectionState == ConnectionState.waiting) { + return Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ); + } else if ((data == null || data.length == 0) && + snapshot.connectionState == ConnectionState.done) { + return Center( + child: Text( + "Sorry you do not have any coupon from this shop to redeem"), + ); + } else { + return ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: data.length, + itemBuilder: (context, index) { + if (data.elementAt(index).type.contains("FREEBIE")) { + return buildActiveFreebieCoupon( + data.elementAt(index), 0, context); + } else { + return buildActiveDiscountCoupon( + data.elementAt(index), 0, context); + } + }, + ); + } + }, + ), + ); + } +} diff --git a/lib/Pages/splash.dart b/lib/Pages/splash.dart new file mode 100644 index 0000000..289a6b7 --- /dev/null +++ b/lib/Pages/splash.dart @@ -0,0 +1,164 @@ +import 'dart:async'; +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:page_transition/page_transition.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Services/DynamicLinks.dart'; +import 'package:teso/Services/locator.dart'; +import 'package:teso/main_screen.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'LandingPage.dart'; +import 'package:http/http.dart' as http; + +class Splash extends StatefulWidget { + @override + _SplashState createState() => _SplashState(); +} + +class _SplashState extends State with TickerProviderStateMixin { + List connectedCameras; + Animation _animation; + AnimationController _controller; + final DynamicLinkService _dynamicLinkService = locator(); + + gotoPersonnalServer() { + _controller = AnimationController( + duration: const Duration(milliseconds: 2000), vsync: this, value: 0.1); + _animation = + CurvedAnimation(parent: _controller, curve: Curves.bounceInOut); + + _controller.forward(); + } + + var timeout = const Duration(seconds: 2); + var ms = const Duration(milliseconds: 1); + + bool gone = false; + + startTimeout() { + // return new Timer(Duration(seconds: 5), loggedIn); + loggedIn(); + } + + void handleTimeout() { + changeScreen(); + } + + changeScreen() async { + Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: LandingPage( + connectedCameras: connectedCameras, + ), + ), + ); + //Provider.of(context, listen: false).getFeeds(); + } + + void loggedIn() async { + await _dynamicLinkService.handleDynamicLinks(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + String tokens = prefs.getString("tokensTeso"); + if (tokens != null) { + Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.rightToLeft, + child: MainScreens( + connectedCameras: connectedCameras, + ), + ), + ); + } else { + changeScreen(); + } + } + + Future apiversionCheck() async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString('tokensTeso'); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'api/version/current'; + var client = await http.get(Uri.parse(register)).timeout( + const Duration(seconds: 5), + onTimeout: () { + return http.Response( + 'Error', 408); // Request Timeout response status code + }, + ); + if (client.statusCode == 200) { + String currentVersion = prefs.getString("api-version") != null + ? prefs.getString("api-version") + : ""; + if (currentVersion != client.body) { + try { + var register = Uri.parse(serverLocation + 'api/logout'); + await http.get(register, headers: requestHeaders); + } catch (_) {} + await prefs.clear(); + await prefs.setBool("launched", true); + await prefs.setString("api-version", client.body); + } + } + return true; + } catch (e) { + print(e); + return false; + } + } + + @override + void initState() { + super.initState(); + gotoPersonnalServer(); + availableCameras().then((value) { + connectedCameras = value; + }); + apiversionCheck().then((t) => {startTimeout()}); + } + + @override + Widget build(BuildContext context) { + SizeConfig().init(context); + return Scaffold( + backgroundColor: Colors.white, + body: Center( + child: Column( +// mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + alignment: Alignment.center, + child: ScaleTransition( + scale: _animation, + child: Image.asset( + "assets/images/tesoCouponInsignia.png", + width: 80.0, + ), + ), + ), + SizedBox( + height: 20, + ), + Text( + "${Constants.appName}", + style: TextStyle( + color: tesoGold, + fontSize: 25, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/Services/DynamicLinks.dart b/lib/Services/DynamicLinks.dart new file mode 100644 index 0000000..41e510e --- /dev/null +++ b/lib/Services/DynamicLinks.dart @@ -0,0 +1,44 @@ +import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; +import 'package:no_context_navigation/no_context_navigation.dart'; + +class DynamicLinkService { + Future handleDynamicLinks() async { + // 1. Get the initial dynamic link if the app is opened with a dynamic link + final PendingDynamicLinkData data = + await FirebaseDynamicLinks.instance.getInitialLink(); + + // 2. handle link that has been retrieved + _handleDeepLink(data); + + // 3. Register a link callback to fire if the app is opened up from the background + // using a dynamic link. + FirebaseDynamicLinks.instance.onLink( + onSuccess: (PendingDynamicLinkData dynamicLink) async { + // 3a. handle link that has been retrieved + _handleDeepLink(dynamicLink); + }, onError: (OnLinkErrorException e) async { + print('Link Failed: ${e.message}'); + }); + } + + void _handleDeepLink(PendingDynamicLinkData data) { + final Uri deepLink = data?.link; + if (deepLink != null) { + try { + if (deepLink.pathSegments.contains('resetpassword')) { + var guid = deepLink.queryParameters['resetguid']; + if (guid != null) { + navService.pushNamed('/resetpassword', args: guid); + } + } else if (deepLink.pathSegments.contains('referral')) { + var guid = deepLink.queryParameters['referrer']; + if (guid != null) { + navService.pushNamed('/login', args: guid); + } + } + } catch (e) { + print(e); + } + } + } +} diff --git a/lib/Services/locator.dart b/lib/Services/locator.dart new file mode 100644 index 0000000..395744b --- /dev/null +++ b/lib/Services/locator.dart @@ -0,0 +1,10 @@ +import 'package:get_it/get_it.dart'; +import 'package:teso/Services/DynamicLinks.dart'; +import 'package:teso/Services/navigation_service.dart'; + +GetIt locator = GetIt.instance; + +void setupLocator() { + locator.registerLazySingleton(() => NavigationService()); + locator.registerLazySingleton(() => DynamicLinkService()); +} diff --git a/lib/Services/navigation_service.dart b/lib/Services/navigation_service.dart new file mode 100644 index 0000000..76be921 --- /dev/null +++ b/lib/Services/navigation_service.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class NavigationService { + GlobalKey _navigationKey = GlobalKey(); + + pop() { + return _navigationKey.currentState.pop(); + } + + Future navigateTo(String routeName, {dynamic arguments}) { + return _navigationKey.currentState + .pushNamed(routeName, arguments: arguments); + } +} diff --git a/lib/Services/services.dart b/lib/Services/services.dart new file mode 100644 index 0000000..24f38f5 --- /dev/null +++ b/lib/Services/services.dart @@ -0,0 +1 @@ +export 'uservideo_controller_service.dart'; diff --git a/lib/Services/services2.dart b/lib/Services/services2.dart new file mode 100644 index 0000000..181d3ee --- /dev/null +++ b/lib/Services/services2.dart @@ -0,0 +1 @@ +export 'video_controller_service.dart'; diff --git a/lib/Services/uservideo_controller_service.dart b/lib/Services/uservideo_controller_service.dart new file mode 100644 index 0000000..e22e6c7 --- /dev/null +++ b/lib/Services/uservideo_controller_service.dart @@ -0,0 +1,67 @@ +import 'package:better_player/better_player.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; +import 'package:teso/util/consts.dart'; + +abstract class VideoControllerService { + Future getControllerForVideo(FBPosts video); +} + +class CachedVideoControllerService extends VideoControllerService { + // ignore: unused_field + final BaseCacheManager _cacheManager; + + CachedVideoControllerService(this._cacheManager) + : assert(_cacheManager != null); + + @override + Future getControllerForVideo(FBPosts video) async { + try { + BetterPlayerDataSource betterPlayerDataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + "https://stream.mux.com/${video.playbackID}.m3u8", + videoFormat: BetterPlayerVideoFormat.hls, + cacheConfiguration: BetterPlayerCacheConfiguration( + useCache: true, + ), + ); + + return BetterPlayerController( + BetterPlayerConfiguration( + autoPlay: true, + aspectRatio: double.tryParse(video.aspect), + looping: true, + fit: double.parse(video.aspect) < 1 + ? BoxFit.fitHeight + : BoxFit.fitWidth, + showPlaceholderUntilPlay: true, + placeholder: Container( + child: Center( + child: CachedNetworkImage( + imageUrl: tesoPostThumb(video.playbackID), + imageBuilder: (context, imageProvider) => FadeInImage( + width: double.infinity, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/blank.jpg"), + ), + errorWidget: (context, url, error) => + Image.asset("assets/images/blank.jpg"), + ), + ), + ), + controlsConfiguration: BetterPlayerControlsConfiguration( + showControls: false, + ), + autoDispose: true, + ), + betterPlayerDataSource: betterPlayerDataSource, + ); + } catch (e) { + // return BetterPlayerController.network(tesoStreaming + "pd/" + video.path); + return null; + } + } +} diff --git a/lib/Services/video_controller_service.dart b/lib/Services/video_controller_service.dart new file mode 100644 index 0000000..abaf346 --- /dev/null +++ b/lib/Services/video_controller_service.dart @@ -0,0 +1,66 @@ +import 'package:better_player/better_player.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/util/consts.dart'; + +abstract class VideoControllerService { + Future getControllerForVideo(Post video); +} + +class CachedVideoControllerService extends VideoControllerService { + // ignore: unused_field + final BaseCacheManager _cacheManager; + + CachedVideoControllerService(this._cacheManager) + : assert(_cacheManager != null); + + @override + Future getControllerForVideo(Post video) async { + try { + BetterPlayerDataSource betterPlayerDataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + "https://stream.mux.com/${video.playbackID}.m3u8", + videoFormat: BetterPlayerVideoFormat.hls, + cacheConfiguration: BetterPlayerCacheConfiguration( + useCache: true, + ), + ); + + return BetterPlayerController( + BetterPlayerConfiguration( + autoPlay: true, + aspectRatio: double.tryParse(video.aspect), + looping: true, + fit: BoxFit.fill, + showPlaceholderUntilPlay: true, + placeholder: Container( + child: Center( + child: CachedNetworkImage( + imageUrl: tesoPostThumb(video.playbackID), + imageBuilder: (context, imageProvider) => FadeInImage( + width: double.infinity, + fit: BoxFit.fill, + image: imageProvider, + placeholder: AssetImage("assets/images/blank.jpg"), + ), + errorWidget: (context, url, error) => + Image.asset("assets/images/blank.jpg"), + ), + ), + ), + controlsConfiguration: BetterPlayerControlsConfiguration( + showControls: false, + ), + autoDispose: true, + ), + betterPlayerDataSource: betterPlayerDataSource, + ); + } catch (e) { + // return BetterPlayerController.network(tesoStreaming + "pd/" + video.path); + return null; + } + } +} diff --git a/lib/blocs/blocs.dart b/lib/blocs/blocs.dart new file mode 100644 index 0000000..66e351c --- /dev/null +++ b/lib/blocs/blocs.dart @@ -0,0 +1 @@ +export 'video_player/video_player.dart'; \ No newline at end of file diff --git a/lib/blocs/video_player/uservideo_player.dart b/lib/blocs/video_player/uservideo_player.dart new file mode 100644 index 0000000..92e29c3 --- /dev/null +++ b/lib/blocs/video_player/uservideo_player.dart @@ -0,0 +1,3 @@ +export 'video_player_bloc.dart'; +export 'uservideo_player_event.dart'; +export 'uservideo_player_state.dart'; diff --git a/lib/blocs/video_player/uservideo_player_bloc.dart b/lib/blocs/video_player/uservideo_player_bloc.dart new file mode 100644 index 0000000..b557ea1 --- /dev/null +++ b/lib/blocs/video_player/uservideo_player_bloc.dart @@ -0,0 +1,28 @@ +import 'package:bloc/bloc.dart'; +import 'package:teso/Services/services.dart'; + +import 'uservideo_player.dart'; + +class VideoPlayerBloc extends Bloc { + final VideoControllerService _videoControllerService; + + VideoPlayerBloc(this._videoControllerService) + : assert(_videoControllerService != null), + super(null); + + VideoPlayerState get initialState => VideoPlayerStateInitial(); + + @override + Stream mapEventToState(VideoPlayerEvent event) async* { + if (event is VideoSelectedEvent) { + yield VideoPlayerStateLoading(); + try { + final videoController = + await _videoControllerService.getControllerForVideo(event.video); + yield VideoPlayerStateLoaded(event.video, videoController); + } catch (e) { + yield VideoPlayerStateError(e.message ?? 'An unknown error occurred'); + } + } + } +} diff --git a/lib/blocs/video_player/uservideo_player_event.dart b/lib/blocs/video_player/uservideo_player_event.dart new file mode 100644 index 0000000..5d9835c --- /dev/null +++ b/lib/blocs/video_player/uservideo_player_event.dart @@ -0,0 +1,16 @@ +import 'package:equatable/equatable.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; + +abstract class VideoPlayerEvent extends Equatable { + @override + List get props => const []; +} + +class VideoSelectedEvent extends VideoPlayerEvent { + final FBPosts video; + + VideoSelectedEvent(this.video) : assert(video != null); + + @override + List get props => [video]; +} diff --git a/lib/blocs/video_player/uservideo_player_state.dart b/lib/blocs/video_player/uservideo_player_state.dart new file mode 100644 index 0000000..eadba7c --- /dev/null +++ b/lib/blocs/video_player/uservideo_player_state.dart @@ -0,0 +1,36 @@ +import 'package:better_player/better_player.dart'; +import 'package:equatable/equatable.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; + +abstract class VideoPlayerState extends Equatable { + @override + List get props => const []; +} + +class VideoPlayerStateInitial extends VideoPlayerState { + @override + List get props => const []; +} + +class VideoPlayerStateLoading extends VideoPlayerState {} + +class VideoPlayerStateError extends VideoPlayerState { + final String message; + + VideoPlayerStateError(this.message); + + @override + List get props => [message]; +} + +class VideoPlayerStateLoaded extends VideoPlayerState { + final FBPosts video; + final BetterPlayerController controller; + + VideoPlayerStateLoaded(this.video, this.controller) + : assert(video != null), + assert(controller != null); + + @override + List get props => [video, controller]; +} diff --git a/lib/blocs/video_player/video_player.dart b/lib/blocs/video_player/video_player.dart new file mode 100644 index 0000000..1bac9a7 --- /dev/null +++ b/lib/blocs/video_player/video_player.dart @@ -0,0 +1,3 @@ +export 'video_player_bloc.dart'; +export 'video_player_event.dart'; +export 'video_player_state.dart'; diff --git a/lib/blocs/video_player/video_player_bloc.dart b/lib/blocs/video_player/video_player_bloc.dart new file mode 100644 index 0000000..16d2d73 --- /dev/null +++ b/lib/blocs/video_player/video_player_bloc.dart @@ -0,0 +1,28 @@ +import 'package:bloc/bloc.dart'; +import 'package:teso/Services/services2.dart'; + +import 'video_player.dart'; + +class VideoPlayerBloc extends Bloc { + final VideoControllerService _videoControllerService; + + VideoPlayerBloc(this._videoControllerService) + : assert(_videoControllerService != null), + super(null); + + VideoPlayerState get initialState => VideoPlayerStateInitial(); + + @override + Stream mapEventToState(VideoPlayerEvent event) async* { + if (event is VideoSelectedEvent) { + yield VideoPlayerStateLoading(); + try { + final videoController = + await _videoControllerService.getControllerForVideo(event.video); + yield VideoPlayerStateLoaded(event.video, videoController); + } catch (e) { + yield VideoPlayerStateError(e.message ?? 'An unknown error occurred'); + } + } + } +} diff --git a/lib/blocs/video_player/video_player_event.dart b/lib/blocs/video_player/video_player_event.dart new file mode 100644 index 0000000..54a527b --- /dev/null +++ b/lib/blocs/video_player/video_player_event.dart @@ -0,0 +1,16 @@ +import 'package:equatable/equatable.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; + +abstract class VideoPlayerEvent extends Equatable { + @override + List get props => const []; +} + +class VideoSelectedEvent extends VideoPlayerEvent { + final Post video; + + VideoSelectedEvent(this.video) : assert(video != null); + + @override + List get props => [video]; +} diff --git a/lib/blocs/video_player/video_player_state.dart b/lib/blocs/video_player/video_player_state.dart new file mode 100644 index 0000000..3e1d987 --- /dev/null +++ b/lib/blocs/video_player/video_player_state.dart @@ -0,0 +1,36 @@ +import 'package:better_player/better_player.dart'; +import 'package:equatable/equatable.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; + +abstract class VideoPlayerState extends Equatable { + @override + List get props => const []; +} + +class VideoPlayerStateInitial extends VideoPlayerState { + @override + List get props => const []; +} + +class VideoPlayerStateLoading extends VideoPlayerState {} + +class VideoPlayerStateError extends VideoPlayerState { + final String message; + + VideoPlayerStateError(this.message); + + @override + List get props => [message]; +} + +class VideoPlayerStateLoaded extends VideoPlayerState { + final Post video; + final BetterPlayerController controller; + + VideoPlayerStateLoaded(this.video, this.controller) + : assert(video != null), + assert(controller != null); + + @override + List get props => [video, controller]; +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..0c333e1 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,107 @@ +import 'package:camera/camera.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:flutter_libphonenumber/flutter_libphonenumber.dart'; +import 'package:no_context_navigation/no_context_navigation.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/Pages/Sub_Pages/LandingPage/Login.dart'; +import 'package:teso/Services/locator.dart'; +import 'package:teso/main_screen.dart'; +import 'package:teso/providers/app_provider.dart'; +import 'package:teso/providers/referral_provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/providers/device_provider.dart'; +import 'package:teso/resetpassword.dart'; +import 'package:teso/util/consts.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'Pages/splash.dart'; +import 'Services/uservideo_controller_service.dart'; +import 'Services/video_controller_service.dart' as ma; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + await FlutterLibphonenumber().init(); +// Initialize Firebase. + setupLocator(); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => AppProvider()), + ChangeNotifierProvider(create: (_) => UserProvider()), + ChangeNotifierProvider(create: (_) => DeviceProvider()), + ChangeNotifierProvider(create: (_) => ReferralProvider()), + RepositoryProvider( + create: (context) => + CachedVideoControllerService(DefaultCacheManager()), + ), + RepositoryProvider( + create: (context) => + ma.CachedVideoControllerService(DefaultCacheManager()), + ), + ], + child: MyApp(), + ), + ); +} + +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + // If you're going to use other Firebase services in the background, such as Firestore, + // make sure you call `initializeApp` before using other Firebase services. + await Firebase.initializeApp(); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + statusBarColor: Theme.of(context).primaryColor.withOpacity(0.7), + // systemNavigationBarColor: Colors.black, + )); + return Consumer( + builder: (_, AppProvider appProvider, __) { + return MaterialApp( + key: appProvider.key, + debugShowCheckedModeBanner: false, + navigatorKey: NavigationService.navigationKey, + title: Constants.appName, + theme: appProvider.theme, + home: Splash(), + onGenerateRoute: (RouteSettings settings) { + switch (settings.name) { + case '/': + return MaterialPageRoute(builder: (_) => MainScreens()); + case '/resetpassword': + return MaterialPageRoute( + builder: (_) => ResetPassword(resetID: settings.arguments)); + case "/login": + List cameras; + availableCameras().then((value) { + cameras = value; + }); + return MaterialPageRoute( + builder: (_) => LoginPage( + referrer: settings.arguments, + connectedCameras: cameras, + ), + ); + default: + return null; + } + }, + ); + }, + ); + } +} + +// Future _firebaseMessagingBackgroundHandler( +// RemoteMessage message, data) async { +// // print('Handling a background message ${message.messageId}'); +// data = message.data["senderID"]; +// } diff --git a/lib/main_screen.dart b/lib/main_screen.dart new file mode 100644 index 0000000..7dad590 --- /dev/null +++ b/lib/main_screen.dart @@ -0,0 +1,905 @@ +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:location/location.dart'; +import 'package:teso/Classes/API%20Clasess/Campaign.dart'; +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API%20Clasess/Product.dart'; +import 'package:teso/Classes/API%20Clasess/TesoBusinessDetail.dart'; +import 'package:teso/Classes/Uploading.dart'; +import 'package:teso/Pages/Campaigns.dart'; +import 'package:camera/camera.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:page_transition/page_transition.dart' as Transition; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Pages/PageWidgets/Uploads/Pending.dart'; +import 'package:teso/Pages/Personnal.dart'; +import 'package:teso/Pages/BusinessLocator.dart'; +import 'package:teso/Pages/Sub_Pages/Notifications/ChatScreen.dart'; +import 'package:teso/Pages/Sub_Pages/PersonalSub/Settings.dart'; +import 'package:teso/Pages/Sub_Pages/PopUp/Personalized.dart'; +import 'package:teso/Pages/codeQR.dart'; +import 'package:teso/Pages/notifications.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'Classes/API Clasess/CouponHead.dart'; +import 'Pages/Sub_Pages/@Generic/Camera/Video/RecordVideo.dart'; +import 'Pages/Sub_Pages/@Generic/prominentDisclosure.dart'; +import 'Pages/Sub_Pages/Campaign/AuditionPage.dart'; +import 'Pages/Sub_Pages/Coupons/ProximityCoupons.dart'; +import 'Pages/explore.dart'; +import 'Pages/home.dart'; +import 'package:teso/Pages/LandingPage.dart'; +import 'package:http/http.dart' as http; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:teso/Pages/scannerqr.dart'; +import 'package:provider/provider.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/providers/device_provider.dart'; +import 'package:teso/Notifications/NotificationPlugin.dart'; +import 'package:teso/Classes/Payload.dart'; +import 'dart:convert'; +import 'package:teso/Pages/Sub_Pages/Coupons/ProximityCoupons.dart'; +import 'package:teso/Pages/DesireComeTrue.dart'; +import 'package:teso/Pages/Sub_Pages/PersonalSub/CompleteProfile.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/CoinPurchase.dart'; +import 'package:teso/Classes/NotificationSpliter.dart'; + +class MainScreens extends StatefulWidget { + final List connectedCameras; + + const MainScreens({Key key, this.connectedCameras}) : super(key: key); + @override + _MainScreensState createState() => _MainScreensState(); +} + +class _MainScreensState extends State + with TickerProviderStateMixin { + final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; + int _page = 0; + PageController _pageController; + String result = ""; + ScrollController _scrollBottomBarController = new ScrollController(); + bool isScrollingDown = false; + double bottomBarHeight = 75; + final picker = ImagePicker(); + FirebaseAuth _auth; + String dd; + bool _initialized = false; + bool navVisible = true; + + Future _scanQRCode() async { + result = ""; + result = await Navigator.push( + context, + PageTransition( + type: PageTransitionType.downToUp, + child: QRCodeScanner(), + ), + ); + try { + if (result.isNotEmpty) { + Navigator.push( + context, + PageTransition( + type: PageTransitionType.downToUp, + child: ScanQR( + shopID: result, + ), + ), + ); + } + } catch (ex) { + setState(() { + result = "Unknown Error $ex"; + }); + } + } + + @override + void dispose() { + super.dispose(); + _pageController.dispose(); + _scrollBottomBarController.removeListener(() {}); + DefaultCacheManager().emptyCache(); + } + + void clear() async { + await DefaultCacheManager().emptyCache(); + } + + @override + void initState() { + notificationPlugin + .setListenerForLowerVersions(onNotificationInLowerVersions); + notificationPlugin.setOnNotificationClick(onNotificationClick); + Provider.of(context, listen: false) + .connectionToServer(context); + Provider.of(context, listen: false).getCurrentUser(); + Provider.of(context, listen: false).getUserInformation(); + Provider.of(context, listen: false).checkBackgroundMode(); + Provider.of(context, listen: false).getMonthlyStatus(); + Provider.of(context, listen: false).pullAds(); + Provider.of(context, listen: false).loadFriends(); + Provider.of(context, listen: false).getCoupons(); + Provider.of(context, listen: false).checkBlockedUsers(); + super.initState(); + _pageController = PageController(initialPage: 0, keepPage: false); + + SharedPreferences.getInstance().then((prefs) async { + String deviceToken; + await _firebaseMessaging.getToken().then((token) { + deviceToken = token; + }); + Map user = + jsonDecode(prefs.getString("currentUser")) as Map; + TesoUser currentuser = TesoUser.fromJSON(user); + Map updates = { + 'chattingWith': "", + 'typing': false, + "username": currentuser.username, + "deviceToken": deviceToken + }; + FirebaseFirestore.instance + .collection('users') + .doc(currentuser.userGUID) + .update(updates); + + init(context); + if (currentuser.dateOfBirth == null || currentuser.gender == null) { + Navigator.push( + context, + PageTransition( + type: PageTransitionType.leftToRight, + child: CompleteProfile(), + )); + } + }); + } + + void toogleNav(bool visibility) { + setState(() { + navVisible = visibility; + }); + } + + void navigationTapped(int page) { + if (page == 0) { + _pageController.jumpToPage(0); + } else if (page == 1) { + _pageController.jumpToPage(1); + } else if (page == 2) { + tesoDialog(context); + } else if (page == 3) { + _pageController.jumpToPage(2); + } else { + _pageController.jumpToPage(3); + } + } + + void onPageChanged(int page) { + setState(() { + if (page < 2) { + this._page = page; + } else { + this._page = page + 1; + } + }); + } + + void uploadsDialog(BuildContext context) { + showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return Container( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Wrap( + children: [ + new Container( + width: double.infinity, + margin: EdgeInsets.only( + top: 15.0, + bottom: 5.0, + ), + child: Center( + child: Text( + "Uploading....", + style: TextStyle( + fontSize: 20.0, + ), + ), + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + getTiles(context), + new Container( + margin: EdgeInsets.symmetric( + vertical: 15.0, + ), + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Theme.of(context).colorScheme.secondary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + ), + onPressed: () => Navigator.pop(context), + child: Text("Close"), + ), + ), + ) + ], + ), + ), + ); + }, + ); + } + + Widget getTiles(BuildContext context) { + try { + return Consumer( + builder: (BuildContext context, UserProvider value, Widget child) { + List provider = value.getPending(); + if (value.pending != null) { + return Column( + children: provider + .map((item) => uploadTile(context, item)) + .toList()); + } else { + return Container(); + } + }); + } catch (e) { + return Container(); + } + } + + void tesoDialog(context) { + showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return Container( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Wrap( + children: [ + new Container( + width: double.infinity, + margin: EdgeInsets.only( + top: 15.0, + bottom: 5.0, + ), + child: Center( + child: Text( + "Tɛso", + style: TextStyle( + fontSize: 20.0, + ), + ), + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Divider(), + ), + new ListTile( + leading: new Icon( + Icons.qr_code_sharp, + ), + title: new Text('Scan QR Code'), + onTap: () => {_scanQRCode()}, + ), + // new ListTile( + // leading: new Icon(Ionicons.md_locate), + // title: new Text('Proximity Coupons'), + // onTap: () => { + // Navigator.push( + // context, + // Transition.PageTransition( + // type: Transition.PageTransitionType.bottomToTop, + // child: ProximityCoupons(), + // ), + // ) + // }, + // ), + new ListTile( + leading: new Icon(Icons.video_camera_back), + title: new Text('Post Video'), + onTap: () async { + await Navigator.of(context).push( + PageRouteBuilder( + opaque: true, + pageBuilder: (_, __, ___) => RecordVideo()), + ); + }, + ), + + new ListTile( + leading: new Icon(Icons.notifications), + title: new Text('Join Campaign'), + onTap: () => Navigator.push( + context, + Transition.PageTransition( + type: Transition.PageTransitionType.fade, + child: Campaigns(), + ), + ), + ), + new ListTile( + leading: new Icon(Icons.location_on), + title: new Text('Business Locator'), + onTap: () async { + PermissionStatus alreadyConsent = + await Location.instance.hasPermission(); + if (alreadyConsent != PermissionStatus.granted) { + bool results = await Navigator.push( + context, + PageTransition( + child: ProminentDisclosure(), + type: PageTransitionType.leftToRight)); + if (results) { + await Location.instance.requestPermission(); + Navigator.push( + context, + Transition.PageTransition( + type: Transition.PageTransitionType.leftToRight, + child: BusinessLocator(), + ), + ); + } + } else { + Navigator.push( + context, + Transition.PageTransition( + type: Transition.PageTransitionType.leftToRight, + child: BusinessLocator(), + ), + ); + } + }, + ), + new Container( + margin: EdgeInsets.symmetric( + vertical: 15.0, + ), + child: new Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Theme.of(context).colorScheme.secondary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + ), + onPressed: () => Navigator.pop(context), + child: Text("Close"), + ), + ), + ) + ], + ), + ), + ); + }, + ); + } + + void settingsDialog(context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + builder: (BuildContext bc) { + return Setting( + logOut: logOut, + connectedCameras: widget.connectedCameras, + ); + }); + } + + void logOut() async { + try { + SharedPreferences.getInstance().then((prefs) async { + String token = prefs.getString('tokensTeso'); + String id = prefs.getString('id'); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Accept': 'application/json', + 'Authorization': token + }; + var register = Uri.parse(serverLocation + 'api/logout'); + var client = await http.get(register, headers: requestHeaders); + + if (client.statusCode == 200) { + clear(); + FirebaseFirestore.instance + .collection('users') + .doc(id) + .update({'deviceToken': ""}); + _auth = FirebaseAuth.instance; + await _auth.signOut(); + + await prefs.clear(); + await prefs.setBool("launched", true); + // print(r.toString()); + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute(builder: (context) => LandingPage()), + (Route route) => false); + } else {} + }); + } catch (e) { + print(e); + } + } + + bool onWillPop() { + _pageController.previousPage( + duration: Duration(milliseconds: 200), + curve: Curves.linear, + ); + return false; + } + + Widget build(BuildContext context) { + return Scaffold( + extendBody: true, + body: WillPopScope( + onWillPop: () => Future.sync(onWillPop), + child: Stack( + children: [ + PageView( + physics: NeverScrollableScrollPhysics(), + controller: _pageController, + onPageChanged: onPageChanged, + children: [ + Home( + showUploads: uploadsDialog, + toggle: toogleNav, + ), + // VideoList(), + Explore( + connectedCameras: widget.connectedCameras, + ), + Notifications(), + Personnal( + showSettings: settingsDialog, + ), + ], + ), + Align( + alignment: Alignment.bottomCenter, + child: Consumer(builder: + (BuildContext context, UserProvider user, Widget child) { + if (user.saving) + return Container( + width: double.infinity, + height: 30, + alignment: Alignment.center, + padding: EdgeInsets.all( + SizeConfig.safeBlockVertical * 1.3, + ), + margin: EdgeInsets.only( + // left: MediaQuery.of(context).size.width * 0.119, + // right: MediaQuery.of(context).size.width * 0.119, + bottom: SizeConfig.safeBlockVertical * 15, + ), + color: Color.fromRGBO(0, 0, 0, 0.2), + child: Text("Saving...", + style: TextStyle( + color: Colors.white, + )), + ); + else + return Container(); + }), + ), + ], + ), + ), + bottomNavigationBar: bottomOptions(context), + ); + } + + onNotificationInLowerVersions(ReceivedNotification receivedNotification) {} + + onNotificationClick(String payload) async { + Payload load = Payload.fromJSON(json.decode(payload.toString())); + switch (load.loadID) { + case "TESN001": + await Navigator.of(context).push( + Transition.PageTransition( + type: Transition.PageTransitionType.bottomToTop, + child: ProximityCoupons(), + ), + ); + break; + case "TESN002": + await Navigator.push( + context, + Transition.PageTransition( + child: DesireComeTrue(), + type: Transition.PageTransitionType.fade, + ), + ); + break; + case "TESN000": + navigationTapped(3); + break; + case "TESN0R0": + navigationTapped(3); + break; + case "TESN003": + await Navigator.push( + context, + Transition.PageTransition( + child: Coins(initalPage: 0), + type: Transition.PageTransitionType.fade, + ), + ); + break; + case "TESN004": + TesoUser user = new TesoUser(); + user.firstname = load.load2; + user.userGUID = load.load1; + user.lastname = load.load3; + await Navigator.push( + context, + Transition.PageTransition( + child: ChatScreen( + user: user, + ), + type: Transition.PageTransitionType.fade, + ), + ); + break; + case "TESN005": + Campaign campaignItem = Campaign( + businessID: load.load1, + campaignID: load.load2, + description: load.load3, + rewards: int.parse(load.load7), + startDate: DateTime.parse(load.load8), + status: load.load4, + targetProduct: load.load5, + title: load.load6); + await Navigator.push( + context, + PageTransition( + child: Audition( + campaign: campaignItem, + ), + type: PageTransitionType.rightToLeft, + ), + ); + break; + } + } + + void _fetchMessage(RemoteMessage message, BuildContext context) async { + if (message == null || message.notification == null) return; + final Map _data = message.data; + final RemoteNotification notification = message.notification; + Payload payload = await NotificationSplitter.getPayload(_data, context); + + await notificationPlugin.showNotification( + notification.title, + notification.body, + payload.toString(), + ); + if (payload.load2 != null && payload.load2 == "personalized") { + try { + var coup = jsonDecode(payload.load1); + Product product = new Product(); + product.productName = payload.load7; + product.unitPrice = double.parse(payload.load6); + TesoBusinessDetail business = new TesoBusinessDetail(); + business.businessName = payload.load5; + CouponsHead head = new CouponsHead(); + head.businessId = coup["BusinessId"]; + head.couponId = payload.load3; + head.expiration = DateTime.parse(coup["Expiration"].toString()); + head.lower = coup["LowerLimit"]; + head.quantity = 1; + head.state = payload.load4; + head.targetProduct = coup["TargetProduct"]; + head.type = coup["Type"]; + head.upper = coup["UpperLimit"]; + head.generated = DateTime.parse(coup["Generated"].toString()); + CouponDetails details = new CouponDetails(); + details.businessId = head.businessId; + details.condition = payload.load4; + details.targetProduct = product; + details.issuer = business; + details.lowerLimit = head.lower; + details.upperLimit = head.upper; + await Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: PersonalCoupon( + details: details, + head: head, + ), + ), + ); + } catch (e) { + print(e); + } + } + } + + Future init(BuildContext context) async { + if (!_initialized) { + await _firebaseMessaging.requestPermission(); + _firebaseMessaging + .getInitialMessage() + .then((message) => _fetchMessage(message, context)); + FirebaseMessaging.onMessage + .listen((message) => _fetchForegroundMessage(message, context)); + FirebaseMessaging.onMessageOpenedApp + .listen((message) => _fromBackground(message, context)); + _firebaseMessaging.getToken().then((token) { + // print('token: $token'); + }); + _initialized = true; + } + } + + void _fromBackground(RemoteMessage message, BuildContext context) async { + if (message == null || message.notification == null) return; + final Map _data = message.data; + Payload payload = await NotificationSplitter.getPayload(_data, context); + if (payload.load2 != null && payload.load2 == "personalized") { + try { + var coup = jsonDecode(payload.load1); + Product product = new Product(); + product.productName = payload.load7; + product.unitPrice = double.parse(payload.load6); + TesoBusinessDetail business = new TesoBusinessDetail(); + business.businessName = payload.load5; + CouponsHead head = new CouponsHead(); + head.businessId = coup["BusinessId"]; + head.couponId = payload.load3; + head.expiration = DateTime.parse(coup["Expiration"].toString()); + head.lower = coup["LowerLimit"]; + head.quantity = 1; + head.state = payload.load4; + head.targetProduct = coup["TargetProduct"]; + head.type = coup["Type"]; + head.upper = coup["UpperLimit"]; + head.generated = DateTime.parse(coup["Generated"].toString()); + CouponDetails details = new CouponDetails(); + details.businessId = head.businessId; + details.condition = payload.load4; + details.targetProduct = product; + details.issuer = business; + details.lowerLimit = head.lower; + details.upperLimit = head.upper; + await Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: PersonalCoupon( + details: details, + head: head, + ), + ), + ); + } catch (e) { + print(e); + } + } else { + switch (_data["notificationType"]) { + case "chats": + TesoUser user = new TesoUser(); + user.firstname = payload.load2; + user.userGUID = payload.load1; + user.lastname = payload.load3; + await Navigator.push( + context, + Transition.PageTransition( + child: ChatScreen( + user: user, + ), + type: Transition.PageTransitionType.fade, + ), + ); + break; + case "campaign": + Campaign campaignItem = Campaign( + businessID: payload.load1, + campaignID: payload.load2, + description: payload.load3, + rewards: int.parse(payload.load7), + startDate: DateTime.parse(payload.load8), + status: payload.load4, + targetProduct: payload.load5, + title: payload.load6); + await Navigator.push( + context, + PageTransition( + child: Audition( + campaign: campaignItem, + ), + type: PageTransitionType.rightToLeft, + ), + ); + break; + default: + navigationTapped(3); + break; + } + } + } + + void _fetchForegroundMessage( + RemoteMessage message, BuildContext context) async { + if (message == null || message.notification == null) { + return; + } + final Map _data = message.data; + final RemoteNotification notification = message.notification; + Payload payload = await NotificationSplitter.getPayload(_data, context); + + await notificationPlugin.showNotification( + notification.title, + notification.body, + payload.toString(), + ); + if (payload.load2 != null && payload.load2 == "personalized") { + try { + var coup = jsonDecode(payload.load1); + Product product = new Product(); + product.productName = payload.load7; + product.unitPrice = double.parse(payload.load6); + TesoBusinessDetail business = new TesoBusinessDetail(); + business.businessName = payload.load5; + CouponsHead head = new CouponsHead(); + head.businessId = coup["BusinessId"]; + head.couponId = payload.load3; + head.expiration = DateTime.parse(coup["Expiration"].toString()); + head.lower = coup["LowerLimit"]; + head.quantity = 1; + head.state = payload.load4; + head.targetProduct = coup["TargetProduct"]; + head.type = coup["Type"]; + head.upper = coup["UpperLimit"]; + head.generated = DateTime.parse(coup["Generated"].toString()); + CouponDetails details = new CouponDetails(); + details.businessId = head.businessId; + details.condition = payload.load4; + details.targetProduct = product; + details.issuer = business; + details.lowerLimit = head.lower; + details.upperLimit = head.upper; + await Navigator.push( + context, + PageTransition( + type: PageTransitionType.fade, + child: PersonalCoupon( + details: details, + head: head, + ), + ), + ); + } catch (e) { + print(e); + } + } else if (payload.loadID == "TESN0R0") { + SharedPreferences.getInstance().then((prefs) async { + String currentSaveUser = prefs.getString("currentUser"); + Map olduser = + jsonDecode(currentSaveUser) as Map; + TesoUser user = TesoUser.fromJSON(olduser); + user.gold = + (int.parse(user.gold) + int.parse(_data["reward"])).toString(); + Provider.of(context, listen: false).setUser(user); + }); + } + } + + Widget bottomOptions(context) { + return AnimatedOpacity( + opacity: navVisible ? 1 : 0, + duration: Duration(seconds: 2), + child: Container( + height: SizeConfig.safeBlockVertical * 12, + margin: EdgeInsets.only( + bottom: SizeConfig.safeBlockVertical * 2, + left: MediaQuery.of(context).size.width * 0.05, + right: MediaQuery.of(context).size.width * 0.05, + ), + padding: EdgeInsets.all(SizeConfig.safeBlockVertical * 0.54), + decoration: BoxDecoration( + color: Colors.white38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(30), + topLeft: Radius.circular(30), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(30), + ), + child: BottomNavigationBar( + //iconSize: 19.5, + showSelectedLabels: false, + showUnselectedLabels: false, + backgroundColor: Theme.of(context).primaryColor, + selectedItemColor: Theme.of(context).colorScheme.secondary, + unselectedItemColor: Colors.grey[500], + elevation: 150, + type: BottomNavigationBarType.fixed, + items: [ + BottomNavigationBarItem( + icon: Icon( + Icons.home, + size: SizeConfig.safeBlockHorizontal * 6.7, + ), + label: "Home", + tooltip: ""), + BottomNavigationBarItem( + icon: Icon( + Icons.search, + size: SizeConfig.safeBlockHorizontal * 6.7, + ), + label: ("Explore"), + ), + BottomNavigationBarItem( + icon: GestureDetector( + onLongPress: () => {_scanQRCode()}, + child: ImageIcon( + AssetImage("assets/images/rawLogo.png"), + size: SizeConfig.safeBlockHorizontal * 6.7, + ), + ), + label: ("TESO"), + ), + BottomNavigationBarItem( + icon: Icon( + Icons.notifications, + size: SizeConfig.safeBlockHorizontal * 6.7, + ), + label: ("Alerts"), + ), + BottomNavigationBarItem( + icon: Icon( + Icons.person, + size: SizeConfig.safeBlockHorizontal * 6.7, + ), + label: ("Personal"), + ), + ], + onTap: navigationTapped, + currentIndex: _page, + ), + ), + ), + ); + } +} diff --git a/lib/providers/app_provider.dart b/lib/providers/app_provider.dart new file mode 100644 index 0000000..0ddec5a --- /dev/null +++ b/lib/providers/app_provider.dart @@ -0,0 +1,62 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/util/consts.dart'; + +class AppProvider extends ChangeNotifier { + AppProvider() { + checkTheme(); + } + + ThemeData theme = Constants.lightTheme; + Key key = UniqueKey(); + GlobalKey navigatorKey = GlobalKey(); + + void setKey(value) { + key = value; + notifyListeners(); + } + + void setNavigatorKey(value) { + navigatorKey = value; + notifyListeners(); + } + + void setTheme(value, c) { + theme = value; + SharedPreferences.getInstance().then((prefs) { + prefs.setString("theme", c).then((val) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, + overlays: SystemUiOverlay.values); + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + statusBarColor: + c == "dark" ? Constants.darkPrimary : Constants.lightPrimary, + statusBarIconBrightness: + c == "dark" ? Brightness.light : Brightness.dark, + )); + }); + }); + notifyListeners(); + } + + ThemeData getTheme(value) { + return theme; + } + + Future checkTheme() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + ThemeData t; + String r = + prefs.getString("theme") == null ? "light" : prefs.getString("theme"); + + if (r == "light") { + t = Constants.lightTheme; + setTheme(Constants.lightTheme, "light"); + } else { + t = Constants.darkTheme; + setTheme(Constants.darkTheme, "dark"); + } + return t; + } +} diff --git a/lib/providers/device_provider.dart b/lib/providers/device_provider.dart new file mode 100644 index 0000000..05ff21c --- /dev/null +++ b/lib/providers/device_provider.dart @@ -0,0 +1,300 @@ +import 'package:provider/provider.dart'; +import 'package:teso/Classes/API%20Clasess/PostionAP.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/prominentDisclosureBackground.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/providers/user_provider.dart'; +import 'package:teso/util/consts.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:location/location.dart'; +import 'package:flutter/services.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'dart:async'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/API Clasess/ProximityCoupon.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:teso/Notifications/NotificationPlugin.dart'; +import 'package:teso/Classes/Payload.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/Error.dart'; + +class DeviceProvider extends ChangeNotifier { + bool serviceEnabled; + Location location = new Location(); + bool _serviceEnabled; + LocationData _location; + + StreamSubscription _locationSubscription; + List proximityCoupons = []; + bool fetching = false; + bool declining = false; + bool runFirst = true; + Position1 position = new Position1(); + + DeviceProvider() { + try { + checkBackgroundMode(); + } catch (e) { + print(e); + } + } + + void checkBackgroundMode() async { + final bool result = await location.isBackgroundModeEnabled(); + serviceEnabled = result; + SharedPreferences.getInstance().then((prefs) { + serviceEnabled = prefs.getBool("backgroundService"); + if (serviceEnabled == null) { + serviceEnabled = false; + } else { + serviceEnabled = prefs.getBool("backgroundService"); + } + if (serviceEnabled) _listenLocation(); + }); + } + + void toggleBackgroundMode(context) async { + try { + bool prominentResult = false; + if (!serviceEnabled) { + prominentResult = await Navigator.push( + context, + PageTransition( + child: ProminentDisclosure(), + type: PageTransitionType.leftToRight)); + } + + final bool result = await location.enableBackgroundMode( + enable: prominentResult, + ); + serviceEnabled = result; + SharedPreferences.getInstance().then((prefs) { + prefs.setBool("backgroundService", serviceEnabled); + }); + if (serviceEnabled) { + PermissionStatus status = await _requestPermission(); + if (status == PermissionStatus.granted) { + await _requestService(); + _listenLocation(); + } + } else { + await _stopListen(); + } + } on PlatformException catch (err) { + Navigator.push( + context, + PageTransition( + child: ErrorPage(error: err.message), + type: PageTransitionType.fade, + ), + ); + } catch (e) { + print(e); + Navigator.push( + context, + PageTransition( + child: ErrorPage(error: e.toString()), + type: PageTransitionType.fade, + ), + ); + } + notifyListeners(); + } + + Future _requestService() async { + if (_serviceEnabled == null || !_serviceEnabled) { + final bool serviceRequestedResult = await location.requestService(); + _serviceEnabled = serviceRequestedResult; + if (!serviceRequestedResult) { + return; + } + } + } + + Future _listenLocation() async { + _locationSubscription = + location.onLocationChanged.handleError((dynamic err) { + print(err.code); + _locationSubscription.cancel(); + }).listen((LocationData currentLocation) async { + try { + double distanceInMeters = Geolocator.distanceBetween( + currentLocation.latitude, + currentLocation.longitude, + _location.latitude, + _location.longitude); + + if (distanceInMeters >= 804.672 || runFirst) { + if (!fetching && !declining) { + position.latitude = currentLocation.latitude; + position.longitude = currentLocation.longitude; + + await proxiCoupons(); + + if (proximityCoupons.length > 0) { + Payload payload = new Payload(); + payload.loadID = "TESN001"; + payload.load1 = "proximityCoupons"; + await notificationPlugin.showNotification( + "Proximity Coupons", + "There are proximity coupons available in your area, click here to accept", + payload.toString(), + ); + } + runFirst = false; + } + _location = currentLocation; + } + fetching = false; + } catch (e) { + if (!fetching && !declining) { + position.latitude = currentLocation.latitude; + position.longitude = currentLocation.longitude; + await proxiCoupons(); + } + _location = currentLocation; + fetching = false; + } + }); + } + + Future _stopListen() async { + try { + _locationSubscription.cancel(); + } catch (e) {} + } + + PermissionStatus _permissionGranted; + + Future _requestPermission() async { + if (_permissionGranted != PermissionStatus.granted) { + _permissionGranted = await location.requestPermission(); + } + return _permissionGranted; + } + + Future> proxiCoupons() async { + fetching = true; + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'coupons/proximity'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(position), headers: requestHeaders); + + if (client1.statusCode == 200) { + var details = jsonDecode(client1.body); + proximityCoupons = List.from( + details.map((model) => ProximityCoupon.fromJSON(model)).toList()); + notifyListeners(); + return proximityCoupons; + } else { + return null; + } + } + + Future declineCoupon(CouponsHead couponsHead) async { + declining = true; + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'coupons/declineCoupon'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(couponsHead), headers: requestHeaders); + + if (client1.statusCode == 200) { + declining = false; + return 1; + } else { + declining = false; + return 0; + } + } + + Future acceptCoupon(CouponsHead couponsHead, int cost, context) async { + try { + declining = true; + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + TesoUser currentUser = + Provider.of(context, listen: false).currentUser; + + int remaining = int.parse(currentUser.silver) - cost.round(); + if (remaining < 0) { + Payload payload = new Payload(); + payload.loadID = "TESN003"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Insufficient coins", + "Unable to acquire coupon due to insufficient silver coins, get more silver coins to acquire coupons!!", + payload.toString(), + ); + return 0; + } + // Calculations end + + var register2 = serverLocation + 'coupons/acceptCoupon'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(couponsHead), headers: requestHeaders); + + if (client1.statusCode == 200) { + currentUser.silver = remaining.toString(); + Provider.of(context, listen: false).setUser(currentUser); + declining = false; + + Payload payload = new Payload(); + payload.loadID = "TESN003"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Coupon Acquired", + client1.body.toString(), + payload.toString(), + ); + Provider.of(context, listen: false).getCoupons(); + return 200; + } else if (client1.statusCode == 999) { + Payload payload = new Payload(); + payload.loadID = "TESN003"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Unable to acquire coupon", + "Copies of coupon not available at the moment for acquisition, please try again after some time", + payload.toString(), + ); + declining = false; + return client1.statusCode; + } else { + Payload payload = new Payload(); + payload.loadID = "TESN003"; + payload.load1 = "CouponAcquisition"; + + await notificationPlugin.showNotification( + "Unable to acquire coupon", + "An error occurred while trying to acquired coupon, please try again", + payload.toString(), + ); + declining = false; + return client1.statusCode; + } + } catch (e) { + return 10; + } + } +} diff --git a/lib/providers/pageAnimations.dart b/lib/providers/pageAnimations.dart new file mode 100644 index 0000000..29e4c92 --- /dev/null +++ b/lib/providers/pageAnimations.dart @@ -0,0 +1,187 @@ +library page_transition; + +import 'package:flutter/material.dart'; + +enum PageTransitionType { + fade, + rightToLeft, + leftToRight, + upToDown, + downToUp, + scale, + rotate, + size, + rightToLeftWithFade, + leftToRightWithFade, +} + +class PageTransition extends PageRouteBuilder { + final Widget child; + final PageTransitionType type; + final Curve curve; + final Alignment alignment; + final Duration duration; + + PageTransition({ + Key key, + @required this.child, + @required this.type, + this.curve = Curves.linear, + this.alignment, + this.duration = const Duration(milliseconds: 300), + }) : super( + pageBuilder: (BuildContext context, Animation animation, + Animation secondaryAnimation) { + return child; + }, + transitionDuration: duration, + transitionsBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { + switch (type) { + case PageTransitionType.fade: + return FadeTransition(opacity: animation, child: child); + break; + case PageTransitionType.rightToLeft: + return SlideTransition( + transformHitTests: false, + position: new Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: new SlideTransition( + position: new Tween( + begin: Offset.zero, + end: const Offset(-1.0, 0.0), + ).animate(secondaryAnimation), + child: child, + ), + ); + break; + case PageTransitionType.leftToRight: + return SlideTransition( + transformHitTests: false, + position: Tween( + begin: const Offset(-1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: new SlideTransition( + position: new Tween( + begin: Offset.zero, + end: const Offset(1.0, 0.0), + ).animate(secondaryAnimation), + child: child, + ), + ); + break; + case PageTransitionType.upToDown: + return SlideTransition( + transformHitTests: false, + position: Tween( + begin: const Offset(0.0, -1.0), + end: Offset.zero, + ).animate(animation), + child: new SlideTransition( + position: new Tween( + begin: Offset.zero, + end: const Offset(0.0, 1.0), + ).animate(secondaryAnimation), + child: child, + ), + ); + break; + case PageTransitionType.downToUp: + return SlideTransition( + transformHitTests: false, + position: Tween( + begin: const Offset(0.0, 1.0), + end: Offset.zero, + ).animate(animation), + child: new SlideTransition( + position: new Tween( + begin: Offset.zero, + end: const Offset(0.0, -1.0), + ).animate(secondaryAnimation), + child: child, + ), + ); + break; + case PageTransitionType.scale: + return ScaleTransition( + alignment: alignment, + scale: CurvedAnimation( + parent: animation, + curve: Interval( + 0.00, + 0.50, + curve: Curves.bounceInOut, + ), + ), + child: child, + ); + break; + case PageTransitionType.rotate: + return new RotationTransition( + alignment: alignment, + turns: animation, + child: new ScaleTransition( + alignment: alignment, + scale: animation, + child: FadeTransition( + opacity: animation, + child: child, + ), + ), + ); + break; + case PageTransitionType.size: + return Align( + alignment: alignment, + child: SizeTransition( + sizeFactor: CurvedAnimation( + parent: animation, + curve: curve, + ), + child: child, + ), + ); + break; + case PageTransitionType.rightToLeftWithFade: + return SlideTransition( + position: Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: Offset.zero, + end: const Offset(-1.0, 0.0), + ).animate(secondaryAnimation), + child: child, + ), + ), + ); + break; + case PageTransitionType.leftToRightWithFade: + return SlideTransition( + position: Tween( + begin: const Offset(-1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: Offset.zero, + end: const Offset(1.0, 0.0), + ).animate(secondaryAnimation), + child: child, + ), + ), + ); + break; + default: + return FadeTransition(opacity: animation, child: child); + } + }); +} diff --git a/lib/providers/referral_provider.dart b/lib/providers/referral_provider.dart new file mode 100644 index 0000000..3a20dab --- /dev/null +++ b/lib/providers/referral_provider.dart @@ -0,0 +1,19 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +class ReferralProvider extends ChangeNotifier { + ReferralProvider() { + getReferral(); + } + String referral; + + void setReferral(user) { + referral = user; + notifyListeners(); + } + + String getReferral() { + return referral; + } +} diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart new file mode 100644 index 0000000..632cb36 --- /dev/null +++ b/lib/providers/user_provider.dart @@ -0,0 +1,874 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:connectivity/connectivity.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:flutter_upchunk/flutter_upchunk.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:tapioca/tapioca.dart'; +import 'package:teso/Classes/API%20Clasess/PostUpload.dart'; +import 'package:teso/Classes/Connection.dart'; +import 'package:teso/Classes/Firebase/Posts.dart'; +import 'package:teso/Classes/ReportedContent.dart'; +import 'package:teso/Classes/Uploading.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/SuccessRedeem.dart'; +import 'package:teso/Pages/Sub_Pages/@Generic/ErrorRedeem.dart'; +import 'package:teso/Classes/API%20Clasess/CommentsPost.dart'; +import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; +import 'package:teso/Classes/API%20Clasess/Post.dart'; +import 'package:teso/Classes/API%20Clasess/PostFav.dart'; +import 'package:teso/providers/pageAnimations.dart'; +import 'package:teso/util/SizeConfig.dart'; +import 'package:teso/util/consts.dart'; +import 'package:http/http.dart' as http; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:teso/Classes/TesoUser.dart'; +import 'package:teso/Classes/API Clasess/UserFavCategory.dart'; +import 'package:teso/Classes/API Clasess/CouponHead.dart'; +import 'package:teso/Notifications/NotificationPlugin.dart'; +import 'package:teso/Classes/Payload.dart'; + +class UserProvider extends ChangeNotifier { + UserProvider() { + getCurrentUser(); + } + + TesoUser currentUser; + List interest = []; + List posts = []; + List friends = []; + List mycoupons = []; + List pending = []; + List uploadOperation = []; + bool wifi = false; + bool firstTime = true; + bool saving = false; + List blockedContent = []; + List blockedUsers = []; + List blockUserList = []; + + void updateUser(user) async { + currentUser = await update(user); + SharedPreferences.getInstance().then((prefs) { + prefs.setString("currentUser", currentUser.toString()); + }); + notifyListeners(); + } + + void setUser(user) { + currentUser = user; + SharedPreferences.getInstance().then((prefs) { + prefs.setString("currentUser", currentUser.toString()); + }); + notifyListeners(); + } + + getPending() { + return pending; + } + + getCurrentUser() async { + String currentSaveUser; + SharedPreferences.getInstance().then((prefs) async { + currentSaveUser = prefs.getString("currentUser"); + if (currentSaveUser == null) { + } else { + Map user = + jsonDecode(currentSaveUser) as Map; + TesoUser olduser = TesoUser.fromJSON(user); + currentUser = olduser; + prefs.setString("currentUser", currentUser.toString()); + } + }); + Future.delayed(Duration(seconds: 5), () => notifyListeners()); + return currentUser; + } + + setFavs(List categories) { + interest = categories; + SharedPreferences.getInstance().then((prefs) { + prefs.setStringList("favoriteCats", categories); + }); + notifyListeners(); + } + + getFavs() async { + try { + await pullFavoriteCategories(); + } catch (e) { + SharedPreferences.getInstance().then((prefs) { + interest = prefs.getStringList("favoriteCats"); + if (interest != null) if (interest.isEmpty) { + interest = []; + } + }); + } + } + + Future update(TesoUser user) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'users/updateUser'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(user), headers: requestHeaders); + + if (client1.statusCode == 200) { + Map handler = jsonDecode(client1.body); + TesoUser tokenHandler = TesoUser.fromJSON(handler); + return tokenHandler; + } else { + return null; + } + } + + Future> pullFavoriteCategories() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List favs; + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'favoriteCategories/pullUser'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(prefs.getString("id")), headers: requestHeaders); + + if (client1.statusCode == 200) { + var handler = jsonDecode(client1.body); + favs = List.from( + handler.map((model) => UserFavCategory.fromJSON(model)).toList()); + setFavs(favs.map((e) => e.categoryCode).toList()); + return interest = favs.map((e) => e.categoryCode).toList(); + } else { + return null; + } + } + + updateFavoriteCategories(List catF) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String id = prefs.getString("id"); + List favs = []; + catF.forEach((element) async { + UserFavCategory category = new UserFavCategory(); + category.userGuid = id; + category.categoryCode = element; + category.countID = DateTime.now().toString() + id; + favs.add(category); + }); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + + var register2 = serverLocation + 'favoriteCategories/updateFavorites'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(favs), headers: requestHeaders); + + if (client1.statusCode == 200) { + setFavs(catF); + } else { + return null; + } + } + + Future getMonthlyStatus() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + try { + var register2 = serverLocation + 'monthly-desires/check-status'; + var client1 = + await http.get(Uri.parse(register2), headers: requestHeaders); + if (client1.statusCode == 200) { + if (client1.body == "not submitted") { + Payload payload = new Payload(); + payload.loadID = "TESN002"; + payload.load1 = "DesireComeTrue"; + + await notificationPlugin.showNotification( + "Desire Come True", + "You haven't set up your Desire Come True list for next month", + payload.toString(), + ); + } + } + } catch (e) { + print(e); + } + } + + void updatePosted(FBPosts postNow) { + posts.add(postNow); + + notifyListeners(); + // predownloadAds(posts.map((e) => e.playbackID).toList()); + } + + Future pullAds() async { + SharedPreferences.getInstance().then((prefs) async { + String id = prefs.getString("id"); + FirebaseFirestore.instance + .collection("posts") + .where("publisher", isEqualTo: id) + .snapshots() + .listen((snap) async { + this.posts = snap.docs.map((e) => FBPosts.fromJSON(e.data())).toList(); + if (this.posts != null) + this.posts.sort((b, a) => a.timestamp.compareTo(b.timestamp)); + }); + notifyListeners(); + }); + //predownloadAds(this.posts.map((e) => e.playbackID).toList()); + } + + Future commentPost(CommentsPost comment) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'posts/add-comment'; + var client = await http.post(Uri.parse(register), + body: json.encode(comment), headers: requestHeaders); + if (client.statusCode == 200) { + print(client.body); + } + } + + void unCommentPost(CommentsPost comment) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'posts/delete-comment'; + var client = await http.post(Uri.parse(register), + body: json.encode(comment), headers: requestHeaders); + if (client.statusCode == 200) { + print(client.body); + } + } + + void addLike(PostFav like) async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'posts/add-like'; + var client = await http.post(Uri.parse(register), + body: json.encode(like), headers: requestHeaders); + if (client.statusCode == 200) { + print(client.body); + } + } catch (e) { + print(e.toString()); + } + } + + void deleteLike(String like) async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'posts/remove-like'; + var client = await http.post(Uri.parse(register), + body: json.encode(like), headers: requestHeaders); + if (client.statusCode == 200) { + print(client.body); + } + } catch (e) { + print(e); + } + } + + void deletePost(Post post) async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + Map muxHeaders = {'Authorization': token}; + var register = serverLocation + 'posts/delete-post'; + var client = await http.post(Uri.parse(register), + body: json.encode(post), headers: requestHeaders); + print(client.body); + if (client.statusCode == 200) { + http.get( + Uri.parse( + tesoStreaming + "api/mobile/upload/delete/" + post.playbackID), + headers: muxHeaders); + this.posts.removeWhere((element) => element.postID == post.postID); + notifyListeners(); + } + } catch (e) { + print(e); + } + } + + void viewPost(Post post) async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'posts/view-post'; + await http.post(Uri.parse(register), + body: json.encode(post.postID), headers: requestHeaders); + } catch (e) { + print(e); + } + } + +// coupons + Future viewCoupon(CouponsHead coupon) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + try { + var register2 = serverLocation + 'coupons/viewCoupon'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(coupon), headers: requestHeaders); + if (client1.statusCode == 200) { + return 200; + } else { + return 400; + } + } catch (e) { + return 400; + } + } + + Future loadFriends() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'relationships/friends'; + var client = await http.get(Uri.parse(register), headers: requestHeaders); + if (client.statusCode == 200) { + var people = jsonDecode(client.body); + friends = List.from( + people.map((model) => TesoUser.fromJSON(model)).toList()); + notifyListeners(); + } + } + + Future getUserInformation() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'user_details/pullInformation'; + var client = await http.get(Uri.parse(register), headers: requestHeaders); + if (client.statusCode == 200) { + var people = jsonDecode(client.body); + TesoUser user = TesoUser.fromJSON(people); + setUser(user); + } + } + + Future getCoupons() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + var register = serverLocation + 'coupons/acquiredcoupons'; + var client = await http.get(Uri.parse(register), headers: requestHeaders); + if (client.statusCode == 200) { + var details = jsonDecode(client.body); + mycoupons = List.from( + details.map((model) => CouponDetails.fromJSON(model)).toList()); + notifyListeners(); + } + } + + Future redeemCoupon(CouponDetails coupon, context) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + try { + var register2 = serverLocation + 'coupons/redeemCoupon'; + var client1 = await http.post(Uri.parse(register2), + body: json.encode(coupon), headers: requestHeaders); + print(client1.body); + if (client1.statusCode == 200) { + var data = client1.body; + int coins = int.tryParse(data); + Payload payload = new Payload(); + payload.loadID = "TESN003"; + payload.load1 = "CouponRedemption"; + + await notificationPlugin.showNotification( + "Coupon Redeem", + "You have successfully redeem a " + + coupon.issuer.businessName + + " " + + coupon.type + + " coupon", + payload.toString(), + ); + getCoupons(); + await Navigator.pushReplacement( + context, + PageTransition( + child: SuccessfullyRedeemed( + couponDetails: coupon, + returns: coins, + ), + type: PageTransitionType.fade, + ), + ); + Navigator.pop(context); + } else { + Navigator.pushReplacement( + context, + PageTransition( + child: ErrorRedeem(), + type: PageTransitionType.fade, + ), + ); + } + } catch (e) { + print(e); + Navigator.pushReplacement( + context, + PageTransition( + child: ErrorRedeem(), + type: PageTransitionType.fade, + ), + ); + } + } + + predownloadAds(List paths) { + try { + // if (wifi) + // paths.forEach((path) async { + // final fileInfo = await DefaultCacheManager() + // .getFileFromCache(tesoStreaming + path); + // if (fileInfo == null || fileInfo.file == null) { + // unawaited(DefaultCacheManager() + // .downloadFile(tesoStreaming + "pd/" + path)); + // } + // }); + } catch (e) { + print(e); + } + } + + Future uploadPost(Uploading uploads) async { + try { + Payload payload = new Payload(); + payload.loadID = "TESN003"; + payload.load1 = "Posted"; + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + Map muxHeaders = {'Authorization': token}; + var uploadOptions = UpChunkOptions() + ..endPoint = uploads.muxuploadURL + ..file = File(uploads.path) + ..onProgress = (progress) { + print('Upload progress: ${progress.ceil()}%'); + pending.forEach((element) { + if (element.id == uploads.id) { + uploads.pending = progress.ceil() / 100; + uploads.isProcessing = false; + } + }); + notifyListeners(); + } + ..onError = (String message, int chunk, int attempts) { + print('UpChunk error 💥 🙀:'); + print(' - Message: $message'); + print(' - Chunk: $chunk'); + print(' - Attempts: $attempts'); + pending.removeWhere((element) => element.id == uploads.id); + notifyListeners(); + } + ..onSuccess = () async { + var client1 = await http.get( + Uri.parse( + "${tesoStreaming}api/mobile/upload/playback/${uploads.muxuploadID}"), + headers: muxHeaders); + print('Upload complete! 👋'); + if (client1.statusCode == 200) { + var details = jsonDecode(client1.body); + + var details1 = details[0]; + var data = details1["0"]; + PostUpload videoUpload = new PostUpload(); + videoUpload.aspect = uploads.aspect; + videoUpload.campaignID = uploads.campaignID; + videoUpload.title = uploads.title; + videoUpload.path = data["data"]["id"]; + videoUpload.thumbnail = details1["asset_id"]; + + var register2 = serverLocation + "posts/upload-post"; + var client2 = await http.post(Uri.parse(register2), + body: json.encode(videoUpload), headers: requestHeaders); + if (client2.statusCode == 200) { + pending.removeWhere((element) => element.id == uploads.id); + notifyListeners(); + } else { + pending.removeWhere((element) => element.id == uploads.id); + notifyListeners(); + } + } else { + pending.removeWhere((element) => element.id == uploads.id); + // print(e); + notifyListeners(); + } + }; + var uploadChunk = UpChunk.createUpload(uploadOptions); + uploads.token = uploadChunk; + pending.add(uploads); + notifyListeners(); + } catch (e) { + pending.removeWhere((element) => element.id == uploads.id); + print(e); + } + } + + Future cancelUpload(Uploading uploads) async { + try { + // uploader.cancel(taskId: uploads.id); + uploads.token.stop(); + pending.remove(uploads); + notifyListeners(); + } catch (e) { + print(e); + } + } + + connectionToServer(context) { + MyConnectivity _connectivity = MyConnectivity.instance; + _connectivity.initialise(); + _connectivity.myStream.listen((source) { + if (source.keys.toList()[0] == ConnectivityResult.wifi && + source.values.toList()[0]) { + final snackBar = SnackBar( + content: Text( + 'Connected', + textAlign: TextAlign.center, + style: TextStyle(color: Colors.white), + ), + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + backgroundColor: Colors.green, + duration: Duration( + seconds: 3, + ), + ); + if (!firstTime) ScaffoldMessenger.of(context).showSnackBar(snackBar); + firstTime = false; + wifi = true; + notifyListeners(); + } else if (source.keys.toList()[0] == ConnectivityResult.mobile && + source.values.toList()[0]) { + final snackBar = SnackBar( + content: Text( + 'Connected', + textAlign: TextAlign.center, + style: TextStyle(color: Colors.white), + ), + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + backgroundColor: Colors.green, + duration: Duration( + seconds: 3, + ), + ); + if (!firstTime) ScaffoldMessenger.of(context).showSnackBar(snackBar); + firstTime = false; + wifi = false; + notifyListeners(); + } else { + final snackBar = SnackBar( + content: Text( + 'No Internet Connection Available', + textAlign: TextAlign.center, + style: TextStyle(color: Colors.white), + ), + action: SnackBarAction( + label: 'Retry', + textColor: Colors.white, + onPressed: () => connectionToServer(context), + ), + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + backgroundColor: Colors.red, + duration: Duration( + seconds: 3, + ), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + firstTime = false; + wifi = false; + notifyListeners(); + } + }); + } + + Future downloadVideo(String postID, String playbackID, String rendition, + Uint8List imageBitmap, context) async { + if (!saving) { + saving = true; + final snackBar = SnackBar( + content: Text( + "Processing", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.blockSizeHorizontal * 3.5), + ), + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + backgroundColor: Color.fromRGBO(0, 0, 0, 0.5), + duration: Duration( + seconds: 3, + ), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + try { + if (rendition == null) { + SharedPreferences prefs = await SharedPreferences.getInstance(); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': prefs.getString('tokensTeso') + }; + var register2 = serverLocation + 'posts/getrendition'; + var client1 = await http + .post(Uri.parse(register2), + body: json.encode(postID), headers: requestHeaders) + .timeout( + Duration( + seconds: 5, + ), + ); + if (client1.statusCode == 200) { + rendition = client1.body; + } else { + saving = false; + final snackBar = SnackBar( + content: Text( + "An error occurred while saving ad try again", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.blockSizeHorizontal * 3.5), + ), + behavior: SnackBarBehavior.floating, + margin: EdgeInsets.only( + left: 10, + right: 10, + bottom: MediaQuery.of(context).size.height * 0.80), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + backgroundColor: Color(0XFF800000), + duration: Duration( + seconds: 2, + ), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + } + var fileInfo = await DefaultCacheManager() + .getSingleFile(tesoStreamMux + playbackID + "/$rendition?download"); + String location = await getTemporaryDirectory().then((value) => + value.path + + "/" + + DateTime.now().millisecondsSinceEpoch.toString() + + ".mp4"); + + final tapiocaBalls = [ + TapiocaBall.imageOverlay(imageBitmap, 0, 50), + ]; + final cup = Cup(Content(fileInfo.path), tapiocaBalls); + await cup.suckUp(location); + Share.shareFiles([location]); + } catch (e) { + saving = false; + final snackBar = SnackBar( + content: Text( + "An error occurred while saving ad try again", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.blockSizeHorizontal * 3.5), + ), + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + backgroundColor: Color(0XFF800000), + duration: Duration( + seconds: 3, + ), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + saving = false; + } else { + final snackBar = SnackBar( + content: Text( + "Try sharing after saving is complete", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.blockSizeHorizontal * 3.5), + ), + behavior: SnackBarBehavior.floating, + margin: + EdgeInsets.only(bottom: MediaQuery.of(context).size.height * 0.80), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + backgroundColor: Colors.grey[800], + duration: Duration( + seconds: 5, + ), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + notifyListeners(); + } + + checkRelationship(String id) { + return friends.any((element) => element.userGUID == id); + } + + Future flagPost(Post post, int reportID) async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + ReportedContent report = new ReportedContent(); + report.postID = post.postID; + report.publisherID = post.publisherID; + report.report = reportID; + + var register = serverLocation + 'posts/flag-post'; + var client = await http.post(Uri.parse(register), + body: json.encode(report), headers: requestHeaders); + if (client.statusCode == 200) { + blockedContent.add(post.postID); + notifyListeners(); + } + } catch (_) {} + } + + checkBlockedUsers() async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + var register = serverLocation + 'users/blocked-users'; + var client = await http.get(Uri.parse(register), headers: requestHeaders); + if (client.statusCode == 200) { + var people = jsonDecode(client.body); + blockUserList = List.from( + people.map((model) => TesoUser.fromJSON(model)).toList()); + blockedUsers = blockUserList.map((b) => b.userGUID).toList(); + } + notifyListeners(); + } catch (e) { + print(e); + } + } + + checkBlockedContent() async {} + + Future blockUser(TesoUser user) async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + var register = serverLocation + 'relationships/block'; + var client = await http.post(Uri.parse(register), + body: json.encode(user.userGUID), headers: requestHeaders); + if (client.statusCode == 200) { + blockedUsers.add(user.userGUID); + blockUserList.add(user); + notifyListeners(); + } + } catch (_) {} + } + + Future unblockUser(TesoUser user) async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String token = prefs.getString("tokensTeso"); + Map requestHeaders = { + 'Content-type': 'application/json', + 'Authorization': token + }; + + var register = serverLocation + 'relationships/unblock'; + var client = await http.post(Uri.parse(register), + body: json.encode(user.userGUID), headers: requestHeaders); + if (client.statusCode == 200) { + blockedUsers.remove(user.userGUID); + blockUserList.remove(user); + notifyListeners(); + } + } catch (_) {} + } +} diff --git a/lib/qr_code_scanner.dart b/lib/qr_code_scanner.dart new file mode 100644 index 0000000..bc2f4d9 --- /dev/null +++ b/lib/qr_code_scanner.dart @@ -0,0 +1,2 @@ +export 'src/qr_code_scanner.dart'; +export 'src/qr_scanner_overlay_shape.dart'; diff --git a/lib/resetpassword.dart b/lib/resetpassword.dart new file mode 100644 index 0000000..87b60a8 --- /dev/null +++ b/lib/resetpassword.dart @@ -0,0 +1,439 @@ +import 'dart:convert'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:teso/Classes/API%20Clasess/ResetClass.dart'; +import 'package:teso/Pages/PageWidgets/Login/validation.dart'; +import 'package:teso/util/consts.dart'; +import 'dart:math' as math; +import 'Pages/PageWidgets/Login/passwordSignUP.dart'; +import 'package:http/http.dart' as http; + +class ResetPassword extends StatefulWidget { + final String resetID; + const ResetPassword({Key key, this.resetID}) : super(key: key); + + @override + _ResetPasswordState createState() => _ResetPasswordState(); +} + +class _ResetPasswordState extends State + with TickerProviderStateMixin { + TextEditingController password = new TextEditingController(); + AnimationController _controller; + Animation _fabScale; + bool eightChars = false; + bool specialChar = false; + bool upperCaseChar = false; + bool lowerCaseChar = false; + bool number = false; + bool changing = false; + + @override + void dispose() { + super.dispose(); + } + + changePassword(context) async { + setState(() { + changing = true; + }); + Map requestHeaders = { + 'Content-type': 'application/json', + }; + var register2 = serverLocation + 'resetpassword/reset'; + ResetClass resetClass = + ResetClass(password: password.text, resetGuid: widget.resetID); + var client1 = await http.post( + Uri.parse(register2), + body: json.encode(resetClass), + headers: requestHeaders, + ); + + if (client1.statusCode == 200) { + await tesoSuccessDialog(context); + setState(() { + changing = false; + }); + Future.delayed(const Duration(seconds: 5), () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + } else if (client1.statusCode == 400 && client1.body == "expired") { + tesoErrorDialog(context); + } else { + tesoErrorDialog(context); + } + } + + tesoSuccessDialog(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Success", + style: TextStyle(color: Colors.green[400]), + ), + actions: [ + TextButton( + child: Text( + 'OK', + style: TextStyle(color: Colors.green[400]), + ), + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Password changed successfully", + style: TextStyle(color: Colors.green[400]), + ), + ); + }); + } + + tesoErrorDialog(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Error Occurred", + style: TextStyle(color: Colors.red[400]), + ), + actions: [ + TextButton( + child: Text( + 'OK', + style: TextStyle(color: Colors.red[400]), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "An error occurred while changing password, please try again!", + style: TextStyle(color: Colors.red[400]), + ), + ); + }); + } + + tesoExpiredDialog(context) { + showDialog( + context: context, + builder: (BuildContext bc) { + return AlertDialog( + title: Text( + "Link Expired", + style: TextStyle(color: Colors.red[400]), + ), + actions: [ + TextButton( + child: Text( + 'OK', + style: TextStyle(color: Colors.red[400]), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + //title: Text("Alert Dialog"), + content: Text( + "Password reset link expired, please try again with a new link!", + style: TextStyle(color: Colors.red[400]), + ), + ); + }); + } + + bool _allValid() { + return eightChars && + number && + specialChar && + upperCaseChar && + lowerCaseChar; + } + + @override + void initState() { + super.initState(); + password.addListener(() { + setState(() { + eightChars = password.text.length >= 8; + number = password.text.contains(RegExp(r'\d'), 0); + upperCaseChar = password.text.contains(new RegExp(r'[A-Z]'), 0); + lowerCaseChar = password.text.contains(new RegExp(r'[a-z]'), 0); + specialChar = password.text.isNotEmpty && + !password.text.contains(RegExp(r'^[\w&.-]+$'), 0); + }); + + if (_allValid()) { + _controller.forward(); + } else { + _controller.reverse(); + } + }); + _controller = AnimationController( + vsync: this, duration: const Duration(milliseconds: 500)); + + _fabScale = Tween(begin: 0, end: 1) + .animate(CurvedAnimation(parent: _controller, curve: Curves.bounceOut)); + + _fabScale.addListener(() { + setState(() {}); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + title: Text("Reset your Teso password"), + automaticallyImplyLeading: false, + ), + body: Container( + height: MediaQuery.of(context).size.height * 0.9, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: new Column( + children: [ + Container( + padding: EdgeInsets.all(10), + width: double.infinity, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height * 0.02, + top: MediaQuery.of(context).size.height * 0.02, + ), + child: Center( + child: Text( + "Enter a password with a minimum of 8 characters and the password must have at least an uppercase letter, a lowercase letter, " + + "a digit and a non-alphanumeric character", + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.grey, + ), + ), + ), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: (MediaQuery.of(context).size.width) * 0.01), + child: _validationStack()), + SizedBox( + height: 50, + ), + createPassword(context, password), + SizedBox(height: MediaQuery.of(context).size.height * 0.002), + Visibility( + visible: !changing, + child: Container( + margin: EdgeInsets.only(top: 20), + width: MediaQuery.of(context).size.width * 0.6, + height: 40.0, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accentMain, + darkAccent, + ], + // stops: [0.1, 0.4, 0.7, 0.8], + ), + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + offset: Offset(0.0, 1.5), + blurRadius: 1.5, + ), + ]), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + ), + ), + color: Colors.transparent, + child: InkWell( + onTap: () async { + if (_allValid()) { + await changePassword(context); + } + }, + child: Center( + child: Text( + "Reset Password", + style: TextStyle( + fontSize: 18, + color: Colors.white, + ), + ), + )), + ), + ), + ), + Visibility( + visible: changing, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center( + child: CupertinoActivityIndicator( + animating: true, + radius: 15, + ), + ), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _separator() { + return Container( + height: 1, + decoration: BoxDecoration(color: Colors.blue.withAlpha(100)), + ); + } + + Stack _validationStack() { + return Stack( + alignment: Alignment.bottomLeft, + children: [ + Card( + shape: CircleBorder(), + color: Colors.black12, + child: Container( + height: 150, + width: 150, + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 32.0, left: 10), + child: Transform.rotate( + angle: -math.pi / 20, + child: Icon( + Icons.lock, + color: Colors.pink, + size: 60, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 50.0, right: 60), + child: Transform.rotate( + angle: -math.pi / -60, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + elevation: 4, + color: Colors.yellow.shade800, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(8, 8, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 4), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 4, 0, 8), + child: Container( + alignment: Alignment.centerLeft, + child: Icon( + Icons.brightness_1, + color: Colors.deepPurple, + )), + ), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 74), + child: Transform.rotate( + angle: math.pi / -45, + child: Card( + elevation: 6, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + child: Stack( + alignment: Alignment.bottomRight, + children: [ + IntrinsicWidth( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ValidationItem("8 or more Characters", eightChars), + _separator(), + ValidationItem("1 Special character", specialChar), + _separator(), + ValidationItem("1 Upper case", upperCaseChar), + _separator(), + ValidationItem("1 Lower case", lowerCaseChar), + _separator(), + ValidationItem("1 Number", number) + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Transform.scale( + scale: _fabScale.value, + child: Card( + shape: CircleBorder(), + color: Colors.green, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.check, + color: Colors.white, + ), + ), + ), + ), + ) + ], + ), + ), + ), + ) + ], + ); + } +} diff --git a/lib/simplewidgets/custom_alert.dart b/lib/simplewidgets/custom_alert.dart new file mode 100644 index 0000000..9adca22 --- /dev/null +++ b/lib/simplewidgets/custom_alert.dart @@ -0,0 +1,82 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +// ignore: must_be_immutable +class CustomAlert extends StatelessWidget { + + final Widget child; + + CustomAlert({ + Key key, + @required this.child + }): super(key: key); + + double deviceWidth; + double deviceHeight; + double dialogHeight; + + + @override + Widget build(BuildContext context) { + + Orientation orientation = MediaQuery.of(context).orientation; + Size screenSize = MediaQuery.of(context).size; + + deviceWidth = orientation == Orientation.portrait + ? screenSize.width + : screenSize.height; + deviceHeight = orientation == Orientation.portrait + ? screenSize.height + : screenSize.width; + dialogHeight = deviceHeight * (0.50); + + + return MediaQuery( + data: MediaQueryData(), + child: GestureDetector( +// onTap: ()=>Navigator.pop(context), + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 0.5, + sigmaY: 0.5, + ), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: Stack( + children: [ + Center( + child: Container( + width: deviceWidth*0.9, + child: GestureDetector( + onTap: (){}, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.0), + topRight: Radius.circular(10.0), + bottomLeft: Radius.circular(10.0), + bottomRight: Radius.circular(10.0), + ), + ), + child: child, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart new file mode 100644 index 0000000..fad8126 --- /dev/null +++ b/lib/src/qr_code_scanner.dart @@ -0,0 +1,145 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +typedef QRViewCreatedCallback = void Function(QRViewController); + +class QRView extends StatefulWidget { + const QRView({ + @required Key key, + @required this.onQRViewCreated, + this.overlay, + }) : assert(key != null), + assert(onQRViewCreated != null), + super(key: key); + + final QRViewCreatedCallback onQRViewCreated; + + final ShapeBorder overlay; + + @override + State createState() => _QRViewState(); +} + +class _QRViewState extends State { + @override + Widget build(BuildContext context) { + return Stack( + children: [ + _getPlatformQrView(), + if (widget.overlay != null) + Container( + decoration: ShapeDecoration( + shape: widget.overlay, + ), + ) + else + Container(), + ], + ); + } + + Widget _getPlatformQrView() { + Widget _platformQrView; + switch (defaultTargetPlatform) { + case TargetPlatform.android: + _platformQrView = AndroidView( + viewType: 'net.touchcapture.qr.flutterqr/qrview', + onPlatformViewCreated: _onPlatformViewCreated, + ); + break; + case TargetPlatform.iOS: + _platformQrView = UiKitView( + viewType: 'net.touchcapture.qr.flutterqr/qrview', + onPlatformViewCreated: _onPlatformViewCreated, + creationParams: _CreationParams.fromWidget(0, 0).toMap(), + creationParamsCodec: StandardMessageCodec(), + ); + break; + default: + throw UnsupportedError( + "Trying to use the default webview implementation for $defaultTargetPlatform but there isn't a default one"); + } + return _platformQrView; + } + + void _onPlatformViewCreated(int id) { + if (widget.onQRViewCreated == null) { + return; + } + widget.onQRViewCreated(QRViewController._(id, widget.key)); + } +} + +class _CreationParams { + _CreationParams({this.width, this.height}); + + static _CreationParams fromWidget(double width, double height) { + return _CreationParams( + width: width, + height: height, + ); + } + + final double width; + final double height; + + Map toMap() { + return { + 'width': width, + 'height': height, + }; + } +} + +class QRViewController { + QRViewController._(int id, GlobalKey qrKey) + : _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id') { + if (defaultTargetPlatform == TargetPlatform.iOS) { + final RenderBox renderBox = qrKey.currentContext.findRenderObject(); + _channel.invokeMethod('setDimensions', + {'width': renderBox.size.width, 'height': renderBox.size.height}); + } + _channel.setMethodCallHandler( + (call) async { + switch (call.method) { + case scanMethodCall: + if (call.arguments != null) { + _scanUpdateController.sink.add(call.arguments.toString()); + } + } + }, + ); + } + + static const scanMethodCall = 'onRecognizeQR'; + + final MethodChannel _channel; + + final StreamController _scanUpdateController = + StreamController(); + + Stream get scannedDataStream => _scanUpdateController.stream; + + void flipCamera() { + _channel.invokeMethod('flipCamera'); + } + + void toggleFlash() { + _channel.invokeMethod('toggleFlash'); + } + + void pauseCamera() { + _channel.invokeMethod('pauseCamera'); + } + + void resumeCamera() { + _channel.invokeMethod('resumeCamera'); + } + + void dispose() { + _scanUpdateController.close(); + } +} diff --git a/lib/src/qr_scanner_overlay_shape.dart b/lib/src/qr_scanner_overlay_shape.dart new file mode 100644 index 0000000..886c533 --- /dev/null +++ b/lib/src/qr_scanner_overlay_shape.dart @@ -0,0 +1,165 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class QrScannerOverlayShape extends ShapeBorder { + QrScannerOverlayShape({ + this.borderColor = Colors.red, + this.borderWidth = 3.0, + this.overlayColor = const Color.fromRGBO(0, 0, 0, 80), + this.borderRadius = 0, + this.borderLength = 40, + this.cutOutSize = 250, + }) : assert( + cutOutSize != null ?? + cutOutSize != null ?? + borderLength <= cutOutSize / 2 + borderWidth * 2, + "Border can't be larger than ${cutOutSize / 2 + borderWidth * 2}"); + + final Color borderColor; + final double borderWidth; + final Color overlayColor; + final double borderRadius; + final double borderLength; + final double cutOutSize; + + @override + EdgeInsetsGeometry get dimensions => const EdgeInsets.all(10); + + @override + Path getInnerPath(Rect rect, {TextDirection textDirection}) { + return Path() + ..fillType = PathFillType.evenOdd + ..addPath(getOuterPath(rect), Offset.zero); + } + + @override + Path getOuterPath(Rect rect, {TextDirection textDirection}) { + Path _getLeftTopPath(Rect rect) { + return Path() + ..moveTo(rect.left, rect.bottom) + ..lineTo(rect.left, rect.top) + ..lineTo(rect.right, rect.top); + } + + return _getLeftTopPath(rect) + ..lineTo( + rect.right, + rect.bottom, + ) + ..lineTo( + rect.left, + rect.bottom, + ) + ..lineTo( + rect.left, + rect.top, + ); + } + + @override + void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) { + final width = rect.width; + final borderWidthSize = width / 2; + final height = rect.height; + final borderOffset = borderWidth / 2; + final _borderLength = borderLength > cutOutSize / 2 + borderWidth * 2 + ? borderWidthSize / 2 + : borderLength; + final _cutOutSize = cutOutSize != null && cutOutSize < width + ? cutOutSize + : width - borderOffset; + + final backgroundPaint = Paint() + ..color = overlayColor + ..style = PaintingStyle.fill; + + final borderPaint = Paint() + ..color = borderColor + ..style = PaintingStyle.stroke + ..strokeWidth = borderWidth; + + final boxPaint = Paint() + ..color = borderColor + ..style = PaintingStyle.fill + ..blendMode = BlendMode.dstOut; + + final cutOutRect = Rect.fromLTWH( + rect.left + width / 2 - _cutOutSize / 2 + borderOffset, + rect.top + height / 2 - _cutOutSize / 2 + borderOffset, + _cutOutSize - borderOffset * 2, + _cutOutSize - borderOffset * 2, + ); + + canvas + ..saveLayer( + rect, + backgroundPaint, + ) + ..drawRect( + rect, + backgroundPaint, + ) + // Draw top right corner + ..drawRRect( + RRect.fromLTRBAndCorners( + cutOutRect.right - _borderLength, + cutOutRect.top, + cutOutRect.right, + cutOutRect.top + _borderLength, + topRight: Radius.circular(borderRadius), + ), + borderPaint, + ) + // Draw top left corner + ..drawRRect( + RRect.fromLTRBAndCorners( + cutOutRect.left, + cutOutRect.top, + cutOutRect.left + _borderLength, + cutOutRect.top + _borderLength, + topLeft: Radius.circular(borderRadius), + ), + borderPaint, + ) + // Draw bottom right corner + ..drawRRect( + RRect.fromLTRBAndCorners( + cutOutRect.right - _borderLength, + cutOutRect.bottom - _borderLength, + cutOutRect.right, + cutOutRect.bottom, + bottomRight: Radius.circular(borderRadius), + ), + borderPaint, + ) + // Draw bottom left corner + ..drawRRect( + RRect.fromLTRBAndCorners( + cutOutRect.left, + cutOutRect.bottom - _borderLength, + cutOutRect.left + _borderLength, + cutOutRect.bottom, + bottomLeft: Radius.circular(borderRadius), + ), + borderPaint, + ) + ..drawRRect( + RRect.fromRectAndRadius( + cutOutRect, + Radius.circular(borderRadius), + ), + boxPaint, + ) + ..restore(); + } + + @override + ShapeBorder scale(double t) { + return QrScannerOverlayShape( + borderColor: borderColor, + borderWidth: borderWidth, + overlayColor: overlayColor, + ); + } +} diff --git a/lib/util/SizeConfig.dart b/lib/util/SizeConfig.dart new file mode 100644 index 0000000..1345b53 --- /dev/null +++ b/lib/util/SizeConfig.dart @@ -0,0 +1,29 @@ +import 'package:flutter/widgets.dart'; + +class SizeConfig { + static MediaQueryData _mediaQueryData; + static double screenWidth; + static double screenHeight; + static double blockSizeHorizontal; + static double blockSizeVertical; + + static double _safeAreaHorizontal; + static double _safeAreaVertical; + static double safeBlockHorizontal; + static double safeBlockVertical; + + void init(BuildContext context) { + _mediaQueryData = MediaQuery.of(context); + screenWidth = _mediaQueryData.size.width; + screenHeight = _mediaQueryData.size.height; + blockSizeHorizontal = screenWidth / 100; + blockSizeVertical = screenHeight / 100; + + _safeAreaHorizontal = + _mediaQueryData.padding.left + _mediaQueryData.padding.right; + _safeAreaVertical = + _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; + safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 100; + safeBlockVertical = (screenHeight - _safeAreaVertical) / 100; + } +} diff --git a/lib/util/consts.dart b/lib/util/consts.dart new file mode 100644 index 0000000..a5c62f4 --- /dev/null +++ b/lib/util/consts.dart @@ -0,0 +1,180 @@ +import 'package:flutter/material.dart'; +import 'dart:math'; + +final bisBoxDecorationStyle = BoxDecoration( + color: Color(0x336CA8F1), + borderRadius: BorderRadius.circular(10.0), + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 6.0, + offset: Offset(0, 2), + ) + ]); +final accentMain = Color(0xFFfedb65); +final darkAccent = Color(0xFFd29b32); +final tesoGold = Color(0xFFd8b468); +final tesoAsh = Color(0xFF908b8f); +final tesoBlue = Color(0xFF2d4dbc); + +//local +// final serverLocation = "http://172.20.10.3:55555/"; + +//testOnline +final serverLocation = "https://mobile.tesoapp.com/"; +final serverwebPrivacy = "https://tesoapp.com/privacy"; +final serverwebHelp = "https://stream.tesoapp.com/frequentlyasked"; +final serverwebTerms = "https://tesoapp.com/eula"; + +final tesoStreaming = "https://stream.tesoapp.com/"; +final paymentServer = "https://securepayments.tesoapp.com/api/mobile/"; +final tesoStreamMux = "https://stream.mux.com/"; + +String tesoPostThumb(post) { + return "https://image.mux.com/$post/thumbnail.png?time=3"; +} + +String tesoProductThumbnail({String productLogo, int width, int height}) { + if (width == null && height != null) { + return "https://mobile.tesoapp.com/products/$productLogo?height=$height"; + } else if (width != null && height != null) { + return "https://mobile.tesoapp.com/products/$productLogo?width=$width&height=$height"; + } else if (width != null && height == null) { + return "https://mobile.tesoapp.com/products/$productLogo?width=$width"; + } else { + return "https://mobile.tesoapp.com/products/$productLogo"; + } +} + +final dynamiclinkprefix = "https://tesoapp.page.link/?link="; +final dynamiclinktrailing = + "&apn=com.sparentechBacware.teso&ibi=com.tesoapp&imv=1.0.1&isi=1573175173"; +final arkeyAndroid = + "7e64228fef25c8ebd5c126411c7fec3506683a430b1668f22073123586c96c1feab0be9721c8cf3e"; +final arkeyios = + "76a6d2a4be1a00714b76d453ab9b95460828b286d07fa89c18b8a6f49a0ccb0813aa3f23be12e73b"; +final slimAccent = Color(0xFF2e7cce); +final mapsKey = "AIzaSyB3gbhILEzrSCFpxE509PnEb7slCkBfBQI"; +//test mapsKey +// final mapsKey = "AIzaSyCHwkEaC_ddxHtTS2GBIX3lUVuiC9wTO-o"; +final twitterKey = "0UMV5CXhhEqrw2QGHqiKzRkn2"; +final twitterSecret = "204Mkhkz5m3swIGgTl74qi8aIiRbC6Nf0zVkB32of0EXpaYHWe"; +// ignore: non_constant_identifier_names +final your_client_id = "1759156777594606"; +// ignore: non_constant_identifier_names +final your_redirect_url = "https://www.facebook.com/connect/login_success.html"; +final userdpURL = serverLocation + "tesoimages/GetProfilePicture/"; +final productURL = serverLocation + "tesoimages/GetProducts/"; +final businessLogoURL = serverLocation + "tesoimages/GetLogo/"; +//final getVideo = serverLocation + "tesoimages/GetVideo/"; + +class Constants { + //App related strings + static String appName = "TESO"; + //Colors for theme + static Color lightPrimary = Colors.white; + static Color darkPrimary = Color(0xFF303030); + static Color lightAccent = Color(0xFFfea404); + static Color darkAccent = Color(0xFFb90222); + static Color lightBG = Color(0xFFF5F5F5); + static Color darkBG = Color(0xFF212121); + static Color darkAccentBright = Colors.white; + static Color lightAccentBright = Colors.black; + + static ThemeData lightTheme = ThemeData( + fontFamily: "TimesNewRoman", + backgroundColor: lightBG, + primaryColor: lightPrimary, + colorScheme: ColorScheme( + primary: Colors.black, + primaryVariant: const Color(0xff3700B3), + secondary: lightAccent, + secondaryVariant: const Color(0xff03dac6), + surface: const Color(0xff121212), + background: darkAccent, + error: const Color(0xffcf6679), + onPrimary: Colors.black, + onSecondary: Colors.black, + onSurface: Colors.white, + onBackground: Colors.white, + onError: Colors.black, + brightness: Brightness.light, + ), + textSelectionTheme: TextSelectionThemeData( + cursorColor: lightAccent, + selectionColor: const Color(0xff90caf9), + selectionHandleColor: const Color(0xFF303030), + ), + primaryColorDark: darkAccentBright, + primaryColorLight: lightAccentBright, + scaffoldBackgroundColor: lightBG, + appBarTheme: AppBarTheme( + backgroundColor: Colors.white, + elevation: 0, + foregroundColor: Colors.black, + titleTextStyle: TextStyle( + color: Colors.black, + fontFamily: "TimesNewRoman", + fontSize: 20, + fontWeight: FontWeight.w800, + ), + ), + ); + + static ThemeData darkTheme = ThemeData( + fontFamily: "TimesNewRoman", + brightness: Brightness.dark, + backgroundColor: darkBG, + primaryColor: darkPrimary, + colorScheme: ColorScheme( + primary: Colors.white, + primaryVariant: const Color(0xff3700B3), + secondary: darkAccent, + secondaryVariant: const Color(0xff03dac6), + surface: const Color(0xff121212), + background: darkAccent, + error: const Color(0xffcf6679), + onPrimary: Colors.black, + onSecondary: Colors.black, + onSurface: Colors.white, + onBackground: Colors.white, + onError: Colors.black, + brightness: Brightness.dark, + ), + textSelectionTheme: TextSelectionThemeData( + cursorColor: darkAccent, + selectionColor: const Color(0xff64ffda), + selectionHandleColor: const Color(0xff1de9b6), + ), + scaffoldBackgroundColor: darkBG, + primaryColorDark: lightAccentBright, + primaryColorLight: darkAccentBright, + appBarTheme: AppBarTheme( + elevation: 0, + titleTextStyle: TextStyle( + fontFamily: "TimesNewRoman", + color: lightBG, + fontSize: 20, + fontWeight: FontWeight.w800, + ), + ), + ); + + static List map(List list, Function handler) { + List result = []; + for (var i = 0; i < list.length; i++) { + result.add(handler(i, list[i])); + } + + return result; + } + + static formatBytes(bytes, decimals) { + if (bytes == 0) return 0.0; + var k = 1024, + dm = decimals <= 0 ? 0 : decimals, + sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], + i = (log(bytes) / log(k)).floor(); + return (((bytes / pow(k, i)).toStringAsFixed(dm)) + ' ' + sizes[i]); + } +} diff --git a/oldupload.txt b/oldupload.txt new file mode 100644 index 0000000..a9a4eae --- /dev/null +++ b/oldupload.txt @@ -0,0 +1,33 @@ +// prefs = await SharedPreferences.getInstance(); + // String token = prefs.getString("tokensTeso"); + // Map requestHeaders = { + // 'Content-type': 'application/json', + // 'Authorization': token + // }; + // String url = serverLocation + "posts/upload-post"; + // var request = http.MultipartRequest('POST', Uri.parse(url)) + // ..headers['Authorization'] = token + // ..fields["title"] = controller.text + // ..fields["aspect"] = aspectRatio + // ..fields["thumbnail"] = + // widget.thumbnail != null ? base64Encode(widget.thumbnail) : null + // ..files.add(await http.MultipartFile.fromPath( + // 'video', widget.video.replaceAll("file://", ""))); + // var res = await request.send(); + + // var response = await dio.Dio().post( + // url, + // data: formData, + // options: dio.Options(headers: requestHeaders), + // onSendProgress: (int sent, int total) { + // print('here : $sent $total'); + // }, + // ); + // if (res.statusCode == 200) { + // var response = await http.Response.fromStream(res); + // Map handler = jsonDecode(response.body); + // PostedAd postedAd = PostedAd.fromJSON(handler); + // Provider.of(context, listen: false) + // .updatePosted(postedAd); + // Navigator.pop(context); + // } else {} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..4177025 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,204 @@ +name: teso +description: Teso Ghana + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev +version: 1.0.3+43 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + better_player: ^0.0.79 + bloc: ^7.2.1 + cached_network_image: ^3.1.0+1 + camera: ^0.9.4+3 + circular_countdown_timer: ^0.2.0 + cloud_firestore: ^3.1.0 + connectivity: ^3.0.6 + country_list_pick: ^1.0.1+5 + cupertino_icons: ^1.0.4 + dio: ^4.0.1 + equatable: ^2.0.3 + firebase_analytics: ^8.3.4 + firebase_auth: ^3.2.0 + firebase_core: ^1.10.0 + firebase_crashlytics: ^2.3.0 + firebase_dynamic_links: ^3.0.1 + firebase_messaging: ^11.1.0 + flare_flutter: ^3.0.2 + flutter: + sdk: flutter + flutter_bloc: ^7.3.2 + flutter_cache_manager: ^3.1.3 + flutter_countdown_timer: ^4.1.0 + flutter_datetime_picker: ^1.5.1 + flutter_inappwebview: ^5.3.2 + flutter_launcher_icons: ^0.9.2 + flutter_libphonenumber: ^1.2.1 + flutter_local_notifications: ^9.0.2 + flutter_polyline_points: ^1.0.0 + flutter_qrcode_scanner: ^0.0.7 + flutter_shapes: ^0.3.0 + flutter_staggered_grid_view: ^0.4.1 + flutter_upchunk: ^1.1.0 + flutter_webview_plugin: ^0.4.0 + geolocator: ^7.7.1 + get_it: ^7.2.0 + google_maps_flutter: ^2.1.0 + google_ml_vision: ^0.0.7 + google_sign_in: ^5.2.1 + http: ^0.13.4 + image_cropper: ^1.4.1 + image_gallery_saver: ^1.7.1 + image_picker: ^0.8.4+4 + inview_notifier_list: ^2.0.0 + intl_phone_number_input: ^0.7.0+2 + jiffy: ^4.1.0 + loading_indicator: ^3.0.2 + location: ^4.3.0 + no_context_navigation: ^2.1.2 + numeral: ^1.2.5 + page_transition: ^2.0.4 + path: ^1.8.0 + path_provider: ^2.0.6 + pedantic: ^1.11.1 + percent_indicator: ^3.4.0 + permission_handler: ^8.2.6 + progress_indicators: ^1.0.0 + provider: ^6.0.1 + pull_to_refresh: ^2.0.0 + rxdart: ^0.27.2 + share_plus: ^3.0.4 + shared_preferences: ^2.0.8 + sign_in_with_apple: ^3.0.0 + crypto: ^3.0.1 + tapioca: ^1.0.5+1 + time_elapsed: ^0.2.6 + url_launcher: ^6.0.12 + video_player: ^2.2.6 + video_thumbnail: ^0.4.3 + video_trimmer: ^0.6.0 + webview_flutter: ^2.3.0 + +flutter_icons: + image_path: "assets/images/tesoCouponInsignia.png" + android: true + ios: true + + dev_dependencies: null + flutter_test: + sdk: flutter + +flutter: + + uses-material-design: true + assets: + - assets/lovw.gif + - assets/styles/light.txt + - assets/styles/dark.txt + - assets/images/color-filters.png + - assets/images/tesoDP/dp1.png + - assets/images/driving_pin.png + - assets/images/destination_map_marker.png + - assets/images/gold1.png + - assets/images/silver1.png + - assets/images/blue.png + - assets/images/wallet.png + - assets/images/refer.png + - assets/images/red.png + - assets/images/redBack.png + - assets/images/grey.png + - assets/images/firstTime.png + - assets/images/blank.jpg + - assets/images/tesoCouponInsignia.png + - assets/images/rawLogo.png + - assets/images/rawLogoOverlay.png + - assets/images/loading.png + - assets/images/empty.png + - assets/images/prominent-disclosure.jpg + - assets/images/google.png + - assets/images/twitter.png + - assets/images/facebook.png + - assets/images/facebook_new.png + - assets/images/MTN.png + - assets/images/Vodafone.png + - assets/images/AirtelTigo.png + - assets/images/background.png + - assets/images/cashOut.png + - assets/images/payGold.png + - assets/images/store.png + - assets/images/silverAnimated.png + - assets/images/emptyBox.png + - assets/images/categories/agrix.png + - assets/images/categories/antique2.png + - assets/images/categories/autos.png + - assets/images/categories/bags.png + - assets/images/categories/clothes.png + - assets/images/categories/electronics.png + - assets/images/categories/food.png + - assets/images/categories/gifts.png + - assets/images/categories/health.png + - assets/images/categories/home.png + - assets/images/categories/machinery.png + - assets/images/categories/pets.png + + fonts: + - family: WickedGrit + fonts: + - asset: fonts/WickedGrit.ttf + - family: DeadheadScript + fonts: + - asset: fonts/Deadhead_Script.ttf + - family: Locanita + fonts: + - asset: fonts/Locanita.ttf + - family: Billabong + fonts: + - asset: fonts/Billabong.ttf + - family: AlexBrush + fonts: + - asset: fonts/AlexBrush-Regular.ttf + - family: Allura + fonts: + - asset: fonts/Allura-Regular.otf + - family: Arizonia + fonts: + - asset: fonts/Arizonia-Regular.ttf + - family: ChunkFive + fonts: + - asset: fonts/ChunkFive-Regular.otf + - family: GrandHotel + fonts: + - asset: fonts/GrandHotel-Regular.otf + - family: GreatVibes + fonts: + - asset: fonts/GreatVibes-Regular.otf + - family: Lobster + fonts: + - asset: fonts/Lobster_1.3.otf + - family: OpenSans + fonts: + - asset: fonts/OpenSans-Regular.ttf + - family: OstrichSans + fonts: + - asset: fonts/OstrichSans-Medium.otf + - family: Oswald + fonts: + - asset: fonts/Oswald-Regular.ttf + - family: Pacifico + fonts: + - asset: fonts/Pacifico.ttf + - family: Quicksand + fonts: + - asset: fonts/Quicksand-Regular.otf + - family: Roboto + fonts: + - asset: fonts/Roboto-Regular.ttf + - family: SEASRN + fonts: + - asset: fonts/SEASRN.ttf + - family: Windsong + fonts: + - asset: fonts/Windsong.ttf diff --git a/teso.iml b/teso.iml new file mode 100644 index 0000000..e5c8371 --- /dev/null +++ b/teso.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..14f51d7 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +// import 'package:flutter/material.dart'; +// import 'package:flutter_test/flutter_test.dart'; + +// import 'package:teso/main.dart'; + +// void main() { +// testWidgets('Counter increments smoke test', (WidgetTester tester) async { +// // Build our app and trigger a frame. +// await tester.pumpWidget(MyApp()); + +// // Verify that our counter starts at 0. +// expect(find.text('0'), findsOneWidget); +// expect(find.text('1'), findsNothing); + +// // Tap the '+' icon and trigger a frame. +// await tester.tap(find.byIcon(Icons.add)); +// await tester.pump(); + +// // Verify that our counter has incremented. +// expect(find.text('0'), findsNothing); +// expect(find.text('1'), findsOneWidget); +// }); +//}