From e2b18d44e7ded82a3319c62ce57b7eb0aee7e462 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 10:24:50 +0500 Subject: [PATCH 001/161] feat: font family localization assets --- evently/.gitignore | 43 ++ evently/.metadata | 45 ++ evently/README.md | 16 + evently/analysis_options.yaml | 28 + evently/android/.gitignore | 13 + evently/android/app/build.gradle | 67 ++ .../android/app/src/main/AndroidManifest.xml | 44 ++ .../com/example/evently/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + evently/android/build.gradle | 18 + evently/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + evently/android/settings.gradle | 26 + evently/assets/fonts/UniversalSans-300.ttf | Bin 0 -> 15368 bytes evently/assets/fonts/UniversalSans-400.ttf | Bin 0 -> 15372 bytes evently/assets/fonts/UniversalSans-500.ttf | Bin 0 -> 15364 bytes evently/assets/fonts/UniversalSans-600.ttf | Bin 0 -> 15372 bytes evently/assets/fonts/UniversalSans-750.ttf | Bin 0 -> 15360 bytes evently/i18n/de.json | 2 + evently/i18n/en-US.json | 2 + evently/i18n/es.json | 2 + evently/i18n/ru-RU.json | 2 + evently/ios/.gitignore | 34 + evently/ios/Flutter/AppFrameworkInfo.plist | 26 + evently/ios/Flutter/Debug.xcconfig | 2 + evently/ios/Flutter/Release.xcconfig | 2 + evently/ios/Podfile | 44 ++ evently/ios/Runner.xcodeproj/project.pbxproj | 619 +++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + evently/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 +++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + evently/ios/Runner/Base.lproj/Main.storyboard | 26 + evently/ios/Runner/Info.plist | 49 ++ evently/ios/Runner/Runner-Bridging-Header.h | 1 + evently/ios/RunnerTests/RunnerTests.swift | 12 + evently/lib/main.dart | 22 + evently/linux/.gitignore | 1 + evently/linux/CMakeLists.txt | 145 ++++ evently/linux/flutter/CMakeLists.txt | 88 +++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + evently/linux/flutter/generated_plugins.cmake | 23 + evently/linux/main.cc | 6 + evently/linux/my_application.cc | 124 +++ evently/linux/my_application.h | 18 + evently/macos/.gitignore | 7 + evently/macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 12 + evently/macos/Podfile | 43 ++ .../macos/Runner.xcodeproj/project.pbxproj | 705 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + evently/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 ++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes evently/macos/Runner/Base.lproj/MainMenu.xib | 343 +++++++++ evently/macos/Runner/Configs/AppInfo.xcconfig | 14 + evently/macos/Runner/Configs/Debug.xcconfig | 2 + evently/macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + evently/macos/Runner/Info.plist | 32 + evently/macos/Runner/MainFlutterWindow.swift | 15 + evently/macos/Runner/Release.entitlements | 8 + evently/macos/RunnerTests/RunnerTests.swift | 12 + evently/pubspec.lock | 391 ++++++++++ evently/pubspec.yaml | 81 ++ evently/test/widget_test.dart | 30 + evently/web/favicon.png | Bin 0 -> 917 bytes evently/web/icons/Icon-192.png | Bin 0 -> 5292 bytes evently/web/icons/Icon-512.png | Bin 0 -> 8252 bytes evently/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes evently/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes evently/web/index.html | 59 ++ evently/web/manifest.json | 35 + evently/windows/.gitignore | 17 + evently/windows/CMakeLists.txt | 108 +++ evently/windows/flutter/CMakeLists.txt | 109 +++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 23 + evently/windows/runner/CMakeLists.txt | 40 + evently/windows/runner/Runner.rc | 121 +++ evently/windows/runner/flutter_window.cpp | 71 ++ evently/windows/runner/flutter_window.h | 33 + evently/windows/runner/main.cpp | 43 ++ evently/windows/runner/resource.h | 16 + evently/windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes evently/windows/runner/runner.exe.manifest | 20 + evently/windows/runner/utils.cpp | 65 ++ evently/windows/runner/utils.h | 19 + evently/windows/runner/win32_window.cpp | 288 +++++++ evently/windows/runner/win32_window.h | 102 +++ 138 files changed, 5119 insertions(+) create mode 100644 evently/.gitignore create mode 100644 evently/.metadata create mode 100644 evently/README.md create mode 100644 evently/analysis_options.yaml create mode 100644 evently/android/.gitignore create mode 100644 evently/android/app/build.gradle create mode 100644 evently/android/app/src/main/AndroidManifest.xml create mode 100644 evently/android/app/src/main/kotlin/com/example/evently/MainActivity.kt create mode 100644 evently/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 evently/android/app/src/main/res/drawable/launch_background.xml create mode 100644 evently/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 evently/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 evently/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 evently/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 evently/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 evently/android/app/src/main/res/values-night/styles.xml create mode 100644 evently/android/app/src/main/res/values/styles.xml create mode 100644 evently/android/app/src/profile/AndroidManifest.xml create mode 100644 evently/android/build.gradle create mode 100644 evently/android/gradle.properties create mode 100644 evently/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 evently/android/settings.gradle create mode 100644 evently/assets/fonts/UniversalSans-300.ttf create mode 100644 evently/assets/fonts/UniversalSans-400.ttf create mode 100644 evently/assets/fonts/UniversalSans-500.ttf create mode 100644 evently/assets/fonts/UniversalSans-600.ttf create mode 100644 evently/assets/fonts/UniversalSans-750.ttf create mode 100644 evently/i18n/de.json create mode 100644 evently/i18n/en-US.json create mode 100644 evently/i18n/es.json create mode 100644 evently/i18n/ru-RU.json create mode 100644 evently/ios/.gitignore create mode 100644 evently/ios/Flutter/AppFrameworkInfo.plist create mode 100644 evently/ios/Flutter/Debug.xcconfig create mode 100644 evently/ios/Flutter/Release.xcconfig create mode 100644 evently/ios/Podfile create mode 100644 evently/ios/Runner.xcodeproj/project.pbxproj create mode 100644 evently/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 evently/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 evently/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 evently/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 evently/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 evently/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 evently/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 evently/ios/Runner/AppDelegate.swift create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 evently/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 evently/ios/Runner/Base.lproj/Main.storyboard create mode 100644 evently/ios/Runner/Info.plist create mode 100644 evently/ios/Runner/Runner-Bridging-Header.h create mode 100644 evently/ios/RunnerTests/RunnerTests.swift create mode 100644 evently/lib/main.dart create mode 100644 evently/linux/.gitignore create mode 100644 evently/linux/CMakeLists.txt create mode 100644 evently/linux/flutter/CMakeLists.txt create mode 100644 evently/linux/flutter/generated_plugin_registrant.cc create mode 100644 evently/linux/flutter/generated_plugin_registrant.h create mode 100644 evently/linux/flutter/generated_plugins.cmake create mode 100644 evently/linux/main.cc create mode 100644 evently/linux/my_application.cc create mode 100644 evently/linux/my_application.h create mode 100644 evently/macos/.gitignore create mode 100644 evently/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 evently/macos/Flutter/Flutter-Release.xcconfig create mode 100644 evently/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 evently/macos/Podfile create mode 100644 evently/macos/Runner.xcodeproj/project.pbxproj create mode 100644 evently/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 evently/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 evently/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 evently/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 evently/macos/Runner/AppDelegate.swift create mode 100644 evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 evently/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 evently/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 evently/macos/Runner/Configs/Debug.xcconfig create mode 100644 evently/macos/Runner/Configs/Release.xcconfig create mode 100644 evently/macos/Runner/Configs/Warnings.xcconfig create mode 100644 evently/macos/Runner/DebugProfile.entitlements create mode 100644 evently/macos/Runner/Info.plist create mode 100644 evently/macos/Runner/MainFlutterWindow.swift create mode 100644 evently/macos/Runner/Release.entitlements create mode 100644 evently/macos/RunnerTests/RunnerTests.swift create mode 100644 evently/pubspec.lock create mode 100644 evently/pubspec.yaml create mode 100644 evently/test/widget_test.dart create mode 100644 evently/web/favicon.png create mode 100644 evently/web/icons/Icon-192.png create mode 100644 evently/web/icons/Icon-512.png create mode 100644 evently/web/icons/Icon-maskable-192.png create mode 100644 evently/web/icons/Icon-maskable-512.png create mode 100644 evently/web/index.html create mode 100644 evently/web/manifest.json create mode 100644 evently/windows/.gitignore create mode 100644 evently/windows/CMakeLists.txt create mode 100644 evently/windows/flutter/CMakeLists.txt create mode 100644 evently/windows/flutter/generated_plugin_registrant.cc create mode 100644 evently/windows/flutter/generated_plugin_registrant.h create mode 100644 evently/windows/flutter/generated_plugins.cmake create mode 100644 evently/windows/runner/CMakeLists.txt create mode 100644 evently/windows/runner/Runner.rc create mode 100644 evently/windows/runner/flutter_window.cpp create mode 100644 evently/windows/runner/flutter_window.h create mode 100644 evently/windows/runner/main.cpp create mode 100644 evently/windows/runner/resource.h create mode 100644 evently/windows/runner/resources/app_icon.ico create mode 100644 evently/windows/runner/runner.exe.manifest create mode 100644 evently/windows/runner/utils.cpp create mode 100644 evently/windows/runner/utils.h create mode 100644 evently/windows/runner/win32_window.cpp create mode 100644 evently/windows/runner/win32_window.h diff --git a/evently/.gitignore b/evently/.gitignore new file mode 100644 index 0000000000..29a3a5017f --- /dev/null +++ b/evently/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/evently/.metadata b/evently/.metadata new file mode 100644 index 0000000000..d2765fcbec --- /dev/null +++ b/evently/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "54e66469a933b60ddf175f858f82eaeb97e48c8d" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + - platform: android + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + - platform: ios + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + - platform: linux + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + - platform: macos + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + - platform: web + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + - platform: windows + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/evently/README.md b/evently/README.md new file mode 100644 index 0000000000..1ccda2c495 --- /dev/null +++ b/evently/README.md @@ -0,0 +1,16 @@ +# evently + +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://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/evently/analysis_options.yaml b/evently/analysis_options.yaml new file mode 100644 index 0000000000..0d2902135c --- /dev/null +++ b/evently/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/evently/android/.gitignore b/evently/android/.gitignore new file mode 100644 index 0000000000..6f568019d3 --- /dev/null +++ b/evently/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/evently/android/app/build.gradle b/evently/android/app/build.gradle new file mode 100644 index 0000000000..5f7c9ab47a --- /dev/null +++ b/evently/android/app/build.gradle @@ -0,0 +1,67 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace "com.example.evently" + compileSdk flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.evently" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies {} diff --git a/evently/android/app/src/main/AndroidManifest.xml b/evently/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..3be49b050a --- /dev/null +++ b/evently/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/evently/android/app/src/main/kotlin/com/example/evently/MainActivity.kt b/evently/android/app/src/main/kotlin/com/example/evently/MainActivity.kt new file mode 100644 index 0000000000..3c7b264762 --- /dev/null +++ b/evently/android/app/src/main/kotlin/com/example/evently/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.evently + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/evently/android/app/src/main/res/drawable-v21/launch_background.xml b/evently/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000000..f74085f3f6 --- /dev/null +++ b/evently/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/evently/android/app/src/main/res/drawable/launch_background.xml b/evently/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000000..304732f884 --- /dev/null +++ b/evently/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/evently/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/evently/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/evently/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/evently/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/evently/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/evently/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/evently/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/evently/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/evently/android/app/src/main/res/values-night/styles.xml b/evently/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000000..06952be745 --- /dev/null +++ b/evently/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/evently/android/app/src/main/res/values/styles.xml b/evently/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..cb1ef88056 --- /dev/null +++ b/evently/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/evently/android/app/src/profile/AndroidManifest.xml b/evently/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000000..399f6981d5 --- /dev/null +++ b/evently/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/evently/android/build.gradle b/evently/android/build.gradle new file mode 100644 index 0000000000..bc157bd1a1 --- /dev/null +++ b/evently/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/evently/android/gradle.properties b/evently/android/gradle.properties new file mode 100644 index 0000000000..598d13fee4 --- /dev/null +++ b/evently/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G +android.useAndroidX=true +android.enableJetifier=true diff --git a/evently/android/gradle/wrapper/gradle-wrapper.properties b/evently/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..e1ca574ef0 --- /dev/null +++ b/evently/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/evently/android/settings.gradle b/evently/android/settings.gradle new file mode 100644 index 0000000000..1d6d19b7f8 --- /dev/null +++ b/evently/android/settings.gradle @@ -0,0 +1,26 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/evently/assets/fonts/UniversalSans-300.ttf b/evently/assets/fonts/UniversalSans-300.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2b315e809886545cccf59902ce784334909632de GIT binary patch literal 15368 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 M;EXs(V}-5%e~K1MBLDyZ literal 0 HcmV?d00001 diff --git a/evently/assets/fonts/UniversalSans-500.ttf b/evently/assets/fonts/UniversalSans-500.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9397e376438258bfe2b5876ef47225716a5b532d GIT binary patch literal 15364 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 WJk6hXmYqsx9nfNh?qP{~v;PkcJwz}7 literal 0 HcmV?d00001 diff --git a/evently/assets/fonts/UniversalSans-600.ttf b/evently/assets/fonts/UniversalSans-600.ttf new file mode 100644 index 0000000000000000000000000000000000000000..44705b9defdb908d15bfaac1fcc57ab720a182ec GIT binary patch literal 15372 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 literal 0 HcmV?d00001 diff --git a/evently/i18n/de.json b/evently/i18n/de.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/evently/i18n/de.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/evently/i18n/en-US.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/evently/i18n/es.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/evently/i18n/ru-RU.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/evently/ios/.gitignore b/evently/ios/.gitignore new file mode 100644 index 0000000000..7a7f9873ad --- /dev/null +++ b/evently/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.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/ephemeral/ +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/evently/ios/Flutter/AppFrameworkInfo.plist b/evently/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000000..7c56964006 --- /dev/null +++ b/evently/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 + 12.0 + + diff --git a/evently/ios/Flutter/Debug.xcconfig b/evently/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000000..ec97fc6f30 --- /dev/null +++ b/evently/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/evently/ios/Flutter/Release.xcconfig b/evently/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000000..c4855bfe20 --- /dev/null +++ b/evently/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/evently/ios/Podfile b/evently/ios/Podfile new file mode 100644 index 0000000000..d97f17e223 --- /dev/null +++ b/evently/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +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 + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/evently/ios/Runner.xcodeproj/project.pbxproj b/evently/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..39d6832f04 --- /dev/null +++ b/evently/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,619 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy 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 = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + 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 */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + 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 */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + 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 = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 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 */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 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 */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + 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 */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency 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; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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_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; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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.0; + 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 = PKBW8U3YH7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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_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; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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.0; + 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; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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_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; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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.0; + 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 = PKBW8U3YH7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently; + 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 = PKBW8U3YH7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently; + 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 */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 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/evently/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/evently/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..919434a625 --- /dev/null +++ b/evently/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/evently/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/evently/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/evently/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/evently/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/evently/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/evently/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/evently/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/evently/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000000..8e3ca5dfe1 --- /dev/null +++ b/evently/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/evently/ios/Runner.xcworkspace/contents.xcworkspacedata b/evently/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..1d526a16ed --- /dev/null +++ b/evently/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/evently/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/evently/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/evently/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/evently/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/evently/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/evently/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/evently/ios/Runner/AppDelegate.swift b/evently/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000000..70693e4a8c --- /dev/null +++ b/evently/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/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d36b1fab2d --- /dev/null +++ b/evently/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/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/evently/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000000..0bedcf2fd4 --- /dev/null +++ b/evently/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/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/evently/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000000..89c2725b70 --- /dev/null +++ b/evently/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/evently/ios/Runner/Base.lproj/LaunchScreen.storyboard b/evently/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..f2e259c7c9 --- /dev/null +++ b/evently/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/evently/ios/Runner/Base.lproj/Main.storyboard b/evently/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..f3c28516fb --- /dev/null +++ b/evently/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/evently/ios/Runner/Info.plist b/evently/ios/Runner/Info.plist new file mode 100644 index 0000000000..aadaf6c0dc --- /dev/null +++ b/evently/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Evently + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + evently + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/evently/ios/Runner/Runner-Bridging-Header.h b/evently/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000000..308a2a560b --- /dev/null +++ b/evently/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/evently/ios/RunnerTests/RunnerTests.swift b/evently/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000000..86a7c3b1b6 --- /dev/null +++ b/evently/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/evently/lib/main.dart b/evently/lib/main.dart new file mode 100644 index 0000000000..01d470fbc0 --- /dev/null +++ b/evently/lib/main.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Evently', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const Placeholder(), + ); + } +} diff --git a/evently/linux/.gitignore b/evently/linux/.gitignore new file mode 100644 index 0000000000..d3896c9844 --- /dev/null +++ b/evently/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/evently/linux/CMakeLists.txt b/evently/linux/CMakeLists.txt new file mode 100644 index 0000000000..3f5f9a3f72 --- /dev/null +++ b/evently/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "evently") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.evently") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/evently/linux/flutter/CMakeLists.txt b/evently/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000000..d5bd01648a --- /dev/null +++ b/evently/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/evently/linux/flutter/generated_plugin_registrant.cc b/evently/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000000..e71a16d23d --- /dev/null +++ b/evently/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/evently/linux/flutter/generated_plugin_registrant.h b/evently/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000000..e0f0a47bc0 --- /dev/null +++ b/evently/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/evently/linux/flutter/generated_plugins.cmake b/evently/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000000..2e1de87a7e --- /dev/null +++ b/evently/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/evently/linux/main.cc b/evently/linux/main.cc new file mode 100644 index 0000000000..e7c5c54370 --- /dev/null +++ b/evently/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/evently/linux/my_application.cc b/evently/linux/my_application.cc new file mode 100644 index 0000000000..549c112a05 --- /dev/null +++ b/evently/linux/my_application.cc @@ -0,0 +1,124 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "evently"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "evently"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/evently/linux/my_application.h b/evently/linux/my_application.h new file mode 100644 index 0000000000..72271d5e41 --- /dev/null +++ b/evently/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/evently/macos/.gitignore b/evently/macos/.gitignore new file mode 100644 index 0000000000..746adbb6b9 --- /dev/null +++ b/evently/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/evently/macos/Flutter/Flutter-Debug.xcconfig b/evently/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000000..4b81f9b2d2 --- /dev/null +++ b/evently/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/evently/macos/Flutter/Flutter-Release.xcconfig b/evently/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000000..5caa9d1579 --- /dev/null +++ b/evently/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/evently/macos/Flutter/GeneratedPluginRegistrant.swift b/evently/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000000..724bb2ac32 --- /dev/null +++ b/evently/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import shared_preferences_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) +} diff --git a/evently/macos/Podfile b/evently/macos/Podfile new file mode 100644 index 0000000000..c795730db8 --- /dev/null +++ b/evently/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +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', 'ephemeral', '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 Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/evently/macos/Runner.xcodeproj/project.pbxproj b/evently/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..fea2009b12 --- /dev/null +++ b/evently/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* evently.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "evently.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* evently.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* evently.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/evently.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/evently"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/evently.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/evently"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.evently.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/evently.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/evently"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/evently/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/evently/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/evently/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/evently/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/evently/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000000..835c74450e --- /dev/null +++ b/evently/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/evently/macos/Runner.xcworkspace/contents.xcworkspacedata b/evently/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..1d526a16ed --- /dev/null +++ b/evently/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/evently/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/evently/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/evently/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/evently/macos/Runner/AppDelegate.swift b/evently/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000000..d53ef64377 --- /dev/null +++ b/evently/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..a2ec33f19f --- /dev/null +++ b/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/evently/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/evently/macos/Runner/Configs/AppInfo.xcconfig b/evently/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000000..e0c7993dcb --- /dev/null +++ b/evently/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = evently + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.evently + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/evently/macos/Runner/Configs/Debug.xcconfig b/evently/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000000..36b0fd9464 --- /dev/null +++ b/evently/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/evently/macos/Runner/Configs/Release.xcconfig b/evently/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000000..dff4f49561 --- /dev/null +++ b/evently/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/evently/macos/Runner/Configs/Warnings.xcconfig b/evently/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000000..42bcbf4780 --- /dev/null +++ b/evently/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/evently/macos/Runner/DebugProfile.entitlements b/evently/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000000..dddb8a30c8 --- /dev/null +++ b/evently/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/evently/macos/Runner/Info.plist b/evently/macos/Runner/Info.plist new file mode 100644 index 0000000000..4789daa6a4 --- /dev/null +++ b/evently/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/evently/macos/Runner/MainFlutterWindow.swift b/evently/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000000..3cc05eb234 --- /dev/null +++ b/evently/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/evently/macos/Runner/Release.entitlements b/evently/macos/Runner/Release.entitlements new file mode 100644 index 0000000000..852fa1a472 --- /dev/null +++ b/evently/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/evently/macos/RunnerTests/RunnerTests.swift b/evently/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000000..5418c9f539 --- /dev/null +++ b/evently/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/evently/pubspec.lock b/evently/pubspec.lock new file mode 100644 index 0000000000..61d3ba1aeb --- /dev/null +++ b/evently/pubspec.lock @@ -0,0 +1,391 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + sha256: "432698c31a488dd64c56d4759f20d04844baba5e9e4f2cb1abb9676257918b17" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + easy_logger: + dependency: transitive + description: + name: easy_logger + sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 + url: "https://pub.dev" + source: hosted + version: "0.0.2" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: transitive + description: + name: meta + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + url: "https://pub.dev" + source: hosted + version: "5.5.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" +sdks: + dart: ">=3.3.4 <4.0.0" + flutter: ">=3.19.0" diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml new file mode 100644 index 0000000000..3676e656ea --- /dev/null +++ b/evently/pubspec.yaml @@ -0,0 +1,81 @@ +name: evently +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=3.3.4 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.6 + easy_localization: ^3.0.6 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^3.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - i18n/en-US.json + - i18n/ru-RU.json + - i18n/de.json + - i18n/es.json + + fonts: + - family: UniversalSans + fonts: + - asset: assets/fonts/UniversalSans-300.ttf + weight: 300 + - asset: assets/fonts/UniversalSans-400.ttf + weight: 400 + - asset: assets/fonts/UniversalSans-500.ttf + weight: 500 + - asset: assets/fonts/UniversalSans-600.ttf + weight: 600 + - asset: assets/fonts/UniversalSans-750.ttf + weight: 700 \ No newline at end of file diff --git a/evently/test/widget_test.dart b/evently/test/widget_test.dart new file mode 100644 index 0000000000..98e97f7664 --- /dev/null +++ b/evently/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 in the flutter_test package. 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:evently/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const 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); + }); +} diff --git a/evently/web/favicon.png b/evently/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/evently/web/icons/Icon-192.png b/evently/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/evently/web/icons/Icon-512.png b/evently/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/evently/web/icons/Icon-maskable-192.png b/evently/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/evently/web/icons/Icon-maskable-512.png b/evently/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/evently/web/index.html b/evently/web/index.html new file mode 100644 index 0000000000..1daf61dade --- /dev/null +++ b/evently/web/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + evently + + + + + + + + + + diff --git a/evently/web/manifest.json b/evently/web/manifest.json new file mode 100644 index 0000000000..5f96e74995 --- /dev/null +++ b/evently/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "evently", + "short_name": "evently", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/evently/windows/.gitignore b/evently/windows/.gitignore new file mode 100644 index 0000000000..d492d0d98c --- /dev/null +++ b/evently/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/evently/windows/CMakeLists.txt b/evently/windows/CMakeLists.txt new file mode 100644 index 0000000000..2ead30126c --- /dev/null +++ b/evently/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(evently LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "evently") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/evently/windows/flutter/CMakeLists.txt b/evently/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000000..903f4899d6 --- /dev/null +++ b/evently/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/evently/windows/flutter/generated_plugin_registrant.cc b/evently/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000000..8b6d4680af --- /dev/null +++ b/evently/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/evently/windows/flutter/generated_plugin_registrant.h b/evently/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000000..dc139d85a9 --- /dev/null +++ b/evently/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/evently/windows/flutter/generated_plugins.cmake b/evently/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000000..b93c4c30c1 --- /dev/null +++ b/evently/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/evently/windows/runner/CMakeLists.txt b/evently/windows/runner/CMakeLists.txt new file mode 100644 index 0000000000..394917c053 --- /dev/null +++ b/evently/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/evently/windows/runner/Runner.rc b/evently/windows/runner/Runner.rc new file mode 100644 index 0000000000..fc1d6dccb2 --- /dev/null +++ b/evently/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "evently" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "evently" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "evently.exe" "\0" + VALUE "ProductName", "evently" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/evently/windows/runner/flutter_window.cpp b/evently/windows/runner/flutter_window.cpp new file mode 100644 index 0000000000..955ee3038f --- /dev/null +++ b/evently/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/evently/windows/runner/flutter_window.h b/evently/windows/runner/flutter_window.h new file mode 100644 index 0000000000..6da0652f05 --- /dev/null +++ b/evently/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/evently/windows/runner/main.cpp b/evently/windows/runner/main.cpp new file mode 100644 index 0000000000..6c53953eb1 --- /dev/null +++ b/evently/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"evently", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/evently/windows/runner/resource.h b/evently/windows/runner/resource.h new file mode 100644 index 0000000000..66a65d1e4a --- /dev/null +++ b/evently/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/evently/windows/runner/resources/app_icon.ico b/evently/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/evently/windows/runner/runner.exe.manifest b/evently/windows/runner/runner.exe.manifest new file mode 100644 index 0000000000..a42ea7687c --- /dev/null +++ b/evently/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/evently/windows/runner/utils.cpp b/evently/windows/runner/utils.cpp new file mode 100644 index 0000000000..b2b08734db --- /dev/null +++ b/evently/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length <= 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/evently/windows/runner/utils.h b/evently/windows/runner/utils.h new file mode 100644 index 0000000000..3879d54755 --- /dev/null +++ b/evently/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/evently/windows/runner/win32_window.cpp b/evently/windows/runner/win32_window.cpp new file mode 100644 index 0000000000..60608d0fe5 --- /dev/null +++ b/evently/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/evently/windows/runner/win32_window.h b/evently/windows/runner/win32_window.h new file mode 100644 index 0000000000..e901dde684 --- /dev/null +++ b/evently/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ From d2ccb8fcff39b265f205d0f15b61a6089c678704 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 10:26:42 +0500 Subject: [PATCH 002/161] feat: easy localizations --- evently/lib/main.dart | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 01d470fbc0..e69a44e404 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,7 +1,21 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp( + EasyLocalization( + supportedLocales: const [ + Locale('en', 'US'), + Locale('ru', 'RU'), + Locale('es'), + Locale('de'), + ], + path: 'i18n', + fallbackLocale: const Locale('en', 'US'), + saveLocale: false, + child: const MyApp(), + ), + ); } class MyApp extends StatelessWidget { From 0f996ab1ed243503660ceb723c05c6ffa2853572 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 10:32:47 +0500 Subject: [PATCH 003/161] feat: screen utils init --- evently/lib/main.dart | 19 +++++++++++-------- evently/pubspec.lock | 8 ++++++++ evently/pubspec.yaml | 1 + 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/evently/lib/main.dart b/evently/lib/main.dart index e69a44e404..5ebe108c9f 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; void main() { runApp( @@ -24,13 +25,15 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Evently', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const Placeholder(), - ); + return ScreenUtilInit( + minTextAdapt: true, + builder: (BuildContext context, child) => MaterialApp( + title: 'Evently', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const Placeholder(), + )); } } diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 61d3ba1aeb..8af3e0b22a 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -115,6 +115,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_screenutil: + dependency: "direct main" + description: + name: flutter_screenutil + sha256: "8cf100b8e4973dc570b6415a2090b0bfaa8756ad333db46939efc3e774ee100d" + url: "https://pub.dev" + source: hosted + version: "5.9.0" flutter_test: dependency: "direct dev" description: flutter diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index 3676e656ea..a936584545 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.6 easy_localization: ^3.0.6 + flutter_screenutil: ^5.9.0 dev_dependencies: flutter_test: From d2651b6b26878d285f9b792fb9df76615f7ab1a8 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 10:37:52 +0500 Subject: [PATCH 004/161] feat: basic app architecture following easel app --- evently/lib/main.dart | 33 ++++++--- evently/lib/utils/evently_app_theme.dart | 73 +++++++++++++++++++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + evently/pubspec.lock | 66 ++++++++++++++++- evently/pubspec.yaml | 1 + 5 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 evently/lib/utils/evently_app_theme.dart diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 5ebe108c9f..620fe85924 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,4 +1,5 @@ import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/utils/evently_app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -19,6 +20,8 @@ void main() { ); } +final navigatorKey = GlobalKey(); + class MyApp extends StatelessWidget { const MyApp({super.key}); @@ -26,14 +29,26 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ScreenUtilInit( - minTextAdapt: true, - builder: (BuildContext context, child) => MaterialApp( - title: 'Evently', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const Placeholder(), - )); + minTextAdapt: true, + builder: (BuildContext context, child) => MaterialApp( + builder: (context, widget) { + ScreenUtil.init(context); + return MediaQuery( + data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), + child: widget!, + ); + }, + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + title: 'Evently', + navigatorKey: navigatorKey, + theme: EventlyAppTheme.theme(context), + initialRoute: '/', + routes: { + '/': (context) => const Placeholder(), + }, + ), + ); } } diff --git a/evently/lib/utils/evently_app_theme.dart b/evently/lib/utils/evently_app_theme.dart new file mode 100644 index 0000000000..01924e6214 --- /dev/null +++ b/evently/lib/utils/evently_app_theme.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class EventlyAppTheme { + static const Color kWhite = Color(0xFFFFFFFF); + static const Color kBgColor = Color.fromRGBO(242, 239, 234, 1); + static const Color kGrey = Color(0xFF8D8C8C); + static const Color kLightGreyText = Color(0xFF9B9A9A); + static const Color kLightGrey = Color(0xFFC4C4C4); + static const Color kLightGrey02 = Color(0xFFF2EFEA); + static const Color kDarkGrey02 = Color(0xFF808080); + static const Color kDartGrey = Color(0xFF333333); + static const Color kDartGrey02 = Color(0xFFA1A1A1); + static const Color kLightGrey03 = Color(0xFFD9D9D9); + static const Color kBlack = Colors.black; + static const Color kTransparent = Colors.transparent; + static const Color kBlue = Color(0xFF1212C4); + static const Color kDarkText = Color(0xFF080830); + static const Color kLightText = Color(0xFF464545); + static const Color kNightBlue = Color(0xFF0A004A); + static const Color kLightWhiteBackground = Color(0xFFE5E5E5); + static const Color kRed = Color(0xFFFC4403); + static const Color kWhite02 = Color.fromRGBO(255, 255, 255, 0.2); + static const Color kWhite03 = Color(0xFFFBFBFB); + static const Color kPurple01 = Color.fromRGBO(18, 18, 196, 0.6); + static const Color kDarkBlue = Color.fromRGBO(18, 18, 196, 1); + static const Color kPurple02 = Color(0xFF4534CE); + static const Color kPurple03 = Color(0xFFCBC8F3); + static const Color kLightPurple = Color(0xFFB6B6E8); + static const Color kDarkGreen = Color(0xFF3A8977); + static const Color kYellow = Color(0xFFF3BA2F); + static const Color kLightYellow = Color(0xFFFED564); + static const Color kLightRed = Color(0xFFEF4421); + static const Color kBgWhite = Color(0xFFE5E5E5); + static const Color kLightBlackText = Color(0xFFA0A6AB); + static const Color kpurpleButtonColor = Color(0xFF8F8FCE); + static const Color kpurpleDark = Color(0xFF1212C4); + static const Color kTextGrey = Color(0xFF7A7A8F); + static const Color kGreyIcon = Color(0xFFAEAEAE); + static const Color kDarkPurple = Color(0xff0A004A); + + static const Color kLightGreyColor = Color(0xFFE5E5E5); + static const Color kPurple = Color(0xFF4421CC); + static const Color kHashtagColor = Color(0xFFB6B6E8); + + static const String universalSansFamily = "UniversalSans"; + + static ThemeData theme(BuildContext context) => ThemeData( + primaryColor: kWhite, + textTheme: GoogleFonts.interTextTheme(Theme.of(context).textTheme), + scaffoldBackgroundColor: kWhite, + visualDensity: VisualDensity.standard, + ); + + static Color cardBackground = const Color(0xFFC4C4C4).withOpacity(0.2); + static Color cardBackgroundSelected = const Color(0x801212C4).withOpacity(0.2); + + static TextStyle titleStyle = TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w800, + color: EventlyAppTheme.kBlack, + fontFamily: universalSansFamily, + ); + static TextStyle digitTextStyle = TextStyle( + fontSize: 20.sp, + fontWeight: FontWeight.w800, + color: EventlyAppTheme.kWhite, + fontFamily: universalSansFamily, + ); + + static TextStyle kDeleteHeaderTextStyle = TextStyle(fontSize: 14.sp, fontFamily: 'UniversalSans', color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w600); +} diff --git a/evently/macos/Flutter/GeneratedPluginRegistrant.swift b/evently/macos/Flutter/GeneratedPluginRegistrant.swift index 724bb2ac32..b8e2b22f76 100644 --- a/evently/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/evently/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,10 @@ import FlutterMacOS import Foundation +import path_provider_foundation import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 8af3e0b22a..9b371c0a60 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -49,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -133,6 +141,30 @@ packages: description: flutter source: sdk version: "0.0.0" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82 + url: "https://pub.dev" + source: hosted + version: "6.2.1" + http: + dependency: transitive + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" intl: dependency: transitive description: @@ -205,6 +237,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + url: "https://pub.dev" + source: hosted + version: "2.2.4" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + url: "https://pub.dev" + source: hosted + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -354,6 +410,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" vector_math: dependency: transitive description: @@ -396,4 +460,4 @@ packages: version: "1.0.4" sdks: dart: ">=3.3.4 <4.0.0" - flutter: ">=3.19.0" + flutter: ">=3.19.2" diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index a936584545..c7d428ee02 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: cupertino_icons: ^1.0.6 easy_localization: ^3.0.6 flutter_screenutil: ^5.9.0 + google_fonts: ^6.2.1 dev_dependencies: flutter_test: From af862850851b82327d9dedddf7f78f4d5be889f0 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 10:43:15 +0500 Subject: [PATCH 005/161] feat: routes configurations --- evently/lib/main.dart | 5 ++++- evently/lib/utils/route_util.dart | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 evently/lib/utils/route_util.dart diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 620fe85924..0cbbca978f 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -3,7 +3,10 @@ import 'package:evently/utils/evently_app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -void main() { +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await EasyLocalization.ensureInitialized(); + runApp( EasyLocalization( supportedLocales: const [ diff --git a/evently/lib/utils/route_util.dart b/evently/lib/utils/route_util.dart new file mode 100644 index 0000000000..c8ce8f45b3 --- /dev/null +++ b/evently/lib/utils/route_util.dart @@ -0,0 +1,5 @@ +class RouteUtil { + RouteUtil(); + + static String kRouteEventHub = "/eventHub"; +} From 890719848e0fa6d55173bc35c649d609e1d5f9ad Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 10:47:39 +0500 Subject: [PATCH 006/161] feat: screen responsiveness --- evently/lib/main.dart | 12 ++++++++++++ evently/lib/utils/constants.dart | 1 + evently/lib/utils/screen_responsive.dart | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 evently/lib/utils/constants.dart create mode 100644 evently/lib/utils/screen_responsive.dart diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 0cbbca978f..250bed3617 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,12 +1,19 @@ +import 'dart:ui'; + import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +bool isTablet = false; + void main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); + isTablet = _getIsCurrentDeviceTablet(); + runApp( EasyLocalization( supportedLocales: const [ @@ -55,3 +62,8 @@ class MyApp extends StatelessWidget { ); } } + +bool _getIsCurrentDeviceTablet() { + final MediaQueryData mediaQuery = MediaQueryData.fromView(PlatformDispatcher.instance.implicitView!); + return mediaQuery.size.shortestSide >= tabletMinWidth; +} diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart new file mode 100644 index 0000000000..40621e4583 --- /dev/null +++ b/evently/lib/utils/constants.dart @@ -0,0 +1 @@ +const double tabletMinWidth = 600; diff --git a/evently/lib/utils/screen_responsive.dart b/evently/lib/utils/screen_responsive.dart new file mode 100644 index 0000000000..6a7510bf5c --- /dev/null +++ b/evently/lib/utils/screen_responsive.dart @@ -0,0 +1,18 @@ +import 'package:evently/main.dart'; +import 'package:flutter/material.dart'; + +class ScreenResponsive extends StatelessWidget { + final WidgetBuilder mobileScreen; + final WidgetBuilder tabletScreen; + + const ScreenResponsive({super.key, required this.mobileScreen, required this.tabletScreen}); + + @override + Widget build(BuildContext context) { + if (isTablet) { + return tabletScreen(context); + } + + return mobileScreen(context); + } +} From a3ab1870a5e1b2c98814cfd8993d083915d30efb Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 10:50:15 +0500 Subject: [PATCH 007/161] feat: splash screen --- evently/lib/main.dart | 4 ++-- evently/lib/screens/splash_screen.dart | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 evently/lib/screens/splash_screen.dart diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 250bed3617..424b766985 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,6 +1,6 @@ import 'dart:ui'; - import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/screens/splash_screen.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:flutter/material.dart'; @@ -56,7 +56,7 @@ class MyApp extends StatelessWidget { theme: EventlyAppTheme.theme(context), initialRoute: '/', routes: { - '/': (context) => const Placeholder(), + '/': (context) => const SplashScreen(), }, ), ); diff --git a/evently/lib/screens/splash_screen.dart b/evently/lib/screens/splash_screen.dart new file mode 100644 index 0000000000..f63c3ff4cd --- /dev/null +++ b/evently/lib/screens/splash_screen.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class SplashScreen extends StatefulWidget { + const SplashScreen({super.key}); + + @override + State createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} From 68a8ba27515a9dc33192355328ceaaf3a8424120 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 10:58:23 +0500 Subject: [PATCH 008/161] feat: splash screen assets --- evently/assets/images/svg/splash.svg | 227 +++++++++++++++++++++++++ evently/lib/screens/splash_screen.dart | 2 +- evently/lib/utils/constants.dart | 5 + evently/pubspec.yaml | 1 + 4 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 evently/assets/images/svg/splash.svg diff --git a/evently/assets/images/svg/splash.svg b/evently/assets/images/svg/splash.svg new file mode 100644 index 0000000000..f0b2fe451a --- /dev/null +++ b/evently/assets/images/svg/splash.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/evently/lib/screens/splash_screen.dart b/evently/lib/screens/splash_screen.dart index f63c3ff4cd..05a58912ec 100644 --- a/evently/lib/screens/splash_screen.dart +++ b/evently/lib/screens/splash_screen.dart @@ -10,6 +10,6 @@ class SplashScreen extends StatefulWidget { class _SplashScreenState extends State { @override Widget build(BuildContext context) { - return const Placeholder(); + return Scaffold(); } } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 40621e4583..ec4ce6ba45 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -1 +1,6 @@ const double tabletMinWidth = 600; + +/// ```SVG assets +class SVGUtils { + static const kSvgSplash = 'assets/images/svg/splash.svg'; +} diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index c7d428ee02..d82275a7c7 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -67,6 +67,7 @@ flutter: - i18n/ru-RU.json - i18n/de.json - i18n/es.json + - assets/images/svg/ fonts: - family: UniversalSans From 0b9c8b60ba925e550696f819d646258156b54cdb Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 11:03:37 +0500 Subject: [PATCH 009/161] feat: screen --- evently/lib/screens/splash_screen.dart | 14 ++++++- evently/pubspec.lock | 56 ++++++++++++++++++++++++++ evently/pubspec.yaml | 1 + 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/evently/lib/screens/splash_screen.dart b/evently/lib/screens/splash_screen.dart index 05a58912ec..11d5e061f5 100644 --- a/evently/lib/screens/splash_screen.dart +++ b/evently/lib/screens/splash_screen.dart @@ -1,4 +1,7 @@ +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/screen_responsive.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; class SplashScreen extends StatefulWidget { const SplashScreen({super.key}); @@ -10,6 +13,15 @@ class SplashScreen extends StatefulWidget { class _SplashScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold(); + return Scaffold( + body: ScreenResponsive( + mobileScreen: (BuildContext context) => buildMobileScreen(context), + tabletScreen: (BuildContext context) => buildMobileScreen(context), + ), + ); + } + + Widget buildMobileScreen(BuildContext context) { + return SvgPicture.asset(SVGUtils.kSvgSplash, fit: BoxFit.cover); } } diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 9b371c0a60..419f03753a 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -131,6 +131,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.9.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" + url: "https://pub.dev" + source: hosted + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -237,6 +245,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" path_provider: dependency: transitive description: @@ -285,6 +301,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" platform: dependency: transitive description: @@ -418,6 +442,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" + url: "https://pub.dev" + source: hosted + version: "1.1.11+1" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da + url: "https://pub.dev" + source: hosted + version: "1.1.11+1" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" + url: "https://pub.dev" + source: hosted + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -458,6 +506,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" sdks: dart: ">=3.3.4 <4.0.0" flutter: ">=3.19.2" diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index d82275a7c7..88554b7604 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: easy_localization: ^3.0.6 flutter_screenutil: ^5.9.0 google_fonts: ^6.2.1 + flutter_svg: ^2.0.10+1 dev_dependencies: flutter_test: From 2f2872d4e4b6d38266b256459a6ba2ac9eb9387b Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 11:08:51 +0500 Subject: [PATCH 010/161] feat: splah basic flow --- evently/lib/main.dart | 2 ++ evently/lib/screens/splash_screen.dart | 10 ++++++++++ evently/lib/utils/constants.dart | 1 + 3 files changed, 13 insertions(+) diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 424b766985..f3da2b4ec7 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/screens/splash_screen.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -57,6 +58,7 @@ class MyApp extends StatelessWidget { initialRoute: '/', routes: { '/': (context) => const SplashScreen(), + RouteUtil.kRouteEventHub: (context) => const Placeholder(), }, ), ); diff --git a/evently/lib/screens/splash_screen.dart b/evently/lib/screens/splash_screen.dart index 11d5e061f5..0a4082b0f6 100644 --- a/evently/lib/screens/splash_screen.dart +++ b/evently/lib/screens/splash_screen.dart @@ -1,4 +1,6 @@ +import 'package:evently/main.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/screen_responsive.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -11,6 +13,14 @@ class SplashScreen extends StatefulWidget { } class _SplashScreenState extends State { + @override + void initState() { + Future.delayed(const Duration(seconds: kSplashScreenDuration), () { + navigatorKey.currentState!.pushReplacementNamed(RouteUtil.kRouteEventHub); + }); + super.initState(); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index ec4ce6ba45..0dca1f9f63 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -1,4 +1,5 @@ const double tabletMinWidth = 600; +const int kSplashScreenDuration = 3; /// ```SVG assets class SVGUtils { From 7e5f5d8a40862d89fd74aaefa92eb7b418ce0ded Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 11:13:08 +0500 Subject: [PATCH 011/161] feat: event hub screen --- evently/lib/main.dart | 3 ++- .../lib/screens/event_hub/event_hub_screen.dart | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 evently/lib/screens/event_hub/event_hub_screen.dart diff --git a/evently/lib/main.dart b/evently/lib/main.dart index f3da2b4ec7..6559377d1c 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:ui'; import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/screens/event_hub/event_hub_screen.dart'; import 'package:evently/screens/splash_screen.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; @@ -58,7 +59,7 @@ class MyApp extends StatelessWidget { initialRoute: '/', routes: { '/': (context) => const SplashScreen(), - RouteUtil.kRouteEventHub: (context) => const Placeholder(), + RouteUtil.kRouteEventHub: (context) => const EventHubScreen(), }, ), ); diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart new file mode 100644 index 0000000000..4b65e6c3ba --- /dev/null +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class EventHubScreen extends StatefulWidget { + const EventHubScreen({super.key}); + + @override + State createState() => _EventHubScreenState(); +} + +class _EventHubScreenState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} From e4e7994f0242829a7a0516689d3d189f44d66ab8 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 11:25:47 +0500 Subject: [PATCH 012/161] feat: event hub screen --- evently/lib/generated/locale_keys.g.dart | 5 +++++ .../screens/event_hub/event_hub_screen.dart | 22 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 evently/lib/generated/locale_keys.g.dart diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart new file mode 100644 index 0000000000..ebfe94fe06 --- /dev/null +++ b/evently/lib/generated/locale_keys.g.dart @@ -0,0 +1,5 @@ +// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart + +abstract class LocaleKeys { + +} diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 4b65e6c3ba..1ade0634f6 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -1,4 +1,6 @@ +import 'package:evently/utils/evently_app_theme.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; class EventHubScreen extends StatefulWidget { const EventHubScreen({super.key}); @@ -10,6 +12,24 @@ class EventHubScreen extends StatefulWidget { class _EventHubScreenState extends State { @override Widget build(BuildContext context) { - return const Placeholder(); + return ColoredBox( + color: EventlyAppTheme.kWhite, + child: SafeArea( + child: Scaffold( + backgroundColor: EventlyAppTheme.kBgWhite, + body: Padding( + padding: EdgeInsets.only(top: 20.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + ], + ), + ), + ), + ), + ); } + + } From 309061db94dd7301655e2accaa8d070cb9870a2f Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 12:04:06 +0500 Subject: [PATCH 013/161] feat: event hub basic with styling --- evently/i18n/de.json | 7 + evently/i18n/en-US.json | 7 + evently/i18n/es.json | 7 + evently/i18n/ru-RU.json | 7 + evently/lib/generated/locale_keys.g.dart | 7 + .../screens/event_hub/event_hub_screen.dart | 131 ++++++++++++- evently/lib/utils/constants.dart | 2 + evently/lib/widgets/clipped_button.dart | 184 ++++++++++++++++++ 8 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 evently/lib/widgets/clipped_button.dart diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 7a73a41bfd..8050886a69 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -1,2 +1,9 @@ { + "eventhub": "Event Hub", + "welcome_event": "Welcome to Evently, where you can create custom events and issue NFT tickets with perks attendees can use during the event!", + "draft": "Draft", + "for_sale" : "For Sale", + "history": "History", + "no_nft_created": "There is no NFTs created.", + "create_event": "Create an event!" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 7a73a41bfd..8050886a69 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -1,2 +1,9 @@ { + "eventhub": "Event Hub", + "welcome_event": "Welcome to Evently, where you can create custom events and issue NFT tickets with perks attendees can use during the event!", + "draft": "Draft", + "for_sale" : "For Sale", + "history": "History", + "no_nft_created": "There is no NFTs created.", + "create_event": "Create an event!" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 7a73a41bfd..8050886a69 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -1,2 +1,9 @@ { + "eventhub": "Event Hub", + "welcome_event": "Welcome to Evently, where you can create custom events and issue NFT tickets with perks attendees can use during the event!", + "draft": "Draft", + "for_sale" : "For Sale", + "history": "History", + "no_nft_created": "There is no NFTs created.", + "create_event": "Create an event!" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 7a73a41bfd..8050886a69 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -1,2 +1,9 @@ { + "eventhub": "Event Hub", + "welcome_event": "Welcome to Evently, where you can create custom events and issue NFT tickets with perks attendees can use during the event!", + "draft": "Draft", + "for_sale" : "For Sale", + "history": "History", + "no_nft_created": "There is no NFTs created.", + "create_event": "Create an event!" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index ebfe94fe06..078c26fb99 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -1,5 +1,12 @@ // DO NOT EDIT. This is code generated via package:easy_localization/generate.dart abstract class LocaleKeys { + static const eventhub = 'eventhub'; + static const welcome_event = 'welcome_event'; + static const draft = 'draft'; + static const for_sale = 'for_sale'; + static const history = 'history'; + static const no_nft_created = 'no_nft_created'; + static const create_event = 'create_event'; } diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 1ade0634f6..f0c395832b 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -1,4 +1,10 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/main.dart'; +import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/widgets/clipped_button.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -10,19 +16,103 @@ class EventHubScreen extends StatefulWidget { } class _EventHubScreenState extends State { + TextStyle headingStyle = TextStyle( + fontSize: isTablet ? 20.sp : 20.sp, + fontWeight: FontWeight.w800, + color: EventlyAppTheme.kLightPurple, + fontFamily: kUniversalFontFamily, + ); + TextStyle titleStyle = TextStyle( + fontSize: isTablet ? 14.sp : 14.sp, + fontWeight: FontWeight.w700, + color: EventlyAppTheme.kWhite, + fontFamily: kUniversalFontFamily, + ); + TextStyle btnTxtStyle = TextStyle( + fontSize: isTablet ? 12.sp : 12.sp, + fontWeight: FontWeight.w700, + color: EventlyAppTheme.kWhite, + fontFamily: kUniversalFontFamily, + ); + + TextStyle subTitleStyle = TextStyle( + fontSize: isTablet ? 12.sp : 12.sp, + fontWeight: FontWeight.w700, + color: EventlyAppTheme.kLightText, + fontFamily: kUniversalFontFamily, + ); + @override Widget build(BuildContext context) { - return ColoredBox( - color: EventlyAppTheme.kWhite, + return ColoredBox( + color: EventlyAppTheme.kBlack, child: SafeArea( child: Scaffold( - backgroundColor: EventlyAppTheme.kBgWhite, + backgroundColor: EventlyAppTheme.kBlack, body: Padding( padding: EdgeInsets.only(top: 20.h), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - + Container( + height: 40.h, + alignment: Alignment.centerRight, + padding: EdgeInsets.symmetric(horizontal: 16.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: () => {}, + child: DecoratedBox( + decoration: BoxDecoration( + color: EventlyAppTheme.kpurpleDark, + boxShadow: [BoxShadow(color: EventlyAppTheme.kpurpleDark.withOpacity(0.6), blurRadius: 8.0)], + ), + child: Icon(Icons.add, size: 27.h, color: EventlyAppTheme.kWhite), + ), + ), + ], + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Text( + LocaleKeys.eventhub.tr(), + style: headingStyle, + textAlign: TextAlign.center, + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Text( + LocaleKeys.welcome_event.tr(), + style: titleStyle, + textAlign: TextAlign.center, + ), + ), + SizedBox(height: 20.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + getButton(title: LocaleKeys.draft), + getButton(title: LocaleKeys.for_sale), + getButton(title: LocaleKeys.history), + ], + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + LocaleKeys.no_nft_created.tr(), + style: subTitleStyle, + )), + ), + const Spacer(), + Padding(padding: EdgeInsets.symmetric(horizontal: 20.w), child: getCreateEventWidget()), ], ), ), @@ -31,5 +121,36 @@ class _EventHubScreenState extends State { ); } + Widget getButton({required String title}) { + return Expanded( + child: Container( + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration(border: Border.all(color: EventlyAppTheme.kWhite)), + padding: EdgeInsets.symmetric(vertical: 5.h, horizontal: 10.w), + child: Text( + title.tr(), + style: btnTxtStyle, + textAlign: TextAlign.center, + ), + ), + ); + } + Widget getCreateEventWidget() { + return Padding( + padding: EdgeInsets.only(bottom: 20.h), + child: ClippedButton( + title: LocaleKeys.create_event.tr(), + bgColor: EventlyAppTheme.kBlue, + textColor: EventlyAppTheme.kWhite, + onPressed: () {}, + cuttingHeight: 15.h, + clipperType: ClipperType.bottomLeftTopRight, + isShadow: false, + fontWeight: FontWeight.w700, + fontSize: 14, + ), + ); + } } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 0dca1f9f63..60501e0e1c 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -1,5 +1,7 @@ const double tabletMinWidth = 600; const int kSplashScreenDuration = 3; +const String kUniversalFontFamily = "UniversalSans"; + /// ```SVG assets class SVGUtils { diff --git a/evently/lib/widgets/clipped_button.dart b/evently/lib/widgets/clipped_button.dart new file mode 100644 index 0000000000..9e19546ede --- /dev/null +++ b/evently/lib/widgets/clipped_button.dart @@ -0,0 +1,184 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +// ignore_for_file: must_be_immutable + +enum ClipperType { topLeftBottomRight, bottomLeftTopRight } + +class ClippedButton extends StatelessWidget { + final VoidCallback onPressed; + final String title; + final Color bgColor; + final Color textColor; + final double cuttingHeight; + final ClipperType clipperType; + final FontWeight fontWeight; + bool? isShadow = true; + final double fontSize; + + ClippedButton({ + super.key, + required this.onPressed, + required this.title, + required this.bgColor, + required this.textColor, + required this.cuttingHeight, + this.isShadow = true, + required this.clipperType, + required this.fontWeight, + this.fontSize = 16, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () { + onPressed.call(); + }, + child: isShadow! + ? CustomPaint( + painter: clipperType == ClipperType.bottomLeftTopRight + ? BoxShadowPainterBottomLeftTopRight(cuttingHeight: cuttingHeight) + : BoxShadowPainterTopLeftBottomRight(cuttingHeight: cuttingHeight), + child: ClipPath( + clipper: clipperType == ClipperType.bottomLeftTopRight + ? ButtonClipperBottomLeftTopRight(cuttingHeight: cuttingHeight) + : ButtonClipperTopLeftBottomRight(cuttingHeight: cuttingHeight), + child: Container( + color: bgColor, + height: 40.h, + child: Center( + child: Text( + title, + style: TextStyle( + color: textColor, + fontSize: fontSize.sp, + fontWeight: fontWeight, + ), + textAlign: TextAlign.center, + ), + ), + ), + ), + ) + : ClipPath( + clipper: clipperType == ClipperType.bottomLeftTopRight + ? ButtonClipperBottomLeftTopRight(cuttingHeight: cuttingHeight) + : ButtonClipperTopLeftBottomRight(cuttingHeight: cuttingHeight), + child: Container( + color: bgColor, + height: 40.h, + child: Center( + child: Text( + title, + style: TextStyle( + color: textColor, + fontSize: fontSize.sp, + fontWeight: fontWeight, + ), + textAlign: TextAlign.center, + ), + ), + ), + ), + ); + } +} + +class ButtonClipperBottomLeftTopRight extends CustomClipper { + final double cuttingHeight; + + ButtonClipperBottomLeftTopRight({required this.cuttingHeight}); + + @override + Path getClip(Size size) { + final path = Path(); + path.lineTo(0, size.height - cuttingHeight); + path.lineTo(cuttingHeight, size.height); + path.lineTo(size.width, size.height); + path.lineTo(size.width, cuttingHeight); + path.lineTo(size.width - cuttingHeight, 0); + path.lineTo(0, 0); + + return path; + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return false; + } +} + +class ButtonClipperTopLeftBottomRight extends CustomClipper { + final double cuttingHeight; + + ButtonClipperTopLeftBottomRight({required this.cuttingHeight}); + + @override + Path getClip(Size size) { + final path = Path(); + path.moveTo(0, size.height); + path.lineTo(0, cuttingHeight); + path.lineTo(cuttingHeight, 0); + path.lineTo(size.width, 0); + path.lineTo(size.width, size.height - cuttingHeight); + path.lineTo(size.width - cuttingHeight, size.height); + path.lineTo(0, size.height); + + return path; + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return false; + } +} + +class BoxShadowPainterBottomLeftTopRight extends CustomPainter { + final double cuttingHeight; + + BoxShadowPainterBottomLeftTopRight({required this.cuttingHeight}); + + @override + void paint(Canvas canvas, Size size) { + final Path path = Path(); + path.lineTo(0, size.height - cuttingHeight); + path.lineTo(cuttingHeight, size.height); + path.lineTo(size.width, size.height); + path.lineTo(size.width, cuttingHeight); + path.lineTo(size.width - cuttingHeight, 0); + path.lineTo(0, 0); + + canvas.drawShadow(path, Colors.black45, 10.0, true); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} + +class BoxShadowPainterTopLeftBottomRight extends CustomPainter { + final double cuttingHeight; + + BoxShadowPainterTopLeftBottomRight({required this.cuttingHeight}); + + @override + void paint(Canvas canvas, Size size) { + final path = Path(); + path.moveTo(0, size.height); + path.lineTo(0, cuttingHeight); + path.lineTo(cuttingHeight, 0); + path.lineTo(size.width, 0); + path.lineTo(size.width, size.height - cuttingHeight); + path.lineTo(size.width - cuttingHeight, size.height); + path.lineTo(0, size.height); + + canvas.drawShadow(path, Colors.black45, 10.0, true); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} From 193577ce7b4613289233d73138d5d84373122a0d Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 12:13:52 +0500 Subject: [PATCH 014/161] feat: home screen --- evently/lib/main.dart | 1 + evently/lib/screens/event_hub/event_hub_screen.dart | 8 +++++--- evently/lib/utils/route_util.dart | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 6559377d1c..2f5c8d71a6 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -60,6 +60,7 @@ class MyApp extends StatelessWidget { routes: { '/': (context) => const SplashScreen(), RouteUtil.kRouteEventHub: (context) => const EventHubScreen(), + RouteUtil.createEvent: (context) => const Placeholder(), }, ), ); diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index f0c395832b..f482f7b09a 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -3,8 +3,8 @@ import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/main.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/widgets/clipped_button.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -62,7 +62,7 @@ class _EventHubScreenState extends State { mainAxisAlignment: MainAxisAlignment.end, children: [ InkWell( - onTap: () => {}, + onTap: () => Navigator.of(context).pushNamed(RouteUtil.createEvent), child: DecoratedBox( decoration: BoxDecoration( color: EventlyAppTheme.kpurpleDark, @@ -144,7 +144,9 @@ class _EventHubScreenState extends State { title: LocaleKeys.create_event.tr(), bgColor: EventlyAppTheme.kBlue, textColor: EventlyAppTheme.kWhite, - onPressed: () {}, + onPressed: () { + Navigator.of(context).pushNamed(RouteUtil.createEvent); + }, cuttingHeight: 15.h, clipperType: ClipperType.bottomLeftTopRight, isShadow: false, diff --git a/evently/lib/utils/route_util.dart b/evently/lib/utils/route_util.dart index c8ce8f45b3..03058cf14b 100644 --- a/evently/lib/utils/route_util.dart +++ b/evently/lib/utils/route_util.dart @@ -2,4 +2,5 @@ class RouteUtil { RouteUtil(); static String kRouteEventHub = "/eventHub"; + static String createEvent = "/createEvent"; } From 39b7352483ea37155e91711641f41911ac4a0e53 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 12:17:14 +0500 Subject: [PATCH 015/161] feat: create event --- evently/lib/main.dart | 3 ++- evently/lib/screens/create_event.dart | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 evently/lib/screens/create_event.dart diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 2f5c8d71a6..588f540d72 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:ui'; import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/screens/create_event.dart'; import 'package:evently/screens/event_hub/event_hub_screen.dart'; import 'package:evently/screens/splash_screen.dart'; import 'package:evently/utils/constants.dart'; @@ -60,7 +61,7 @@ class MyApp extends StatelessWidget { routes: { '/': (context) => const SplashScreen(), RouteUtil.kRouteEventHub: (context) => const EventHubScreen(), - RouteUtil.createEvent: (context) => const Placeholder(), + RouteUtil.createEvent: (context) => const CreateEvent(), }, ), ); diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart new file mode 100644 index 0000000000..c0c4516f9a --- /dev/null +++ b/evently/lib/screens/create_event.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class CreateEvent extends StatefulWidget { + const CreateEvent({super.key}); + + @override + State createState() => _CreateEventState(); +} + +class _CreateEventState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} From 92c7c4079d19e0a1e8f50487677ab3c5ab94e65c Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 12:38:42 +0500 Subject: [PATCH 016/161] feat: GetIt via injectable and build runner --- evently/lib/main.dart | 4 + evently/lib/utils/di/di.config.dart | 30 ++ evently/lib/utils/di/di.dart | 13 + evently/lib/utils/di/register_modules.dart | 12 + .../viewmodels/create_event_viewmodel.dart | 12 + evently/pubspec.lock | 320 ++++++++++++++++++ evently/pubspec.yaml | 5 + 7 files changed, 396 insertions(+) create mode 100644 evently/lib/utils/di/di.config.dart create mode 100644 evently/lib/utils/di/di.dart create mode 100644 evently/lib/utils/di/register_modules.dart create mode 100644 evently/lib/viewmodels/create_event_viewmodel.dart diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 588f540d72..f9ec0db562 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -4,17 +4,21 @@ import 'package:evently/screens/create_event.dart'; import 'package:evently/screens/event_hub/event_hub_screen.dart'; import 'package:evently/screens/splash_screen.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/route_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; + bool isTablet = false; void main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); + configureDependencies(); + isTablet = _getIsCurrentDeviceTablet(); runApp( diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart new file mode 100644 index 0000000000..af886c649e --- /dev/null +++ b/evently/lib/utils/di/di.config.dart @@ -0,0 +1,30 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// InjectableConfigGenerator +// ************************************************************************** + +// ignore_for_file: type=lint +// coverage:ignore-file + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; +import 'package:get_it/get_it.dart' as _i1; +import 'package:injectable/injectable.dart' as _i2; + +extension GetItInjectableX on _i1.GetIt { +// initializes the registration of main-scope dependencies inside of GetIt + _i1.GetIt init({ + String? environment, + _i2.EnvironmentFilter? environmentFilter, + }) { + final gh = _i2.GetItHelper( + this, + environment, + environmentFilter, + ); + gh.lazySingleton<_i3.CreateEventViewModel>( + () => _i3.CreateEventViewModel()); + return this; + } +} diff --git a/evently/lib/utils/di/di.dart b/evently/lib/utils/di/di.dart new file mode 100644 index 0000000000..ecf2d0d691 --- /dev/null +++ b/evently/lib/utils/di/di.dart @@ -0,0 +1,13 @@ +// coverage: false +// coverage:ignore-file + +import 'package:evently/utils/di/di.config.dart'; +import 'package:get_it/get_it.dart'; +import 'package:injectable/injectable.dart'; + +// Service Locator for project +final sl = GetIt.I; + +// initialization of Service locator +@InjectableInit() +void configureDependencies() => sl.init(); diff --git a/evently/lib/utils/di/register_modules.dart b/evently/lib/utils/di/register_modules.dart new file mode 100644 index 0000000000..4782ae37f2 --- /dev/null +++ b/evently/lib/utils/di/register_modules.dart @@ -0,0 +1,12 @@ +// coverage: false +// coverage:ignore-file +// Injectable is a convenient code generator for get_it. +// All you have to do now is annotate your injectable classes with @injectable and let the generator do the work. +// This class is use to generate code to register objects on app start +import 'package:injectable/injectable.dart'; + + +@module +abstract class RegisterModule { + +} diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart new file mode 100644 index 0000000000..89eaae418e --- /dev/null +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -0,0 +1,12 @@ + + + +import 'package:flutter/material.dart'; +import 'package:injectable/injectable.dart'; + + +@lazySingleton +class CreateEventViewModel extends ChangeNotifier { + + +} \ No newline at end of file diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 419f03753a..af6ab5919f 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" + source: hosted + version: "67.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" + source: hosted + version: "6.4.1" args: dependency: transitive description: @@ -25,6 +41,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + url: "https://pub.dev" + source: hosted + version: "4.0.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct main" + description: + name: build_runner + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + url: "https://pub.dev" + source: hosted + version: "2.4.9" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + url: "https://pub.dev" + source: hosted + version: "7.3.0" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" + source: hosted + version: "8.9.2" characters: dependency: transitive description: @@ -33,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" clock: dependency: transitive description: @@ -41,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" + source: hosted + version: "4.10.0" collection: dependency: transitive description: @@ -49,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" crypto: dependency: transitive description: @@ -65,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + url: "https://pub.dev" + source: hosted + version: "2.3.6" easy_localization: dependency: "direct main" description: @@ -105,6 +217,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -149,6 +269,30 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + url: "https://pub.dev" + source: hosted + version: "7.7.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" google_fonts: dependency: "direct main" description: @@ -157,6 +301,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.2.1" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" http: dependency: transitive description: @@ -165,6 +317,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: @@ -173,6 +333,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + injectable: + dependency: "direct main" + description: + name: injectable + sha256: "3d98967224a5fdd4094a61bf53ed9616c3fbcf3e090bf83e7cb7d436d0c20041" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + injectable_generator: + dependency: "direct main" + description: + name: injectable_generator + sha256: "2ca3ada337eac0ef6b82f8049c970ddb63947738fdf32ac6cbef8d1567d7ba05" + url: "https://pub.dev" + source: hosted + version: "2.6.1" intl: dependency: transitive description: @@ -181,6 +357,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -213,6 +413,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -237,6 +445,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -325,6 +549,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" shared_preferences: dependency: transitive description: @@ -381,11 +637,35 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" source_span: dependency: transitive description: @@ -410,6 +690,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -434,6 +722,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" typed_data: dependency: transitive description: @@ -482,6 +778,14 @@ packages: url: "https://pub.dev" source: hosted version: "13.0.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" web: dependency: transitive description: @@ -490,6 +794,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + url: "https://pub.dev" + source: hosted + version: "2.4.5" win32: dependency: transitive description: @@ -514,6 +826,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" sdks: dart: ">=3.3.4 <4.0.0" flutter: ">=3.19.2" diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index 88554b7604..b1eb5db376 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -39,6 +39,10 @@ dependencies: flutter_screenutil: ^5.9.0 google_fonts: ^6.2.1 flutter_svg: ^2.0.10+1 + get_it: ^7.7.0 + injectable_generator: ^2.6.1 + build_runner: ^2.4.9 + injectable: ^2.4.1 dev_dependencies: flutter_test: @@ -51,6 +55,7 @@ dev_dependencies: # rules and activating additional ones. flutter_lints: ^3.0.0 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From c464c09702afb20198e5b010b851d2697ceb3f33 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 12:45:16 +0500 Subject: [PATCH 017/161] feat: translation for create event screen --- evently/i18n/de.json | 6 +++- evently/i18n/en-US.json | 6 +++- evently/i18n/es.json | 6 +++- evently/i18n/ru-RU.json | 6 +++- evently/lib/utils/constants.dart | 2 ++ .../viewmodels/create_event_viewmodel.dart | 35 ++++++++++++++++--- 6 files changed, 52 insertions(+), 9 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 8050886a69..4fd5ed6e56 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -5,5 +5,9 @@ "for_sale" : "For Sale", "history": "History", "no_nft_created": "There is no NFTs created.", - "create_event": "Create an event!" + "create_event": "Create an event!", + "overview": "Overview", + "detail": "Detail", + "perks": "Perks", + "price": "Price" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 8050886a69..4fd5ed6e56 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -5,5 +5,9 @@ "for_sale" : "For Sale", "history": "History", "no_nft_created": "There is no NFTs created.", - "create_event": "Create an event!" + "create_event": "Create an event!", + "overview": "Overview", + "detail": "Detail", + "perks": "Perks", + "price": "Price" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 8050886a69..4fd5ed6e56 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -5,5 +5,9 @@ "for_sale" : "For Sale", "history": "History", "no_nft_created": "There is no NFTs created.", - "create_event": "Create an event!" + "create_event": "Create an event!", + "overview": "Overview", + "detail": "Detail", + "perks": "Perks", + "price": "Price" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 8050886a69..4fd5ed6e56 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -5,5 +5,9 @@ "for_sale" : "For Sale", "history": "History", "no_nft_created": "There is no NFTs created.", - "create_event": "Create an event!" + "create_event": "Create an event!", + "overview": "Overview", + "detail": "Detail", + "perks": "Perks", + "price": "Price" } \ No newline at end of file diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 60501e0e1c..3244672e3b 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -1,6 +1,8 @@ const double tabletMinWidth = 600; const int kSplashScreenDuration = 3; const String kUniversalFontFamily = "UniversalSans"; +const int kPageAnimationTimeInMillis = 300; +const kDraft = "Draft"; /// ```SVG assets diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart index 89eaae418e..ca8c5c7c24 100644 --- a/evently/lib/viewmodels/create_event_viewmodel.dart +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -1,12 +1,37 @@ - - - +import 'package:evently/utils/constants.dart'; import 'package:flutter/material.dart'; import 'package:injectable/injectable.dart'; - @lazySingleton class CreateEventViewModel extends ChangeNotifier { + CreateEventViewModel(); + + late ValueNotifier currentPage; + late ValueNotifier currentStep; + late PageController pageController; + + String? from; + final List pageTitles = ['LocaleKeys.upload.tr()', ' LocaleKeys.nft_detail_text.tr()', ' LocaleKeys.nft_pricing.tr()', '']; + + void init({required VoidCallback setTextField}) { + currentPage = ValueNotifier(0); + currentStep = ValueNotifier(0); + pageController = PageController(); + } + + Future nextPage() async { + await pageController.nextPage(duration: const Duration(milliseconds: kPageAnimationTimeInMillis), curve: Curves.easeIn); + notifyListeners(); + } + + Future previousPage() async { + from = kDraft; + await pageController.previousPage(duration: const Duration(milliseconds: kPageAnimationTimeInMillis), curve: Curves.easeIn); + notifyListeners(); + } -} \ No newline at end of file + void disposeControllers() { + pageController.dispose(); + } +} From ce99eacd42f6be6d754abd0de8326d5b9fc5b9eb Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 14:01:35 +0500 Subject: [PATCH 018/161] feat: create event view model --- evently/lib/generated/locale_keys.g.dart | 4 ++++ evently/lib/viewmodels/create_event_viewmodel.dart | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 078c26fb99..942681b16c 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -8,5 +8,9 @@ abstract class LocaleKeys { static const history = 'history'; static const no_nft_created = 'no_nft_created'; static const create_event = 'create_event'; + static const overview = 'overview'; + static const detail = 'detail'; + static const perks = 'perks'; + static const price = 'price'; } diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart index ca8c5c7c24..77055543d1 100644 --- a/evently/lib/viewmodels/create_event_viewmodel.dart +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -1,3 +1,5 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/utils/constants.dart'; import 'package:flutter/material.dart'; import 'package:injectable/injectable.dart'; @@ -11,7 +13,12 @@ class CreateEventViewModel extends ChangeNotifier { late PageController pageController; String? from; - final List pageTitles = ['LocaleKeys.upload.tr()', ' LocaleKeys.nft_detail_text.tr()', ' LocaleKeys.nft_pricing.tr()', '']; + final List pageTitles = [ + LocaleKeys.overview.tr(), + LocaleKeys.detail.tr(), + LocaleKeys.perks.tr(), + LocaleKeys.price.tr(), + ]; void init({required VoidCallback setTextField}) { currentPage = ValueNotifier(0); From 15dd7686a15807bae3e53c2a9d14eadb05ced688 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 14:14:35 +0500 Subject: [PATCH 019/161] feat: page controller --- evently/lib/screens/create_event.dart | 66 ++++++++++++++++++- .../viewmodels/create_event_viewmodel.dart | 2 +- evently/pubspec.lock | 16 +++++ evently/pubspec.yaml | 1 + 4 files changed, 83 insertions(+), 2 deletions(-) diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index c0c4516f9a..e9cd7bcf85 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,4 +1,8 @@ +import 'package:evently/utils/di/di.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class CreateEvent extends StatefulWidget { const CreateEvent({super.key}); @@ -8,8 +12,68 @@ class CreateEvent extends StatefulWidget { } class _CreateEventState extends State { + CreateEventViewModel createEventViewModel = sl(); + + @override + void initState() { + createEventViewModel.init(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: EventlyAppTheme.kWhite, + child: SafeArea( + bottom: false, + child: Scaffold( + body: ChangeNotifierProvider.value(value: createEventViewModel, child: const CreateEventContent()), + ), + ), + ); + } +} + +class CreateEventContent extends StatelessWidget { + const CreateEventContent({super.key}); + @override Widget build(BuildContext context) { - return const Placeholder(); + final createEventViewModel = context.watch(); + return PageView.builder( + controller: createEventViewModel.pageController, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (int page) { + createEventViewModel.currentPage.value = page; + final map = {0: 0, 1: 1, 2: 2, 3: 2}; + createEventViewModel.currentStep.value = map[page]!; + }, + itemBuilder: (BuildContext context, int index) { + final map = { + 0: overview, + 1: details, + 2: perks, + 3: price, + }; + + return map[index]?.call() ?? const SizedBox(); + }, + ); + } + + Widget overview() { + return const Text("overview"); + } + + Widget details() { + return const Text("details"); + } + + Widget perks() { + return const Text("perks"); + } + + Widget price() { + return const Text("price"); } } diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart index 77055543d1..85a277e351 100644 --- a/evently/lib/viewmodels/create_event_viewmodel.dart +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -20,7 +20,7 @@ class CreateEventViewModel extends ChangeNotifier { LocaleKeys.price.tr(), ]; - void init({required VoidCallback setTextField}) { + void init() { currentPage = ValueNotifier(0); currentStep = ValueNotifier(0); pageController = PageController(); diff --git a/evently/pubspec.lock b/evently/pubspec.lock index af6ab5919f..add78ea306 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -453,6 +453,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_config: dependency: transitive description: @@ -557,6 +565,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" pub_semver: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index b1eb5db376..e8d7f24d1e 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: injectable_generator: ^2.6.1 build_runner: ^2.4.9 injectable: ^2.4.1 + provider: ^6.1.2 dev_dependencies: flutter_test: From 6febf7055a0a11fd0ed3d5e213ef793181cc2329 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 14:27:34 +0500 Subject: [PATCH 020/161] feat: over view screen and step indicator --- evently/lib/screens/create_event.dart | 3 ++- evently/lib/screens/overview_screen.dart | 28 ++++++++++++++++++++++++ evently/lib/utils/space_utils.dart | 24 ++++++++++++++++++++ evently/pubspec.lock | 8 +++++++ evently/pubspec.yaml | 1 + 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 evently/lib/screens/overview_screen.dart create mode 100644 evently/lib/utils/space_utils.dart diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index e9cd7bcf85..71d4af69a7 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,3 +1,4 @@ +import 'package:evently/screens/overview_screen.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; @@ -62,7 +63,7 @@ class CreateEventContent extends StatelessWidget { } Widget overview() { - return const Text("overview"); + return const OverViewScreen(); } Widget details() { diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart new file mode 100644 index 0000000000..4f25195199 --- /dev/null +++ b/evently/lib/screens/overview_screen.dart @@ -0,0 +1,28 @@ +import 'package:evently/viewmodels/create_event_viewmodel.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class OverViewScreen extends StatefulWidget { + const OverViewScreen({Key? key}) : super(key: key); + + @override + State createState() => _OverViewScreenState(); +} + +class _OverViewScreenState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final homeViewModel = context.watch(); + + return Scaffold( + body: Column( + children: [], + ), + ); + } +} diff --git a/evently/lib/utils/space_utils.dart b/evently/lib/utils/space_utils.dart new file mode 100644 index 0000000000..ac39b726f4 --- /dev/null +++ b/evently/lib/utils/space_utils.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class VerticalSpace extends StatelessWidget { + final double height; + + const VerticalSpace(this.height, {super.key}); + + @override + Widget build(BuildContext context) => SizedBox( + height: height.h, + ); +} + +class HorizontalSpace extends StatelessWidget { + final double width; + + const HorizontalSpace(this.width, {super.key}); + + @override + Widget build(BuildContext context) => SizedBox( + width: width, + ); +} diff --git a/evently/pubspec.lock b/evently/pubspec.lock index add78ea306..796764cdd7 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -698,6 +698,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + steps_indicator: + dependency: "direct main" + description: + name: steps_indicator + sha256: a6e55fae97f29db3df2b8bed77d55ead127f16ffa024e0fe0234b5d71eddffa1 + url: "https://pub.dev" + source: hosted + version: "1.3.0" stream_channel: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index e8d7f24d1e..d7decb6b93 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: build_runner: ^2.4.9 injectable: ^2.4.1 provider: ^6.1.2 + steps_indicator: ^1.3.0 dev_dependencies: flutter_test: From 4ef1bcc6a23a30b465c1eda522646c47e1c0e93a Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 15:17:44 +0500 Subject: [PATCH 021/161] feat: dotted border --- evently/assets/images/svg/upload.svg | 3 + .../assets/images/text_field_multi_line.png | Bin 0 -> 649 bytes .../assets/images/text_field_single_line.png | Bin 0 -> 358 bytes .../screens/custom_widgets/step_labels.dart | 50 +++++++ .../custom_widgets/steps_indicator.dart | 40 ++++++ evently/lib/widgets/easel_text_field.dart | 127 ++++++++++++++++++ 6 files changed, 220 insertions(+) create mode 100644 evently/assets/images/svg/upload.svg create mode 100644 evently/assets/images/text_field_multi_line.png create mode 100644 evently/assets/images/text_field_single_line.png create mode 100644 evently/lib/screens/custom_widgets/step_labels.dart create mode 100644 evently/lib/screens/custom_widgets/steps_indicator.dart create mode 100644 evently/lib/widgets/easel_text_field.dart diff --git a/evently/assets/images/svg/upload.svg b/evently/assets/images/svg/upload.svg new file mode 100644 index 0000000000..a8383b830b --- /dev/null +++ b/evently/assets/images/svg/upload.svg @@ -0,0 +1,3 @@ + + + diff --git a/evently/assets/images/text_field_multi_line.png b/evently/assets/images/text_field_multi_line.png new file mode 100644 index 0000000000000000000000000000000000000000..5a1fc8bdc3bb4a9f6a5179215aa00d0c931bf87a GIT binary patch literal 649 zcmeAS@N?(olHy`uVBq!ia0y~yU`z(G>p0keVM%xNb!1@J*w6hZk(GggNzT*7F{Fa=?Uj?BEQ}%y7cU-AGY!7% z+Pb-N_sxp$>N zl>;&U>cv7h@-A}f&p+tbA{q=ND7mEF9D97I?H7Z_}J%NE$7 zOtv|qdkmP)$)ag$qjvWy*q#| zP`~dMb4Fr8bG))%8qb9r`vt{~*)lTDc63WJn`|lrsh7H8^eE{V1N&_&Fm=lqOx?78 b^oc>qz<>G_cV=KHFfe$!`njxgN@xNAAzX4v literal 0 HcmV?d00001 diff --git a/evently/lib/screens/custom_widgets/step_labels.dart b/evently/lib/screens/custom_widgets/step_labels.dart new file mode 100644 index 0000000000..a66cf071b5 --- /dev/null +++ b/evently/lib/screens/custom_widgets/step_labels.dart @@ -0,0 +1,50 @@ +import 'package:easel_flutter/utils/constants.dart'; +import 'package:easel_flutter/utils/easel_app_theme.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + + +class StepLabels extends StatelessWidget { + final ValueNotifier currentStep; + final ValueNotifier currentPage; + + const StepLabels({ + Key? key, + required this.currentPage, + required this.currentStep, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 0.1.sw), + child: Row( + children: List.generate(stepLabels.length, (index) { + return SizedBox( + width: 0.8.sw / stepLabels.length, + child: ValueListenableBuilder( + valueListenable: currentPage, + builder: (_, int currentPage, __) => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + stepLabels[index].tr(), + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontSize: 8.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + color: currentStep.value >= index + ? EaselAppTheme.kBlue + : EaselAppTheme.kGrey, + ), + ), + ], + ), + ), + ); + }), + ), + ); + } +} diff --git a/evently/lib/screens/custom_widgets/steps_indicator.dart b/evently/lib/screens/custom_widgets/steps_indicator.dart new file mode 100644 index 0000000000..74dab820d7 --- /dev/null +++ b/evently/lib/screens/custom_widgets/steps_indicator.dart @@ -0,0 +1,40 @@ +import 'package:easel_flutter/utils/easel_app_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:steps_indicator/steps_indicator.dart'; + +class MyStepsIndicator extends StatelessWidget { + final ValueNotifier currentStep; + + const MyStepsIndicator({ + required this.currentStep, + }); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: currentStep, + builder: (_, int value, __) { + return StepsIndicator( + selectedStep: currentStep.value, + nbSteps: 3, + lineLength: 0.68.sw / 3, + doneLineColor: EaselAppTheme.kBlue, + undoneLineColor: EaselAppTheme.kLightGrey, + doneLineThickness: 1.5, + undoneLineThickness: 1.5, + unselectedStepColorIn: EaselAppTheme.kLightGrey, + unselectedStepColorOut: EaselAppTheme.kLightGrey, + doneStepColor: EaselAppTheme.kBlue, + selectedStepColorIn: EaselAppTheme.kBlue, + selectedStepColorOut: EaselAppTheme.kBlue, + enableLineAnimation: true, + enableStepAnimation: true, + lineLengthCustomStep: const [], + doneStepWidget: Container(width: 10.w, height: 10.h, decoration: const BoxDecoration(color: EaselAppTheme.kBlue)), + unselectedStepWidget: Container(width: 10.w, height: 10.h, decoration: const BoxDecoration(color: EaselAppTheme.kLightGrey)), + selectedStepWidget: Container(width: 15.w, height: 15.h, decoration: const BoxDecoration(color: EaselAppTheme.kBlue)), + ); + }); + } +} diff --git a/evently/lib/widgets/easel_text_field.dart b/evently/lib/widgets/easel_text_field.dart new file mode 100644 index 0000000000..14c36fe396 --- /dev/null +++ b/evently/lib/widgets/easel_text_field.dart @@ -0,0 +1,127 @@ +import 'package:easel_flutter/utils/constants.dart'; +import 'package:easel_flutter/utils/easel_app_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +import '../utils/screen_responsive.dart'; + +class EaselTextField extends StatelessWidget { + const EaselTextField( + {Key? key, + required this.label, + this.hint = "", + this.controller, + this.validator, + this.noOfLines = 1, // default to single line + this.inputFormatters = const [], + this.keyboardType = TextInputType.text, + this.textCapitalization = TextCapitalization.none}) + : super(key: key); + + final String label; + final String hint; + final TextEditingController? controller; + final String? Function(String?)? validator; + final int noOfLines; + final TextInputType keyboardType; + final List inputFormatters; + final TextCapitalization textCapitalization; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + textAlign: TextAlign.start, + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), + ), + SizedBox(height: 4.h), + Stack( + children: [ + ScreenResponsive( + mobileScreen: (context) => Image.asset( + noOfLines == 1 ? PngUtils.kTextFieldSingleLine : PngUtils.kTextFieldMultiLine, + height: noOfLines == 1 ? 40.h : 120.h, + width: 1.sw, + fit: BoxFit.fill, + ), + tabletScreen: (context) => Image.asset( + noOfLines == 1 ? PngUtils.kTextFieldSingleLine : PngUtils.kTextFieldMultiLine, + height: noOfLines == 1 ? 32.h : 110.h, + width: 1.sw, + fit: BoxFit.fill, + ), + ), + ScreenResponsive( + mobileScreen: (_) => buildMobileTextField(), + tabletScreen: (_) => buildTabletTextField()), + ], + ), + ], + ); + } + + SizedBox buildMobileTextField() { + return SizedBox( + height: noOfLines == 1 ? 40.h : 120.h, + child: Align( + child: TextFormField( + style: TextStyle( + fontSize: noOfLines == 1 ? 18.sp : 15.sp, + fontWeight: FontWeight.w400, + color: EaselAppTheme.kDarkText), + controller: controller, + validator: validator, + minLines: noOfLines, + maxLines: noOfLines, + keyboardType: keyboardType, + textCapitalization: textCapitalization, + inputFormatters: inputFormatters, + decoration: InputDecoration( + hintText: hint, + hintStyle: TextStyle( + fontSize: 15.sp, + fontWeight: FontWeight.w400, + color: EaselAppTheme.kGrey), + border: const OutlineInputBorder(borderSide: BorderSide.none), + floatingLabelBehavior: FloatingLabelBehavior.always, + contentPadding: EdgeInsets.fromLTRB(10.w, 0.h, 10.w, 0.h), + ), + ), + ), + ); + } + + SizedBox buildTabletTextField() { + return SizedBox( + height: noOfLines == 1 ? 32.h : 110.h, + child: Align( + child: TextFormField( + style: TextStyle( + fontSize: noOfLines == 1 ? 16.sp : 14.sp, + fontWeight: FontWeight.w400, + color: EaselAppTheme.kDarkText), + controller: controller, + validator: validator, + minLines: noOfLines, + maxLines: noOfLines, + keyboardType: keyboardType, + textCapitalization: textCapitalization, + inputFormatters: inputFormatters, + decoration: InputDecoration( + hintText: hint, + hintStyle: TextStyle( + fontSize: noOfLines == 1 ? 16.sp : 14.sp, + color: EaselAppTheme.kGrey), + border: const OutlineInputBorder(borderSide: BorderSide.none), + floatingLabelBehavior: FloatingLabelBehavior.always, + contentPadding: EdgeInsets.fromLTRB(10.w, 0.h, 10.w, 0.h), + ), + ), + ), + ); + } +} From d9225978247c34c875bc68a9c3696134e6c8305a Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 15:18:20 +0500 Subject: [PATCH 022/161] feat: dotted border --- evently/i18n/de.json | 11 +- evently/i18n/en-US.json | 11 +- evently/i18n/es.json | 11 +- evently/i18n/ru-RU.json | 11 +- evently/lib/generated/locale_keys.g.dart | 7 + .../screens/custom_widgets/step_labels.dart | 13 +- .../custom_widgets/steps_indicator.dart | 27 +-- evently/lib/screens/overview_screen.dart | 161 +++++++++++++++++- evently/lib/utils/constants.dart | 8 + evently/lib/widgets/easel_text_field.dart | 39 ++--- evently/pubspec.lock | 16 ++ evently/pubspec.yaml | 2 + 12 files changed, 262 insertions(+), 55 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 4fd5ed6e56..9d24282a4c 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -9,5 +9,14 @@ "overview": "Overview", "detail": "Detail", "perks": "Perks", - "price": "Price" + "price": "Price", + "event_name" : "Event Name", + "what_your_event_called": "What is your event called?", + "host_name": "Host Name", + "who_hosting_it": "Who is hosting it?", + "thumbnail" : "Thumbnail", + "tap_select": "Tap to select ", + "mb_limit": "200MB Limit", + "continue": "Continue", + "save_draft": "Save as draft" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 4fd5ed6e56..9d24282a4c 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -9,5 +9,14 @@ "overview": "Overview", "detail": "Detail", "perks": "Perks", - "price": "Price" + "price": "Price", + "event_name" : "Event Name", + "what_your_event_called": "What is your event called?", + "host_name": "Host Name", + "who_hosting_it": "Who is hosting it?", + "thumbnail" : "Thumbnail", + "tap_select": "Tap to select ", + "mb_limit": "200MB Limit", + "continue": "Continue", + "save_draft": "Save as draft" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 4fd5ed6e56..9d24282a4c 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -9,5 +9,14 @@ "overview": "Overview", "detail": "Detail", "perks": "Perks", - "price": "Price" + "price": "Price", + "event_name" : "Event Name", + "what_your_event_called": "What is your event called?", + "host_name": "Host Name", + "who_hosting_it": "Who is hosting it?", + "thumbnail" : "Thumbnail", + "tap_select": "Tap to select ", + "mb_limit": "200MB Limit", + "continue": "Continue", + "save_draft": "Save as draft" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 4fd5ed6e56..9d24282a4c 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -9,5 +9,14 @@ "overview": "Overview", "detail": "Detail", "perks": "Perks", - "price": "Price" + "price": "Price", + "event_name" : "Event Name", + "what_your_event_called": "What is your event called?", + "host_name": "Host Name", + "who_hosting_it": "Who is hosting it?", + "thumbnail" : "Thumbnail", + "tap_select": "Tap to select ", + "mb_limit": "200MB Limit", + "continue": "Continue", + "save_draft": "Save as draft" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 942681b16c..d6554a6536 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -12,5 +12,12 @@ abstract class LocaleKeys { static const detail = 'detail'; static const perks = 'perks'; static const price = 'price'; + static const event_name = 'event_name'; + static const what_your_event_called = 'what_your_event_called'; + static const host_name = 'host_name'; + static const who_hosting_it = 'who_hosting_it'; + static const thumbnail = 'thumbnail'; + static const tap_select = 'tap_select'; + static const mb_limit = 'mb_limit'; } diff --git a/evently/lib/screens/custom_widgets/step_labels.dart b/evently/lib/screens/custom_widgets/step_labels.dart index a66cf071b5..af388a4b75 100644 --- a/evently/lib/screens/custom_widgets/step_labels.dart +++ b/evently/lib/screens/custom_widgets/step_labels.dart @@ -1,19 +1,18 @@ -import 'package:easel_flutter/utils/constants.dart'; -import 'package:easel_flutter/utils/easel_app_theme.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/evently_app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; - class StepLabels extends StatelessWidget { final ValueNotifier currentStep; final ValueNotifier currentPage; const StepLabels({ - Key? key, + super.key, required this.currentPage, required this.currentStep, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -34,9 +33,7 @@ class StepLabels extends StatelessWidget { fontSize: 8.sp, fontFamily: 'Inter', fontWeight: FontWeight.w700, - color: currentStep.value >= index - ? EaselAppTheme.kBlue - : EaselAppTheme.kGrey, + color: currentStep.value >= index ? EventlyAppTheme.kBlue : EventlyAppTheme.kGrey, ), ), ], diff --git a/evently/lib/screens/custom_widgets/steps_indicator.dart b/evently/lib/screens/custom_widgets/steps_indicator.dart index 74dab820d7..dd371d675d 100644 --- a/evently/lib/screens/custom_widgets/steps_indicator.dart +++ b/evently/lib/screens/custom_widgets/steps_indicator.dart @@ -1,4 +1,4 @@ -import 'package:easel_flutter/utils/easel_app_theme.dart'; +import 'package:evently/utils/evently_app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:steps_indicator/steps_indicator.dart'; @@ -7,6 +7,7 @@ class MyStepsIndicator extends StatelessWidget { final ValueNotifier currentStep; const MyStepsIndicator({ + super.key, required this.currentStep, }); @@ -17,23 +18,23 @@ class MyStepsIndicator extends StatelessWidget { builder: (_, int value, __) { return StepsIndicator( selectedStep: currentStep.value, - nbSteps: 3, - lineLength: 0.68.sw / 3, - doneLineColor: EaselAppTheme.kBlue, - undoneLineColor: EaselAppTheme.kLightGrey, + nbSteps: 4, + lineLength: 0.68.sw / 4, + doneLineColor: EventlyAppTheme.kBlue, + undoneLineColor: EventlyAppTheme.kLightGrey, doneLineThickness: 1.5, undoneLineThickness: 1.5, - unselectedStepColorIn: EaselAppTheme.kLightGrey, - unselectedStepColorOut: EaselAppTheme.kLightGrey, - doneStepColor: EaselAppTheme.kBlue, - selectedStepColorIn: EaselAppTheme.kBlue, - selectedStepColorOut: EaselAppTheme.kBlue, + unselectedStepColorIn: EventlyAppTheme.kLightGrey, + unselectedStepColorOut: EventlyAppTheme.kLightGrey, + doneStepColor: EventlyAppTheme.kBlue, + selectedStepColorIn: EventlyAppTheme.kBlue, + selectedStepColorOut: EventlyAppTheme.kBlue, enableLineAnimation: true, enableStepAnimation: true, lineLengthCustomStep: const [], - doneStepWidget: Container(width: 10.w, height: 10.h, decoration: const BoxDecoration(color: EaselAppTheme.kBlue)), - unselectedStepWidget: Container(width: 10.w, height: 10.h, decoration: const BoxDecoration(color: EaselAppTheme.kLightGrey)), - selectedStepWidget: Container(width: 15.w, height: 15.h, decoration: const BoxDecoration(color: EaselAppTheme.kBlue)), + doneStepWidget: Container(width: 10.w, height: 10.h, decoration: const BoxDecoration(color: EventlyAppTheme.kBlue)), + unselectedStepWidget: Container(width: 10.w, height: 10.h, decoration: const BoxDecoration(color: EventlyAppTheme.kLightGrey)), + selectedStepWidget: Container(width: 15.w, height: 15.h, decoration: const BoxDecoration(color: EventlyAppTheme.kBlue)), ); }); } diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 4f25195199..1ae23e3179 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -1,5 +1,16 @@ +import 'package:dotted_border/dotted_border.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/screens/custom_widgets/step_labels.dart'; +import 'package:evently/screens/custom_widgets/steps_indicator.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; +import 'package:evently/widgets/easel_text_field.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; class OverViewScreen extends StatefulWidget { @@ -10,6 +21,10 @@ class OverViewScreen extends StatefulWidget { } class _OverViewScreenState extends State { + final _formKey = GlobalKey(); + final ValueNotifier _eventNameFieldError = ValueNotifier(""); + final ValueNotifier _hostNameFieldError = ValueNotifier(""); + @override void initState() { super.initState(); @@ -17,11 +32,151 @@ class _OverViewScreenState extends State { @override Widget build(BuildContext context) { - final homeViewModel = context.watch(); + final createEventViewModel = context.watch(); return Scaffold( - body: Column( - children: [], + body: SingleChildScrollView( + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: createEventViewModel.currentStep), + const VerticalSpace(5), + StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), + const VerticalSpace(10), + const VerticalSpace(20), + Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: ValueListenableBuilder( + valueListenable: createEventViewModel.currentPage, + builder: (_, int currentPage, __) => Padding( + padding: EdgeInsets.only(left: 10.sp), + child: IconButton( + onPressed: () { + FocusScope.of(context).unfocus(); + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + Navigator.pop(context); + }, + icon: const Icon( + Icons.arrow_back_ios, + color: EventlyAppTheme.kGrey, + ), + )), + )), + ValueListenableBuilder( + valueListenable: createEventViewModel.currentPage, + builder: (_, int currentPage, __) { + return Text( + createEventViewModel.pageTitles[createEventViewModel.currentPage.value], + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 18.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kDarkText), + ); + }, + ), + ], + ), + const VerticalSpace(20), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + EventlyTextField( + label: LocaleKeys.event_name.tr(), + hint: LocaleKeys.what_your_event_called.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ValueListenableBuilder( + valueListenable: _eventNameFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + VerticalSpace(20.h), + EventlyTextField( + label: LocaleKeys.host_name.tr(), + hint: LocaleKeys.who_hosting_it.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ValueListenableBuilder( + valueListenable: _hostNameFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + VerticalSpace(20.h), + Text( + LocaleKeys.thumbnail.tr(), + textAlign: TextAlign.start, + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), + ), + VerticalSpace(20.h), + DottedBorder( + color: EventlyAppTheme.kLightPurple, + dashPattern: [8, 4], + strokeWidth: 2, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 20.w), + child: Column( + children: [ + Text( + LocaleKeys.tap_select.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), + ), + VerticalSpace(10.h), + SvgPicture.asset(SVGUtils.kSvgUpload), + VerticalSpace(10.h), + Text( + LocaleKeys.mb_limit.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), + ), + ], + ), + ), + ) + ], + ), + ), + ], + ), + ), ), ); } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 3244672e3b..b5a3daba07 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -4,8 +4,16 @@ const String kUniversalFontFamily = "UniversalSans"; const int kPageAnimationTimeInMillis = 300; const kDraft = "Draft"; +final List stepLabels = ["overview", "detail", "perks", "price"]; /// ```SVG assets class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; + static const kSvgUpload = "assets/images/svg/upload.svg"; +} + +/// ```PNG assets +class PngUtils { + static const kTextFieldSingleLine = 'assets/images/text_field_single_line.png'; + static const kTextFieldMultiLine = 'assets/images/text_field_multi_line.png'; } diff --git a/evently/lib/widgets/easel_text_field.dart b/evently/lib/widgets/easel_text_field.dart index 14c36fe396..1d855daaa2 100644 --- a/evently/lib/widgets/easel_text_field.dart +++ b/evently/lib/widgets/easel_text_field.dart @@ -1,14 +1,13 @@ -import 'package:easel_flutter/utils/constants.dart'; -import 'package:easel_flutter/utils/easel_app_theme.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/screen_responsive.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import '../utils/screen_responsive.dart'; - -class EaselTextField extends StatelessWidget { - const EaselTextField( - {Key? key, +class EventlyTextField extends StatelessWidget { + const EventlyTextField( + {super.key, required this.label, this.hint = "", this.controller, @@ -16,8 +15,7 @@ class EaselTextField extends StatelessWidget { this.noOfLines = 1, // default to single line this.inputFormatters = const [], this.keyboardType = TextInputType.text, - this.textCapitalization = TextCapitalization.none}) - : super(key: key); + this.textCapitalization = TextCapitalization.none}); final String label; final String hint; @@ -55,9 +53,7 @@ class EaselTextField extends StatelessWidget { fit: BoxFit.fill, ), ), - ScreenResponsive( - mobileScreen: (_) => buildMobileTextField(), - tabletScreen: (_) => buildTabletTextField()), + ScreenResponsive(mobileScreen: (_) => buildMobileTextField(), tabletScreen: (_) => buildTabletTextField()), ], ), ], @@ -69,10 +65,7 @@ class EaselTextField extends StatelessWidget { height: noOfLines == 1 ? 40.h : 120.h, child: Align( child: TextFormField( - style: TextStyle( - fontSize: noOfLines == 1 ? 18.sp : 15.sp, - fontWeight: FontWeight.w400, - color: EaselAppTheme.kDarkText), + style: TextStyle(fontSize: noOfLines == 1 ? 18.sp : 15.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kDarkText), controller: controller, validator: validator, minLines: noOfLines, @@ -82,10 +75,7 @@ class EaselTextField extends StatelessWidget { inputFormatters: inputFormatters, decoration: InputDecoration( hintText: hint, - hintStyle: TextStyle( - fontSize: 15.sp, - fontWeight: FontWeight.w400, - color: EaselAppTheme.kGrey), + hintStyle: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kGrey), border: const OutlineInputBorder(borderSide: BorderSide.none), floatingLabelBehavior: FloatingLabelBehavior.always, contentPadding: EdgeInsets.fromLTRB(10.w, 0.h, 10.w, 0.h), @@ -100,10 +90,7 @@ class EaselTextField extends StatelessWidget { height: noOfLines == 1 ? 32.h : 110.h, child: Align( child: TextFormField( - style: TextStyle( - fontSize: noOfLines == 1 ? 16.sp : 14.sp, - fontWeight: FontWeight.w400, - color: EaselAppTheme.kDarkText), + style: TextStyle(fontSize: noOfLines == 1 ? 16.sp : 14.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kDarkText), controller: controller, validator: validator, minLines: noOfLines, @@ -113,9 +100,7 @@ class EaselTextField extends StatelessWidget { inputFormatters: inputFormatters, decoration: InputDecoration( hintText: hint, - hintStyle: TextStyle( - fontSize: noOfLines == 1 ? 16.sp : 14.sp, - color: EaselAppTheme.kGrey), + hintStyle: TextStyle(fontSize: noOfLines == 1 ? 16.sp : 14.sp, color: EventlyAppTheme.kGrey), border: const OutlineInputBorder(borderSide: BorderSide.none), floatingLabelBehavior: FloatingLabelBehavior.always, contentPadding: EdgeInsets.fromLTRB(10.w, 0.h, 10.w, 0.h), diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 796764cdd7..9ea74c0867 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -177,6 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.6" + dotted_border: + dependency: "direct main" + description: + name: dotted_border + sha256: "108837e11848ca776c53b30bc870086f84b62ed6e01c503ed976e8f8c7df9c04" + url: "https://pub.dev" + source: hosted + version: "2.1.0" easy_localization: dependency: "direct main" description: @@ -477,6 +485,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_drawing: + dependency: transitive + description: + name: path_drawing + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 + url: "https://pub.dev" + source: hosted + version: "1.0.1" path_parsing: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index d7decb6b93..33ec5726be 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: injectable: ^2.4.1 provider: ^6.1.2 steps_indicator: ^1.3.0 + dotted_border: ^2.1.0 dev_dependencies: flutter_test: @@ -76,6 +77,7 @@ flutter: - i18n/de.json - i18n/es.json - assets/images/svg/ + - assets/images/ fonts: - family: UniversalSans From 5119d0e9e8ce8d4e2fed820c1719b32e20aa7412 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 15:36:29 +0500 Subject: [PATCH 023/161] feat: dotted border --- evently/i18n/de.json | 2 +- evently/i18n/en-US.json | 2 +- evently/i18n/es.json | 2 +- evently/i18n/ru-RU.json | 2 +- evently/lib/generated/locale_keys.g.dart | 2 ++ evently/lib/screens/overview_screen.dart | 33 +++++++++++++++++++++--- 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 9d24282a4c..026f298d20 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -17,6 +17,6 @@ "thumbnail" : "Thumbnail", "tap_select": "Tap to select ", "mb_limit": "200MB Limit", - "continue": "Continue", + "continue_key": "Continue", "save_draft": "Save as draft" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 9d24282a4c..026f298d20 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -17,6 +17,6 @@ "thumbnail" : "Thumbnail", "tap_select": "Tap to select ", "mb_limit": "200MB Limit", - "continue": "Continue", + "continue_key": "Continue", "save_draft": "Save as draft" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 9d24282a4c..026f298d20 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -17,6 +17,6 @@ "thumbnail" : "Thumbnail", "tap_select": "Tap to select ", "mb_limit": "200MB Limit", - "continue": "Continue", + "continue_key": "Continue", "save_draft": "Save as draft" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 9d24282a4c..026f298d20 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -17,6 +17,6 @@ "thumbnail" : "Thumbnail", "tap_select": "Tap to select ", "mb_limit": "200MB Limit", - "continue": "Continue", + "continue_key": "Continue", "save_draft": "Save as draft" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index d6554a6536..852e36882b 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -19,5 +19,7 @@ abstract class LocaleKeys { static const thumbnail = 'thumbnail'; static const tap_select = 'tap_select'; static const mb_limit = 'mb_limit'; + static const continue_key = 'continue_key'; + static const save_draft = 'save_draft'; } diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 1ae23e3179..6c49e4e195 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -7,14 +7,17 @@ import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; +import 'package:evently/widgets/clipped_button.dart'; import 'package:evently/widgets/easel_text_field.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; class OverViewScreen extends StatefulWidget { - const OverViewScreen({Key? key}) : super(key: key); + const OverViewScreen({super.key}); @override State createState() => _OverViewScreenState(); @@ -149,7 +152,7 @@ class _OverViewScreenState extends State { VerticalSpace(20.h), DottedBorder( color: EventlyAppTheme.kLightPurple, - dashPattern: [8, 4], + dashPattern: const [10, 2], strokeWidth: 2, child: Container( width: double.infinity, @@ -170,7 +173,31 @@ class _OverViewScreenState extends State { ], ), ), - ) + ), + VerticalSpace(20.h), + ClippedButton( + title: LocaleKeys.continue_key.tr(), + bgColor: EventlyAppTheme.kBlue, + textColor: EventlyAppTheme.kWhite, + onPressed: () { + // validateAndUpdateDescription(moveNextPage: true); + }, + cuttingHeight: 15.h, + clipperType: ClipperType.bottomLeftTopRight, + isShadow: false, + fontWeight: FontWeight.w700, + ), + VerticalSpace(10.h), + Center( + child: InkWell( + onTap: () {}, + child: Text( + LocaleKeys.save_draft.tr(), + style: TextStyle(color: EventlyAppTheme.kLightGreyText, fontSize: 14.sp, fontWeight: FontWeight.w700), + ), + ), + ), + VerticalSpace(5.h), ], ), ), From 59eb0988d735b1f620c153ea17396740d8958325 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 15:54:04 +0500 Subject: [PATCH 024/161] dotted border from design --- evently/assets/images/svg/dotted_border.svg | 3 ++ evently/lib/screens/overview_screen.dart | 56 ++++++++++++--------- evently/lib/utils/constants.dart | 1 + 3 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 evently/assets/images/svg/dotted_border.svg diff --git a/evently/assets/images/svg/dotted_border.svg b/evently/assets/images/svg/dotted_border.svg new file mode 100644 index 0000000000..00eb751c3e --- /dev/null +++ b/evently/assets/images/svg/dotted_border.svg @@ -0,0 +1,3 @@ + + + diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 6c49e4e195..025cda3844 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -150,30 +150,36 @@ class _OverViewScreenState extends State { style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), ), VerticalSpace(20.h), - DottedBorder( - color: EventlyAppTheme.kLightPurple, - dashPattern: const [10, 2], - strokeWidth: 2, - child: Container( - width: double.infinity, - padding: EdgeInsets.symmetric(vertical: 20.w), - child: Column( - children: [ - Text( - LocaleKeys.tap_select.tr(), - style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), - ), - VerticalSpace(10.h), - SvgPicture.asset(SVGUtils.kSvgUpload), - VerticalSpace(10.h), - Text( - LocaleKeys.mb_limit.tr(), - style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), - ), - ], - ), - ), - ), + // DottedBorder( + // borderType: BorderType.RRect, + // radius: const Radius.circular(10), + // // borderType: BorderType.Rect, + // color: EventlyAppTheme.kLightPurple, + // + // strokeWidth: 2, + // child: Container( + // width: double.infinity, + // padding: EdgeInsets.symmetric(vertical: 20.w), + // child: Column( + // children: [ + // Text( + // LocaleKeys.tap_select.tr(), + // style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), + // ), + // VerticalSpace(10.h), + // SvgPicture.asset(SVGUtils.kSvgUpload), + // VerticalSpace(10.h), + // Text( + // LocaleKeys.mb_limit.tr(), + // style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), + // ), + // ], + // ), + // ), + // ), + + SvgPicture.asset(SVGUtils.dottedBorder), + VerticalSpace(20.h), ClippedButton( title: LocaleKeys.continue_key.tr(), @@ -198,6 +204,8 @@ class _OverViewScreenState extends State { ), ), VerticalSpace(5.h), + + ], ), ), diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index b5a3daba07..e6a1940b00 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -10,6 +10,7 @@ final List stepLabels = ["overview", "detail", "perks", "price"]; class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; static const kSvgUpload = "assets/images/svg/upload.svg"; + static const dottedBorder = "assets/images/svg/dotted_border.svg"; } /// ```PNG assets From cb49b3e5ddb9c1742c4ffbd47c70870580b074ab Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 16:08:08 +0500 Subject: [PATCH 025/161] feat: detail screen translations --- evently/assets/images/svg/dotted_border.svg | 3 - evently/i18n/de.json | 10 +++- evently/i18n/en-US.json | 10 +++- evently/i18n/es.json | 10 +++- evently/i18n/ru-RU.json | 10 +++- evently/lib/screens/create_event.dart | 3 +- evently/lib/screens/detail_screen.dart | 15 +++++ evently/lib/screens/overview_screen.dart | 64 ++++++++++----------- evently/lib/utils/constants.dart | 2 +- 9 files changed, 83 insertions(+), 44 deletions(-) delete mode 100644 evently/assets/images/svg/dotted_border.svg create mode 100644 evently/lib/screens/detail_screen.dart diff --git a/evently/assets/images/svg/dotted_border.svg b/evently/assets/images/svg/dotted_border.svg deleted file mode 100644 index 00eb751c3e..0000000000 --- a/evently/assets/images/svg/dotted_border.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 026f298d20..300fa8f4f8 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -18,5 +18,13 @@ "tap_select": "Tap to select ", "mb_limit": "200MB Limit", "continue_key": "Continue", - "save_draft": "Save as draft" + "save_draft": "Save as draft", + "start_date": "Start date", + "end_date" : "End date", + "start_time": "Start time", + "end_time": "End time", + "location": "Location", + "search_location": "Search location", + "description": "Description", + "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 026f298d20..300fa8f4f8 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -18,5 +18,13 @@ "tap_select": "Tap to select ", "mb_limit": "200MB Limit", "continue_key": "Continue", - "save_draft": "Save as draft" + "save_draft": "Save as draft", + "start_date": "Start date", + "end_date" : "End date", + "start_time": "Start time", + "end_time": "End time", + "location": "Location", + "search_location": "Search location", + "description": "Description", + "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 026f298d20..300fa8f4f8 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -18,5 +18,13 @@ "tap_select": "Tap to select ", "mb_limit": "200MB Limit", "continue_key": "Continue", - "save_draft": "Save as draft" + "save_draft": "Save as draft", + "start_date": "Start date", + "end_date" : "End date", + "start_time": "Start time", + "end_time": "End time", + "location": "Location", + "search_location": "Search location", + "description": "Description", + "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 026f298d20..300fa8f4f8 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -18,5 +18,13 @@ "tap_select": "Tap to select ", "mb_limit": "200MB Limit", "continue_key": "Continue", - "save_draft": "Save as draft" + "save_draft": "Save as draft", + "start_date": "Start date", + "end_date" : "End date", + "start_time": "Start time", + "end_time": "End time", + "location": "Location", + "search_location": "Search location", + "description": "Description", + "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n" } \ No newline at end of file diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 71d4af69a7..89116dad35 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,3 +1,4 @@ +import 'package:evently/screens/detail_screen.dart'; import 'package:evently/screens/overview_screen.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; @@ -67,7 +68,7 @@ class CreateEventContent extends StatelessWidget { } Widget details() { - return const Text("details"); + return const DetailsScreen(); } Widget perks() { diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart new file mode 100644 index 0000000000..4ba7b632f9 --- /dev/null +++ b/evently/lib/screens/detail_screen.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class DetailsScreen extends StatefulWidget { + const DetailsScreen({super.key}); + + @override + State createState() => _DetailsScreenState(); +} + +class _DetailsScreenState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 025cda3844..2cddf9f312 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -9,9 +9,7 @@ import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; import 'package:evently/widgets/easel_text_field.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; @@ -150,43 +148,41 @@ class _OverViewScreenState extends State { style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), ), VerticalSpace(20.h), - // DottedBorder( - // borderType: BorderType.RRect, - // radius: const Radius.circular(10), - // // borderType: BorderType.Rect, - // color: EventlyAppTheme.kLightPurple, - // - // strokeWidth: 2, - // child: Container( - // width: double.infinity, - // padding: EdgeInsets.symmetric(vertical: 20.w), - // child: Column( - // children: [ - // Text( - // LocaleKeys.tap_select.tr(), - // style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), - // ), - // VerticalSpace(10.h), - // SvgPicture.asset(SVGUtils.kSvgUpload), - // VerticalSpace(10.h), - // Text( - // LocaleKeys.mb_limit.tr(), - // style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), - // ), - // ], - // ), - // ), - // ), - - SvgPicture.asset(SVGUtils.dottedBorder), - + ClipRRect( + borderRadius: BorderRadius.only(topRight: Radius.circular(8.r), topLeft: Radius.circular(8.r)), + child: DottedBorder( + borderType: BorderType.Rect, + dashPattern: const [10, 6], + color: EventlyAppTheme.kLightPurple, + strokeWidth: 3.h, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 20.w), + child: Column( + children: [ + Text( + LocaleKeys.tap_select.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), + ), + VerticalSpace(10.h), + SvgPicture.asset(SVGUtils.kSvgUpload), + VerticalSpace(10.h), + Text( + LocaleKeys.mb_limit.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kLightPurple), + ), + ], + ), + ), + ), + ), VerticalSpace(20.h), ClippedButton( title: LocaleKeys.continue_key.tr(), bgColor: EventlyAppTheme.kBlue, textColor: EventlyAppTheme.kWhite, onPressed: () { - // validateAndUpdateDescription(moveNextPage: true); + createEventViewModel.nextPage(); }, cuttingHeight: 15.h, clipperType: ClipperType.bottomLeftTopRight, @@ -204,8 +200,6 @@ class _OverViewScreenState extends State { ), ), VerticalSpace(5.h), - - ], ), ), diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index e6a1940b00..5c538046c0 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -10,7 +10,7 @@ final List stepLabels = ["overview", "detail", "perks", "price"]; class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; static const kSvgUpload = "assets/images/svg/upload.svg"; - static const dottedBorder = "assets/images/svg/dotted_border.svg"; + } /// ```PNG assets From 804a61275ac01453bcc33883ba9cef997891f430 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 16:24:33 +0500 Subject: [PATCH 026/161] feat: detail screen ui development --- evently/lib/generated/locale_keys.g.dart | 8 + evently/lib/screens/detail_screen.dart | 310 ++++++++++++++++++++++- 2 files changed, 317 insertions(+), 1 deletion(-) diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 852e36882b..8bcda64816 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -21,5 +21,13 @@ abstract class LocaleKeys { static const mb_limit = 'mb_limit'; static const continue_key = 'continue_key'; static const save_draft = 'save_draft'; + static const start_date = 'start_date'; + static const end_date = 'end_date'; + static const start_time = 'start_time'; + static const end_time = 'end_time'; + static const location = 'location'; + static const search_location = 'search_location'; + static const description = 'description'; + static const what_event_for = 'what_event_for'; } diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 4ba7b632f9..d310ce27bd 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -1,4 +1,15 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/screens/custom_widgets/step_labels.dart'; +import 'package:evently/screens/custom_widgets/steps_indicator.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/space_utils.dart'; +import 'package:evently/viewmodels/create_event_viewmodel.dart'; +import 'package:evently/widgets/clipped_button.dart'; +import 'package:evently/widgets/easel_text_field.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; class DetailsScreen extends StatefulWidget { const DetailsScreen({super.key}); @@ -8,8 +19,305 @@ class DetailsScreen extends StatefulWidget { } class _DetailsScreenState extends State { + final _formKey = GlobalKey(); + final ValueNotifier _startDateFieldError = ValueNotifier(""); + final ValueNotifier _endDateFieldError = ValueNotifier(""); + final ValueNotifier _startTimeFieldError = ValueNotifier(""); + final ValueNotifier _endTimeFieldError = ValueNotifier(""); + + @override + void initState() { + super.initState(); + } + @override Widget build(BuildContext context) { - return const Placeholder(); + final createEventViewModel = context.watch(); + + return Scaffold( + body: SingleChildScrollView( + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: createEventViewModel.currentStep), + const VerticalSpace(5), + StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), + const VerticalSpace(10), + const VerticalSpace(20), + Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: ValueListenableBuilder( + valueListenable: createEventViewModel.currentPage, + builder: (_, int currentPage, __) => Padding( + padding: EdgeInsets.only(left: 10.sp), + child: IconButton( + onPressed: () { + FocusScope.of(context).unfocus(); + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + Navigator.pop(context); + }, + icon: const Icon( + Icons.arrow_back_ios, + color: EventlyAppTheme.kGrey, + ), + )), + )), + ValueListenableBuilder( + valueListenable: createEventViewModel.currentPage, + builder: (_, int currentPage, __) { + return Text( + createEventViewModel.pageTitles[createEventViewModel.currentPage.value], + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 18.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kDarkText), + ); + }, + ), + ], + ), + const VerticalSpace(20), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Column( + children: [ + EventlyTextField( + label: LocaleKeys.start_date.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ValueListenableBuilder( + valueListenable: _startDateFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + ], + ), + ), + HorizontalSpace(20.w), + Expanded( + child: Column( + children: [ + EventlyTextField( + label: LocaleKeys.end_date.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ValueListenableBuilder( + valueListenable: _endDateFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + ], + ), + ), + ], + ), + VerticalSpace(20.h), + Row( + children: [ + Expanded( + child: Column( + children: [ + EventlyTextField( + label: LocaleKeys.start_time.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ValueListenableBuilder( + valueListenable: _startTimeFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + ], + ), + ), + HorizontalSpace(20.w), + Expanded( + child: Column( + children: [ + EventlyTextField( + label: LocaleKeys.end_time.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ValueListenableBuilder( + valueListenable: _endTimeFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + ], + ), + ), + ], + ), + VerticalSpace(20.h), + + EventlyTextField( + label: LocaleKeys.location.tr(), + hint: LocaleKeys.search_location.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ValueListenableBuilder( + valueListenable: _startDateFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + VerticalSpace(20.h), + + + EventlyTextField( + + label: LocaleKeys.description.tr(), + hint: LocaleKeys.what_event_for.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + + noOfLines: 4, + ), + ValueListenableBuilder( + valueListenable: _startDateFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + VerticalSpace(20.h), + + ClippedButton( + title: LocaleKeys.continue_key.tr(), + bgColor: EventlyAppTheme.kBlue, + textColor: EventlyAppTheme.kWhite, + onPressed: () { + createEventViewModel.nextPage(); + }, + cuttingHeight: 15.h, + clipperType: ClipperType.bottomLeftTopRight, + isShadow: false, + fontWeight: FontWeight.w700, + ), + VerticalSpace(10.h), + Center( + child: InkWell( + onTap: () {}, + child: Text( + LocaleKeys.save_draft.tr(), + style: TextStyle(color: EventlyAppTheme.kLightGreyText, fontSize: 14.sp, fontWeight: FontWeight.w700), + ), + ), + ), + VerticalSpace(5.h), + ], + ), + ), + ], + ), + ), + ), + ); } } From f67e860be9fe3533670a689172faa20a106b2847 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 16:36:41 +0500 Subject: [PATCH 027/161] feat: perks translations --- evently/i18n/de.json | 8 +- evently/i18n/en-US.json | 8 +- evently/i18n/es.json | 8 +- evently/i18n/ru-RU.json | 8 +- evently/lib/screens/create_event.dart | 3 +- evently/lib/screens/detail_screen.dart | 9 +- evently/lib/screens/perks_screen.dart | 111 +++++++++++++++++++++++++ 7 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 evently/lib/screens/perks_screen.dart diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 300fa8f4f8..46ccddcc01 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -26,5 +26,11 @@ "location": "Location", "search_location": "Search location", "description": "Description", - "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n" + "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n", + "add_perk": "Add perk", + "perk_icon": "Perk icon", + "description_optional": "Description (optional)", + "claim_free_drink": "Claim your free drink at the bar during the event!", + "character_limit": "256 character limit", + "there_no_perks_created": "There are no perks created." } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 300fa8f4f8..46ccddcc01 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -26,5 +26,11 @@ "location": "Location", "search_location": "Search location", "description": "Description", - "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n" + "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n", + "add_perk": "Add perk", + "perk_icon": "Perk icon", + "description_optional": "Description (optional)", + "claim_free_drink": "Claim your free drink at the bar during the event!", + "character_limit": "256 character limit", + "there_no_perks_created": "There are no perks created." } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 300fa8f4f8..46ccddcc01 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -26,5 +26,11 @@ "location": "Location", "search_location": "Search location", "description": "Description", - "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n" + "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n", + "add_perk": "Add perk", + "perk_icon": "Perk icon", + "description_optional": "Description (optional)", + "claim_free_drink": "Claim your free drink at the bar during the event!", + "character_limit": "256 character limit", + "there_no_perks_created": "There are no perks created." } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 300fa8f4f8..46ccddcc01 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -26,5 +26,11 @@ "location": "Location", "search_location": "Search location", "description": "Description", - "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n" + "what_event_for": "What is the event for? What should guests expect? Is there a dress code? Can they bring a guest?\n\n", + "add_perk": "Add perk", + "perk_icon": "Perk icon", + "description_optional": "Description (optional)", + "claim_free_drink": "Claim your free drink at the bar during the event!", + "character_limit": "256 character limit", + "there_no_perks_created": "There are no perks created." } \ No newline at end of file diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 89116dad35..ab041d4aff 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,5 +1,6 @@ import 'package:evently/screens/detail_screen.dart'; import 'package:evently/screens/overview_screen.dart'; +import 'package:evently/screens/perks_screen.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; @@ -72,7 +73,7 @@ class CreateEventContent extends StatelessWidget { } Widget perks() { - return const Text("perks"); + return const PerksScreen(); } Widget price() { diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index d310ce27bd..3e47e8cd04 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -24,6 +24,7 @@ class _DetailsScreenState extends State { final ValueNotifier _endDateFieldError = ValueNotifier(""); final ValueNotifier _startTimeFieldError = ValueNotifier(""); final ValueNotifier _endTimeFieldError = ValueNotifier(""); + final ValueNotifier _descriptionFieldError = ValueNotifier(""); @override void initState() { @@ -225,7 +226,6 @@ class _DetailsScreenState extends State { ], ), VerticalSpace(20.h), - EventlyTextField( label: LocaleKeys.location.tr(), hint: LocaleKeys.search_location.tr(), @@ -254,10 +254,7 @@ class _DetailsScreenState extends State { }, ), VerticalSpace(20.h), - - EventlyTextField( - label: LocaleKeys.description.tr(), hint: LocaleKeys.what_event_for.tr(), controller: TextEditingController(), @@ -265,11 +262,10 @@ class _DetailsScreenState extends State { validator: (value) { return null; }, - noOfLines: 4, ), ValueListenableBuilder( - valueListenable: _startDateFieldError, + valueListenable: _descriptionFieldError, builder: (_, String artNameFieldError, __) { if (artNameFieldError.isEmpty) { return const SizedBox.shrink(); @@ -287,7 +283,6 @@ class _DetailsScreenState extends State { }, ), VerticalSpace(20.h), - ClippedButton( title: LocaleKeys.continue_key.tr(), bgColor: EventlyAppTheme.kBlue, diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart new file mode 100644 index 0000000000..ed4cc7d92b --- /dev/null +++ b/evently/lib/screens/perks_screen.dart @@ -0,0 +1,111 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/screens/custom_widgets/step_labels.dart'; +import 'package:evently/screens/custom_widgets/steps_indicator.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/space_utils.dart'; +import 'package:evently/viewmodels/create_event_viewmodel.dart'; +import 'package:evently/widgets/clipped_button.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; + +class PerksScreen extends StatefulWidget { + const PerksScreen({super.key}); + + @override + State createState() => _PerksScreenState(); +} + +class _PerksScreenState extends State { + + final _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + final createEventViewModel = context.watch(); + + return Scaffold( + body: SingleChildScrollView( + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: createEventViewModel.currentStep), + const VerticalSpace(5), + StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), + const VerticalSpace(10), + const VerticalSpace(20), + Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: ValueListenableBuilder( + valueListenable: createEventViewModel.currentPage, + builder: (_, int currentPage, __) => Padding( + padding: EdgeInsets.only(left: 10.sp), + child: IconButton( + onPressed: () { + FocusScope.of(context).unfocus(); + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + Navigator.pop(context); + }, + icon: const Icon( + Icons.arrow_back_ios, + color: EventlyAppTheme.kGrey, + ), + )), + )), + ValueListenableBuilder( + valueListenable: createEventViewModel.currentPage, + builder: (_, int currentPage, __) { + return Text( + createEventViewModel.pageTitles[createEventViewModel.currentPage.value], + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 18.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kDarkText), + ); + }, + ), + ], + ), + const VerticalSpace(20), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClippedButton( + title: LocaleKeys.continue_key.tr(), + bgColor: EventlyAppTheme.kBlue, + textColor: EventlyAppTheme.kWhite, + onPressed: () { + createEventViewModel.nextPage(); + }, + cuttingHeight: 15.h, + clipperType: ClipperType.bottomLeftTopRight, + isShadow: false, + fontWeight: FontWeight.w700, + ), + VerticalSpace(10.h), + Center( + child: InkWell( + onTap: () {}, + child: Text( + LocaleKeys.save_draft.tr(), + style: TextStyle(color: EventlyAppTheme.kLightGreyText, fontSize: 14.sp, fontWeight: FontWeight.w700), + ), + ), + ), + VerticalSpace(5.h), + ], + ), + ), + ], + ), + ), + ), + ); + } +} From 15cfe9844c396af886cc003739578faa553c117d Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 16:51:59 +0500 Subject: [PATCH 028/161] feat: asset on perk screen --- evently/assets/images/svg/drinks.svg | 7 +++ evently/assets/images/svg/gift.svg | 3 + evently/assets/images/svg/shirt.svg | 3 + evently/lib/screens/perks_screen.dart | 86 +++++++++++++++------------ evently/lib/utils/constants.dart | 4 +- 5 files changed, 64 insertions(+), 39 deletions(-) create mode 100644 evently/assets/images/svg/drinks.svg create mode 100644 evently/assets/images/svg/gift.svg create mode 100644 evently/assets/images/svg/shirt.svg diff --git a/evently/assets/images/svg/drinks.svg b/evently/assets/images/svg/drinks.svg new file mode 100644 index 0000000000..982911aaa5 --- /dev/null +++ b/evently/assets/images/svg/drinks.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/evently/assets/images/svg/gift.svg b/evently/assets/images/svg/gift.svg new file mode 100644 index 0000000000..20f05015f8 --- /dev/null +++ b/evently/assets/images/svg/gift.svg @@ -0,0 +1,3 @@ + + + diff --git a/evently/assets/images/svg/shirt.svg b/evently/assets/images/svg/shirt.svg new file mode 100644 index 0000000000..b7d8d23fe3 --- /dev/null +++ b/evently/assets/images/svg/shirt.svg @@ -0,0 +1,3 @@ + + + diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index ed4cc7d92b..31ccd0c43d 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -18,7 +18,6 @@ class PerksScreen extends StatefulWidget { } class _PerksScreenState extends State { - final _formKey = GlobalKey(); @override @@ -26,51 +25,62 @@ class _PerksScreenState extends State { final createEventViewModel = context.watch(); return Scaffold( - body: SingleChildScrollView( + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const VerticalSpace(20), - MyStepsIndicator(currentStep: createEventViewModel.currentStep), - const VerticalSpace(5), - StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), - const VerticalSpace(10), - const VerticalSpace(20), - Stack( - alignment: Alignment.center, + Column( children: [ - Align( - alignment: Alignment.centerLeft, - child: ValueListenableBuilder( + const VerticalSpace(20), + MyStepsIndicator(currentStep: createEventViewModel.currentStep), + const VerticalSpace(5), + StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), + const VerticalSpace(10), + const VerticalSpace(20), + Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: ValueListenableBuilder( + valueListenable: createEventViewModel.currentPage, + builder: (_, int currentPage, __) => Padding( + padding: EdgeInsets.only(left: 10.sp), + child: IconButton( + onPressed: () { + FocusScope.of(context).unfocus(); + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + Navigator.pop(context); + }, + icon: const Icon( + Icons.arrow_back_ios, + color: EventlyAppTheme.kGrey, + ), + )), + )), + ValueListenableBuilder( valueListenable: createEventViewModel.currentPage, - builder: (_, int currentPage, __) => Padding( - padding: EdgeInsets.only(left: 10.sp), - child: IconButton( - onPressed: () { - FocusScope.of(context).unfocus(); - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - Navigator.pop(context); - }, - icon: const Icon( - Icons.arrow_back_ios, - color: EventlyAppTheme.kGrey, - ), - )), - )), - ValueListenableBuilder( - valueListenable: createEventViewModel.currentPage, - builder: (_, int currentPage, __) { - return Text( - createEventViewModel.pageTitles[createEventViewModel.currentPage.value], - style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 18.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kDarkText), - ); - }, + builder: (_, int currentPage, __) { + return Text( + createEventViewModel.pageTitles[createEventViewModel.currentPage.value], + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 18.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kDarkText), + ); + }, + ), + ], ), + const VerticalSpace(20), + + + ], ), - const VerticalSpace(20), Padding( padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), child: Column( @@ -105,7 +115,7 @@ class _PerksScreenState extends State { ], ), ), - ), - ); + )), + )); } } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 5c538046c0..3c5dcc6e86 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -10,7 +10,9 @@ final List stepLabels = ["overview", "detail", "perks", "price"]; class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; static const kSvgUpload = "assets/images/svg/upload.svg"; - + static const kShirt = "assets/images/svg/shirt.svg"; + static const kGift = "assets/images/svg/gift.svg"; + static const kDrinks = "assets/images/svg/drinks.svg"; } /// ```PNG assets From dad05b8d981e0a9218e8cb6eea7d76e80eb62d6a Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 17:35:13 +0500 Subject: [PATCH 029/161] feat: add perk ui --- .../images/svg/dark_purple_single_line.png | Bin 0 -> 362 bytes .../images/svg/light_purple_single_line.png | Bin 0 -> 361 bytes evently/assets/images/svg/minus.svg | 4 + .../assets/images/text_field_multi_line.png | Bin 649 -> 665 bytes .../assets/images/text_field_single_line.png | Bin 358 -> 416 bytes evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/generated/locale_keys.g.dart | 7 ++ evently/lib/screens/perks_screen.dart | 98 +++++++++++++++++- evently/lib/utils/constants.dart | 3 + 12 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 evently/assets/images/svg/dark_purple_single_line.png create mode 100644 evently/assets/images/svg/light_purple_single_line.png create mode 100644 evently/assets/images/svg/minus.svg diff --git a/evently/assets/images/svg/dark_purple_single_line.png b/evently/assets/images/svg/dark_purple_single_line.png new file mode 100644 index 0000000000000000000000000000000000000000..8881c70bcdca4190666b2954fb8698882404d2b4 GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0y~yU`z(GwK>>;&U>cv7h@-A}f&p-_yl0q=ND76>r`{0V1v!#q2!}GA141 z=DN@{|B$bQz@g>2xAqxFWN#6D^RHqDuPlS_TPucH#>ou~X*>c4dKj5z7&Zp?P$ zQahMBH62VH>IYE~x95YY%N=0q^a(I^xCKnzIs&FH^?<2UX9TBPv+$aBw{;7P1A~IW M)78&qol`;+02efBj{pDw literal 0 HcmV?d00001 diff --git a/evently/assets/images/svg/light_purple_single_line.png b/evently/assets/images/svg/light_purple_single_line.png new file mode 100644 index 0000000000000000000000000000000000000000..7a2914d411af6110c3d19e1ef60e3836328df1b7 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0y~yU`z(GwK>>;&U>cv7h@-A}f&p&(p;*q=ND7mEF9D97I?Ho&79XO4#oz z2J*2o_$xVd^yl8(ZEm4Hiz}_zGCrS~AvTwtVV7}o149~*z=0k{rWu9~490963C9>1 z&qydVNHVh+P(iY4H?O)e+l!ZmkNzf>n8KQ zN#-q|QjZ+%VT}4=_$cWZL-ucDFeL@yNm_z=GYmkK_iPI=H47}~J=5}05xY&LhN$&L SU1?xQFnGH9xvX literal 0 HcmV?d00001 diff --git a/evently/assets/images/svg/minus.svg b/evently/assets/images/svg/minus.svg new file mode 100644 index 0000000000..588b49faa0 --- /dev/null +++ b/evently/assets/images/svg/minus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/evently/assets/images/text_field_multi_line.png b/evently/assets/images/text_field_multi_line.png index 5a1fc8bdc3bb4a9f6a5179215aa00d0c931bf87a..e596c1e486d216bf0b7841dafb58c8c9931d96be 100644 GIT binary patch literal 665 zcmeAS@N?(olHy`uVBq!ia0y~yU^D`>MJ&U>cv7h@-A}a#}lb)xGV@L(#+Z(oC%#IQb4}Bjkb>f&Y zJ1VNG#X;|ZppI@ZSLT@xuBQD5lj1AnBFZGwZcm??UuYq-Zh58@zr)2kOU5Idk`vTZ z9Og}6taM^~GRmP&7;KBt>vv+C^45Iz*}W4OLw~<5+bu8e#3r)WJ!i)(2B&kz8+RAZ z{jGvOLqO#OKP6L zxcQ6N#=Cjne_W0W_f9A~CuqIT?ahzbfBjMptodUKR#519giVn-?ct>k-tgoFWp)35 lx;NCDu>IaX-6f}jjeDDOti~d>24G5J@O1TaS?83{1OTl>?hyb0 literal 649 zcmeAS@N?(olHy`uVBq!ia0y~yU`z(G>p0keVM%xNb!1@J*w6hZk(GggNzT*7F{Fa=?Uj?BEQ}%y7cU-AGY!7% z+Pb-N_sxp$>N zl>;``W$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIa5y;$B+ufx3?AZ4m*gr23B7QJ<4_B z+^#8y4(J>VG82f1kdTm2J@C~dS5$O~-iE&iEHj>+KR(^J+D0y3nn7s_TZ6swsT&*6hm0B z*UuNb-hC*}XNYjUz3ut!x7+IHbQ&GtI;If+?)&e$?YCp)pD*IsuwlKz{$9s@f9ix& z8(3|*i&RfBa7#AV)QUavn!+e%pn77e2D8qBDMb>^HUEm&GbV(Yo|t|1S?#4f<^ywn xt$XbkJl$0?!S3((?=M%nNG8br=PUZksr$xevG4l>i+};j;OXk;vd$@?2>@^#lF0x7 literal 358 zcmeAS@N?(olHy`uVBq!ia0y~yU`z(GwK>>;&U>cv7h@-A}f&p+tbA{q=ND7mEF9D97I?H7Z_}J%NE$7 zOtv|qdkmP)$)ag$qjvWy*q#| zP`~dMb4Fr8bG))%8qb9r`vt{~*)lTDc63WJn`|lrsh7H8^eE{V1N&_&Fm=lqOx?78 b^oc>qz<>G_cV=KHFfe$!`njxgN@xNAAzX4v diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 46ccddcc01..e52f2127d1 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -32,5 +32,6 @@ "description_optional": "Description (optional)", "claim_free_drink": "Claim your free drink at the bar during the event!", "character_limit": "256 character limit", - "there_no_perks_created": "There are no perks created." + "there_no_perks_created": "There are no perks created.", + "free_shirt": "1 free t-shirt" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 46ccddcc01..e52f2127d1 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -32,5 +32,6 @@ "description_optional": "Description (optional)", "claim_free_drink": "Claim your free drink at the bar during the event!", "character_limit": "256 character limit", - "there_no_perks_created": "There are no perks created." + "there_no_perks_created": "There are no perks created.", + "free_shirt": "1 free t-shirt" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 46ccddcc01..e52f2127d1 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -32,5 +32,6 @@ "description_optional": "Description (optional)", "claim_free_drink": "Claim your free drink at the bar during the event!", "character_limit": "256 character limit", - "there_no_perks_created": "There are no perks created." + "there_no_perks_created": "There are no perks created.", + "free_shirt": "1 free t-shirt" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 46ccddcc01..e52f2127d1 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -32,5 +32,6 @@ "description_optional": "Description (optional)", "claim_free_drink": "Claim your free drink at the bar during the event!", "character_limit": "256 character limit", - "there_no_perks_created": "There are no perks created." + "there_no_perks_created": "There are no perks created.", + "free_shirt": "1 free t-shirt" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 8bcda64816..a1e7e06fb8 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -29,5 +29,12 @@ abstract class LocaleKeys { static const search_location = 'search_location'; static const description = 'description'; static const what_event_for = 'what_event_for'; + static const add_perk = 'add_perk'; + static const perk_icon = 'perk_icon'; + static const description_optional = 'description_optional'; + static const claim_free_drink = 'claim_free_drink'; + static const character_limit = 'character_limit'; + static const there_no_perks_created = 'there_no_perks_created'; + static const free_shirt = 'free_shirt'; } diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index 31ccd0c43d..01396b05d0 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -2,12 +2,17 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; import 'package:evently/screens/custom_widgets/steps_indicator.dart'; +import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/screen_responsive.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; class PerksScreen extends StatefulWidget { @@ -36,12 +41,12 @@ class _PerksScreenState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ const VerticalSpace(20), MyStepsIndicator(currentStep: createEventViewModel.currentStep), const VerticalSpace(5), StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), - const VerticalSpace(10), const VerticalSpace(20), Stack( alignment: Alignment.center, @@ -76,9 +81,94 @@ class _PerksScreenState extends State { ], ), const VerticalSpace(20), - - - + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + LocaleKeys.add_perk.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold), + ), + InkWell( + onTap: () {}, + child: DecoratedBox( + decoration: const BoxDecoration( + color: EventlyAppTheme.kpurpleDark, + ), + child: Icon(Icons.add, size: 16.h, color: EventlyAppTheme.kWhite), + ), + ), + ], + ), + ), + const VerticalSpace(10), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Text( + LocaleKeys.there_no_perks_created.tr(), + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kDarkGrey02), + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Stack( + alignment: Alignment.center, + children: [ + ScreenResponsive( + mobileScreen: (context) => Image.asset( + PngUtils.kLightPurpleSingleLine, + height: 40.h, + width: 1.sw, + fit: BoxFit.fill, + ), + tabletScreen: (context) => Image.asset( + PngUtils.kLightPurpleSingleLine, + height: 32.h, + width: 1.sw, + fit: BoxFit.fill, + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset(SVGUtils.kMinus), + HorizontalSpace(10.w), + Text( + LocaleKeys.free_shirt.tr(), + style: TextStyle(color: EventlyAppTheme.kWhite, fontSize: 15.sp, fontWeight: FontWeight.bold), + ), + ], + ), + ) + ], + ), + ), + const VerticalSpace(10), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50.w), + child: Text( + LocaleKeys.perk_icon.tr(), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 13.sp, + ), + ), + ), + const VerticalSpace(6), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container(padding: const EdgeInsets.all(7), color: EventlyAppTheme.kPurple03, child: SvgPicture.asset(SVGUtils.kShirt)), + Container(padding: const EdgeInsets.all(7), color: Colors.transparent, child: SvgPicture.asset(SVGUtils.kGift)), + Container(padding: const EdgeInsets.all(7), color: Colors.transparent, child: SvgPicture.asset(SVGUtils.kDrinks)), + ], + ), + ) ], ), Padding( diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 3c5dcc6e86..6dc9923896 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -13,10 +13,13 @@ class SVGUtils { static const kShirt = "assets/images/svg/shirt.svg"; static const kGift = "assets/images/svg/gift.svg"; static const kDrinks = "assets/images/svg/drinks.svg"; + static const kMinus = "assets/images/svg/minus.svg"; } /// ```PNG assets class PngUtils { static const kTextFieldSingleLine = 'assets/images/text_field_single_line.png'; static const kTextFieldMultiLine = 'assets/images/text_field_multi_line.png'; + static const kDarkPurpleSingleLine = 'assets/images/svg/dark_purple_single_line.png'; + static const kLightPurpleSingleLine = 'assets/images/svg/light_purple_single_line.png'; } From 7effd6cd789c766a8fd7fd2e12b57829fbcfd7b3 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 17:43:23 +0500 Subject: [PATCH 030/161] feat: perks screen ui --- evently/lib/screens/perks_screen.dart | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index 01396b05d0..df744303e2 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -8,6 +8,7 @@ import 'package:evently/utils/screen_responsive.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; +import 'package:evently/widgets/easel_text_field.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -168,7 +169,22 @@ class _PerksScreenState extends State { Container(padding: const EdgeInsets.all(7), color: Colors.transparent, child: SvgPicture.asset(SVGUtils.kDrinks)), ], ), - ) + ), + const VerticalSpace(20), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50.w), + child: EventlyTextField( + noOfLines: 4, + label: LocaleKeys.description_optional.tr(), + hint: LocaleKeys.claim_free_drink.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ), + ], ), Padding( From c6c1028a7184b6bf64f1b21cc57c4d91ce3e5902 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Tue, 7 May 2024 17:58:26 +0500 Subject: [PATCH 031/161] feat: price screen --- evently/lib/screens/create_event.dart | 3 ++- evently/lib/screens/perks_screen.dart | 2 -- evently/lib/screens/price_screen.dart | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 evently/lib/screens/price_screen.dart diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index ab041d4aff..83a818cfa9 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,6 +1,7 @@ import 'package:evently/screens/detail_screen.dart'; import 'package:evently/screens/overview_screen.dart'; import 'package:evently/screens/perks_screen.dart'; +import 'package:evently/screens/price_screen.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; @@ -77,6 +78,6 @@ class CreateEventContent extends StatelessWidget { } Widget price() { - return const Text("price"); + return const PriceScreen(); } } diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index df744303e2..66da0da1c4 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -9,9 +9,7 @@ import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; import 'package:evently/widgets/easel_text_field.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart new file mode 100644 index 0000000000..5bdbc6c110 --- /dev/null +++ b/evently/lib/screens/price_screen.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class PriceScreen extends StatefulWidget { + const PriceScreen({super.key}); + + @override + State createState() => _PriceScreenState(); +} + +class _PriceScreenState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} From 3fa7bb29a81a3bdf5cbd67fe1bd2c09e7dde18fd Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 11:03:27 +0500 Subject: [PATCH 032/161] feat: Event provide, price text input field --- evently/assets/images/denom_pylon.png | Bin 0 -> 804 bytes evently/assets/images/denom_usd.png | Bin 0 -> 1169 bytes evently/assets/images/text_field_button.png | Bin 0 -> 370 bytes evently/i18n/de.json | 7 +- evently/i18n/en-US.json | 7 +- evently/i18n/es.json | 7 +- evently/i18n/ru-RU.json | 7 +- evently/lib/evently_provider.dart | 26 ++ evently/lib/generated/locale_keys.g.dart | 5 + evently/lib/models/denom.dart | 84 ++++++ evently/lib/screens/detail_screen.dart | 2 +- evently/lib/screens/overview_screen.dart | 2 +- evently/lib/screens/perks_screen.dart | 2 +- evently/lib/screens/price_screen.dart | 249 +++++++++++++++++- evently/lib/utils/amount_formatter.dart | 28 ++ evently/lib/utils/constants.dart | 24 ++ .../widgets/evently_price_input_field.dart | 113 ++++++++ ...ext_field.dart => evently_text_field.dart} | 0 18 files changed, 554 insertions(+), 9 deletions(-) create mode 100644 evently/assets/images/denom_pylon.png create mode 100644 evently/assets/images/denom_usd.png create mode 100644 evently/assets/images/text_field_button.png create mode 100644 evently/lib/evently_provider.dart create mode 100644 evently/lib/models/denom.dart create mode 100644 evently/lib/utils/amount_formatter.dart create mode 100644 evently/lib/widgets/evently_price_input_field.dart rename evently/lib/widgets/{easel_text_field.dart => evently_text_field.dart} (100%) diff --git a/evently/assets/images/denom_pylon.png b/evently/assets/images/denom_pylon.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e43bc030e44103893f304c429fc9c0f293c2c5 GIT binary patch literal 804 zcmV+<1Ka$GP)GhHiikuh>|G|IGAhe-Hb3COvV)zJnj$;{1jl|2C#31ZD$fSG4+U~29vNRZM@P9k4OqHsW5lJQYmDLlzTO@#a{+(p#t4Md)R#W6zhfC*jjswiU>?v#bSzuB1^SPYp968 ziLPGNC`-$XT)r1*9v)L|vtdMx_k{%NBQUTa)W#Q6n(#jBsIV_&fg(8E(p!SwgOnq< irN>dC_zWqw^o{}S80M}ArXj`v0000_sXggY7^}pWx?nOe2!IY{6A4Qx$m=V1Iy6Tq2Yvyj5Hx3bNDY;t zDTB%DlW^Tv?23f#)r>(7Ob0kyVOzK$nSZflV)TgJH)#`D6qO)|%w&()KTo@uQR>2~ z5M`K8iLWJ}nL#l`t{5{8V}_z(T?zjI9eBaz!UujTkIs++rC^br?t|j8ibyMl42aH- z1yjvZ(;;(#8*-L!fem<3*%COm(157o0fX}r-Gz|NnLETq>+JXIQS4c^;nUn;o!9GIJ)tM zOds+U60jm;jkurdm=CIA7tiutyU=g4MYiq!-2oOzBSBt}q@}kJ`UZPpmOUMuvlk`Q z(Y<>~E;@BSH22htG#~>d#voXrq4OO)Mqjz*zaS!uOw|@=s*JQnuytIe(42IG35$VntkKikg4JmXDm^gfh*i^D?GHh3k#V+5_Yiym8tOJP(+e3Ni=yza>A)nC((mlz<5EI76$nk`gT1^QC zIO7z_u>)Z$q&`@2#?RwIoC^}*=Nv!d?BSGsuo}yC{G`XlCq1?ucM7iPPQe+i4o0^# jD1z6AG_^XFX;SeQf-|ptuI;pw00000NkvXXu0mjf5mFd) literal 0 HcmV?d00001 diff --git a/evently/assets/images/text_field_button.png b/evently/assets/images/text_field_button.png new file mode 100644 index 0000000000000000000000000000000000000000..3554ba3ac5eb8dbf6ed46362557124334fea8f6d GIT binary patch literal 370 zcmeAS@N?(olHy`uVBq!ia0vp^JwU9@!3HE(-Au>;Qk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFKsk0#7srqa#<$n@`Z60z98ZjQ@$7oz)S;wh z*65ocVwS+8BITCAqVg&sX&?Wgr~fkFS6ZDe`+hmQ{CoNNtp_|dGcg_%5abc0fLMGr zNS^huhVC@qyK}hP_Sz*!t`}G1Ib3(_(lvM1f5ERWFRM9zX@@?((WGhSrd*xVSN~Y;RGP8UN&zHMlAv=|J9Vn7q~4mnH-SOK;OXk;vd$@? F2>{y$f`R}5 literal 0 HcmV?d00001 diff --git a/evently/i18n/de.json b/evently/i18n/de.json index e52f2127d1..dfa99e77e0 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -33,5 +33,10 @@ "claim_free_drink": "Claim your free drink at the bar during the event!", "character_limit": "256 character limit", "there_no_perks_created": "There are no perks created.", - "free_shirt": "1 free t-shirt" + "free_shirt": "1 free t-shirt", + "is_free_drop": "Is this a free drop?", + "yes": "Yes", + "no" : "No", + "number_tickets": "Number of Tickets", + "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index e52f2127d1..dfa99e77e0 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -33,5 +33,10 @@ "claim_free_drink": "Claim your free drink at the bar during the event!", "character_limit": "256 character limit", "there_no_perks_created": "There are no perks created.", - "free_shirt": "1 free t-shirt" + "free_shirt": "1 free t-shirt", + "is_free_drop": "Is this a free drop?", + "yes": "Yes", + "no" : "No", + "number_tickets": "Number of Tickets", + "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index e52f2127d1..dfa99e77e0 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -33,5 +33,10 @@ "claim_free_drink": "Claim your free drink at the bar during the event!", "character_limit": "256 character limit", "there_no_perks_created": "There are no perks created.", - "free_shirt": "1 free t-shirt" + "free_shirt": "1 free t-shirt", + "is_free_drop": "Is this a free drop?", + "yes": "Yes", + "no" : "No", + "number_tickets": "Number of Tickets", + "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index e52f2127d1..dfa99e77e0 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -33,5 +33,10 @@ "claim_free_drink": "Claim your free drink at the bar during the event!", "character_limit": "256 character limit", "there_no_perks_created": "There are no perks created.", - "free_shirt": "1 free t-shirt" + "free_shirt": "1 free t-shirt", + "is_free_drop": "Is this a free drop?", + "yes": "Yes", + "no" : "No", + "number_tickets": "Number of Tickets", + "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain" } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart new file mode 100644 index 0000000000..d742f7c69c --- /dev/null +++ b/evently/lib/evently_provider.dart @@ -0,0 +1,26 @@ + + + +import 'package:evently/models/denom.dart'; +import 'package:flutter/cupertino.dart'; + +enum FreeDrop { yes, no, unselected } + +class EventlyProvider extends ChangeNotifier { + + Denom _selectedDenom = Denom.availableDenoms.first; + + Denom get selectedDenom => _selectedDenom; + + List supportedDenomList = []; + + TextEditingController priceController = TextEditingController(); + + FreeDrop isFreeDrop = FreeDrop.unselected; + + void setSelectedDenom(Denom value) { + _selectedDenom = value; + notifyListeners(); + } + +} \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index a1e7e06fb8..47bf5b4a74 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -36,5 +36,10 @@ abstract class LocaleKeys { static const character_limit = 'character_limit'; static const there_no_perks_created = 'there_no_perks_created'; static const free_shirt = 'free_shirt'; + static const is_free_drop = 'is_free_drop'; + static const yes = 'yes'; + static const no = 'no'; + static const number_tickets = 'number_tickets'; + static const network_fee_listed_price_occur_on_chain = 'network_fee_listed_price_occur_on_chain'; } diff --git a/evently/lib/models/denom.dart b/evently/lib/models/denom.dart new file mode 100644 index 0000000000..1c94ab5551 --- /dev/null +++ b/evently/lib/models/denom.dart @@ -0,0 +1,84 @@ +import 'package:evently/utils/amount_formatter.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +import '../main.dart'; + +class Denom { + final String name; + final String symbol; + final String icon; + + Denom({required this.name, required this.symbol, required this.icon}); + + factory Denom.initial() { + return Denom(icon: '', name: '', symbol: ''); + } + + static List get availableDenoms => [ + Denom(name: kUSDText, symbol: kUsdSymbol, icon: PngUtils.kIconDenomUsd), + Denom(name: kPylonText, symbol: kPylonSymbol, icon: PngUtils.kIconDenomPylon), + ]; + + TextInputFormatter getFormatter() { + if (symbol == kPylonSymbol) { + return AmountFormatter(maxDigits: kMaxPriceLength); + } + return AmountFormatter(maxDigits: kMaxPriceLength, isDecimal: true); + } + + @override + String toString() { + return 'Denom{name: $name, symbol: $symbol, icon: $icon}'; + } + + Widget getIconWidget() { + switch (symbol) { + case kUsdSymbol: + case kPylonSymbol: + case kAtomSymbol: + case kEuroSymbol: + case kJunoSymbol: + return Image.asset( + icon, + height: 20.h, + fit: BoxFit.contain, + width: 20.w, + ); + case kEthereumSymbol: + return Container( + padding: EdgeInsets.symmetric(vertical: isTablet ? 5.h : 10.h), + child: Image.asset( + icon, + height: 20.h, + )); + case kAgoricSymbol: + return Image.asset( + icon, + height: 15.h, + width: 15.w, + fit: BoxFit.contain, + ); + default: + return Image.asset(icon); + } + } + + String formatAmount({required String price}) { + switch (symbol) { + case kUsdSymbol: + case kPylonSymbol: + case kAtomSymbol: + case kEuroSymbol: + case kAgoricSymbol: + case kJunoSymbol: + return (double.parse(price.replaceAll(",", "").trim()) * kBigIntBase).toStringAsFixed(0); + case kEthereumSymbol: + return (double.parse(price.replaceAll(",", "").trim()) * kEthIntBase).toStringAsFixed(0); + default: + return (double.parse(price.replaceAll(",", "").trim()) * kBigIntBase).toStringAsFixed(0); + } + } +} diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 3e47e8cd04..65884fcc66 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -6,7 +6,7 @@ import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; -import 'package:evently/widgets/easel_text_field.dart'; +import 'package:evently/widgets/evently_text_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 2cddf9f312..8917dc63ec 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -8,7 +8,7 @@ import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; -import 'package:evently/widgets/easel_text_field.dart'; +import 'package:evently/widgets/evently_text_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index 66da0da1c4..6da878b98a 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -8,7 +8,7 @@ import 'package:evently/utils/screen_responsive.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; -import 'package:evently/widgets/easel_text_field.dart'; +import 'package:evently/widgets/evently_text_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 5bdbc6c110..61360c9ac6 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -1,15 +1,260 @@ + +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/evently_provider.dart'; +import 'package:evently/screens/custom_widgets/step_labels.dart'; +import 'package:evently/screens/custom_widgets/steps_indicator.dart'; +import 'package:evently/utils/amount_formatter.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/screen_responsive.dart'; +import 'package:evently/utils/space_utils.dart'; +import 'package:evently/viewmodels/create_event_viewmodel.dart'; +import 'package:evently/widgets/clipped_button.dart'; +import 'package:evently/widgets/evently_price_input_field.dart'; +import 'package:evently/widgets/evently_text_field.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get_it/get_it.dart'; +import 'package:provider/provider.dart'; + +import '../generated/locale_keys.g.dart'; class PriceScreen extends StatefulWidget { - const PriceScreen({super.key}); + const PriceScreen({Key? key}) : super(key: key); @override State createState() => _PriceScreenState(); } class _PriceScreenState extends State { + final _formKey = GlobalKey(); + final ValueNotifier _priceFieldError = ValueNotifier(""); + + @override + void dispose() { + _formKey.currentState?.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + } + @override Widget build(BuildContext context) { - return const Placeholder(); + final homeViewModel = context.watch(); + + return Scaffold( + body: SingleChildScrollView( + child: Consumer(builder: (_, provider, __) { + return Form( + key: _formKey, + child: Column( + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: homeViewModel.currentStep), + const VerticalSpace(5), + StepLabels(currentPage: homeViewModel.currentPage, currentStep: homeViewModel.currentStep), + const VerticalSpace(10), + const VerticalSpace(20), + Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: ValueListenableBuilder( + valueListenable: homeViewModel.currentPage, + builder: (_, int currentPage, __) => Padding( + padding: EdgeInsets.only(left: 10.sp), + child: IconButton( + onPressed: () { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + homeViewModel.previousPage(); + }, + icon: const Icon( + Icons.arrow_back_ios, + color: EventlyAppTheme.kGrey, + ), + )), + )), + ValueListenableBuilder( + valueListenable: homeViewModel.currentPage, + builder: (_, int currentPage, __) { + return Text( + homeViewModel.pageTitles[homeViewModel.currentPage.value], + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 18.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kDarkText), + ); + }, + ), + ], + ), + ScreenResponsive( + mobileScreen: (context) => const VerticalSpace(6), + tabletScreen: (context) => const VerticalSpace(30), + ), + VerticalSpace(10.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + LocaleKeys.is_free_drop.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), + ), + SizedBox( + height: 10.h, + ), + Row(children: [ + InkWell( + onTap: () { + // provider.updateIsFreeDropStatus(FreeDrop.yes); + }, + child: Container( + width: 140.w, + height: 30.h, + decoration: BoxDecoration( + color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kBlue : EventlyAppTheme.kTransparent, + border: Border.all(color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kBlue : EventlyAppTheme.kBlack, width: 2.w), + ), + child: Center( + child: Text( + LocaleKeys.yes.tr(), + style: TextStyle( + color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kWhite : EventlyAppTheme.kBlack, + ), + ), + ), + ), + ), + SizedBox( + width: 30.w, + ), + InkWell( + onTap: () { + + }, + child: Container( + width: 140.w, + height: 30.h, + decoration: BoxDecoration( + color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kBlue : EventlyAppTheme.kTransparent, + border: Border.all(color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kBlue : EventlyAppTheme.kBlack, width: 2.w), + ), + child: Center( + child: Text( + LocaleKeys.no.tr(), + style: TextStyle( + color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kWhite : EventlyAppTheme.kBlack, + ), + ), + ), + ), + ), + ]), + if (provider.isFreeDrop != FreeDrop.unselected) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (provider.isFreeDrop == FreeDrop.no) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + VerticalSpace(20.h), + EventlyPriceInputField( + key: ValueKey("${provider.selectedDenom.name}-amount"), + inputFormatters: [FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(kMaxPriceLength), provider.selectedDenom.getFormatter()], + controller: provider.priceController, + validator: (value) { + + }, + ), + + Text( + LocaleKeys.network_fee_listed_price_occur_on_chain.tr(), + style: TextStyle(color: EventlyAppTheme.kLightPurple, fontSize: 14.sp, fontWeight: FontWeight.w800), + ), + ], + ), + + ], + ), + if (provider.isFreeDrop == FreeDrop.unselected) + ScreenResponsive( + mobileScreen: (_) => VerticalSpace(0.38.sh), + tabletScreen: (_) => VerticalSpace(0.2.sh), + ), + if (provider.isFreeDrop == FreeDrop.yes) + ScreenResponsive( + mobileScreen: (_) => VerticalSpace(0.1.sh), + tabletScreen: (_) => VerticalSpace(0.05.sh), + ), + VerticalSpace(20.h), + ClippedButton( + title: LocaleKeys.continue_key.tr(), + bgColor: provider.isFreeDrop != FreeDrop.unselected ? EventlyAppTheme.kBlue : EventlyAppTheme.kPurple03, + textColor: EventlyAppTheme.kWhite, + onPressed: () async { + if (provider.isFreeDrop != FreeDrop.unselected) { + FocusScope.of(context).unfocus(); + validateAndUpdatePrice(moveNextPage: true); + } + }, + cuttingHeight: 15.h, + clipperType: ClipperType.bottomLeftTopRight, + isShadow: false, + fontWeight: FontWeight.w700, + ), + VerticalSpace(10.h), + Center( + child: InkWell( + + onTap: () { + if (provider.isFreeDrop == FreeDrop.unselected) { + Navigator.pop(context); + return; + } + FocusScope.of(context).unfocus(); + validateAndUpdatePrice(moveNextPage: false); + }, + child: Text( + LocaleKeys.save_as_draft.tr(), + style: TextStyle(color: EaselAppTheme.kLightGreyText, fontSize: 14.sp, fontWeight: FontWeight.w700), + ), + ), + ), + VerticalSpace(5.h), + ], + ), + ), + ], + ), + ); + }), + ), + ); + } + + Future validateAndUpdatePrice({required bool moveNextPage}) async { + final navigator = Navigator.of(context); + final HomeViewModel homeViewModel = context.read(); + + if (!_formKey.currentState!.validate()) { + return; + } + GetIt.I.get().changeSelectedCollection(CollectionType.draft); + + if (context.read().isFreeDrop == FreeDrop.yes) { + if (_royaltiesFieldError.value.isNotEmpty || _noOfEditionsFieldError.value.isNotEmpty) return; + await context.read().updateNftFromPrice(nft!.id!); + moveNextPage ? homeViewModel.nextPage() : navigator.pop(); + + return; + } + if (_royaltiesFieldError.value.isNotEmpty || _noOfEditionsFieldError.value.isNotEmpty || _priceFieldError.value.isNotEmpty) return; + await context.read().updateNftFromPrice(nft!.id!); + moveNextPage ? homeViewModel.nextPage() : navigator.pop(); } } diff --git a/evently/lib/utils/amount_formatter.dart b/evently/lib/utils/amount_formatter.dart new file mode 100644 index 0000000000..246092db6d --- /dev/null +++ b/evently/lib/utils/amount_formatter.dart @@ -0,0 +1,28 @@ +import 'package:flutter/services.dart'; +import 'package:intl/intl.dart'; + +class AmountFormatter extends TextInputFormatter { + AmountFormatter({required this.maxDigits, this.isDecimal = false}); + + int maxDigits; + bool isDecimal; + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, TextEditingValue newValue) { + maxDigits = 20; + if (newValue.selection.baseOffset == 0) { + return newValue; + } + if (newValue.selection.baseOffset > maxDigits) { + return oldValue; + } + final double value = double.parse(newValue.text); + final formatter = NumberFormat(isDecimal ? "#,##0.00" : "#,###", "en_US"); + final String newText = formatter.format(isDecimal ? value / 100 : value); + + return newValue.copyWith( + text: newText, + selection: TextSelection.collapsed(offset: newText.length)); + } +} diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 6dc9923896..ee5435b429 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -6,6 +6,27 @@ const kDraft = "Draft"; final List stepLabels = ["overview", "detail", "perks", "price"]; +const kPylonSymbol = 'upylon'; +const kUsdSymbol = 'ustripeusd'; +const kAtomSymbol = 'uatom'; +const kEuroSymbol = 'eeur'; +const kAgoricSymbol = 'urun'; +const kJunoSymbol = 'ujunox'; +const String kEthereumSymbol = "weth-wei"; + +const kPylonText = 'Pylon'; +const kUSDText = 'USD'; +const kAtomText = 'Atom'; +const kEurText = 'EEur'; +const kAgoricText = 'Agoric'; +const kJunoText = 'Juno'; +const kEthereum = "Ethereum"; + +const kMaxPriceLength = 14; +const int kBigIntBase = 1000000; +const int kEthIntBase = 1000000000000000000; + + /// ```SVG assets class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; @@ -22,4 +43,7 @@ class PngUtils { static const kTextFieldMultiLine = 'assets/images/text_field_multi_line.png'; static const kDarkPurpleSingleLine = 'assets/images/svg/dark_purple_single_line.png'; static const kLightPurpleSingleLine = 'assets/images/svg/light_purple_single_line.png'; + static const kTextFieldButton = 'assets/images/text_field_button.png'; + static const kIconDenomUsd = 'assets/images/denom_usd.png'; + static const kIconDenomPylon = 'assets/images/denom_pylon.png'; } diff --git a/evently/lib/widgets/evently_price_input_field.dart b/evently/lib/widgets/evently_price_input_field.dart new file mode 100644 index 0000000000..6ecacec8dc --- /dev/null +++ b/evently/lib/widgets/evently_price_input_field.dart @@ -0,0 +1,113 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/evently_provider.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; +import '../generated/locale_keys.g.dart'; +import '../main.dart'; +import '../models/denom.dart'; + +class EventlyPriceInputField extends StatelessWidget { + const EventlyPriceInputField({super.key, this.controller, this.validator, this.inputFormatters = const []}); + + final TextEditingController? controller; + final String? Function(String?)? validator; + final List inputFormatters; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + LocaleKeys.price.tr(), + textAlign: TextAlign.start, + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), + ), + SizedBox(height: 4.h), + Stack( + children: [ + Positioned( + child: Image.asset(PngUtils.kTextFieldSingleLine, width: 1.sw, height: isTablet ? 32.h : 40.h, fit: BoxFit.fill), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: TextFormField( + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kDarkText), + controller: controller, + validator: validator, + minLines: 1, + keyboardType: TextInputType.number, + inputFormatters: inputFormatters, + decoration: InputDecoration( + hintText: LocaleKeys.network_fee_listed_price_occur_on_chain.tr(), + hintStyle: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kGrey), + border: const OutlineInputBorder(borderSide: BorderSide.none), + floatingLabelBehavior: FloatingLabelBehavior.always, + contentPadding: EdgeInsets.fromLTRB(10.w, 0.h, 10.w, 0.h)))), + const _CurrencyDropDown() + ], + ), + ], + ), + ], + ); + } +} + +class _CurrencyDropDown extends StatelessWidget { + const _CurrencyDropDown({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (_, provider, __) => Stack( + alignment: Alignment.center, + children: [ + Positioned(left: 0, top: 0, bottom: 0, right: 0, child: Image.asset(PngUtils.kTextFieldButton, height: isTablet ? 32.h : 40.h, fit: BoxFit.fill)), + Container( + padding: EdgeInsets.only(left: 5.w), + height: isTablet ? 32.h : 40.h, + child: Align( + child: DropdownButton( + onTap: () { + FocusManager.instance.primaryFocus?.unfocus(); + }, + value: provider.selectedDenom.symbol, + iconSize: 0, + elevation: 0, + underline: const SizedBox(), + dropdownColor: EventlyAppTheme.kPurple03, + style: TextStyle(color: EventlyAppTheme.kWhite, fontSize: 18.sp, fontWeight: FontWeight.w400), + onChanged: (String? data) { + if (data != null) { + final value = provider.supportedDenomList.firstWhere((denom) => denom.symbol == data); + provider.priceController.clear(); + provider.setSelectedDenom(value); + } + }, + items: provider.supportedDenomList.map((Denom value) { + return DropdownMenuItem( + value: value.symbol, + child: Row(children: [ + value.getIconWidget(), + SizedBox(width: isTablet ? 10.w : 10.w), + Text( + value.name, + style: TextStyle(fontSize: isTablet ? 16.sp : 18.sp), + ), + SizedBox(width: isTablet ? 0.w : 5.w), + ]), + ); + }).toList(), + ), + )), + ], + )); + } +} diff --git a/evently/lib/widgets/easel_text_field.dart b/evently/lib/widgets/evently_text_field.dart similarity index 100% rename from evently/lib/widgets/easel_text_field.dart rename to evently/lib/widgets/evently_text_field.dart From 3716f87f337f35d9805bdb74e9c0e2dbaa30c960 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 11:09:01 +0500 Subject: [PATCH 033/161] feat: evently provider --- evently/lib/evently_provider.dart | 9 ++--- evently/lib/main.dart | 52 +++++++++++++++------------ evently/lib/screens/price_screen.dart | 52 ++++----------------------- evently/lib/utils/di/di.config.dart | 2 ++ 4 files changed, 41 insertions(+), 74 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index d742f7c69c..2d4de74169 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -1,13 +1,11 @@ - - - import 'package:evently/models/denom.dart'; import 'package:flutter/cupertino.dart'; +import 'package:injectable/injectable.dart'; enum FreeDrop { yes, no, unselected } +@lazySingleton class EventlyProvider extends ChangeNotifier { - Denom _selectedDenom = Denom.availableDenoms.first; Denom get selectedDenom => _selectedDenom; @@ -22,5 +20,4 @@ class EventlyProvider extends ChangeNotifier { _selectedDenom = value; notifyListeners(); } - -} \ No newline at end of file +} diff --git a/evently/lib/main.dart b/evently/lib/main.dart index f9ec0db562..34b7bcdbdb 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:ui'; import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/evently_provider.dart'; import 'package:evently/screens/create_event.dart'; import 'package:evently/screens/event_hub/event_hub_screen.dart'; import 'package:evently/screens/splash_screen.dart'; @@ -9,7 +10,7 @@ import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/route_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; - +import 'package:provider/provider.dart'; bool isTablet = false; @@ -45,28 +46,33 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return ScreenUtilInit( - minTextAdapt: true, - builder: (BuildContext context, child) => MaterialApp( - builder: (context, widget) { - ScreenUtil.init(context); - return MediaQuery( - data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), - child: widget!, - ); - }, - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: context.locale, - title: 'Evently', - navigatorKey: navigatorKey, - theme: EventlyAppTheme.theme(context), - initialRoute: '/', - routes: { - '/': (context) => const SplashScreen(), - RouteUtil.kRouteEventHub: (context) => const EventHubScreen(), - RouteUtil.createEvent: (context) => const CreateEvent(), - }, + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => sl()), + ], + child: ScreenUtilInit( + minTextAdapt: true, + builder: (BuildContext context, child) => MaterialApp( + builder: (context, widget) { + ScreenUtil.init(context); + return MediaQuery( + data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), + child: widget!, + ); + }, + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + title: 'Evently', + navigatorKey: navigatorKey, + theme: EventlyAppTheme.theme(context), + initialRoute: '/', + routes: { + '/': (context) => const SplashScreen(), + RouteUtil.kRouteEventHub: (context) => const EventHubScreen(), + RouteUtil.createEvent: (context) => const CreateEvent(), + }, + ), ), ); } diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 61360c9ac6..13d0fe61e8 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -1,9 +1,7 @@ - import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; import 'package:evently/screens/custom_widgets/steps_indicator.dart'; -import 'package:evently/utils/amount_formatter.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/screen_responsive.dart'; @@ -11,17 +9,15 @@ import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; import 'package:evently/widgets/evently_price_input_field.dart'; -import 'package:evently/widgets/evently_text_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get_it/get_it.dart'; import 'package:provider/provider.dart'; import '../generated/locale_keys.g.dart'; class PriceScreen extends StatefulWidget { - const PriceScreen({Key? key}) : super(key: key); + const PriceScreen({super.key}); @override State createState() => _PriceScreenState(); @@ -29,7 +25,7 @@ class PriceScreen extends StatefulWidget { class _PriceScreenState extends State { final _formKey = GlobalKey(); - final ValueNotifier _priceFieldError = ValueNotifier(""); + @override void dispose() { @@ -133,9 +129,7 @@ class _PriceScreenState extends State { width: 30.w, ), InkWell( - onTap: () { - - }, + onTap: () {}, child: Container( width: 140.w, height: 30.h, @@ -168,17 +162,15 @@ class _PriceScreenState extends State { inputFormatters: [FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(kMaxPriceLength), provider.selectedDenom.getFormatter()], controller: provider.priceController, validator: (value) { - + return null; }, ), - Text( LocaleKeys.network_fee_listed_price_occur_on_chain.tr(), style: TextStyle(color: EventlyAppTheme.kLightPurple, fontSize: 14.sp, fontWeight: FontWeight.w800), ), ], ), - ], ), if (provider.isFreeDrop == FreeDrop.unselected) @@ -199,7 +191,6 @@ class _PriceScreenState extends State { onPressed: () async { if (provider.isFreeDrop != FreeDrop.unselected) { FocusScope.of(context).unfocus(); - validateAndUpdatePrice(moveNextPage: true); } }, cuttingHeight: 15.h, @@ -210,18 +201,10 @@ class _PriceScreenState extends State { VerticalSpace(10.h), Center( child: InkWell( - - onTap: () { - if (provider.isFreeDrop == FreeDrop.unselected) { - Navigator.pop(context); - return; - } - FocusScope.of(context).unfocus(); - validateAndUpdatePrice(moveNextPage: false); - }, + onTap: () {}, child: Text( - LocaleKeys.save_as_draft.tr(), - style: TextStyle(color: EaselAppTheme.kLightGreyText, fontSize: 14.sp, fontWeight: FontWeight.w700), + LocaleKeys.save_draft.tr(), + style: TextStyle(color: EventlyAppTheme.kLightGreyText, fontSize: 14.sp, fontWeight: FontWeight.w700), ), ), ), @@ -236,25 +219,4 @@ class _PriceScreenState extends State { ), ); } - - Future validateAndUpdatePrice({required bool moveNextPage}) async { - final navigator = Navigator.of(context); - final HomeViewModel homeViewModel = context.read(); - - if (!_formKey.currentState!.validate()) { - return; - } - GetIt.I.get().changeSelectedCollection(CollectionType.draft); - - if (context.read().isFreeDrop == FreeDrop.yes) { - if (_royaltiesFieldError.value.isNotEmpty || _noOfEditionsFieldError.value.isNotEmpty) return; - await context.read().updateNftFromPrice(nft!.id!); - moveNextPage ? homeViewModel.nextPage() : navigator.pop(); - - return; - } - if (_royaltiesFieldError.value.isNotEmpty || _noOfEditionsFieldError.value.isNotEmpty || _priceFieldError.value.isNotEmpty) return; - await context.read().updateNftFromPrice(nft!.id!); - moveNextPage ? homeViewModel.nextPage() : navigator.pop(); - } } diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index af886c649e..a3e42a76dd 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -8,6 +8,7 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:evently/evently_provider.dart' as _i4; import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; import 'package:get_it/get_it.dart' as _i1; import 'package:injectable/injectable.dart' as _i2; @@ -25,6 +26,7 @@ extension GetItInjectableX on _i1.GetIt { ); gh.lazySingleton<_i3.CreateEventViewModel>( () => _i3.CreateEventViewModel()); + gh.lazySingleton<_i4.EventlyProvider>(() => _i4.EventlyProvider()); return this; } } From 3796366c7923bc26e7e3fcb849a78b1d2a1fc6c2 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 11:17:55 +0500 Subject: [PATCH 034/161] feat: price screen --- evently/lib/evently_provider.dart | 4 ++-- evently/lib/widgets/evently_price_input_field.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 2d4de74169..1e0d13c90a 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -10,11 +10,11 @@ class EventlyProvider extends ChangeNotifier { Denom get selectedDenom => _selectedDenom; - List supportedDenomList = []; + List supportedDenomList = Denom.availableDenoms; TextEditingController priceController = TextEditingController(); - FreeDrop isFreeDrop = FreeDrop.unselected; + FreeDrop isFreeDrop = FreeDrop.no; void setSelectedDenom(Denom value) { _selectedDenom = value; diff --git a/evently/lib/widgets/evently_price_input_field.dart b/evently/lib/widgets/evently_price_input_field.dart index 6ecacec8dc..5e59b98522 100644 --- a/evently/lib/widgets/evently_price_input_field.dart +++ b/evently/lib/widgets/evently_price_input_field.dart @@ -45,7 +45,7 @@ class EventlyPriceInputField extends StatelessWidget { keyboardType: TextInputType.number, inputFormatters: inputFormatters, decoration: InputDecoration( - hintText: LocaleKeys.network_fee_listed_price_occur_on_chain.tr(), + hintText: "\$21.99", hintStyle: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kGrey), border: const OutlineInputBorder(borderSide: BorderSide.none), floatingLabelBehavior: FloatingLabelBehavior.always, From 9914ac8cb910c117213edaa06dffeaf22e849068 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 11:24:43 +0500 Subject: [PATCH 035/161] feat: event price --- evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/lib/generated/locale_keys.g.dart | 1 + evently/lib/screens/price_screen.dart | 52 +++++++++++++++++++----- 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index dfa99e77e0..ab097c5b84 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -38,5 +38,6 @@ "yes": "Yes", "no" : "No", "number_tickets": "Number of Tickets", - "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain" + "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", + "maximum_1000": "1,000 maximum" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index dfa99e77e0..ab097c5b84 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -38,5 +38,6 @@ "yes": "Yes", "no" : "No", "number_tickets": "Number of Tickets", - "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain" + "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", + "maximum_1000": "1,000 maximum" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index dfa99e77e0..ab097c5b84 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -38,5 +38,6 @@ "yes": "Yes", "no" : "No", "number_tickets": "Number of Tickets", - "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain" + "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", + "maximum_1000": "1,000 maximum" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 47bf5b4a74..021910113a 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -41,5 +41,6 @@ abstract class LocaleKeys { static const no = 'no'; static const number_tickets = 'number_tickets'; static const network_fee_listed_price_occur_on_chain = 'network_fee_listed_price_occur_on_chain'; + static const maximum_1000 = 'maximum_1000'; } diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 13d0fe61e8..a09420f03d 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -9,6 +9,7 @@ import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/clipped_button.dart'; import 'package:evently/widgets/evently_price_input_field.dart'; +import 'package:evently/widgets/evently_text_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -25,7 +26,7 @@ class PriceScreen extends StatefulWidget { class _PriceScreenState extends State { final _formKey = GlobalKey(); - + final ValueNotifier _numberOfTicketsFieldError = ValueNotifier(""); @override void dispose() { @@ -169,20 +170,49 @@ class _PriceScreenState extends State { LocaleKeys.network_fee_listed_price_occur_on_chain.tr(), style: TextStyle(color: EventlyAppTheme.kLightPurple, fontSize: 14.sp, fontWeight: FontWeight.w800), ), + VerticalSpace(20.h), + EventlyTextField( + label: LocaleKeys.number_tickets.tr(), + controller: TextEditingController(), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + ValueListenableBuilder( + valueListenable: _numberOfTicketsFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + Align( + alignment: Alignment.centerRight, + child: Text( + LocaleKeys.maximum_1000.tr(), + style: TextStyle(color: EventlyAppTheme.kLightPurple, fontSize: 14.sp, fontWeight: FontWeight.w800), + textAlign: TextAlign.right, + ), + ), ], ), ], ), - if (provider.isFreeDrop == FreeDrop.unselected) - ScreenResponsive( - mobileScreen: (_) => VerticalSpace(0.38.sh), - tabletScreen: (_) => VerticalSpace(0.2.sh), - ), - if (provider.isFreeDrop == FreeDrop.yes) - ScreenResponsive( - mobileScreen: (_) => VerticalSpace(0.1.sh), - tabletScreen: (_) => VerticalSpace(0.05.sh), - ), + ScreenResponsive( + mobileScreen: (_) => VerticalSpace(0.1.sh), + tabletScreen: (_) => VerticalSpace(0.05.sh), + ), VerticalSpace(20.h), ClippedButton( title: LocaleKeys.continue_key.tr(), From 8d0cac010ff0f646951aeab6826c2740178956c7 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 11:42:32 +0500 Subject: [PATCH 036/161] feat: host ticket preview --- evently/lib/main.dart | 4 +++- evently/lib/screens/create_event.dart | 2 +- .../lib/screens/event_hub/event_hub_screen.dart | 4 ++-- evently/lib/screens/host_view_ticket_preview.dart | 15 +++++++++++++++ evently/lib/screens/price_screen.dart | 5 ++++- evently/lib/utils/route_util.dart | 3 ++- 6 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 evently/lib/screens/host_view_ticket_preview.dart diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 34b7bcdbdb..91402dc708 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; import 'package:evently/screens/create_event.dart'; import 'package:evently/screens/event_hub/event_hub_screen.dart'; +import 'package:evently/screens/host_view_ticket_preview.dart'; import 'package:evently/screens/splash_screen.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/di/di.dart'; @@ -70,7 +71,8 @@ class MyApp extends StatelessWidget { routes: { '/': (context) => const SplashScreen(), RouteUtil.kRouteEventHub: (context) => const EventHubScreen(), - RouteUtil.createEvent: (context) => const CreateEvent(), + RouteUtil.kCreateEvent: (context) => const CreateEvent(), + RouteUtil.kHostTicketPreview: (context) => const HostTicketPreview(), }, ), ), diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 83a818cfa9..a9ab0a8633 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -49,7 +49,7 @@ class CreateEventContent extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), onPageChanged: (int page) { createEventViewModel.currentPage.value = page; - final map = {0: 0, 1: 1, 2: 2, 3: 2}; + final map = {0: 0, 1: 1, 2: 2, 3: 3}; createEventViewModel.currentStep.value = map[page]!; }, itemBuilder: (BuildContext context, int index) { diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index f482f7b09a..b279718b90 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -62,7 +62,7 @@ class _EventHubScreenState extends State { mainAxisAlignment: MainAxisAlignment.end, children: [ InkWell( - onTap: () => Navigator.of(context).pushNamed(RouteUtil.createEvent), + onTap: () => Navigator.of(context).pushNamed(RouteUtil.kCreateEvent), child: DecoratedBox( decoration: BoxDecoration( color: EventlyAppTheme.kpurpleDark, @@ -145,7 +145,7 @@ class _EventHubScreenState extends State { bgColor: EventlyAppTheme.kBlue, textColor: EventlyAppTheme.kWhite, onPressed: () { - Navigator.of(context).pushNamed(RouteUtil.createEvent); + Navigator.of(context).pushNamed(RouteUtil.kCreateEvent); }, cuttingHeight: 15.h, clipperType: ClipperType.bottomLeftTopRight, diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart new file mode 100644 index 0000000000..6cac5b1ab8 --- /dev/null +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class HostTicketPreview extends StatefulWidget { + const HostTicketPreview({super.key}); + + @override + State createState() => _HostTicketPreviewState(); +} + +class _HostTicketPreviewState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index a09420f03d..86235af21e 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -4,6 +4,7 @@ import 'package:evently/screens/custom_widgets/step_labels.dart'; import 'package:evently/screens/custom_widgets/steps_indicator.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/screen_responsive.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; @@ -231,7 +232,9 @@ class _PriceScreenState extends State { VerticalSpace(10.h), Center( child: InkWell( - onTap: () {}, + onTap: () { + Navigator.of(context).pushNamed(RouteUtil.kHostTicketPreview); + }, child: Text( LocaleKeys.save_draft.tr(), style: TextStyle(color: EventlyAppTheme.kLightGreyText, fontSize: 14.sp, fontWeight: FontWeight.w700), diff --git a/evently/lib/utils/route_util.dart b/evently/lib/utils/route_util.dart index 03058cf14b..d4c1cdb492 100644 --- a/evently/lib/utils/route_util.dart +++ b/evently/lib/utils/route_util.dart @@ -2,5 +2,6 @@ class RouteUtil { RouteUtil(); static String kRouteEventHub = "/eventHub"; - static String createEvent = "/createEvent"; + static String kCreateEvent = "/createEvent"; + static String kHostTicketPreview = "/hostTicketPreview"; } From f53b6563a136adbee89626d2eda6be2a7fad171a Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 12:22:16 +0500 Subject: [PATCH 037/161] feat: host preview screen --- evently/assets/images/host_preview.png | Bin 0 -> 5098 bytes evently/assets/images/phantom.png | Bin 0 -> 74369 bytes .../lib/screens/host_view_ticket_preview.dart | 150 +++++++++++++++++- evently/lib/screens/price_screen.dart | 4 +- evently/lib/utils/constants.dart | 5 +- 5 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 evently/assets/images/host_preview.png create mode 100644 evently/assets/images/phantom.png diff --git a/evently/assets/images/host_preview.png b/evently/assets/images/host_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..780b95cc1fa56c7d1f2a7205be39df703cfe9f73 GIT binary patch literal 5098 zcmeHL`BxK09v`5JMFF>11Ub?bDN;f}i4Y7B6(wn_%8_6WphB>QLq#Ov3M6YHkbp6elaLMj>;4D(_+j4n&G*fFGxMF#=QE#~ zYsbF}v$on{1pu%<`t9K;0L;h$SYS78G-Y>vO%%I1~i4FK4YjvhYveZHA|^4Q~A?8PpHlwkET=YzGI`0kAx zv76j}Y5n83JEin-SOj(y_BLk(W)&Uqmz;)#_Ug)4M_PU#J2D~I_8Tv;A-4LjHyv>^ z?#cheKc=QMT#6E^OYjTJzZ!R`e>tktci&R8Jm>+~+|}Yb_4(9^d$y>Rk1tvaQg+xz ziZyW8+uWOSvo1NE7F>d?sE@!Dr>4Gt!#Lt4{yIHO)DQnI5|aUNkH)E7r(wZ}uKpF=dXGO`*{*=QlCmLOOf zQQN?AfXfRc$n3;=p>YMS&qpid+G&k48qQIVu8wy)kmWI~VEd}O635LE z+9j=iGDcXoHrs*)?`(J?$)FVXTni5WJ_+&RSw-0;#itlV7rUL1W~##~n4W_Bgf}*$ zScqZ1L)>ePTr@OEXZZDs`eXjy%i7{@57g}i?z2m?UgEhCTf)@-kapO8K4xeO#)|9H zPQulojPr_#Y!n0#gKC00_D>O&J)$_}Z2LSC$@C+8+AG`a`=q0%n^#Me>AOKlhy*jD z6g=I`X=Ai7cIBRx_Bxjb?f^uVjG2>?p@%`L{mW>@GLdGb(59E=yrHXa=v_jPD*g}+ z$>ZNnCgw`5!%qN6mUK`aTXklfGm$buFTOc7HYj9FnNN!|>QZIryKieL1y^WA^^dhp zEtLu2<9!I5$zb{$@j_oFK-WWznt*aoNI+ESIyu3OShVwj^TOFN zVs3gbW;8B~jpS$tlBgMAqCs+iwW{l^yybCPZ5+|T6RF$>2D__e|3=HGe<*rd3kuQ) z4)_ny5XHLR&w+{LN*8?(12@ytI+HB2r^p=Jm(&v4D?=lA~i)J$8WNcCq@lh z>V{~!*WXDzxMFC2y7RqI zYpyEo%sf(0L9J3OO2;umnW(L-=^f`w9)qo2fMSwnLCn2d9+3D6!xZ9&tFB~uELY+# zQPQ^Xq5v;|GzyE)rs4gzOAOw;!qPXH0@atcrS5!n0yCrv9ox+h7Ul^%x&XBMNp#Vv zY#7-+=9KTdyu|)UoH@F~m53Yi<^^!SfO3`NjmMyz3$eG%CZp@_zpJ5c7xd6>! z50LqXC@-U+y>}qy-4+ux`=c)HL8)@3%fuv4P<39xBq)#?U+?o*@I>ilr>*8tA;%cl zi7ZRLV)m41vc{bzgS5651Y4O>a`y|s%hi6WLkI0$%f*Zr;1!}Gi_-UF>ID4=?K9I3 zXVJVde&RpwP?D(004iKbVB$7Szfk2V8qRRHVCd;7W;p$tIqaRxst6@K?xtQR5XWi+ z0@Cb*f0;@sZlssAFqi~>3;B6nGRrh6tTy^bv&{N}lUU))!G^)THr@`=e@bi#M66I| zKCLQR=&1K`Mkh zuy(bv?Mr9)_qSS=GRK|QnGXWOiWZdRQ;PIttc_u&xFFD~F#vuAi8l_2BwkF@4Q}qes3w J+;j+i=|8=%-ev#* literal 0 HcmV?d00001 diff --git a/evently/assets/images/phantom.png b/evently/assets/images/phantom.png new file mode 100644 index 0000000000000000000000000000000000000000..135a21e20c79375e53e56dd1a11c19c7f981ef5f GIT binary patch literal 74369 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 z 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 nJ9k~ { @override Widget build(BuildContext context) { - return const Placeholder(); + return ColoredBox( + color: EventlyAppTheme.kWhite, + child: SafeArea( + child: Scaffold( + backgroundColor: EventlyAppTheme.kWhite, + body: Padding( + padding: EdgeInsets.only(top: 20.h), + child: Stack( + children: [ + Image.asset( + PngUtils.kHostPreview, + width: double.infinity, + ), + Padding( + padding: EdgeInsets.only(left: 25.h, right: 27.w), + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topRight: Radius.circular(10.r), + topLeft: Radius.circular(10.r), + ), + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Image.asset( + PngUtils.kPhantom, + fit: BoxFit.contain, + ), + Padding( + padding: EdgeInsets.only(left: 10.w, bottom: 10.h), + child: Text( + 'The Phantom of the Opera', + style: TextStyle(fontSize: 20.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w700), + ), + ) + ], + ), + ), + VerticalSpace(10.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'DATE', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + 'Jul 04, 2022', + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Time', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + '7:15 PM', + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], + ), + ], + ), + ), + VerticalSpace(20.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'LOCATION', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 1.sw / 2.4), + child: Text( + '245 W 44th St New York, NY 10036', + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + maxLines: 2, + ), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'SEAT', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '6A', + style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + Text( + 'Room 3', + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ) + ], + ), + ], + ), + ], + ), + ), + + + ], + ), + ) + ], + ), + ), + ), + ), + ); } } diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 86235af21e..61407f0385 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -220,9 +220,7 @@ class _PriceScreenState extends State { bgColor: provider.isFreeDrop != FreeDrop.unselected ? EventlyAppTheme.kBlue : EventlyAppTheme.kPurple03, textColor: EventlyAppTheme.kWhite, onPressed: () async { - if (provider.isFreeDrop != FreeDrop.unselected) { - FocusScope.of(context).unfocus(); - } + Navigator.of(context).pushNamed(RouteUtil.kHostTicketPreview); }, cuttingHeight: 15.h, clipperType: ClipperType.bottomLeftTopRight, diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index ee5435b429..a7b57b7012 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -26,7 +26,6 @@ const kMaxPriceLength = 14; const int kBigIntBase = 1000000; const int kEthIntBase = 1000000000000000000; - /// ```SVG assets class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; @@ -46,4 +45,8 @@ class PngUtils { static const kTextFieldButton = 'assets/images/text_field_button.png'; static const kIconDenomUsd = 'assets/images/denom_usd.png'; static const kIconDenomPylon = 'assets/images/denom_pylon.png'; + static const kHostPreview = "assets/images/host_preview.png"; + /// will remove this variable for ui dev + /// i need this variable to be used + static const kPhantom = "assets/images/phantom.png"; } From a1f34bd65ba11bd78447c27f44bb71e221bdcc44 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 12:37:06 +0500 Subject: [PATCH 038/161] feat: dotted line --- evently/assets/images/dotted_line.png | Bin 0 -> 364 bytes evently/assets/images/svg/diamond.svg | 3 ++ .../lib/screens/host_view_ticket_preview.dart | 45 ++++++++++++++++-- evently/lib/utils/constants.dart | 2 + evently/lib/utils/evently_app_theme.dart | 2 +- 5 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 evently/assets/images/dotted_line.png create mode 100644 evently/assets/images/svg/diamond.svg diff --git a/evently/assets/images/dotted_line.png b/evently/assets/images/dotted_line.png new file mode 100644 index 0000000000000000000000000000000000000000..a42539bee300815f1d3c40c4eb9e071f4ec34fed GIT binary patch literal 364 zcmV-y0h9iTP)x1vXcunRT!v|>tkykr{@s`YYhWGA!&+J}{ z8~iy+i$p%mbJkJY5qT|cusBQ0MBY1`w=wRw{LPh=l$4a59^Kjc9d3`=^4YMN%qPPq zG9L{e$$T(;xQN%?I(j#e%vuC}l^D~*Z;@z?M-^B+ry$P81M+`3j0000< KMNUMnLSTYC&Y9l; literal 0 HcmV?d00001 diff --git a/evently/assets/images/svg/diamond.svg b/evently/assets/images/svg/diamond.svg new file mode 100644 index 0000000000..4e874f6959 --- /dev/null +++ b/evently/assets/images/svg/diamond.svg @@ -0,0 +1,3 @@ + + + diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 1951414c69..310e827106 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; class HostTicketPreview extends StatefulWidget { const HostTicketPreview({super.key}); @@ -100,6 +101,7 @@ class _HostTicketPreviewState extends State { padding: EdgeInsets.only(left: 10.w, right: 30.h), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -148,11 +150,48 @@ class _HostTicketPreviewState extends State { ], ), ), - - + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'PERKS', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Row( + children: [ + SvgPicture.asset(SVGUtils.kDiamond), + SizedBox(width: 5.w), + Text( + 'x 3', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), + ), + SizedBox(width: 5.w), + Text( + 'Redeem', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kDarkGreen, fontWeight: FontWeight.bold), + ) + ], + ) + ], + ), + ], + ), + ), + VerticalSpace(13.h), + Image.asset(PngUtils.kDottedLine) ], ), - ) + ), + + ], ), ), diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index a7b57b7012..8c74ed7d7f 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -34,6 +34,7 @@ class SVGUtils { static const kGift = "assets/images/svg/gift.svg"; static const kDrinks = "assets/images/svg/drinks.svg"; static const kMinus = "assets/images/svg/minus.svg"; + static const kDiamond = "assets/images/svg/diamond.svg"; } /// ```PNG assets @@ -49,4 +50,5 @@ class PngUtils { /// will remove this variable for ui dev /// i need this variable to be used static const kPhantom = "assets/images/phantom.png"; + static const kDottedLine = "assets/images/dotted_line.png"; } diff --git a/evently/lib/utils/evently_app_theme.dart b/evently/lib/utils/evently_app_theme.dart index 01924e6214..bf9c10a22a 100644 --- a/evently/lib/utils/evently_app_theme.dart +++ b/evently/lib/utils/evently_app_theme.dart @@ -28,7 +28,7 @@ class EventlyAppTheme { static const Color kPurple02 = Color(0xFF4534CE); static const Color kPurple03 = Color(0xFFCBC8F3); static const Color kLightPurple = Color(0xFFB6B6E8); - static const Color kDarkGreen = Color(0xFF3A8977); + static const Color kDarkGreen = Color(0xFF14FB00); static const Color kYellow = Color(0xFFF3BA2F); static const Color kLightYellow = Color(0xFFFED564); static const Color kLightRed = Color(0xFFEF4421); From 6b1798dd7c460442bc1c28a52efee68343c8731b Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 16:35:30 +0500 Subject: [PATCH 039/161] feat: host ticket preview --- evently/assets/images/svg/camera.svg | 3 + .../lib/screens/host_view_ticket_preview.dart | 311 ++++++++++-------- 2 files changed, 168 insertions(+), 146 deletions(-) create mode 100644 evently/assets/images/svg/camera.svg diff --git a/evently/assets/images/svg/camera.svg b/evently/assets/images/svg/camera.svg new file mode 100644 index 0000000000..c0d27cad39 --- /dev/null +++ b/evently/assets/images/svg/camera.svg @@ -0,0 +1,3 @@ + + + diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 310e827106..77854973d9 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -21,33 +21,36 @@ class _HostTicketPreviewState extends State { color: EventlyAppTheme.kWhite, child: SafeArea( child: Scaffold( - backgroundColor: EventlyAppTheme.kWhite, - body: Padding( - padding: EdgeInsets.only(top: 20.h), - child: Stack( - children: [ - Image.asset( - PngUtils.kHostPreview, - width: double.infinity, + body: SingleChildScrollView( + child: ClipRRect( + borderRadius: BorderRadius.circular(10.r), + child: Container( + margin: EdgeInsets.symmetric(vertical: 20.w, horizontal: 20.w), + // height: 1.sh, + // width: 1.sw, + decoration: BoxDecoration( + image: const DecorationImage(image: AssetImage(PngUtils.kHostPreview), fit: BoxFit.fitHeight), + borderRadius: BorderRadius.circular(10.r), ), - Padding( - padding: EdgeInsets.only(left: 25.h, right: 27.w), - child: Column( - children: [ - ClipRRect( + child: Column( + children: [ + // SizedBox(height: 20.h,), + Padding( + padding: const EdgeInsets.only(left: 3.0, right: 3.0, top: 16), + child: ClipRRect( borderRadius: BorderRadius.only( - topRight: Radius.circular(10.r), - topLeft: Radius.circular(10.r), + topRight: Radius.circular(14), + topLeft: Radius.circular(14), ), child: Stack( alignment: Alignment.bottomLeft, children: [ Image.asset( PngUtils.kPhantom, - fit: BoxFit.contain, + fit: BoxFit.cover, ), Padding( - padding: EdgeInsets.only(left: 10.w, bottom: 10.h), + padding: EdgeInsets.only(bottom: 10.h, left: 10.w), child: Text( 'The Phantom of the Opera', style: TextStyle(fontSize: 20.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w700), @@ -56,143 +59,159 @@ class _HostTicketPreviewState extends State { ], ), ), - VerticalSpace(10.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'DATE', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Text( - 'Jul 04, 2022', - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ), - ], - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'Time', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Text( - '7:15 PM', - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ), - ], - ), - ], - ), + ), + VerticalSpace(10.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'DATE', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + 'Jul 04, 2022', + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Time', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + '7:15 PM', + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], + ), + ], ), - VerticalSpace(20.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'LOCATION', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + VerticalSpace(20.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'LOCATION', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 1.sw / 2.4), + child: Text( + '245 W 44th St New York, NY 10036', + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + maxLines: 2, ), - SizedBox(height: 1.h), - ConstrainedBox( - constraints: BoxConstraints(maxWidth: 1.sw / 2.4), - child: Text( - '245 W 44th St New York, NY 10036', + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'SEAT', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '6A', + style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + Text( + 'Room 3', style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - maxLines: 2, + ) + ], + ), + ], + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'PERKS', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Row( + children: [ + SvgPicture.asset(SVGUtils.kDiamond), + SizedBox(width: 5.w), + Text( + 'x 3', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), ), - ), - ], - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'SEAT', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '6A', - style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ), - Text( - 'Room 3', - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ) - ], - ), - ], - ), - ], - ), + SizedBox(width: 5.w), + Text( + 'Redeem', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kDarkGreen, fontWeight: FontWeight.bold), + ) + ], + ) + ], + ), + ], ), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'PERKS', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Row( - children: [ - SvgPicture.asset(SVGUtils.kDiamond), - SizedBox(width: 5.w), - Text( - 'x 3', - style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), - ), - SizedBox(width: 5.w), - Text( - 'Redeem', - style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kDarkGreen, fontWeight: FontWeight.bold), - ) - ], - ) - ], - ), - ], - ), + ), + VerticalSpace(16.h), + Image.asset(PngUtils.kDottedLine), + VerticalSpace(40.h), + Text( + 'Scan QR to enter', + style: TextStyle( + color: EventlyAppTheme.kWhite, + fontWeight: FontWeight.bold, + fontSize: 15.sp, ), - VerticalSpace(13.h), - Image.asset(PngUtils.kDottedLine) - ], - ), - ), + ), + Container( + + decoration: BoxDecoration(color: EventlyAppTheme.kBlack), + width: 338, + height: 338, - ], + + ) + ], + ), + ), ), ), ), From 108c9015b80a52e4d9bcd3429853cb4ab7f4b659 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 16:38:29 +0500 Subject: [PATCH 040/161] feat: host view ticket preview --- evently/lib/screens/host_view_ticket_preview.dart | 11 ++++------- evently/lib/utils/constants.dart | 2 ++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 77854973d9..7cf8ef4d35 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -34,9 +34,8 @@ class _HostTicketPreviewState extends State { ), child: Column( children: [ - // SizedBox(height: 20.h,), Padding( - padding: const EdgeInsets.only(left: 3.0, right: 3.0, top: 16), + padding: const EdgeInsets.only(), child: ClipRRect( borderRadius: BorderRadius.only( topRight: Radius.circular(14), @@ -200,15 +199,13 @@ class _HostTicketPreviewState extends State { fontSize: 15.sp, ), ), + VerticalSpace(10.h), Container( - decoration: BoxDecoration(color: EventlyAppTheme.kBlack), width: 338, - height: 338, - - - ) + ), + SizedBox(height: 20.h), ], ), ), diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 8c74ed7d7f..a28399f439 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -35,6 +35,7 @@ class SVGUtils { static const kDrinks = "assets/images/svg/drinks.svg"; static const kMinus = "assets/images/svg/minus.svg"; static const kDiamond = "assets/images/svg/diamond.svg"; + static const kCamera = "assets/images/svg/camera.svg"; } /// ```PNG assets @@ -47,6 +48,7 @@ class PngUtils { static const kIconDenomUsd = 'assets/images/denom_usd.png'; static const kIconDenomPylon = 'assets/images/denom_pylon.png'; static const kHostPreview = "assets/images/host_preview.png"; + /// will remove this variable for ui dev /// i need this variable to be used static const kPhantom = "assets/images/phantom.png"; From 42db5206a34e1e65e6709a9d8c24174ba7ffcb6c Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 16:38:35 +0500 Subject: [PATCH 041/161] feat: host view ticket preview --- evently/lib/screens/host_view_ticket_preview.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 7cf8ef4d35..de2430bf61 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -37,7 +37,7 @@ class _HostTicketPreviewState extends State { Padding( padding: const EdgeInsets.only(), child: ClipRRect( - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topRight: Radius.circular(14), topLeft: Radius.circular(14), ), From 9bf97ead2449df6721251154eeb056ad9d58fb44 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 16:53:51 +0500 Subject: [PATCH 042/161] feat: basic flow ui implementation --- evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 4 +- evently/lib/generated/locale_keys.g.dart | 1 + .../lib/screens/host_view_ticket_preview.dart | 43 ++++++++++++++++--- evently/lib/utils/constants.dart | 1 - 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index ab097c5b84..0bfaed5329 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -39,5 +39,6 @@ "no" : "No", "number_tickets": "Number of Tickets", "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", - "maximum_1000": "1,000 maximum" + "maximum_1000": "1,000 maximum", + "publish": "Publish" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index ab097c5b84..0bfaed5329 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -39,5 +39,6 @@ "no" : "No", "number_tickets": "Number of Tickets", "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", - "maximum_1000": "1,000 maximum" + "maximum_1000": "1,000 maximum", + "publish": "Publish" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index ab097c5b84..0bfaed5329 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -39,5 +39,6 @@ "no" : "No", "number_tickets": "Number of Tickets", "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", - "maximum_1000": "1,000 maximum" + "maximum_1000": "1,000 maximum", + "publish": "Publish" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index dfa99e77e0..0bfaed5329 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -38,5 +38,7 @@ "yes": "Yes", "no" : "No", "number_tickets": "Number of Tickets", - "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain" + "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", + "maximum_1000": "1,000 maximum", + "publish": "Publish" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 021910113a..76941d2da7 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -42,5 +42,6 @@ abstract class LocaleKeys { static const number_tickets = 'number_tickets'; static const network_fee_listed_price_occur_on_chain = 'network_fee_listed_price_occur_on_chain'; static const maximum_1000 = 'maximum_1000'; + static const publish = 'publish'; } diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index de2430bf61..75178cb432 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -1,9 +1,11 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/space_utils.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:evently/widgets/clipped_button.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -21,13 +23,44 @@ class _HostTicketPreviewState extends State { color: EventlyAppTheme.kWhite, child: SafeArea( child: Scaffold( + bottomNavigationBar: Container( + padding: EdgeInsets.symmetric(horizontal: 30.w), + height: 110.h, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ClippedButton( + title: LocaleKeys.publish.tr(), + bgColor: EventlyAppTheme.kBlue, + textColor: EventlyAppTheme.kWhite, + onPressed: () { + Navigator.of(context).pushReplacementNamed(RouteUtil.kRouteEventHub); + }, + cuttingHeight: 15.h, + clipperType: ClipperType.bottomLeftTopRight, + isShadow: false, + fontWeight: FontWeight.w700, + ), + VerticalSpace(10.h), + Center( + child: InkWell( + onTap: () {}, + child: Text( + LocaleKeys.save_draft.tr(), + style: TextStyle(color: EventlyAppTheme.kLightGreyText, fontSize: 14.sp, fontWeight: FontWeight.w700), + ), + ), + ), + VerticalSpace(5.h), + ], + ), + ), body: SingleChildScrollView( child: ClipRRect( borderRadius: BorderRadius.circular(10.r), child: Container( margin: EdgeInsets.symmetric(vertical: 20.w, horizontal: 20.w), - // height: 1.sh, - // width: 1.sw, decoration: BoxDecoration( image: const DecorationImage(image: AssetImage(PngUtils.kHostPreview), fit: BoxFit.fitHeight), borderRadius: BorderRadius.circular(10.r), @@ -201,7 +234,7 @@ class _HostTicketPreviewState extends State { ), VerticalSpace(10.h), Container( - decoration: BoxDecoration(color: EventlyAppTheme.kBlack), + decoration: const BoxDecoration(color: EventlyAppTheme.kBlack), width: 338, height: 338, ), diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index a28399f439..a0cc943487 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -48,7 +48,6 @@ class PngUtils { static const kIconDenomUsd = 'assets/images/denom_usd.png'; static const kIconDenomPylon = 'assets/images/denom_pylon.png'; static const kHostPreview = "assets/images/host_preview.png"; - /// will remove this variable for ui dev /// i need this variable to be used static const kPhantom = "assets/images/phantom.png"; From 2ee40644137fdf4eadb38f00733e4bbe603d1a05 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 8 May 2024 17:28:26 +0500 Subject: [PATCH 043/161] feat: response screen --- evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/generated/locale_keys.g.dart | 1 + evently/lib/main.dart | 4 ++ evently/lib/screens/buyer_status_screen.dart | 47 ++++++++++++++++++++ evently/lib/screens/splash_screen.dart | 2 +- evently/lib/utils/evently_app_theme.dart | 1 + evently/lib/utils/route_util.dart | 1 + 10 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 evently/lib/screens/buyer_status_screen.dart diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 0bfaed5329..4db952a64d 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -40,5 +40,6 @@ "number_tickets": "Number of Tickets", "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", "maximum_1000": "1,000 maximum", - "publish": "Publish" + "publish": "Publish", + "enjoy_the_event": "Enjoy the event!" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 0bfaed5329..4db952a64d 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -40,5 +40,6 @@ "number_tickets": "Number of Tickets", "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", "maximum_1000": "1,000 maximum", - "publish": "Publish" + "publish": "Publish", + "enjoy_the_event": "Enjoy the event!" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 0bfaed5329..4db952a64d 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -40,5 +40,6 @@ "number_tickets": "Number of Tickets", "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", "maximum_1000": "1,000 maximum", - "publish": "Publish" + "publish": "Publish", + "enjoy_the_event": "Enjoy the event!" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 0bfaed5329..4db952a64d 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -40,5 +40,6 @@ "number_tickets": "Number of Tickets", "network_fee_listed_price_occur_on_chain": "A network fee of 10% the listed price is required for all transactions that occur on-chain", "maximum_1000": "1,000 maximum", - "publish": "Publish" + "publish": "Publish", + "enjoy_the_event": "Enjoy the event!" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 76941d2da7..e4d53c71c1 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -43,5 +43,6 @@ abstract class LocaleKeys { static const network_fee_listed_price_occur_on_chain = 'network_fee_listed_price_occur_on_chain'; static const maximum_1000 = 'maximum_1000'; static const publish = 'publish'; + static const enjoy_the_event = 'enjoy_the_event'; } diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 91402dc708..12a8646ba1 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; +import 'package:evently/screens/buyer_status_screen.dart'; import 'package:evently/screens/create_event.dart'; import 'package:evently/screens/event_hub/event_hub_screen.dart'; import 'package:evently/screens/host_view_ticket_preview.dart'; @@ -54,9 +55,11 @@ class MyApp extends StatelessWidget { child: ScreenUtilInit( minTextAdapt: true, builder: (BuildContext context, child) => MaterialApp( + builder: (context, widget) { ScreenUtil.init(context); return MediaQuery( + data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), child: widget!, ); @@ -73,6 +76,7 @@ class MyApp extends StatelessWidget { RouteUtil.kRouteEventHub: (context) => const EventHubScreen(), RouteUtil.kCreateEvent: (context) => const CreateEvent(), RouteUtil.kHostTicketPreview: (context) => const HostTicketPreview(), + RouteUtil.kBuyerResponse: (context) => const BuyerResponseScreen(), }, ), ), diff --git a/evently/lib/screens/buyer_status_screen.dart b/evently/lib/screens/buyer_status_screen.dart new file mode 100644 index 0000000000..2d2e62606f --- /dev/null +++ b/evently/lib/screens/buyer_status_screen.dart @@ -0,0 +1,47 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class BuyerResponseScreen extends StatefulWidget { + const BuyerResponseScreen({super.key}); + + @override + State createState() => _BuyerResponseScreenState(); +} + +class _BuyerResponseScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: EventlyAppTheme.kLightGreen, + body: SizedBox( + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + LocaleKeys.enjoy_the_event.tr(), + style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + SizedBox( + height: 10.h, + ), + Container( + height: 161, + width: 161, + decoration: const BoxDecoration(shape: BoxShape.circle, color: EventlyAppTheme.kWhite), + child: const Icon( + Icons.check, + size: 161, + color: EventlyAppTheme.kLightGreen, + ), + ) + ], + ), + ), + ); + } +} diff --git a/evently/lib/screens/splash_screen.dart b/evently/lib/screens/splash_screen.dart index 0a4082b0f6..c2c8a3b5d9 100644 --- a/evently/lib/screens/splash_screen.dart +++ b/evently/lib/screens/splash_screen.dart @@ -16,7 +16,7 @@ class _SplashScreenState extends State { @override void initState() { Future.delayed(const Duration(seconds: kSplashScreenDuration), () { - navigatorKey.currentState!.pushReplacementNamed(RouteUtil.kRouteEventHub); + navigatorKey.currentState!.pushReplacementNamed(RouteUtil.kBuyerResponse); }); super.initState(); } diff --git a/evently/lib/utils/evently_app_theme.dart b/evently/lib/utils/evently_app_theme.dart index bf9c10a22a..9bc6f9c03b 100644 --- a/evently/lib/utils/evently_app_theme.dart +++ b/evently/lib/utils/evently_app_theme.dart @@ -29,6 +29,7 @@ class EventlyAppTheme { static const Color kPurple03 = Color(0xFFCBC8F3); static const Color kLightPurple = Color(0xFFB6B6E8); static const Color kDarkGreen = Color(0xFF14FB00); + static const Color kLightGreen = Color(0xFF0CAF59); static const Color kYellow = Color(0xFFF3BA2F); static const Color kLightYellow = Color(0xFFFED564); static const Color kLightRed = Color(0xFFEF4421); diff --git a/evently/lib/utils/route_util.dart b/evently/lib/utils/route_util.dart index d4c1cdb492..7599ec4346 100644 --- a/evently/lib/utils/route_util.dart +++ b/evently/lib/utils/route_util.dart @@ -4,4 +4,5 @@ class RouteUtil { static String kRouteEventHub = "/eventHub"; static String kCreateEvent = "/createEvent"; static String kHostTicketPreview = "/hostTicketPreview"; + static String kBuyerResponse = "/buyerResponse"; } From dab99f4c47eb228b2270a2f1397e8935caddfbc4 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Thu, 9 May 2024 09:42:41 +0500 Subject: [PATCH 044/161] feat: push --- evently/lib/main.dart | 3 +-- evently/lib/screens/splash_screen.dart | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 12a8646ba1..0fbf11cd38 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -53,13 +53,12 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => sl()), ], child: ScreenUtilInit( + designSize: const Size(428, 932), minTextAdapt: true, builder: (BuildContext context, child) => MaterialApp( - builder: (context, widget) { ScreenUtil.init(context); return MediaQuery( - data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), child: widget!, ); diff --git a/evently/lib/screens/splash_screen.dart b/evently/lib/screens/splash_screen.dart index c2c8a3b5d9..0a4082b0f6 100644 --- a/evently/lib/screens/splash_screen.dart +++ b/evently/lib/screens/splash_screen.dart @@ -16,7 +16,7 @@ class _SplashScreenState extends State { @override void initState() { Future.delayed(const Duration(seconds: kSplashScreenDuration), () { - navigatorKey.currentState!.pushReplacementNamed(RouteUtil.kBuyerResponse); + navigatorKey.currentState!.pushReplacementNamed(RouteUtil.kRouteEventHub); }); super.initState(); } From 4fc44ae5b279eea16929a3d1d268c028f2fe419f Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Thu, 9 May 2024 10:29:36 +0500 Subject: [PATCH 045/161] feat: dependency for pick file and image crooper --- .../android/app/src/main/AndroidManifest.xml | 5 + .../app/src/main/res/values/styles.xml | 2 + evently/lib/models/picked_file_model.dart | 8 + evently/lib/repository/repository.dart | 6 + evently/lib/utils/file_utils_helper.dart | 180 ++++++++++++++++++ evently/pubspec.lock | 48 +++++ evently/pubspec.yaml | 2 + 7 files changed, 251 insertions(+) create mode 100644 evently/lib/models/picked_file_model.dart create mode 100644 evently/lib/repository/repository.dart create mode 100644 evently/lib/utils/file_utils_helper.dart diff --git a/evently/android/app/src/main/AndroidManifest.xml b/evently/android/app/src/main/AndroidManifest.xml index 3be49b050a..dfa7edcdfd 100644 --- a/evently/android/app/src/main/AndroidManifest.xml +++ b/evently/android/app/src/main/AndroidManifest.xml @@ -29,6 +29,11 @@ + + + + + + + + + + @@ -36,7 +48,7 @@ android:theme="@style/AppTheme.UCropTheme"/> diff --git a/evently/android/app/src/main/kotlin/com/example/evently/MainActivity.kt b/evently/android/app/src/main/kotlin/tech/pylons/evently/MainActivity.kt similarity index 77% rename from evently/android/app/src/main/kotlin/com/example/evently/MainActivity.kt rename to evently/android/app/src/main/kotlin/tech/pylons/evently/MainActivity.kt index a0d1b936ab..1ffa24feaf 100644 --- a/evently/android/app/src/main/kotlin/com/example/evently/MainActivity.kt +++ b/evently/android/app/src/main/kotlin/tech/pylons/evently/MainActivity.kt @@ -1,4 +1,4 @@ -package com.example.evently +package tech.pylons.evently import io.flutter.embedding.android.FlutterActivity diff --git a/evently/ios/Runner/Info.plist b/evently/ios/Runner/Info.plist index aadaf6c0dc..b3d7d090d9 100644 --- a/evently/ios/Runner/Info.plist +++ b/evently/ios/Runner/Info.plist @@ -2,6 +2,25 @@ + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + tech.pylons.evently + CFBundleURLSchemes + + pylons-evently + + + + LSApplicationQueriesSchemes + + pylons-wallet + + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 319eb448b7..096122181a 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -8,6 +8,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:injectable/injectable.dart'; +import 'models/events.dart'; + enum FreeDrop { yes, no, unselected } @LazySingleton() @@ -136,5 +138,11 @@ class EventlyProvider extends ChangeNotifier { if (result.path.isEmpty) return; setThumbnail = File(result.path); + } + + void createRecipe({required Event event}) { + + + } } diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 0fbf11cd38..1f4afea8a3 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -13,11 +13,15 @@ import 'package:evently/utils/route_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; +import 'package:pylons_sdk/pylons_sdk.dart'; bool isTablet = false; void main() async { WidgetsFlutterBinding.ensureInitialized(); + + PylonsWallet.setup(mode: PylonsMode.prod, host: 'evently'); + await EasyLocalization.ensureInitialized(); configureDependencies(); diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 55d833d46e..3fc46fb3b5 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -2,6 +2,7 @@ import 'package:dartz/dartz.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/models/picked_file_model.dart'; +import 'package:evently/services/datasources/local_datasource.dart'; import 'package:evently/utils/failure/failure.dart'; import 'package:evently/utils/file_utils_helper.dart'; import 'package:injectable/injectable.dart'; @@ -19,9 +20,13 @@ abstract class Repository { @LazySingleton(as: Repository) class RepositoryImp implements Repository { - RepositoryImp({required this.fileUtilsHelper}); + RepositoryImp({ + required this.fileUtilsHelper, + required this.localDataSource, + }); final FileUtilsHelper fileUtilsHelper; + final LocalDataSource localDataSource; @override Future> pickFile() async { @@ -35,8 +40,5 @@ class RepositoryImp implements Repository { } @override - String autoGenerateEventlyId() { - // TODO: implement autoGenerateEaselId - throw UnimplementedError(); - } + String autoGenerateEventlyId() => localDataSource.autoGenerateEventlyId(); } diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart new file mode 100644 index 0000000000..3c36564e5a --- /dev/null +++ b/evently/lib/services/datasources/local_datasource.dart @@ -0,0 +1,17 @@ +import 'package:evently/utils/date_utils.dart'; +import 'package:injectable/injectable.dart'; + +abstract class LocalDataSource { + /// This method will generate Evently Id for the Event + /// Output: [String] the id of the Event that is going to be added in the recipe + String autoGenerateEventlyId(); +} + +@LazySingleton(as: LocalDataSource) +class LocalDataSourceImpl extends LocalDataSource { + @override + String autoGenerateEventlyId() { + final String cookbookId = "Event_Recipe_auto_recipe_${getFullDateTime()}"; + return cookbookId; + } +} diff --git a/evently/lib/utils/date_utils.dart b/evently/lib/utils/date_utils.dart new file mode 100644 index 0000000000..76e78a9678 --- /dev/null +++ b/evently/lib/utils/date_utils.dart @@ -0,0 +1,9 @@ +import 'package:easy_localization/easy_localization.dart'; + +String getDate() { + return DateFormat.yMd('en_US').format(DateTime.now()); +} + +String getFullDateTime() { + return DateFormat('yyyy_MM_dd_HHmmss_SSS').format(DateTime.now()); +} diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index 8c6b8dc150..78fa8ea0ae 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -8,10 +8,11 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:evently/evently_provider.dart' as _i8; -import 'package:evently/repository/repository.dart' as _i7; -import 'package:evently/utils/di/register_modules.dart' as _i9; -import 'package:evently/utils/file_utils_helper.dart' as _i6; +import 'package:evently/evently_provider.dart' as _i9; +import 'package:evently/repository/repository.dart' as _i8; +import 'package:evently/services/datasources/local_datasource.dart' as _i6; +import 'package:evently/utils/di/register_modules.dart' as _i10; +import 'package:evently/utils/file_utils_helper.dart' as _i7; import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; import 'package:file_picker/file_picker.dart' as _i5; import 'package:get_it/get_it.dart' as _i1; @@ -34,16 +35,19 @@ extension GetItInjectableX on _i1.GetIt { () => _i3.CreateEventViewModel()); gh.lazySingleton<_i4.ImageCropper>(() => registerModule.imageCropper); gh.lazySingleton<_i5.FilePicker>(() => registerModule.filePicker); - gh.lazySingleton<_i6.FileUtilsHelper>(() => _i6.FileUtilsHelperImpl( + gh.lazySingleton<_i6.LocalDataSource>(() => _i6.LocalDataSourceImpl()); + gh.lazySingleton<_i7.FileUtilsHelper>(() => _i7.FileUtilsHelperImpl( imageCropper: gh<_i4.ImageCropper>(), filePicker: gh<_i5.FilePicker>(), )); - gh.lazySingleton<_i7.Repository>( - () => _i7.RepositoryImp(fileUtilsHelper: gh<_i6.FileUtilsHelper>())); - gh.lazySingleton<_i8.EventlyProvider>( - () => _i8.EventlyProvider(repository: gh<_i7.Repository>())); + gh.lazySingleton<_i8.Repository>(() => _i8.RepositoryImp( + fileUtilsHelper: gh<_i7.FileUtilsHelper>(), + localDataSource: gh<_i6.LocalDataSource>(), + )); + gh.lazySingleton<_i9.EventlyProvider>( + () => _i9.EventlyProvider(repository: gh<_i8.Repository>())); return this; } } -class _$RegisterModule extends _i9.RegisterModule {} +class _$RegisterModule extends _i10.RegisterModule {} diff --git a/evently/linux/flutter/generated_plugin_registrant.cc b/evently/linux/flutter/generated_plugin_registrant.cc index e71a16d23d..f6f23bfe97 100644 --- a/evently/linux/flutter/generated_plugin_registrant.cc +++ b/evently/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/evently/linux/flutter/generated_plugins.cmake b/evently/linux/flutter/generated_plugins.cmake index 2e1de87a7e..f16b4c3421 100644 --- a/evently/linux/flutter/generated_plugins.cmake +++ b/evently/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/evently/macos/Flutter/GeneratedPluginRegistrant.swift b/evently/macos/Flutter/GeneratedPluginRegistrant.swift index b8e2b22f76..b19945c53d 100644 --- a/evently/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/evently/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,8 +7,10 @@ import Foundation import path_provider_foundation import shared_preferences_foundation +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 038f069fbc..7e4c2016cf 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bitstream: + dependency: transitive + description: + name: bitstream + sha256: "19a6b9f2d148010ae0c4149294cb1ad89cc212af961956dc4d2e931e0eadf963" + url: "https://pub.dev" + source: hosted + version: "1.1.0" boolean_selector: dependency: transitive description: @@ -185,6 +193,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.10.1" + decimal: + dependency: transitive + description: + name: decimal + sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21" + url: "https://pub.dev" + source: hosted + version: "2.3.3" dotted_border: dependency: "direct main" description: @@ -637,6 +653,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + protobuf: + dependency: transitive + description: + name: protobuf + sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08" + url: "https://pub.dev" + source: hosted + version: "2.1.0" provider: dependency: "direct main" description: @@ -661,6 +685,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" + pylons_sdk: + dependency: "direct main" + description: + name: pylons_sdk + sha256: "448b4a88a7ac578918f856e283a34c8cfdb14709e626a5ae3f6c2fddab2d8c3b" + url: "https://pub.dev" + source: hosted + version: "0.1.4" + rational: + dependency: transitive + description: + name: rational + sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf + url: "https://pub.dev" + source: hosted + version: "2.2.2" recase: dependency: transitive description: @@ -834,6 +874,94 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uni_links: + dependency: transitive + description: + name: uni_links + sha256: "051098acfc9e26a9fde03b487bef5d3d228ca8f67693480c6f33fd4fbb8e2b6e" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + uni_links_platform_interface: + dependency: transitive + description: + name: uni_links_platform_interface + sha256: "929cf1a71b59e3b7c2d8a2605a9cf7e0b125b13bc858e55083d88c62722d4507" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + uni_links_web: + dependency: transitive + description: + name: uni_links_web + sha256: "7539db908e25f67de2438e33cc1020b30ab94e66720b5677ba6763b25f6394df" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + url_launcher: + dependency: transitive + description: + name: url_launcher + sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" + url: "https://pub.dev" + source: hosted + version: "6.2.6" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" + url: "https://pub.dev" + source: hosted + version: "6.2.5" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + url: "https://pub.dev" + source: hosted + version: "3.1.1" vector_graphics: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index b7dcc58255..d5c3def475 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -50,6 +50,7 @@ dependencies: image_cropper: ^6.0.0 equatable: ^2.0.5 dartz: ^0.10.1 + pylons_sdk: ^0.1.4 dev_dependencies: flutter_test: diff --git a/evently/windows/flutter/generated_plugin_registrant.cc b/evently/windows/flutter/generated_plugin_registrant.cc index 8b6d4680af..4f7884874d 100644 --- a/evently/windows/flutter/generated_plugin_registrant.cc +++ b/evently/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/evently/windows/flutter/generated_plugins.cmake b/evently/windows/flutter/generated_plugins.cmake index b93c4c30c1..88b22e5c77 100644 --- a/evently/windows/flutter/generated_plugins.cmake +++ b/evently/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 49338c634530656be6895c784df4683afb0caa4e Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Fri, 10 May 2024 11:17:27 +0500 Subject: [PATCH 061/161] feat: get pylons profile --- evently/lib/evently_provider.dart | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 096122181a..fa5dbd0251 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -7,6 +7,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:injectable/injectable.dart'; +import 'package:pylons_sdk/low_level.dart'; import 'models/events.dart'; @@ -140,9 +141,29 @@ class EventlyProvider extends ChangeNotifier { setThumbnail = File(result.path); } - void createRecipe({required Event event}) { + void createRecipe({required Event event}) {} + void onPublishPress() {} + String currentUseName = ""; + bool stripeAccountExists = false; + ///* this method is used to get the profile + Future> getProfile() async { + final sdkResponse = await PylonsWallet.instance.getProfile(); + + if (sdkResponse.success) { + currentUseName = sdkResponse.data!.username; + stripeAccountExists = sdkResponse.data!.stripeExists; + + supportedDenomList = Denom.availableDenoms.where((Denom e) => sdkResponse.data!.supportedCoins.contains(e.symbol)).toList(); + + if (supportedDenomList.isNotEmpty && selectedDenom.symbol.isEmpty) { + _selectedDenom = supportedDenomList.first; + } + } + setHostName = currentUseName; + + return sdkResponse; } } From d9c72e50eef6cafb974536a4c903ef0a0638322e Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Fri, 10 May 2024 12:17:31 +0500 Subject: [PATCH 062/161] feat: receipe creation basic structure --- evently/assets/images/svg/i_icon.svg | 4 + evently/i18n/de.json | 8 +- evently/i18n/en-US.json | 8 +- evently/i18n/es.json | 8 +- evently/i18n/ru-RU.json | 8 +- evently/lib/evently_provider.dart | 7 +- evently/lib/generated/locale_keys.g.dart | 6 + evently/lib/main.dart | 1 - evently/lib/screens/create_event.dart | 8 +- .../lib/screens/host_view_ticket_preview.dart | 96 ++++++++++ evently/lib/utils/constants.dart | 4 + evently/lib/utils/evently_app_theme.dart | 2 + evently/lib/utils/route_util.dart | 1 - evently/lib/widgets/dialog_clipper.dart | 22 +++ .../lib/widgets/pylon_loading_animation.dart | 38 ++++ evently/lib/widgets/pylons_button.dart | 75 ++++++++ .../widgets/show_wallet_install_dialog.dart | 164 ++++++++++++++++++ 17 files changed, 452 insertions(+), 8 deletions(-) create mode 100644 evently/assets/images/svg/i_icon.svg create mode 100644 evently/lib/widgets/dialog_clipper.dart create mode 100644 evently/lib/widgets/pylon_loading_animation.dart create mode 100644 evently/lib/widgets/pylons_button.dart create mode 100644 evently/lib/widgets/show_wallet_install_dialog.dart diff --git a/evently/assets/images/svg/i_icon.svg b/evently/assets/images/svg/i_icon.svg new file mode 100644 index 0000000000..05391d279d --- /dev/null +++ b/evently/assets/images/svg/i_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/evently/i18n/de.json b/evently/i18n/de.json index e1a203b9b1..a435c50c2a 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -47,5 +47,11 @@ "event_name_remaining_characters": "Event name should have {} characters or more", "enter_host_name": "Enter host name", "host_name_remaining_characters": "Host name should have {} characters or more", - "please_select_thumbnail" : "please select thumbnail" + "please_select_thumbnail" : "please select thumbnail", + "download_pylons_description": "Please download the Pylons app and create a username to publish a Event with Evently.", + "download_pylons_app": "Download Pylons app", + "create_username_description": "Please Open Pylons app and create a username to publish a Event with Evently.", + "open_pylons_app": "Öffnen Sie die Pylons-App", + "create_stripe_description": "Bitte registrieren Sie sich bei Stripe in der Pylons-App, um Ihre NFT für USD aufzulisten.", + "start": "Anfang" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index e1a203b9b1..b7fb9024fa 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -47,5 +47,11 @@ "event_name_remaining_characters": "Event name should have {} characters or more", "enter_host_name": "Enter host name", "host_name_remaining_characters": "Host name should have {} characters or more", - "please_select_thumbnail" : "please select thumbnail" + "please_select_thumbnail" : "please select thumbnail", + "download_pylons_description": "Please download the Pylons app and create a username to publish a Event with Evently.", + "download_pylons_app": "Download Pylons app", + "create_username_description": "Please Open Pylons app and create a username to publish a Event with Evently.", + "open_pylons_app": "Open Pylons app", + "create_stripe_description": "Please register with Stripe in the Pylons app to list your NFT for USD.", + "start": "Start" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index e1a203b9b1..3c2d3d9904 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -47,5 +47,11 @@ "event_name_remaining_characters": "Event name should have {} characters or more", "enter_host_name": "Enter host name", "host_name_remaining_characters": "Host name should have {} characters or more", - "please_select_thumbnail" : "please select thumbnail" + "please_select_thumbnail" : "please select thumbnail", + "download_pylons_description": "Please download the Pylons app and create a username to publish a Event with Evently.", + "download_pylons_app": "Download Pylons app", + "create_username_description": "Please Open Pylons app and create a username to publish a Event with Evently.", + "open_pylons_app": "Abrir la aplicación Pylons", + "create_stripe_description": "Regístrese con Stripe en la aplicación Pylons para enumerar su NFT para USD.", + "start": "Comienzo" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index e1a203b9b1..deaa8772b4 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -47,5 +47,11 @@ "event_name_remaining_characters": "Event name should have {} characters or more", "enter_host_name": "Enter host name", "host_name_remaining_characters": "Host name should have {} characters or more", - "please_select_thumbnail" : "please select thumbnail" + "please_select_thumbnail" : "please select thumbnail", + "download_pylons_description": "Please download the Pylons app and create a username to publish a Event with Evently.", + "download_pylons_app": "Download Pylons app", + "create_username_description": "Please Open Pylons app and create a username to publish a Event with Evently.", + "open_pylons_app": "Откройте приложение пилонов", + "create_stripe_description": "Пожалуйста, зарегистрируйтесь в Stripe в приложении Pylons, чтобы перечислить свои NFT в долларах США.", + "start": "Начинать" } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index fa5dbd0251..a07a08231e 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -141,12 +141,17 @@ class EventlyProvider extends ChangeNotifier { setThumbnail = File(result.path); } - void createRecipe({required Event event}) {} + Future createRecipe({required Event event}) { + return Future.value(true); + } void onPublishPress() {} String currentUseName = ""; bool stripeAccountExists = false; + late Event event; + + bool showStripeDialog() => !stripeAccountExists && _selectedDenom.symbol == kUsdSymbol && isFreeDrop == FreeDrop.no; ///* this method is used to get the profile Future> getProfile() async { diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 002f02322a..6f4dadf90c 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -50,5 +50,11 @@ abstract class LocaleKeys { static const enter_host_name = 'enter_host_name'; static const host_name_remaining_characters = 'host_name_remaining_characters'; static const please_select_thumbnail = 'please_select_thumbnail'; + static const download_pylons_description = 'download_pylons_description'; + static const download_pylons_app = 'download_pylons_app'; + static const create_username_description = 'create_username_description'; + static const open_pylons_app = 'open_pylons_app'; + static const create_stripe_description = 'create_stripe_description'; + static const start = 'start'; } diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 1f4afea8a3..55b0e9ac72 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -78,7 +78,6 @@ class MyApp extends StatelessWidget { '/': (context) => const SplashScreen(), RouteUtil.kRouteEventHub: (context) => const EventHubScreen(), RouteUtil.kCreateEvent: (context) => const CreateEvent(), - RouteUtil.kHostTicketPreview: (context) => const HostTicketPreview(), RouteUtil.kBuyerResponse: (context) => const BuyerResponseScreen(), }, ), diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 15f522b05f..6ec350472d 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,4 +1,5 @@ import 'package:evently/screens/detail_screen.dart'; +import 'package:evently/screens/host_view_ticket_preview.dart'; import 'package:evently/screens/overview_screen.dart'; import 'package:evently/screens/perks_screen.dart'; import 'package:evently/screens/price_screen.dart'; @@ -56,7 +57,7 @@ class CreateEventContent extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), onPageChanged: (int page) { createEventViewModel.currentPage.value = page; - final map = {0: 0, 1: 1, 2: 2, 3: 3}; + final map = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}; createEventViewModel.currentStep.value = map[page]!; }, itemBuilder: (BuildContext context, int index) { @@ -65,6 +66,7 @@ class CreateEventContent extends StatelessWidget { 1: details, 2: perks, 3: price, + 4: ticketPreview, }; return map[index]?.call() ?? const SizedBox(); @@ -87,4 +89,8 @@ class CreateEventContent extends StatelessWidget { Widget price() { return const PriceScreen(); } + + Widget ticketPreview() { + return const HostTicketPreview(); + } } diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 3438340504..08fb71519d 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -1,10 +1,19 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/evently_provider.dart'; +import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/main.dart'; import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/space_utils.dart'; +import 'package:evently/widgets/pylon_loading_animation.dart'; +import 'package:evently/widgets/show_wallet_install_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; +import 'package:pylons_sdk/pylons_sdk.dart'; class HostTicketPreview extends StatefulWidget { const HostTicketPreview({super.key}); @@ -221,4 +230,91 @@ class _HostTicketPreviewState extends State { ), ); } + + Future onPublishPressed() async { + final viewModel = context.read(); + final navigator = Navigator.of(context); + + final PylonsLoadingAnimation pylonsLoadingAnimation = PylonsLoadingAnimation(context: context); + pylonsLoadingAnimation.show(); + + final isPylonsWalletExists = await PylonsWallet.instance.exists(); + + if (!isPylonsWalletExists) { + pylonsLoadingAnimation.hide(); + _showInstallWalletDialog(); + return; + } + + final profileResponse = await viewModel.getProfile(); + + if (profileResponse.errorCode == kErrProfileNotExist) { + pylonsLoadingAnimation.hide(); + _showCreateAccountDialog(); + return; + } + + if (viewModel.showStripeDialog()) { + pylonsLoadingAnimation.hide(); + _showStripeDialog(); + return; + } + + final bool isRecipeCreated = await viewModel.createRecipe(event: viewModel.event); + pylonsLoadingAnimation.hide(); + if (!isRecipeCreated) { + return; + } + + navigator.popUntil((route) { + return route.settings.name == RouteUtil.kCreateEvent; + }); + } + + void _showInstallWalletDialog() { + final ShowWalletInstallDialog showWalletInstallDialog = ShowWalletInstallDialog( + context: context, + errorMessage: LocaleKeys.download_pylons_description.tr(), + buttonMessage: LocaleKeys.download_pylons_app.tr(), + onButtonPressed: () { + PylonsWallet.instance.goToInstall(); + }, + onClose: () { + Navigator.of(navigatorKey.currentState!.overlay!.context).pop(); + }, + ); + + showWalletInstallDialog.show(); + } + + void _showCreateAccountDialog() { + final ShowWalletInstallDialog showWalletInstallDialog = ShowWalletInstallDialog( + context: context, + errorMessage: LocaleKeys.create_username_description.tr(), + buttonMessage: LocaleKeys.open_pylons_app.tr(), + onButtonPressed: () { + PylonsWallet.instance.goToPylons(); + }, + onClose: () { + Navigator.of(navigatorKey.currentState!.overlay!.context).pop(); + }, + ); + showWalletInstallDialog.show(); + } + + void _showStripeDialog() { + final ShowWalletInstallDialog showWalletInstallDialog = ShowWalletInstallDialog( + context: context, + errorMessage: LocaleKeys.create_stripe_description.tr(), + buttonMessage: LocaleKeys.start.tr(), + onButtonPressed: () async { + Navigator.pop(navigatorKey.currentState!.overlay!.context); + await PylonsWallet.instance.showStripe(); + }, + onClose: () { + Navigator.of(navigatorKey.currentState!.overlay!.context).pop(); + }, + ); + showWalletInstallDialog.show(); + } } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 148370e685..cdd81d873d 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -33,6 +33,9 @@ const kPylons = "Pylons"; const kMinEventName = 9; const kMinHostName = 9; +const kErrProfileNotExist = 'profileDoesNotExist'; + + /// ```SVG assets class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; @@ -43,6 +46,7 @@ class SVGUtils { static const kMinus = "assets/images/svg/minus.svg"; static const kDiamond = "assets/images/svg/diamond.svg"; static const kCamera = "assets/images/svg/camera.svg"; + static const kAlertIcon = 'assets/images/svg/i_icon.svg'; } /// ```PNG assets diff --git a/evently/lib/utils/evently_app_theme.dart b/evently/lib/utils/evently_app_theme.dart index 84a4173fb8..ecccef6fd6 100644 --- a/evently/lib/utils/evently_app_theme.dart +++ b/evently/lib/utils/evently_app_theme.dart @@ -24,6 +24,8 @@ class EventlyAppTheme { static const Color kGrey04 = Color(0xFFA1A1A1); static const Color kGreen = Color(0xFF0CAF59); + static const Color kLightRed = Color(0xFFEF4421); + static const String universalSansFamily = "UniversalSans"; static ThemeData theme(BuildContext context) => ThemeData( diff --git a/evently/lib/utils/route_util.dart b/evently/lib/utils/route_util.dart index 7599ec4346..b5820a1996 100644 --- a/evently/lib/utils/route_util.dart +++ b/evently/lib/utils/route_util.dart @@ -3,6 +3,5 @@ class RouteUtil { static String kRouteEventHub = "/eventHub"; static String kCreateEvent = "/createEvent"; - static String kHostTicketPreview = "/hostTicketPreview"; static String kBuyerResponse = "/buyerResponse"; } diff --git a/evently/lib/widgets/dialog_clipper.dart b/evently/lib/widgets/dialog_clipper.dart new file mode 100644 index 0000000000..f0aad3111d --- /dev/null +++ b/evently/lib/widgets/dialog_clipper.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class DialogClipper extends CustomClipper { + @override + Path getClip(Size size) { + final path = Path(); + path.lineTo(0, size.height); + path.lineTo(size.width - 65.h, size.height); + path.lineTo(size.width, size.height - 65.h); + path.lineTo(size.width, 0); + path.lineTo(65.h, 0); + path.lineTo(0, 65.h); + + return path; + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return false; + } +} diff --git a/evently/lib/widgets/pylon_loading_animation.dart b/evently/lib/widgets/pylon_loading_animation.dart new file mode 100644 index 0000000000..5cead0a6b2 --- /dev/null +++ b/evently/lib/widgets/pylon_loading_animation.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class PylonsLoadingAnimation { + PylonsLoadingAnimation({ + required this.context, + double height = 100, + double width = 100, + }) : _height = height, + _width = width; + + Future show() { + return showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.white.withOpacity(0), + builder: (ctx) => PopScope( + canPop: false, + child: AlertDialog( + elevation: 0, + backgroundColor: Colors.transparent, + content: SizedBox( + height: _height, + width: _width, + child: Image.asset('assets/loading.gif', package: 'pylons_sdk'), + ), + ), + ), + ); + } + + void hide() { + Navigator.of(context).pop(); + } + + final BuildContext context; + final double _height; + final double _width; +} diff --git a/evently/lib/widgets/pylons_button.dart b/evently/lib/widgets/pylons_button.dart new file mode 100644 index 0000000000..3422a719cc --- /dev/null +++ b/evently/lib/widgets/pylons_button.dart @@ -0,0 +1,75 @@ +import 'package:evently/main.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class PylonsButton extends StatelessWidget { + final VoidCallback onPressed; + final String btnText; + final bool showArrow; + + final double mobileScreenButtonWidth; + final Color color; + final Color textColor; + + const PylonsButton({super.key, this.showArrow = false, this.textColor = Colors.white, required this.btnText, required this.onPressed, this.mobileScreenButtonWidth = 0.5, required this.color}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + onPressed(); + }, + child: ClipPath( + clipper: PylonsButtonClipper(), + child: Container( + width: isTablet ? 0.3.sw : mobileScreenButtonWidth.sw, + height: isTablet ? 0.07.sw : 0.12.sw, + decoration: BoxDecoration(color: color), + child: Stack( + children: [ + if (showArrow) + Positioned( + top: 0, + bottom: 0, + right: isTablet ? 2.w : 8.w, + child: Icon( + Icons.arrow_forward, + color: EventlyAppTheme.kWhite, + size: isTablet ? 15.w : 20.w, + )), + Center( + child: Text( + btnText, + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 15.sp, color: textColor, fontWeight: FontWeight.w600), + ), + ) + ], + ), + ), + ), + ); + } +} + +class PylonsButtonClipper extends CustomClipper { + @override + Path getClip(Size size) { + final Path path0 = Path(); + path0.moveTo(0, 0); + path0.lineTo(size.width * 0.8991000, 0); + path0.lineTo(size.width, size.height * 0.3484000); + path0.lineTo(size.width, size.height); + path0.lineTo(size.width * 0.1008500, size.height); + path0.lineTo(0, size.height * 0.6466000); + path0.lineTo(0, 0); + path0.close(); + + return path0; + } + + @override + bool shouldReclip(CustomClipper oldClipper) { + return oldClipper != this; + } +} diff --git a/evently/lib/widgets/show_wallet_install_dialog.dart b/evently/lib/widgets/show_wallet_install_dialog.dart new file mode 100644 index 0000000000..5d75b31703 --- /dev/null +++ b/evently/lib/widgets/show_wallet_install_dialog.dart @@ -0,0 +1,164 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/screen_responsive.dart'; +import 'package:evently/widgets/dialog_clipper.dart'; +import 'package:evently/widgets/pylons_button.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class ShowWalletInstallDialog { + BuildContext context; + String errorMessage; + String buttonMessage; + VoidCallback onClose; + VoidCallback onButtonPressed; + + ShowWalletInstallDialog({required this.context, required this.errorMessage, required this.buttonMessage, required this.onClose, required this.onButtonPressed}); + + Future show() { + return showDialog( + context: context, + builder: (context) { + return Dialog( + elevation: 0, + backgroundColor: EventlyAppTheme.kTransparent, + child: ScreenResponsive( + mobileScreen: (context) => buildMobile(context), + tabletScreen: (context) => buildTablet(context), + ), + ); + }); + } + + ClipPath buildMobile(BuildContext context) { + return ClipPath( + clipper: DialogClipper(), + child: Container( + color: EventlyAppTheme.kLightRed, + padding: EdgeInsets.symmetric(horizontal: 0.05.sw), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: 30.h), + SvgPicture.asset( + SVGUtils.kAlertIcon, + height: 30.h, + ), + SizedBox(height: 30.h), + Text( + errorMessage, + style: TextStyle(color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.w800), + textAlign: TextAlign.center, + ), + SizedBox(height: 40.h), + Container( + padding: EdgeInsets.symmetric(horizontal: 0.05.sw), + height: 40.h, + child: PylonsButton( + btnText: buttonMessage, + onPressed: () { + onButtonPressed(); + Navigator.of(context).pop(); + }, + color: EventlyAppTheme.kBlue), + ), + SizedBox(height: 10.h), + GestureDetector( + child: SizedBox( + width: 0.35.sw, + height: 0.09.sw, + child: Stack( + children: [ + Center( + child: Text( + "cancel".tr(), + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 14.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w300), + ), + ), + ], + ), + ), + onTap: () { + onClose(); + }, + ), + SizedBox(height: 20.h), + ], + ), + ), + ); + } + + Padding buildTablet(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 0.1.sw), + child: ClipPath( + clipper: DialogClipper(), + child: ColoredBox( + color: EventlyAppTheme.kLightRed, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: 30.h), + SvgPicture.asset( + SVGUtils.kAlertIcon, + height: 30.h, + ), + SizedBox(height: 30.h), + Container( + padding: EdgeInsets.symmetric(horizontal: 0.09.sw), + child: Text( + errorMessage, + style: TextStyle( + color: Colors.white, + fontSize: 12.sp, + fontWeight: FontWeight.w800, + ), + textAlign: TextAlign.center, + ), + ), + SizedBox(height: 30.h), + Container( + padding: EdgeInsets.symmetric(horizontal: 0.1.sw), + height: 40.h, + child: PylonsButton( + btnText: buttonMessage, + onPressed: () { + onButtonPressed(); + Navigator.of(context).pop(); + }, + color: EventlyAppTheme.kBlue), + ), + SizedBox(height: 10.h), + GestureDetector( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 0.1.sw), + child: Stack( + children: [ + Center( + child: Text( + "cancel".tr(), + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontSize: 16.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w200), + ), + ), + ], + ), + ), + onTap: () { + onClose(); + }, + ), + SizedBox(height: 30.h), + ], + ), + ), + ), + ); + } +} From f92643c9546fad0773b101a8c84fb362ab9426a6 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Fri, 10 May 2024 12:55:07 +0500 Subject: [PATCH 063/161] feat: event cook book --- evently/lib/utils/extension_util.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 evently/lib/utils/extension_util.dart diff --git a/evently/lib/utils/extension_util.dart b/evently/lib/utils/extension_util.dart new file mode 100644 index 0000000000..e69de29bb2 From 6b8d1fec8be60acb6f8976a95b2d56c6f31991f6 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Fri, 10 May 2024 12:55:44 +0500 Subject: [PATCH 064/161] feat: create cook book evently --- evently/lib/evently_provider.dart | 72 ++++++++++++++++--- evently/lib/repository/repository.dart | 29 ++++++++ .../datasources/local_datasource.dart | 38 ++++++++++ evently/lib/utils/constants.dart | 9 +++ evently/lib/utils/extension_util.dart | 34 +++++++++ evently/pubspec.lock | 2 +- evently/pubspec.yaml | 1 + 7 files changed, 175 insertions(+), 10 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index a07a08231e..f4204f9c8a 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -1,8 +1,10 @@ import 'dart:io'; +import 'package:evently/main.dart'; import 'package:evently/models/denom.dart'; import 'package:evently/models/picked_file_model.dart'; import 'package:evently/repository/repository.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/extension_util.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -141,15 +143,11 @@ class EventlyProvider extends ChangeNotifier { setThumbnail = File(result.path); } - Future createRecipe({required Event event}) { - return Future.value(true); - } - - void onPublishPress() {} - - String currentUseName = ""; + String currentUserName = ""; bool stripeAccountExists = false; late Event event; + String? _cookbookId; + String _recipeId = ""; bool showStripeDialog() => !stripeAccountExists && _selectedDenom.symbol == kUsdSymbol && isFreeDrop == FreeDrop.no; @@ -158,7 +156,7 @@ class EventlyProvider extends ChangeNotifier { final sdkResponse = await PylonsWallet.instance.getProfile(); if (sdkResponse.success) { - currentUseName = sdkResponse.data!.username; + currentUserName = sdkResponse.data!.username; stripeAccountExists = sdkResponse.data!.stripeExists; supportedDenomList = Denom.availableDenoms.where((Denom e) => sdkResponse.data!.supportedCoins.contains(e.symbol)).toList(); @@ -167,8 +165,64 @@ class EventlyProvider extends ChangeNotifier { _selectedDenom = supportedDenomList.first; } } - setHostName = currentUseName; + setHostName = currentUserName; return sdkResponse; } + + Future createRecipe({required Event event}) async { + final scaffoldMessengerState = navigatorKey.getState(); + + _cookbookId = repository.getCookbookId(); + + final String savedUserName = repository.getCookBookGeneratorUsername(); + + if (_cookbookId == null || isDifferentUserName(savedUserName)) { + // create cookbook + final isCookBookCreated = await createCookbook(); + + if (isCookBookCreated) { + // this delay is added to wait the transaction is settle + // on the blockchain + Future.delayed(const Duration(milliseconds: 800)); + // get device cookbook id + _cookbookId = repository.getCookbookId(); + notifyListeners(); + } else { + return false; + } + } + + _recipeId = repository.autoGenerateEventlyId(); + + + return Future.value(true); + } + + /// send createCookBook tx message to the wallet app + /// return true or false depending on the response from the wallet app + Future createCookbook() async { + _cookbookId = repository.autoGenerateEventlyId(); + final cookBook1 = Cookbook( + creator: "", + id: _cookbookId, + name: cookbookName, + description: cookbookDesc, + developer: hostName, + version: kVersionCookboox, + supportEmail: supportedEmail, + enabled: true, + ); + + final response = await PylonsWallet.instance.txCreateCookbook(cookBook1); + if (response.success) { + repository.saveCookBookGeneratorUsername(currentUserName); + return true; + } + + navigatorKey.showMsg(message: response.error); + return false; + } + + bool isDifferentUserName(String savedUserName) => currentUserName.isNotEmpty && savedUserName != currentUserName; } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 3fc46fb3b5..2cb8440741 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -16,6 +16,19 @@ abstract class Repository { /// This method will generate evently Id for the event /// Output: [String] the id of the Event that is going to be added in the recipe String autoGenerateEventlyId(); + + /// This method will save the username of the cookbook generator + /// Input: [username] the username of the user who created the cookbook + /// Output: [bool] returns whether the operation is successful or not + Future saveCookBookGeneratorUsername(String username); + + /// This method will get the already created cookbook from the local database + /// Output: [String] if the cookbook already exists return cookbook else return null + String? getCookbookId(); + + /// This method will get the username of the cookbook generator + /// Output: [String] returns whether the operation is successful or not + String getCookBookGeneratorUsername(); } @LazySingleton(as: Repository) @@ -41,4 +54,20 @@ class RepositoryImp implements Repository { @override String autoGenerateEventlyId() => localDataSource.autoGenerateEventlyId(); + + @override + Future saveCookBookGeneratorUsername(String username) { + return localDataSource.saveCookBookGeneratorUsername(username); + } + + @override + String? getCookbookId() { + return localDataSource.getCookbookId(); + } + + @override + String getCookBookGeneratorUsername() { + return localDataSource.getCookBookGeneratorUsername(); + } + } diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 3c36564e5a..314e155d45 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -1,17 +1,55 @@ +import 'package:evently/utils/constants.dart'; import 'package:evently/utils/date_utils.dart'; import 'package:injectable/injectable.dart'; +import 'package:shared_preferences/shared_preferences.dart'; abstract class LocalDataSource { /// This method will generate Evently Id for the Event /// Output: [String] the id of the Event that is going to be added in the recipe String autoGenerateEventlyId(); + + /// This method will save the username of the cookbook generator + /// Input: [username] the username of the user who created the cookbook + /// Output: [bool] returns whether the operation is successful or not + Future saveCookBookGeneratorUsername(String username); + + /// This method will get the already created cookbook from the local database + /// Output: [String] if the cookbook already exists return cookbook else return null + String? getCookbookId(); + + /// This method will get the username of the cookbook generator + /// Output: [String] returns whether the operation is successful or not + String getCookBookGeneratorUsername(); } @LazySingleton(as: LocalDataSource) class LocalDataSourceImpl extends LocalDataSource { + LocalDataSourceImpl({required this.sharedPreferences}); + + final SharedPreferences sharedPreferences; + @override String autoGenerateEventlyId() { final String cookbookId = "Event_Recipe_auto_recipe_${getFullDateTime()}"; return cookbookId; } + + @override + Future saveCookBookGeneratorUsername(String username) async { + await sharedPreferences.setString(kUsername, username); + return true; + } + + /// gets cookbookId from local storage + ///return String or null + @override + String? getCookbookId() { + return sharedPreferences.getString(kCookbookId); + } + + @override + String getCookBookGeneratorUsername() { + return sharedPreferences.getString(kUsername) ?? ''; + } + } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index cdd81d873d..282d96c28e 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -35,6 +35,15 @@ const kMinHostName = 9; const kErrProfileNotExist = 'profileDoesNotExist'; +const cookbookName = "Evently Cookbook"; +const cookbookDesc = "Cookbook for Evenlty Event"; +const kVersionCookboox = "v0.0.1"; +const supportedEmail = "support@pylons.tech"; + +/// ````Reserved words, symbols, IDs etc +const kCookbookId = 'cookbook_id'; +const kUsername = 'username'; +const kHostName = 'artistName'; /// ```SVG assets class SVGUtils { diff --git a/evently/lib/utils/extension_util.dart b/evently/lib/utils/extension_util.dart index e69de29bb2..1039c40ed5 100644 --- a/evently/lib/utils/extension_util.dart +++ b/evently/lib/utils/extension_util.dart @@ -0,0 +1,34 @@ +import 'package:evently/main.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +extension NavigatorKey on GlobalKey { + void showMsg({required String message}) { + ScaffoldMessenger.maybeOf(currentState!.context) + ?..hideCurrentSnackBar() + ..showSnackBar(SnackBar( + content: Text( + message, + textAlign: TextAlign.start, + style: TextStyle( + fontSize: 14.sp, + ), + ), + duration: const Duration(seconds: 2), + )); + } +} + +extension ScaffoldMessengerKeyHelper on GlobalKey { + ScaffoldMessengerState? getState() { + if (navigatorKey.currentState == null) { + return null; + } + + if (navigatorKey.currentState!.overlay == null) { + return null; + } + + return ScaffoldMessenger.maybeOf(navigatorKey.currentState!.overlay!.context); + } +} diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 7e4c2016cf..09c999facf 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -710,7 +710,7 @@ packages: source: hosted version: "4.1.0" shared_preferences: - dependency: transitive + dependency: "direct main" description: name: shared_preferences sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index d5c3def475..557eef9e4d 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: equatable: ^2.0.5 dartz: ^0.10.1 pylons_sdk: ^0.1.4 + shared_preferences: ^2.2.3 dev_dependencies: flutter_test: From 0eb015d6eae1fdef3cad3f21210ee036ee766263 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Fri, 10 May 2024 15:54:35 +0500 Subject: [PATCH 065/161] feat: basic implementation of the pylons sdk --- evently/lib/evently_provider.dart | 5 ++--- evently/lib/repository/repository.dart | 13 +++++++++++-- .../services/datasources/local_datasource.dart | 17 ++++++++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index f4204f9c8a..c7f5e6b058 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:evently/main.dart'; import 'package:evently/models/denom.dart'; +import 'package:evently/models/events.dart'; import 'package:evently/models/picked_file_model.dart'; import 'package:evently/repository/repository.dart'; import 'package:evently/utils/constants.dart'; @@ -11,7 +12,6 @@ import 'package:flutter/widgets.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/low_level.dart'; -import 'models/events.dart'; enum FreeDrop { yes, no, unselected } @@ -195,14 +195,13 @@ class EventlyProvider extends ChangeNotifier { _recipeId = repository.autoGenerateEventlyId(); - return Future.value(true); } /// send createCookBook tx message to the wallet app /// return true or false depending on the response from the wallet app Future createCookbook() async { - _cookbookId = repository.autoGenerateEventlyId(); + _cookbookId = repository.autoGenerateCookbookId(); final cookBook1 = Cookbook( creator: "", id: _cookbookId, diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 2cb8440741..2706d8d076 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -15,7 +15,7 @@ abstract class Repository { /// This method will generate evently Id for the event /// Output: [String] the id of the Event that is going to be added in the recipe - String autoGenerateEventlyId(); + String autoGenerateCookbookId(); /// This method will save the username of the cookbook generator /// Input: [username] the username of the user who created the cookbook @@ -29,6 +29,10 @@ abstract class Repository { /// This method will get the username of the cookbook generator /// Output: [String] returns whether the operation is successful or not String getCookBookGeneratorUsername(); + + /// This method will generate easel Id for the NFT + /// Output: [String] the id of the NFT that is going to be added in the recipe + String autoGenerateEventlyId(); } @LazySingleton(as: Repository) @@ -53,7 +57,7 @@ class RepositoryImp implements Repository { } @override - String autoGenerateEventlyId() => localDataSource.autoGenerateEventlyId(); + String autoGenerateCookbookId() => localDataSource.autoGenerateCookbookId(); @override Future saveCookBookGeneratorUsername(String username) { @@ -70,4 +74,9 @@ class RepositoryImp implements Repository { return localDataSource.getCookBookGeneratorUsername(); } + @override + String autoGenerateEventlyId() { + return localDataSource.autoGenerateEventlyId(); + } + } diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 314e155d45..6bc64cef88 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -6,7 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart'; abstract class LocalDataSource { /// This method will generate Evently Id for the Event /// Output: [String] the id of the Event that is going to be added in the recipe - String autoGenerateEventlyId(); + String autoGenerateCookbookId(); /// This method will save the username of the cookbook generator /// Input: [username] the username of the user who created the cookbook @@ -20,6 +20,10 @@ abstract class LocalDataSource { /// This method will get the username of the cookbook generator /// Output: [String] returns whether the operation is successful or not String getCookBookGeneratorUsername(); + + /// This method will generate easel Id for the NFT + /// Output: [String] the id of the NFT that is going to be added in the recipe + String autoGenerateEventlyId(); } @LazySingleton(as: LocalDataSource) @@ -29,8 +33,8 @@ class LocalDataSourceImpl extends LocalDataSource { final SharedPreferences sharedPreferences; @override - String autoGenerateEventlyId() { - final String cookbookId = "Event_Recipe_auto_recipe_${getFullDateTime()}"; + String autoGenerateCookbookId() { + final String cookbookId = "Evently_CookBook_auto_cookbook_${getFullDateTime()}"; return cookbookId; } @@ -52,4 +56,11 @@ class LocalDataSourceImpl extends LocalDataSource { return sharedPreferences.getString(kUsername) ?? ''; } + /// auto generates easelID string + /// returns easelId + @override + String autoGenerateEventlyId() { + final String cookbookId = "Evently_Recipe_auto_recipe_${getFullDateTime()}"; + return cookbookId; + } } From 90503bd6ef82fabda138fb04906539485d883e5f Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Fri, 10 May 2024 16:48:06 +0500 Subject: [PATCH 066/161] feat: shared preference init --- evently/lib/utils/di/di.config.dart | 24 +++++++++++++----------- evently/lib/utils/di/di.dart | 6 +++++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index 78fa8ea0ae..aa017fe664 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -8,16 +8,17 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:evently/evently_provider.dart' as _i9; -import 'package:evently/repository/repository.dart' as _i8; +import 'package:evently/evently_provider.dart' as _i10; +import 'package:evently/repository/repository.dart' as _i9; import 'package:evently/services/datasources/local_datasource.dart' as _i6; -import 'package:evently/utils/di/register_modules.dart' as _i10; -import 'package:evently/utils/file_utils_helper.dart' as _i7; +import 'package:evently/utils/di/register_modules.dart' as _i11; +import 'package:evently/utils/file_utils_helper.dart' as _i8; import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; import 'package:file_picker/file_picker.dart' as _i5; import 'package:get_it/get_it.dart' as _i1; import 'package:image_cropper/image_cropper.dart' as _i4; import 'package:injectable/injectable.dart' as _i2; +import 'package:shared_preferences/shared_preferences.dart' as _i7; extension GetItInjectableX on _i1.GetIt { // initializes the registration of main-scope dependencies inside of GetIt @@ -35,19 +36,20 @@ extension GetItInjectableX on _i1.GetIt { () => _i3.CreateEventViewModel()); gh.lazySingleton<_i4.ImageCropper>(() => registerModule.imageCropper); gh.lazySingleton<_i5.FilePicker>(() => registerModule.filePicker); - gh.lazySingleton<_i6.LocalDataSource>(() => _i6.LocalDataSourceImpl()); - gh.lazySingleton<_i7.FileUtilsHelper>(() => _i7.FileUtilsHelperImpl( + gh.lazySingleton<_i6.LocalDataSource>(() => _i6.LocalDataSourceImpl( + sharedPreferences: gh<_i7.SharedPreferences>())); + gh.lazySingleton<_i8.FileUtilsHelper>(() => _i8.FileUtilsHelperImpl( imageCropper: gh<_i4.ImageCropper>(), filePicker: gh<_i5.FilePicker>(), )); - gh.lazySingleton<_i8.Repository>(() => _i8.RepositoryImp( - fileUtilsHelper: gh<_i7.FileUtilsHelper>(), + gh.lazySingleton<_i9.Repository>(() => _i9.RepositoryImp( + fileUtilsHelper: gh<_i8.FileUtilsHelper>(), localDataSource: gh<_i6.LocalDataSource>(), )); - gh.lazySingleton<_i9.EventlyProvider>( - () => _i9.EventlyProvider(repository: gh<_i8.Repository>())); + gh.lazySingleton<_i10.EventlyProvider>( + () => _i10.EventlyProvider(repository: gh<_i9.Repository>())); return this; } } -class _$RegisterModule extends _i10.RegisterModule {} +class _$RegisterModule extends _i11.RegisterModule {} diff --git a/evently/lib/utils/di/di.dart b/evently/lib/utils/di/di.dart index ecf2d0d691..62d8189d3a 100644 --- a/evently/lib/utils/di/di.dart +++ b/evently/lib/utils/di/di.dart @@ -4,10 +4,14 @@ import 'package:evently/utils/di/di.config.dart'; import 'package:get_it/get_it.dart'; import 'package:injectable/injectable.dart'; +import 'package:shared_preferences/shared_preferences.dart'; // Service Locator for project final sl = GetIt.I; // initialization of Service locator @InjectableInit() -void configureDependencies() => sl.init(); +void configureDependencies() { + sl.registerSingletonAsync(() => SharedPreferences.getInstance()); + sl.init(); +} From fbfcc79503634163978a275e5910bdffa19b9fe5 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Fri, 10 May 2024 17:44:20 +0500 Subject: [PATCH 067/161] feat: date time picker at detail screen --- evently/lib/screens/detail_screen.dart | 100 ++++++++++++++++++------- 1 file changed, 72 insertions(+), 28 deletions(-) diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 6408b7e44d..963e436601 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -54,26 +54,39 @@ class _DetailsScreenState extends State { Row( children: [ Expanded( - child: EventlyTextField( - enable: false, - label: LocaleKeys.start_date.tr(), - controller: TextEditingController(text: provider.startDate), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; + child: GestureDetector( + onTap: () async { + _showDatePicker(onSelected: (DateTime? val) { + if (val == null) return; + provider.setStartDate = DateFormat("dd-MM-yyyy").format(val); + }); }, + child: EventlyTextField( + enable: false, + label: LocaleKeys.start_date.tr(), + controller: TextEditingController(text: provider.startDate), + textCapitalization: TextCapitalization.sentences, + ), ), ), HorizontalSpace(20.w), Expanded( - child: EventlyTextField( - enable: false, - label: LocaleKeys.end_date.tr(), - controller: TextEditingController(text: provider.endDate), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; + child: GestureDetector( + onTap: () { + _showDatePicker(onSelected: (DateTime? val) { + if (val == null) return; + provider.setEndDate = DateFormat("dd-MM-yyyy").format(val); + }); }, + child: EventlyTextField( + enable: false, + label: LocaleKeys.end_date.tr(), + controller: TextEditingController(text: provider.endDate), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), ), ), ], @@ -82,26 +95,42 @@ class _DetailsScreenState extends State { Row( children: [ Expanded( - child: EventlyTextField( - enable: false, - label: LocaleKeys.start_time.tr(), - controller: TextEditingController(text: provider.startTime), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; + child: GestureDetector( + onTap: () { + _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { + if (timeOfDay == null) return; + provider.setStartTime = '${timeOfDay.hour}:${timeOfDay.minute}'; + }); }, + child: EventlyTextField( + enable: false, + label: LocaleKeys.start_time.tr(), + controller: TextEditingController(text: provider.startTime), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), ), ), HorizontalSpace(20.w), Expanded( - child: EventlyTextField( - enable: false, - label: LocaleKeys.end_time.tr(), - controller: TextEditingController(text: provider.endTime), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; + child: GestureDetector( + onTap: () { + _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { + if (timeOfDay == null) return; + provider.setEndTime = '${timeOfDay.hour}:${timeOfDay.minute}'; + }); }, + child: EventlyTextField( + enable: false, + label: LocaleKeys.end_time.tr(), + controller: TextEditingController(text: provider.endTime), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), ), ), ], @@ -146,4 +175,19 @@ class _DetailsScreenState extends State { )), ); } + + _showTimerPicker({required ValueChanged onSelected}) { + showTimePicker( + initialTime: TimeOfDay.now(), + context: context, + ).then((value) => onSelected(value!)); + } + + _showDatePicker({required ValueChanged onSelected}) { + showDatePicker( + context: context, + firstDate: DateTime.now(), + lastDate: DateTime.now(), + ).then((value) => onSelected(value!)); + } } From 1c98c45ec8af19f1496093826d64b190ab9281d9 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 13 May 2024 10:12:38 +0500 Subject: [PATCH 068/161] feat: detail data collection --- evently/lib/evently_provider.dart | 12 +++++------- evently/lib/screens/detail_screen.dart | 12 ++++++++++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index c7f5e6b058..1698f37577 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -12,7 +12,6 @@ import 'package:flutter/widgets.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/low_level.dart'; - enum FreeDrop { yes, no, unselected } @LazySingleton() @@ -68,7 +67,6 @@ class EventlyProvider extends ChangeNotifier { String _endTime = ""; String _location = ""; String _description = ""; - bool _isDetailEnable = false; String get startDate => _startDate; String get endDate => _endDate; @@ -76,40 +74,40 @@ class EventlyProvider extends ChangeNotifier { String get endTime => _endTime; String get location => _location; String get description => _description; - bool get isDetailEnable => _isDetailEnable; set setStartDate(String value) { _startDate = value; + notifyListeners(); } set setEndDate(String value) { _endDate = value; + notifyListeners(); } set setStartTime(String value) { _startTime = value; + notifyListeners(); } set setEndTime(String value) { _endTime = value; + notifyListeners(); } set setLocation(String value) { _location = value; + notifyListeners(); } set setDescription(String value) { _description = value; - notifyListeners(); - } - set isDetailEnable(bool value) { - _isDetailEnable = value; notifyListeners(); } diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 963e436601..31544ba913 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -151,6 +151,7 @@ class _DetailsScreenState extends State { ), VerticalSpace(20.h), EventlyTextField( + onChanged: (_) => provider.setDescription = _, label: LocaleKeys.description.tr(), hint: LocaleKeys.what_event_for.tr(), controller: TextEditingController(text: provider.description) @@ -162,9 +163,16 @@ class _DetailsScreenState extends State { ), const VerticalSpace(80), BottomButtons( - onPressContinue: () {}, + onPressContinue: () { + createEventViewModel.nextPage(); + }, onPressSaveDraft: () {}, - isContinueEnable: provider.isDetailEnable, + isContinueEnable: provider.startDate.isNotEmpty && + provider.endDate.isNotEmpty && + provider.startTime.isNotEmpty && + provider.endTime.isNotEmpty && + provider.description.isNotEmpty && + provider.location.isNotEmpty, ), ], ), From b004a9a5dab1215d936fb70a78a60f8c839cb254 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 13 May 2024 11:37:42 +0500 Subject: [PATCH 069/161] feat: perks screen --- evently/i18n/de.json | 5 +- evently/i18n/en-US.json | 5 +- evently/i18n/es.json | 5 +- evently/i18n/ru-RU.json | 5 +- evently/lib/evently_provider.dart | 33 ++- evently/lib/models/perks_model.dart | 16 ++ evently/lib/screens/perks_screen.dart | 335 +++++++++++++++++--------- evently/lib/utils/constants.dart | 6 + 8 files changed, 282 insertions(+), 128 deletions(-) create mode 100644 evently/lib/models/perks_model.dart diff --git a/evently/i18n/de.json b/evently/i18n/de.json index a435c50c2a..756503c09a 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -53,5 +53,8 @@ "create_username_description": "Please Open Pylons app and create a username to publish a Event with Evently.", "open_pylons_app": "Öffnen Sie die Pylons-App", "create_stripe_description": "Bitte registrieren Sie sich bei Stripe in der Pylons-App, um Ihre NFT für USD aufzulisten.", - "start": "Anfang" + "start": "Anfang", + "free_shirt": "1 free t-shirt", + "free_gift": "Free Gift", + "free_drink": "1 free drink" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index b7fb9024fa..6cef40ae5f 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -53,5 +53,8 @@ "create_username_description": "Please Open Pylons app and create a username to publish a Event with Evently.", "open_pylons_app": "Open Pylons app", "create_stripe_description": "Please register with Stripe in the Pylons app to list your NFT for USD.", - "start": "Start" + "start": "Start", + "free_shirt": "1 free t-shirt", + "free_gift": "Free Gift", + "free_drink": "1 free drink" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 3c2d3d9904..56fb8558b1 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -53,5 +53,8 @@ "create_username_description": "Please Open Pylons app and create a username to publish a Event with Evently.", "open_pylons_app": "Abrir la aplicación Pylons", "create_stripe_description": "Regístrese con Stripe en la aplicación Pylons para enumerar su NFT para USD.", - "start": "Comienzo" + "start": "Comienzo", + "free_shirt": "1 free t-shirt", + "free_gift": "Free Gift", + "free_drink": "1 free drink" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index deaa8772b4..0cfb75ca3d 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -53,5 +53,8 @@ "create_username_description": "Please Open Pylons app and create a username to publish a Event with Evently.", "open_pylons_app": "Откройте приложение пилонов", "create_stripe_description": "Пожалуйста, зарегистрируйтесь в Stripe в приложении Pylons, чтобы перечислить свои NFT в долларах США.", - "start": "Начинать" + "start": "Начинать", + "free_shirt": "1 free t-shirt", + "free_gift": "Free Gift", + "free_drink": "1 free drink" } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 1698f37577..3afd308232 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:evently/main.dart'; import 'package:evently/models/denom.dart'; import 'package:evently/models/events.dart'; +import 'package:evently/models/perks_model.dart'; import 'package:evently/models/picked_file_model.dart'; import 'package:evently/repository/repository.dart'; import 'package:evently/utils/constants.dart'; @@ -77,37 +78,59 @@ class EventlyProvider extends ChangeNotifier { set setStartDate(String value) { _startDate = value; - notifyListeners(); } set setEndDate(String value) { _endDate = value; - notifyListeners(); } set setStartTime(String value) { _startTime = value; - notifyListeners(); } set setEndTime(String value) { _endTime = value; - notifyListeners(); } set setLocation(String value) { _location = value; - notifyListeners(); } set setDescription(String value) { _description = value; + notifyListeners(); + } + + /// perks screen + final List _perks = []; + int _selectedPerk = 0; + + List get perks => _perks; + + int get selectedPerk => _selectedPerk; + + set setSelectedPerks(int val) { + _selectedPerk = val; + notifyListeners(); + } + + set setPerks(PerksModel perksModel) { + _perks.add(perksModel); + notifyListeners(); + } + + updatePerks(PerksModel perksModel, int index) { + _perks[index] = perksModel; + notifyListeners(); + } + removePerks(int index) { + _perks.removeAt(index); notifyListeners(); } diff --git a/evently/lib/models/perks_model.dart b/evently/lib/models/perks_model.dart new file mode 100644 index 0000000000..ef148513e7 --- /dev/null +++ b/evently/lib/models/perks_model.dart @@ -0,0 +1,16 @@ +class PerksModel { + PerksModel({required this.name, required this.description}); + + final String name; + final String description; + + factory PerksModel.updateName({required String name, required PerksModel perksModel}) => PerksModel( + name: name, + description: perksModel.description, + ); + + factory PerksModel.updateDescription({required String description, required PerksModel perksModel}) => PerksModel( + name: perksModel.name, + description: description, + ); +} diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index 2f41bf61d0..e4054143c1 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -1,5 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/evently_provider.dart'; import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/models/perks_model.dart'; import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; @@ -10,6 +12,7 @@ import 'package:evently/utils/screen_responsive.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/evently_text_field.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -32,142 +35,236 @@ class _PerksScreenState extends State { return Scaffold( body: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) => SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints(minHeight: constraints.maxHeight), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: Consumer( + builder: (_, provider, __) => Form( + key: _formKey, + child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const VerticalSpace(20), - MyStepsIndicator(currentStep: createEventViewModel.currentStep), - const VerticalSpace(5), - StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), - const VerticalSpace(20), - PageAppBar( - onPressBack: () { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - createEventViewModel.previousPage(); - }, - ), - const VerticalSpace(20), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - LocaleKeys.add_perk.tr(), - style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold), - ), - InkWell( - onTap: () {}, - child: DecoratedBox( - decoration: const BoxDecoration( - color: EventlyAppTheme.kBlue, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: createEventViewModel.currentStep), + const VerticalSpace(5), + StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), + const VerticalSpace(20), + PageAppBar( + onPressBack: () { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + createEventViewModel.previousPage(); + }, + ), + const VerticalSpace(20), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + LocaleKeys.add_perk.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold), ), - child: Icon(Icons.add, size: 16.h, color: EventlyAppTheme.kWhite), + InkWell( + onTap: () => provider.setPerks = PerksModel(name: kFreeShirt, description: ''), + child: DecoratedBox( + decoration: const BoxDecoration( + color: EventlyAppTheme.kBlue, + ), + child: Icon(Icons.add, size: 16.h, color: EventlyAppTheme.kWhite), + ), + ), + ], + ), + ), + const VerticalSpace(10), + if (provider.perks.isEmpty) + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Text( + LocaleKeys.there_no_perks_created.tr(), + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kGrey01), ), ), - ], - ), - ), - const VerticalSpace(10), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Text( - LocaleKeys.there_no_perks_created.tr(), - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w700, color: EventlyAppTheme.kGrey01), - ), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Stack( - alignment: Alignment.center, - children: [ - ScreenResponsive( - mobileScreen: (context) => Image.asset( - PngUtils.kLightPurpleSingleLine, - height: 40.h, - width: 1.sw, - fit: BoxFit.fill, + if (provider.perks.isNotEmpty) + PerksView( + perksModel: provider.perks[provider.selectedPerk], + onChangeDescription: (String value) => provider.updatePerks( + PerksModel.updateDescription( + description: value, + perksModel: provider.perks[provider.selectedPerk], + ), + provider.selectedPerk, ), - tabletScreen: (context) => Image.asset( - PngUtils.kLightPurpleSingleLine, - height: 32.h, - width: 1.sw, - fit: BoxFit.fill, + onChangeName: (String value) => provider.updatePerks( + PerksModel.updateName( + name: value, + perksModel: provider.perks[provider.selectedPerk], + ), + provider.selectedPerk, ), + removePerk: () { + provider.removePerks(provider.selectedPerk); + }, ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 10.w), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset(SVGUtils.kMinus), - HorizontalSpace(10.w), - Text( - LocaleKeys.free_shirt.tr(), - style: TextStyle(color: EventlyAppTheme.kWhite, fontSize: 15.sp, fontWeight: FontWeight.bold), - ), - ], - ), - ) - ], - ), - ), - const VerticalSpace(10), - Padding( - padding: EdgeInsets.symmetric(horizontal: 50.w), - child: Text( - LocaleKeys.perk_icon.tr(), - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 13.sp, - ), - ), - ), - const VerticalSpace(6), - Padding( - padding: EdgeInsets.symmetric(horizontal: 50.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container(padding: const EdgeInsets.all(7), color: EventlyAppTheme.kTextLightPurple, child: SvgPicture.asset(SVGUtils.kShirt)), - Container(padding: const EdgeInsets.all(7), color: Colors.transparent, child: SvgPicture.asset(SVGUtils.kGift)), - Container(padding: const EdgeInsets.all(7), color: Colors.transparent, child: SvgPicture.asset(SVGUtils.kDrinks)), - ], - ), + ], ), const VerticalSpace(20), Padding( - padding: EdgeInsets.symmetric(horizontal: 50.w), - child: EventlyTextField( - noOfLines: 4, - label: LocaleKeys.description_optional.tr(), - hint: LocaleKeys.claim_free_drink.tr(), - controller: TextEditingController(), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, + padding: const EdgeInsets.symmetric(horizontal: 20), + child: BottomButtons( + onPressContinue: () {}, + onPressSaveDraft: () {}, + isContinueEnable: true, ), - ), + ) ], ), - BottomButtons( - onPressContinue: () {}, - onPressSaveDraft: () {}, - isContinueEnable: false, + ), + ), + ), + ), + )); + } +} + +class PerksView extends StatelessWidget { + const PerksView({ + super.key, + required this.perksModel, + required this.onChangeDescription, + required this.onChangeName, + required this.removePerk, + }); + + final PerksModel perksModel; + + final ValueChanged onChangeName; + final ValueChanged onChangeDescription; + final VoidCallback removePerk; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Stack( + alignment: Alignment.center, + children: [ + ScreenResponsive( + mobileScreen: (context) => Image.asset( + PngUtils.kLightPurpleSingleLine, + height: 40.h, + width: 1.sw, + fit: BoxFit.fill, + ), + tabletScreen: (context) => Image.asset( + PngUtils.kLightPurpleSingleLine, + height: 32.h, + width: 1.sw, + fit: BoxFit.fill, + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + child: SvgPicture.asset(SVGUtils.kMinus), + onTap: () => removePerk(), + ), + HorizontalSpace(10.w), + Text( + perksModel.name.tr(), + style: TextStyle(color: EventlyAppTheme.kWhite, fontSize: 15.sp, fontWeight: FontWeight.bold), + ), + ], + ), ) ], ), ), - )), - )); + const VerticalSpace(10), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50.w), + child: Text( + LocaleKeys.perk_icon.tr(), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 13.sp, + ), + ), + ), + const VerticalSpace(6), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + PerksIconSelection( + icon: SVGUtils.kShirt, + isSelected: perksModel.name == kFreeShirt, + onTap: () => onChangeName(kFreeShirt), + ), + PerksIconSelection( + icon: SVGUtils.kGift, + isSelected: perksModel.name == kFreeGift, + onTap: () => onChangeName(kFreeGift), + ), + PerksIconSelection( + icon: SVGUtils.kDrinks, + isSelected: perksModel.name == kFreeDrink, + onTap: () => onChangeName(kFreeDrink), + ), + ], + ), + ), + const VerticalSpace(20), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50.w), + child: EventlyTextField( + onChanged: (_) => onChangeDescription(_), + noOfLines: 4, + label: LocaleKeys.description_optional.tr(), + hint: LocaleKeys.claim_free_drink.tr(), + controller: TextEditingController(text: perksModel.description), + textCapitalization: TextCapitalization.sentences, + ), + ), + ], + ); + } +} + +class PerksIconSelection extends StatelessWidget { + const PerksIconSelection({ + super.key, + required this.icon, + required this.isSelected, + required this.onTap, + }); + + final String icon; + final bool isSelected; + + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => onTap(), + child: Container( + padding: const EdgeInsets.all(7), + color: isSelected ? EventlyAppTheme.kTextLightPurple : Colors.transparent, + child: SvgPicture.asset(icon), + ), + ); } } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 282d96c28e..956c4119e4 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -45,6 +45,12 @@ const kCookbookId = 'cookbook_id'; const kUsername = 'username'; const kHostName = 'artistName'; + +///perks +const kFreeShirt = "free_shirt"; +const kFreeGift = "free_gift"; +const kFreeDrink = "free_drink"; + /// ```SVG assets class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; From 3ae013aee747cd91c0f7a9c56d14b1b6b415f105 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 13 May 2024 12:27:04 +0500 Subject: [PATCH 070/161] feat: perk screen state managment and data collection --- evently/lib/screens/perks_screen.dart | 108 +++++++++++++++----------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index e4054143c1..dfffa389b3 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -12,7 +12,6 @@ import 'package:evently/utils/screen_responsive.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/evently_text_field.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -91,7 +90,7 @@ class _PerksScreenState extends State { ), if (provider.perks.isNotEmpty) PerksView( - perksModel: provider.perks[provider.selectedPerk], + perksModel: provider.perks, onChangeDescription: (String value) => provider.updatePerks( PerksModel.updateDescription( description: value, @@ -106,9 +105,9 @@ class _PerksScreenState extends State { ), provider.selectedPerk, ), - removePerk: () { - provider.removePerks(provider.selectedPerk); - }, + removePerk: () => provider.removePerks(provider.selectedPerk), + selectedIndex: provider.selectedPerk, + setSelectedPerk: (_) => provider.setSelectedPerks = _, ), ], ), @@ -138,13 +137,16 @@ class PerksView extends StatelessWidget { required this.onChangeDescription, required this.onChangeName, required this.removePerk, + required this.selectedIndex, + required this.setSelectedPerk, }); - final PerksModel perksModel; - + final List perksModel; + final int selectedIndex; final ValueChanged onChangeName; final ValueChanged onChangeDescription; final VoidCallback removePerk; + final ValueChanged setSelectedPerk; @override Widget build(BuildContext context) { @@ -152,44 +154,55 @@ class PerksView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Stack( - alignment: Alignment.center, - children: [ - ScreenResponsive( - mobileScreen: (context) => Image.asset( - PngUtils.kLightPurpleSingleLine, - height: 40.h, - width: 1.sw, - fit: BoxFit.fill, - ), - tabletScreen: (context) => Image.asset( - PngUtils.kLightPurpleSingleLine, - height: 32.h, - width: 1.sw, - fit: BoxFit.fill, - ), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 10.w), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - GestureDetector( - child: SvgPicture.asset(SVGUtils.kMinus), - onTap: () => removePerk(), - ), - HorizontalSpace(10.w), - Text( - perksModel.name.tr(), - style: TextStyle(color: EventlyAppTheme.kWhite, fontSize: 15.sp, fontWeight: FontWeight.bold), + Column( + children: perksModel + .asMap() + .entries + .map( + (e) => GestureDetector( + onTap: () => setSelectedPerk(e.key), + child: Padding( + padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 10.h), + child: Stack( + alignment: Alignment.center, + children: [ + ScreenResponsive( + mobileScreen: (context) => Image.asset( + selectedIndex == e.key ? PngUtils.kLightPurpleSingleLine : PngUtils.kDarkPurpleSingleLine, + height: 40.h, + width: 1.sw, + fit: BoxFit.fill, + ), + tabletScreen: (context) => Image.asset( + selectedIndex == e.key ? PngUtils.kLightPurpleSingleLine : PngUtils.kDarkPurpleSingleLine, + height: 32.h, + width: 1.sw, + fit: BoxFit.fill, + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + child: SvgPicture.asset(SVGUtils.kMinus), + onTap: () => removePerk(), + ), + HorizontalSpace(10.w), + Text( + e.value.name.tr(), + style: TextStyle(color: EventlyAppTheme.kWhite, fontSize: 15.sp, fontWeight: FontWeight.bold), + ), + ], + ), + ) + ], ), - ], + ), ), ) - ], - ), + .toList(), ), const VerticalSpace(10), Padding( @@ -210,17 +223,17 @@ class PerksView extends StatelessWidget { children: [ PerksIconSelection( icon: SVGUtils.kShirt, - isSelected: perksModel.name == kFreeShirt, + isSelected: perksModel[selectedIndex].name == kFreeShirt, onTap: () => onChangeName(kFreeShirt), ), PerksIconSelection( icon: SVGUtils.kGift, - isSelected: perksModel.name == kFreeGift, + isSelected: perksModel[selectedIndex].name == kFreeGift, onTap: () => onChangeName(kFreeGift), ), PerksIconSelection( icon: SVGUtils.kDrinks, - isSelected: perksModel.name == kFreeDrink, + isSelected: perksModel[selectedIndex].name == kFreeDrink, onTap: () => onChangeName(kFreeDrink), ), ], @@ -234,7 +247,10 @@ class PerksView extends StatelessWidget { noOfLines: 4, label: LocaleKeys.description_optional.tr(), hint: LocaleKeys.claim_free_drink.tr(), - controller: TextEditingController(text: perksModel.description), + controller: TextEditingController(text: perksModel[selectedIndex].description) + ..selection = TextSelection.fromPosition( + TextPosition(offset: perksModel[selectedIndex].description.length), + ), textCapitalization: TextCapitalization.sentences, ), ), From a0d0aba3f1694302058af77d1e016027c4d606d2 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 13 May 2024 15:53:41 +0500 Subject: [PATCH 071/161] feat: price screen state data collection --- evently/lib/evently_provider.dart | 28 +- evently/lib/models/denom.dart | 2 +- evently/lib/screens/detail_screen.dart | 265 ++++++++------- evently/lib/screens/overview_screen.dart | 209 ++++++------ evently/lib/screens/perks_screen.dart | 22 +- evently/lib/screens/price_screen.dart | 308 +++++++++--------- .../widgets/evently_price_input_field.dart | 45 ++- 7 files changed, 460 insertions(+), 419 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 3afd308232..c00b2cc4c4 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -134,15 +134,39 @@ class EventlyProvider extends ChangeNotifier { notifyListeners(); } + ///* price screen + Denom _selectedDenom = Denom.availableDenoms.first; Denom get selectedDenom => _selectedDenom; List supportedDenomList = Denom.availableDenoms; - TextEditingController priceController = TextEditingController(); + FreeDrop _isFreeDrop = FreeDrop.unselected; + + int _numberOfTickets = 0; + int _price = 0; + + set setNumberOfTickets(int numberOfTickets) { + _numberOfTickets = numberOfTickets; + notifyListeners(); + } + + get numberOfTickets => _numberOfTickets; + + get isFreeDrop => _isFreeDrop; + + set setFreeDrop(FreeDrop freeDrop) { + _isFreeDrop = freeDrop; + notifyListeners(); + } + + set setPrice(int price) { + _price = price; + notifyListeners(); + } - FreeDrop isFreeDrop = FreeDrop.no; + get price => _price; void setSelectedDenom(Denom value) { _selectedDenom = value; diff --git a/evently/lib/models/denom.dart b/evently/lib/models/denom.dart index 1c94ab5551..c35b34ce8a 100644 --- a/evently/lib/models/denom.dart +++ b/evently/lib/models/denom.dart @@ -26,7 +26,7 @@ class Denom { if (symbol == kPylonSymbol) { return AmountFormatter(maxDigits: kMaxPriceLength); } - return AmountFormatter(maxDigits: kMaxPriceLength, isDecimal: true); + return AmountFormatter(maxDigits: kMaxPriceLength, isDecimal: false); } @override diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 31544ba913..8f6cb89e65 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -20,8 +20,6 @@ class DetailsScreen extends StatefulWidget { } class _DetailsScreenState extends State { - final _formKey = GlobalKey(); - @override void initState() { super.initState(); @@ -34,151 +32,148 @@ class _DetailsScreenState extends State { return Scaffold( body: SingleChildScrollView( child: Consumer( - builder: (_, provider, __) => Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const VerticalSpace(20), - MyStepsIndicator(currentStep: createEventViewModel.currentStep), - StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), - const VerticalSpace(20), - PageAppBar(onPressBack: () { - createEventViewModel.previousPage(); - }), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: GestureDetector( - onTap: () async { - _showDatePicker(onSelected: (DateTime? val) { - if (val == null) return; - provider.setStartDate = DateFormat("dd-MM-yyyy").format(val); - }); - }, - child: EventlyTextField( - enable: false, - label: LocaleKeys.start_date.tr(), - controller: TextEditingController(text: provider.startDate), - textCapitalization: TextCapitalization.sentences, - ), + builder: (_, provider, __) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: createEventViewModel.currentStep), + StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), + const VerticalSpace(20), + PageAppBar(onPressBack: () { + createEventViewModel.previousPage(); + }), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () async { + _showDatePicker(onSelected: (DateTime? val) { + if (val == null) return; + provider.setStartDate = DateFormat("dd-MM-yyyy").format(val); + }); + }, + child: EventlyTextField( + enable: false, + label: LocaleKeys.start_date.tr(), + controller: TextEditingController(text: provider.startDate), + textCapitalization: TextCapitalization.sentences, ), ), - HorizontalSpace(20.w), - Expanded( - child: GestureDetector( - onTap: () { - _showDatePicker(onSelected: (DateTime? val) { - if (val == null) return; - provider.setEndDate = DateFormat("dd-MM-yyyy").format(val); - }); + ), + HorizontalSpace(20.w), + Expanded( + child: GestureDetector( + onTap: () { + _showDatePicker(onSelected: (DateTime? val) { + if (val == null) return; + provider.setEndDate = DateFormat("dd-MM-yyyy").format(val); + }); + }, + child: EventlyTextField( + enable: false, + label: LocaleKeys.end_date.tr(), + controller: TextEditingController(text: provider.endDate), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; }, - child: EventlyTextField( - enable: false, - label: LocaleKeys.end_date.tr(), - controller: TextEditingController(text: provider.endDate), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, - ), ), ), - ], - ), - VerticalSpace(20.h), - Row( - children: [ - Expanded( - child: GestureDetector( - onTap: () { - _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { - if (timeOfDay == null) return; - provider.setStartTime = '${timeOfDay.hour}:${timeOfDay.minute}'; - }); + ), + ], + ), + VerticalSpace(20.h), + Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { + if (timeOfDay == null) return; + provider.setStartTime = '${timeOfDay.hour}:${timeOfDay.minute}'; + }); + }, + child: EventlyTextField( + enable: false, + label: LocaleKeys.start_time.tr(), + controller: TextEditingController(text: provider.startTime), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; }, - child: EventlyTextField( - enable: false, - label: LocaleKeys.start_time.tr(), - controller: TextEditingController(text: provider.startTime), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, - ), ), ), - HorizontalSpace(20.w), - Expanded( - child: GestureDetector( - onTap: () { - _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { - if (timeOfDay == null) return; - provider.setEndTime = '${timeOfDay.hour}:${timeOfDay.minute}'; - }); + ), + HorizontalSpace(20.w), + Expanded( + child: GestureDetector( + onTap: () { + _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { + if (timeOfDay == null) return; + provider.setEndTime = '${timeOfDay.hour}:${timeOfDay.minute}'; + }); + }, + child: EventlyTextField( + enable: false, + label: LocaleKeys.end_time.tr(), + controller: TextEditingController(text: provider.endTime), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; }, - child: EventlyTextField( - enable: false, - label: LocaleKeys.end_time.tr(), - controller: TextEditingController(text: provider.endTime), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, - ), ), ), - ], - ), - VerticalSpace(20.h), - EventlyTextField( - onChanged: (_) => provider.setLocation = _, - label: LocaleKeys.location.tr(), - hint: LocaleKeys.search_location.tr(), - controller: TextEditingController(text: provider.location) - ..selection = TextSelection.fromPosition( - TextPosition(offset: provider.location.length), - ), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, - ), - VerticalSpace(20.h), - EventlyTextField( - onChanged: (_) => provider.setDescription = _, - label: LocaleKeys.description.tr(), - hint: LocaleKeys.what_event_for.tr(), - controller: TextEditingController(text: provider.description) - ..selection = TextSelection.fromPosition( - TextPosition(offset: provider.description.length), - ), - textCapitalization: TextCapitalization.sentences, - noOfLines: 4, - ), - const VerticalSpace(80), - BottomButtons( - onPressContinue: () { - createEventViewModel.nextPage(); - }, - onPressSaveDraft: () {}, - isContinueEnable: provider.startDate.isNotEmpty && - provider.endDate.isNotEmpty && - provider.startTime.isNotEmpty && - provider.endTime.isNotEmpty && - provider.description.isNotEmpty && - provider.location.isNotEmpty, - ), - ], - ), + ), + ], + ), + VerticalSpace(20.h), + EventlyTextField( + onChanged: (_) => provider.setLocation = _, + label: LocaleKeys.location.tr(), + hint: LocaleKeys.search_location.tr(), + controller: TextEditingController(text: provider.location) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.location.length), + ), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + ), + VerticalSpace(20.h), + EventlyTextField( + onChanged: (_) => provider.setDescription = _, + label: LocaleKeys.description.tr(), + hint: LocaleKeys.what_event_for.tr(), + controller: TextEditingController(text: provider.description) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.description.length), + ), + textCapitalization: TextCapitalization.sentences, + noOfLines: 4, + ), + const VerticalSpace(80), + BottomButtons( + onPressContinue: () { + createEventViewModel.nextPage(); + }, + onPressSaveDraft: () {}, + isContinueEnable: provider.startDate.isNotEmpty && + provider.endDate.isNotEmpty && + provider.startTime.isNotEmpty && + provider.endTime.isNotEmpty && + provider.description.isNotEmpty && + provider.location.isNotEmpty, + ), + ], ), - ], - ), + ), + ], ), )), ); diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index c0d8c037db..1e321ece31 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -24,8 +24,6 @@ class OverViewScreen extends StatefulWidget { } class _OverViewScreenState extends State { - final _formKey = GlobalKey(); - @override void initState() { super.initState(); @@ -38,120 +36,117 @@ class _OverViewScreenState extends State { return Scaffold( body: SingleChildScrollView( child: Consumer( - builder: (_, provider, __) => Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const VerticalSpace(20), - MyStepsIndicator(currentStep: createEventViewModel.currentStep), - StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), - const VerticalSpace(20), - PageAppBar(onPressBack: () { - Navigator.of(context).pop(); - }), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - EventlyTextField( - controller: TextEditingController(text: provider.eventName) - ..selection = TextSelection.fromPosition( - TextPosition(offset: provider.eventName.length), - ), - onChanged: (_) => provider.setEventName = _, - label: LocaleKeys.event_name.tr(), - hint: LocaleKeys.what_your_event_called.tr(), - textCapitalization: TextCapitalization.sentences, - ), - VerticalSpace(20.h), - EventlyTextField( - controller: TextEditingController(text: provider.hostName) - ..selection = TextSelection.fromPosition( - TextPosition(offset: provider.hostName.length), - ), - onChanged: (String _) => provider.setHostName = _, - label: LocaleKeys.host_name.tr(), - hint: LocaleKeys.who_hosting_it.tr(), - textCapitalization: TextCapitalization.sentences, - ), - VerticalSpace(20.h), - Text( - LocaleKeys.thumbnail.tr(), - textAlign: TextAlign.start, - style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), - ), - SizedBox(height: 4.h), - ClipRRect( - borderRadius: BorderRadius.only(topRight: Radius.circular(8.r), topLeft: Radius.circular(8.r)), - child: Center( - child: DottedBorder( - borderType: BorderType.Rect, - dashPattern: const [10, 6], - color: EventlyAppTheme.kTextDarkPurple, - strokeWidth: 3.h, - child: provider.thumbnail != null - ? SizedBox( - height: 180, + builder: (_, provider, __) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: createEventViewModel.currentStep), + StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), + const VerticalSpace(20), + PageAppBar(onPressBack: () { + Navigator.of(context).pop(); + }), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + EventlyTextField( + controller: TextEditingController(text: provider.eventName) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.eventName.length), + ), + onChanged: (_) => provider.setEventName = _, + label: LocaleKeys.event_name.tr(), + hint: LocaleKeys.what_your_event_called.tr(), + textCapitalization: TextCapitalization.sentences, + ), + VerticalSpace(20.h), + EventlyTextField( + controller: TextEditingController(text: provider.hostName) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.hostName.length), + ), + onChanged: (String _) => provider.setHostName = _, + label: LocaleKeys.host_name.tr(), + hint: LocaleKeys.who_hosting_it.tr(), + textCapitalization: TextCapitalization.sentences, + ), + VerticalSpace(20.h), + Text( + LocaleKeys.thumbnail.tr(), + textAlign: TextAlign.start, + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), + ), + SizedBox(height: 4.h), + ClipRRect( + borderRadius: BorderRadius.only(topRight: Radius.circular(8.r), topLeft: Radius.circular(8.r)), + child: Center( + child: DottedBorder( + borderType: BorderType.Rect, + dashPattern: const [10, 6], + color: EventlyAppTheme.kTextDarkPurple, + strokeWidth: 3.h, + child: provider.thumbnail != null + ? SizedBox( + height: 180, + width: double.infinity, + child: Stack( + alignment: Alignment.center, + children: [ + Image.file( + provider.thumbnail!, + fit: BoxFit.cover, + height: 176, + width: double.infinity, + ), + GestureDetector( + onTap: () => provider.pickThumbnail(), + child: SvgPicture.asset( + SVGUtils.kSvgUpload, + colorFilter: const ColorFilter.mode(EventlyAppTheme.kWhite, BlendMode.srcIn), + )), + ], + ), + ) + : GestureDetector( + onTap: () => provider.pickThumbnail(), + child: Container( width: double.infinity, - child: Stack( - alignment: Alignment.center, + height: 180, + padding: EdgeInsets.symmetric(vertical: 20.w), + child: Column( children: [ - Image.file( - provider.thumbnail!, - fit: BoxFit.cover, - height: 176, - width: double.infinity, + Text( + LocaleKeys.tap_select.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kTextDarkPurple), + ), + VerticalSpace(10.h), + SvgPicture.asset(SVGUtils.kSvgUpload), + VerticalSpace(10.h), + Text( + LocaleKeys.mb_limit.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kTextDarkPurple), ), - GestureDetector( - onTap: () => provider.pickThumbnail(), - child: SvgPicture.asset( - SVGUtils.kSvgUpload, - colorFilter: const ColorFilter.mode(EventlyAppTheme.kWhite, BlendMode.srcIn), - )), ], ), - ) - : GestureDetector( - onTap: () => provider.pickThumbnail(), - child: Container( - width: double.infinity, - height: 180, - padding: EdgeInsets.symmetric(vertical: 20.w), - child: Column( - children: [ - Text( - LocaleKeys.tap_select.tr(), - style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kTextDarkPurple), - ), - VerticalSpace(10.h), - SvgPicture.asset(SVGUtils.kSvgUpload), - VerticalSpace(10.h), - Text( - LocaleKeys.mb_limit.tr(), - style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kTextDarkPurple), - ), - ], - ), - ), ), - ), + ), ), ), - const VerticalSpace(80), - BottomButtons( - onPressContinue: () { - createEventViewModel.nextPage(); - }, - onPressSaveDraft: () {}, - isContinueEnable: provider.isOverviewEnable, - ) - ], - ), + ), + const VerticalSpace(80), + BottomButtons( + onPressContinue: () { + createEventViewModel.nextPage(); + }, + onPressSaveDraft: () {}, + isContinueEnable: provider.isOverviewEnable, + ) + ], ), - ], - ), + ), + ], ), )), ); diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index dfffa389b3..fc9efab293 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -25,21 +25,17 @@ class PerksScreen extends StatefulWidget { } class _PerksScreenState extends State { - final _formKey = GlobalKey(); - @override Widget build(BuildContext context) { final createEventViewModel = context.watch(); return Scaffold( - body: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) => SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints(minHeight: constraints.maxHeight), - child: Consumer( - builder: (_, provider, __) => Form( - key: _formKey, - child: Column( + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: Consumer( + builder: (_, provider, __) => Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -115,7 +111,9 @@ class _PerksScreenState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: BottomButtons( - onPressContinue: () {}, + onPressContinue: () { + createEventViewModel.nextPage(); + }, onPressSaveDraft: () {}, isContinueEnable: true, ), @@ -126,7 +124,7 @@ class _PerksScreenState extends State { ), ), ), - )); + ); } } diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 97adf14b48..0631f29f4b 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -26,12 +26,10 @@ class PriceScreen extends StatefulWidget { } class _PriceScreenState extends State { - final _formKey = GlobalKey(); final ValueNotifier _numberOfTicketsFieldError = ValueNotifier(""); @override void dispose() { - _formKey.currentState?.dispose(); super.dispose(); } @@ -45,164 +43,180 @@ class _PriceScreenState extends State { final homeViewModel = context.watch(); return Scaffold( - body: SingleChildScrollView( - child: Consumer(builder: (_, provider, __) { - return Form( - key: _formKey, - child: Column( + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: Consumer(builder: (_, provider, __) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const VerticalSpace(20), - MyStepsIndicator(currentStep: homeViewModel.currentStep), - const VerticalSpace(5), - StepLabels(currentPage: homeViewModel.currentPage, currentStep: homeViewModel.currentStep), - const VerticalSpace(10), - const VerticalSpace(20), - PageAppBar( - onPressBack: () { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - homeViewModel.previousPage(); - }, - ), - ScreenResponsive( - mobileScreen: (context) => const VerticalSpace(6), - tabletScreen: (context) => const VerticalSpace(30), - ), - VerticalSpace(10.h), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - LocaleKeys.is_free_drop.tr(), - style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), - ), - SizedBox( - height: 10.h, - ), - Row(children: [ - InkWell( - onTap: () { - // provider.updateIsFreeDropStatus(FreeDrop.yes); - }, - child: Container( - width: 140.w, - height: 30.h, - decoration: BoxDecoration( - color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kBlue : EventlyAppTheme.kTransparent, - border: Border.all(color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kBlue : EventlyAppTheme.kBlack, width: 2.w), - ), - child: Center( - child: Text( - LocaleKeys.yes.tr(), - style: TextStyle( - color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kWhite : EventlyAppTheme.kBlack, + Column( + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: homeViewModel.currentStep), + const VerticalSpace(5), + StepLabels(currentPage: homeViewModel.currentPage, currentStep: homeViewModel.currentStep), + const VerticalSpace(10), + const VerticalSpace(20), + PageAppBar( + onPressBack: () { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + homeViewModel.previousPage(); + }, + ), + ScreenResponsive( + mobileScreen: (context) => const VerticalSpace(6), + tabletScreen: (context) => const VerticalSpace(30), + ), + VerticalSpace(10.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + LocaleKeys.is_free_drop.tr(), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w700), + ), + SizedBox(height: 10.h), + Row(children: [ + InkWell( + onTap: () => provider.setFreeDrop = FreeDrop.yes, + child: Container( + width: 140.w, + height: 30.h, + decoration: BoxDecoration( + color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kBlue : EventlyAppTheme.kTransparent, + border: Border.all(color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kBlue : EventlyAppTheme.kBlack, width: 2.w), + ), + child: Center( + child: Text( + LocaleKeys.yes.tr(), + style: TextStyle( + color: provider.isFreeDrop == FreeDrop.yes ? EventlyAppTheme.kWhite : EventlyAppTheme.kBlack, + ), + ), ), ), ), - ), - ), - SizedBox( - width: 30.w, - ), - InkWell( - onTap: () {}, - child: Container( - width: 140.w, - height: 30.h, - decoration: BoxDecoration( - color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kBlue : EventlyAppTheme.kTransparent, - border: Border.all(color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kBlue : EventlyAppTheme.kBlack, width: 2.w), + SizedBox( + width: 30.w, ), - child: Center( - child: Text( - LocaleKeys.no.tr(), - style: TextStyle( - color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kWhite : EventlyAppTheme.kBlack, + InkWell( + onTap: () => provider.setFreeDrop = FreeDrop.no, + child: Container( + width: 140.w, + height: 30.h, + decoration: BoxDecoration( + color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kBlue : EventlyAppTheme.kTransparent, + border: Border.all(color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kBlue : EventlyAppTheme.kBlack, width: 2.w), + ), + child: Center( + child: Text( + LocaleKeys.no.tr(), + style: TextStyle( + color: provider.isFreeDrop == FreeDrop.no ? EventlyAppTheme.kWhite : EventlyAppTheme.kBlack, + ), + ), ), ), ), - ), - ), - ]), - if (provider.isFreeDrop != FreeDrop.unselected) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (provider.isFreeDrop == FreeDrop.no) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - VerticalSpace(20.h), - EventlyPriceInputField( - key: ValueKey("${provider.selectedDenom.name}-amount"), - inputFormatters: [FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(kMaxPriceLength), provider.selectedDenom.getFormatter()], - controller: provider.priceController, - validator: (value) { - return null; - }, - ), - Text( - LocaleKeys.network_fee_listed_price_occur_on_chain.tr(), - style: TextStyle(color: EventlyAppTheme.kTextDarkPurple, fontSize: 14.sp, fontWeight: FontWeight.w800), - ), - VerticalSpace(20.h), - EventlyTextField( - label: LocaleKeys.number_tickets.tr(), - controller: TextEditingController(), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, - ), - ValueListenableBuilder( - valueListenable: _numberOfTicketsFieldError, - builder: (_, String artNameFieldError, __) { - if (artNameFieldError.isEmpty) { - return const SizedBox.shrink(); - } - return Padding( - padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), - child: Text( - artNameFieldError, - style: TextStyle( - fontSize: 12.sp, - color: Colors.red, + ]), + if (provider.isFreeDrop != FreeDrop.unselected) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: provider.isFreeDrop == FreeDrop.no + ? [ + VerticalSpace(20.h), + EventlyPriceInputField( + key: ValueKey("${provider.selectedDenom.name}-amount"), + inputFormatters: [FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(kMaxPriceLength), provider.selectedDenom.getFormatter()], + onChange: (_) => provider.setPrice = int.parse(_), + controller: TextEditingController(text: provider.price.toString()) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.price.toString().length), ), + ), + Text( + LocaleKeys.network_fee_listed_price_occur_on_chain.tr(), + style: TextStyle(color: EventlyAppTheme.kTextDarkPurple, fontSize: 14.sp, fontWeight: FontWeight.w800), + ), + VerticalSpace(20.h), + EventlyTextField( + keyboardType: TextInputType.number, + label: LocaleKeys.number_tickets.tr(), + controller: TextEditingController(text: provider.numberOfTickets.toString()) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.numberOfTickets.toString().length), + ), + textCapitalization: TextCapitalization.sentences, + onChanged: (_) => _.isEmpty ? provider.setNumberOfTickets = 0 : provider.setNumberOfTickets = int.parse(_), + ), + ValueListenableBuilder( + valueListenable: _numberOfTicketsFieldError, + builder: (_, String artNameFieldError, __) { + if (artNameFieldError.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 2.h), + child: Text( + artNameFieldError, + style: TextStyle( + fontSize: 12.sp, + color: Colors.red, + ), + ), + ); + }, + ), + Align( + alignment: Alignment.centerRight, + child: Text( + LocaleKeys.maximum_1000.tr(), + style: TextStyle(color: EventlyAppTheme.kTextDarkPurple, fontSize: 14.sp, fontWeight: FontWeight.w800), + textAlign: TextAlign.right, ), - ); - }, - ), - Align( - alignment: Alignment.centerRight, - child: Text( - LocaleKeys.maximum_1000.tr(), - style: TextStyle(color: EventlyAppTheme.kTextDarkPurple, fontSize: 14.sp, fontWeight: FontWeight.w800), - textAlign: TextAlign.right, - ), - ), - ], - ), - ], - ), - ScreenResponsive( - mobileScreen: (_) => VerticalSpace(0.1.sh), - tabletScreen: (_) => VerticalSpace(0.05.sh), + ), + ] + : [ + VerticalSpace(20.h), + EventlyTextField( + keyboardType: TextInputType.number, + label: LocaleKeys.number_tickets.tr(), + controller: TextEditingController(text: provider.numberOfTickets.toString()) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.numberOfTickets.toString().length), + ), + textCapitalization: TextCapitalization.sentences, + onChanged: (_) => _.isEmpty ? provider.setNumberOfTickets = 0 : provider.setNumberOfTickets = int.parse(_), + ), + ], + ), + ], ), - VerticalSpace(20.h), - BottomButtons( - onPressContinue: () {}, - onPressSaveDraft: () {}, - isContinueEnable: false, - ) - ], - ), + ), + ], ), + VerticalSpace(20.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: BottomButtons( + onPressContinue: () {}, + onPressSaveDraft: () {}, + isContinueEnable: provider.isFreeDrop == FreeDrop.unselected + ? false + : provider.isFreeDrop == FreeDrop.yes + ? provider.numberOfTickets > 0 + : provider.numberOfTickets > 0 && provider.price > 0, + ), + ) ], - ), - ); - }), + ); + }), + ), ), - ); + )); } } diff --git a/evently/lib/widgets/evently_price_input_field.dart b/evently/lib/widgets/evently_price_input_field.dart index 636a07a14c..d6843671f8 100644 --- a/evently/lib/widgets/evently_price_input_field.dart +++ b/evently/lib/widgets/evently_price_input_field.dart @@ -11,11 +11,18 @@ import '../main.dart'; import '../models/denom.dart'; class EventlyPriceInputField extends StatelessWidget { - const EventlyPriceInputField({super.key, this.controller, this.validator, this.inputFormatters = const []}); + const EventlyPriceInputField({ + super.key, + this.controller, + this.validator, + this.inputFormatters = const [], + required this.onChange, + }); final TextEditingController? controller; final String? Function(String?)? validator; final List inputFormatters; + final ValueChanged onChange; @override Widget build(BuildContext context) { @@ -37,19 +44,28 @@ class EventlyPriceInputField extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: TextFormField( - style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kTextGrey), - controller: controller, - validator: validator, - minLines: 1, - keyboardType: TextInputType.number, - inputFormatters: inputFormatters, - decoration: InputDecoration( - hintText: "\$21.99", - hintStyle: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kTextGrey), - border: const OutlineInputBorder(borderSide: BorderSide.none), - floatingLabelBehavior: FloatingLabelBehavior.always, - contentPadding: EdgeInsets.fromLTRB(10.w, 0.h, 10.w, 0.h)))), + child: TextFormField( + onChanged: (_) => onChange(_), + style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kTextGrey), + controller: controller, + validator: validator, + minLines: 1, + keyboardType: TextInputType.number, + inputFormatters: inputFormatters, + decoration: InputDecoration( + hintText: "\$21.99", + hintStyle: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kTextGrey), + border: const OutlineInputBorder(borderSide: BorderSide.none), + floatingLabelBehavior: FloatingLabelBehavior.always, + contentPadding: EdgeInsets.fromLTRB( + 10.w, + 0.h, + 10.w, + 0.h, + ), + ), + ), + ), const _CurrencyDropDown() ], ), @@ -87,7 +103,6 @@ class _CurrencyDropDown extends StatelessWidget { onChanged: (String? data) { if (data != null) { final value = provider.supportedDenomList.firstWhere((denom) => denom.symbol == data); - provider.priceController.clear(); provider.setSelectedDenom(value); } }, From e0ee09321d7d545be911cece38741a8d55e407e9 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 13 May 2024 16:25:54 +0500 Subject: [PATCH 072/161] feat: host view preview screen --- .../lib/screens/host_view_ticket_preview.dart | 344 +++++++++--------- evently/lib/screens/price_screen.dart | 4 +- 2 files changed, 176 insertions(+), 172 deletions(-) diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 08fb71519d..0cc58835c3 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -39,189 +39,191 @@ class _HostTicketPreviewState extends State { ), ), body: SingleChildScrollView( - child: ClipRRect( - borderRadius: BorderRadius.circular(10.r), - child: Container( - margin: EdgeInsets.symmetric(vertical: 20.w, horizontal: 20.w), - decoration: BoxDecoration( - image: const DecorationImage(image: AssetImage(PngUtils.kHostPreview), fit: BoxFit.fitHeight), - borderRadius: BorderRadius.circular(10.r), - ), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(), - child: ClipRRect( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(14), - topLeft: Radius.circular(14), - ), - child: Stack( - alignment: Alignment.bottomLeft, - children: [ - Image.asset( - PngUtils.kPhantom, - fit: BoxFit.cover, - ), - Padding( - padding: EdgeInsets.only(bottom: 10.h, left: 10.w), - child: Text( - 'The Phantom of the Opera', - style: TextStyle(fontSize: 20.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w700), - ), - ) - ], - ), - ), - ), - VerticalSpace(10.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'DATE', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Text( - 'Jul 04, 2022', - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ), - ], + child: Consumer( + builder: (_, provider, __) => ClipRRect( + borderRadius: BorderRadius.circular(10.r), + child: Container( + margin: EdgeInsets.symmetric(vertical: 20.w, horizontal: 20.w), + decoration: BoxDecoration( + image: const DecorationImage(image: AssetImage(PngUtils.kHostPreview), fit: BoxFit.fitHeight), + borderRadius: BorderRadius.circular(10.r), + ), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(), + child: ClipRRect( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(14), + topLeft: Radius.circular(14), ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + child: Stack( + alignment: Alignment.bottomLeft, children: [ - Text( - 'Time', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Text( - '7:15 PM', - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + Image.asset( + PngUtils.kPhantom, + fit: BoxFit.cover, ), + Padding( + padding: EdgeInsets.only(bottom: 10.h, left: 10.w), + child: Text( + provider.eventName, + style: TextStyle(fontSize: 20.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w700), + ), + ) ], ), - ], + ), ), - ), - VerticalSpace(20.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'LOCATION', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - ConstrainedBox( - constraints: BoxConstraints(maxWidth: 1.sw / 2.4), - child: Text( - '245 W 44th St New York, NY 10036', + VerticalSpace(10.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'DATE', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + provider.startDate, style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - maxLines: 2, ), - ), - ], - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'SEAT', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '6A', - style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ), - Text( - 'Room 3', - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ) - ], - ), - ], - ), - ], + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Time', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + provider.startTime, + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], + ), + ], + ), ), - ), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'PERKS', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Row( - children: [ - SvgPicture.asset(SVGUtils.kDiamond), - SizedBox(width: 5.w), - Text( - 'x 3', - style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), + VerticalSpace(20.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'LOCATION', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 1.sw / 2.4), + child: Text( + provider.location, + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + maxLines: 2, ), - SizedBox(width: 5.w), - Text( - 'Redeem', - style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kGreenText, fontWeight: FontWeight.bold), - ) - ], - ) - ], - ), - ], + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'SEAT', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '6A', + style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + Text( + 'Room 3', + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ) + ], + ), + ], + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'PERKS', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Row( + children: [ + SvgPicture.asset(SVGUtils.kDiamond), + SizedBox(width: 5.w), + Text( + 'x ${provider.perks.length}', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), + ), + SizedBox(width: 5.w), + Text( + 'Redeem', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kGreenText, fontWeight: FontWeight.bold), + ) + ], + ) + ], + ), + ], + ), + ), + VerticalSpace(16.h), + Image.asset(PngUtils.kDottedLine), + VerticalSpace(40.h), + Text( + 'Scan QR to enter', + style: TextStyle( + color: EventlyAppTheme.kWhite, + fontWeight: FontWeight.bold, + fontSize: 15.sp, + ), ), - ), - VerticalSpace(16.h), - Image.asset(PngUtils.kDottedLine), - VerticalSpace(40.h), - Text( - 'Scan QR to enter', - style: TextStyle( - color: EventlyAppTheme.kWhite, - fontWeight: FontWeight.bold, - fontSize: 15.sp, + VerticalSpace(10.h), + Container( + decoration: const BoxDecoration(color: EventlyAppTheme.kBlack), + width: 338, + height: 338, ), - ), - VerticalSpace(10.h), - Container( - decoration: const BoxDecoration(color: EventlyAppTheme.kBlack), - width: 338, - height: 338, - ), - SizedBox(height: 20.h), - ], + SizedBox(height: 20.h), + ], + ), ), ), ), diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 0631f29f4b..3db25b2b1d 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -203,7 +203,9 @@ class _PriceScreenState extends State { Padding( padding: EdgeInsets.symmetric(horizontal: 20.w), child: BottomButtons( - onPressContinue: () {}, + onPressContinue: () { + homeViewModel.nextPage(); + }, onPressSaveDraft: () {}, isContinueEnable: provider.isFreeDrop == FreeDrop.unselected ? false From 892a71f3bd1e6ad0e47a488a44c03b6f2dfae4ff Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 13 May 2024 16:30:26 +0500 Subject: [PATCH 073/161] feat: publish button enable --- evently/i18n/de.json | 3 ++- evently/i18n/en-US.json | 3 ++- evently/i18n/es.json | 3 ++- evently/i18n/ru-RU.json | 3 ++- evently/lib/generated/locale_keys.g.dart | 2 ++ .../screens/custom_widgets/bottom_buttons.dart | 4 +++- evently/lib/screens/host_view_ticket_preview.dart | 15 +++++++++++---- 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 756503c09a..f02d08ce0c 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -56,5 +56,6 @@ "start": "Anfang", "free_shirt": "1 free t-shirt", "free_gift": "Free Gift", - "free_drink": "1 free drink" + "free_drink": "1 free drink", + "publish" : "Publish" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 6cef40ae5f..9c6a3d4733 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -56,5 +56,6 @@ "start": "Start", "free_shirt": "1 free t-shirt", "free_gift": "Free Gift", - "free_drink": "1 free drink" + "free_drink": "1 free drink", + "publish" : "Publish" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 56fb8558b1..4a4084f507 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -56,5 +56,6 @@ "start": "Comienzo", "free_shirt": "1 free t-shirt", "free_gift": "Free Gift", - "free_drink": "1 free drink" + "free_drink": "1 free drink", + "publish" : "Publish" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 0cfb75ca3d..e584dea16b 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -56,5 +56,6 @@ "start": "Начинать", "free_shirt": "1 free t-shirt", "free_gift": "Free Gift", - "free_drink": "1 free drink" + "free_drink": "1 free drink", + "publish" : "Publish" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 6f4dadf90c..03d9acb19a 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -56,5 +56,7 @@ abstract class LocaleKeys { static const open_pylons_app = 'open_pylons_app'; static const create_stripe_description = 'create_stripe_description'; static const start = 'start'; + static const free_gift = 'free_gift'; + static const free_drink = 'free_drink'; } diff --git a/evently/lib/screens/custom_widgets/bottom_buttons.dart b/evently/lib/screens/custom_widgets/bottom_buttons.dart index ef09ff97f8..6c8809be16 100644 --- a/evently/lib/screens/custom_widgets/bottom_buttons.dart +++ b/evently/lib/screens/custom_widgets/bottom_buttons.dart @@ -12,18 +12,20 @@ class BottomButtons extends StatelessWidget { required this.onPressContinue, required this.onPressSaveDraft, required this.isContinueEnable, + this.clipBtnTxt, }); final VoidCallback onPressContinue; final VoidCallback onPressSaveDraft; final bool isContinueEnable; + final String? clipBtnTxt; @override Widget build(BuildContext context) { return Column( children: [ ClippedButton( - title: LocaleKeys.continue_key.tr(), + title: clipBtnTxt ?? LocaleKeys.continue_key.tr(), bgColor: isContinueEnable ? EventlyAppTheme.kBlue : EventlyAppTheme.kGery03, textColor: EventlyAppTheme.kWhite, onPressed: () { diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 0cc58835c3..65dc35e0cd 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -32,10 +32,17 @@ class _HostTicketPreviewState extends State { bottomNavigationBar: Container( padding: EdgeInsets.symmetric(horizontal: 30.w), height: 110.h, - child: BottomButtons( - onPressContinue: () {}, - onPressSaveDraft: () {}, - isContinueEnable: false, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + BottomButtons( + onPressContinue: () {}, + onPressSaveDraft: () {}, + isContinueEnable: true, + clipBtnTxt: LocaleKeys.publish.tr(), + ), + ], ), ), body: SingleChildScrollView( From 371768106d030649c7394a2dba34d7c1b6c89057 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 13 May 2024 17:17:36 +0500 Subject: [PATCH 074/161] feat: publish button --- evently/lib/screens/host_view_ticket_preview.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 65dc35e0cd..e2ccd8e32d 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -37,7 +37,9 @@ class _HostTicketPreviewState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ BottomButtons( - onPressContinue: () {}, + onPressContinue: () { + onPublishPressed(); + }, onPressSaveDraft: () {}, isContinueEnable: true, clipBtnTxt: LocaleKeys.publish.tr(), From 487e599286c2bc3e8ea545137cf53fa9f3fe5d04 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 14 May 2024 15:30:05 +0500 Subject: [PATCH 075/161] feat: dart sdk from ../dart_sdk --- .../lib/screens/host_view_ticket_preview.dart | 1 - evently/lib/screens/overview_screen.dart | 11 +-- evently/pubspec.lock | 77 +++++++------------ evently/pubspec.yaml | 10 ++- 4 files changed, 38 insertions(+), 61 deletions(-) diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index e2ccd8e32d..c5e25ad33a 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -7,7 +7,6 @@ import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/space_utils.dart'; -import 'package:evently/widgets/pylon_loading_animation.dart'; import 'package:evently/widgets/show_wallet_install_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 1e321ece31..0a1d3e8b55 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -101,11 +101,12 @@ class _OverViewScreenState extends State { width: double.infinity, ), GestureDetector( - onTap: () => provider.pickThumbnail(), - child: SvgPicture.asset( - SVGUtils.kSvgUpload, - colorFilter: const ColorFilter.mode(EventlyAppTheme.kWhite, BlendMode.srcIn), - )), + onTap: () => provider.pickThumbnail(), + child: SvgPicture.asset( + SVGUtils.kSvgUpload, + color: EventlyAppTheme.kWhite, + ), + ), ], ), ) diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 09c999facf..9d93b09ee9 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -33,14 +33,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" - bitstream: - dependency: transitive - description: - name: bitstream - sha256: "19a6b9f2d148010ae0c4149294cb1ad89cc212af961956dc4d2e931e0eadf963" - url: "https://pub.dev" - source: hosted - version: "1.1.0" boolean_selector: dependency: transitive description: @@ -311,10 +303,18 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" + sha256: "6ff9fa12892ae074092de2fa6a9938fb21dbabfdaa2ff57dc697ff912fc8d4b2" + url: "https://pub.dev" + source: hosted + version: "1.1.6" + flutter_svg_provider: + dependency: "direct main" + description: + name: flutter_svg_provider + sha256: cbb2d02fd9feb70fc30221fc36a7ee5347f1cceae6b0c99ab4fa011bbd9f1f7f url: "https://pub.dev" source: hosted - version: "2.0.10+1" + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -353,10 +353,10 @@ packages: dependency: "direct main" description: name: google_fonts - sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82 + sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "4.0.4" graphs: dependency: transitive description: @@ -369,10 +369,10 @@ packages: dependency: transitive description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "0.13.6" http_multi_server: dependency: transitive description: @@ -393,26 +393,26 @@ packages: dependency: "direct main" description: name: image_cropper - sha256: db779a8b620cd509874cb0e2a8bdc8649177f8f5ca46c13273ceaffe071e3f4a + sha256: "542c3453109d16bcc388e43ae2276044d2cd6a6d20c68bdcff2c94ab9363ea15" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "4.0.1" image_cropper_for_web: dependency: transitive description: name: image_cropper_for_web - sha256: ba67de40a98b3294084eed0b025b557cb594356e1171c9a830b340527dbd5e5f + sha256: "89c936aa772a35b69ca67b78049ae9fa163a4fb8da2f6dee3893db8883fb49d2" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "2.0.0" image_cropper_platform_interface: dependency: transitive description: name: image_cropper_platform_interface - sha256: ee160d686422272aa306125f3b6fb1c1894d9b87a5e20ed33fa008e7285da11e + sha256: b232175c132b2f7ede3e1f101652bcd635cb4079a77c6dded8e6d32e6578d685 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "4.0.0" injectable: dependency: "direct main" description: @@ -449,10 +449,10 @@ packages: dependency: transitive description: name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.6.7" json_annotation: dependency: transitive description: @@ -688,10 +688,9 @@ packages: pylons_sdk: dependency: "direct main" description: - name: pylons_sdk - sha256: "448b4a88a7ac578918f856e283a34c8cfdb14709e626a5ae3f6c2fddab2d8c3b" - url: "https://pub.dev" - source: hosted + path: "../dart_sdk" + relative: true + source: path version: "0.1.4" rational: dependency: transitive @@ -962,30 +961,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" - vector_graphics: - dependency: transitive - description: - name: vector_graphics - sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" - url: "https://pub.dev" - source: hosted - version: "1.1.11+1" - vector_graphics_codec: - dependency: transitive - description: - name: vector_graphics_codec - sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da - url: "https://pub.dev" - source: hosted - version: "1.1.11+1" - vector_graphics_compiler: - dependency: transitive - description: - name: vector_graphics_compiler - sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" - url: "https://pub.dev" - source: hosted - version: "1.1.11+1" vector_math: dependency: transitive description: @@ -1060,4 +1035,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.3.4 <4.0.0" - flutter: ">=3.19.2" + flutter: ">=3.19.0" diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index 557eef9e4d..b91c6f749d 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -37,8 +37,10 @@ dependencies: cupertino_icons: ^1.0.6 easy_localization: ^3.0.6 flutter_screenutil: ^5.9.0 - google_fonts: ^6.2.1 - flutter_svg: ^2.0.10+1 + google_fonts: ^4.0.3 + image_cropper: ^4.0.1 + flutter_svg: ^1.0.3 + flutter_svg_provider: ^1.0.3 get_it: ^7.7.0 injectable_generator: ^2.6.1 build_runner: ^2.4.9 @@ -47,10 +49,10 @@ dependencies: steps_indicator: ^1.3.0 dotted_border: ^2.1.0 file_picker: ^5.0.1 - image_cropper: ^6.0.0 equatable: ^2.0.5 dartz: ^0.10.1 - pylons_sdk: ^0.1.4 + pylons_sdk: + path: ../dart_sdk shared_preferences: ^2.2.3 dev_dependencies: From bf5dacea273325877a55f4f8ca37a5d0f1259965 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 14 May 2024 15:43:35 +0500 Subject: [PATCH 076/161] feat: get profile using local dart_sdk --- wallet/lib/ipc/handler/handlers/get_profile_handler.dart | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/wallet/lib/ipc/handler/handlers/get_profile_handler.dart b/wallet/lib/ipc/handler/handlers/get_profile_handler.dart index 36628b2e9b..6047110b79 100644 --- a/wallet/lib/ipc/handler/handlers/get_profile_handler.dart +++ b/wallet/lib/ipc/handler/handlers/get_profile_handler.dart @@ -8,7 +8,6 @@ import 'package:pylons_wallet/model/balance.dart'; import 'package:pylons_wallet/modules/Pylonstech.pylons.pylons/module/client/pylons/item.pb.dart'; import 'package:pylons_wallet/stores/wallet_store.dart'; - class GetProfileHandler implements BaseHandler { @override SdkIpcMessage sdkIpcMessage; @@ -38,13 +37,7 @@ class Profile { final List items; final List supportedCoins; - Profile( - {required this.address, - required this.username, - required this.coins, - required this.stripeExists, - required this.items, - required this.supportedCoins}); + Profile({required this.address, required this.username, required this.coins, required this.stripeExists, required this.items, required this.supportedCoins}); Map toJson() => { "address": address, From af0e881506789137b8dfa32c5111beabb1b7483b Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 14 May 2024 15:44:14 +0500 Subject: [PATCH 077/161] feat: get profile using loacal dart sdk --- evently/lib/screens/host_view_ticket_preview.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index c5e25ad33a..30b79c033e 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -270,14 +270,14 @@ class _HostTicketPreviewState extends State { return; } - final bool isRecipeCreated = await viewModel.createRecipe(event: viewModel.event); - pylonsLoadingAnimation.hide(); - if (!isRecipeCreated) { - return; - } + // final bool isRecipeCreated = await viewModel.createRecipe(event: viewModel.event); + // pylonsLoadingAnimation.hide(); + // if (!isRecipeCreated) { + // return; + // } navigator.popUntil((route) { - return route.settings.name == RouteUtil.kCreateEvent; + return route.settings.name == RouteUtil.kRouteEventHub; }); } From ad7bf6313d81926a3af40bb2748a02684b237493 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Wed, 15 May 2024 10:21:33 +0500 Subject: [PATCH 078/161] feat: receipe creation --- evently/lib/evently_provider.dart | 4 - evently/lib/models/events.dart | 114 +++++++++++++++++- .../lib/screens/host_view_ticket_preview.dart | 10 +- evently/lib/utils/constants.dart | 20 +++ 4 files changed, 137 insertions(+), 11 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index c00b2cc4c4..416b96935f 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -227,10 +227,6 @@ class EventlyProvider extends ChangeNotifier { final isCookBookCreated = await createCookbook(); if (isCookBookCreated) { - // this delay is added to wait the transaction is settle - // on the blockchain - Future.delayed(const Duration(milliseconds: 800)); - // get device cookbook id _cookbookId = repository.getCookbookId(); notifyListeners(); } else { diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 9b1d884f74..4a57fd6e05 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -1,8 +1,15 @@ + + +import 'package:evently/evently_provider.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:pylons_sdk/low_level.dart'; +import 'package:fixnum/fixnum.dart'; + class Event { Event({ required this.eventName, required this.hostName, - required this.url, + required this.thumbnail, required this.startDate, required this.endDate, required this.startTime, @@ -19,7 +26,7 @@ class Event { final String hostName; - final String url; + final String thumbnail; final String startDate; @@ -40,4 +47,107 @@ class Event { final String price; final List listOfPerks; + + +} + + + +extension CreateRecipe on Event { + Recipe createRecipe({ + required String cookbookId, + required String recipeId, + required FreeDrop isFreeDrop, + required String symbol, + required List hashtagsList, + required String tradePercentage, + required String price, + }) { + return Recipe( + cookbookId: cookbookId, + id: recipeId, + nodeVersion: Int64(1), + name: eventName.trim(), + description: description.trim(), + version: kVersion, + coinInputs: [ + if (isFreeDrop == FreeDrop.yes) + CoinInput() + else + CoinInput( + coins: [Coin(amount: price, denom: symbol)], + ) + ], + itemInputs: [], + costPerBlock: Coin(denom: kUpylon, amount: costPerBlock), + entries: EntriesList( + coinOutputs: [], + itemOutputs: [ + ItemOutput( + id: kEventlyEvent, + doubles: [ + DoubleParam( + key: kResidual, + weightRanges: [ + DoubleWeightRange( + lower: tradePercentage, + upper: tradePercentage, + weight: Int64(1), + ) + ], + ) + ], + longs: [ + LongParam( + key: kQuantity, + weightRanges: [ + IntWeightRange( + lower: Int64(int.parse(numberOfTickets.replaceAll(",", "").trim())), + upper: Int64(int.parse(numberOfTickets.replaceAll(",", "").trim())), + weight: Int64(1), + ) + ], + ), + LongParam(key: kWidth, weightRanges: [_buildIntWeightRange(upperRange: width, lowerRange: width)]), + LongParam(key: kHeight, weightRanges: [_buildIntWeightRange(upperRange: height, lowerRange: height)]), + LongParam( + key: kDuration, weightRanges: [_buildIntWeightRange(upperRange: duration, lowerRange: duration)]), + ], + strings: [ + StringParam(key: kName, value: name.trim()), + StringParam(key: kAppType, value: kEasel), + StringParam(key: kDescription, value: description.trim()), + StringParam( + key: kHashtags, + value: hashtagsList.join(kHashtagSymbol), + ), + StringParam(key: kNFTFormat, value: assetType), + StringParam(key: kNFTURL, value: url), + StringParam(key: kThumbnailUrl, value: thumbnailUrl), + StringParam(key: kCreator, value: creator.trim()), + StringParam(key: kFileExtension, value: fileExtension.trim()), + StringParam(key: kCID, value: cid), + StringParam(key: kFileSize, value: fileSize), + StringParam(key: kRealWorld, value: "false"), + ], + mutableStrings: [], + transferFee: [Coin(denom: kPylonSymbol, amount: transferFeeAmount)], + tradePercentage: tradePercentage, + tradeable: true, + amountMinted: Int64(), + quantity: Int64(int.parse(quantity.replaceAll(",", "").trim())), + ), + ], + itemModifyOutputs: [], + ), + outputs: [ + WeightedOutputs(entryIds: [kEaselNFT], weight: Int64(1)) + ], + blockInterval: Int64(), + enabled: true, + // extraInfo: kExtraInfo, + ); + } + + } diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 30b79c033e..348f15846b 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -270,11 +270,11 @@ class _HostTicketPreviewState extends State { return; } - // final bool isRecipeCreated = await viewModel.createRecipe(event: viewModel.event); - // pylonsLoadingAnimation.hide(); - // if (!isRecipeCreated) { - // return; - // } + final bool isRecipeCreated = await viewModel.createRecipe(event: viewModel.event); + pylonsLoadingAnimation.hide(); + if (!isRecipeCreated) { + return; + } navigator.popUntil((route) { return route.settings.name == RouteUtil.kRouteEventHub; diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 956c4119e4..37c2a70a02 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -51,6 +51,26 @@ const kFreeShirt = "free_shirt"; const kFreeGift = "free_gift"; const kFreeDrink = "free_drink"; +const kVersion = "v0.2.0"; +const kUpylon = "upylon"; +const String costPerBlock = '0'; +const kEventlyEvent = "Evently_Event"; +const kResidual = "Residual"; +const kQuantity = "Quantity"; +const String transferFeeAmount = '1'; +const kEaselNFT = "Easel_NFT"; + +/// Event String keys +const kEventName = ""; +const kEventHostName = ""; +const kThumbnail = ""; +const kStartDate = ""; +const kEndDate = ""; +const kStartTime = ""; +const kEndTime = ""; + + + /// ```SVG assets class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; From 0b60620d3311a1066c7b96350d34cb0e49e057cb Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 15 May 2024 10:46:34 +0500 Subject: [PATCH 079/161] feat: placing every data in Item input list of strings --- evently/lib/evently_provider.dart | 8 ++++ evently/lib/models/events.dart | 73 +++++++------------------------ evently/lib/utils/constants.dart | 23 +++++----- 3 files changed, 36 insertions(+), 68 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 416b96935f..af05e9aea4 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -235,6 +235,14 @@ class EventlyProvider extends ChangeNotifier { } _recipeId = repository.autoGenerateEventlyId(); + event.createRecipe( + cookbookId: _cookbookId!, + recipeId: _recipeId, + isFreeDrop: isFreeDrop, + symbol: selectedDenom.symbol, + hashtagsList: perks, + price: price, + ); return Future.value(true); } diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 4a57fd6e05..fc9e0290d7 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -1,6 +1,5 @@ - - import 'package:evently/evently_provider.dart'; +import 'package:evently/models/perks_model.dart'; import 'package:evently/utils/constants.dart'; import 'package:pylons_sdk/low_level.dart'; import 'package:fixnum/fixnum.dart'; @@ -47,20 +46,15 @@ class Event { final String price; final List listOfPerks; - - } - - extension CreateRecipe on Event { Recipe createRecipe({ required String cookbookId, required String recipeId, required FreeDrop isFreeDrop, required String symbol, - required List hashtagsList, - required String tradePercentage, + required List hashtagsList, required String price, }) { return Recipe( @@ -85,69 +79,34 @@ extension CreateRecipe on Event { itemOutputs: [ ItemOutput( id: kEventlyEvent, - doubles: [ - DoubleParam( - key: kResidual, - weightRanges: [ - DoubleWeightRange( - lower: tradePercentage, - upper: tradePercentage, - weight: Int64(1), - ) - ], - ) - ], - longs: [ - LongParam( - key: kQuantity, - weightRanges: [ - IntWeightRange( - lower: Int64(int.parse(numberOfTickets.replaceAll(",", "").trim())), - upper: Int64(int.parse(numberOfTickets.replaceAll(",", "").trim())), - weight: Int64(1), - ) - ], - ), - LongParam(key: kWidth, weightRanges: [_buildIntWeightRange(upperRange: width, lowerRange: width)]), - LongParam(key: kHeight, weightRanges: [_buildIntWeightRange(upperRange: height, lowerRange: height)]), - LongParam( - key: kDuration, weightRanges: [_buildIntWeightRange(upperRange: duration, lowerRange: duration)]), - ], + doubles: [], + longs: [], strings: [ - StringParam(key: kName, value: name.trim()), - StringParam(key: kAppType, value: kEasel), + StringParam(key: kEventName, value: eventName.trim()), + StringParam(key: kEventHostName, value: hostName.trim()), + StringParam(key: kEventHostName, value: thumbnail.trim()), + StringParam(key: kStartDate, value: startDate.trim()), + StringParam(key: kEndDate, value: endDate.trim()), + StringParam(key: kStartTime, value: startTime.trim()), + StringParam(key: kEndTime, value: endTime.trim()), + StringParam(key: kLocation, value: location.trim()), StringParam(key: kDescription, value: description.trim()), - StringParam( - key: kHashtags, - value: hashtagsList.join(kHashtagSymbol), - ), - StringParam(key: kNFTFormat, value: assetType), - StringParam(key: kNFTURL, value: url), - StringParam(key: kThumbnailUrl, value: thumbnailUrl), - StringParam(key: kCreator, value: creator.trim()), - StringParam(key: kFileExtension, value: fileExtension.trim()), - StringParam(key: kCID, value: cid), - StringParam(key: kFileSize, value: fileSize), - StringParam(key: kRealWorld, value: "false"), + StringParam(key: kPerks, value: kPerks.trim()), + StringParam(key: kNumberOfTickets, value: numberOfTickets.trim()), + StringParam(key: kPrice, value: price.trim()), ], mutableStrings: [], transferFee: [Coin(denom: kPylonSymbol, amount: transferFeeAmount)], - tradePercentage: tradePercentage, tradeable: true, amountMinted: Int64(), - quantity: Int64(int.parse(quantity.replaceAll(",", "").trim())), ), ], itemModifyOutputs: [], ), - outputs: [ - WeightedOutputs(entryIds: [kEaselNFT], weight: Int64(1)) - ], + outputs: [], blockInterval: Int64(), enabled: true, // extraInfo: kExtraInfo, ); } - - } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 37c2a70a02..72a691d8c5 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -45,7 +45,6 @@ const kCookbookId = 'cookbook_id'; const kUsername = 'username'; const kHostName = 'artistName'; - ///perks const kFreeShirt = "free_shirt"; const kFreeGift = "free_gift"; @@ -58,18 +57,20 @@ const kEventlyEvent = "Evently_Event"; const kResidual = "Residual"; const kQuantity = "Quantity"; const String transferFeeAmount = '1'; -const kEaselNFT = "Easel_NFT"; /// Event String keys -const kEventName = ""; -const kEventHostName = ""; -const kThumbnail = ""; -const kStartDate = ""; -const kEndDate = ""; -const kStartTime = ""; -const kEndTime = ""; - - +const kEventName = "kEventName"; +const kEventHostName = "kEventHostName"; +const kThumbnail = "kThumbnail"; +const kStartDate = "kStartDate"; +const kEndDate = "kEndDate"; +const kStartTime = "kStartTime"; +const kEndTime = "kEndTime"; +const kLocation = "kLocation"; +const kDescription = "kDescription"; +const kPerks = "kPerks"; +const kNumberOfTickets = "kNumberOfTickets"; +const kPrice = "kPrice"; /// ```SVG assets class SVGUtils { From c08da5c76e0c86c4026ac71d6fc9bf6d182af0b3 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 15 May 2024 11:45:10 +0500 Subject: [PATCH 080/161] feat: toString --- evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/evently_provider.dart | 38 ++++++++++++++++--- evently/lib/generated/locale_keys.g.dart | 1 + evently/lib/models/events.dart | 2 +- evently/lib/models/perks_model.dart | 5 +++ .../lib/screens/host_view_ticket_preview.dart | 2 +- evently/lib/utils/constants.dart | 3 ++ 10 files changed, 51 insertions(+), 12 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index f02d08ce0c..b140eac4eb 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -57,5 +57,6 @@ "free_shirt": "1 free t-shirt", "free_gift": "Free Gift", "free_drink": "1 free drink", - "publish" : "Publish" + "publish" : "Publish", + "recipe_created": "Recipe созданный" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 9c6a3d4733..53e3d3bd59 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -57,5 +57,6 @@ "free_shirt": "1 free t-shirt", "free_gift": "Free Gift", "free_drink": "1 free drink", - "publish" : "Publish" + "publish" : "Publish", + "recipe_created": "Recipe created" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 4a4084f507..a539f4f67c 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -57,5 +57,6 @@ "free_shirt": "1 free t-shirt", "free_gift": "Free Gift", "free_drink": "1 free drink", - "publish" : "Publish" + "publish" : "Publish", + "recipe_created": "Recipe creada" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index e584dea16b..706db0ce31 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -57,5 +57,6 @@ "free_shirt": "1 free t-shirt", "free_gift": "Free Gift", "free_drink": "1 free drink", - "publish" : "Publish" + "publish" : "Publish", + "recipe_created": "Recipe созданный" } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index af05e9aea4..eae5102946 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -190,7 +190,7 @@ class EventlyProvider extends ChangeNotifier { String currentUserName = ""; bool stripeAccountExists = false; - late Event event; + String? _cookbookId; String _recipeId = ""; @@ -215,8 +215,8 @@ class EventlyProvider extends ChangeNotifier { return sdkResponse; } - Future createRecipe({required Event event}) async { - final scaffoldMessengerState = navigatorKey.getState(); + Future createRecipe() async { + // final scaffoldMessengerState = navigatorKey.getState(); _cookbookId = repository.getCookbookId(); @@ -235,16 +235,42 @@ class EventlyProvider extends ChangeNotifier { } _recipeId = repository.autoGenerateEventlyId(); - event.createRecipe( + + final event = Event( + eventName: eventName, + hostName: hostName, + thumbnail: thumbnail!.path, + startDate: startDate, + endDate: endDate, + startTime: startTime, + endTime: endTime, + location: location, + description: description, + isFreeDrop: isFreeDrop, + numberOfTickets: numberOfTickets, + price: price, + listOfPerks: perks.map((e) => e.toString()).toList()); + + final recipe = event.createRecipe( cookbookId: _cookbookId!, recipeId: _recipeId, isFreeDrop: isFreeDrop, symbol: selectedDenom.symbol, - hashtagsList: perks, + perksList: perks, price: price, ); - return Future.value(true); + final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); + + if (!response.success) { + // scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); + return false; + } + // scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); + // final nftFromRecipe = NFT.fromRecipe(recipe); + // GetIt.I.get<>().updatePublishedNFTList(nft: nftFromRecipe); + // deleteNft(nft.id); + return false; } /// send createCookBook tx message to the wallet app diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 03d9acb19a..a79682b06a 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -58,5 +58,6 @@ abstract class LocaleKeys { static const start = 'start'; static const free_gift = 'free_gift'; static const free_drink = 'free_drink'; + static const recipe_created = 'recipe_created'; } diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index fc9e0290d7..c092c61c8b 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -54,7 +54,7 @@ extension CreateRecipe on Event { required String recipeId, required FreeDrop isFreeDrop, required String symbol, - required List hashtagsList, + required List perksList, required String price, }) { return Recipe( diff --git a/evently/lib/models/perks_model.dart b/evently/lib/models/perks_model.dart index ef148513e7..bc9d10c233 100644 --- a/evently/lib/models/perks_model.dart +++ b/evently/lib/models/perks_model.dart @@ -13,4 +13,9 @@ class PerksModel { name: perksModel.name, description: description, ); + + @override + String toString() { + return 'PerksModel{name: $name, description: $description}'; + } } diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 348f15846b..8ac54606b0 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -270,7 +270,7 @@ class _HostTicketPreviewState extends State { return; } - final bool isRecipeCreated = await viewModel.createRecipe(event: viewModel.event); + final bool isRecipeCreated = await viewModel.createRecipe(); pylonsLoadingAnimation.hide(); if (!isRecipeCreated) { return; diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 72a691d8c5..c30adf8dd3 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -72,6 +72,9 @@ const kPerks = "kPerks"; const kNumberOfTickets = "kNumberOfTickets"; const kPrice = "kPrice"; +const kErrRecipe = 'Recipe error :'; + + /// ```SVG assets class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; From 00af513a3cadd2559565b5e83584349c4a7ad348 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 15 May 2024 15:19:30 +0500 Subject: [PATCH 081/161] feat: quick node for thumbnail uploading --- evently/.gitignore | 2 + .../third_party_services/quick_node.dart | 131 ++++++++++++++++++ evently/lib/utils/di/register_modules.dart | 4 + evently/pubspec.lock | 8 ++ evently/pubspec.yaml | 1 + 5 files changed, 146 insertions(+) create mode 100644 evently/lib/services/third_party_services/quick_node.dart diff --git a/evently/.gitignore b/evently/.gitignore index 29a3a5017f..392bfbbe7a 100644 --- a/evently/.gitignore +++ b/evently/.gitignore @@ -41,3 +41,5 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +lib/env.dart diff --git a/evently/lib/services/third_party_services/quick_node.dart b/evently/lib/services/third_party_services/quick_node.dart new file mode 100644 index 0000000000..d4739795dd --- /dev/null +++ b/evently/lib/services/third_party_services/quick_node.dart @@ -0,0 +1,131 @@ +import 'dart:async'; + +import 'package:dio/dio.dart'; +import 'package:evently/env.dart'; +import 'package:injectable/injectable.dart'; + +typedef OnUploadProgressCallback = void Function(UploadProgress uploadProgress); + +class UploadProgress { + int totalSize; + int sendSize; + double uploadedProgressData; + + UploadProgress({ + required this.totalSize, + required this.sendSize, + required this.uploadedProgressData, + }); +} + +abstract class QuickNode { + /// Upload a new object to IPFS and pins it for permanent storage on the network. + /// [UploadIPFSInput] as an input + /// [UploadIPFSOutput] as an output + Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); + + /// these are the list of extension required + static List listOfQuickNodeAllowedExtension() => ['jpg', 'png', 'heif', 'jpeg', 'gif']; + + /// this method is used to get the content type while making request input to quick node + static String getContentType(String fileExtension) { + final dict = { + ///* images + "jpg": "image/jpg", + "png": "image/png", + 'heif': "image/heif", + 'jpeg': "image/jpeg", + 'gif': "image/gif", + }; + return dict[fileExtension]!; + } +} + +@LazySingleton(as: QuickNode) +class QuickNodeImpl extends QuickNode { + QuickNodeImpl({required this.httpClient}); + + final Dio httpClient; + + @override + Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + try { + httpClient.options.headers['x-api-key'] = xApiKey; + + final response = await httpClient.post( + 'https://api.quicknode.com/ipfs/rest/v1/s3/put-object', + data: FormData.fromMap({ + 'Body': await MultipartFile.fromFile(uploadIPFSInput.filePath), + 'Key': uploadIPFSInput.fileName, + 'ContentType': uploadIPFSInput.contentType, + }), + onSendProgress: (uploaded, total) { + final double uploadedPercentage = uploaded / total; + onUploadProgressCallback( + UploadProgress(totalSize: total, sendSize: uploaded, uploadedProgressData: uploadedPercentage), + ); + }, + ); + + final uploadIPFSOutput = UploadIPFSOutput.fromJson(response.data as Map); + + // return //StorageResponseModel.fromQuickNode(uploadIPFSOutput: uploadIPFSOutput); + } catch (e) { + throw Exception('Failed to upload file: $e'); + } + } +} + +class UploadIPFSInput { + final String fileName; + final String filePath; + final String contentType; + + UploadIPFSInput({ + required this.fileName, + required this.filePath, + required this.contentType, + }); +} + +class UploadIPFSOutput { + final String? requestId; + final String? status; + final String? created; + final Pin? pin; + final Info? info; + final List? delegates; + + UploadIPFSOutput({this.requestId, this.status, this.created, this.pin, this.info, this.delegates}); + + factory UploadIPFSOutput.fromJson(Map json) { + return UploadIPFSOutput( + requestId: json['requestid'] as String?, + status: json['status'] as String?, + created: json['created'] as String?, + pin: Pin.fromJson(json['pin'] as Map), + info: Info.fromJson(json['info'] as Map), + delegates: [], + ); + } +} + +class Pin { + String? cid; + String? name; + + Pin({this.cid, this.name}); + + factory Pin.fromJson(Map json) => Pin( + cid: json['cid'] as String?, + name: json['name'] as String?, + ); +} + +class Info { + final String? size; + + Info({this.size}); + + factory Info.fromJson(Map json) => Info(size: json['size'] as String?); +} diff --git a/evently/lib/utils/di/register_modules.dart b/evently/lib/utils/di/register_modules.dart index 7eafd2668e..43af19d53a 100644 --- a/evently/lib/utils/di/register_modules.dart +++ b/evently/lib/utils/di/register_modules.dart @@ -3,6 +3,7 @@ // Injectable is a convenient code generator for get_it. // All you have to do now is annotate your injectable classes with @injectable and let the generator do the work. // This class is use to generate code to register objects on app start +import 'package:dio/dio.dart'; import 'package:file_picker/file_picker.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:injectable/injectable.dart'; @@ -14,4 +15,7 @@ abstract class RegisterModule { @LazySingleton() FilePicker get filePicker => FilePicker.platform; + + @LazySingleton() + Dio get dio => Dio(); } diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 9d93b09ee9..a27085d01f 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -193,6 +193,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" + url: "https://pub.dev" + source: hosted + version: "5.4.3+1" dotted_border: dependency: "direct main" description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index b91c6f749d..68e089ac9f 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -54,6 +54,7 @@ dependencies: pylons_sdk: path: ../dart_sdk shared_preferences: ^2.2.3 + dio: ^5.4.3+1 dev_dependencies: flutter_test: From e7d46d3bb0e276185a69c63b7fef759df4566427 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 15 May 2024 16:09:55 +0500 Subject: [PATCH 082/161] feat: repositoty and remote data sources for the uploading thumbnail --- evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/generated/locale_keys.g.dart | 1 + .../lib/models/storage_response_model.dart | 111 ++++++++++++++++++ evently/lib/repository/repository.dart | 22 +++- .../datasources/remote_datasource.dart | 23 ++++ .../third_party_services/quick_node.dart | 10 +- evently/lib/utils/di/di.config.dart | 39 +++--- evently/lib/utils/failure/failure.dart | 7 ++ 11 files changed, 199 insertions(+), 26 deletions(-) create mode 100644 evently/lib/models/storage_response_model.dart create mode 100644 evently/lib/services/datasources/remote_datasource.dart diff --git a/evently/i18n/de.json b/evently/i18n/de.json index b140eac4eb..7429990434 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -58,5 +58,6 @@ "free_gift": "Free Gift", "free_drink": "1 free drink", "publish" : "Publish", - "recipe_created": "Recipe созданный" + "recipe_created": "Recipe созданный", + "update_failed": "Upload Fehlgeschlagen" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 53e3d3bd59..6e7cda9719 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -58,5 +58,6 @@ "free_gift": "Free Gift", "free_drink": "1 free drink", "publish" : "Publish", - "recipe_created": "Recipe created" + "recipe_created": "Recipe created", + "update_failed": "Upload Failed" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index a539f4f67c..4d4f7ac878 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -58,5 +58,6 @@ "free_gift": "Free Gift", "free_drink": "1 free drink", "publish" : "Publish", - "recipe_created": "Recipe creada" + "recipe_created": "Recipe creada", + "update_failed": "Subida ha fallado" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 706db0ce31..5699723f32 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -58,5 +58,6 @@ "free_gift": "Free Gift", "free_drink": "1 free drink", "publish" : "Publish", - "recipe_created": "Recipe созданный" + "recipe_created": "Recipe созданный", + "update_failed": "Загрузка не удалась" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index a79682b06a..561dcc6fd1 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -59,5 +59,6 @@ abstract class LocaleKeys { static const free_gift = 'free_gift'; static const free_drink = 'free_drink'; static const recipe_created = 'recipe_created'; + static const update_failed = 'update_failed'; } diff --git a/evently/lib/models/storage_response_model.dart b/evently/lib/models/storage_response_model.dart new file mode 100644 index 0000000000..8b0b760cd0 --- /dev/null +++ b/evently/lib/models/storage_response_model.dart @@ -0,0 +1,111 @@ +import 'package:evently/services/third_party_services/quick_node.dart'; + +class StorageResponseModel { + bool? ok; + Value? value; + + StorageResponseModel({this.ok, this.value}); + + StorageResponseModel.fromJson(Map json) { + ok = json['ok'] as bool?; + value = json['value'] != null ? Value.fromJson(json['value'] as Map) : null; + } + + Map toJson() { + final map = {}; + map['ok'] = ok; + if (value != null) { + map['value'] = value?.toJson(); + } + return map; + } + + factory StorageResponseModel.initial() { + return StorageResponseModel(); + } + + factory StorageResponseModel.fromQuickNode({required UploadIPFSOutput uploadIPFSOutput}) { + return StorageResponseModel( + ok: true, + value: Value( + cid: uploadIPFSOutput.pin?.cid, + created: uploadIPFSOutput.created, + size: int.parse( + uploadIPFSOutput.info?.size ?? '0', + ), + pin: Pin( + cid: uploadIPFSOutput.pin?.cid, + created: uploadIPFSOutput.created, + size: int.parse( + uploadIPFSOutput.info?.size ?? '0', + ), + status: uploadIPFSOutput.status, + )), + ); + } +} + +class Value { + String? cid; + String? created; + String? type; + String? scope; + int? size; + Pin? pin; + + Value({ + this.cid, + this.created, + this.type, + this.scope, + this.size, + this.pin, + }); + + Value.fromJson(Map json) { + cid = json['cid'] as String?; + created = json['created'] as String?; + type = json['type'] as String?; + scope = json['scope'] as String?; + size = json['size'] as int?; + pin = json['pin'] != null ? Pin.fromJson(json['pin'] as Map) : null; + } + + Map toJson() { + final map = {}; + map['cid'] = cid; + map['created'] = created; + map['type'] = type; + map['scope'] = scope; + map['size'] = size; + if (pin != null) { + map['pin'] = pin?.toJson(); + } + return map; + } +} + +class Pin { + String? cid; + String? created; + int? size; + String? status; + + Pin({this.cid, this.created, this.size, this.status}); + + Pin.fromJson(Map json) { + cid = json['cid'] as String?; + created = json['created'] as String?; + size = json['size'] as int?; + status = json['status'] as String?; + } + + Map toJson() { + final map = {}; + map['cid'] = cid; + map['created'] = created; + map['size'] = size; + map['status'] = status; + return map; + } +} diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 2706d8d076..8cafb3c8aa 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -2,11 +2,15 @@ import 'package:dartz/dartz.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/models/picked_file_model.dart'; +import 'package:evently/models/storage_response_model.dart'; import 'package:evently/services/datasources/local_datasource.dart'; -import 'package:evently/utils/failure/failure.dart'; +import 'package:evently/services/datasources/remote_datasource.dart'; +import 'package:evently/services/third_party_services/quick_node.dart'; import 'package:evently/utils/file_utils_helper.dart'; import 'package:injectable/injectable.dart'; +import '../utils/failure/failure.dart'; + abstract class Repository { /// This function picks a file from device storage /// Input: [format] it is the file format which needs to be picked from local storage @@ -33,6 +37,11 @@ abstract class Repository { /// This method will generate easel Id for the NFT /// Output: [String] the id of the NFT that is going to be added in the recipe String autoGenerateEventlyId(); + + /// This method is used uploading provided file to the server using [QuickNode] + /// Input : [UploadIPFSInput] which needs to be uploaded + /// Output : [ApiResponse] the ApiResponse which can contain [success] or [error] response + Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); } @LazySingleton(as: Repository) @@ -40,10 +49,12 @@ class RepositoryImp implements Repository { RepositoryImp({ required this.fileUtilsHelper, required this.localDataSource, + required this.remoteDataSource, }); final FileUtilsHelper fileUtilsHelper; final LocalDataSource localDataSource; + final RemoteDataSource remoteDataSource; @override Future> pickFile() async { @@ -79,4 +90,13 @@ class RepositoryImp implements Repository { return localDataSource.autoGenerateEventlyId(); } + @override + Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + try { + final storageResponseModel = await remoteDataSource.uploadFileUsingQuickNode(uploadIPFSInput: uploadIPFSInput, onUploadProgressCallback: onUploadProgressCallback); + return Right(storageResponseModel); + } on Exception catch (_) { + return Left(CacheFailure(LocaleKeys.update_failed.tr())); + } + } } diff --git a/evently/lib/services/datasources/remote_datasource.dart b/evently/lib/services/datasources/remote_datasource.dart new file mode 100644 index 0000000000..b82bdd55e7 --- /dev/null +++ b/evently/lib/services/datasources/remote_datasource.dart @@ -0,0 +1,23 @@ +import 'package:evently/models/storage_response_model.dart'; +import 'package:evently/services/third_party_services/quick_node.dart'; +import 'package:injectable/injectable.dart'; + +abstract class RemoteDataSource { + /// This method is used uploading provided file to the server using [QuickNode] + /// Input : [UploadIPFSInput] which needs to be uploaded + /// Output : [Future>] the ApiResponse which can contain [success] or [error] response + Future uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); +} + +@LazySingleton(as: RemoteDataSource) +class RemoteDataSourceImpl extends RemoteDataSource { + RemoteDataSourceImpl({required this.quickNode}); + + final QuickNode quickNode; + + @override + Future uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + final response = await quickNode.uploadNewObjectToIPFS(uploadIPFSInput: uploadIPFSInput, onUploadProgressCallback: onUploadProgressCallback); + return response; + } +} diff --git a/evently/lib/services/third_party_services/quick_node.dart b/evently/lib/services/third_party_services/quick_node.dart index d4739795dd..4707240201 100644 --- a/evently/lib/services/third_party_services/quick_node.dart +++ b/evently/lib/services/third_party_services/quick_node.dart @@ -1,7 +1,7 @@ import 'dart:async'; - import 'package:dio/dio.dart'; import 'package:evently/env.dart'; +import 'package:evently/models/storage_response_model.dart'; import 'package:injectable/injectable.dart'; typedef OnUploadProgressCallback = void Function(UploadProgress uploadProgress); @@ -22,7 +22,7 @@ abstract class QuickNode { /// Upload a new object to IPFS and pins it for permanent storage on the network. /// [UploadIPFSInput] as an input /// [UploadIPFSOutput] as an output - Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); + Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); /// these are the list of extension required static List listOfQuickNodeAllowedExtension() => ['jpg', 'png', 'heif', 'jpeg', 'gif']; @@ -30,7 +30,6 @@ abstract class QuickNode { /// this method is used to get the content type while making request input to quick node static String getContentType(String fileExtension) { final dict = { - ///* images "jpg": "image/jpg", "png": "image/png", 'heif': "image/heif", @@ -48,7 +47,7 @@ class QuickNodeImpl extends QuickNode { final Dio httpClient; @override - Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { try { httpClient.options.headers['x-api-key'] = xApiKey; @@ -68,8 +67,7 @@ class QuickNodeImpl extends QuickNode { ); final uploadIPFSOutput = UploadIPFSOutput.fromJson(response.data as Map); - - // return //StorageResponseModel.fromQuickNode(uploadIPFSOutput: uploadIPFSOutput); + return StorageResponseModel.fromQuickNode(uploadIPFSOutput: uploadIPFSOutput); } catch (e) { throw Exception('Failed to upload file: $e'); } diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index aa017fe664..84941fbb75 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -8,17 +8,20 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:evently/evently_provider.dart' as _i10; -import 'package:evently/repository/repository.dart' as _i9; -import 'package:evently/services/datasources/local_datasource.dart' as _i6; -import 'package:evently/utils/di/register_modules.dart' as _i11; -import 'package:evently/utils/file_utils_helper.dart' as _i8; +import 'package:dio/dio.dart' as _i6; +import 'package:evently/evently_provider.dart' as _i13; +import 'package:evently/repository/repository.dart' as _i12; +import 'package:evently/services/datasources/local_datasource.dart' as _i7; +import 'package:evently/services/datasources/remote_datasource.dart' as _i11; +import 'package:evently/services/third_party_services/quick_node.dart' as _i10; +import 'package:evently/utils/di/register_modules.dart' as _i14; +import 'package:evently/utils/file_utils_helper.dart' as _i9; import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; import 'package:file_picker/file_picker.dart' as _i5; import 'package:get_it/get_it.dart' as _i1; import 'package:image_cropper/image_cropper.dart' as _i4; import 'package:injectable/injectable.dart' as _i2; -import 'package:shared_preferences/shared_preferences.dart' as _i7; +import 'package:shared_preferences/shared_preferences.dart' as _i8; extension GetItInjectableX on _i1.GetIt { // initializes the registration of main-scope dependencies inside of GetIt @@ -36,20 +39,26 @@ extension GetItInjectableX on _i1.GetIt { () => _i3.CreateEventViewModel()); gh.lazySingleton<_i4.ImageCropper>(() => registerModule.imageCropper); gh.lazySingleton<_i5.FilePicker>(() => registerModule.filePicker); - gh.lazySingleton<_i6.LocalDataSource>(() => _i6.LocalDataSourceImpl( - sharedPreferences: gh<_i7.SharedPreferences>())); - gh.lazySingleton<_i8.FileUtilsHelper>(() => _i8.FileUtilsHelperImpl( + gh.lazySingleton<_i6.Dio>(() => registerModule.dio); + gh.lazySingleton<_i7.LocalDataSource>(() => _i7.LocalDataSourceImpl( + sharedPreferences: gh<_i8.SharedPreferences>())); + gh.lazySingleton<_i9.FileUtilsHelper>(() => _i9.FileUtilsHelperImpl( imageCropper: gh<_i4.ImageCropper>(), filePicker: gh<_i5.FilePicker>(), )); - gh.lazySingleton<_i9.Repository>(() => _i9.RepositoryImp( - fileUtilsHelper: gh<_i8.FileUtilsHelper>(), - localDataSource: gh<_i6.LocalDataSource>(), + gh.lazySingleton<_i10.QuickNode>( + () => _i10.QuickNodeImpl(httpClient: gh<_i6.Dio>())); + gh.lazySingleton<_i11.RemoteDataSource>( + () => _i11.RemoteDataSourceImpl(quickNode: gh<_i10.QuickNode>())); + gh.lazySingleton<_i12.Repository>(() => _i12.RepositoryImp( + fileUtilsHelper: gh<_i9.FileUtilsHelper>(), + localDataSource: gh<_i7.LocalDataSource>(), + remoteDataSource: gh<_i11.RemoteDataSource>(), )); - gh.lazySingleton<_i10.EventlyProvider>( - () => _i10.EventlyProvider(repository: gh<_i9.Repository>())); + gh.lazySingleton<_i13.EventlyProvider>( + () => _i13.EventlyProvider(repository: gh<_i12.Repository>())); return this; } } -class _$RegisterModule extends _i11.RegisterModule {} +class _$RegisterModule extends _i14.RegisterModule {} diff --git a/evently/lib/utils/failure/failure.dart b/evently/lib/utils/failure/failure.dart index 31034608d1..c6cdf1afe5 100644 --- a/evently/lib/utils/failure/failure.dart +++ b/evently/lib/utils/failure/failure.dart @@ -12,3 +12,10 @@ class PickingFileFailure extends Failure { @override List get props => [message]; } + +class CacheFailure extends Failure { + const CacheFailure(super.message); + + @override + List get props => [message]; +} From c599e5a2e1a4db236f276b277a98dfce789aa31c Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 15 May 2024 17:22:47 +0500 Subject: [PATCH 083/161] feat: image uploading completed --- evently/assets/images/gifs/loading.gif | Bin 0 -> 107900 bytes evently/i18n/de.json | 4 +- evently/i18n/en-US.json | 4 +- evently/i18n/es.json | 4 +- evently/i18n/ru-RU.json | 4 +- evently/lib/evently_provider.dart | 43 ++++++-- evently/lib/generated/locale_keys.g.dart | 2 + evently/lib/screens/overview_screen.dart | 14 ++- evently/lib/utils/constants.dart | 2 + evently/lib/utils/extension_util.dart | 14 +++ evently/lib/utils/image_util.dart | 7 ++ .../lib/widgets/loading_with_progress.dart | 42 ++++++++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + evently/pubspec.lock | 96 ++++++++++++++++++ evently/pubspec.yaml | 3 + 15 files changed, 226 insertions(+), 15 deletions(-) create mode 100644 evently/assets/images/gifs/loading.gif create mode 100644 evently/lib/utils/image_util.dart create mode 100644 evently/lib/widgets/loading_with_progress.dart diff --git a/evently/assets/images/gifs/loading.gif b/evently/assets/images/gifs/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..adc3a7d6d794d334bed14f61ce5f297a05f934df GIT binary patch literal 107900 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 B;;H}u literal 0 HcmV?d00001 diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 7429990434..9bed4c9030 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -59,5 +59,7 @@ "free_drink": "1 free drink", "publish" : "Publish", "recipe_created": "Recipe созданный", - "update_failed": "Upload Fehlgeschlagen" + "update_failed": "Upload Fehlgeschlagen", + "uploading": "Hochladen ...", + "something_wrong_while_uploading": "Beim Hochladen ist etwas schief gelaufen. Bitte versuche es erneut." } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 6e7cda9719..25487d1b89 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -59,5 +59,7 @@ "free_drink": "1 free drink", "publish" : "Publish", "recipe_created": "Recipe created", - "update_failed": "Upload Failed" + "update_failed": "Upload Failed", + "uploading": "Uploading ...", + "something_wrong_while_uploading": "Something went wrong while uploading. Please try again." } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 4d4f7ac878..281c7ee53a 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -59,5 +59,7 @@ "free_drink": "1 free drink", "publish" : "Publish", "recipe_created": "Recipe creada", - "update_failed": "Subida ha fallado" + "update_failed": "Subida ha fallado", + "uploading": "Subiendo...", + "something_wrong_while_uploading": "Algo salió mal durante la carga. Inténtalo de nuevo." } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 5699723f32..20ed93a46b 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -59,5 +59,7 @@ "free_drink": "1 free drink", "publish" : "Publish", "recipe_created": "Recipe созданный", - "update_failed": "Загрузка не удалась" + "update_failed": "Загрузка не удалась", + "uploading": "Загрузка...", + "something_wrong_while_uploading": "Что-то пошло не так во время загрузки. Пожалуйста, попробуйте еще раз." } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index eae5102946..c72724d465 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -1,18 +1,26 @@ -import 'dart:io'; +import 'dart:async'; +import 'package:dartz/dartz.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/main.dart'; import 'package:evently/models/denom.dart'; import 'package:evently/models/events.dart'; import 'package:evently/models/perks_model.dart'; import 'package:evently/models/picked_file_model.dart'; +import 'package:evently/models/storage_response_model.dart'; import 'package:evently/repository/repository.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/extension_util.dart'; +import 'package:evently/utils/failure/failure.dart'; +import 'package:evently/widgets/loading_with_progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/low_level.dart'; +import 'services/third_party_services/quick_node.dart'; + enum FreeDrop { yes, no, unselected } @LazySingleton() @@ -26,10 +34,10 @@ class EventlyProvider extends ChangeNotifier { ///* overview screen variable String _eventName = ''; String _hostName = ''; - File? _thumbnail; + String? _thumbnail; bool _isOverviewEnable = false; - File? get thumbnail => _thumbnail; + String? get thumbnail => _thumbnail; String get eventName => _eventName; String get hostName => _hostName; bool get isOverviewEnable => _isOverviewEnable; @@ -46,7 +54,7 @@ class EventlyProvider extends ChangeNotifier { notifyListeners(); } - set setThumbnail(File? file) { + set setThumbnail(String? file) { _thumbnail = file; checkIsOverEnable(); notifyListeners(); @@ -173,6 +181,10 @@ class EventlyProvider extends ChangeNotifier { notifyListeners(); } + final StreamController _uploadProgressController = StreamController.broadcast(); + + Stream get uploadProgressStream => _uploadProgressController.stream; + void pickThumbnail() async { final pickedFile = await repository.pickFile(); @@ -185,7 +197,26 @@ class EventlyProvider extends ChangeNotifier { ); if (result.path.isEmpty) return; - setThumbnail = File(result.path); + + final loading = LoadingProgress()..showLoadingWithProgress(message: LocaleKeys.uploading.tr()); + + Either response; + response = await repository.uploadFileUsingQuickNode( + uploadIPFSInput: UploadIPFSInput(fileName: result.fileName, filePath: result!.path, contentType: QuickNode.getContentType(result.extension)), + onUploadProgressCallback: (value) { + _uploadProgressController.sink.add(value); + }, + ); + + if (response.isLeft()) { + loading.dismiss(); + LocaleKeys.something_wrong_while_uploading.tr().show(); + return; + } + final fileUploadResponse = response.getOrElse(() => StorageResponseModel.initial()); + loading.dismiss(); + + setThumbnail = "$ipfsDomain/${fileUploadResponse.value?.cid}"; } String currentUserName = ""; @@ -239,7 +270,7 @@ class EventlyProvider extends ChangeNotifier { final event = Event( eventName: eventName, hostName: hostName, - thumbnail: thumbnail!.path, + thumbnail: thumbnail!, startDate: startDate, endDate: endDate, startTime: startTime, diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 561dcc6fd1..a55f453afc 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -60,5 +60,7 @@ abstract class LocaleKeys { static const free_drink = 'free_drink'; static const recipe_created = 'recipe_created'; static const update_failed = 'update_failed'; + static const uploading = 'uploading'; + static const something_wrong_while_uploading = 'something_wrong_while_uploading'; } diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 0a1d3e8b55..d8df77987a 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -1,3 +1,4 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; @@ -15,6 +16,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; +import 'package:shimmer_animation/shimmer_animation.dart'; class OverViewScreen extends StatefulWidget { const OverViewScreen({super.key}); @@ -94,11 +96,13 @@ class _OverViewScreenState extends State { child: Stack( alignment: Alignment.center, children: [ - Image.file( - provider.thumbnail!, - fit: BoxFit.cover, - height: 176, - width: double.infinity, + CachedNetworkImage( + fit: BoxFit.fill, + imageUrl: provider.thumbnail!, + errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), + progressIndicatorBuilder: (context, _, progress) { + return Shimmer(color: EventlyAppTheme.kGrey04, child: const SizedBox.expand()); + }, ), GestureDetector( onTap: () => provider.pickThumbnail(), diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index c30adf8dd3..a8a8884c12 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -74,6 +74,8 @@ const kPrice = "kPrice"; const kErrRecipe = 'Recipe error :'; +/// ```URL constants +const ipfsDomain = 'https://ipfs.io/ipfs'; /// ```SVG assets class SVGUtils { diff --git a/evently/lib/utils/extension_util.dart b/evently/lib/utils/extension_util.dart index 1039c40ed5..0a8b41ad0c 100644 --- a/evently/lib/utils/extension_util.dart +++ b/evently/lib/utils/extension_util.dart @@ -32,3 +32,17 @@ extension ScaffoldMessengerKeyHelper on GlobalKey { return ScaffoldMessenger.maybeOf(navigatorKey.currentState!.overlay!.context); } } + +extension MyStringSnackBar on String { + void show({BuildContext? context}) { + ScaffoldMessenger.of(context ?? navigatorKey.currentState!.overlay!.context).showSnackBar( + SnackBar( + content: Text( + this, + ), + duration: const Duration(seconds: 3), + ), + ); + } +} + diff --git a/evently/lib/utils/image_util.dart b/evently/lib/utils/image_util.dart new file mode 100644 index 0000000000..b288e2b05b --- /dev/null +++ b/evently/lib/utils/image_util.dart @@ -0,0 +1,7 @@ +// ignore_for_file: non_constant_identifier_names + +class ImageUtil { + ImageUtil(); + + static String LOADING_GIF = 'assets/images/gifs/loading.gif'; +} diff --git a/evently/lib/widgets/loading_with_progress.dart b/evently/lib/widgets/loading_with_progress.dart new file mode 100644 index 0000000000..5ed272e31a --- /dev/null +++ b/evently/lib/widgets/loading_with_progress.dart @@ -0,0 +1,42 @@ +import 'dart:async'; + +import 'package:evently/evently_provider.dart'; +import 'package:evently/main.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/image_util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get_it/get_it.dart'; + +class LoadingProgress { + LoadingProgress(); + + void dismiss() { + navigatorKey.currentState?.pop(); + } + + final EventlyProvider easelProvider = GetIt.I.get(); + + Future showLoadingWithProgress({String? message}) { + if (navigatorKey.currentState?.overlay == null) { + return Completer() + .future; // return a fake future if state is screwy - this only ever happens during testing. todo: eliminate this hack + } + return showDialog( + context: navigatorKey.currentState!.overlay!.context, + barrierDismissible: true, + builder: (ctx) => PopScope( + canPop: false, + child: AlertDialog( + elevation: 0, + backgroundColor: EventlyAppTheme.kTransparent, + content: SizedBox( + width: 100.w, + height: 100.h, + child: Image.asset(ImageUtil.LOADING_GIF), + ), + ), + ), + ); + } +} diff --git a/evently/macos/Flutter/GeneratedPluginRegistrant.swift b/evently/macos/Flutter/GeneratedPluginRegistrant.swift index b19945c53d..63ad0d132c 100644 --- a/evently/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/evently/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,10 +7,12 @@ import Foundation import path_provider_foundation import shared_preferences_foundation +import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/evently/pubspec.lock b/evently/pubspec.lock index a27085d01f..c088c17954 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -105,6 +105,30 @@ packages: url: "https://pub.dev" source: hosted version: "8.9.2" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" + url: "https://pub.dev" + source: hosted + version: "3.3.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" + url: "https://pub.dev" + source: hosted + version: "1.2.0" characters: dependency: transitive description: @@ -278,6 +302,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" + url: "https://pub.dev" + source: hosted + version: "3.3.1" flutter_lints: dependency: "direct dev" description: @@ -549,6 +581,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" + url: "https://pub.dev" + source: hosted + version: "2.0.0" package_config: dependency: transitive description: @@ -716,6 +756,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" shared_preferences: dependency: "direct main" description: @@ -788,6 +836,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + shimmer_animation: + dependency: "direct main" + description: + name: shimmer_animation + sha256: "1f12d0f3fc20acbf2dcf8438b79f4268c523d55b2f3de55cf49e5bd3acbc5719" + url: "https://pub.dev" + source: hosted + version: "2.1.0+1" sky_engine: dependency: transitive description: flutter @@ -809,6 +865,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d + url: "https://pub.dev" + source: hosted + version: "2.3.3+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" + url: "https://pub.dev" + source: hosted + version: "2.5.4" stack_trace: dependency: transitive description: @@ -849,6 +929,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" term_glyph: dependency: transitive description: @@ -969,6 +1057,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" + url: "https://pub.dev" + source: hosted + version: "4.4.0" vector_math: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index 68e089ac9f..d1e4e4fa9e 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -55,6 +55,8 @@ dependencies: path: ../dart_sdk shared_preferences: ^2.2.3 dio: ^5.4.3+1 + cached_network_image: ^3.3.1 + shimmer_animation: 2.1.0+1 dev_dependencies: flutter_test: @@ -87,6 +89,7 @@ flutter: - i18n/es.json - assets/images/svg/ - assets/images/ + - assets/images/gifs/ fonts: - family: UniversalSans From c7c5f2d94c01fd5f975659852fd66cf0ab9c03de Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 10:43:47 +0500 Subject: [PATCH 084/161] feat: cookbook logic --- evently/lib/evently_provider.dart | 22 ++++---- evently/lib/models/events.dart | 56 ++++++++++++++++++- evently/lib/utils/extension_util.dart | 16 ++++++ .../viewmodels/create_event_viewmodel.dart | 2 + 4 files changed, 83 insertions(+), 13 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index c72724d465..753360ad16 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -202,7 +202,7 @@ class EventlyProvider extends ChangeNotifier { Either response; response = await repository.uploadFileUsingQuickNode( - uploadIPFSInput: UploadIPFSInput(fileName: result.fileName, filePath: result!.path, contentType: QuickNode.getContentType(result.extension)), + uploadIPFSInput: UploadIPFSInput(fileName: result.fileName, filePath: result.path, contentType: QuickNode.getContentType(result.extension)), onUploadProgressCallback: (value) { _uploadProgressController.sink.add(value); }, @@ -225,6 +225,8 @@ class EventlyProvider extends ChangeNotifier { String? _cookbookId; String _recipeId = ""; + String? get cookbookId => _cookbookId; + bool showStripeDialog() => !stripeAccountExists && _selectedDenom.symbol == kUsdSymbol && isFreeDrop == FreeDrop.no; ///* this method is used to get the profile @@ -247,7 +249,7 @@ class EventlyProvider extends ChangeNotifier { } Future createRecipe() async { - // final scaffoldMessengerState = navigatorKey.getState(); + final scaffoldMessengerState = navigatorKey.getState(); _cookbookId = repository.getCookbookId(); @@ -277,9 +279,9 @@ class EventlyProvider extends ChangeNotifier { endTime: endTime, location: location, description: description, - isFreeDrop: isFreeDrop, - numberOfTickets: numberOfTickets, - price: price, + isFreeDrop: "isFreeDrop", + numberOfTickets: numberOfTickets.toString(), + price: price.toString(), listOfPerks: perks.map((e) => e.toString()).toList()); final recipe = event.createRecipe( @@ -294,11 +296,11 @@ class EventlyProvider extends ChangeNotifier { final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); if (!response.success) { - // scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); + scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); return false; } - // scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); - // final nftFromRecipe = NFT.fromRecipe(recipe); + scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); + // final nftFromRecipe = Event.fromRecipe(recipe); // GetIt.I.get<>().updatePublishedNFTList(nft: nftFromRecipe); // deleteNft(nft.id); return false; @@ -307,7 +309,7 @@ class EventlyProvider extends ChangeNotifier { /// send createCookBook tx message to the wallet app /// return true or false depending on the response from the wallet app Future createCookbook() async { - _cookbookId = repository.autoGenerateCookbookId(); + _cookbookId = await repository.autoGenerateCookbookId(); final cookBook1 = Cookbook( creator: "", id: _cookbookId, @@ -321,7 +323,7 @@ class EventlyProvider extends ChangeNotifier { final response = await PylonsWallet.instance.txCreateCookbook(cookBook1); if (response.success) { - repository.saveCookBookGeneratorUsername(currentUserName); + repository.saveCookBookGeneratorUsername(currentUsername); return true; } diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index c092c61c8b..442755d644 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -1,11 +1,12 @@ +import 'package:equatable/equatable.dart'; import 'package:evently/evently_provider.dart'; import 'package:evently/models/perks_model.dart'; import 'package:evently/utils/constants.dart'; import 'package:pylons_sdk/low_level.dart'; import 'package:fixnum/fixnum.dart'; -class Event { - Event({ +class Event extends Equatable { + const Event({ required this.eventName, required this.hostName, required this.thumbnail, @@ -46,6 +47,55 @@ class Event { final String price; final List listOfPerks; + + @override + List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, isFreeDrop, numberOfTickets, price]; + + static Map _extractAttributeValues(List attributes) { + final Map attributeValues = {}; + for (final attribute in attributes) { + switch (attribute.key) { + case kEventName: + case kEventHostName: + case kThumbnail: + case kStartDate: + case kEndDate: + case kStartTime: + case kEndTime: + case kLocation: + case kDescription: + case kPerks: + case kNumberOfTickets: + case kPrice: + attributeValues[attribute.key] = attribute.value; + break; + default: + continue; + } + } + + return attributeValues; + } + + factory Event.fromRecipe(Recipe recipe) { + Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); + + return Event( + eventName: map[kEventName]!, + hostName: map[kEventHostName]!, + thumbnail: map[kThumbnail]!, + startDate: map[kStartDate]!, + endDate: map[kEndDate]!, + startTime: map[kStartTime]!, + endTime: map[kEndTime]!, + location: map[kLocation]!, + description: map[kDescription]!, + isFreeDrop: '', + numberOfTickets: map[kNumberOfTickets]!, + price: map[kPrice]!, + listOfPerks: [], + ); + } } extension CreateRecipe on Event { @@ -84,7 +134,7 @@ extension CreateRecipe on Event { strings: [ StringParam(key: kEventName, value: eventName.trim()), StringParam(key: kEventHostName, value: hostName.trim()), - StringParam(key: kEventHostName, value: thumbnail.trim()), + StringParam(key: kThumbnail, value: thumbnail.trim()), StringParam(key: kStartDate, value: startDate.trim()), StringParam(key: kEndDate, value: endDate.trim()), StringParam(key: kStartTime, value: startTime.trim()), diff --git a/evently/lib/utils/extension_util.dart b/evently/lib/utils/extension_util.dart index 0a8b41ad0c..ef67399174 100644 --- a/evently/lib/utils/extension_util.dart +++ b/evently/lib/utils/extension_util.dart @@ -46,3 +46,19 @@ extension MyStringSnackBar on String { } } +extension ScaffoldStateHelper on ScaffoldMessengerState { + void show({required String message}) { + this + ..hideCurrentSnackBar() + ..showSnackBar(SnackBar( + content: Text( + message, + textAlign: TextAlign.start, + style: TextStyle( + fontSize: 14.sp, + ), + ), + duration: const Duration(seconds: 2), + )); + } +} diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart index 85a277e351..e213efb59b 100644 --- a/evently/lib/viewmodels/create_event_viewmodel.dart +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -41,4 +41,6 @@ class CreateEventViewModel extends ChangeNotifier { void disposeControllers() { pageController.dispose(); } + + } From daa0d88c7a7556d66bf69e0a069b09b62c863b7c Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 10:48:00 +0500 Subject: [PATCH 085/161] feat: cookbook creation --- evently/lib/evently_provider.dart | 7 ++++--- evently/lib/repository/repository.dart | 10 ++++++---- .../lib/services/datasources/local_datasource.dart | 13 ++++++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 753360ad16..4cb670d428 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -222,9 +222,9 @@ class EventlyProvider extends ChangeNotifier { String currentUserName = ""; bool stripeAccountExists = false; - String? _cookbookId; - String _recipeId = ""; + + String? _cookbookId; String? get cookbookId => _cookbookId; bool showStripeDialog() => !stripeAccountExists && _selectedDenom.symbol == kUsdSymbol && isFreeDrop == FreeDrop.no; @@ -315,7 +315,7 @@ class EventlyProvider extends ChangeNotifier { id: _cookbookId, name: cookbookName, description: cookbookDesc, - developer: hostName, + developer: artistNameController.text, version: kVersionCookboox, supportEmail: supportedEmail, enabled: true, @@ -331,5 +331,6 @@ class EventlyProvider extends ChangeNotifier { return false; } + bool isDifferentUserName(String savedUserName) => currentUserName.isNotEmpty && savedUserName != currentUserName; } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 8cafb3c8aa..46ded6fd78 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -17,9 +17,9 @@ abstract class Repository { /// returns [PickedFileModel] the selected file or [Failure] if aborted Future> pickFile(); - /// This method will generate evently Id for the event - /// Output: [String] the id of the Event that is going to be added in the recipe - String autoGenerateCookbookId(); + /// This method will generate the cookbook id for the easel app + /// Output: [String] the id of the cookbook which will contains all the NFTs. + Future autoGenerateCookbookId(); /// This method will save the username of the cookbook generator /// Input: [username] the username of the user who created the cookbook @@ -68,7 +68,9 @@ class RepositoryImp implements Repository { } @override - String autoGenerateCookbookId() => localDataSource.autoGenerateCookbookId(); + Future autoGenerateCookbookId() async { + return localDataSource.autoGenerateCookbookId(); + } @override Future saveCookBookGeneratorUsername(String username) { diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 6bc64cef88..0d670eb215 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -4,9 +4,9 @@ import 'package:injectable/injectable.dart'; import 'package:shared_preferences/shared_preferences.dart'; abstract class LocalDataSource { - /// This method will generate Evently Id for the Event - /// Output: [String] the id of the Event that is going to be added in the recipe - String autoGenerateCookbookId(); + /// This method will generate the cookbook id for the easel app + /// Output: [String] the id of the cookbook which will contains all the NFTs. + Future autoGenerateCookbookId(); /// This method will save the username of the cookbook generator /// Input: [username] the username of the user who created the cookbook @@ -32,9 +32,12 @@ class LocalDataSourceImpl extends LocalDataSource { final SharedPreferences sharedPreferences; + /// auto generates cookbookID string and saves into local storage + /// returns cookbookId @override - String autoGenerateCookbookId() { - final String cookbookId = "Evently_CookBook_auto_cookbook_${getFullDateTime()}"; + Future autoGenerateCookbookId() async { + final String cookbookId = "Easel_CookBook_auto_cookbook_${getFullDateTime()}"; + await sharedPreferences.setString(kCookbookId, cookbookId); return cookbookId; } From 1395297b2a5f949b44f7af7ca1a37b943fd246ce Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 11:01:04 +0500 Subject: [PATCH 086/161] coobbok creation like in easel --- evently/lib/evently_provider.dart | 85 +++++++++++-------- evently/lib/repository/repository.dart | 9 ++ .../datasources/local_datasource.dart | 20 +++++ 3 files changed, 77 insertions(+), 37 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 4cb670d428..96cd3e83c2 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -222,8 +222,6 @@ class EventlyProvider extends ChangeNotifier { String currentUserName = ""; bool stripeAccountExists = false; - - String? _cookbookId; String? get cookbookId => _cookbookId; @@ -267,55 +265,69 @@ class EventlyProvider extends ChangeNotifier { } } - _recipeId = repository.autoGenerateEventlyId(); - - final event = Event( - eventName: eventName, - hostName: hostName, - thumbnail: thumbnail!, - startDate: startDate, - endDate: endDate, - startTime: startTime, - endTime: endTime, - location: location, - description: description, - isFreeDrop: "isFreeDrop", - numberOfTickets: numberOfTickets.toString(), - price: price.toString(), - listOfPerks: perks.map((e) => e.toString()).toList()); - - final recipe = event.createRecipe( - cookbookId: _cookbookId!, - recipeId: _recipeId, - isFreeDrop: isFreeDrop, - symbol: selectedDenom.symbol, - perksList: perks, - price: price, - ); - - final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); - - if (!response.success) { - scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); - return false; - } - scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); + // _recipeId = repository.autoGenerateEventlyId(); + // + // final event = Event( + // eventName: eventName, + // hostName: hostName, + // thumbnail: thumbnail!, + // startDate: startDate, + // endDate: endDate, + // startTime: startTime, + // endTime: endTime, + // location: location, + // description: description, + // isFreeDrop: "isFreeDrop", + // numberOfTickets: numberOfTickets.toString(), + // price: price.toString(), + // listOfPerks: perks.map((e) => e.toString()).toList()); + // + // final recipe = event.createRecipe( + // cookbookId: _cookbookId!, + // recipeId: _recipeId, + // isFreeDrop: isFreeDrop, + // symbol: selectedDenom.symbol, + // perksList: perks, + // price: price, + // ); + // + // final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); + // + // if (!response.success) { + // scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); + // return false; + // } + // scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); // final nftFromRecipe = Event.fromRecipe(recipe); // GetIt.I.get<>().updatePublishedNFTList(nft: nftFromRecipe); // deleteNft(nft.id); return false; } + String currentUsername = ''; + bool isPylonsInstalled = false; + + Future populateUserName() async { + isPylonsInstalled = await PylonsWallet.instance.exists(); + if (currentUsername.isEmpty) { + final String savedHostName = repository.getHostName(); + + currentUsername = savedHostName; + } + notifyListeners(); + } + /// send createCookBook tx message to the wallet app /// return true or false depending on the response from the wallet app Future createCookbook() async { _cookbookId = await repository.autoGenerateCookbookId(); + final cookBook1 = Cookbook( creator: "", id: _cookbookId, name: cookbookName, description: cookbookDesc, - developer: artistNameController.text, + developer: hostName, version: kVersionCookboox, supportEmail: supportedEmail, enabled: true, @@ -331,6 +343,5 @@ class EventlyProvider extends ChangeNotifier { return false; } - bool isDifferentUserName(String savedUserName) => currentUserName.isNotEmpty && savedUserName != currentUserName; } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 46ded6fd78..071eac59cc 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -42,6 +42,10 @@ abstract class Repository { /// Input : [UploadIPFSInput] which needs to be uploaded /// Output : [ApiResponse] the ApiResponse which can contain [success] or [error] response Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); + + /// This method will get the artist name + /// Output: [String] returns whether the operation is successful or not + String getHostName(); } @LazySingleton(as: Repository) @@ -101,4 +105,9 @@ class RepositoryImp implements Repository { return Left(CacheFailure(LocaleKeys.update_failed.tr())); } } + + @override + String getHostName() { + return localDataSource.getHostName(); + } } diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 0d670eb215..824c64bf07 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -24,6 +24,15 @@ abstract class LocalDataSource { /// This method will generate easel Id for the NFT /// Output: [String] the id of the NFT that is going to be added in the recipe String autoGenerateEventlyId(); + + /// This method will get the artist name + /// Output: [String] returns whether the operation is successful or not + String getHostName(); + + /// This method will save the artist name + /// Input: [name] the name of the artist which the user want to save + /// Output: [bool] returns whether the operation is successful or not + Future saveHostName(String name); } @LazySingleton(as: LocalDataSource) @@ -66,4 +75,15 @@ class LocalDataSourceImpl extends LocalDataSource { final String cookbookId = "Evently_Recipe_auto_recipe_${getFullDateTime()}"; return cookbookId; } + + @override + String getHostName() { + return sharedPreferences.getString(kHostName) ?? ''; + } + + @override + Future saveHostName(String name) async { + await sharedPreferences.setString(kHostName, name); + return true; + } } From d8c25eea1b2fa0f61ecc0c859f3a7cf4f207e790 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 11:30:01 +0500 Subject: [PATCH 087/161] feat: weighted output --- evently/lib/evently_provider.dart | 112 ++++++++++++------------------ evently/lib/models/events.dart | 6 +- evently/lib/utils/constants.dart | 1 + 3 files changed, 50 insertions(+), 69 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 96cd3e83c2..b9485e5f24 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -160,9 +160,9 @@ class EventlyProvider extends ChangeNotifier { notifyListeners(); } - get numberOfTickets => _numberOfTickets; + int get numberOfTickets => _numberOfTickets; - get isFreeDrop => _isFreeDrop; + FreeDrop get isFreeDrop => _isFreeDrop; set setFreeDrop(FreeDrop freeDrop) { _isFreeDrop = freeDrop; @@ -174,7 +174,7 @@ class EventlyProvider extends ChangeNotifier { notifyListeners(); } - get price => _price; + int get price => _price; void setSelectedDenom(Denom value) { _selectedDenom = value; @@ -246,77 +246,58 @@ class EventlyProvider extends ChangeNotifier { return sdkResponse; } + String _recipeId = ""; + String get recipeId => _recipeId; + Future createRecipe() async { final scaffoldMessengerState = navigatorKey.getState(); - _cookbookId = repository.getCookbookId(); - - final String savedUserName = repository.getCookBookGeneratorUsername(); - - if (_cookbookId == null || isDifferentUserName(savedUserName)) { - // create cookbook - final isCookBookCreated = await createCookbook(); - - if (isCookBookCreated) { - _cookbookId = repository.getCookbookId(); - notifyListeners(); - } else { + final isCookBookCreated = await createCookbook(); + + if (isCookBookCreated) { + _recipeId = repository.autoGenerateEventlyId(); + + final event = Event( + eventName: eventName, + hostName: hostName, + thumbnail: thumbnail!, + startDate: startDate, + endDate: endDate, + startTime: startTime, + endTime: endTime, + location: location, + description: description, + isFreeDrop: "isFreeDrop", + numberOfTickets: numberOfTickets.toString(), + price: price.toString(), + listOfPerks: perks.map((e) => e.toString()).toList(), + ); + + final recipe = event.createRecipe( + cookbookId: _cookbookId!, + recipeId: _recipeId, + isFreeDrop: isFreeDrop, + symbol: selectedDenom.symbol, + perksList: perks, + price: price.toString(), + ); + + final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); + + if (!response.success) { + scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); return false; } + scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); + // final nftFromRecipe = Event.fromRecipe(recipe); + // GetIt.I.get<>().updatePublishedNFTList(nft: nftFromRecipe); + // deleteNft(nft.id); + return true; } - // _recipeId = repository.autoGenerateEventlyId(); - // - // final event = Event( - // eventName: eventName, - // hostName: hostName, - // thumbnail: thumbnail!, - // startDate: startDate, - // endDate: endDate, - // startTime: startTime, - // endTime: endTime, - // location: location, - // description: description, - // isFreeDrop: "isFreeDrop", - // numberOfTickets: numberOfTickets.toString(), - // price: price.toString(), - // listOfPerks: perks.map((e) => e.toString()).toList()); - // - // final recipe = event.createRecipe( - // cookbookId: _cookbookId!, - // recipeId: _recipeId, - // isFreeDrop: isFreeDrop, - // symbol: selectedDenom.symbol, - // perksList: perks, - // price: price, - // ); - // - // final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); - // - // if (!response.success) { - // scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); - // return false; - // } - // scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); - // final nftFromRecipe = Event.fromRecipe(recipe); - // GetIt.I.get<>().updatePublishedNFTList(nft: nftFromRecipe); - // deleteNft(nft.id); return false; } - String currentUsername = ''; - bool isPylonsInstalled = false; - - Future populateUserName() async { - isPylonsInstalled = await PylonsWallet.instance.exists(); - if (currentUsername.isEmpty) { - final String savedHostName = repository.getHostName(); - - currentUsername = savedHostName; - } - notifyListeners(); - } - /// send createCookBook tx message to the wallet app /// return true or false depending on the response from the wallet app Future createCookbook() async { @@ -335,13 +316,10 @@ class EventlyProvider extends ChangeNotifier { final response = await PylonsWallet.instance.txCreateCookbook(cookBook1); if (response.success) { - repository.saveCookBookGeneratorUsername(currentUsername); return true; } navigatorKey.showMsg(message: response.error); return false; } - - bool isDifferentUserName(String savedUserName) => currentUserName.isNotEmpty && savedUserName != currentUserName; } diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 442755d644..78d10cdc78 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -153,10 +153,12 @@ extension CreateRecipe on Event { ], itemModifyOutputs: [], ), - outputs: [], + outputs: [ + WeightedOutputs(entryIds: [kEventlyEvent], weight: Int64(1)) + ], blockInterval: Int64(), enabled: true, - // extraInfo: kExtraInfo, + extraInfo: kExtraInfo, ); } } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index a8a8884c12..17b1221a18 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -73,6 +73,7 @@ const kNumberOfTickets = "kNumberOfTickets"; const kPrice = "kPrice"; const kErrRecipe = 'Recipe error :'; +const kExtraInfo = "extraInfo"; /// ```URL constants const ipfsDomain = 'https://ipfs.io/ipfs'; From 60c5c8797dac7e258bd5ce90f661ec2ea1e6e845 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 11:53:11 +0500 Subject: [PATCH 088/161] feat: event view integration at pylons apps --- .../handlers/create_recipe_handler.dart | 16 ++- wallet/lib/model/event.dart | 109 ++++++++++++++++++ wallet/lib/model/nft.dart | 14 +++ wallet/lib/utils/constants.dart | 40 +++---- wallet/lib/utils/route_util.dart | 7 +- 5 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 wallet/lib/model/event.dart diff --git a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart index 360047341f..661ff9e213 100644 --- a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart +++ b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart @@ -6,6 +6,7 @@ import 'package:pylons_wallet/components/loading.dart'; import 'package:pylons_wallet/ipc/handler/base_handler.dart'; import 'package:pylons_wallet/ipc/models/sdk_ipc_message.dart'; import 'package:pylons_wallet/ipc/models/sdk_ipc_response.dart'; +import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/pages/home/home_provider.dart'; import 'package:pylons_wallet/providers/collections_tab_provider.dart'; import 'package:pylons_wallet/pylons_app.dart'; @@ -36,11 +37,16 @@ class CreateRecipeHandler implements BaseHandler { if (response.success) { if (shouldShowNFTPreview()) { final msgObj = pylons.MsgCreateRecipe.create()..mergeFromProto3Json(jsonMap); - - final nft = await NFT.fromRecipeId(msgObj.cookbookId, msgObj.id); - - if (nft != null) { - await navigatorKey.currentState!.pushNamed(Routes.ownerView.name, arguments: nft); + if (msgObj.cookbookId.contains('Evently')) { + final events = Events.fromRecipeId(msgObj.cookbookId, msgObj.id); + if (events != null) { + await navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events); + } + } else { + final nft = await NFT.fromRecipeId(msgObj.cookbookId, msgObj.id); + if (nft != null) { + await navigatorKey.currentState!.pushNamed(Routes.ownerView.name, arguments: nft); + } } GetIt.I.get().changeTabs(0); diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart new file mode 100644 index 0000000000..eaf8a36929 --- /dev/null +++ b/wallet/lib/model/event.dart @@ -0,0 +1,109 @@ +import 'package:equatable/equatable.dart'; +import 'package:get_it/get_it.dart'; +import 'package:pylons_wallet/modules/Pylonstech.pylons.pylons/module/client/pylons/recipe.pb.dart'; +import 'package:pylons_wallet/stores/wallet_store.dart'; +import 'package:pylons_wallet/utils/constants.dart'; + +class Events extends Equatable { + const Events({ + required this.eventName, + required this.hostName, + required this.thumbnail, + required this.startDate, + required this.endDate, + required this.startTime, + required this.endTime, + required this.location, + required this.description, + required this.isFreeDrop, + required this.numberOfTickets, + required this.price, + required this.listOfPerks, + }); + + final String eventName; + + final String hostName; + + final String thumbnail; + + final String startDate; + + final String endDate; + + final String startTime; + + final String endTime; + + final String location; + + final String description; + + final String isFreeDrop; + + final String numberOfTickets; + + final String price; + + final List listOfPerks; + + @override + List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, isFreeDrop, numberOfTickets, price]; + + static Map _extractAttributeValues(List attributes) { + final Map attributeValues = {}; + for (final attribute in attributes) { + switch (attribute.key) { + case kEventName: + case kEventHostName: + case kThumbnail: + case kStartDate: + case kEndDate: + case kStartTime: + case kEndTime: + case kLocation: + case kDescription: + case kPerks: + case kNumberOfTickets: + case kPrice: + attributeValues[attribute.key] = attribute.value; + break; + default: + continue; + } + } + + return attributeValues; + } + + factory Events.fromRecipe(Recipe recipe) { + final Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); + + return Events( + eventName: map[kEventName]!, + hostName: map[kEventHostName]!, + thumbnail: map[kThumbnail]!, + startDate: map[kStartDate]!, + endDate: map[kEndDate]!, + startTime: map[kStartTime]!, + endTime: map[kEndTime]!, + location: map[kLocation]!, + description: map[kDescription]!, + isFreeDrop: '', + numberOfTickets: map[kNumberOfTickets]!, + price: map[kPrice]!, + listOfPerks: [], + ); + } + + static Future fromRecipeId(String cookbookId, String recipeId) async { + final walletsStore = GetIt.I.get(); + final recipeEither = await walletsStore.getRecipe(cookbookId, recipeId); + + if (recipeEither.isLeft()) { + return null; + } + + return Events.fromRecipe(recipeEither.toOption().toNullable()!); + } +} diff --git a/wallet/lib/model/nft.dart b/wallet/lib/model/nft.dart index bd78386e10..1d0e7f153f 100644 --- a/wallet/lib/model/nft.dart +++ b/wallet/lib/model/nft.dart @@ -349,6 +349,20 @@ class NFT extends Equatable { return NFT.fromRecipe(recipeEither.toOption().toNullable()!); } + + static Future eventFromRecipeId(String cookbookId, String recipeId) async { + final walletsStore = GetIt.I.get(); + final recipeEither = await walletsStore.getRecipe(cookbookId, recipeId); + + if (recipeEither.isLeft()) { + return null; + } + + return NFT.fromRecipe(recipeEither.toOption().toNullable()!); + } + + + factory NFT.fromJson(Map json) { return NFT( name: json['name'].toString(), diff --git a/wallet/lib/utils/constants.dart b/wallet/lib/utils/constants.dart index d143ff4159..5305120a6d 100644 --- a/wallet/lib/utils/constants.dart +++ b/wallet/lib/utils/constants.dart @@ -106,13 +106,7 @@ const int kDenomInitial = 1; const int kDenomFinal = 4; const Map kCoinDenom = { - 'upylon': { - "name": "Pylon", - "denom": "upylon", - "short": "pylon", - "icon": "assets/images/icons/pylons_logo_24x24.png", - "faucet": true - }, + 'upylon': {"name": "Pylon", "denom": "upylon", "short": "pylon", "icon": "assets/images/icons/pylons_logo_24x24.png", "faucet": true}, 'BTC': { "name": "Bitcoin", "denom": "BTC", @@ -199,13 +193,7 @@ const IOS_VERSION = '1.0.8+3'; const kCurrencyDecimalLength = 2; const kMaxPriceLength = 14; -List colorList = [ - AppColors.kYellow, - AppColors.kBlue, - AppColors.kDarkPurple, - AppColors.kDarkRed, - AppColors.kDarkGreen -]; +List colorList = [AppColors.kYellow, AppColors.kBlue, AppColors.kDarkPurple, AppColors.kDarkRed, AppColors.kDarkGreen]; List colorListForPracticeTest = [ AppColors.kYellow, AppColors.kDarkPurple, @@ -290,8 +278,7 @@ List> languagesSupported = [ ///review your nft -const String kPylonsFeeMsg = - "The Pylons fee is the network fee assessed on all transactions which is equal to 10% of the listed price."; +const String kPylonsFeeMsg = "The Pylons fee is the network fee assessed on all transactions which is equal to 10% of the listed price."; const String kStripeAccountNotCreatedIdentifier = "onboarding"; const String kNftFormat = "NFT_Format"; const String kDuration = "Duration"; @@ -447,11 +434,7 @@ const kCheckoutDialogKey = "checkout_dialog_key"; const kPurchaseItemBottomSheetKey = "purchase_item_bottom_sheet"; const kCloseBottomSheetKey = "close_bottom_sheet_key"; -Map denomColors = { - 'upylon': const Color(0xFF5252d5), - 'ustripeusd': const Color(0xFF85bb65), - 'uusd': const Color(0xFF85bb65) -}; +Map denomColors = {'upylon': const Color(0xFF5252d5), 'ustripeusd': const Color(0xFF85bb65), 'uusd': const Color(0xFF85bb65)}; const String kNftName = "nftName"; const String kNftPrice = "nftPrice"; @@ -515,3 +498,18 @@ const String drawerKey = "drawer_key"; const String kRemaining = 'remaining'; const kTotal = 'total'; const kFileExtension = "file_extension"; + +///* Event String +/// Event String keys +const kEventName = "kEventName"; +const kEventHostName = "kEventHostName"; +const kThumbnail = "kThumbnail"; +const kStartDate = "kStartDate"; +const kEndDate = "kEndDate"; +const kStartTime = "kStartTime"; +const kEndTime = "kEndTime"; +const kLocation = "kLocation"; +const kPerks = "kPerks"; +const kNumberOfTickets = "kNumberOfTickets"; +const kPrice = "kPrice"; +const kErrRecipe = 'Recipe error :'; diff --git a/wallet/lib/utils/route_util.dart b/wallet/lib/utils/route_util.dart index 5083937cd8..0f7ccf9cf2 100644 --- a/wallet/lib/utils/route_util.dart +++ b/wallet/lib/utils/route_util.dart @@ -111,6 +111,8 @@ class RouteUtil { return createRoute(const SizedBox()); case Routes.transactionHistory: return createRoute(const TransactionHistoryScreen()); + case Routes.eventView: + return createRoute(const Placeholder()); } return null; @@ -146,7 +148,8 @@ enum Routes { purchaseView, transactionHistory, acceptPolicy, - fallback; + fallback, + eventView; static Routes getAppRouteFromString(String routeName) { switch (routeName) { @@ -198,6 +201,8 @@ enum Routes { return localTransactionDetails; case 'transactionHistory': return transactionHistory; + case 'eventView': + return eventView; default: return fallback; } From 6cc146319809ed52132682e5e0f68c0ba742e4b3 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 12:27:34 +0500 Subject: [PATCH 089/161] feat: route for publishing view --- .../datasources/local_datasource.dart | 2 +- wallet/lib/pages/events/events_screen.dart | 24 +++++++++++++++++++ wallet/lib/utils/route_util.dart | 11 ++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 wallet/lib/pages/events/events_screen.dart diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 824c64bf07..07b8b19e5c 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -45,7 +45,7 @@ class LocalDataSourceImpl extends LocalDataSource { /// returns cookbookId @override Future autoGenerateCookbookId() async { - final String cookbookId = "Easel_CookBook_auto_cookbook_${getFullDateTime()}"; + final String cookbookId = "Evently_CookBook_auto_cookbook_${getFullDateTime()}"; await sharedPreferences.setString(kCookbookId, cookbookId); return cookbookId; } diff --git a/wallet/lib/pages/events/events_screen.dart b/wallet/lib/pages/events/events_screen.dart new file mode 100644 index 0000000000..b62aa5579a --- /dev/null +++ b/wallet/lib/pages/events/events_screen.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import '../../model/event.dart'; + +class EventPassViewScreen extends StatefulWidget { + const EventPassViewScreen({super.key, required this.events}); + + final Events events; + + @override + State createState() => _EventPassViewScreenState(); +} + +class _EventPassViewScreenState extends State { + @override + Widget build(BuildContext context) { + return const Scaffold( + backgroundColor: Colors.black, + body: Column( + children: [], + ), + ); + } +} diff --git a/wallet/lib/utils/route_util.dart b/wallet/lib/utils/route_util.dart index 0f7ccf9cf2..e654a5de99 100644 --- a/wallet/lib/utils/route_util.dart +++ b/wallet/lib/utils/route_util.dart @@ -1,9 +1,11 @@ // ignore_for_file: cast_nullable_to_non_nullable import 'package:flutter/material.dart'; +import 'package:pylons_wallet/pages/events/events_screen.dart'; import 'package:pylons_wallet/pages/settings/screens/general_screen/general_screen.dart'; import 'package:pylons_wallet/pages/settings/screens/recovery_screen/recovery_screen.dart'; +import '../model/event.dart'; import '../model/nft.dart'; import '../model/transaction_failure_model.dart'; import '../pages/detailed_asset_view/owner_view.dart'; @@ -112,7 +114,14 @@ class RouteUtil { case Routes.transactionHistory: return createRoute(const TransactionHistoryScreen()); case Routes.eventView: - return createRoute(const Placeholder()); + if (settings.arguments != null && settings.arguments is NFT) { + return createRoute(EventPassViewScreen( + key: ValueKey(settings.arguments), + events: settings.arguments as Events, + )); + } + + return createRoute(const SizedBox()); } return null; From 3f6e180dd474f66319b0be58729c128e6c27ccff Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 12:59:06 +0500 Subject: [PATCH 090/161] feat: event on the pylons app --- wallet/assets/images/svg/share_icon.svg | 3 + .../handlers/create_recipe_handler.dart | 2 + wallet/lib/pages/events/events_screen.dart | 97 ++++++++++++++++++- wallet/lib/utils/constants.dart | 1 + 4 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 wallet/assets/images/svg/share_icon.svg diff --git a/wallet/assets/images/svg/share_icon.svg b/wallet/assets/images/svg/share_icon.svg new file mode 100644 index 0000000000..60360dfeb0 --- /dev/null +++ b/wallet/assets/images/svg/share_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart index 661ff9e213..98d38b1883 100644 --- a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart +++ b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart @@ -37,6 +37,8 @@ class CreateRecipeHandler implements BaseHandler { if (response.success) { if (shouldShowNFTPreview()) { final msgObj = pylons.MsgCreateRecipe.create()..mergeFromProto3Json(jsonMap); + + if (msgObj.cookbookId.contains('Evently')) { final events = Events.fromRecipeId(msgObj.cookbookId, msgObj.id); if (events != null) { diff --git a/wallet/lib/pages/events/events_screen.dart b/wallet/lib/pages/events/events_screen.dart index b62aa5579a..c938c18544 100644 --- a/wallet/lib/pages/events/events_screen.dart +++ b/wallet/lib/pages/events/events_screen.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:pylons_wallet/utils/constants.dart'; import '../../model/event.dart'; @@ -14,10 +17,96 @@ class EventPassViewScreen extends StatefulWidget { class _EventPassViewScreenState extends State { @override Widget build(BuildContext context) { - return const Scaffold( - backgroundColor: Colors.black, - body: Column( - children: [], + return ColoredBox( + color: AppColors.kBlack87, + child: SafeArea( + child: Scaffold( + backgroundColor: AppColors.kBlack87, + appBar: AppBar( + backgroundColor: Colors.black, + flexibleSpace: Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Icon( + Icons.arrow_back_ios, + color: AppColors.kWhite, + ), + Text( + 'Event Pass', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w700, + color: AppColors.kWhite, + ), + ), + SvgPicture.asset(shareIcon), + ], + ), + ), + ), + body: Column( + children: [ + Container( + decoration: BoxDecoration(color: AppColors.kBlue), + child: Column( + children: [ + Text(widget.events.eventName), + Row( + children: [ + Column( + children: [ + Text('Date'), + Text(widget.events.startDate), + ], + ), + Column( + children: [ + Text('Time'), + Text(widget.events.startDate), + ], + ) + ], + ), + Row( + children: [ + Column( + children: [ + Text('LOCATION'), + Text(widget.events.location), + ], + ), + Column( + children: [ + Text('SEAT'), + Text("no seat"), + ], + ) + ], + ), + Row( + children: [ + Column( + children: [ + Text('Date'), + Text(widget.events.startDate), + ], + ), + Column( + children: [ + Text('Time'), + Text(widget.events.startDate), + ], + ) + ], + ) + ], + ), + ) + ], + ), + ), ), ); } diff --git a/wallet/lib/utils/constants.dart b/wallet/lib/utils/constants.dart index 5305120a6d..04b1c209a3 100644 --- a/wallet/lib/utils/constants.dart +++ b/wallet/lib/utils/constants.dart @@ -338,6 +338,7 @@ const kUploadErrorIcon = "assets/images/icons/upload_error_background.svg"; const kSvgCloseButton = 'assets/images/svg/close_button.svg'; const email = "support@pylons.tech"; const mailto = "mailto"; +const shareIcon = "assets/images/svg/share_icon.svg"; const String k3DText = "3D"; const String kThreeDText = "ThreeD"; From d0ed5ff4300da586b1c85412519f2bfa861b62e7 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 14:13:44 +0500 Subject: [PATCH 091/161] feat: publishing event seting on wallet --- wallet/lib/ipc/handler/handlers/create_recipe_handler.dart | 3 +-- wallet/lib/model/event.dart | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart index 98d38b1883..259e03eecb 100644 --- a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart +++ b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart @@ -38,9 +38,8 @@ class CreateRecipeHandler implements BaseHandler { if (shouldShowNFTPreview()) { final msgObj = pylons.MsgCreateRecipe.create()..mergeFromProto3Json(jsonMap); - if (msgObj.cookbookId.contains('Evently')) { - final events = Events.fromRecipeId(msgObj.cookbookId, msgObj.id); + final events = await Events.fromRecipeId(msgObj.cookbookId, msgObj.id); if (events != null) { await navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events); } diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index eaf8a36929..f47ea28073 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -77,7 +77,7 @@ class Events extends Equatable { } factory Events.fromRecipe(Recipe recipe) { - final Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); + final map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); return Events( eventName: map[kEventName]!, From 46f4b19582e59fefc080d880cab1481090bb6150 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 14:28:07 +0500 Subject: [PATCH 092/161] event class to be able to create from receipe --- evently/lib/evently_provider.dart | 1 - evently/lib/models/events.dart | 86 ++++++++++++++----------------- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index b9485e5f24..bbe871c570 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -267,7 +267,6 @@ class EventlyProvider extends ChangeNotifier { endTime: endTime, location: location, description: description, - isFreeDrop: "isFreeDrop", numberOfTickets: numberOfTickets.toString(), price: price.toString(), listOfPerks: perks.map((e) => e.toString()).toList(), diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 78d10cdc78..ebc2ed0a50 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -7,49 +7,60 @@ import 'package:fixnum/fixnum.dart'; class Event extends Equatable { const Event({ - required this.eventName, - required this.hostName, - required this.thumbnail, - required this.startDate, - required this.endDate, - required this.startTime, - required this.endTime, - required this.location, - required this.description, - required this.isFreeDrop, - required this.numberOfTickets, - required this.price, + ///* overview data + this.eventName = '', + this.hostName = '', + this.thumbnail = '', + + ///* details + this.startDate = '', + this.endDate = '', + this.startTime = '', + this.endTime = '', + this.location = '', + this.description = '', + + ///* perks required this.listOfPerks, + + ///* price + this.numberOfTickets = '0', + this.price = '', + this.isFreeDrop = 'unselected', }); final String eventName; - final String hostName; - final String thumbnail; - final String startDate; - final String endDate; - final String startTime; - final String endTime; - final String location; - final String description; - - final String isFreeDrop; - final String numberOfTickets; - final String price; - final List listOfPerks; + final String isFreeDrop; - @override - List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, isFreeDrop, numberOfTickets, price]; + factory Event.fromRecipe(Recipe recipe) { + Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); + + return Event( + eventName: map[kEventName]!, + hostName: map[kEventHostName]!, + thumbnail: map[kThumbnail]!, + startDate: map[kStartDate]!, + endDate: map[kEndDate]!, + startTime: map[kStartTime]!, + endTime: map[kEndTime]!, + location: map[kLocation]!, + description: map[kDescription]!, + numberOfTickets: map[kNumberOfTickets]!, + price: map[kPrice]!, + listOfPerks: [], + ); + } static Map _extractAttributeValues(List attributes) { final Map attributeValues = {}; @@ -77,25 +88,8 @@ class Event extends Equatable { return attributeValues; } - factory Event.fromRecipe(Recipe recipe) { - Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); - - return Event( - eventName: map[kEventName]!, - hostName: map[kEventHostName]!, - thumbnail: map[kThumbnail]!, - startDate: map[kStartDate]!, - endDate: map[kEndDate]!, - startTime: map[kStartTime]!, - endTime: map[kEndTime]!, - location: map[kLocation]!, - description: map[kDescription]!, - isFreeDrop: '', - numberOfTickets: map[kNumberOfTickets]!, - price: map[kPrice]!, - listOfPerks: [], - ); - } + @override + List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price]; } extension CreateRecipe on Event { From b1b5f45f52b3dc339021dd9c13dffe0cf51544af Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 14:44:20 +0500 Subject: [PATCH 093/161] feat: event class necessary parameters to set --- evently/lib/models/denom.dart | 1 + evently/lib/models/events.dart | 53 +++++++++++++++++++++++++++----- evently/lib/utils/constants.dart | 21 +------------ 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/evently/lib/models/denom.dart b/evently/lib/models/denom.dart index c35b34ce8a..a107ecff18 100644 --- a/evently/lib/models/denom.dart +++ b/evently/lib/models/denom.dart @@ -1,3 +1,4 @@ +import 'package:evently/models/events.dart'; import 'package:evently/utils/amount_formatter.dart'; import 'package:evently/utils/constants.dart'; import 'package:flutter/material.dart'; diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index ebc2ed0a50..1a99c27470 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -1,7 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:evently/evently_provider.dart'; import 'package:evently/models/perks_model.dart'; -import 'package:evently/utils/constants.dart'; import 'package:pylons_sdk/low_level.dart'; import 'package:fixnum/fixnum.dart'; @@ -21,12 +20,16 @@ class Event extends Equatable { this.description = '', ///* perks - required this.listOfPerks, + this.listOfPerks = '', ///* price this.numberOfTickets = '0', this.price = '', - this.isFreeDrop = 'unselected', + this.isFreeDrops = 'unselected', + + ///* other + this.cookbookID = '', + this.recipeID = '', }); final String eventName; @@ -40,8 +43,10 @@ class Event extends Equatable { final String description; final String numberOfTickets; final String price; - final List listOfPerks; - final String isFreeDrop; + final String listOfPerks; + final String isFreeDrops; + final String cookbookID; + final String recipeID; factory Event.fromRecipe(Recipe recipe) { Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); @@ -58,7 +63,7 @@ class Event extends Equatable { description: map[kDescription]!, numberOfTickets: map[kNumberOfTickets]!, price: map[kPrice]!, - listOfPerks: [], + listOfPerks: map[kPerks]!, ); } @@ -89,7 +94,12 @@ class Event extends Equatable { } @override - List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price]; + List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price, listOfPerks, isFreeDrops, cookbookID, recipeID]; + + @override + String toString() { + return 'Event{eventName: $eventName, hostName: $hostName, thumbnail: $thumbnail, startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime, location: $location, description: $description, numberOfTickets: $numberOfTickets, price: $price, tradePercentage: $listOfPerks, isFreeDrop: $isFreeDrops, cookbookID: $cookbookID, recipeID: $recipeID}'; + } } extension CreateRecipe on Event { @@ -135,9 +145,12 @@ extension CreateRecipe on Event { StringParam(key: kEndTime, value: endTime.trim()), StringParam(key: kLocation, value: location.trim()), StringParam(key: kDescription, value: description.trim()), - StringParam(key: kPerks, value: kPerks.trim()), StringParam(key: kNumberOfTickets, value: numberOfTickets.trim()), StringParam(key: kPrice, value: price.trim()), + StringParam(key: kPerks, value: listOfPerks.trim()), + StringParam(key: kFreeDrop, value: isFreeDrops), + StringParam(key: kCookBookId, value: cookbookID), + StringParam(key: kRecipeId, value: recipeID), ], mutableStrings: [], transferFee: [Coin(denom: kPylonSymbol, amount: transferFeeAmount)], @@ -156,3 +169,27 @@ extension CreateRecipe on Event { ); } } + +/// Event String keys +const kEventName = "kEventName"; +const kEventHostName = "kEventHostName"; +const kThumbnail = "kThumbnail"; +const kStartDate = "kStartDate"; +const kEndDate = "kEndDate"; +const kStartTime = "kStartTime"; +const kEndTime = "kEndTime"; +const kLocation = "kLocation"; +const kDescription = "kDescription"; +const kPerks = "kPerks"; +const kNumberOfTickets = "kNumberOfTickets"; +const kPrice = "kPrice"; +const kFreeDrop = "kFreeDrop"; +const kRecipeId = "kRecipeId"; +const kCookBookId = "kCookBookId"; +const kVersion = "v0.2.0"; +const kUpylon = "upylon"; +const kPylonSymbol = 'upylon'; +const String transferFeeAmount = '1'; +const kEventlyEvent = "Evently_Event"; +const kExtraInfo = "extraInfo"; +const String costPerBlock = '0'; diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 17b1221a18..7d9d93c6cf 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -6,7 +6,6 @@ const kDraft = "Draft"; final List stepLabels = ["overview", "detail", "perks", "price"]; -const kPylonSymbol = 'upylon'; const kUsdSymbol = 'ustripeusd'; const kAtomSymbol = 'uatom'; const kEuroSymbol = 'eeur'; @@ -50,30 +49,12 @@ const kFreeShirt = "free_shirt"; const kFreeGift = "free_gift"; const kFreeDrink = "free_drink"; -const kVersion = "v0.2.0"; -const kUpylon = "upylon"; -const String costPerBlock = '0'; -const kEventlyEvent = "Evently_Event"; + const kResidual = "Residual"; const kQuantity = "Quantity"; const String transferFeeAmount = '1'; -/// Event String keys -const kEventName = "kEventName"; -const kEventHostName = "kEventHostName"; -const kThumbnail = "kThumbnail"; -const kStartDate = "kStartDate"; -const kEndDate = "kEndDate"; -const kStartTime = "kStartTime"; -const kEndTime = "kEndTime"; -const kLocation = "kLocation"; -const kDescription = "kDescription"; -const kPerks = "kPerks"; -const kNumberOfTickets = "kNumberOfTickets"; -const kPrice = "kPrice"; - const kErrRecipe = 'Recipe error :'; -const kExtraInfo = "extraInfo"; /// ```URL constants const ipfsDomain = 'https://ipfs.io/ipfs'; From 8b36ee86dbd49873e9592920bf81e6e5799800a8 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 14:49:37 +0500 Subject: [PATCH 094/161] feat: publishing code cleaning --- evently/lib/evently_provider.dart | 3 +- evently/lib/models/events.dart | 37 ++++- evently/lib/models/perks_model.dart | 21 --- wallet/lib/model/event.dart | 216 ++++++++++++++++++++++------ wallet/lib/utils/constants.dart | 15 -- 5 files changed, 200 insertions(+), 92 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index bbe871c570..fe2e8a1cb2 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -21,7 +21,6 @@ import 'package:pylons_sdk/low_level.dart'; import 'services/third_party_services/quick_node.dart'; -enum FreeDrop { yes, no, unselected } @LazySingleton() class EventlyProvider extends ChangeNotifier { @@ -257,7 +256,7 @@ class EventlyProvider extends ChangeNotifier { if (isCookBookCreated) { _recipeId = repository.autoGenerateEventlyId(); - final event = Event( + final event = Events( eventName: eventName, hostName: hostName, thumbnail: thumbnail!, diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 1a99c27470..34a3b14a55 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -1,11 +1,11 @@ import 'package:equatable/equatable.dart'; -import 'package:evently/evently_provider.dart'; -import 'package:evently/models/perks_model.dart'; import 'package:pylons_sdk/low_level.dart'; import 'package:fixnum/fixnum.dart'; -class Event extends Equatable { - const Event({ +enum FreeDrop { yes, no, unselected } + +class Events extends Equatable { + const Events({ ///* overview data this.eventName = '', this.hostName = '', @@ -48,10 +48,10 @@ class Event extends Equatable { final String cookbookID; final String recipeID; - factory Event.fromRecipe(Recipe recipe) { + factory Events.fromRecipe(Recipe recipe) { Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); - return Event( + return Events( eventName: map[kEventName]!, hostName: map[kEventHostName]!, thumbnail: map[kThumbnail]!, @@ -102,7 +102,7 @@ class Event extends Equatable { } } -extension CreateRecipe on Event { +extension CreateRecipe on Events { Recipe createRecipe({ required String cookbookId, required String recipeId, @@ -193,3 +193,26 @@ const String transferFeeAmount = '1'; const kEventlyEvent = "Evently_Event"; const kExtraInfo = "extraInfo"; const String costPerBlock = '0'; + + +class PerksModel { + PerksModel({required this.name, required this.description}); + + final String name; + final String description; + + factory PerksModel.updateName({required String name, required PerksModel perksModel}) => PerksModel( + name: name, + description: perksModel.description, + ); + + factory PerksModel.updateDescription({required String description, required PerksModel perksModel}) => PerksModel( + name: perksModel.name, + description: description, + ); + + @override + String toString() { + return 'PerksModel{name: $name, description: $description}'; + } +} diff --git a/evently/lib/models/perks_model.dart b/evently/lib/models/perks_model.dart index bc9d10c233..e69de29bb2 100644 --- a/evently/lib/models/perks_model.dart +++ b/evently/lib/models/perks_model.dart @@ -1,21 +0,0 @@ -class PerksModel { - PerksModel({required this.name, required this.description}); - - final String name; - final String description; - - factory PerksModel.updateName({required String name, required PerksModel perksModel}) => PerksModel( - name: name, - description: perksModel.description, - ); - - factory PerksModel.updateDescription({required String description, required PerksModel perksModel}) => PerksModel( - name: perksModel.name, - description: description, - ); - - @override - String toString() { - return 'PerksModel{name: $name, description: $description}'; - } -} diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index f47ea28073..60d41f4d97 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -1,54 +1,74 @@ import 'package:equatable/equatable.dart'; +import 'package:fixnum/fixnum.dart'; import 'package:get_it/get_it.dart'; -import 'package:pylons_wallet/modules/Pylonstech.pylons.pylons/module/client/pylons/recipe.pb.dart'; import 'package:pylons_wallet/stores/wallet_store.dart'; -import 'package:pylons_wallet/utils/constants.dart'; +import '../modules/Pylonstech.pylons.pylons/module/client/cosmos/base/v1beta1/coin.pb.dart'; +import '../modules/Pylonstech.pylons.pylons/module/client/pylons/recipe.pb.dart'; + +enum FreeDrop { yes, no, unselected } class Events extends Equatable { const Events({ - required this.eventName, - required this.hostName, - required this.thumbnail, - required this.startDate, - required this.endDate, - required this.startTime, - required this.endTime, - required this.location, - required this.description, - required this.isFreeDrop, - required this.numberOfTickets, - required this.price, - required this.listOfPerks, + ///* overview data + this.eventName = '', + this.hostName = '', + this.thumbnail = '', + + ///* details + this.startDate = '', + this.endDate = '', + this.startTime = '', + this.endTime = '', + this.location = '', + this.description = '', + + ///* perks + this.listOfPerks = '', + + ///* price + this.numberOfTickets = '0', + this.price = '', + this.isFreeDrops = 'unselected', + + ///* other + this.cookbookID = '', + this.recipeID = '', }); final String eventName; - final String hostName; - final String thumbnail; - final String startDate; - final String endDate; - final String startTime; - final String endTime; - final String location; - final String description; - - final String isFreeDrop; - final String numberOfTickets; - final String price; + final String listOfPerks; + final String isFreeDrops; + final String cookbookID; + final String recipeID; - final List listOfPerks; + factory Events.fromRecipe(Recipe recipe) { + Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); - @override - List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, isFreeDrop, numberOfTickets, price]; + return Events( + eventName: map[kEventName]!, + hostName: map[kEventHostName]!, + thumbnail: map[kThumbnail]!, + startDate: map[kStartDate]!, + endDate: map[kEndDate]!, + startTime: map[kStartTime]!, + endTime: map[kEndTime]!, + location: map[kLocation]!, + description: map[kDescription]!, + numberOfTickets: map[kNumberOfTickets]!, + price: map[kPrice]!, + listOfPerks: map[kPerks]!, + ); + } static Map _extractAttributeValues(List attributes) { final Map attributeValues = {}; @@ -76,24 +96,12 @@ class Events extends Equatable { return attributeValues; } - factory Events.fromRecipe(Recipe recipe) { - final map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); + @override + List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price, listOfPerks, isFreeDrops, cookbookID, recipeID]; - return Events( - eventName: map[kEventName]!, - hostName: map[kEventHostName]!, - thumbnail: map[kThumbnail]!, - startDate: map[kStartDate]!, - endDate: map[kEndDate]!, - startTime: map[kStartTime]!, - endTime: map[kEndTime]!, - location: map[kLocation]!, - description: map[kDescription]!, - isFreeDrop: '', - numberOfTickets: map[kNumberOfTickets]!, - price: map[kPrice]!, - listOfPerks: [], - ); + @override + String toString() { + return 'Event{eventName: $eventName, hostName: $hostName, thumbnail: $thumbnail, startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime, location: $location, description: $description, numberOfTickets: $numberOfTickets, price: $price, tradePercentage: $listOfPerks, isFreeDrop: $isFreeDrops, cookbookID: $cookbookID, recipeID: $recipeID}'; } static Future fromRecipeId(String cookbookId, String recipeId) async { @@ -107,3 +115,117 @@ class Events extends Equatable { return Events.fromRecipe(recipeEither.toOption().toNullable()!); } } + +extension CreateRecipe on Events { + Recipe createRecipe({ + required String cookbookId, + required String recipeId, + required FreeDrop isFreeDrop, + required String symbol, + required List perksList, + required String price, + }) { + return Recipe( + cookbookId: cookbookId, + id: recipeId, + nodeVersion: Int64(1), + name: eventName.trim(), + description: description.trim(), + version: kVersion, + coinInputs: [ + if (isFreeDrop == FreeDrop.yes) + CoinInput() + else + CoinInput( + coins: [Coin(amount: price, denom: symbol)], + ) + ], + itemInputs: [], + costPerBlock: Coin(denom: kUpylon, amount: costPerBlock), + entries: EntriesList( + coinOutputs: [], + itemOutputs: [ + ItemOutput( + id: kEventlyEvent, + doubles: [], + longs: [], + strings: [ + StringParam(key: kEventName, value: eventName.trim()), + StringParam(key: kEventHostName, value: hostName.trim()), + StringParam(key: kThumbnail, value: thumbnail.trim()), + StringParam(key: kStartDate, value: startDate.trim()), + StringParam(key: kEndDate, value: endDate.trim()), + StringParam(key: kStartTime, value: startTime.trim()), + StringParam(key: kEndTime, value: endTime.trim()), + StringParam(key: kLocation, value: location.trim()), + StringParam(key: kDescription, value: description.trim()), + StringParam(key: kNumberOfTickets, value: numberOfTickets.trim()), + StringParam(key: kPrice, value: price.trim()), + StringParam(key: kPerks, value: listOfPerks.trim()), + StringParam(key: kFreeDrop, value: isFreeDrops), + StringParam(key: kCookBookId, value: cookbookID), + StringParam(key: kRecipeId, value: recipeID), + ], + mutableStrings: [], + transferFee: [Coin(denom: kPylonSymbol, amount: transferFeeAmount)], + tradeable: true, + amountMinted: Int64(), + ), + ], + itemModifyOutputs: [], + ), + outputs: [ + WeightedOutputs(entryIds: [kEventlyEvent], weight: Int64(1)) + ], + blockInterval: Int64(), + enabled: true, + extraInfo: kExtraInfo, + ); + } +} + +/// Event String keys +const kEventName = "kEventName"; +const kEventHostName = "kEventHostName"; +const kThumbnail = "kThumbnail"; +const kStartDate = "kStartDate"; +const kEndDate = "kEndDate"; +const kStartTime = "kStartTime"; +const kEndTime = "kEndTime"; +const kLocation = "kLocation"; +const kDescription = "kDescription"; +const kPerks = "kPerks"; +const kNumberOfTickets = "kNumberOfTickets"; +const kPrice = "kPrice"; +const kFreeDrop = "kFreeDrop"; +const kRecipeId = "kRecipeId"; +const kCookBookId = "kCookBookId"; +const kVersion = "v0.2.0"; +const kUpylon = "upylon"; +const kPylonSymbol = 'upylon'; +const String transferFeeAmount = '1'; +const kEventlyEvent = "Evently_Event"; +const kExtraInfo = "extraInfo"; +const String costPerBlock = '0'; + +class PerksModel { + PerksModel({required this.name, required this.description}); + + final String name; + final String description; + + factory PerksModel.updateName({required String name, required PerksModel perksModel}) => PerksModel( + name: name, + description: perksModel.description, + ); + + factory PerksModel.updateDescription({required String description, required PerksModel perksModel}) => PerksModel( + name: perksModel.name, + description: description, + ); + + @override + String toString() { + return 'PerksModel{name: $name, description: $description}'; + } +} diff --git a/wallet/lib/utils/constants.dart b/wallet/lib/utils/constants.dart index 04b1c209a3..ecde213025 100644 --- a/wallet/lib/utils/constants.dart +++ b/wallet/lib/utils/constants.dart @@ -499,18 +499,3 @@ const String drawerKey = "drawer_key"; const String kRemaining = 'remaining'; const kTotal = 'total'; const kFileExtension = "file_extension"; - -///* Event String -/// Event String keys -const kEventName = "kEventName"; -const kEventHostName = "kEventHostName"; -const kThumbnail = "kThumbnail"; -const kStartDate = "kStartDate"; -const kEndDate = "kEndDate"; -const kStartTime = "kStartTime"; -const kEndTime = "kEndTime"; -const kLocation = "kLocation"; -const kPerks = "kPerks"; -const kNumberOfTickets = "kNumberOfTickets"; -const kPrice = "kPrice"; -const kErrRecipe = 'Recipe error :'; From c873140c1777605859123059501dce359ac625b0 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 15:00:43 +0500 Subject: [PATCH 095/161] feat:fixes on publishing --- evently/lib/evently_provider.dart | 4 +--- evently/lib/models/events.dart | 23 ++++++++++++----------- evently/lib/models/perks_model.dart | 0 evently/lib/screens/perks_screen.dart | 2 +- evently/lib/screens/price_screen.dart | 1 + 5 files changed, 15 insertions(+), 15 deletions(-) delete mode 100644 evently/lib/models/perks_model.dart diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index fe2e8a1cb2..55fd917fc0 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -5,7 +5,6 @@ import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/main.dart'; import 'package:evently/models/denom.dart'; import 'package:evently/models/events.dart'; -import 'package:evently/models/perks_model.dart'; import 'package:evently/models/picked_file_model.dart'; import 'package:evently/models/storage_response_model.dart'; import 'package:evently/repository/repository.dart'; @@ -21,7 +20,6 @@ import 'package:pylons_sdk/low_level.dart'; import 'services/third_party_services/quick_node.dart'; - @LazySingleton() class EventlyProvider extends ChangeNotifier { EventlyProvider({ @@ -268,7 +266,7 @@ class EventlyProvider extends ChangeNotifier { description: description, numberOfTickets: numberOfTickets.toString(), price: price.toString(), - listOfPerks: perks.map((e) => e.toString()).toList(), + listOfPerks: perks.join(','), ); final recipe = event.createRecipe( diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 34a3b14a55..579384d132 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -50,7 +50,6 @@ class Events extends Equatable { factory Events.fromRecipe(Recipe recipe) { Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); - return Events( eventName: map[kEventName]!, hostName: map[kEventHostName]!, @@ -80,9 +79,12 @@ class Events extends Equatable { case kEndTime: case kLocation: case kDescription: - case kPerks: case kNumberOfTickets: case kPrice: + case kPerks: + case kFreeDrop: + case kCookBookId: + case kRecipeId: attributeValues[attribute.key] = attribute.value; break; default: @@ -189,11 +191,10 @@ const kCookBookId = "kCookBookId"; const kVersion = "v0.2.0"; const kUpylon = "upylon"; const kPylonSymbol = 'upylon'; -const String transferFeeAmount = '1'; +const transferFeeAmount = '1'; const kEventlyEvent = "Evently_Event"; const kExtraInfo = "extraInfo"; -const String costPerBlock = '0'; - +const costPerBlock = '0'; class PerksModel { PerksModel({required this.name, required this.description}); @@ -202,14 +203,14 @@ class PerksModel { final String description; factory PerksModel.updateName({required String name, required PerksModel perksModel}) => PerksModel( - name: name, - description: perksModel.description, - ); + name: name, + description: perksModel.description, + ); factory PerksModel.updateDescription({required String description, required PerksModel perksModel}) => PerksModel( - name: perksModel.name, - description: description, - ); + name: perksModel.name, + description: description, + ); @override String toString() { diff --git a/evently/lib/models/perks_model.dart b/evently/lib/models/perks_model.dart deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index fc9efab293..097aed9675 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -1,7 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; import 'package:evently/generated/locale_keys.g.dart'; -import 'package:evently/models/perks_model.dart'; +import 'package:evently/models/events.dart'; import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 3db25b2b1d..9ab2f504ae 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; +import 'package:evently/models/events.dart'; import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; From 03cd830546c9a635e554cf144702b0189c452873 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 15:04:55 +0500 Subject: [PATCH 096/161] fix: host name while creating event --- evently/lib/evently_provider.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 55fd917fc0..448a54e4e7 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -238,8 +238,6 @@ class EventlyProvider extends ChangeNotifier { _selectedDenom = supportedDenomList.first; } } - setHostName = currentUserName; - return sdkResponse; } From 7712600e5812fceadc815b9be61fed01e97cea6d Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 15:08:57 +0500 Subject: [PATCH 097/161] feat: cookbook id and recepie id in --- evently/lib/evently_provider.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 448a54e4e7..d5a32b9816 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -265,6 +265,8 @@ class EventlyProvider extends ChangeNotifier { numberOfTickets: numberOfTickets.toString(), price: price.toString(), listOfPerks: perks.join(','), + cookbookID: _cookbookId!, + recipeID: _recipeId, ); final recipe = event.createRecipe( From c477523b59af52917ddffc2fc80a687613d597ed Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 15:18:20 +0500 Subject: [PATCH 098/161] feat: delete created event from draftr --- evently/lib/evently_provider.dart | 13 ++++++++++--- .../lib/screens/event_hub/event_hub_view_model.dart | 10 ++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 evently/lib/screens/event_hub/event_hub_view_model.dart diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index d5a32b9816..00fb772465 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -8,6 +8,8 @@ import 'package:evently/models/events.dart'; import 'package:evently/models/picked_file_model.dart'; import 'package:evently/models/storage_response_model.dart'; import 'package:evently/repository/repository.dart'; +import 'package:evently/screens/event_hub/event_hub_screen.dart'; +import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/extension_util.dart'; import 'package:evently/utils/failure/failure.dart'; @@ -15,6 +17,7 @@ import 'package:evently/widgets/loading_with_progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:get_it/get_it.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/low_level.dart'; @@ -285,15 +288,19 @@ class EventlyProvider extends ChangeNotifier { return false; } scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); - // final nftFromRecipe = Event.fromRecipe(recipe); - // GetIt.I.get<>().updatePublishedNFTList(nft: nftFromRecipe); - // deleteNft(nft.id); + final nftFromRecipe = Events.fromRecipe(recipe); + GetIt.I.get().updatePublishedNFTList(nft: nftFromRecipe); + deleteNft(); return true; } return false; } + Future deleteNft() async { + notifyListeners(); + } + /// send createCookBook tx message to the wallet app /// return true or false depending on the response from the wallet app Future createCookbook() async { diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart new file mode 100644 index 0000000000..9dbcad391e --- /dev/null +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -0,0 +1,10 @@ +import 'package:evently/models/events.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:injectable/injectable.dart'; + +@lazySingleton +class EventHubViewModel extends ChangeNotifier { + + + void updatePublishedNFTList({required Events nft}) {} +} From 7c383fd9a5bf2b613660229a7ef111b8506df72d Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 15:20:23 +0500 Subject: [PATCH 099/161] feat: update publish events --- evently/lib/evently_provider.dart | 6 +++--- evently/lib/screens/event_hub/event_hub_view_model.dart | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 00fb772465..d08d8b9fb6 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -289,15 +289,15 @@ class EventlyProvider extends ChangeNotifier { } scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); final nftFromRecipe = Events.fromRecipe(recipe); - GetIt.I.get().updatePublishedNFTList(nft: nftFromRecipe); - deleteNft(); + GetIt.I.get().updatePublishedEventList(nft: nftFromRecipe); + deleteEvent(); return true; } return false; } - Future deleteNft() async { + Future deleteEvent() async { notifyListeners(); } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 9dbcad391e..6e5e0d0075 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -4,7 +4,9 @@ import 'package:injectable/injectable.dart'; @lazySingleton class EventHubViewModel extends ChangeNotifier { + final List _eventPublishedList = []; + List get eventPublishedList => _eventPublishedList; - void updatePublishedNFTList({required Events nft}) {} + void updatePublishedEventList({required Events nft}) {} } From 3bcb53eb6a6ff8e2768408433e138c755888c27d Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 15:25:30 +0500 Subject: [PATCH 100/161] feat: event sale list on the creator hub --- evently/lib/evently_provider.dart | 4 ++-- .../event_hub/event_hub_view_model.dart | 22 +++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index d08d8b9fb6..e9fd280f46 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -288,8 +288,8 @@ class EventlyProvider extends ChangeNotifier { return false; } scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); - final nftFromRecipe = Events.fromRecipe(recipe); - GetIt.I.get().updatePublishedEventList(nft: nftFromRecipe); + final eventsFromRecipe = Events.fromRecipe(recipe); + GetIt.I.get().updatePublishedEventList(events: eventsFromRecipe); deleteEvent(); return true; } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 6e5e0d0075..3a7e7e260e 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -4,9 +4,27 @@ import 'package:injectable/injectable.dart'; @lazySingleton class EventHubViewModel extends ChangeNotifier { - final List _eventPublishedList = []; + List _eventPublishedList = []; List get eventPublishedList => _eventPublishedList; - void updatePublishedEventList({required Events nft}) {} + List _eventForSaleList = []; + + List get eventForSaleList => _eventForSaleList; + + set setEventForSaleList(List nftForSale) { + _eventForSaleList = nftForSale; + notifyListeners(); + } + + set setEventPublishList(List events) { + _eventPublishedList = events; + notifyListeners(); + } + + void updatePublishedEventList({required Events events}) { + _eventPublishedList.add(events); + _eventForSaleList.add(events); + notifyListeners(); + } } From 05ce159c036d350d81adb8eb82a5d2d5a48d5f26 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 15:37:09 +0500 Subject: [PATCH 101/161] feat: event hub --- evently/lib/evently_provider.dart | 1 - .../event_hub/event_hub_view_model.dart | 23 ++++++++++ evently/lib/utils/di/di.config.dart | 46 ++++++++++--------- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index e9fd280f46..415dd3b844 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -8,7 +8,6 @@ import 'package:evently/models/events.dart'; import 'package:evently/models/picked_file_model.dart'; import 'package:evently/models/storage_response_model.dart'; import 'package:evently/repository/repository.dart'; -import 'package:evently/screens/event_hub/event_hub_screen.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/extension_util.dart'; diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 3a7e7e260e..481d783fc9 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -2,6 +2,8 @@ import 'package:evently/models/events.dart'; import 'package:flutter/cupertino.dart'; import 'package:injectable/injectable.dart'; +enum CollectionType { draft, forSale, history } + @lazySingleton class EventHubViewModel extends ChangeNotifier { List _eventPublishedList = []; @@ -27,4 +29,25 @@ class EventHubViewModel extends ChangeNotifier { _eventForSaleList.add(events); notifyListeners(); } + + CollectionType selectedCollectionType = CollectionType.draft; + + void changeSelectedCollection(CollectionType collectionType) { + switch (collectionType) { + case CollectionType.draft: + selectedCollectionType = CollectionType.draft; + notifyListeners(); + break; + + case CollectionType.forSale: + selectedCollectionType = CollectionType.forSale; + notifyListeners(); + break; + + case CollectionType.history: + selectedCollectionType = CollectionType.history; + notifyListeners(); + break; + } + } } diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index 84941fbb75..e681b1037a 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -9,19 +9,20 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:dio/dio.dart' as _i6; -import 'package:evently/evently_provider.dart' as _i13; -import 'package:evently/repository/repository.dart' as _i12; -import 'package:evently/services/datasources/local_datasource.dart' as _i7; -import 'package:evently/services/datasources/remote_datasource.dart' as _i11; -import 'package:evently/services/third_party_services/quick_node.dart' as _i10; -import 'package:evently/utils/di/register_modules.dart' as _i14; -import 'package:evently/utils/file_utils_helper.dart' as _i9; +import 'package:evently/evently_provider.dart' as _i14; +import 'package:evently/repository/repository.dart' as _i13; +import 'package:evently/screens/event_hub/event_hub_view_model.dart' as _i7; +import 'package:evently/services/datasources/local_datasource.dart' as _i8; +import 'package:evently/services/datasources/remote_datasource.dart' as _i12; +import 'package:evently/services/third_party_services/quick_node.dart' as _i11; +import 'package:evently/utils/di/register_modules.dart' as _i15; +import 'package:evently/utils/file_utils_helper.dart' as _i10; import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; import 'package:file_picker/file_picker.dart' as _i5; import 'package:get_it/get_it.dart' as _i1; import 'package:image_cropper/image_cropper.dart' as _i4; import 'package:injectable/injectable.dart' as _i2; -import 'package:shared_preferences/shared_preferences.dart' as _i8; +import 'package:shared_preferences/shared_preferences.dart' as _i9; extension GetItInjectableX on _i1.GetIt { // initializes the registration of main-scope dependencies inside of GetIt @@ -40,25 +41,26 @@ extension GetItInjectableX on _i1.GetIt { gh.lazySingleton<_i4.ImageCropper>(() => registerModule.imageCropper); gh.lazySingleton<_i5.FilePicker>(() => registerModule.filePicker); gh.lazySingleton<_i6.Dio>(() => registerModule.dio); - gh.lazySingleton<_i7.LocalDataSource>(() => _i7.LocalDataSourceImpl( - sharedPreferences: gh<_i8.SharedPreferences>())); - gh.lazySingleton<_i9.FileUtilsHelper>(() => _i9.FileUtilsHelperImpl( + gh.lazySingleton<_i7.EventHubViewModel>(() => _i7.EventHubViewModel()); + gh.lazySingleton<_i8.LocalDataSource>(() => _i8.LocalDataSourceImpl( + sharedPreferences: gh<_i9.SharedPreferences>())); + gh.lazySingleton<_i10.FileUtilsHelper>(() => _i10.FileUtilsHelperImpl( imageCropper: gh<_i4.ImageCropper>(), filePicker: gh<_i5.FilePicker>(), )); - gh.lazySingleton<_i10.QuickNode>( - () => _i10.QuickNodeImpl(httpClient: gh<_i6.Dio>())); - gh.lazySingleton<_i11.RemoteDataSource>( - () => _i11.RemoteDataSourceImpl(quickNode: gh<_i10.QuickNode>())); - gh.lazySingleton<_i12.Repository>(() => _i12.RepositoryImp( - fileUtilsHelper: gh<_i9.FileUtilsHelper>(), - localDataSource: gh<_i7.LocalDataSource>(), - remoteDataSource: gh<_i11.RemoteDataSource>(), + gh.lazySingleton<_i11.QuickNode>( + () => _i11.QuickNodeImpl(httpClient: gh<_i6.Dio>())); + gh.lazySingleton<_i12.RemoteDataSource>( + () => _i12.RemoteDataSourceImpl(quickNode: gh<_i11.QuickNode>())); + gh.lazySingleton<_i13.Repository>(() => _i13.RepositoryImp( + fileUtilsHelper: gh<_i10.FileUtilsHelper>(), + localDataSource: gh<_i8.LocalDataSource>(), + remoteDataSource: gh<_i12.RemoteDataSource>(), )); - gh.lazySingleton<_i13.EventlyProvider>( - () => _i13.EventlyProvider(repository: gh<_i12.Repository>())); + gh.lazySingleton<_i14.EventlyProvider>( + () => _i14.EventlyProvider(repository: gh<_i13.Repository>())); return this; } } -class _$RegisterModule extends _i14.RegisterModule {} +class _$RegisterModule extends _i15.RegisterModule {} From f27370cfc73a86ae83fcbdb8332202e0c2d3bc60 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 15:44:29 +0500 Subject: [PATCH 102/161] feat: if there is one cook book id then we can get the data on the basis of cook bookl --- evently/lib/evently_provider.dart | 90 +++++++++++++++++-------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 415dd3b844..d8f843d012 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -249,51 +249,59 @@ class EventlyProvider extends ChangeNotifier { Future createRecipe() async { final scaffoldMessengerState = navigatorKey.getState(); - final isCookBookCreated = await createCookbook(); - - if (isCookBookCreated) { - _recipeId = repository.autoGenerateEventlyId(); - - final event = Events( - eventName: eventName, - hostName: hostName, - thumbnail: thumbnail!, - startDate: startDate, - endDate: endDate, - startTime: startTime, - endTime: endTime, - location: location, - description: description, - numberOfTickets: numberOfTickets.toString(), - price: price.toString(), - listOfPerks: perks.join(','), - cookbookID: _cookbookId!, - recipeID: _recipeId, - ); - - final recipe = event.createRecipe( - cookbookId: _cookbookId!, - recipeId: _recipeId, - isFreeDrop: isFreeDrop, - symbol: selectedDenom.symbol, - perksList: perks, - price: price.toString(), - ); - - final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); - - if (!response.success) { - scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); + _cookbookId = repository.getCookbookId(); + + if (_cookbookId == null) { + // create cookbook + final isCookBookCreated = await createCookbook(); + + if (isCookBookCreated) { + _cookbookId = repository.getCookbookId(); + notifyListeners(); + } else { return false; } - scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); - final eventsFromRecipe = Events.fromRecipe(recipe); - GetIt.I.get().updatePublishedEventList(events: eventsFromRecipe); - deleteEvent(); - return true; } - return false; + _recipeId = repository.autoGenerateEventlyId(); + + final event = Events( + eventName: eventName, + hostName: hostName, + thumbnail: thumbnail!, + startDate: startDate, + endDate: endDate, + startTime: startTime, + endTime: endTime, + location: location, + description: description, + numberOfTickets: numberOfTickets.toString(), + price: price.toString(), + listOfPerks: perks.join(','), + cookbookID: _cookbookId!, + recipeID: _recipeId, + ); + + final recipe = event.createRecipe( + cookbookId: _cookbookId!, + recipeId: _recipeId, + isFreeDrop: isFreeDrop, + symbol: selectedDenom.symbol, + perksList: perks, + price: price.toString(), + ); + + final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); + + if (!response.success) { + scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); + return false; + } + scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); + final eventsFromRecipe = Events.fromRecipe(recipe); + GetIt.I.get().updatePublishedEventList(events: eventsFromRecipe); + deleteEvent(); + return true; } Future deleteEvent() async { From 790d9328364447d999af2c4efe8566f0be56f7a5 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 16:09:37 +0500 Subject: [PATCH 103/161] feat: get publish event list --- evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/evently_provider.dart | 1 - evently/lib/generated/locale_keys.g.dart | 1 + evently/lib/repository/repository.dart | 21 ++++++++- .../event_hub/event_hub_view_model.dart | 39 ++++++++++++++++ .../datasources/remote_datasource.dart | 12 +++++ evently/lib/utils/di/di.config.dart | 45 ++++++++++--------- evently/lib/utils/failure/failure.dart | 14 ++++++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + evently/pubspec.lock | 32 +++++++++++++ evently/pubspec.yaml | 1 + 14 files changed, 152 insertions(+), 28 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 9bed4c9030..033e923f1f 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -61,5 +61,6 @@ "recipe_created": "Recipe созданный", "update_failed": "Upload Fehlgeschlagen", "uploading": "Hochladen ...", - "something_wrong_while_uploading": "Beim Hochladen ist etwas schief gelaufen. Bitte versuche es erneut." + "something_wrong_while_uploading": "Beim Hochladen ist etwas schief gelaufen. Bitte versuche es erneut.", + "cookbook_not_found": "Cookbook nicht gefunden" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 25487d1b89..63453ae74e 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -61,5 +61,6 @@ "recipe_created": "Recipe created", "update_failed": "Upload Failed", "uploading": "Uploading ...", - "something_wrong_while_uploading": "Something went wrong while uploading. Please try again." + "something_wrong_while_uploading": "Something went wrong while uploading. Please try again.", + "cookbook_not_found": "Cookbook not found" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 281c7ee53a..ddd61defe1 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -61,5 +61,6 @@ "recipe_created": "Recipe creada", "update_failed": "Subida ha fallado", "uploading": "Subiendo...", - "something_wrong_while_uploading": "Algo salió mal durante la carga. Inténtalo de nuevo." + "something_wrong_while_uploading": "Algo salió mal durante la carga. Inténtalo de nuevo.", + "cookbook_not_found": "Cookbook extraviado" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 20ed93a46b..1940b33798 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -61,5 +61,6 @@ "recipe_created": "Recipe созданный", "update_failed": "Загрузка не удалась", "uploading": "Загрузка...", - "something_wrong_while_uploading": "Что-то пошло не так во время загрузки. Пожалуйста, попробуйте еще раз." + "something_wrong_while_uploading": "Что-то пошло не так во время загрузки. Пожалуйста, попробуйте еще раз.", + "cookbook_not_found": "Cookbook не найден" } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index d8f843d012..4e2275e7e8 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -252,7 +252,6 @@ class EventlyProvider extends ChangeNotifier { _cookbookId = repository.getCookbookId(); if (_cookbookId == null) { - // create cookbook final isCookBookCreated = await createCookbook(); if (isCookBookCreated) { diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index a55f453afc..7091981e24 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -62,5 +62,6 @@ abstract class LocaleKeys { static const update_failed = 'update_failed'; static const uploading = 'uploading'; static const something_wrong_while_uploading = 'something_wrong_while_uploading'; + static const cookbook_not_found = 'cookbook_not_found'; } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 071eac59cc..81cc023cd7 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -1,3 +1,4 @@ +import 'dart:developer'; import 'package:dartz/dartz.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; @@ -8,7 +9,7 @@ import 'package:evently/services/datasources/remote_datasource.dart'; import 'package:evently/services/third_party_services/quick_node.dart'; import 'package:evently/utils/file_utils_helper.dart'; import 'package:injectable/injectable.dart'; - +import 'package:pylons_sdk/low_level.dart'; import '../utils/failure/failure.dart'; abstract class Repository { @@ -46,6 +47,12 @@ abstract class Repository { /// This method will get the artist name /// Output: [String] returns whether the operation is successful or not String getHostName(); + + /// This method returns the recipe list + /// Input : [cookBookId] id of the cookbook + /// Output: if successful the output will be the list of [pylons.Recipe] + /// will return error in the form of failure + Future>> getRecipesBasedOnCookBookId({required String cookBookId}); } @LazySingleton(as: Repository) @@ -110,4 +117,16 @@ class RepositoryImp implements Repository { String getHostName() { return localDataSource.getHostName(); } + + @override + Future>> getRecipesBasedOnCookBookId({required String cookBookId}) async { + try { + final sdkResponse = await remoteDataSource.getRecipesByCookbookID(cookBookId); + log(sdkResponse.toString(), name: 'pylons_sdk'); + + return Right(sdkResponse); + } on Exception catch (_) { + return Left(CookBookNotFoundFailure(LocaleKeys.cookbook_not_found.tr())); + } + } } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 481d783fc9..0ef88415b4 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -1,11 +1,17 @@ import 'package:evently/models/events.dart'; +import 'package:evently/repository/repository.dart'; import 'package:flutter/cupertino.dart'; import 'package:injectable/injectable.dart'; +import 'package:pylons_sdk/pylons_sdk.dart'; enum CollectionType { draft, forSale, history } @lazySingleton class EventHubViewModel extends ChangeNotifier { + EventHubViewModel(this.repository); + + final Repository repository; + List _eventPublishedList = []; List get eventPublishedList => _eventPublishedList; @@ -50,4 +56,37 @@ class EventHubViewModel extends ChangeNotifier { break; } } + + String? getCookbookIdFromLocalDatasource() { + return repository.getCookbookId(); + } + + Future getRecipesList() async { + final isPylonsExist = await PylonsWallet.instance.exists(); + + if (!isPylonsExist) { + return; + } + + final cookBookId = getCookbookIdFromLocalDatasource(); + if (cookBookId == null) { + return; + } + + final recipesListEither = await repository.getRecipesBasedOnCookBookId(cookBookId: cookBookId); + + if (recipesListEither.isLeft()) { + return; + } + + final recipesList = recipesListEither.getOrElse(() => []); + + if (recipesList.isEmpty) { + return; + } + for (final recipe in recipesList) { + final nft = Events.fromRecipe(recipe); + _eventPublishedList.add(nft); + } + } } diff --git a/evently/lib/services/datasources/remote_datasource.dart b/evently/lib/services/datasources/remote_datasource.dart index b82bdd55e7..01f938f428 100644 --- a/evently/lib/services/datasources/remote_datasource.dart +++ b/evently/lib/services/datasources/remote_datasource.dart @@ -1,12 +1,18 @@ import 'package:evently/models/storage_response_model.dart'; import 'package:evently/services/third_party_services/quick_node.dart'; import 'package:injectable/injectable.dart'; +import 'package:pylons_sdk/low_level.dart'; abstract class RemoteDataSource { /// This method is used uploading provided file to the server using [QuickNode] /// Input : [UploadIPFSInput] which needs to be uploaded /// Output : [Future>] the ApiResponse which can contain [success] or [error] response Future uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); + + /// This method is used to getRecipesList based on CookbookID + /// Input : [cookBookID] against which recipes needs to be fetched + /// Output : [Future>] which will be a Future list of Recipes against the given [cookbookID] + Future> getRecipesByCookbookID(String cookBookID); } @LazySingleton(as: RemoteDataSource) @@ -20,4 +26,10 @@ class RemoteDataSourceImpl extends RemoteDataSource { final response = await quickNode.uploadNewObjectToIPFS(uploadIPFSInput: uploadIPFSInput, onUploadProgressCallback: onUploadProgressCallback); return response; } + + @override + Future> getRecipesByCookbookID(String cookBookID) async { + final sdkResponse = await PylonsWallet.instance.getRecipes(cookBookID); + return sdkResponse.data!; + } } diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index e681b1037a..d702697c9d 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -9,20 +9,20 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:dio/dio.dart' as _i6; -import 'package:evently/evently_provider.dart' as _i14; -import 'package:evently/repository/repository.dart' as _i13; -import 'package:evently/screens/event_hub/event_hub_view_model.dart' as _i7; -import 'package:evently/services/datasources/local_datasource.dart' as _i8; -import 'package:evently/services/datasources/remote_datasource.dart' as _i12; -import 'package:evently/services/third_party_services/quick_node.dart' as _i11; +import 'package:evently/evently_provider.dart' as _i13; +import 'package:evently/repository/repository.dart' as _i12; +import 'package:evently/screens/event_hub/event_hub_view_model.dart' as _i14; +import 'package:evently/services/datasources/local_datasource.dart' as _i7; +import 'package:evently/services/datasources/remote_datasource.dart' as _i11; +import 'package:evently/services/third_party_services/quick_node.dart' as _i10; import 'package:evently/utils/di/register_modules.dart' as _i15; -import 'package:evently/utils/file_utils_helper.dart' as _i10; +import 'package:evently/utils/file_utils_helper.dart' as _i9; import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; import 'package:file_picker/file_picker.dart' as _i5; import 'package:get_it/get_it.dart' as _i1; import 'package:image_cropper/image_cropper.dart' as _i4; import 'package:injectable/injectable.dart' as _i2; -import 'package:shared_preferences/shared_preferences.dart' as _i9; +import 'package:shared_preferences/shared_preferences.dart' as _i8; extension GetItInjectableX on _i1.GetIt { // initializes the registration of main-scope dependencies inside of GetIt @@ -41,24 +41,25 @@ extension GetItInjectableX on _i1.GetIt { gh.lazySingleton<_i4.ImageCropper>(() => registerModule.imageCropper); gh.lazySingleton<_i5.FilePicker>(() => registerModule.filePicker); gh.lazySingleton<_i6.Dio>(() => registerModule.dio); - gh.lazySingleton<_i7.EventHubViewModel>(() => _i7.EventHubViewModel()); - gh.lazySingleton<_i8.LocalDataSource>(() => _i8.LocalDataSourceImpl( - sharedPreferences: gh<_i9.SharedPreferences>())); - gh.lazySingleton<_i10.FileUtilsHelper>(() => _i10.FileUtilsHelperImpl( + gh.lazySingleton<_i7.LocalDataSource>(() => _i7.LocalDataSourceImpl( + sharedPreferences: gh<_i8.SharedPreferences>())); + gh.lazySingleton<_i9.FileUtilsHelper>(() => _i9.FileUtilsHelperImpl( imageCropper: gh<_i4.ImageCropper>(), filePicker: gh<_i5.FilePicker>(), )); - gh.lazySingleton<_i11.QuickNode>( - () => _i11.QuickNodeImpl(httpClient: gh<_i6.Dio>())); - gh.lazySingleton<_i12.RemoteDataSource>( - () => _i12.RemoteDataSourceImpl(quickNode: gh<_i11.QuickNode>())); - gh.lazySingleton<_i13.Repository>(() => _i13.RepositoryImp( - fileUtilsHelper: gh<_i10.FileUtilsHelper>(), - localDataSource: gh<_i8.LocalDataSource>(), - remoteDataSource: gh<_i12.RemoteDataSource>(), + gh.lazySingleton<_i10.QuickNode>( + () => _i10.QuickNodeImpl(httpClient: gh<_i6.Dio>())); + gh.lazySingleton<_i11.RemoteDataSource>( + () => _i11.RemoteDataSourceImpl(quickNode: gh<_i10.QuickNode>())); + gh.lazySingleton<_i12.Repository>(() => _i12.RepositoryImp( + fileUtilsHelper: gh<_i9.FileUtilsHelper>(), + localDataSource: gh<_i7.LocalDataSource>(), + remoteDataSource: gh<_i11.RemoteDataSource>(), )); - gh.lazySingleton<_i14.EventlyProvider>( - () => _i14.EventlyProvider(repository: gh<_i13.Repository>())); + gh.lazySingleton<_i13.EventlyProvider>( + () => _i13.EventlyProvider(repository: gh<_i12.Repository>())); + gh.lazySingleton<_i14.EventHubViewModel>( + () => _i14.EventHubViewModel(gh<_i12.Repository>())); return this; } } diff --git a/evently/lib/utils/failure/failure.dart b/evently/lib/utils/failure/failure.dart index c6cdf1afe5..6a688a9c76 100644 --- a/evently/lib/utils/failure/failure.dart +++ b/evently/lib/utils/failure/failure.dart @@ -19,3 +19,17 @@ class CacheFailure extends Failure { @override List get props => [message]; } + +class NoInternetFailure extends Failure { + const NoInternetFailure(super.message); + + @override + List get props => [message]; +} + +class CookBookNotFoundFailure extends Failure { + const CookBookNotFoundFailure(super.message); + + @override + List get props => [message]; +} diff --git a/evently/macos/Flutter/GeneratedPluginRegistrant.swift b/evently/macos/Flutter/GeneratedPluginRegistrant.swift index 63ad0d132c..405f9c1b98 100644 --- a/evently/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/evently/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,14 @@ import FlutterMacOS import Foundation +import network_info_plus import path_provider_foundation import shared_preferences_foundation import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/evently/pubspec.lock b/evently/pubspec.lock index c088c17954..42a83c01b9 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -209,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.10.1" + dbus: + dependency: transitive + description: + name: dbus + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + url: "https://pub.dev" + source: hosted + version: "0.7.10" decimal: dependency: transitive description: @@ -581,6 +589,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + network_info_plus: + dependency: "direct main" + description: + name: network_info_plus + sha256: "5bd4b86e28fed5ed4e6ac7764133c031dfb7d3f46aa2a81b46f55038aa78ecc0" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + network_info_plus_platform_interface: + dependency: transitive + description: + name: network_info_plus_platform_interface + sha256: "2e193d61d3072ac17824638793d3b89c6d581ce90c11604f4ca87311b42f2706" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" octo_image: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index d1e4e4fa9e..7e6b303c53 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -57,6 +57,7 @@ dependencies: dio: ^5.4.3+1 cached_network_image: ^3.3.1 shimmer_animation: 2.1.0+1 + network_info_plus: ^5.0.3 dev_dependencies: flutter_test: From db9dc09e78f8b8a989c036b04a847d05f88d0321 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 16:22:44 +0500 Subject: [PATCH 104/161] feat: floor database for storing draft data --- evently/i18n/de.json | 4 +- evently/i18n/en-US.json | 4 +- evently/i18n/es.json | 4 +- evently/i18n/ru-RU.json | 4 +- evently/lib/generated/locale_keys.g.dart | 2 + evently/lib/repository/repository.dart | 17 ++++ .../event_hub/event_hub_view_model.dart | 21 +++++ .../datasources/local_datasource.dart | 11 +++ evently/lib/utils/extension_util.dart | 1 + evently/pubspec.lock | 80 +++++++++++++++++++ evently/pubspec.yaml | 1 + 11 files changed, 145 insertions(+), 4 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 033e923f1f..55697d6cdf 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -62,5 +62,7 @@ "update_failed": "Upload Fehlgeschlagen", "uploading": "Hochladen ...", "something_wrong_while_uploading": "Beim Hochladen ist etwas schief gelaufen. Bitte versuche es erneut.", - "cookbook_not_found": "Cookbook nicht gefunden" + "cookbook_not_found": "Cookbook nicht gefunden", + "loading": "Wird geladen ...", + "something_wrong": "Etwas ist schief gelaufen. Bitte versuchen Sie, die Anwendung neu zu starten." } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 63453ae74e..1452511178 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -62,5 +62,7 @@ "update_failed": "Upload Failed", "uploading": "Uploading ...", "something_wrong_while_uploading": "Something went wrong while uploading. Please try again.", - "cookbook_not_found": "Cookbook not found" + "cookbook_not_found": "Cookbook not found", + "loading": "loading ...", + "something_wrong": "Something went wrong. Please try restarting the application." } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index ddd61defe1..09c9039b18 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -62,5 +62,7 @@ "update_failed": "Subida ha fallado", "uploading": "Subiendo...", "something_wrong_while_uploading": "Algo salió mal durante la carga. Inténtalo de nuevo.", - "cookbook_not_found": "Cookbook extraviado" + "cookbook_not_found": "Cookbook extraviado", + "loading": "cargando ...", + "something_wrong": "Algo salió mal. Intenta reiniciar la aplicación." } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 1940b33798..dc5f3b742d 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -62,5 +62,7 @@ "update_failed": "Загрузка не удалась", "uploading": "Загрузка...", "something_wrong_while_uploading": "Что-то пошло не так во время загрузки. Пожалуйста, попробуйте еще раз.", - "cookbook_not_found": "Cookbook не найден" + "cookbook_not_found": "Cookbook не найден", + "loading": "cargando ...", + "something_wrong": "Что-то пошло не так. Попробуйте перезапустить приложение." } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 7091981e24..e405267b99 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -63,5 +63,7 @@ abstract class LocaleKeys { static const uploading = 'uploading'; static const something_wrong_while_uploading = 'something_wrong_while_uploading'; static const cookbook_not_found = 'cookbook_not_found'; + static const loading = 'loading'; + static const something_wrong = 'something_wrong'; } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 81cc023cd7..31bced463f 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -2,6 +2,7 @@ import 'dart:developer'; import 'package:dartz/dartz.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/models/events.dart'; import 'package:evently/models/picked_file_model.dart'; import 'package:evently/models/storage_response_model.dart'; import 'package:evently/services/datasources/local_datasource.dart'; @@ -53,6 +54,10 @@ abstract class Repository { /// Output: if successful the output will be the list of [pylons.Recipe] /// will return error in the form of failure Future>> getRecipesBasedOnCookBookId({required String cookBookId}); + + /// This method will get the drafts List from the local database + /// Output: [List] returns that contains a number of [NFT] + Future>> getEvents(); } @LazySingleton(as: Repository) @@ -129,4 +134,16 @@ class RepositoryImp implements Repository { return Left(CookBookNotFoundFailure(LocaleKeys.cookbook_not_found.tr())); } } + + @override + Future>> getEvents() async { + try { + final response = await localDataSource.getEvents(); + + return Right(response); + } on Exception catch (exception) { + crashlyticsHelper.recordFatalError(error: exception.toString()); + return Left(CacheFailure(LocaleKeys.something_wrong.tr())); + } + } } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 0ef88415b4..20d17b712b 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -1,5 +1,8 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/models/events.dart'; import 'package:evently/repository/repository.dart'; +import 'package:evently/utils/extension_util.dart'; import 'package:flutter/cupertino.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/pylons_sdk.dart'; @@ -89,4 +92,22 @@ class EventHubViewModel extends ChangeNotifier { _eventPublishedList.add(nft); } } + + Future getDraftsList() async { + // final loading = Loading()..showLoading(message: LocaleKeys.loading.tr()); + + final getNftResponse = await repository.getEvents(); + + if (getNftResponse.isLeft()) { + loading.dismiss(); + LocaleKeys.something_wrong.tr().show(); + return; + } + + nftDraftList = getNftResponse.getOrElse(() => []); + + loading.dismiss(); + + notifyListeners(); + } } diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 07b8b19e5c..88dc782fae 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -1,3 +1,4 @@ +import 'package:evently/models/events.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/date_utils.dart'; import 'package:injectable/injectable.dart'; @@ -33,6 +34,10 @@ abstract class LocalDataSource { /// Input: [name] the name of the artist which the user want to save /// Output: [bool] returns whether the operation is successful or not Future saveHostName(String name); + + /// This method will get the drafts List from the local database + /// Output: [List][NFT] returns the List of drafts + Future> getEvents(); } @LazySingleton(as: LocalDataSource) @@ -86,4 +91,10 @@ class LocalDataSourceImpl extends LocalDataSource { await sharedPreferences.setString(kHostName, name); return true; } + + @override + Future> getEvents() { + // TODO: implement getEvents + throw UnimplementedError(); + } } diff --git a/evently/lib/utils/extension_util.dart b/evently/lib/utils/extension_util.dart index ef67399174..5cba9e0363 100644 --- a/evently/lib/utils/extension_util.dart +++ b/evently/lib/utils/extension_util.dart @@ -62,3 +62,4 @@ extension ScaffoldStateHelper on ScaffoldMessengerState { )); } } + diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 42a83c01b9..7dd01b7323 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -137,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -225,6 +233,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.3" + dev_build: + dependency: transitive + description: + name: dev_build + sha256: "2fa3bf81b5e92504f8d7a412df2c69b61a24ceca468466e5e777cbb59c30af96" + url: "https://pub.dev" + source: hosted + version: "0.16.5" dio: dependency: "direct main" description: @@ -305,6 +321,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + floor: + dependency: "direct main" + description: + name: floor + sha256: c1b06023912b5b8e49deb6a9d867863c535ae1a232d991c3582bba3ee8687867 + url: "https://pub.dev" + source: hosted + version: "1.5.0" + floor_annotation: + dependency: transitive + description: + name: floor_annotation + sha256: a40949580a7ab0eee572686e2d3b1638fd6bd6a753e661d792ab4236b365b23b + url: "https://pub.dev" + source: hosted + version: "1.5.0" + floor_common: + dependency: transitive + description: + name: floor_common + sha256: "41c9914862f83a821815e1b1ffd47a1e1ae2130c35ff882ba2d000a67713ba64" + url: "https://pub.dev" + source: hosted + version: "1.5.0" flutter: dependency: "direct main" description: flutter @@ -733,6 +773,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + process_run: + dependency: transitive + description: + name: process_run + sha256: "8d9c6198b98fbbfb511edd42e7364e24d85c163e47398919871b952dc86a423e" + url: "https://pub.dev" + source: hosted + version: "0.14.2" protobuf: dependency: transitive description: @@ -921,6 +969,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.4" + sqflite_common_ffi: + dependency: transitive + description: + name: sqflite_common_ffi + sha256: "4d6137c29e930d6e4a8ff373989dd9de7bac12e3bc87bce950f6e844e8ad3bb5" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + sqflite_common_ffi_web: + dependency: transitive + description: + name: sqflite_common_ffi_web + sha256: cfc9d1c61a3e06e5b2e96994a44b11125b4f451fee95b9fad8bd473b4613d592 + url: "https://pub.dev" + source: hosted + version: "0.4.3+1" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: b384f598b813b347c5a7e5ffad82cbaff1bec3d1561af267041e66f6f0899295 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + sqlparser: + dependency: transitive + description: + name: sqlparser + sha256: "7b20045d1ccfb7bc1df7e8f9fee5ae58673fce6ff62cefbb0e0fd7214e90e5a0" + url: "https://pub.dev" + source: hosted + version: "0.34.1" stack_trace: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index 7e6b303c53..a5f78805eb 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -58,6 +58,7 @@ dependencies: cached_network_image: ^3.3.1 shimmer_animation: 2.1.0+1 network_info_plus: ^5.0.3 + floor: ^1.5.0 dev_dependencies: flutter_test: From 7b70ac8394db4bd5c76afdf5151ecc110c802536 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 16:50:10 +0500 Subject: [PATCH 105/161] feat: floor database for events storage --- evently/lib/models/events.dart | 38 ++-- evently/lib/repository/repository.dart | 3 +- .../event_hub/event_hub_view_model.dart | 9 +- .../third_party_services/database.dart | 12 + .../third_party_services/database.g.dart | 205 ++++++++++++++++++ .../third_party_services/event_dao.dart | 17 ++ evently/lib/utils/di/di.dart | 2 + evently/pubspec.lock | 34 ++- evently/pubspec.yaml | 3 +- 9 files changed, 299 insertions(+), 24 deletions(-) create mode 100644 evently/lib/services/third_party_services/database.dart create mode 100644 evently/lib/services/third_party_services/database.g.dart create mode 100644 evently/lib/services/third_party_services/event_dao.dart diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 579384d132..de4ffa4b5b 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -1,10 +1,31 @@ import 'package:equatable/equatable.dart'; +import 'package:floor/floor.dart'; import 'package:pylons_sdk/low_level.dart'; import 'package:fixnum/fixnum.dart'; enum FreeDrop { yes, no, unselected } +@entity class Events extends Equatable { + @primaryKey + final int? id; + + final String eventName; + final String hostName; + final String thumbnail; + final String startDate; + final String endDate; + final String startTime; + final String endTime; + final String location; + final String description; + final String numberOfTickets; + final String price; + final String listOfPerks; + final String isFreeDrops; + final String cookbookID; + final String recipeID; + const Events({ ///* overview data this.eventName = '', @@ -30,24 +51,9 @@ class Events extends Equatable { ///* other this.cookbookID = '', this.recipeID = '', + this.id, }); - final String eventName; - final String hostName; - final String thumbnail; - final String startDate; - final String endDate; - final String startTime; - final String endTime; - final String location; - final String description; - final String numberOfTickets; - final String price; - final String listOfPerks; - final String isFreeDrops; - final String cookbookID; - final String recipeID; - factory Events.fromRecipe(Recipe recipe) { Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); return Events( diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 31bced463f..e265ad0cf1 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -141,8 +141,7 @@ class RepositoryImp implements Repository { final response = await localDataSource.getEvents(); return Right(response); - } on Exception catch (exception) { - crashlyticsHelper.recordFatalError(error: exception.toString()); + } on Exception catch (_) { return Left(CacheFailure(LocaleKeys.something_wrong.tr())); } } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 20d17b712b..627b48aa34 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -3,6 +3,7 @@ import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/models/events.dart'; import 'package:evently/repository/repository.dart'; import 'package:evently/utils/extension_util.dart'; +import 'package:evently/widgets/loading_with_progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/pylons_sdk.dart'; @@ -94,17 +95,17 @@ class EventHubViewModel extends ChangeNotifier { } Future getDraftsList() async { - // final loading = Loading()..showLoading(message: LocaleKeys.loading.tr()); + final loading = LoadingProgress()..showLoadingWithProgress(message: LocaleKeys.loading.tr()); - final getNftResponse = await repository.getEvents(); + final getEventResponse = await repository.getEvents(); - if (getNftResponse.isLeft()) { + if (getEventResponse.isLeft()) { loading.dismiss(); LocaleKeys.something_wrong.tr().show(); return; } - nftDraftList = getNftResponse.getOrElse(() => []); + // nftDraftList = getNftResponse.getOrElse(() => []); loading.dismiss(); diff --git a/evently/lib/services/third_party_services/database.dart b/evently/lib/services/third_party_services/database.dart new file mode 100644 index 0000000000..cda3365232 --- /dev/null +++ b/evently/lib/services/third_party_services/database.dart @@ -0,0 +1,12 @@ +import 'dart:async'; +import 'package:evently/models/events.dart'; +import 'package:evently/services/third_party_services/event_dao.dart'; +import 'package:floor/floor.dart'; +import 'package:sqflite/sqflite.dart' as sqflite; + +part 'database.g.dart'; + +@Database(version: 6, entities: [Events]) +abstract class AppDatabase extends FloorDatabase { + EventsDao get eventsDao; +} diff --git a/evently/lib/services/third_party_services/database.g.dart b/evently/lib/services/third_party_services/database.g.dart new file mode 100644 index 0000000000..e00269f83d --- /dev/null +++ b/evently/lib/services/third_party_services/database.g.dart @@ -0,0 +1,205 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'database.dart'; + +// ************************************************************************** +// FloorGenerator +// ************************************************************************** + +abstract class $AppDatabaseBuilderContract { + /// Adds migrations to the builder. + $AppDatabaseBuilderContract addMigrations(List migrations); + + /// Adds a database [Callback] to the builder. + $AppDatabaseBuilderContract addCallback(Callback callback); + + /// Creates the database and initializes it. + Future build(); +} + +// ignore: avoid_classes_with_only_static_members +class $FloorAppDatabase { + /// Creates a database builder for a persistent database. + /// Once a database is built, you should keep a reference to it and re-use it. + static $AppDatabaseBuilderContract databaseBuilder(String name) => + _$AppDatabaseBuilder(name); + + /// Creates a database builder for an in memory database. + /// Information stored in an in memory database disappears when the process is killed. + /// Once a database is built, you should keep a reference to it and re-use it. + static $AppDatabaseBuilderContract inMemoryDatabaseBuilder() => + _$AppDatabaseBuilder(null); +} + +class _$AppDatabaseBuilder implements $AppDatabaseBuilderContract { + _$AppDatabaseBuilder(this.name); + + final String? name; + + final List _migrations = []; + + Callback? _callback; + + @override + $AppDatabaseBuilderContract addMigrations(List migrations) { + _migrations.addAll(migrations); + return this; + } + + @override + $AppDatabaseBuilderContract addCallback(Callback callback) { + _callback = callback; + return this; + } + + @override + Future build() async { + final path = name != null + ? await sqfliteDatabaseFactory.getDatabasePath(name!) + : ':memory:'; + final database = _$AppDatabase(); + database.database = await database.open( + path, + _migrations, + _callback, + ); + return database; + } +} + +class _$AppDatabase extends AppDatabase { + _$AppDatabase([StreamController? listener]) { + changeListener = listener ?? StreamController.broadcast(); + } + + EventsDao? _eventsDaoInstance; + + Future open( + String path, + List migrations, [ + Callback? callback, + ]) async { + final databaseOptions = sqflite.OpenDatabaseOptions( + version: 6, + onConfigure: (database) async { + await database.execute('PRAGMA foreign_keys = ON'); + await callback?.onConfigure?.call(database); + }, + onOpen: (database) async { + await callback?.onOpen?.call(database); + }, + onUpgrade: (database, startVersion, endVersion) async { + await MigrationAdapter.runMigrations( + database, startVersion, endVersion, migrations); + + await callback?.onUpgrade?.call(database, startVersion, endVersion); + }, + onCreate: (database, version) async { + await database.execute( + 'CREATE TABLE IF NOT EXISTS `Events` (`id` INTEGER, `eventName` TEXT NOT NULL, `hostName` TEXT NOT NULL, `thumbnail` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `startTime` TEXT NOT NULL, `endTime` TEXT NOT NULL, `location` TEXT NOT NULL, `description` TEXT NOT NULL, `numberOfTickets` TEXT NOT NULL, `price` TEXT NOT NULL, `listOfPerks` TEXT NOT NULL, `isFreeDrops` TEXT NOT NULL, `cookbookID` TEXT NOT NULL, `recipeID` TEXT NOT NULL, PRIMARY KEY (`id`))'); + + await callback?.onCreate?.call(database, version); + }, + ); + return sqfliteDatabaseFactory.openDatabase(path, options: databaseOptions); + } + + @override + EventsDao get eventsDao { + return _eventsDaoInstance ??= _$EventsDao(database, changeListener); + } +} + +class _$EventsDao extends EventsDao { + _$EventsDao( + this.database, + this.changeListener, + ) : _queryAdapter = QueryAdapter(database), + _eventsInsertionAdapter = InsertionAdapter( + database, + 'Events', + (Events item) => { + 'id': item.id, + 'eventName': item.eventName, + 'hostName': item.hostName, + 'thumbnail': item.thumbnail, + 'startDate': item.startDate, + 'endDate': item.endDate, + 'startTime': item.startTime, + 'endTime': item.endTime, + 'location': item.location, + 'description': item.description, + 'numberOfTickets': item.numberOfTickets, + 'price': item.price, + 'listOfPerks': item.listOfPerks, + 'isFreeDrops': item.isFreeDrops, + 'cookbookID': item.cookbookID, + 'recipeID': item.recipeID + }); + + final sqflite.DatabaseExecutor database; + + final StreamController changeListener; + + final QueryAdapter _queryAdapter; + + final InsertionAdapter _eventsInsertionAdapter; + + @override + Future> findAllEvents() async { + return _queryAdapter.queryList( + 'SELECT * FROM events ORDER BY dateTime DESC', + mapper: (Map row) => Events( + eventName: row['eventName'] as String, + hostName: row['hostName'] as String, + thumbnail: row['thumbnail'] as String, + startDate: row['startDate'] as String, + endDate: row['endDate'] as String, + startTime: row['startTime'] as String, + endTime: row['endTime'] as String, + location: row['location'] as String, + description: row['description'] as String, + listOfPerks: row['listOfPerks'] as String, + numberOfTickets: row['numberOfTickets'] as String, + price: row['price'] as String, + isFreeDrops: row['isFreeDrops'] as String, + cookbookID: row['cookbookID'] as String, + recipeID: row['recipeID'] as String, + id: row['id'] as int?)); + } + + @override + Future findEventsById(int id) async { + return _queryAdapter.query('SELECT * FROM events WHERE id = ?1', + mapper: (Map row) => Events( + eventName: row['eventName'] as String, + hostName: row['hostName'] as String, + thumbnail: row['thumbnail'] as String, + startDate: row['startDate'] as String, + endDate: row['endDate'] as String, + startTime: row['startTime'] as String, + endTime: row['endTime'] as String, + location: row['location'] as String, + description: row['description'] as String, + listOfPerks: row['listOfPerks'] as String, + numberOfTickets: row['numberOfTickets'] as String, + price: row['price'] as String, + isFreeDrops: row['isFreeDrops'] as String, + cookbookID: row['cookbookID'] as String, + recipeID: row['recipeID'] as String, + id: row['id'] as int?), + arguments: [id]); + } + + @override + Future delete(int id) async { + await _queryAdapter + .queryNoReturn('DELETE FROM events WHERE id = ?1', arguments: [id]); + } + + @override + Future insertEvents(Events events) { + return _eventsInsertionAdapter.insertAndReturnId( + events, OnConflictStrategy.abort); + } +} diff --git a/evently/lib/services/third_party_services/event_dao.dart b/evently/lib/services/third_party_services/event_dao.dart new file mode 100644 index 0000000000..93e6f60b68 --- /dev/null +++ b/evently/lib/services/third_party_services/event_dao.dart @@ -0,0 +1,17 @@ +import 'package:evently/models/events.dart'; +import 'package:floor/floor.dart'; + +@dao +abstract class EventsDao { + @Query('SELECT * FROM events ORDER BY dateTime DESC') + Future> findAllEvents(); + + @Query('SELECT * FROM events WHERE id = :id') + Future findEventsById(int id); + + @insert + Future insertEvents(Events events); + + @Query('DELETE FROM events WHERE id = :id') + Future delete(int id); +} diff --git a/evently/lib/utils/di/di.dart b/evently/lib/utils/di/di.dart index 62d8189d3a..7e4d6d57fa 100644 --- a/evently/lib/utils/di/di.dart +++ b/evently/lib/utils/di/di.dart @@ -1,6 +1,7 @@ // coverage: false // coverage:ignore-file +import 'package:evently/services/third_party_services/database.dart'; import 'package:evently/utils/di/di.config.dart'; import 'package:get_it/get_it.dart'; import 'package:injectable/injectable.dart'; @@ -13,5 +14,6 @@ final sl = GetIt.I; @InjectableInit() void configureDependencies() { sl.registerSingletonAsync(() => SharedPreferences.getInstance()); + sl.registerSingletonAsync(() => $FloorAppDatabase.databaseBuilder('app_database.db').build()); sl.init(); } diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 7dd01b7323..3e54b6d1c0 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -74,7 +74,7 @@ packages: source: hosted version: "2.4.2" build_runner: - dependency: "direct main" + dependency: "direct dev" description: name: build_runner sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" @@ -345,6 +345,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + floor_generator: + dependency: "direct dev" + description: + name: floor_generator + sha256: "1499b3ab878a807e6fbe6f140dc014124845cd1df3090a113aae5fa7577a1e77" + url: "https://pub.dev" + source: hosted + version: "1.5.0" flutter: dependency: "direct main" description: flutter @@ -581,6 +589,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + lists: + dependency: transitive + description: + name: lists + sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27" + url: "https://pub.dev" + source: hosted + version: "1.0.1" logging: dependency: transitive description: @@ -1041,6 +1057,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + strings: + dependency: transitive + description: + name: strings + sha256: b33f40c4dd3e597bf6d9e7f4f4dc282dad0f19b07d9f320cb5c2183859cbccf5 + url: "https://pub.dev" + source: hosted + version: "3.1.1" synchronized: dependency: transitive description: @@ -1105,6 +1129,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0" + unicode: + dependency: transitive + description: + name: unicode + sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1" + url: "https://pub.dev" + source: hosted + version: "0.3.1" url_launcher: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index a5f78805eb..763477446b 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -43,7 +43,6 @@ dependencies: flutter_svg_provider: ^1.0.3 get_it: ^7.7.0 injectable_generator: ^2.6.1 - build_runner: ^2.4.9 injectable: ^2.4.1 provider: ^6.1.2 steps_indicator: ^1.3.0 @@ -63,6 +62,8 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + floor_generator: ^1.4.2 + build_runner: ^2.4.9 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is From d52d97618099d07c204d2c777687dd35e80d8179 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 16 May 2024 16:58:26 +0500 Subject: [PATCH 106/161] feat: draft list --- evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/generated/locale_keys.g.dart | 1 + .../event_hub/event_hub_view_model.dart | 13 ++--- .../datasources/local_datasource.dart | 19 +++++-- evently/lib/utils/di/di.config.dart | 53 ++++++++++--------- 8 files changed, 59 insertions(+), 39 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 55697d6cdf..f97e482361 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -64,5 +64,6 @@ "something_wrong_while_uploading": "Beim Hochladen ist etwas schief gelaufen. Bitte versuche es erneut.", "cookbook_not_found": "Cookbook nicht gefunden", "loading": "Wird geladen ...", - "something_wrong": "Etwas ist schief gelaufen. Bitte versuchen Sie, die Anwendung neu zu starten." + "something_wrong": "Etwas ist schief gelaufen. Bitte versuchen Sie, die Anwendung neu zu starten.", + "get_error": "Entwurf konnte nicht abgerufen werden" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 1452511178..973207bd10 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -64,5 +64,6 @@ "something_wrong_while_uploading": "Something went wrong while uploading. Please try again.", "cookbook_not_found": "Cookbook not found", "loading": "loading ...", - "something_wrong": "Something went wrong. Please try restarting the application." + "something_wrong": "Something went wrong. Please try restarting the application.", + "get_error": "Failed to get draft" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 09c9039b18..0a9e319a8c 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -64,5 +64,6 @@ "something_wrong_while_uploading": "Algo salió mal durante la carga. Inténtalo de nuevo.", "cookbook_not_found": "Cookbook extraviado", "loading": "cargando ...", - "something_wrong": "Algo salió mal. Intenta reiniciar la aplicación." + "something_wrong": "Algo salió mal. Intenta reiniciar la aplicación.", + "get_error": "No se pudo obtener el Plan Preliminar" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index dc5f3b742d..24d581e1bc 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -64,5 +64,6 @@ "something_wrong_while_uploading": "Что-то пошло не так во время загрузки. Пожалуйста, попробуйте еще раз.", "cookbook_not_found": "Cookbook не найден", "loading": "cargando ...", - "something_wrong": "Что-то пошло не так. Попробуйте перезапустить приложение." + "something_wrong": "Что-то пошло не так. Попробуйте перезапустить приложение.", + "get_error": "Не удалось получить черновик" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index e405267b99..51700b00fe 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -65,5 +65,6 @@ abstract class LocaleKeys { static const cookbook_not_found = 'cookbook_not_found'; static const loading = 'loading'; static const something_wrong = 'something_wrong'; + static const get_error = 'get_error'; } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 627b48aa34..b35b17d1ee 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -20,12 +20,12 @@ class EventHubViewModel extends ChangeNotifier { List get eventPublishedList => _eventPublishedList; - List _eventForSaleList = []; + List _eventForDraftList = []; - List get eventForSaleList => _eventForSaleList; + List get eventForSaleList => _eventForDraftList; - set setEventForSaleList(List nftForSale) { - _eventForSaleList = nftForSale; + set setEventForDraftList(List nftForSale) { + _eventForDraftList = nftForSale; notifyListeners(); } @@ -36,7 +36,7 @@ class EventHubViewModel extends ChangeNotifier { void updatePublishedEventList({required Events events}) { _eventPublishedList.add(events); - _eventForSaleList.add(events); + _eventForDraftList.add(events); notifyListeners(); } @@ -105,7 +105,8 @@ class EventHubViewModel extends ChangeNotifier { return; } - // nftDraftList = getNftResponse.getOrElse(() => []); + List draftEvent = getEventResponse.getOrElse(() => []); + setEventForDraftList = draftEvent; loading.dismiss(); diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 88dc782fae..0ba1334087 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -1,6 +1,10 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/models/events.dart'; +import 'package:evently/services/third_party_services/database.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/date_utils.dart'; +import 'package:evently/utils/failure/failure.dart'; import 'package:injectable/injectable.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -42,9 +46,13 @@ abstract class LocalDataSource { @LazySingleton(as: LocalDataSource) class LocalDataSourceImpl extends LocalDataSource { - LocalDataSourceImpl({required this.sharedPreferences}); + LocalDataSourceImpl({ + required this.sharedPreferences, + required this.database, + }); final SharedPreferences sharedPreferences; + final AppDatabase database; /// auto generates cookbookID string and saves into local storage /// returns cookbookId @@ -93,8 +101,11 @@ class LocalDataSourceImpl extends LocalDataSource { } @override - Future> getEvents() { - // TODO: implement getEvents - throw UnimplementedError(); + Future> getEvents() async { + try { + return await database.eventsDao.findAllEvents(); + } catch (e) { + throw CacheFailure(LocaleKeys.get_error.tr()); + } } } diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index d702697c9d..c987c79dd8 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -9,20 +9,21 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:dio/dio.dart' as _i6; -import 'package:evently/evently_provider.dart' as _i13; -import 'package:evently/repository/repository.dart' as _i12; -import 'package:evently/screens/event_hub/event_hub_view_model.dart' as _i14; -import 'package:evently/services/datasources/local_datasource.dart' as _i7; -import 'package:evently/services/datasources/remote_datasource.dart' as _i11; -import 'package:evently/services/third_party_services/quick_node.dart' as _i10; -import 'package:evently/utils/di/register_modules.dart' as _i15; -import 'package:evently/utils/file_utils_helper.dart' as _i9; +import 'package:evently/evently_provider.dart' as _i14; +import 'package:evently/repository/repository.dart' as _i13; +import 'package:evently/screens/event_hub/event_hub_view_model.dart' as _i15; +import 'package:evently/services/datasources/local_datasource.dart' as _i9; +import 'package:evently/services/datasources/remote_datasource.dart' as _i12; +import 'package:evently/services/third_party_services/database.dart' as _i11; +import 'package:evently/services/third_party_services/quick_node.dart' as _i8; +import 'package:evently/utils/di/register_modules.dart' as _i16; +import 'package:evently/utils/file_utils_helper.dart' as _i7; import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; import 'package:file_picker/file_picker.dart' as _i5; import 'package:get_it/get_it.dart' as _i1; import 'package:image_cropper/image_cropper.dart' as _i4; import 'package:injectable/injectable.dart' as _i2; -import 'package:shared_preferences/shared_preferences.dart' as _i8; +import 'package:shared_preferences/shared_preferences.dart' as _i10; extension GetItInjectableX on _i1.GetIt { // initializes the registration of main-scope dependencies inside of GetIt @@ -41,27 +42,29 @@ extension GetItInjectableX on _i1.GetIt { gh.lazySingleton<_i4.ImageCropper>(() => registerModule.imageCropper); gh.lazySingleton<_i5.FilePicker>(() => registerModule.filePicker); gh.lazySingleton<_i6.Dio>(() => registerModule.dio); - gh.lazySingleton<_i7.LocalDataSource>(() => _i7.LocalDataSourceImpl( - sharedPreferences: gh<_i8.SharedPreferences>())); - gh.lazySingleton<_i9.FileUtilsHelper>(() => _i9.FileUtilsHelperImpl( + gh.lazySingleton<_i7.FileUtilsHelper>(() => _i7.FileUtilsHelperImpl( imageCropper: gh<_i4.ImageCropper>(), filePicker: gh<_i5.FilePicker>(), )); - gh.lazySingleton<_i10.QuickNode>( - () => _i10.QuickNodeImpl(httpClient: gh<_i6.Dio>())); - gh.lazySingleton<_i11.RemoteDataSource>( - () => _i11.RemoteDataSourceImpl(quickNode: gh<_i10.QuickNode>())); - gh.lazySingleton<_i12.Repository>(() => _i12.RepositoryImp( - fileUtilsHelper: gh<_i9.FileUtilsHelper>(), - localDataSource: gh<_i7.LocalDataSource>(), - remoteDataSource: gh<_i11.RemoteDataSource>(), + gh.lazySingleton<_i8.QuickNode>( + () => _i8.QuickNodeImpl(httpClient: gh<_i6.Dio>())); + gh.lazySingleton<_i9.LocalDataSource>(() => _i9.LocalDataSourceImpl( + sharedPreferences: gh<_i10.SharedPreferences>(), + database: gh<_i11.AppDatabase>(), )); - gh.lazySingleton<_i13.EventlyProvider>( - () => _i13.EventlyProvider(repository: gh<_i12.Repository>())); - gh.lazySingleton<_i14.EventHubViewModel>( - () => _i14.EventHubViewModel(gh<_i12.Repository>())); + gh.lazySingleton<_i12.RemoteDataSource>( + () => _i12.RemoteDataSourceImpl(quickNode: gh<_i8.QuickNode>())); + gh.lazySingleton<_i13.Repository>(() => _i13.RepositoryImp( + fileUtilsHelper: gh<_i7.FileUtilsHelper>(), + localDataSource: gh<_i9.LocalDataSource>(), + remoteDataSource: gh<_i12.RemoteDataSource>(), + )); + gh.lazySingleton<_i14.EventlyProvider>( + () => _i14.EventlyProvider(repository: gh<_i13.Repository>())); + gh.lazySingleton<_i15.EventHubViewModel>( + () => _i15.EventHubViewModel(gh<_i13.Repository>())); return this; } } -class _$RegisterModule extends _i15.RegisterModule {} +class _$RegisterModule extends _i16.RegisterModule {} From f6e2dfea51d4a7ee4cdef4f18ddad24708181aae Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Fri, 17 May 2024 17:15:49 +0500 Subject: [PATCH 107/161] feat: database operation needed to work in app for draft and publish list --- evently/i18n/de.json | 5 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 4 +- evently/i18n/ru-RU.json | 4 +- evently/lib/evently_provider.dart | 27 +++++++-- evently/lib/generated/locale_keys.g.dart | 2 + evently/lib/models/events.dart | 9 ++- evently/lib/repository/repository.dart | 7 +-- evently/lib/screens/create_event.dart | 5 ++ .../event_hub/event_hub_view_model.dart | 2 +- .../datasources/local_datasource.dart | 34 ++++++++++- .../third_party_services/database.g.dart | 59 +++++++++++++++++-- .../third_party_services/event_dao.dart | 27 ++++++++- evently/lib/utils/enums.dart | 1 + 14 files changed, 164 insertions(+), 25 deletions(-) create mode 100644 evently/lib/utils/enums.dart diff --git a/evently/i18n/de.json b/evently/i18n/de.json index f97e482361..42dceb774f 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -65,5 +65,8 @@ "cookbook_not_found": "Cookbook nicht gefunden", "loading": "Wird geladen ...", "something_wrong": "Etwas ist schief gelaufen. Bitte versuchen Sie, die Anwendung neu zu starten.", - "get_error": "Entwurf konnte nicht abgerufen werden" + "get_error": "Entwurf konnte nicht abgerufen werden", + "delete_error": "Entwurf konnte nicht gelöscht werden", + "save_error": "Entwurf konnte nicht gespeichert werden", + "save_error": "Failed to save draft" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 973207bd10..40c0782be3 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -65,5 +65,6 @@ "cookbook_not_found": "Cookbook not found", "loading": "loading ...", "something_wrong": "Something went wrong. Please try restarting the application.", - "get_error": "Failed to get draft" + "get_error": "Failed to get draft", + "delete_error": "Failed to delete draft" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 0a9e319a8c..d8a8ead539 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -65,5 +65,7 @@ "cookbook_not_found": "Cookbook extraviado", "loading": "cargando ...", "something_wrong": "Algo salió mal. Intenta reiniciar la aplicación.", - "get_error": "No se pudo obtener el Plan Preliminar" + "get_error": "No se pudo obtener el Plan Preliminar", + "delete_error": "No se pudo eliminar el Plan Preliminar", + "save_error": "No se pudo guardar el Plan Preliminar" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 24d581e1bc..0128535959 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -65,5 +65,7 @@ "cookbook_not_found": "Cookbook не найден", "loading": "cargando ...", "something_wrong": "Что-то пошло не так. Попробуйте перезапустить приложение.", - "get_error": "Не удалось получить черновик" + "get_error": "Не удалось получить черновик", + "delete_error": "Не удалось удалить черновик", + "save_error": "Не удалось сохранить черновик" } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 4e2275e7e8..f9b30d491f 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -19,7 +19,6 @@ import 'package:flutter/widgets.dart'; import 'package:get_it/get_it.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/low_level.dart'; - import 'services/third_party_services/quick_node.dart'; @LazySingleton() @@ -218,7 +217,6 @@ class EventlyProvider extends ChangeNotifier { setThumbnail = "$ipfsDomain/${fileUploadResponse.value?.cid}"; } - String currentUserName = ""; bool stripeAccountExists = false; String? _cookbookId; @@ -231,7 +229,6 @@ class EventlyProvider extends ChangeNotifier { final sdkResponse = await PylonsWallet.instance.getProfile(); if (sdkResponse.success) { - currentUserName = sdkResponse.data!.username; stripeAccountExists = sdkResponse.data!.stripeExists; supportedDenomList = Denom.availableDenoms.where((Denom e) => sdkResponse.data!.supportedCoins.contains(e.symbol)).toList(); @@ -278,7 +275,7 @@ class EventlyProvider extends ChangeNotifier { price: price.toString(), listOfPerks: perks.join(','), cookbookID: _cookbookId!, - recipeID: _recipeId, + recipeID: _recipeId, step: '', ); final recipe = event.createRecipe( @@ -331,4 +328,26 @@ class EventlyProvider extends ChangeNotifier { navigatorKey.showMsg(message: response.error); return false; } + + void initStore() { + _eventName = ""; + _hostName = ""; + _thumbnail = ""; + _isOverviewEnable = false; + + _startDate = ""; + _endDate = ""; + _startTime = ""; + _endTime = ""; + _location = ""; + _description = ""; + _selectedDenom = Denom.availableDenoms.first; + + _isFreeDrop = FreeDrop.unselected; + _numberOfTickets = 0; + _price = 0; + _cookbookId = ''; + + notifyListeners(); + } } diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 51700b00fe..1abafc579b 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -66,5 +66,7 @@ abstract class LocaleKeys { static const loading = 'loading'; static const something_wrong = 'something_wrong'; static const get_error = 'get_error'; + static const delete_error = 'delete_error'; + static const save_error = 'save_error'; } diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index de4ffa4b5b..e4bf31cd73 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -9,7 +9,7 @@ enum FreeDrop { yes, no, unselected } class Events extends Equatable { @primaryKey final int? id; - + final String recipeID; final String eventName; final String hostName; final String thumbnail; @@ -24,9 +24,11 @@ class Events extends Equatable { final String listOfPerks; final String isFreeDrops; final String cookbookID; - final String recipeID; + final String step; const Events({ + this.id, + ///* overview data this.eventName = '', this.hostName = '', @@ -51,7 +53,7 @@ class Events extends Equatable { ///* other this.cookbookID = '', this.recipeID = '', - this.id, + required this.step, }); factory Events.fromRecipe(Recipe recipe) { @@ -69,6 +71,7 @@ class Events extends Equatable { numberOfTickets: map[kNumberOfTickets]!, price: map[kPrice]!, listOfPerks: map[kPerks]!, + step: '', ); } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index e265ad0cf1..1105c8d17d 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -57,7 +57,7 @@ abstract class Repository { /// This method will get the drafts List from the local database /// Output: [List] returns that contains a number of [NFT] - Future>> getEvents(); + Future>> getAllEvents(); } @LazySingleton(as: Repository) @@ -136,10 +136,9 @@ class RepositoryImp implements Repository { } @override - Future>> getEvents() async { + Future>> getAllEvents() async { try { - final response = await localDataSource.getEvents(); - + final response = await localDataSource.getAllEvents(); return Right(response); } on Exception catch (_) { return Left(CacheFailure(LocaleKeys.something_wrong.tr())); diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 6ec350472d..f0b9a1d812 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,3 +1,4 @@ +import 'package:evently/evently_provider.dart'; import 'package:evently/screens/detail_screen.dart'; import 'package:evently/screens/host_view_ticket_preview.dart'; import 'package:evently/screens/overview_screen.dart'; @@ -23,6 +24,10 @@ class _CreateEventState extends State { void initState() { super.initState(); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + context.read().initStore(); + }); + createEventViewModel.init(); } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index b35b17d1ee..2979e8a422 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -97,7 +97,7 @@ class EventHubViewModel extends ChangeNotifier { Future getDraftsList() async { final loading = LoadingProgress()..showLoadingWithProgress(message: LocaleKeys.loading.tr()); - final getEventResponse = await repository.getEvents(); + final getEventResponse = await repository.getAllEvents(); if (getEventResponse.isLeft()) { loading.dismiss(); diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 0ba1334087..0cbbf985f1 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -41,7 +41,17 @@ abstract class LocalDataSource { /// This method will get the drafts List from the local database /// Output: [List][NFT] returns the List of drafts - Future> getEvents(); + Future> getAllEvents(); + + /// This method will delete draft from the local database + /// Input: [recipeID] the id of the draft which the user wants to delete + /// Output: [bool] returns whether the operation is successful or not + Future deleteEvents(int id); + + /// This method will save the draft of the NFT + /// Input: [Events] the draft that will will be saved in database + /// Output: [int] returns id of the inserted document + Future saveEvents(Events events); } @LazySingleton(as: LocalDataSource) @@ -101,11 +111,31 @@ class LocalDataSourceImpl extends LocalDataSource { } @override - Future> getEvents() async { + Future> getAllEvents() async { try { return await database.eventsDao.findAllEvents(); } catch (e) { throw CacheFailure(LocaleKeys.get_error.tr()); } } + + @override + Future deleteEvents(int id) async { + try { + await database.eventsDao.delete(id); + return true; + } catch (e) { + throw CacheFailure(LocaleKeys.delete_error.tr()); + } + } + + @override + Future saveEvents(Events events) async { + try { + final result = await database.eventsDao.insertEvents(events); + return result; + } catch (e) { + throw LocaleKeys.save_error.tr(); + } + } } diff --git a/evently/lib/services/third_party_services/database.g.dart b/evently/lib/services/third_party_services/database.g.dart index e00269f83d..8cecdbea4f 100644 --- a/evently/lib/services/third_party_services/database.g.dart +++ b/evently/lib/services/third_party_services/database.g.dart @@ -96,7 +96,7 @@ class _$AppDatabase extends AppDatabase { }, onCreate: (database, version) async { await database.execute( - 'CREATE TABLE IF NOT EXISTS `Events` (`id` INTEGER, `eventName` TEXT NOT NULL, `hostName` TEXT NOT NULL, `thumbnail` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `startTime` TEXT NOT NULL, `endTime` TEXT NOT NULL, `location` TEXT NOT NULL, `description` TEXT NOT NULL, `numberOfTickets` TEXT NOT NULL, `price` TEXT NOT NULL, `listOfPerks` TEXT NOT NULL, `isFreeDrops` TEXT NOT NULL, `cookbookID` TEXT NOT NULL, `recipeID` TEXT NOT NULL, PRIMARY KEY (`id`))'); + 'CREATE TABLE IF NOT EXISTS `Events` (`id` INTEGER, `recipeID` TEXT NOT NULL, `eventName` TEXT NOT NULL, `hostName` TEXT NOT NULL, `thumbnail` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `startTime` TEXT NOT NULL, `endTime` TEXT NOT NULL, `location` TEXT NOT NULL, `description` TEXT NOT NULL, `numberOfTickets` TEXT NOT NULL, `price` TEXT NOT NULL, `listOfPerks` TEXT NOT NULL, `isFreeDrops` TEXT NOT NULL, `cookbookID` TEXT NOT NULL, `step` TEXT NOT NULL, PRIMARY KEY (`id`))'); await callback?.onCreate?.call(database, version); }, @@ -120,6 +120,7 @@ class _$EventsDao extends EventsDao { 'Events', (Events item) => { 'id': item.id, + 'recipeID': item.recipeID, 'eventName': item.eventName, 'hostName': item.hostName, 'thumbnail': item.thumbnail, @@ -134,7 +135,7 @@ class _$EventsDao extends EventsDao { 'listOfPerks': item.listOfPerks, 'isFreeDrops': item.isFreeDrops, 'cookbookID': item.cookbookID, - 'recipeID': item.recipeID + 'step': item.step }); final sqflite.DatabaseExecutor database; @@ -147,9 +148,9 @@ class _$EventsDao extends EventsDao { @override Future> findAllEvents() async { - return _queryAdapter.queryList( - 'SELECT * FROM events ORDER BY dateTime DESC', + return _queryAdapter.queryList('SELECT * FROM events', mapper: (Map row) => Events( + id: row['id'] as int?, eventName: row['eventName'] as String, hostName: row['hostName'] as String, thumbnail: row['thumbnail'] as String, @@ -165,13 +166,14 @@ class _$EventsDao extends EventsDao { isFreeDrops: row['isFreeDrops'] as String, cookbookID: row['cookbookID'] as String, recipeID: row['recipeID'] as String, - id: row['id'] as int?)); + step: row['step'] as String)); } @override Future findEventsById(int id) async { return _queryAdapter.query('SELECT * FROM events WHERE id = ?1', mapper: (Map row) => Events( + id: row['id'] as int?, eventName: row['eventName'] as String, hostName: row['hostName'] as String, thumbnail: row['thumbnail'] as String, @@ -187,7 +189,7 @@ class _$EventsDao extends EventsDao { isFreeDrops: row['isFreeDrops'] as String, cookbookID: row['cookbookID'] as String, recipeID: row['recipeID'] as String, - id: row['id'] as int?), + step: row['step'] as String), arguments: [id]); } @@ -197,6 +199,51 @@ class _$EventsDao extends EventsDao { .queryNoReturn('DELETE FROM events WHERE id = ?1', arguments: [id]); } + @override + Future updateNFTFromDetail( + String startDate, + String endDate, + String startTime, + String endTime, + String location, + String description, + int id, + ) async { + await _queryAdapter.queryNoReturn( + 'UPDATE events SET startDate = ?1, endDate= ?2, startTime = ?3, endTime = ?4,location = ?5, description = ?6 WHERE id = ?7', + arguments: [ + startDate, + endDate, + startTime, + endTime, + location, + description, + id + ]); + } + + @override + Future updateNFTFromPerks( + String listOfPerks, + int id, + ) async { + await _queryAdapter.queryNoReturn( + 'UPDATE events SET listOfPerks = ?1 WHERE id = ?2', + arguments: [listOfPerks, id]); + } + + @override + Future updateNFTFromPrice( + int id, + String numberOfTickets, + String price, + String isFreeDrops, + ) async { + await _queryAdapter.queryNoReturn( + 'UPDATE events SET numberOfTickets = ?2, price = ?3, isFreeDrops = ?4 WHERE id = ?1', + arguments: [id, numberOfTickets, price, isFreeDrops]); + } + @override Future insertEvents(Events events) { return _eventsInsertionAdapter.insertAndReturnId( diff --git a/evently/lib/services/third_party_services/event_dao.dart b/evently/lib/services/third_party_services/event_dao.dart index 93e6f60b68..95bbcfe26e 100644 --- a/evently/lib/services/third_party_services/event_dao.dart +++ b/evently/lib/services/third_party_services/event_dao.dart @@ -3,7 +3,7 @@ import 'package:floor/floor.dart'; @dao abstract class EventsDao { - @Query('SELECT * FROM events ORDER BY dateTime DESC') + @Query('SELECT * FROM events') Future> findAllEvents(); @Query('SELECT * FROM events WHERE id = :id') @@ -14,4 +14,29 @@ abstract class EventsDao { @Query('DELETE FROM events WHERE id = :id') Future delete(int id); + + @Query('UPDATE events SET startDate = :startDate, endDate= :endDate, startTime = :startTime, endTime = :endTime,location = :location, description = :description WHERE id = :id') + Future updateNFTFromDetail( + String startDate, + String endDate, + String startTime, + String endTime, + String location, + String description, + int id, + ); + + @Query('UPDATE events SET listOfPerks = :listOfPerks WHERE id = :id') + Future updateNFTFromPerks( + String listOfPerks, + int id, + ); + + @Query('UPDATE events SET numberOfTickets = :numberOfTickets, price = :price, isFreeDrops = :isFreeDrops WHERE id = :id') + Future updateNFTFromPrice( + int id, + String numberOfTickets, + String price, + String isFreeDrops, + ); } diff --git a/evently/lib/utils/enums.dart b/evently/lib/utils/enums.dart new file mode 100644 index 0000000000..15f439515d --- /dev/null +++ b/evently/lib/utils/enums.dart @@ -0,0 +1 @@ +enum UploadStep { overView, detail, perks, price } From 6b63d45259e41134db5d098a6998fc6180d96c38 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 09:43:05 +0500 Subject: [PATCH 108/161] feat: event hub --- .../screens/event_hub/event_hub_screen.dart | 123 ++++++++++-------- .../event_hub/event_hub_view_model.dart | 6 + 2 files changed, 75 insertions(+), 54 deletions(-) diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 0f4ca9ed5e..5f9cad9b8c 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -1,12 +1,15 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/main.dart'; +import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/route_util.dart'; import 'package:evently/widgets/clipped_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; class EventHubScreen extends StatefulWidget { const EventHubScreen({super.key}); @@ -42,68 +45,80 @@ class _EventHubScreenState extends State { fontFamily: kUniversalFontFamily, ); + EventHubViewModel get eventHubViewModel => sl(); + + @override + void initState() { + eventHubViewModel.getPublishAndDraftData(); + + super.initState(); + } + @override Widget build(BuildContext context) { - return ColoredBox( - color: EventlyAppTheme.kBlack, - child: SafeArea( - child: Scaffold( - backgroundColor: EventlyAppTheme.kBlack, - body: Padding( - padding: const EdgeInsets.only(top: 20), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(right: 20), - child: Align( - alignment: Alignment.topRight, - child: InkWell( - onTap: () => Navigator.of(context).pushNamed(RouteUtil.kCreateEvent), - child: const DecoratedBox( - decoration: BoxDecoration(color: EventlyAppTheme.kTextLightBlue), - child: Icon(Icons.add, size: 21, color: EventlyAppTheme.kWhite), + return ChangeNotifierProvider.value( + value: eventHubViewModel, + child: ColoredBox( + color: EventlyAppTheme.kBlack, + child: SafeArea( + child: Scaffold( + backgroundColor: EventlyAppTheme.kBlack, + body: Padding( + padding: const EdgeInsets.only(top: 20), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(right: 20), + child: Align( + alignment: Alignment.topRight, + child: InkWell( + onTap: () => Navigator.of(context).pushNamed(RouteUtil.kCreateEvent), + child: const DecoratedBox( + decoration: BoxDecoration(color: EventlyAppTheme.kTextLightBlue), + child: Icon(Icons.add, size: 21, color: EventlyAppTheme.kWhite), + ), ), ), ), - ), - Text( - LocaleKeys.eventhub.tr(), - style: headingStyle, - textAlign: TextAlign.center, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: Text( - LocaleKeys.welcome_event.tr(), - style: titleStyle, + Text( + LocaleKeys.eventhub.tr(), + style: headingStyle, textAlign: TextAlign.center, ), - ), - const SizedBox(height: 40), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 50), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - getButton(title: LocaleKeys.draft), - getButton(title: LocaleKeys.for_sale), - getButton(title: LocaleKeys.history), - ], + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: Text( + LocaleKeys.welcome_event.tr(), + style: titleStyle, + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 40), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 50), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + getButton(title: LocaleKeys.draft), + getButton(title: LocaleKeys.for_sale), + getButton(title: LocaleKeys.history), + ], + ), + ), + const SizedBox(height: 40), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + LocaleKeys.no_nft_created.tr(), + style: subTitleStyle, + )), ), - ), - const SizedBox(height: 40), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Align( - alignment: Alignment.centerLeft, - child: Text( - LocaleKeys.no_nft_created.tr(), - style: subTitleStyle, - )), - ), - const Spacer(), - Padding(padding: EdgeInsets.symmetric(horizontal: 20.w), child: getCreateEventWidget()), - ], + const Spacer(), + Padding(padding: EdgeInsets.symmetric(horizontal: 20.w), child: getCreateEventWidget()), + ], + ), ), ), ), diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 2979e8a422..3d20c33684 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -112,4 +112,10 @@ class EventHubViewModel extends ChangeNotifier { notifyListeners(); } + + Future getPublishAndDraftData() async { + await getRecipesList(); + await getDraftsList(); + notifyListeners(); + } } From 8d952dc9c05a4db7bb9c1f2d98e7fcb63000a767 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 09:53:08 +0500 Subject: [PATCH 109/161] feat: save event from overview --- evently/lib/models/save_event.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 evently/lib/models/save_event.dart diff --git a/evently/lib/models/save_event.dart b/evently/lib/models/save_event.dart new file mode 100644 index 0000000000..829d6409b4 --- /dev/null +++ b/evently/lib/models/save_event.dart @@ -0,0 +1,16 @@ +class SaveEvent { + final int? id; + final String? nftName; + final String? hostName; + final String? thumbnail; + + final String? step; + + SaveEvent( + this.id, + this.nftName, + this.hostName, + this.thumbnail, + this.step, + ); +} From 5677cccaddd0e0e9e725fa2fab9c237627d909a9 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 10:08:11 +0500 Subject: [PATCH 110/161] feat: overviewScreen save draft --- evently/lib/evently_provider.dart | 12 +++++++++++- evently/lib/screens/overview_screen.dart | 10 +++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index f9b30d491f..26bcf3f70f 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -10,6 +10,7 @@ import 'package:evently/models/storage_response_model.dart'; import 'package:evently/repository/repository.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/enums.dart'; import 'package:evently/utils/extension_util.dart'; import 'package:evently/utils/failure/failure.dart'; import 'package:evently/widgets/loading_with_progress.dart'; @@ -211,6 +212,7 @@ class EventlyProvider extends ChangeNotifier { LocaleKeys.something_wrong_while_uploading.tr().show(); return; } + final fileUploadResponse = response.getOrElse(() => StorageResponseModel.initial()); loading.dismiss(); @@ -275,7 +277,8 @@ class EventlyProvider extends ChangeNotifier { price: price.toString(), listOfPerks: perks.join(','), cookbookID: _cookbookId!, - recipeID: _recipeId, step: '', + recipeID: _recipeId, + step: '', ); final recipe = event.createRecipe( @@ -350,4 +353,11 @@ class EventlyProvider extends ChangeNotifier { notifyListeners(); } + + saveAsDraft({ + required VoidCallback onCompleted, + required UploadStep uploadStep, + }) { + onCompleted(); + } } diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index d8df77987a..76fd3280f7 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -8,7 +8,9 @@ import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; import 'package:evently/screens/custom_widgets/steps_indicator.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/enums.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/evently_text_field.dart'; @@ -145,7 +147,13 @@ class _OverViewScreenState extends State { onPressContinue: () { createEventViewModel.nextPage(); }, - onPressSaveDraft: () {}, + onPressSaveDraft: () { + final navigator = Navigator.of(context); + provider.saveAsDraft( + onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), + uploadStep: UploadStep.overView, + ); + }, isContinueEnable: provider.isOverviewEnable, ) ], From 12c30613423850d54caa5c635b7b8ca6f039ef6b Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 10:39:29 +0500 Subject: [PATCH 111/161] feat: save from overview and show the list of draft and then open from price screen --- evently/lib/evently_provider.dart | 4 +++- evently/lib/repository/repository.dart | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 26bcf3f70f..f2e50087a2 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -357,7 +357,9 @@ class EventlyProvider extends ChangeNotifier { saveAsDraft({ required VoidCallback onCompleted, required UploadStep uploadStep, - }) { + }) async { + final id = await repository.saveEvents(Events(step: uploadStep.toString(), eventName: eventName, hostName: hostName, thumbnail: thumbnail!)); + onCompleted(); } } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 1105c8d17d..4dfe2861e9 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -58,6 +58,10 @@ abstract class Repository { /// This method will get the drafts List from the local database /// Output: [List] returns that contains a number of [NFT] Future>> getAllEvents(); + + /// This method will save event + /// Output: [int] returns that id of [Event] + Future> saveEvents(Events events); } @LazySingleton(as: Repository) @@ -144,4 +148,14 @@ class RepositoryImp implements Repository { return Left(CacheFailure(LocaleKeys.something_wrong.tr())); } } + + @override + Future> saveEvents(Events events) async { + try { + int id = await localDataSource.saveEvents(events); + return Right(id); + } on Exception catch (_) { + return Left(CacheFailure(LocaleKeys.save_error.tr())); + } + } } From 9efd66d3f53877b8aceed23e9743a34c5b163a67 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 12:51:06 +0500 Subject: [PATCH 112/161] feat: delete add and ui view for event hub --- evently/assets/images/ipfs_logo.png | Bin 0 -> 1331 bytes evently/assets/images/svg/delete.svg | 4 + evently/assets/images/svg/grid.svg | 11 + evently/assets/images/svg/list.svg | 5 + evently/assets/images/svg/more_options.svg | 5 + evently/assets/images/svg/publish.svg | 3 + evently/i18n/de.json | 4 +- evently/i18n/en-US.json | 4 +- evently/i18n/es.json | 4 +- evently/i18n/ru-RU.json | 4 +- evently/lib/evently_provider.dart | 1 - evently/lib/generated/locale_keys.g.dart | 1 + evently/lib/models/events.dart | 3 + evently/lib/repository/repository.dart | 16 + .../screens/event_hub/event_hub_screen.dart | 502 +++++++++++++++--- .../event_hub/event_hub_view_model.dart | 32 +- .../widgets/delete_confirmation_dialog.dart | 113 ++++ .../widgets/drafts_more_bottomsheet.dart | 153 ++++++ .../event_hub/widgets/nfts_grid_view.dart | 212 ++++++++ .../datasources/local_datasource.dart | 15 + evently/lib/utils/constants.dart | 9 +- evently/lib/utils/evently_app_theme.dart | 1 + .../clippers/bottom_sheet_clipper.dart | 49 ++ evently/pubspec.lock | 24 + evently/pubspec.yaml | 2 + 25 files changed, 1100 insertions(+), 77 deletions(-) create mode 100644 evently/assets/images/ipfs_logo.png create mode 100644 evently/assets/images/svg/delete.svg create mode 100644 evently/assets/images/svg/grid.svg create mode 100644 evently/assets/images/svg/list.svg create mode 100644 evently/assets/images/svg/more_options.svg create mode 100644 evently/assets/images/svg/publish.svg create mode 100644 evently/lib/screens/event_hub/widgets/delete_confirmation_dialog.dart create mode 100644 evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart create mode 100644 evently/lib/screens/event_hub/widgets/nfts_grid_view.dart create mode 100644 evently/lib/widgets/clippers/bottom_sheet_clipper.dart diff --git a/evently/assets/images/ipfs_logo.png b/evently/assets/images/ipfs_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f607c46a471e7f8d9209097d7675c7c7c6016e GIT binary patch literal 1331 zcmV-313RkT3jJCCQAVYAOEM8`@RH{_!eS)ZdQ1bb}Yc!C9!3!Ztezl|F zqo2~XlNM?Djvcos3O*JMS?hIqdEnLR=p2#4H!%Uf!}+NK>Fth&hQUgkzut>pK^Ju(uK0)clWIaq(Tv2iNdT0YqX>9$eN^DVmh^Vb^+3e>Z` zO9Yi5Kx;B_ydYE}C{^W`-)A2^)p{n;+Bi?kcJ6$Dmy~_l+<9xrJ8&u$GDS$1;h&m< zZ)&QSleov*8xLMgNP1UIO@Sz5k6N$Wt}iG+D$mjZ)7wjW2d>jwh%698=;1h+#BAf7 z5Hc`3+MFB&oz5*pwU&k!&_{`qlFKIc^jsN(f6`17A(G(Pui%zIz{_B+P%^RVs7Cm;|J zbAsXZZ<=w|HWIV;hH>LmJVq(Zo9gNzQOIjHY{1KFR-^W-rtth*O-Il@a0!yf6W-7l zwY%~2`SUn<{1?b_2zM40;@owF3~3c1H3;YzF2sq8{o(&bq9**D92>*%<;%EAgdFLX zWzZv^vVWddhY(4Wh%lw65!^~4&m~Q(mM@2m60K`!C_Mhkx^)=2-ww6GfFF8#aHzEn z)vF)E;TJcflO*__#*X&%W=>jYG~yl7iD7VJ5_`UGMu0qH7@u~)O&)P{;`oCSn$P@& z69WS%U$h7t%gXS^qmRHr$#`mTC^J%~=?kA7iWw`WY-VWb>PENS5i>V7?L^DpUE$An z)mu?nQj$4oA_z0(B51Yr{U7O5_bkP@(}h8jpxB%nF0FH;qw()!4pBSNpF)djmiNHBLjMfkKvuaVI zB_mTuaVH4H%^=dbBJfbBl4uhV%&yZ*0crrsti^Kz%5J~i&VJeBNM)XTm{v5pC=4Mw z2j>Fp3gaXh6b|)de+k)>7Rx415D$nsIg52Ob(xZsLg}S)@!K8!k|bAmHdud5WIO4_%+}V{WjpzYHSg}F z6Lh{-K&8?Nnrl6mg`erq&Psk}j3o-4nRXiApKUf(ojmaVg=DtVy7z`dE-QP9WD9Hl z?Culsl#zNVJA+=&Cr}G-p02CAn#x{AH+)7e$jV{Tc@-m`Ihc>jQfO~SgZ0;R?PNq6 p0p0jFa|*XrQlY-nYCVDs@h|236}=fNBS8QF002ovPDHLkV1m~bc&z{c literal 0 HcmV?d00001 diff --git a/evently/assets/images/svg/delete.svg b/evently/assets/images/svg/delete.svg new file mode 100644 index 0000000000..8848d56905 --- /dev/null +++ b/evently/assets/images/svg/delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/evently/assets/images/svg/grid.svg b/evently/assets/images/svg/grid.svg new file mode 100644 index 0000000000..7863c25fe6 --- /dev/null +++ b/evently/assets/images/svg/grid.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/evently/assets/images/svg/list.svg b/evently/assets/images/svg/list.svg new file mode 100644 index 0000000000..32fa01075a --- /dev/null +++ b/evently/assets/images/svg/list.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/evently/assets/images/svg/more_options.svg b/evently/assets/images/svg/more_options.svg new file mode 100644 index 0000000000..653364018c --- /dev/null +++ b/evently/assets/images/svg/more_options.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/evently/assets/images/svg/publish.svg b/evently/assets/images/svg/publish.svg new file mode 100644 index 0000000000..4dca606d37 --- /dev/null +++ b/evently/assets/images/svg/publish.svg @@ -0,0 +1,3 @@ + + + diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 42dceb774f..6c5ad47a7f 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -68,5 +68,7 @@ "get_error": "Entwurf konnte nicht abgerufen werden", "delete_error": "Entwurf konnte nicht gelöscht werden", "save_error": "Entwurf konnte nicht gespeichert werden", - "save_error": "Failed to save draft" + "save_error": "Failed to save draft", + "event_name" : "Event Name", + "are_you_sure_you_want_to_delete": "Möchten Sie diesen Entwurf wirklich löschen?" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 40c0782be3..d382d5e13e 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -66,5 +66,7 @@ "loading": "loading ...", "something_wrong": "Something went wrong. Please try restarting the application.", "get_error": "Failed to get draft", - "delete_error": "Failed to delete draft" + "delete_error": "Failed to delete draft", + "event_name" : "Event Name", + "are_you_sure_you_want_to_delete": "Are you sure you want to delete this draft?" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index d8a8ead539..d851a4fa0e 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -67,5 +67,7 @@ "something_wrong": "Algo salió mal. Intenta reiniciar la aplicación.", "get_error": "No se pudo obtener el Plan Preliminar", "delete_error": "No se pudo eliminar el Plan Preliminar", - "save_error": "No se pudo guardar el Plan Preliminar" + "save_error": "No se pudo guardar el Plan Preliminar", + "event_name" : "Event Name", + "are_you_sure_you_want_to_delete": "¿Seguro que quieres eliminar este Plan Preliminar?" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 0128535959..3dcfd108a5 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -67,5 +67,7 @@ "something_wrong": "Что-то пошло не так. Попробуйте перезапустить приложение.", "get_error": "Не удалось получить черновик", "delete_error": "Не удалось удалить черновик", - "save_error": "Не удалось сохранить черновик" + "save_error": "Не удалось сохранить черновик", + "event_name" : "Event Name", + "are_you_sure_you_want_to_delete": "Вы уверены, что хотите удалить этот черновик?" } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index f2e50087a2..1f674f5ab1 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -359,7 +359,6 @@ class EventlyProvider extends ChangeNotifier { required UploadStep uploadStep, }) async { final id = await repository.saveEvents(Events(step: uploadStep.toString(), eventName: eventName, hostName: hostName, thumbnail: thumbnail!)); - onCompleted(); } } diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 1abafc579b..a7c4637f10 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -68,5 +68,6 @@ abstract class LocaleKeys { static const get_error = 'get_error'; static const delete_error = 'delete_error'; static const save_error = 'save_error'; + static const are_you_sure_you_want_to_delete = 'are_you_sure_you_want_to_delete'; } diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index e4bf31cd73..a12ee9ab0a 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -25,6 +25,7 @@ class Events extends Equatable { final String isFreeDrops; final String cookbookID; final String step; + final String denom; const Events({ this.id, @@ -49,11 +50,13 @@ class Events extends Equatable { this.numberOfTickets = '0', this.price = '', this.isFreeDrops = 'unselected', + this.denom = '', ///* other this.cookbookID = '', this.recipeID = '', required this.step, + }); factory Events.fromRecipe(Recipe recipe) { diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 4dfe2861e9..a8d3bf9fc5 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -62,6 +62,11 @@ abstract class Repository { /// This method will save event /// Output: [int] returns that id of [Event] Future> saveEvents(Events events); + + /// This method will delete draft from the local database + /// Input: [id] the id of the nft which the user wants to delete + /// Output: [bool] returns whether the operation is successful or not + Future> deleteNft(int id); } @LazySingleton(as: Repository) @@ -158,4 +163,15 @@ class RepositoryImp implements Repository { return Left(CacheFailure(LocaleKeys.save_error.tr())); } } + + @override + Future> deleteNft(int id) async { + try { + final bool result = await localDataSource.deleteNft(id); + return Right(result); + } on Exception catch (exception) { + + return Left(CacheFailure(LocaleKeys.something_wrong.tr())); + } + } } diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 5f9cad9b8c..b4021a9490 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -1,7 +1,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/main.dart'; +import 'package:evently/models/events.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; +import 'package:evently/screens/event_hub/widgets/delete_confirmation_dialog.dart'; +import 'package:evently/screens/event_hub/widgets/drafts_more_bottomsheet.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; @@ -9,6 +12,9 @@ import 'package:evently/utils/route_util.dart'; import 'package:evently/widgets/clipped_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:focus_detector/focus_detector.dart'; import 'package:provider/provider.dart'; class EventHubScreen extends StatefulWidget { @@ -19,6 +25,36 @@ class EventHubScreen extends StatefulWidget { } class _EventHubScreenState extends State { + EventHubViewModel get eventHubViewModel => sl(); + + @override + void initState() { + eventHubViewModel.getPublishAndDraftData(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: eventHubViewModel, + child: FocusDetector( + onFocusGained: () { + eventHubViewModel.getPublishAndDraftData(); + }, + child: const EventHubContent(), + ), + ); + } +} + +class EventHubContent extends StatefulWidget { + const EventHubContent({super.key}); + + @override + State createState() => _EventHubContentState(); +} + +class _EventHubContentState extends State { TextStyle headingStyle = TextStyle( fontSize: isTablet ? 20.sp : 25, fontWeight: FontWeight.bold, @@ -45,68 +81,99 @@ class _EventHubScreenState extends State { fontFamily: kUniversalFontFamily, ); - EventHubViewModel get eventHubViewModel => sl(); - - @override - void initState() { - eventHubViewModel.getPublishAndDraftData(); - - super.initState(); - } - @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: eventHubViewModel, - child: ColoredBox( - color: EventlyAppTheme.kBlack, - child: SafeArea( - child: Scaffold( - backgroundColor: EventlyAppTheme.kBlack, - body: Padding( - padding: const EdgeInsets.only(top: 20), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(right: 20), - child: Align( - alignment: Alignment.topRight, - child: InkWell( - onTap: () => Navigator.of(context).pushNamed(RouteUtil.kCreateEvent), - child: const DecoratedBox( - decoration: BoxDecoration(color: EventlyAppTheme.kTextLightBlue), - child: Icon(Icons.add, size: 21, color: EventlyAppTheme.kWhite), - ), + final viewModel = context.watch(); + return ColoredBox( + color: EventlyAppTheme.kBlack, + child: SafeArea( + child: Scaffold( + backgroundColor: EventlyAppTheme.kBlack, + body: Padding( + padding: const EdgeInsets.only(top: 20), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(right: 20), + child: Align( + alignment: Alignment.topRight, + child: InkWell( + onTap: () => Navigator.of(context).pushNamed(RouteUtil.kCreateEvent), + child: const DecoratedBox( + decoration: BoxDecoration(color: EventlyAppTheme.kTextLightBlue), + child: Icon(Icons.add, size: 21, color: EventlyAppTheme.kWhite), ), ), ), - Text( - LocaleKeys.eventhub.tr(), - style: headingStyle, + ), + Text( + LocaleKeys.eventhub.tr(), + style: headingStyle, + textAlign: TextAlign.center, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: Text( + LocaleKeys.welcome_event.tr(), + style: titleStyle, textAlign: TextAlign.center, ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: Text( - LocaleKeys.welcome_event.tr(), - style: titleStyle, - textAlign: TextAlign.center, - ), + ), + const SizedBox(height: 40), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 50), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + getButton( + title: LocaleKeys.draft, + onTap: () => viewModel.changeSelectedCollection(CollectionType.draft), + isSelected: viewModel.selectedCollectionType == CollectionType.draft, + ), + getButton( + title: LocaleKeys.for_sale, + onTap: () => viewModel.changeSelectedCollection(CollectionType.forSale), + isSelected: viewModel.selectedCollectionType == CollectionType.forSale, + ), + getButton( + title: LocaleKeys.history, + onTap: () => viewModel.changeSelectedCollection(CollectionType.history), + isSelected: viewModel.selectedCollectionType == CollectionType.history, + ), + ], ), - const SizedBox(height: 40), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 50), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - getButton(title: LocaleKeys.draft), - getButton(title: LocaleKeys.for_sale), - getButton(title: LocaleKeys.history), - ], - ), + ), + const SizedBox(height: 40), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox(width: 16.w), + InkWell( + onTap: () => viewModel.updateViewType(ViewType.viewGrid), + child: SvgPicture.asset( + SVGUtils.kGridIcon, + height: 18.h, + color: getButtonColor(isSelected: viewModel.viewType == ViewType.viewGrid), + ), + ), + SizedBox(width: 14.w), + InkWell( + onTap: () => viewModel.updateViewType(ViewType.viewList), + child: SvgPicture.asset( + SVGUtils.kListIcon, + height: 18.h, + color: getButtonColor(isSelected: viewModel.viewType == ViewType.viewList), + ), + ), + ], ), - const SizedBox(height: 40), - Padding( + ), + const SizedBox(height: 10), + BuildListView( + eventsList: viewModel.eventForDraftList, + onEmptyList: (BuildContext context) => Padding( padding: EdgeInsets.symmetric(horizontal: 20.w), child: Align( alignment: Alignment.centerLeft, @@ -115,10 +182,10 @@ class _EventHubScreenState extends State { style: subTitleStyle, )), ), - const Spacer(), - Padding(padding: EdgeInsets.symmetric(horizontal: 20.w), child: getCreateEventWidget()), - ], - ), + ), + const Spacer(), + Padding(padding: EdgeInsets.symmetric(horizontal: 20.w), child: getCreateEventWidget()), + ], ), ), ), @@ -126,17 +193,28 @@ class _EventHubScreenState extends State { ); } - Widget getButton({required String title}) { + Color getButtonColor({required bool isSelected}) { + if (isSelected) { + return EventlyAppTheme.kBlue; + } else { + return EventlyAppTheme.kWhite; + } + } + + Widget getButton({required String title, required VoidCallback onTap, required bool isSelected}) { return Expanded( - child: Container( - alignment: Alignment.center, - margin: EdgeInsets.symmetric(horizontal: 8.w), - decoration: BoxDecoration(border: Border.all(color: EventlyAppTheme.kWhite)), - padding: EdgeInsets.symmetric(vertical: 5.h, horizontal: 10.w), - child: Text( - title.tr(), - style: btnTxtStyle, - textAlign: TextAlign.center, + child: GestureDetector( + onTap: () => onTap(), + child: Container( + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration(border: Border.all(color: isSelected ? EventlyAppTheme.kBlue : EventlyAppTheme.kWhite)), + padding: EdgeInsets.symmetric(vertical: 5.h, horizontal: 10.w), + child: Text( + title.tr(), + style: btnTxtStyle, + textAlign: TextAlign.center, + ), ), ), ); @@ -161,3 +239,293 @@ class _EventHubScreenState extends State { ); } } + +class BuildListView extends StatelessWidget { + final List eventsList; + final WidgetBuilder onEmptyList; + + const BuildListView({super.key, required this.eventsList, required this.onEmptyList}); + + EventHubViewModel get viewModel => sl(); + + @override + Widget build(BuildContext context) { + if (eventsList.isEmpty) { + return onEmptyList(context); + } + return ListView.builder( + shrinkWrap: true, + itemCount: eventsList.length, + itemBuilder: (context, index) { + final events = eventsList[index]; + if (viewModel.selectedCollectionType == CollectionType.draft) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: DraftListTile(events: events, viewModel: viewModel), + ); + } else { + return const Placeholder(); + } + }, + ); + } +} + +class DraftListTile extends StatefulWidget { + final Events events; + final EventHubViewModel viewModel; + + const DraftListTile({super.key, required this.events, required this.viewModel}); + + @override + State createState() => _DraftListTileState(); +} + +class _DraftListTileState extends State { + Widget getPlaceHolder() { + return Container( + margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 3.w), + decoration: BoxDecoration( + color: EventlyAppTheme.kGery03, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + offset: const Offset(0.0, 1.0), + blurRadius: 4.0, + ), + ], + ), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 15.h), + child: Row( + children: [ + Container( + width: 50.0.w, + height: 50.0.w, + decoration: const BoxDecoration( + color: EventlyAppTheme.kGery03, + ), + ), + SizedBox( + width: 10.w, + ), + Expanded( + child: ListView.separated( + shrinkWrap: true, + itemBuilder: (context, index) { + return Container( + width: 30.0.w, + height: 9.0.h, + color: EventlyAppTheme.kGery03, + ); + }, + separatorBuilder: (context, index) { + return SizedBox( + height: 8.0.h, + ); + }, + itemCount: 3, + ), + ), + SizedBox( + width: 10.w, + ), + Padding( + padding: EdgeInsets.all(4.0.w), + child: SvgPicture.asset( + SVGUtils.kSvgMoreOption, + color: EventlyAppTheme.kGery03, + ), + ) + ], + ), + ), + ); + } + + Widget getDraftCard() { + return DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + offset: const Offset(0.0, 1.0), + blurRadius: 4.0, + ), + ], + ), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 15.h), + child: Row( + children: [ + SizedBox( + width: 10.w, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + // "event_name".tr(args: [if (widget.events.eventName.isNotEmpty) widget.nft.name else 'Event Name']), + (widget.events.eventName.isNotEmpty) ? widget.events.eventName : 'Event Name', + style: titleStyle.copyWith(fontSize: isTablet ? 13.sp : 18.sp), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + SizedBox( + height: 6.h, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(1.h), + color: EventlyAppTheme.kLightRed, + ), + padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 3.h), + child: Text( + LocaleKeys.draft.tr(), + style: EventlyAppTheme.titleStyle.copyWith(color: EventlyAppTheme.kWhite, fontSize: isTablet ? 8.sp : 11.sp), + ), + ), + ], + ), + ), + SizedBox( + width: 10.w, + ), + InkWell( + onTap: () {}, + child: Padding( + padding: EdgeInsets.all(4.0.w), + child: SvgPicture.asset(SVGUtils.kSvgMoreOption), + ), + ), + SizedBox( + width: 10.w, + ), + ], + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Slidable( + key: ValueKey(widget.events.id), + closeOnScroll: false, + endActionPane: ActionPane( + extentRatio: 0.3, + motion: const ScrollMotion(), + children: [ + buildSlidableAction( + context, + callback: () { + final DeleteDialog deleteDialog = DeleteDialog(contextt: context, events: widget.events); + deleteDialog.show(); + }, + icon: SVGUtils.kSvgDelete, + ), + ], + ), + child: (widget.events.price.isNotEmpty && double.parse(widget.events.price) > 0) + ? Card( + elevation: 5, + margin: EdgeInsets.zero, + child: ClipRRect( + child: Banner( + color: EventlyAppTheme.kDarkGreen, + location: BannerLocation.topEnd, + message: "\$ ${widget.events.price}", + child: getDraftCard(), + ), + ), + ) + : getDraftCard(), + ), + // IgnorePointer( + // child: SizedBox( + // height: 85.0.h, + // width: double.infinity, + // child: CachedNetworkImage( + // imageUrl: widget.events.thumbnail, + // fit: BoxFit.fill, + // color: EventlyAppTheme.kTransparent, + // colorBlendMode: BlendMode.clear, + // placeholder: (context, _) => getPlaceHolder(), + // errorWidget: (context, _, __) { + // return const IgnorePointer(child: SizedBox()); + // }, + // ), + // ), + // ) + ], + ); + } + + Widget buildSlidableAction(BuildContext context, {required VoidCallback callback, required String icon, bool isSvg = true}) { + return Expanded( + child: InkWell( + onTap: callback, + child: isSvg ? SvgPicture.asset(icon) : Image.asset(icon), + ), + ); + } +} + +class BuildGridView extends StatelessWidget { + final List eventsList; + final WidgetBuilder onEmptyList; + final String Function({ + required String price, + required String currency, + }) calculateBannerPrice; + + const BuildGridView({ + super.key, + required this.eventsList, + required this.onEmptyList, + required this.calculateBannerPrice, + }); + + @override + Widget build(BuildContext context) { + if (eventsList.isEmpty) { + return onEmptyList(context); + } + return Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: GridView.builder( + itemCount: eventsList.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 0.5, + crossAxisSpacing: 15.w, + mainAxisSpacing: 15.h, + crossAxisCount: 3, + ), + itemBuilder: (context, index) { + final events = eventsList[index]; + if (events.price.isNotEmpty && double.parse(events.price) > 0) { + return ClipRRect( + child: Banner( + + color: EventlyAppTheme.kDarkGreen, + location: BannerLocation.topEnd, + message: calculateBannerPrice(price: events.price, currency: events.denom), + child: NftGridViewItem( + nft: nft, + ), + ), + ); + } else { + return NftGridViewItem( + nft: nft, + ); + } + }, + ), + ); + } +} diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 3d20c33684..234159f8c2 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -10,6 +10,8 @@ import 'package:pylons_sdk/pylons_sdk.dart'; enum CollectionType { draft, forSale, history } +enum ViewType { viewGrid, viewList } + @lazySingleton class EventHubViewModel extends ChangeNotifier { EventHubViewModel(this.repository); @@ -22,6 +24,15 @@ class EventHubViewModel extends ChangeNotifier { List _eventForDraftList = []; + ViewType viewType = ViewType.viewGrid; + + void updateViewType(ViewType selectedViewType) { + viewType = selectedViewType; + notifyListeners(); + } + + List get eventForDraftList => _eventForDraftList; + List get eventForSaleList => _eventForDraftList; set setEventForDraftList(List nftForSale) { @@ -40,22 +51,24 @@ class EventHubViewModel extends ChangeNotifier { notifyListeners(); } - CollectionType selectedCollectionType = CollectionType.draft; + CollectionType _selectedCollectionType = CollectionType.draft; + + CollectionType get selectedCollectionType => _selectedCollectionType; void changeSelectedCollection(CollectionType collectionType) { switch (collectionType) { case CollectionType.draft: - selectedCollectionType = CollectionType.draft; + _selectedCollectionType = CollectionType.draft; notifyListeners(); break; case CollectionType.forSale: - selectedCollectionType = CollectionType.forSale; + _selectedCollectionType = CollectionType.forSale; notifyListeners(); break; case CollectionType.history: - selectedCollectionType = CollectionType.history; + _selectedCollectionType = CollectionType.history; notifyListeners(); break; } @@ -118,4 +131,15 @@ class EventHubViewModel extends ChangeNotifier { await getDraftsList(); notifyListeners(); } + + Future deleteNft(int? id) async { + final deleteNftResponse = await repository.deleteNft(id!); + + if (deleteNftResponse.isLeft()) { + LocaleKeys.delete_error.tr().show(); + return; + } + eventForDraftList.removeWhere((element) => element.id == id); + notifyListeners(); + } } diff --git a/evently/lib/screens/event_hub/widgets/delete_confirmation_dialog.dart b/evently/lib/screens/event_hub/widgets/delete_confirmation_dialog.dart new file mode 100644 index 0000000000..4873cde777 --- /dev/null +++ b/evently/lib/screens/event_hub/widgets/delete_confirmation_dialog.dart @@ -0,0 +1,113 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/main.dart'; +import 'package:evently/models/events.dart'; +import 'package:evently/screens/event_hub/event_hub_view_model.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/di/di.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/widgets/clipped_button.dart'; +import 'package:evently/widgets/dialog_clipper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../generated/locale_keys.g.dart'; + +class DeleteDialog { + final BuildContext contextt; + final Events events; + + DeleteDialog({required this.contextt, required this.events}); + + EventHubViewModel get creatorHubViewModel => sl(); + + Future show() async { + return showDialog( + barrierColor: Colors.black38, + barrierDismissible: false, + context: contextt, + builder: (context) { + return Dialog( + elevation: 0, + insetPadding: EdgeInsets.symmetric(horizontal: isTablet ? 45.w : 15.w), + backgroundColor: EventlyAppTheme.kTransparent, + child: ClipPath( + clipper: DialogClipper(), + child: Container( + color: EventlyAppTheme.kLightRed, + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + height: 20.h, + ), + Align( + child: SvgPicture.asset( + SVGUtils.kAlertIcon, + height: 40.h, + fit: BoxFit.cover, + ), + ), + SizedBox( + height: 35.h, + ), + Text( + LocaleKeys.are_you_sure_you_want_to_delete.tr(), + textAlign: TextAlign.center, + style: EventlyAppTheme.kDeleteHeaderTextStyle, + ), + SizedBox( + height: 30.h, + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 20.w), + height: 40.h, + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: ClippedButton( + title: LocaleKeys.yes.tr(), + bgColor: EventlyAppTheme.kTextDarkPurple, + textColor: EventlyAppTheme.kWhite, + fontWeight: FontWeight.w300, + clipperType: ClipperType.bottomLeftTopRight, + onPressed: () async { + Navigator.of(context).pop(); + creatorHubViewModel.deleteNft(events.id); + }, + cuttingHeight: 12.h, + ), + ), + SizedBox( + width: 15.w, + ), + Expanded( + child: ClippedButton( + title: LocaleKeys.no.tr(), + bgColor: EventlyAppTheme.kWhite.withOpacity(0.3), + textColor: EventlyAppTheme.kWhite, + onPressed: () { + Navigator.of(context).pop(); + }, + cuttingHeight: 12.h, + fontWeight: FontWeight.w300, + clipperType: ClipperType.bottomLeftTopRight, + ), + ), + ], + ), + ), + SizedBox( + height: 30.h, + ), + ], + ), + ), + ), + ); + }); + } +} diff --git a/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart b/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart new file mode 100644 index 0000000000..208d44930c --- /dev/null +++ b/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart @@ -0,0 +1,153 @@ +import 'package:evently/evently_provider.dart'; +import 'package:evently/main.dart'; +import 'package:evently/models/events.dart'; +import 'package:evently/screens/event_hub/event_hub_view_model.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/di/di.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/widgets/clippers/bottom_sheet_clipper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; + +import '../../../generated/locale_keys.g.dart'; + +TextStyle titleStyle = TextStyle(fontSize: isTablet ? 13.sp : 16.sp, fontWeight: FontWeight.w800, fontFamily: kUniversalFontFamily, color: EventlyAppTheme.kBlack); + +class DraftsBottomSheet { + final BuildContext buildContext; + final Events events; + + EventHubViewModel get creatorHubViewModel => sl(); + + DraftsBottomSheet({required this.buildContext, required this.events}); + + Future show() async { + return showModalBottomSheet( + backgroundColor: EventlyAppTheme.kTransparent, + context: buildContext, + builder: (_) { + return ChangeNotifierProvider.value( + value: creatorHubViewModel, + child: DraftsMoreBottomSheet( + events: events, + ), + ); + }, + ); + } +} + +class DraftsMoreBottomSheet extends StatelessWidget { + const DraftsMoreBottomSheet({super.key, required this.events}); + + final Events events; + + EventlyProvider get easelProvider => sl(); + + Future onViewOnIPFSPressed({required BuildContext context, required Events events}) async { + // final easelProvider = Provider.of(context, listen: false); + // await easelProvider.repository.launchMyUrl(url: nft.url.changeDomain()); + } + + @override + Widget build(BuildContext context) { + final viewModel = context.watch(); + return ClipPath( + clipper: BottomSheetClipper(), + child: Container( + color: EventlyAppTheme.kGrey02, + padding: EdgeInsets.symmetric(horizontal: 30.w, vertical: 30.h), + child: Wrap( + children: [ + moreOptionTile( + title: "publish", + image: SVGUtils.kSvgPublish, + onPressed: () { + viewModel.saveNFT(nft: nft); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed(RouteUtil.kRouteHome); + }), + const Divider( + color: EaselAppTheme.kGrey, + ), + moreOptionTile( + title: "delete", + image: SVGUtils.kSvgDelete, + onPressed: () { + Navigator.of(context).pop(); + + final DeleteDialog deleteDialog = DeleteDialog(contextt: context, nft: nft); + + deleteDialog.show(); + }), + const Divider( + color: EaselAppTheme.kGrey, + ), + CidOrIpfs( + viewCid: (context) { + return moreOptionTile( + onPressed: () async { + final state = ScaffoldMessenger.of(context); + Navigator.of(context).pop(); + await Clipboard.setData(ClipboardData(text: nft.cid)); + state + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar(content: Text(LocaleKeys.copied_to_clipboard.tr())), + ); + }, + title: LocaleKeys.copy_cid.tr(), + image: PngUtils.kSvgIpfsLogo, + isSvg: false, + ); + }, + viewIpfs: (context) { + return moreOptionTile( + onPressed: () async { + Navigator.of(context).pop(); + onViewOnIPFSPressed(nft: nft, context: context); + }, + title: LocaleKeys.view.tr(), + image: PngUtils.kSvgIpfsLogo, + isSvg: false, + ); + }, + type: nft.assetType) + ], + ), + ), + ); + } + + void navigateToPreviewScreen({required BuildContext context, required NFT nft}) { + easelProvider.setPublishedNFTClicked(nft); + easelProvider.setPublishedNFTDuration(nft.duration); + Navigator.of(context).pushReplacementNamed(RouteUtil.kRoutePreviewNFTFullScreen); + } +} + +Widget moreOptionTile({required String title, required String image, required VoidCallback onPressed, bool isSvg = true}) { + final TextStyle titleStyle = TextStyle(fontSize: isTablet ? 13.sp : 16.sp, fontWeight: FontWeight.w800, fontFamily: kUniversalFontFamily, color: EaselAppTheme.kBlack); + + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.h), + child: InkWell( + onTap: onPressed, + child: Row( + children: [ + if (isSvg) SvgPicture.asset(image) else Image.asset(image), + SizedBox( + width: 30.w, + ), + Text( + title, + style: titleStyle.copyWith(fontSize: 16.sp), + ) + ], + ), + ), + ); +} diff --git a/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart b/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart new file mode 100644 index 0000000000..3786203d03 --- /dev/null +++ b/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart @@ -0,0 +1,212 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/evently_provider.dart'; +import 'package:evently/models/events.dart'; +import 'package:evently/screens/event_hub/event_hub_view_model.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:get_it/get_it.dart'; +import 'package:provider/provider.dart'; +import 'package:shimmer_animation/shimmer_animation.dart'; + +import '../../../generated/locale_keys.g.dart'; +import '../../../main.dart'; + +List gradientBlackToTransparent = [ + Colors.black87, + Colors.black54, + Colors.black45, + Colors.black38, + Colors.black26, + Colors.black12, + EventlyAppTheme.kTransparent, +]; + +List gradientTransparentToBlack = [ + EventlyAppTheme.kTransparent, + Colors.black12, + Colors.black26, + Colors.black38, + Colors.black45, + Colors.black54, + Colors.black87, +]; + +class NftGridViewItem extends StatelessWidget { + const NftGridViewItem({ + super.key, + required this.events, + }); + final Events events; + + EventlyProvider get _easelProvider => GetIt.I.get(); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + SizedBox( + height: 200.h, + width: 150.w, + child: InkWell( + + onTap: () { + if (context.read().selectedCollectionType == CollectionType.draft) { + final DraftsBottomSheet draftsBottomSheet = DraftsBottomSheet( + buildContext: context, + nft: nft, + ); + draftsBottomSheet.show(); + return; + } + buildBottomSheet(context: context); + }, + child: NftTypeBuilder( + onImage: (context) => buildNFTPreview(url: nft.url.changeDomain()), + onVideo: (context) => buildNFTPreview(url: nft.thumbnailUrl.changeDomain()), + onAudio: (context) => buildNFTPreview(url: nft.thumbnailUrl.changeDomain()), + onPdf: (context) => buildNFTPreview(url: nft.thumbnailUrl.changeDomain()), + on3D: (context) => IgnorePointer( + child: ModelViewer( + src: nft.url.changeDomain(), + ar: false, + autoRotate: false, + backgroundColor: EaselAppTheme.kWhite, + cameraControls: false, + ), + ), + assetType: nft.assetType.toAssetTypeEnum(), + ), + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + alignment: Alignment.bottomLeft, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: gradientBlackToTransparent, + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Padding( + padding: EdgeInsets.only(left: 8.w, top: 10.0.h), + child: SvgPicture.asset( + getNFTIcon(), + key: Key(getNFTIconKey()), + color: Colors.white, + width: 14, + height: 14, + ), + ), + ), + Container( + alignment: Alignment.bottomLeft, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: gradientTransparentToBlack, + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Row( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(1.h), + color: context.read().selectedCollectionType == CollectionType.draft + ? EaselAppTheme.kLightRed + : EaselAppTheme.kDarkGreen, + ), + padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 3.h), + margin: EdgeInsets.symmetric(horizontal: 6.w, vertical: 6.h), + child: Text( + context.read().selectedCollectionType == CollectionType.draft + ? LocaleKeys.draft.tr() + : LocaleKeys.published.tr(), + style: EaselAppTheme.titleStyle.copyWith( + color: EaselAppTheme.kWhite, + fontSize: isTablet ? 8.sp : 11.sp, + ), + ), + ), + const Spacer(), + InkWell( + key: const Key(kGridViewTileMoreOptionKey), + onTap: () { + if (context.read().selectedCollectionType == CollectionType.draft) { + final DraftsBottomSheet draftsBottomSheet = DraftsBottomSheet( + buildContext: context, + nft: nft, + ); + draftsBottomSheet.show(); + return; + } + buildBottomSheet(context: context); + }, + child: Padding( + padding: EdgeInsets.all(4.0.w), + child: SvgPicture.asset(SVGUtils.kSvgMoreOption, color: Colors.white), + ), + ), + const SizedBox( + width: 5, + ) + ], + ), + ), + ], + ) + ], + ); + } + + CachedNetworkImage buildNFTPreview({required String url}) { + return CachedNetworkImage( + fit: BoxFit.fitHeight, + imageUrl: url, + errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), + placeholder: (context, url) => Shimmer(color: EaselAppTheme.cardBackground, child: const SizedBox.expand()), + ); + } + + void buildBottomSheet({required BuildContext context}) { + final bottomSheet = BuildPublishedNFTsBottomSheet(context: context, nft: nft, easelProvider: _easelProvider); + + bottomSheet.show(); + } + + String getNFTIcon() { + switch (nft.assetType) { + case kVideoText: + return SVGUtils.kSvgNftFormatVideo; + case kAudioText: + return SVGUtils.kSvgNftFormatAudio; + case kPdfText: + return SVGUtils.kSvgNftFormatPDF; + case k3dText: + return SVGUtils.kSvgNftFormat3d; + default: + return SVGUtils.kFileTypeImageIcon; + } + } + + String getNFTIconKey() { + switch (nft.assetType) { + case kVideoText: + return kNFTTypeVideoIconKey; + case kAudioText: + return kNFTTypeAudioIconKey; + case kPdfText: + return kNFTTypePdfIconKey; + case k3dText: + return kNFTType3dModelIconKey; + default: + return kNFTTypeImageIconKey; + } + } +} diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 0cbbf985f1..0c17fe2eca 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -52,6 +52,11 @@ abstract class LocalDataSource { /// Input: [Events] the draft that will will be saved in database /// Output: [int] returns id of the inserted document Future saveEvents(Events events); + + /// This method will delete draft from the local database + /// Input: [id] the id of the draft which the user wants to delete + /// Output: [bool] returns whether the operation is successful or not + Future deleteNft(int id); } @LazySingleton(as: LocalDataSource) @@ -138,4 +143,14 @@ class LocalDataSourceImpl extends LocalDataSource { throw LocaleKeys.save_error.tr(); } } + + @override + Future deleteNft(int id) async { + try { + await database.eventsDao.delete(id); + return true; + } catch (e) { + throw CacheFailure(LocaleKeys.delete_error.tr()); + } + } } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index 7d9d93c6cf..f5173ec7e3 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -49,7 +49,6 @@ const kFreeShirt = "free_shirt"; const kFreeGift = "free_gift"; const kFreeDrink = "free_drink"; - const kResidual = "Residual"; const kQuantity = "Quantity"; const String transferFeeAmount = '1'; @@ -70,6 +69,13 @@ class SVGUtils { static const kDiamond = "assets/images/svg/diamond.svg"; static const kCamera = "assets/images/svg/camera.svg"; static const kAlertIcon = 'assets/images/svg/i_icon.svg'; + static const kSvgMoreOption = 'assets/images/svg/more_options.svg'; + static const kSvgDelete = 'assets/images/svg/delete.svg'; + static const kGridIcon = 'assets/images/svg/grid.svg'; + static const kListIcon = 'assets/images/svg/list.svg'; + static const kSvgPublish = 'assets/images/svg/publish.svg'; + + } /// ```PNG assets @@ -82,6 +88,7 @@ class PngUtils { static const kIconDenomUsd = 'assets/images/denom_usd.png'; static const kIconDenomPylon = 'assets/images/denom_pylon.png'; static const kHostPreview = "assets/images/host_preview.png"; + static const kSvgIpfsLogo = 'assets/images/ipfs_logo.png'; /// will remove this variable for ui dev /// i need this variable to be used diff --git a/evently/lib/utils/evently_app_theme.dart b/evently/lib/utils/evently_app_theme.dart index ecccef6fd6..e5020bf64a 100644 --- a/evently/lib/utils/evently_app_theme.dart +++ b/evently/lib/utils/evently_app_theme.dart @@ -25,6 +25,7 @@ class EventlyAppTheme { static const Color kGreen = Color(0xFF0CAF59); static const Color kLightRed = Color(0xFFEF4421); + static const Color kDarkGreen = Color(0xFF3A8977); static const String universalSansFamily = "UniversalSans"; diff --git a/evently/lib/widgets/clippers/bottom_sheet_clipper.dart b/evently/lib/widgets/clippers/bottom_sheet_clipper.dart new file mode 100644 index 0000000000..af5ff24708 --- /dev/null +++ b/evently/lib/widgets/clippers/bottom_sheet_clipper.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class BottomSheetClipperDialog extends CustomClipper { + BottomSheetClipperDialog(); + + @override + Path getClip(Size size) { + final path = Path(); + path.moveTo(0, size.height); + path.lineTo(0, size.height * 0.08.h); + path.lineTo(size.width * 0.045.w, 0); + path.lineTo(size.width * 0.88.w, 0); + path.lineTo(size.width, size.height * 0.08.h); + path.lineTo(size.width, size.height); + path.lineTo(0, size.height); + + return path; + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return false; + } +} + +class BottomSheetClipper extends CustomClipper { + BottomSheetClipper(); + + @override + Path getClip(Size size) { + final Path path0 = Path(); + path0.moveTo(0, size.height); + path0.lineTo(0, size.height * 0.1241250); + path0.lineTo(size.width * 0.0753750, 0); + path0.lineTo(size.width * 0.9253000, 0); + path0.lineTo(size.width, size.height * 0.1236000); + path0.lineTo(size.width, size.height); + path0.lineTo(0, size.height); + path0.close(); + + return path0; + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return false; + } +} diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 3e54b6d1c0..ce96ed57ce 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -395,6 +395,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.9.0" + flutter_slidable: + dependency: "direct main" + description: + name: flutter_slidable + sha256: "673403d2eeef1f9e8483bd6d8d92aae73b1d8bd71f382bc3930f699c731bc27c" + url: "https://pub.dev" + source: hosted + version: "3.1.0" flutter_svg: dependency: "direct main" description: @@ -421,6 +429,14 @@ packages: description: flutter source: sdk version: "0.0.0" + focus_detector: + dependency: "direct main" + description: + name: focus_detector + sha256: "05e32d9dd378cd54f1a3f9ce813c05156f28eb83f8e68f5bf1a37e9cdb21af1c" + url: "https://pub.dev" + source: hosted + version: "2.0.1" frontend_server_client: dependency: transitive description: @@ -1217,6 +1233,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: ec932527913f32f65aa01d3a393504240b9e9021ecc77123f017755605e48832 + url: "https://pub.dev" + source: hosted + version: "0.2.2" vm_service: dependency: transitive description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index 763477446b..6b94dee83d 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -58,6 +58,8 @@ dependencies: shimmer_animation: 2.1.0+1 network_info_plus: ^5.0.3 floor: ^1.5.0 + focus_detector: ^2.0.1 + flutter_slidable: ^3.1.0 dev_dependencies: flutter_test: From 06d0920816390921ab04466a7ac5357d10015ad5 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 13:02:30 +0500 Subject: [PATCH 113/161] feat: list view and grid view of the events on event hub screen --- evently/assets/images/svg/file_type_image.svg | 3 + evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/generated/locale_keys.g.dart | 1 + .../screens/event_hub/event_hub_screen.dart | 5 +- .../widgets/drafts_more_bottomsheet.dart | 56 ++----------- .../event_hub/widgets/nfts_grid_view.dart | 83 +++++-------------- evently/lib/utils/constants.dart | 1 + 10 files changed, 44 insertions(+), 117 deletions(-) create mode 100644 evently/assets/images/svg/file_type_image.svg diff --git a/evently/assets/images/svg/file_type_image.svg b/evently/assets/images/svg/file_type_image.svg new file mode 100644 index 0000000000..ee05e35217 --- /dev/null +++ b/evently/assets/images/svg/file_type_image.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 6c5ad47a7f..3fed743e92 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -70,5 +70,6 @@ "save_error": "Entwurf konnte nicht gespeichert werden", "save_error": "Failed to save draft", "event_name" : "Event Name", - "are_you_sure_you_want_to_delete": "Möchten Sie diesen Entwurf wirklich löschen?" + "are_you_sure_you_want_to_delete": "Möchten Sie diesen Entwurf wirklich löschen?", + "published": "Veröffentlicht" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index d382d5e13e..cf23ed0c46 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -68,5 +68,6 @@ "get_error": "Failed to get draft", "delete_error": "Failed to delete draft", "event_name" : "Event Name", - "are_you_sure_you_want_to_delete": "Are you sure you want to delete this draft?" + "are_you_sure_you_want_to_delete": "Are you sure you want to delete this draft?", + "published": "Published" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index d851a4fa0e..02c7a04d8a 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -69,5 +69,6 @@ "delete_error": "No se pudo eliminar el Plan Preliminar", "save_error": "No se pudo guardar el Plan Preliminar", "event_name" : "Event Name", - "are_you_sure_you_want_to_delete": "¿Seguro que quieres eliminar este Plan Preliminar?" + "are_you_sure_you_want_to_delete": "¿Seguro que quieres eliminar este Plan Preliminar?", + "published": "Publicado" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 3dcfd108a5..7a757cef60 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -69,5 +69,6 @@ "delete_error": "Не удалось удалить черновик", "save_error": "Не удалось сохранить черновик", "event_name" : "Event Name", - "are_you_sure_you_want_to_delete": "Вы уверены, что хотите удалить этот черновик?" + "are_you_sure_you_want_to_delete": "Вы уверены, что хотите удалить этот черновик?", + "published": "изданный" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index a7c4637f10..4813e6b851 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -69,5 +69,6 @@ abstract class LocaleKeys { static const delete_error = 'delete_error'; static const save_error = 'save_error'; static const are_you_sure_you_want_to_delete = 'are_you_sure_you_want_to_delete'; + static const published = 'published'; } diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index b4021a9490..31962dcc2b 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -5,6 +5,7 @@ import 'package:evently/models/events.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/screens/event_hub/widgets/delete_confirmation_dialog.dart'; import 'package:evently/screens/event_hub/widgets/drafts_more_bottomsheet.dart'; +import 'package:evently/screens/event_hub/widgets/nfts_grid_view.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; @@ -515,13 +516,13 @@ class BuildGridView extends StatelessWidget { location: BannerLocation.topEnd, message: calculateBannerPrice(price: events.price, currency: events.denom), child: NftGridViewItem( - nft: nft, + events: events, ), ), ); } else { return NftGridViewItem( - nft: nft, + events: events, ); } }, diff --git a/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart b/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart index 208d44930c..099c96b8cf 100644 --- a/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart +++ b/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart @@ -2,18 +2,16 @@ import 'package:evently/evently_provider.dart'; import 'package:evently/main.dart'; import 'package:evently/models/events.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; +import 'package:evently/screens/event_hub/widgets/delete_confirmation_dialog.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/widgets/clippers/bottom_sheet_clipper.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; -import '../../../generated/locale_keys.g.dart'; - TextStyle titleStyle = TextStyle(fontSize: isTablet ? 13.sp : 16.sp, fontWeight: FontWeight.w800, fontFamily: kUniversalFontFamily, color: EventlyAppTheme.kBlack); class DraftsBottomSheet { @@ -54,7 +52,7 @@ class DraftsMoreBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { - final viewModel = context.watch(); + // final viewModel = context.watch(); return ClipPath( clipper: BottomSheetClipper(), child: Container( @@ -66,12 +64,12 @@ class DraftsMoreBottomSheet extends StatelessWidget { title: "publish", image: SVGUtils.kSvgPublish, onPressed: () { - viewModel.saveNFT(nft: nft); - Navigator.of(context).pop(); - Navigator.of(context).pushNamed(RouteUtil.kRouteHome); + // viewModel.saveNFT(nft: nft); + // Navigator.of(context).pop(); + // Navigator.of(context).pushNamed(RouteUtil.kRouteHome); }), const Divider( - color: EaselAppTheme.kGrey, + color: EventlyAppTheme.kGrey01, ), moreOptionTile( title: "delete", @@ -79,58 +77,22 @@ class DraftsMoreBottomSheet extends StatelessWidget { onPressed: () { Navigator.of(context).pop(); - final DeleteDialog deleteDialog = DeleteDialog(contextt: context, nft: nft); + final DeleteDialog deleteDialog = DeleteDialog(contextt: context, events: events); deleteDialog.show(); }), const Divider( - color: EaselAppTheme.kGrey, + color: EventlyAppTheme.kGrey01, ), - CidOrIpfs( - viewCid: (context) { - return moreOptionTile( - onPressed: () async { - final state = ScaffoldMessenger.of(context); - Navigator.of(context).pop(); - await Clipboard.setData(ClipboardData(text: nft.cid)); - state - ..hideCurrentSnackBar() - ..showSnackBar( - SnackBar(content: Text(LocaleKeys.copied_to_clipboard.tr())), - ); - }, - title: LocaleKeys.copy_cid.tr(), - image: PngUtils.kSvgIpfsLogo, - isSvg: false, - ); - }, - viewIpfs: (context) { - return moreOptionTile( - onPressed: () async { - Navigator.of(context).pop(); - onViewOnIPFSPressed(nft: nft, context: context); - }, - title: LocaleKeys.view.tr(), - image: PngUtils.kSvgIpfsLogo, - isSvg: false, - ); - }, - type: nft.assetType) ], ), ), ); } - - void navigateToPreviewScreen({required BuildContext context, required NFT nft}) { - easelProvider.setPublishedNFTClicked(nft); - easelProvider.setPublishedNFTDuration(nft.duration); - Navigator.of(context).pushReplacementNamed(RouteUtil.kRoutePreviewNFTFullScreen); - } } Widget moreOptionTile({required String title, required String image, required VoidCallback onPressed, bool isSvg = true}) { - final TextStyle titleStyle = TextStyle(fontSize: isTablet ? 13.sp : 16.sp, fontWeight: FontWeight.w800, fontFamily: kUniversalFontFamily, color: EaselAppTheme.kBlack); + final TextStyle titleStyle = TextStyle(fontSize: isTablet ? 13.sp : 16.sp, fontWeight: FontWeight.w800, fontFamily: kUniversalFontFamily, color: EventlyAppTheme.kBlack); return Padding( padding: EdgeInsets.symmetric(vertical: 8.h), diff --git a/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart b/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart index 3786203d03..1cc98f3383 100644 --- a/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart +++ b/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart @@ -3,6 +3,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; import 'package:evently/models/events.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; +import 'package:evently/screens/event_hub/widgets/drafts_more_bottomsheet.dart'; +import 'package:evently/utils/constants.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -51,33 +53,22 @@ class NftGridViewItem extends StatelessWidget { height: 200.h, width: 150.w, child: InkWell( - onTap: () { if (context.read().selectedCollectionType == CollectionType.draft) { final DraftsBottomSheet draftsBottomSheet = DraftsBottomSheet( buildContext: context, - nft: nft, + events: events, ); draftsBottomSheet.show(); return; } buildBottomSheet(context: context); }, - child: NftTypeBuilder( - onImage: (context) => buildNFTPreview(url: nft.url.changeDomain()), - onVideo: (context) => buildNFTPreview(url: nft.thumbnailUrl.changeDomain()), - onAudio: (context) => buildNFTPreview(url: nft.thumbnailUrl.changeDomain()), - onPdf: (context) => buildNFTPreview(url: nft.thumbnailUrl.changeDomain()), - on3D: (context) => IgnorePointer( - child: ModelViewer( - src: nft.url.changeDomain(), - ar: false, - autoRotate: false, - backgroundColor: EaselAppTheme.kWhite, - cameraControls: false, - ), - ), - assetType: nft.assetType.toAssetTypeEnum(), + child: CachedNetworkImage( + fit: BoxFit.fitHeight, + imageUrl: events.thumbnail, + errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), + placeholder: (context, url) => Shimmer(color: EventlyAppTheme.kGery03, child: const SizedBox.expand()), ), ), ), @@ -96,8 +87,7 @@ class NftGridViewItem extends StatelessWidget { child: Padding( padding: EdgeInsets.only(left: 8.w, top: 10.0.h), child: SvgPicture.asset( - getNFTIcon(), - key: Key(getNFTIconKey()), + SVGUtils.kFileTypeImageIcon, color: Colors.white, width: 14, height: 14, @@ -118,30 +108,25 @@ class NftGridViewItem extends StatelessWidget { Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(1.h), - color: context.read().selectedCollectionType == CollectionType.draft - ? EaselAppTheme.kLightRed - : EaselAppTheme.kDarkGreen, + color: context.read().selectedCollectionType == CollectionType.draft ? EventlyAppTheme.kLightRed : EventlyAppTheme.kDarkGreen, ), padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 3.h), margin: EdgeInsets.symmetric(horizontal: 6.w, vertical: 6.h), child: Text( - context.read().selectedCollectionType == CollectionType.draft - ? LocaleKeys.draft.tr() - : LocaleKeys.published.tr(), - style: EaselAppTheme.titleStyle.copyWith( - color: EaselAppTheme.kWhite, + context.read().selectedCollectionType == CollectionType.draft ? LocaleKeys.draft.tr() : LocaleKeys.published.tr(), + style: EventlyAppTheme.titleStyle.copyWith( + color: EventlyAppTheme.kWhite, fontSize: isTablet ? 8.sp : 11.sp, ), ), ), const Spacer(), InkWell( - key: const Key(kGridViewTileMoreOptionKey), onTap: () { - if (context.read().selectedCollectionType == CollectionType.draft) { + if (context.read().selectedCollectionType == CollectionType.draft) { final DraftsBottomSheet draftsBottomSheet = DraftsBottomSheet( buildContext: context, - nft: nft, + events: events, ); draftsBottomSheet.show(); return; @@ -170,43 +155,13 @@ class NftGridViewItem extends StatelessWidget { fit: BoxFit.fitHeight, imageUrl: url, errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), - placeholder: (context, url) => Shimmer(color: EaselAppTheme.cardBackground, child: const SizedBox.expand()), + placeholder: (context, url) => Shimmer(color: EventlyAppTheme.kGrey01, child: const SizedBox.expand()), ); } void buildBottomSheet({required BuildContext context}) { - final bottomSheet = BuildPublishedNFTsBottomSheet(context: context, nft: nft, easelProvider: _easelProvider); - - bottomSheet.show(); - } - - String getNFTIcon() { - switch (nft.assetType) { - case kVideoText: - return SVGUtils.kSvgNftFormatVideo; - case kAudioText: - return SVGUtils.kSvgNftFormatAudio; - case kPdfText: - return SVGUtils.kSvgNftFormatPDF; - case k3dText: - return SVGUtils.kSvgNftFormat3d; - default: - return SVGUtils.kFileTypeImageIcon; - } - } - - String getNFTIconKey() { - switch (nft.assetType) { - case kVideoText: - return kNFTTypeVideoIconKey; - case kAudioText: - return kNFTTypeAudioIconKey; - case kPdfText: - return kNFTTypePdfIconKey; - case k3dText: - return kNFTType3dModelIconKey; - default: - return kNFTTypeImageIconKey; - } + // final bottomSheet = BuildPublishedNFTsBottomSheet(context: context, nft: nft, easelProvider: _easelProvider); + // + // bottomSheet.show(); } } diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index f5173ec7e3..ccfed0884b 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -74,6 +74,7 @@ class SVGUtils { static const kGridIcon = 'assets/images/svg/grid.svg'; static const kListIcon = 'assets/images/svg/list.svg'; static const kSvgPublish = 'assets/images/svg/publish.svg'; + static const kFileTypeImageIcon = 'assets/images/svg/file_type_image.svg'; } From 2ef5b9fa713450036e33ffc7ebcfa43f0e00722f Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 14:40:35 +0500 Subject: [PATCH 114/161] feat: implement evently --- .../screens/event_hub/event_hub_screen.dart | 156 +++++------------- .../event_hub/widgets/nfts_grid_view.dart | 6 +- 2 files changed, 44 insertions(+), 118 deletions(-) diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 31962dcc2b..ff5f2f3979 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -1,3 +1,4 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/main.dart'; @@ -17,6 +18,7 @@ import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:focus_detector/focus_detector.dart'; import 'package:provider/provider.dart'; +import 'package:shimmer_animation/shimmer_animation.dart'; class EventHubScreen extends StatefulWidget { const EventHubScreen({super.key}); @@ -172,18 +174,38 @@ class _EventHubContentState extends State { ), ), const SizedBox(height: 10), - BuildListView( - eventsList: viewModel.eventForDraftList, - onEmptyList: (BuildContext context) => Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Align( - alignment: Alignment.centerLeft, - child: Text( - LocaleKeys.no_nft_created.tr(), - style: subTitleStyle, - )), - ), - ), + viewModel.viewType == ViewType.viewGrid + ? Expanded( + child: BuildGridView( + eventsList: viewModel.eventForDraftList, + onEmptyList: (BuildContext context) => Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + LocaleKeys.no_nft_created.tr(), + style: subTitleStyle, + )), + ), + calculateBannerPrice: ({required String currency, required String price}) { + return ''; + }, + ), + ) + : Expanded( + child: BuildListView( + eventsList: viewModel.eventForDraftList, + onEmptyList: (BuildContext context) => Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + LocaleKeys.no_nft_created.tr(), + style: subTitleStyle, + )), + ), + ), + ), const Spacer(), Padding(padding: EdgeInsets.symmetric(horizontal: 20.w), child: getCreateEventWidget()), ], @@ -283,67 +305,6 @@ class DraftListTile extends StatefulWidget { } class _DraftListTileState extends State { - Widget getPlaceHolder() { - return Container( - margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 3.w), - decoration: BoxDecoration( - color: EventlyAppTheme.kGery03, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.3), - offset: const Offset(0.0, 1.0), - blurRadius: 4.0, - ), - ], - ), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 15.h), - child: Row( - children: [ - Container( - width: 50.0.w, - height: 50.0.w, - decoration: const BoxDecoration( - color: EventlyAppTheme.kGery03, - ), - ), - SizedBox( - width: 10.w, - ), - Expanded( - child: ListView.separated( - shrinkWrap: true, - itemBuilder: (context, index) { - return Container( - width: 30.0.w, - height: 9.0.h, - color: EventlyAppTheme.kGery03, - ); - }, - separatorBuilder: (context, index) { - return SizedBox( - height: 8.0.h, - ); - }, - itemCount: 3, - ), - ), - SizedBox( - width: 10.w, - ), - Padding( - padding: EdgeInsets.all(4.0.w), - child: SvgPicture.asset( - SVGUtils.kSvgMoreOption, - color: EventlyAppTheme.kGery03, - ), - ) - ], - ), - ), - ); - } - Widget getDraftCard() { return DecoratedBox( decoration: BoxDecoration( @@ -360,15 +321,20 @@ class _DraftListTileState extends State { padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 15.h), child: Row( children: [ - SizedBox( - width: 10.w, + CachedNetworkImage( + width: 90.w, + fit: BoxFit.contain, + imageUrl: widget.events.thumbnail, + errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), + progressIndicatorBuilder: (context, _, progress) { + return Shimmer(color: EventlyAppTheme.kGrey04, child: const SizedBox.expand()); + }, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - // "event_name".tr(args: [if (widget.events.eventName.isNotEmpty) widget.nft.name else 'Event Name']), (widget.events.eventName.isNotEmpty) ? widget.events.eventName : 'Event Name', style: titleStyle.copyWith(fontSize: isTablet ? 13.sp : 18.sp), maxLines: 1, @@ -391,9 +357,6 @@ class _DraftListTileState extends State { ], ), ), - SizedBox( - width: 10.w, - ), InkWell( onTap: () {}, child: Padding( @@ -401,9 +364,6 @@ class _DraftListTileState extends State { child: SvgPicture.asset(SVGUtils.kSvgMoreOption), ), ), - SizedBox( - width: 10.w, - ), ], ), ), @@ -431,37 +391,8 @@ class _DraftListTileState extends State { ), ], ), - child: (widget.events.price.isNotEmpty && double.parse(widget.events.price) > 0) - ? Card( - elevation: 5, - margin: EdgeInsets.zero, - child: ClipRRect( - child: Banner( - color: EventlyAppTheme.kDarkGreen, - location: BannerLocation.topEnd, - message: "\$ ${widget.events.price}", - child: getDraftCard(), - ), - ), - ) - : getDraftCard(), + child: getDraftCard(), ), - // IgnorePointer( - // child: SizedBox( - // height: 85.0.h, - // width: double.infinity, - // child: CachedNetworkImage( - // imageUrl: widget.events.thumbnail, - // fit: BoxFit.fill, - // color: EventlyAppTheme.kTransparent, - // colorBlendMode: BlendMode.clear, - // placeholder: (context, _) => getPlaceHolder(), - // errorWidget: (context, _, __) { - // return const IgnorePointer(child: SizedBox()); - // }, - // ), - // ), - // ) ], ); } @@ -511,7 +442,6 @@ class BuildGridView extends StatelessWidget { if (events.price.isNotEmpty && double.parse(events.price) > 0) { return ClipRRect( child: Banner( - color: EventlyAppTheme.kDarkGreen, location: BannerLocation.topEnd, message: calculateBannerPrice(price: events.price, currency: events.denom), diff --git a/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart b/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart index 1cc98f3383..3dcacd6d20 100644 --- a/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart +++ b/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart @@ -159,9 +159,5 @@ class NftGridViewItem extends StatelessWidget { ); } - void buildBottomSheet({required BuildContext context}) { - // final bottomSheet = BuildPublishedNFTsBottomSheet(context: context, nft: nft, easelProvider: _easelProvider); - // - // bottomSheet.show(); - } + void buildBottomSheet({required BuildContext context}) {} } From 47656336f5e7458311b3ef64b3ead3c510e93feb Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 15:49:59 +0500 Subject: [PATCH 115/161] feat: pylon events publisher --- evently/i18n/de.json | 4 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/evently_provider.dart | 2 +- evently/lib/generated/locale_keys.g.dart | 1 + evently/lib/repository/repository.dart | 51 ++++++- evently/lib/screens/create_event.dart | 2 +- .../screens/event_hub/event_hub_screen.dart | 137 +++++++++--------- .../event_hub/event_hub_view_model.dart | 6 + .../widgets/drafts_more_bottomsheet.dart | 19 +-- .../event_hub/widgets/nfts_grid_view.dart | 18 +-- .../services/datasources/cache_manager.dart | 80 ++++++++++ .../datasources/local_datasource.dart | 61 ++++++++ .../third_party_services/database.g.dart | 7 +- evently/lib/utils/constants.dart | 5 +- evently/lib/utils/di/di.config.dart | 37 ++--- evently/lib/utils/enums.dart | 10 +- .../viewmodels/create_event_viewmodel.dart | 67 ++++++++- 19 files changed, 386 insertions(+), 130 deletions(-) create mode 100644 evently/lib/services/datasources/cache_manager.dart diff --git a/evently/i18n/de.json b/evently/i18n/de.json index 3fed743e92..c1082e519e 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -61,6 +61,7 @@ "recipe_created": "Recipe созданный", "update_failed": "Upload Fehlgeschlagen", "uploading": "Hochladen ...", + "uploading": "Hochladen ...", "something_wrong_while_uploading": "Beim Hochladen ist etwas schief gelaufen. Bitte versuche es erneut.", "cookbook_not_found": "Cookbook nicht gefunden", "loading": "Wird geladen ...", @@ -71,5 +72,6 @@ "save_error": "Failed to save draft", "event_name" : "Event Name", "are_you_sure_you_want_to_delete": "Möchten Sie diesen Entwurf wirklich löschen?", - "published": "Veröffentlicht" + "published": "Veröffentlicht", + "delete": "Löschen" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index cf23ed0c46..777c9749cf 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -69,5 +69,6 @@ "delete_error": "Failed to delete draft", "event_name" : "Event Name", "are_you_sure_you_want_to_delete": "Are you sure you want to delete this draft?", - "published": "Published" + "published": "Published", + "delete": "Delete" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index 02c7a04d8a..ecbd2db646 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -70,5 +70,6 @@ "save_error": "No se pudo guardar el Plan Preliminar", "event_name" : "Event Name", "are_you_sure_you_want_to_delete": "¿Seguro que quieres eliminar este Plan Preliminar?", - "published": "Publicado" + "published": "Publicado", + "delete": "Borrar" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 7a757cef60..1c347bb983 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -70,5 +70,6 @@ "save_error": "Не удалось сохранить черновик", "event_name" : "Event Name", "are_you_sure_you_want_to_delete": "Вы уверены, что хотите удалить этот черновик?", - "published": "изданный" + "published": "изданный", + "delete": "удалять" } \ No newline at end of file diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 1f674f5ab1..d8267b6ccc 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -358,7 +358,7 @@ class EventlyProvider extends ChangeNotifier { required VoidCallback onCompleted, required UploadStep uploadStep, }) async { - final id = await repository.saveEvents(Events(step: uploadStep.toString(), eventName: eventName, hostName: hostName, thumbnail: thumbnail!)); + await repository.saveEvents(Events(step: uploadStep.toString(), eventName: eventName, hostName: hostName, thumbnail: thumbnail!)); onCompleted(); } } diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 4813e6b851..48bceccb94 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -70,5 +70,6 @@ abstract class LocaleKeys { static const save_error = 'save_error'; static const are_you_sure_you_want_to_delete = 'are_you_sure_you_want_to_delete'; static const published = 'published'; + static const delete = 'delete'; } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index a8d3bf9fc5..ef71a3076f 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -67,6 +67,29 @@ abstract class Repository { /// Input: [id] the id of the nft which the user wants to delete /// Output: [bool] returns whether the operation is successful or not Future> deleteNft(int id); + + /// This method will set the input in the cache + /// Input: [key] the key against which the value is to be set, [value] the value that is to be set. + bool setCacheDynamicType({required String key, required dynamic value}); + + /// This method will set the input in the cache + /// Input: [key] the key against which the value is to be set, [value] the value that is to be set. + void setCacheString({required String key, required String value}); + + /// This method will return the saved String if exists + /// Input: [key] the key of the value + /// Output: [String] the value of the key + String getCacheString({required String key}); + + /// This method will delete the value from the cache + /// Input: [key] the key of the value + /// Output: [value] will return the value that is just removed + String deleteCacheString({required String key}); + + /// This method will return the saved String if exists + /// Input: [key] the key of the value + /// Output: [String] the value of the key + dynamic getCacheDynamicType({required String key}); } @LazySingleton(as: Repository) @@ -169,9 +192,33 @@ class RepositoryImp implements Repository { try { final bool result = await localDataSource.deleteNft(id); return Right(result); - } on Exception catch (exception) { - + } on Exception catch (_) { return Left(CacheFailure(LocaleKeys.something_wrong.tr())); } } + + @override + bool setCacheDynamicType({required String key, required dynamic value}) { + return localDataSource.setCacheDynamicType(key: key, value: value); + } + + @override + void setCacheString({required String key, required String value}) { + localDataSource.setCacheString(key: key, value: value); + } + + @override + String getCacheString({required String key}) { + return localDataSource.getCacheString(key: key); + } + + @override + String deleteCacheString({required String key}) { + return localDataSource.deleteCacheString(key: key); + } + + @override + dynamic getCacheDynamicType({required String key}) { + return localDataSource.getCacheDynamicType(key: key); + } } diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index f0b9a1d812..2d88f8b73f 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -28,7 +28,7 @@ class _CreateEventState extends State { context.read().initStore(); }); - createEventViewModel.init(); + createEventViewModel.init(setTextField: () {}); } @override diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index ff5f2f3979..4e78b08d59 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -306,65 +306,77 @@ class DraftListTile extends StatefulWidget { class _DraftListTileState extends State { Widget getDraftCard() { - return DecoratedBox( - decoration: BoxDecoration( - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.3), - offset: const Offset(0.0, 1.0), - blurRadius: 4.0, - ), - ], - ), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 15.h), - child: Row( - children: [ - CachedNetworkImage( - width: 90.w, - fit: BoxFit.contain, - imageUrl: widget.events.thumbnail, - errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), - progressIndicatorBuilder: (context, _, progress) { - return Shimmer(color: EventlyAppTheme.kGrey04, child: const SizedBox.expand()); - }, + return InkWell( + onTap: (){ + + final DraftsBottomSheet draftsBottomSheet = DraftsBottomSheet( + buildContext: context, + events: widget.events, + ); + draftsBottomSheet.show(); + return; + + }, + child: DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + offset: const Offset(0.0, 1.0), + blurRadius: 4.0, ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - (widget.events.eventName.isNotEmpty) ? widget.events.eventName : 'Event Name', - style: titleStyle.copyWith(fontSize: isTablet ? 13.sp : 18.sp), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - SizedBox( - height: 6.h, - ), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(1.h), - color: EventlyAppTheme.kLightRed, + ], + ), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 15.h), + child: Row( + children: [ + CachedNetworkImage( + width: 90.w, + fit: BoxFit.contain, + imageUrl: widget.events.thumbnail, + errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), + progressIndicatorBuilder: (context, _, progress) { + return Shimmer(color: EventlyAppTheme.kGrey04, child: const SizedBox.expand()); + }, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + (widget.events.eventName.isNotEmpty) ? widget.events.eventName : 'Event Name', + style: titleStyle.copyWith(fontSize: isTablet ? 13.sp : 18.sp), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 3.h), - child: Text( - LocaleKeys.draft.tr(), - style: EventlyAppTheme.titleStyle.copyWith(color: EventlyAppTheme.kWhite, fontSize: isTablet ? 8.sp : 11.sp), + SizedBox( + height: 6.h, ), - ), - ], + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(1.h), + color: EventlyAppTheme.kLightRed, + ), + padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 3.h), + child: Text( + LocaleKeys.draft.tr(), + style: EventlyAppTheme.titleStyle.copyWith(color: EventlyAppTheme.kWhite, fontSize: isTablet ? 8.sp : 11.sp), + ), + ), + ], + ), ), - ), - InkWell( - onTap: () {}, - child: Padding( - padding: EdgeInsets.all(4.0.w), - child: SvgPicture.asset(SVGUtils.kSvgMoreOption), + InkWell( + onTap: () {}, + child: Padding( + padding: EdgeInsets.all(4.0.w), + child: SvgPicture.asset(SVGUtils.kSvgMoreOption), + ), ), - ), - ], + ], + ), ), ), ); @@ -439,22 +451,7 @@ class BuildGridView extends StatelessWidget { ), itemBuilder: (context, index) { final events = eventsList[index]; - if (events.price.isNotEmpty && double.parse(events.price) > 0) { - return ClipRRect( - child: Banner( - color: EventlyAppTheme.kDarkGreen, - location: BannerLocation.topEnd, - message: calculateBannerPrice(price: events.price, currency: events.denom), - child: NftGridViewItem( - events: events, - ), - ), - ); - } else { - return NftGridViewItem( - events: events, - ); - } + return NftGridViewItem(events: events); }, ), ); diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 234159f8c2..7142851f18 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/models/events.dart'; import 'package:evently/repository/repository.dart'; +import 'package:evently/utils/constants.dart'; import 'package:evently/utils/extension_util.dart'; import 'package:evently/widgets/loading_with_progress.dart'; import 'package:flutter/cupertino.dart'; @@ -142,4 +143,9 @@ class EventHubViewModel extends ChangeNotifier { eventForDraftList.removeWhere((element) => element.id == id); notifyListeners(); } + + void saveEvent({required Events events}) { + repository.setCacheDynamicType(key: eventKey, value: events); + repository.setCacheString(key: fromKey, value: kDraft); + } } diff --git a/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart b/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart index 099c96b8cf..2d47992eb7 100644 --- a/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart +++ b/evently/lib/screens/event_hub/widgets/drafts_more_bottomsheet.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; import 'package:evently/main.dart'; import 'package:evently/models/events.dart'; @@ -6,6 +7,7 @@ import 'package:evently/screens/event_hub/widgets/delete_confirmation_dialog.dar import 'package:evently/utils/constants.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/widgets/clippers/bottom_sheet_clipper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -45,18 +47,13 @@ class DraftsMoreBottomSheet extends StatelessWidget { EventlyProvider get easelProvider => sl(); - Future onViewOnIPFSPressed({required BuildContext context, required Events events}) async { - // final easelProvider = Provider.of(context, listen: false); - // await easelProvider.repository.launchMyUrl(url: nft.url.changeDomain()); - } - @override Widget build(BuildContext context) { - // final viewModel = context.watch(); + final viewModel = context.watch(); return ClipPath( clipper: BottomSheetClipper(), child: Container( - color: EventlyAppTheme.kGrey02, + color: EventlyAppTheme.kGery03, padding: EdgeInsets.symmetric(horizontal: 30.w, vertical: 30.h), child: Wrap( children: [ @@ -64,9 +61,9 @@ class DraftsMoreBottomSheet extends StatelessWidget { title: "publish", image: SVGUtils.kSvgPublish, onPressed: () { - // viewModel.saveNFT(nft: nft); - // Navigator.of(context).pop(); - // Navigator.of(context).pushNamed(RouteUtil.kRouteHome); + viewModel.saveEvent(events: events); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed(RouteUtil.kCreateEvent); }), const Divider( color: EventlyAppTheme.kGrey01, @@ -105,7 +102,7 @@ Widget moreOptionTile({required String title, required String image, required Vo width: 30.w, ), Text( - title, + title.tr(), style: titleStyle.copyWith(fontSize: 16.sp), ) ], diff --git a/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart b/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart index 3dcacd6d20..60392f70e7 100644 --- a/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart +++ b/evently/lib/screens/event_hub/widgets/nfts_grid_view.dart @@ -54,15 +54,12 @@ class NftGridViewItem extends StatelessWidget { width: 150.w, child: InkWell( onTap: () { - if (context.read().selectedCollectionType == CollectionType.draft) { - final DraftsBottomSheet draftsBottomSheet = DraftsBottomSheet( - buildContext: context, - events: events, - ); - draftsBottomSheet.show(); - return; - } - buildBottomSheet(context: context); + final DraftsBottomSheet draftsBottomSheet = DraftsBottomSheet( + buildContext: context, + events: events, + ); + draftsBottomSheet.show(); + return; }, child: CachedNetworkImage( fit: BoxFit.fitHeight, @@ -131,7 +128,6 @@ class NftGridViewItem extends StatelessWidget { draftsBottomSheet.show(); return; } - buildBottomSheet(context: context); }, child: Padding( padding: EdgeInsets.all(4.0.w), @@ -158,6 +154,4 @@ class NftGridViewItem extends StatelessWidget { placeholder: (context, url) => Shimmer(color: EventlyAppTheme.kGrey01, child: const SizedBox.expand()), ); } - - void buildBottomSheet({required BuildContext context}) {} } diff --git a/evently/lib/services/datasources/cache_manager.dart b/evently/lib/services/datasources/cache_manager.dart new file mode 100644 index 0000000000..152a51f36b --- /dev/null +++ b/evently/lib/services/datasources/cache_manager.dart @@ -0,0 +1,80 @@ +import 'package:injectable/injectable.dart'; + +abstract class CacheManager { + /// This method will return the saved String if exists + /// Input: [key] the key of the value + /// Output: [String] return the value of the key + String getString({required String key}); + + /// This method will delete the value from the cache + /// Input: [key] the key of the value + /// Output: [String] will return which value is just removed + String deleteString({required String key}); + + /// This method will set the input in the cache + /// Input: [key] the key against which the value is to be set, [value] the value that is to be set. + void setString({required String key, required String value}); + + /// This method will set the input in the cache + /// Input: [key] the key against which the value is to be set, [value] the value that is to be set. + /// Output: [bool] will tell whether its set or not. + bool setDynamicType({required String key, required dynamic value}); + + /// This method will delete the value from the cache + /// Input: [key] the key of the value + /// Output: [bool] will tell whether its removed or not. + bool deleteCacheDynamic({required String key}); + + /// This method will return the saved String if exists + /// Input: [key] the key of the value + /// Output: [String] the value of the key + dynamic getDynamicType({required String key}); +} + +@LazySingleton(as: CacheManager) +class CacheManagerImp extends CacheManager { + Map cache = {}; + + @override + String deleteString({required String key}) { + if (cache.containsKey(key)) { + return cache.remove(key) as String; + } + return ''; + } + + @override + bool deleteCacheDynamic({required String key}) { + if (cache.containsKey(key)) { + cache.remove(key); + return true; + } + return false; + } + + @override + dynamic getDynamicType({required String key}) { + if (cache.containsKey(key)) { + return cache[key]; + } + } + + @override + String getString({required String key}) { + if (cache.containsKey(key)) { + return cache[key] as String; + } + return ''; + } + + @override + bool setDynamicType({required String key, required dynamic value}) { + cache[key] = value; + return true; + } + + @override + void setString({required String key, required String value}) { + cache[key] = value; + } +} diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 0c17fe2eca..87fb13ad79 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/models/events.dart'; +import 'package:evently/services/datasources/cache_manager.dart'; import 'package:evently/services/third_party_services/database.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/date_utils.dart'; @@ -57,6 +58,34 @@ abstract class LocalDataSource { /// Input: [id] the id of the draft which the user wants to delete /// Output: [bool] returns whether the operation is successful or not Future deleteNft(int id); + + /// This method will delete the value from the cache + /// Input: [key] the key of the value + /// Output: [value] will return the value that is just removed + String deleteCacheString({required String key}); + + /// This method will set the input in the cache + /// Input: [key] the key against which the value is to be set, [value] the value that is to be set. + void setCacheString({required String key, required String value}); + + /// This method will set the input in the cache + /// Input: [key] the key against which the value is to be set, [value] the value that is to be set. + bool setCacheDynamicType({required String key, required dynamic value}); + + /// This method will return the saved String if exists + /// Input: [key] the key of the value + /// Output: [String] the value of the key + dynamic getCacheDynamicType({required String key}); + + /// This method will delete the value from the cache + /// Input: [key] the key of the value + /// Output: [value] will return the value that is just removed + dynamic deleteCacheDynamic({required String key}); + + /// This method will return the saved String if exists + /// Input: [key] the key of the value + /// Output: [String] the value of the key + String getCacheString({required String key}); } @LazySingleton(as: LocalDataSource) @@ -64,10 +93,12 @@ class LocalDataSourceImpl extends LocalDataSource { LocalDataSourceImpl({ required this.sharedPreferences, required this.database, + required this.cacheManager, }); final SharedPreferences sharedPreferences; final AppDatabase database; + final CacheManager cacheManager; /// auto generates cookbookID string and saves into local storage /// returns cookbookId @@ -153,4 +184,34 @@ class LocalDataSourceImpl extends LocalDataSource { throw CacheFailure(LocaleKeys.delete_error.tr()); } } + + @override + String deleteCacheString({required String key}) { + return cacheManager.deleteString(key: key); + } + + @override + dynamic getCacheDynamicType({required String key}) { + return cacheManager.getDynamicType(key: key); + } + + @override + String getCacheString({required String key}) { + return cacheManager.getString(key: key); + } + + @override + bool setCacheDynamicType({required String key, required dynamic value}) { + return cacheManager.setDynamicType(key: key, value: value); + } + + @override + void setCacheString({required String key, required String value}) { + cacheManager.setString(key: key, value: value); + } + + @override + void deleteCacheDynamic({required String key}) { + cacheManager.deleteCacheDynamic(key: key); + } } diff --git a/evently/lib/services/third_party_services/database.g.dart b/evently/lib/services/third_party_services/database.g.dart index 8cecdbea4f..d4a454e287 100644 --- a/evently/lib/services/third_party_services/database.g.dart +++ b/evently/lib/services/third_party_services/database.g.dart @@ -96,7 +96,7 @@ class _$AppDatabase extends AppDatabase { }, onCreate: (database, version) async { await database.execute( - 'CREATE TABLE IF NOT EXISTS `Events` (`id` INTEGER, `recipeID` TEXT NOT NULL, `eventName` TEXT NOT NULL, `hostName` TEXT NOT NULL, `thumbnail` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `startTime` TEXT NOT NULL, `endTime` TEXT NOT NULL, `location` TEXT NOT NULL, `description` TEXT NOT NULL, `numberOfTickets` TEXT NOT NULL, `price` TEXT NOT NULL, `listOfPerks` TEXT NOT NULL, `isFreeDrops` TEXT NOT NULL, `cookbookID` TEXT NOT NULL, `step` TEXT NOT NULL, PRIMARY KEY (`id`))'); + 'CREATE TABLE IF NOT EXISTS `Events` (`id` INTEGER, `recipeID` TEXT NOT NULL, `eventName` TEXT NOT NULL, `hostName` TEXT NOT NULL, `thumbnail` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `startTime` TEXT NOT NULL, `endTime` TEXT NOT NULL, `location` TEXT NOT NULL, `description` TEXT NOT NULL, `numberOfTickets` TEXT NOT NULL, `price` TEXT NOT NULL, `listOfPerks` TEXT NOT NULL, `isFreeDrops` TEXT NOT NULL, `cookbookID` TEXT NOT NULL, `step` TEXT NOT NULL, `denom` TEXT NOT NULL, PRIMARY KEY (`id`))'); await callback?.onCreate?.call(database, version); }, @@ -135,7 +135,8 @@ class _$EventsDao extends EventsDao { 'listOfPerks': item.listOfPerks, 'isFreeDrops': item.isFreeDrops, 'cookbookID': item.cookbookID, - 'step': item.step + 'step': item.step, + 'denom': item.denom }); final sqflite.DatabaseExecutor database; @@ -164,6 +165,7 @@ class _$EventsDao extends EventsDao { numberOfTickets: row['numberOfTickets'] as String, price: row['price'] as String, isFreeDrops: row['isFreeDrops'] as String, + denom: row['denom'] as String, cookbookID: row['cookbookID'] as String, recipeID: row['recipeID'] as String, step: row['step'] as String)); @@ -187,6 +189,7 @@ class _$EventsDao extends EventsDao { numberOfTickets: row['numberOfTickets'] as String, price: row['price'] as String, isFreeDrops: row['isFreeDrops'] as String, + denom: row['denom'] as String, cookbookID: row['cookbookID'] as String, recipeID: row['recipeID'] as String, step: row['step'] as String), diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index ccfed0884b..eec19a237c 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -58,6 +58,9 @@ const kErrRecipe = 'Recipe error :'; /// ```URL constants const ipfsDomain = 'https://ipfs.io/ipfs'; +const String fromKey = 'from'; +const String eventKey = 'event'; + /// ```SVG assets class SVGUtils { static const kSvgSplash = 'assets/images/svg/splash.svg'; @@ -75,8 +78,6 @@ class SVGUtils { static const kListIcon = 'assets/images/svg/list.svg'; static const kSvgPublish = 'assets/images/svg/publish.svg'; static const kFileTypeImageIcon = 'assets/images/svg/file_type_image.svg'; - - } /// ```PNG assets diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index c987c79dd8..78bf584ff1 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -8,20 +8,21 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:dio/dio.dart' as _i6; +import 'package:dio/dio.dart' as _i5; import 'package:evently/evently_provider.dart' as _i14; import 'package:evently/repository/repository.dart' as _i13; -import 'package:evently/screens/event_hub/event_hub_view_model.dart' as _i15; +import 'package:evently/screens/event_hub/event_hub_view_model.dart' as _i16; +import 'package:evently/services/datasources/cache_manager.dart' as _i6; import 'package:evently/services/datasources/local_datasource.dart' as _i9; import 'package:evently/services/datasources/remote_datasource.dart' as _i12; import 'package:evently/services/third_party_services/database.dart' as _i11; import 'package:evently/services/third_party_services/quick_node.dart' as _i8; -import 'package:evently/utils/di/register_modules.dart' as _i16; +import 'package:evently/utils/di/register_modules.dart' as _i17; import 'package:evently/utils/file_utils_helper.dart' as _i7; -import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; -import 'package:file_picker/file_picker.dart' as _i5; +import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i15; +import 'package:file_picker/file_picker.dart' as _i4; import 'package:get_it/get_it.dart' as _i1; -import 'package:image_cropper/image_cropper.dart' as _i4; +import 'package:image_cropper/image_cropper.dart' as _i3; import 'package:injectable/injectable.dart' as _i2; import 'package:shared_preferences/shared_preferences.dart' as _i10; @@ -37,20 +38,20 @@ extension GetItInjectableX on _i1.GetIt { environmentFilter, ); final registerModule = _$RegisterModule(); - gh.lazySingleton<_i3.CreateEventViewModel>( - () => _i3.CreateEventViewModel()); - gh.lazySingleton<_i4.ImageCropper>(() => registerModule.imageCropper); - gh.lazySingleton<_i5.FilePicker>(() => registerModule.filePicker); - gh.lazySingleton<_i6.Dio>(() => registerModule.dio); + gh.lazySingleton<_i3.ImageCropper>(() => registerModule.imageCropper); + gh.lazySingleton<_i4.FilePicker>(() => registerModule.filePicker); + gh.lazySingleton<_i5.Dio>(() => registerModule.dio); + gh.lazySingleton<_i6.CacheManager>(() => _i6.CacheManagerImp()); gh.lazySingleton<_i7.FileUtilsHelper>(() => _i7.FileUtilsHelperImpl( - imageCropper: gh<_i4.ImageCropper>(), - filePicker: gh<_i5.FilePicker>(), + imageCropper: gh<_i3.ImageCropper>(), + filePicker: gh<_i4.FilePicker>(), )); gh.lazySingleton<_i8.QuickNode>( - () => _i8.QuickNodeImpl(httpClient: gh<_i6.Dio>())); + () => _i8.QuickNodeImpl(httpClient: gh<_i5.Dio>())); gh.lazySingleton<_i9.LocalDataSource>(() => _i9.LocalDataSourceImpl( sharedPreferences: gh<_i10.SharedPreferences>(), database: gh<_i11.AppDatabase>(), + cacheManager: gh<_i6.CacheManager>(), )); gh.lazySingleton<_i12.RemoteDataSource>( () => _i12.RemoteDataSourceImpl(quickNode: gh<_i8.QuickNode>())); @@ -61,10 +62,12 @@ extension GetItInjectableX on _i1.GetIt { )); gh.lazySingleton<_i14.EventlyProvider>( () => _i14.EventlyProvider(repository: gh<_i13.Repository>())); - gh.lazySingleton<_i15.EventHubViewModel>( - () => _i15.EventHubViewModel(gh<_i13.Repository>())); + gh.lazySingleton<_i15.CreateEventViewModel>( + () => _i15.CreateEventViewModel(gh<_i13.Repository>())); + gh.lazySingleton<_i16.EventHubViewModel>( + () => _i16.EventHubViewModel(gh<_i13.Repository>())); return this; } } -class _$RegisterModule extends _i16.RegisterModule {} +class _$RegisterModule extends _i17.RegisterModule {} diff --git a/evently/lib/utils/enums.dart b/evently/lib/utils/enums.dart index 15f439515d..0ac17bcff8 100644 --- a/evently/lib/utils/enums.dart +++ b/evently/lib/utils/enums.dart @@ -1 +1,9 @@ -enum UploadStep { overView, detail, perks, price } +enum UploadStep { overView, detail, perks, price, none } + +extension ToUploadStepPar on String { + UploadStep toUploadStepEnum() { + return UploadStep.values.firstWhere((e) { + return e.toString().toLowerCase() == 'UploadStep.$this'.toLowerCase(); + }, orElse: () => UploadStep.none); //return null if not found + } +} diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart index e213efb59b..2c72670e02 100644 --- a/evently/lib/viewmodels/create_event_viewmodel.dart +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -1,12 +1,17 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; +import 'package:evently/models/events.dart'; +import 'package:evently/repository/repository.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/enums.dart'; import 'package:flutter/material.dart'; import 'package:injectable/injectable.dart'; @lazySingleton class CreateEventViewModel extends ChangeNotifier { - CreateEventViewModel(); + final Repository repository; + + CreateEventViewModel(this.repository); late ValueNotifier currentPage; late ValueNotifier currentStep; @@ -20,10 +25,60 @@ class CreateEventViewModel extends ChangeNotifier { LocaleKeys.price.tr(), ]; - void init() { - currentPage = ValueNotifier(0); - currentStep = ValueNotifier(0); - pageController = PageController(); + Events? events; + + void init({required VoidCallback setTextField}) { + from = repository.getCacheString(key: fromKey); + repository.deleteCacheString(key: fromKey); + + if (from == kDraft) { + events = repository.getCacheDynamicType(key: eventKey) as Events; + + Future.delayed(const Duration(milliseconds: 1), () { + setTextField.call(); + }); + + Future.delayed(const Duration(milliseconds: 1), () { + setTextField.call(); + }); + + final uploadStep = events!.step.toUploadStepEnum(); + + switch (uploadStep) { + case UploadStep.overView: + currentPage = ValueNotifier(1); + currentStep = ValueNotifier(1); + pageController = PageController(initialPage: 1); + return; + case UploadStep.detail: + currentPage = ValueNotifier(2); + currentStep = ValueNotifier(2); + pageController = PageController(initialPage: 2); + return; + + case UploadStep.perks: + currentPage = ValueNotifier(3); + currentStep = ValueNotifier(3); + pageController = PageController(initialPage: 3); + return; + + case UploadStep.price: + currentPage = ValueNotifier(3); + currentStep = ValueNotifier(3); + pageController = PageController(initialPage: 3); + return; + + case UploadStep.none: + currentPage = ValueNotifier(0); + currentStep = ValueNotifier(0); + pageController = PageController(initialPage: 0); + return; + } + } else { + currentPage = ValueNotifier(0); + currentStep = ValueNotifier(0); + pageController = PageController(); + } } Future nextPage() async { @@ -41,6 +96,4 @@ class CreateEventViewModel extends ChangeNotifier { void disposeControllers() { pageController.dispose(); } - - } From efd3808c5716a28f59e87d7834a6d2ef416005a9 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 16:15:14 +0500 Subject: [PATCH 116/161] feat: database is not registered error --- evently/lib/main.dart | 4 +++- evently/lib/screens/event_hub/event_hub_screen.dart | 4 +--- evently/lib/screens/event_hub/event_hub_view_model.dart | 4 ++-- evently/lib/utils/di/di.dart | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 55b0e9ac72..2d9ea8ad0b 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -4,14 +4,15 @@ import 'package:evently/evently_provider.dart'; import 'package:evently/screens/buyer_status_screen.dart'; import 'package:evently/screens/create_event.dart'; import 'package:evently/screens/event_hub/event_hub_screen.dart'; -import 'package:evently/screens/host_view_ticket_preview.dart'; import 'package:evently/screens/splash_screen.dart'; +import 'package:evently/services/third_party_services/database.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/route_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get_it/get_it.dart'; import 'package:provider/provider.dart'; import 'package:pylons_sdk/pylons_sdk.dart'; @@ -25,6 +26,7 @@ void main() async { await EasyLocalization.ensureInitialized(); configureDependencies(); + await GetIt.I.isReady(); isTablet = _getIsCurrentDeviceTablet(); diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 4e78b08d59..33151b8124 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -307,15 +307,13 @@ class DraftListTile extends StatefulWidget { class _DraftListTileState extends State { Widget getDraftCard() { return InkWell( - onTap: (){ - + onTap: () { final DraftsBottomSheet draftsBottomSheet = DraftsBottomSheet( buildContext: context, events: widget.events, ); draftsBottomSheet.show(); return; - }, child: DecoratedBox( decoration: BoxDecoration( diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 7142851f18..3073068c6d 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -36,8 +36,8 @@ class EventHubViewModel extends ChangeNotifier { List get eventForSaleList => _eventForDraftList; - set setEventForDraftList(List nftForSale) { - _eventForDraftList = nftForSale; + set setEventForDraftList(List eventDraftList) { + _eventForDraftList = eventDraftList; notifyListeners(); } diff --git a/evently/lib/utils/di/di.dart b/evently/lib/utils/di/di.dart index 7e4d6d57fa..b44750f727 100644 --- a/evently/lib/utils/di/di.dart +++ b/evently/lib/utils/di/di.dart @@ -13,7 +13,7 @@ final sl = GetIt.I; // initialization of Service locator @InjectableInit() void configureDependencies() { - sl.registerSingletonAsync(() => SharedPreferences.getInstance()); - sl.registerSingletonAsync(() => $FloorAppDatabase.databaseBuilder('app_database.db').build()); - sl.init(); + GetIt.I.registerSingletonAsync(() => SharedPreferences.getInstance()); + GetIt.I.registerSingletonAsync(() => $FloorAppDatabase.databaseBuilder('app_database.db').build()); + GetIt.I.init(); } From cefc426b3d22318f5b7662860886931b13a0c5e6 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 16:45:58 +0500 Subject: [PATCH 117/161] feat: set draft values --- evently/lib/models/events.dart | 8 ++-- evently/lib/screens/create_event.dart | 9 ++++- .../screens/event_hub/event_hub_screen.dart | 10 ++--- .../third_party_services/database.g.dart | 40 +++++-------------- evently/lib/utils/enums.dart | 2 +- .../viewmodels/create_event_viewmodel.dart | 3 -- 6 files changed, 28 insertions(+), 44 deletions(-) diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index a12ee9ab0a..20b82aa7cb 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -55,8 +55,9 @@ class Events extends Equatable { ///* other this.cookbookID = '', this.recipeID = '', - required this.step, + ///* for tracking where its save as draft + this.step = '', }); factory Events.fromRecipe(Recipe recipe) { @@ -108,11 +109,12 @@ class Events extends Equatable { } @override - List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price, listOfPerks, isFreeDrops, cookbookID, recipeID]; + List get props => + [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price, listOfPerks, isFreeDrops, cookbookID, recipeID, step]; @override String toString() { - return 'Event{eventName: $eventName, hostName: $hostName, thumbnail: $thumbnail, startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime, location: $location, description: $description, numberOfTickets: $numberOfTickets, price: $price, tradePercentage: $listOfPerks, isFreeDrop: $isFreeDrops, cookbookID: $cookbookID, recipeID: $recipeID}'; + return 'Event{eventName: $eventName, hostName: $hostName, thumbnail: $thumbnail, startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime, location: $location, description: $description, numberOfTickets: $numberOfTickets, price: $price, tradePercentage: $listOfPerks, isFreeDrop: $isFreeDrops, cookbookID: $cookbookID, recipeID: $recipeID, step: $step}'; } } diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 2d88f8b73f..4c13785a05 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -28,7 +28,14 @@ class _CreateEventState extends State { context.read().initStore(); }); - createEventViewModel.init(setTextField: () {}); + createEventViewModel.init(setTextField: () { + + + + + + + }); } @override diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 33151b8124..57c68d93d3 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -197,12 +197,10 @@ class _EventHubContentState extends State { eventsList: viewModel.eventForDraftList, onEmptyList: (BuildContext context) => Padding( padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Align( - alignment: Alignment.centerLeft, - child: Text( - LocaleKeys.no_nft_created.tr(), - style: subTitleStyle, - )), + child: Text( + LocaleKeys.no_nft_created.tr(), + style: subTitleStyle, + ), ), ), ), diff --git a/evently/lib/services/third_party_services/database.g.dart b/evently/lib/services/third_party_services/database.g.dart index d4a454e287..3ae7cff4fa 100644 --- a/evently/lib/services/third_party_services/database.g.dart +++ b/evently/lib/services/third_party_services/database.g.dart @@ -21,14 +21,12 @@ abstract class $AppDatabaseBuilderContract { class $FloorAppDatabase { /// Creates a database builder for a persistent database. /// Once a database is built, you should keep a reference to it and re-use it. - static $AppDatabaseBuilderContract databaseBuilder(String name) => - _$AppDatabaseBuilder(name); + static $AppDatabaseBuilderContract databaseBuilder(String name) => _$AppDatabaseBuilder(name); /// Creates a database builder for an in memory database. /// Information stored in an in memory database disappears when the process is killed. /// Once a database is built, you should keep a reference to it and re-use it. - static $AppDatabaseBuilderContract inMemoryDatabaseBuilder() => - _$AppDatabaseBuilder(null); + static $AppDatabaseBuilderContract inMemoryDatabaseBuilder() => _$AppDatabaseBuilder(null); } class _$AppDatabaseBuilder implements $AppDatabaseBuilderContract { @@ -54,9 +52,7 @@ class _$AppDatabaseBuilder implements $AppDatabaseBuilderContract { @override Future build() async { - final path = name != null - ? await sqfliteDatabaseFactory.getDatabasePath(name!) - : ':memory:'; + final path = name != null ? await sqfliteDatabaseFactory.getDatabasePath(name!) : ':memory:'; final database = _$AppDatabase(); database.database = await database.open( path, @@ -89,8 +85,7 @@ class _$AppDatabase extends AppDatabase { await callback?.onOpen?.call(database); }, onUpgrade: (database, startVersion, endVersion) async { - await MigrationAdapter.runMigrations( - database, startVersion, endVersion, migrations); + await MigrationAdapter.runMigrations(database, startVersion, endVersion, migrations); await callback?.onUpgrade?.call(database, startVersion, endVersion); }, @@ -198,8 +193,7 @@ class _$EventsDao extends EventsDao { @override Future delete(int id) async { - await _queryAdapter - .queryNoReturn('DELETE FROM events WHERE id = ?1', arguments: [id]); + await _queryAdapter.queryNoReturn('DELETE FROM events WHERE id = ?1', arguments: [id]); } @override @@ -212,17 +206,8 @@ class _$EventsDao extends EventsDao { String description, int id, ) async { - await _queryAdapter.queryNoReturn( - 'UPDATE events SET startDate = ?1, endDate= ?2, startTime = ?3, endTime = ?4,location = ?5, description = ?6 WHERE id = ?7', - arguments: [ - startDate, - endDate, - startTime, - endTime, - location, - description, - id - ]); + await _queryAdapter.queryNoReturn('UPDATE events SET startDate = ?1, endDate= ?2, startTime = ?3, endTime = ?4,location = ?5, description = ?6 WHERE id = ?7', + arguments: [startDate, endDate, startTime, endTime, location, description, id]); } @override @@ -230,9 +215,7 @@ class _$EventsDao extends EventsDao { String listOfPerks, int id, ) async { - await _queryAdapter.queryNoReturn( - 'UPDATE events SET listOfPerks = ?1 WHERE id = ?2', - arguments: [listOfPerks, id]); + await _queryAdapter.queryNoReturn('UPDATE events SET listOfPerks = ?1 WHERE id = ?2', arguments: [listOfPerks, id]); } @override @@ -242,14 +225,11 @@ class _$EventsDao extends EventsDao { String price, String isFreeDrops, ) async { - await _queryAdapter.queryNoReturn( - 'UPDATE events SET numberOfTickets = ?2, price = ?3, isFreeDrops = ?4 WHERE id = ?1', - arguments: [id, numberOfTickets, price, isFreeDrops]); + await _queryAdapter.queryNoReturn('UPDATE events SET numberOfTickets = ?2, price = ?3, isFreeDrops = ?4 WHERE id = ?1', arguments: [id, numberOfTickets, price, isFreeDrops]); } @override Future insertEvents(Events events) { - return _eventsInsertionAdapter.insertAndReturnId( - events, OnConflictStrategy.abort); + return _eventsInsertionAdapter.insertAndReturnId(events, OnConflictStrategy.abort); } } diff --git a/evently/lib/utils/enums.dart b/evently/lib/utils/enums.dart index 0ac17bcff8..255631625c 100644 --- a/evently/lib/utils/enums.dart +++ b/evently/lib/utils/enums.dart @@ -3,7 +3,7 @@ enum UploadStep { overView, detail, perks, price, none } extension ToUploadStepPar on String { UploadStep toUploadStepEnum() { return UploadStep.values.firstWhere((e) { - return e.toString().toLowerCase() == 'UploadStep.$this'.toLowerCase(); + return e.toString().toLowerCase() == toLowerCase(); }, orElse: () => UploadStep.none); //return null if not found } } diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart index 2c72670e02..3f4e3d4f43 100644 --- a/evently/lib/viewmodels/create_event_viewmodel.dart +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -34,9 +34,6 @@ class CreateEventViewModel extends ChangeNotifier { if (from == kDraft) { events = repository.getCacheDynamicType(key: eventKey) as Events; - Future.delayed(const Duration(milliseconds: 1), () { - setTextField.call(); - }); Future.delayed(const Duration(milliseconds: 1), () { setTextField.call(); From 7ff8fb2704f0c7ad83bdd9881362d39b7e31a555 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 16:51:46 +0500 Subject: [PATCH 118/161] feat: create event with overView screen data --- evently/lib/screens/create_event.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 4c13785a05..b50f5fc41e 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -18,10 +18,12 @@ class CreateEvent extends StatefulWidget { } class _CreateEventState extends State { + late EventlyProvider eventlyProvider; CreateEventViewModel createEventViewModel = sl(); @override void initState() { + eventlyProvider = Provider.of(context, listen: false); super.initState(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { @@ -29,12 +31,9 @@ class _CreateEventState extends State { }); createEventViewModel.init(setTextField: () { - - - - - - + eventlyProvider.setEventName = createEventViewModel.events!.eventName; + eventlyProvider.setHostName = createEventViewModel.events!.hostName; + eventlyProvider.setThumbnail = createEventViewModel.events!.thumbnail; }); } From bb29843e4c9efbe4db30ea6e1ce787e5c49cb0a4 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 17:53:02 +0500 Subject: [PATCH 119/161] feat: save draft functionality upto details --- evently/lib/evently_provider.dart | 35 +++++++++++++++- evently/lib/repository/repository.dart | 15 +++++++ evently/lib/screens/create_event.dart | 6 +++ evently/lib/screens/detail_screen.dart | 10 ++++- .../datasources/local_datasource.dart | 22 ++++++++++ .../third_party_services/database.g.dart | 42 ++++++++++++++----- .../third_party_services/event_dao.dart | 3 +- 7 files changed, 120 insertions(+), 13 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index d8267b6ccc..0dcec1ace6 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -83,6 +83,13 @@ class EventlyProvider extends ChangeNotifier { String get location => _location; String get description => _description; + int? _id; + set setId(int id) { + _id = id; + } + + get id => _id; + set setStartDate(String value) { _startDate = value; notifyListeners(); @@ -358,7 +365,33 @@ class EventlyProvider extends ChangeNotifier { required VoidCallback onCompleted, required UploadStep uploadStep, }) async { - await repository.saveEvents(Events(step: uploadStep.toString(), eventName: eventName, hostName: hostName, thumbnail: thumbnail!)); + switch (uploadStep) { + case UploadStep.overView: + await repository.saveEvents(Events(step: uploadStep.toString(), eventName: eventName, hostName: hostName, thumbnail: thumbnail!)); + break; + case UploadStep.detail: + await repository.saveFromDetail(Events( + id: 1, + step: uploadStep.toString(), + eventName: eventName, + hostName: hostName, + thumbnail: thumbnail!, + startDate: startDate, + endDate: endDate, + startTime: startTime, + endTime: endTime, + location: location, + description: description, + )); + break; + case UploadStep.perks: + // TODO: Handle this case. + case UploadStep.price: + // TODO: Handle this case. + case UploadStep.none: + // TODO: Handle this case. + } + onCompleted(); } } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index ef71a3076f..214dfbe9f9 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -90,6 +90,8 @@ abstract class Repository { /// Input: [key] the key of the value /// Output: [String] the value of the key dynamic getCacheDynamicType({required String key}); + + Future> saveFromDetail(Events events); } @LazySingleton(as: Repository) @@ -221,4 +223,17 @@ class RepositoryImp implements Repository { dynamic getCacheDynamicType({required String key}) { return localDataSource.getCacheDynamicType(key: key); } + + @override + Future> saveFromDetail(Events events) async { + try { + final bool result = await localDataSource.saveEventFromDetail(events: events); + if (!result) { + return Left(CacheFailure(LocaleKeys.get_error.tr())); + } + return Right(result); + } on Exception catch (_) { + return Left(CacheFailure(LocaleKeys.get_error.tr())); + } + } } diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index b50f5fc41e..0a95327c00 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -34,6 +34,12 @@ class _CreateEventState extends State { eventlyProvider.setEventName = createEventViewModel.events!.eventName; eventlyProvider.setHostName = createEventViewModel.events!.hostName; eventlyProvider.setThumbnail = createEventViewModel.events!.thumbnail; + eventlyProvider.setStartDate = createEventViewModel.events!.startDate; + eventlyProvider.setEndDate = createEventViewModel.events!.endDate; + eventlyProvider.setStartTime = createEventViewModel.events!.startTime; + eventlyProvider.setEndTime = createEventViewModel.events!.endTime; + eventlyProvider.setLocation = createEventViewModel.events!.location; + eventlyProvider.setDescription = createEventViewModel.events!.description; }); } diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 8f6cb89e65..60a3631eb7 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -5,6 +5,8 @@ import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; import 'package:evently/screens/custom_widgets/steps_indicator.dart'; +import 'package:evently/utils/enums.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/evently_text_field.dart'; @@ -162,7 +164,13 @@ class _DetailsScreenState extends State { onPressContinue: () { createEventViewModel.nextPage(); }, - onPressSaveDraft: () {}, + onPressSaveDraft: () { + final navigator = Navigator.of(context); + provider.saveAsDraft( + onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), + uploadStep: UploadStep.detail, + ); + }, isContinueEnable: provider.startDate.isNotEmpty && provider.endDate.isNotEmpty && provider.startTime.isNotEmpty && diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 87fb13ad79..16c1f7576b 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -86,6 +86,9 @@ abstract class LocalDataSource { /// Input: [key] the key of the value /// Output: [String] the value of the key String getCacheString({required String key}); + + /// this method is used to save draft from + Future saveEventFromDetail({required Events events}); } @LazySingleton(as: LocalDataSource) @@ -214,4 +217,23 @@ class LocalDataSourceImpl extends LocalDataSource { void deleteCacheDynamic({required String key}) { cacheManager.deleteCacheDynamic(key: key); } + + @override + Future saveEventFromDetail({required Events events}) async { + try { + await database.eventsDao.updateNFTFromDetail( + events.startDate, + events.endDate, + events.startTime, + events.endTime, + events.location, + events.description, + events.id!, + events.step, + ); + return true; + } catch (e) { + throw CacheFailure(LocaleKeys.get_error.tr()); + } + } } diff --git a/evently/lib/services/third_party_services/database.g.dart b/evently/lib/services/third_party_services/database.g.dart index 3ae7cff4fa..6a349f3fe3 100644 --- a/evently/lib/services/third_party_services/database.g.dart +++ b/evently/lib/services/third_party_services/database.g.dart @@ -21,12 +21,14 @@ abstract class $AppDatabaseBuilderContract { class $FloorAppDatabase { /// Creates a database builder for a persistent database. /// Once a database is built, you should keep a reference to it and re-use it. - static $AppDatabaseBuilderContract databaseBuilder(String name) => _$AppDatabaseBuilder(name); + static $AppDatabaseBuilderContract databaseBuilder(String name) => + _$AppDatabaseBuilder(name); /// Creates a database builder for an in memory database. /// Information stored in an in memory database disappears when the process is killed. /// Once a database is built, you should keep a reference to it and re-use it. - static $AppDatabaseBuilderContract inMemoryDatabaseBuilder() => _$AppDatabaseBuilder(null); + static $AppDatabaseBuilderContract inMemoryDatabaseBuilder() => + _$AppDatabaseBuilder(null); } class _$AppDatabaseBuilder implements $AppDatabaseBuilderContract { @@ -52,7 +54,9 @@ class _$AppDatabaseBuilder implements $AppDatabaseBuilderContract { @override Future build() async { - final path = name != null ? await sqfliteDatabaseFactory.getDatabasePath(name!) : ':memory:'; + final path = name != null + ? await sqfliteDatabaseFactory.getDatabasePath(name!) + : ':memory:'; final database = _$AppDatabase(); database.database = await database.open( path, @@ -85,7 +89,8 @@ class _$AppDatabase extends AppDatabase { await callback?.onOpen?.call(database); }, onUpgrade: (database, startVersion, endVersion) async { - await MigrationAdapter.runMigrations(database, startVersion, endVersion, migrations); + await MigrationAdapter.runMigrations( + database, startVersion, endVersion, migrations); await callback?.onUpgrade?.call(database, startVersion, endVersion); }, @@ -193,7 +198,8 @@ class _$EventsDao extends EventsDao { @override Future delete(int id) async { - await _queryAdapter.queryNoReturn('DELETE FROM events WHERE id = ?1', arguments: [id]); + await _queryAdapter + .queryNoReturn('DELETE FROM events WHERE id = ?1', arguments: [id]); } @override @@ -205,9 +211,20 @@ class _$EventsDao extends EventsDao { String location, String description, int id, + String step, ) async { - await _queryAdapter.queryNoReturn('UPDATE events SET startDate = ?1, endDate= ?2, startTime = ?3, endTime = ?4,location = ?5, description = ?6 WHERE id = ?7', - arguments: [startDate, endDate, startTime, endTime, location, description, id]); + await _queryAdapter.queryNoReturn( + 'UPDATE events SET startDate = ?1, endDate= ?2, startTime = ?3, endTime = ?4,location = ?5, description = ?6, step = ?8 WHERE id = ?7', + arguments: [ + startDate, + endDate, + startTime, + endTime, + location, + description, + id, + step + ]); } @override @@ -215,7 +232,9 @@ class _$EventsDao extends EventsDao { String listOfPerks, int id, ) async { - await _queryAdapter.queryNoReturn('UPDATE events SET listOfPerks = ?1 WHERE id = ?2', arguments: [listOfPerks, id]); + await _queryAdapter.queryNoReturn( + 'UPDATE events SET listOfPerks = ?1 WHERE id = ?2', + arguments: [listOfPerks, id]); } @override @@ -225,11 +244,14 @@ class _$EventsDao extends EventsDao { String price, String isFreeDrops, ) async { - await _queryAdapter.queryNoReturn('UPDATE events SET numberOfTickets = ?2, price = ?3, isFreeDrops = ?4 WHERE id = ?1', arguments: [id, numberOfTickets, price, isFreeDrops]); + await _queryAdapter.queryNoReturn( + 'UPDATE events SET numberOfTickets = ?2, price = ?3, isFreeDrops = ?4 WHERE id = ?1', + arguments: [id, numberOfTickets, price, isFreeDrops]); } @override Future insertEvents(Events events) { - return _eventsInsertionAdapter.insertAndReturnId(events, OnConflictStrategy.abort); + return _eventsInsertionAdapter.insertAndReturnId( + events, OnConflictStrategy.abort); } } diff --git a/evently/lib/services/third_party_services/event_dao.dart b/evently/lib/services/third_party_services/event_dao.dart index 95bbcfe26e..33f424672b 100644 --- a/evently/lib/services/third_party_services/event_dao.dart +++ b/evently/lib/services/third_party_services/event_dao.dart @@ -15,7 +15,7 @@ abstract class EventsDao { @Query('DELETE FROM events WHERE id = :id') Future delete(int id); - @Query('UPDATE events SET startDate = :startDate, endDate= :endDate, startTime = :startTime, endTime = :endTime,location = :location, description = :description WHERE id = :id') + @Query('UPDATE events SET startDate = :startDate, endDate= :endDate, startTime = :startTime, endTime = :endTime,location = :location, description = :description, step = :step WHERE id = :id') Future updateNFTFromDetail( String startDate, String endDate, @@ -24,6 +24,7 @@ abstract class EventsDao { String location, String description, int id, + String step, ); @Query('UPDATE events SET listOfPerks = :listOfPerks WHERE id = :id') From d253f8b06134a9f9c78655c072f6f32fdc35e80b Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 20 May 2024 17:53:13 +0500 Subject: [PATCH 120/161] feat: save draft functionality upto details --- evently/lib/evently_provider.dart | 2 +- evently/lib/screens/create_event.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 0dcec1ace6..6794425810 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -371,7 +371,7 @@ class EventlyProvider extends ChangeNotifier { break; case UploadStep.detail: await repository.saveFromDetail(Events( - id: 1, + id: id, step: uploadStep.toString(), eventName: eventName, hostName: hostName, diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 0a95327c00..12a2212557 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -40,6 +40,7 @@ class _CreateEventState extends State { eventlyProvider.setEndTime = createEventViewModel.events!.endTime; eventlyProvider.setLocation = createEventViewModel.events!.location; eventlyProvider.setDescription = createEventViewModel.events!.description; + eventlyProvider.setId = createEventViewModel.events!.id!; }); } From 448ad648d66a42357a276f5d7f716c209a3c2275 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 21 May 2024 12:02:41 +0500 Subject: [PATCH 121/161] feat: draft functional upto price --- evently/lib/evently_provider.dart | 26 +++++++++---------- evently/lib/repository/repository.dart | 15 +++++++++++ evently/lib/screens/perks_screen.dart | 10 ++++++- .../datasources/local_datasource.dart | 15 ++++++++++- .../third_party_services/database.g.dart | 5 ++-- .../third_party_services/event_dao.dart | 3 ++- .../viewmodels/create_event_viewmodel.dart | 7 +++-- 7 files changed, 59 insertions(+), 22 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 6794425810..7fdf072753 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -371,21 +371,21 @@ class EventlyProvider extends ChangeNotifier { break; case UploadStep.detail: await repository.saveFromDetail(Events( - id: id, - step: uploadStep.toString(), - eventName: eventName, - hostName: hostName, - thumbnail: thumbnail!, - startDate: startDate, - endDate: endDate, - startTime: startTime, - endTime: endTime, - location: location, - description: description, - )); + id: id, + step: uploadStep.toString(), + eventName: eventName, + hostName: hostName, + thumbnail: thumbnail!, + startDate: startDate, + endDate: endDate, + startTime: startTime, + endTime: endTime, + location: location, + description: description)); break; case UploadStep.perks: - // TODO: Handle this case. + await repository.saveFromPerks(Events(id: id, listOfPerks: perks.join(','), step: uploadStep.toString())); + break; case UploadStep.price: // TODO: Handle this case. case UploadStep.none: diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 214dfbe9f9..22d1553296 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -92,6 +92,8 @@ abstract class Repository { dynamic getCacheDynamicType({required String key}); Future> saveFromDetail(Events events); + + Future> saveFromPerks(Events events); } @LazySingleton(as: Repository) @@ -236,4 +238,17 @@ class RepositoryImp implements Repository { return Left(CacheFailure(LocaleKeys.get_error.tr())); } } + + @override + Future> saveFromPerks(Events events) async { + try { + final bool result = await localDataSource.saveEventFromPerks(events: events); + if (!result) { + return Left(CacheFailure(LocaleKeys.get_error.tr())); + } + return Right(result); + } on Exception catch (_) { + return Left(CacheFailure(LocaleKeys.get_error.tr())); + } + } } diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index 097aed9675..03f14088de 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -7,7 +7,9 @@ import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; import 'package:evently/screens/custom_widgets/steps_indicator.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/enums.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/screen_responsive.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; @@ -114,7 +116,13 @@ class _PerksScreenState extends State { onPressContinue: () { createEventViewModel.nextPage(); }, - onPressSaveDraft: () {}, + onPressSaveDraft: () { + final navigator = Navigator.of(context); + provider.saveAsDraft( + onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), + uploadStep: UploadStep.perks, + ); + }, isContinueEnable: true, ), ) diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 16c1f7576b..d0346a6ccc 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -87,8 +87,11 @@ abstract class LocalDataSource { /// Output: [String] the value of the key String getCacheString({required String key}); - /// this method is used to save draft from + /// this method is used to save draft from details screen Future saveEventFromDetail({required Events events}); + + /// this method is used to save draft from details screen + Future saveEventFromPerks({required Events events}); } @LazySingleton(as: LocalDataSource) @@ -236,4 +239,14 @@ class LocalDataSourceImpl extends LocalDataSource { throw CacheFailure(LocaleKeys.get_error.tr()); } } + + @override + Future saveEventFromPerks({required Events events}) async { + try { + await database.eventsDao.updateNFTFromPerks(events.listOfPerks, events.id!, events.step); + return true; + } catch (_) { + throw CacheFailure(LocaleKeys.get_error.tr()); + } + } } diff --git a/evently/lib/services/third_party_services/database.g.dart b/evently/lib/services/third_party_services/database.g.dart index 6a349f3fe3..3e1308b38b 100644 --- a/evently/lib/services/third_party_services/database.g.dart +++ b/evently/lib/services/third_party_services/database.g.dart @@ -231,10 +231,11 @@ class _$EventsDao extends EventsDao { Future updateNFTFromPerks( String listOfPerks, int id, + String step, ) async { await _queryAdapter.queryNoReturn( - 'UPDATE events SET listOfPerks = ?1 WHERE id = ?2', - arguments: [listOfPerks, id]); + 'UPDATE events SET listOfPerks = ?1, step = ?3 WHERE id = ?2', + arguments: [listOfPerks, id, step]); } @override diff --git a/evently/lib/services/third_party_services/event_dao.dart b/evently/lib/services/third_party_services/event_dao.dart index 33f424672b..ca56a30dc2 100644 --- a/evently/lib/services/third_party_services/event_dao.dart +++ b/evently/lib/services/third_party_services/event_dao.dart @@ -27,10 +27,11 @@ abstract class EventsDao { String step, ); - @Query('UPDATE events SET listOfPerks = :listOfPerks WHERE id = :id') + @Query('UPDATE events SET listOfPerks = :listOfPerks, step = :step WHERE id = :id') Future updateNFTFromPerks( String listOfPerks, int id, + String step, ); @Query('UPDATE events SET numberOfTickets = :numberOfTickets, price = :price, isFreeDrops = :isFreeDrops WHERE id = :id') diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart index 3f4e3d4f43..b898ed55f3 100644 --- a/evently/lib/viewmodels/create_event_viewmodel.dart +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -34,7 +34,6 @@ class CreateEventViewModel extends ChangeNotifier { if (from == kDraft) { events = repository.getCacheDynamicType(key: eventKey) as Events; - Future.delayed(const Duration(milliseconds: 1), () { setTextField.call(); }); @@ -60,9 +59,9 @@ class CreateEventViewModel extends ChangeNotifier { return; case UploadStep.price: - currentPage = ValueNotifier(3); - currentStep = ValueNotifier(3); - pageController = PageController(initialPage: 3); + currentPage = ValueNotifier(4); + currentStep = ValueNotifier(4); + pageController = PageController(initialPage: 4); return; case UploadStep.none: From 0704d27a62ffb46aef75d6c6487bb2f329db75f5 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 21 May 2024 14:26:23 +0500 Subject: [PATCH 122/161] feat: draft upto price screen --- evently/lib/evently_provider.dart | 45 ++++++++++++++++++--------- evently/lib/models/denom.dart | 2 +- evently/lib/models/events.dart | 12 ++++++- evently/lib/screens/create_event.dart | 9 ++++++ 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 7fdf072753..43bb446c23 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:dartz/dartz.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; @@ -365,26 +366,41 @@ class EventlyProvider extends ChangeNotifier { required VoidCallback onCompleted, required UploadStep uploadStep, }) async { + final saveEvent = Events( + id: id, + step: uploadStep.toString(), + eventName: eventName, + hostName: hostName, + thumbnail: thumbnail!, + startDate: startDate, + endDate: endDate, + startTime: startTime, + endTime: endTime, + location: location, + description: description, + listOfPerks: perks.map((e) => jsonEncode(e)).toList().toString(), + isFreeDrops: isFreeDrop.toString(), + denom: selectedDenom.toString(), + numberOfTickets: numberOfTickets.toString(), + price: price.toString(), + ); + + /// this check is for when no event is save as draft + if (saveEvent.id == null) { + await repository.saveEvents(saveEvent); + onCompleted(); + return; + } + switch (uploadStep) { case UploadStep.overView: - await repository.saveEvents(Events(step: uploadStep.toString(), eventName: eventName, hostName: hostName, thumbnail: thumbnail!)); + await repository.saveEvents(saveEvent); break; case UploadStep.detail: - await repository.saveFromDetail(Events( - id: id, - step: uploadStep.toString(), - eventName: eventName, - hostName: hostName, - thumbnail: thumbnail!, - startDate: startDate, - endDate: endDate, - startTime: startTime, - endTime: endTime, - location: location, - description: description)); + await repository.saveFromDetail(saveEvent); break; case UploadStep.perks: - await repository.saveFromPerks(Events(id: id, listOfPerks: perks.join(','), step: uploadStep.toString())); + await repository.saveFromPerks(saveEvent); break; case UploadStep.price: // TODO: Handle this case. @@ -394,4 +410,5 @@ class EventlyProvider extends ChangeNotifier { onCompleted(); } + } diff --git a/evently/lib/models/denom.dart b/evently/lib/models/denom.dart index a107ecff18..6930b067f1 100644 --- a/evently/lib/models/denom.dart +++ b/evently/lib/models/denom.dart @@ -32,7 +32,7 @@ class Denom { @override String toString() { - return 'Denom{name: $name, symbol: $symbol, icon: $icon}'; + return '{name: $name, symbol: $symbol, icon: $icon}'; } Widget getIconWidget() { diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 20b82aa7cb..4cb47372b5 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -114,7 +114,7 @@ class Events extends Equatable { @override String toString() { - return 'Event{eventName: $eventName, hostName: $hostName, thumbnail: $thumbnail, startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime, location: $location, description: $description, numberOfTickets: $numberOfTickets, price: $price, tradePercentage: $listOfPerks, isFreeDrop: $isFreeDrops, cookbookID: $cookbookID, recipeID: $recipeID, step: $step}'; + return 'Event{eventName: $eventName, hostName: $hostName, thumbnail: $thumbnail, startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime, location: $location, description: $description, numberOfTickets: $numberOfTickets, price: $price, listOfPerks: $listOfPerks, isFreeDrop: $isFreeDrops, cookbookID: $cookbookID, recipeID: $recipeID, step: $step}'; } } @@ -226,6 +226,16 @@ class PerksModel { description: description, ); + factory PerksModel.fromJson(Map map) => PerksModel(name: map['name'] ?? '', description: map['description'] ?? ''); + + Map toJson() { + Map map = {}; + map['name'] = name; + map['description'] = description; + + return map; + } + @override String toString() { return 'PerksModel{name: $name, description: $description}'; diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 12a2212557..64f67bb57e 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,4 +1,7 @@ +import 'dart:convert'; + import 'package:evently/evently_provider.dart'; +import 'package:evently/models/events.dart'; import 'package:evently/screens/detail_screen.dart'; import 'package:evently/screens/host_view_ticket_preview.dart'; import 'package:evently/screens/overview_screen.dart'; @@ -41,6 +44,12 @@ class _CreateEventState extends State { eventlyProvider.setLocation = createEventViewModel.events!.location; eventlyProvider.setDescription = createEventViewModel.events!.description; eventlyProvider.setId = createEventViewModel.events!.id!; + + final listOfPerkJson = jsonDecode(createEventViewModel.events!.listOfPerks); + + for (var perksJson in listOfPerkJson) { + eventlyProvider.setPerks = PerksModel.fromJson(perksJson); + } }); } From a2de1fab408ad31136cb01eb2f601ea24104b0d2 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 21 May 2024 15:54:26 +0500 Subject: [PATCH 123/161] feat: upto preview screen --- evently/devtools_options.yaml | 1 + evently/lib/evently_provider.dart | 6 +++--- evently/lib/models/denom.dart | 14 +++++++++++++ evently/lib/models/events.dart | 3 +-- evently/lib/repository/repository.dart | 15 +++++++++++++ evently/lib/screens/create_event.dart | 21 +++++++++++++------ evently/lib/screens/price_screen.dart | 10 ++++++++- .../datasources/local_datasource.dart | 13 ++++++++++++ .../third_party_services/database.g.dart | 6 ++++-- .../third_party_services/event_dao.dart | 4 +++- evently/lib/utils/enums.dart | 10 +++++++++ 11 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 evently/devtools_options.yaml diff --git a/evently/devtools_options.yaml b/evently/devtools_options.yaml new file mode 100644 index 0000000000..7e7e7f67de --- /dev/null +++ b/evently/devtools_options.yaml @@ -0,0 +1 @@ +extensions: diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 43bb446c23..67466ccdc1 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -380,7 +380,7 @@ class EventlyProvider extends ChangeNotifier { description: description, listOfPerks: perks.map((e) => jsonEncode(e)).toList().toString(), isFreeDrops: isFreeDrop.toString(), - denom: selectedDenom.toString(), + denom: jsonEncode(selectedDenom), numberOfTickets: numberOfTickets.toString(), price: price.toString(), ); @@ -403,12 +403,12 @@ class EventlyProvider extends ChangeNotifier { await repository.saveFromPerks(saveEvent); break; case UploadStep.price: - // TODO: Handle this case. + await repository.saveEventFromPrice(saveEvent); + break; case UploadStep.none: // TODO: Handle this case. } onCompleted(); } - } diff --git a/evently/lib/models/denom.dart b/evently/lib/models/denom.dart index 6930b067f1..1f4cb66b36 100644 --- a/evently/lib/models/denom.dart +++ b/evently/lib/models/denom.dart @@ -82,4 +82,18 @@ class Denom { return (double.parse(price.replaceAll(",", "").trim()) * kBigIntBase).toStringAsFixed(0); } } + + Map toJson() { + Map map = {}; + map['name'] = name; + map['symbol'] = symbol; + map['icon'] = icon; + return map; + } + + factory Denom.fromJson(Map json) => Denom( + name: json['name']!, + symbol: json['symbol']!, + icon: json['icon']!, + ); } diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 4cb47372b5..e4d9572878 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -1,10 +1,9 @@ import 'package:equatable/equatable.dart'; +import 'package:evently/utils/enums.dart'; import 'package:floor/floor.dart'; import 'package:pylons_sdk/low_level.dart'; import 'package:fixnum/fixnum.dart'; -enum FreeDrop { yes, no, unselected } - @entity class Events extends Equatable { @primaryKey diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 22d1553296..7855273e53 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -94,6 +94,8 @@ abstract class Repository { Future> saveFromDetail(Events events); Future> saveFromPerks(Events events); + + Future> saveEventFromPrice(Events events); } @LazySingleton(as: Repository) @@ -251,4 +253,17 @@ class RepositoryImp implements Repository { return Left(CacheFailure(LocaleKeys.get_error.tr())); } } + + @override + Future> saveEventFromPrice(Events events) async { + try { + final bool result = await localDataSource.saveEventFromPrice(events: events); + if (!result) { + return Left(CacheFailure(LocaleKeys.get_error.tr())); + } + return Right(result); + } on Exception catch (_) { + return Left(CacheFailure(LocaleKeys.get_error.tr())); + } + } } diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 64f67bb57e..6e4766abbe 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:evently/evently_provider.dart'; +import 'package:evently/models/denom.dart'; import 'package:evently/models/events.dart'; import 'package:evently/screens/detail_screen.dart'; import 'package:evently/screens/host_view_ticket_preview.dart'; @@ -8,6 +9,7 @@ import 'package:evently/screens/overview_screen.dart'; import 'package:evently/screens/perks_screen.dart'; import 'package:evently/screens/price_screen.dart'; import 'package:evently/utils/di/di.dart'; +import 'package:evently/utils/enums.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:flutter/material.dart'; @@ -50,17 +52,24 @@ class _CreateEventState extends State { for (var perksJson in listOfPerkJson) { eventlyProvider.setPerks = PerksModel.fromJson(perksJson); } + + eventlyProvider.setFreeDrop = createEventViewModel.events!.isFreeDrops.toFreeDrop(); + eventlyProvider.setPrice = int.parse(createEventViewModel.events!.price); + eventlyProvider.setNumberOfTickets = int.parse(createEventViewModel.events!.numberOfTickets); + eventlyProvider.setSelectedDenom(Denom.fromJson(jsonDecode(createEventViewModel.events!.denom))); }); } @override Widget build(BuildContext context) { - return ColoredBox( - color: EventlyAppTheme.kWhite, - child: SafeArea( - bottom: false, - child: Scaffold( - body: ChangeNotifierProvider.value(value: createEventViewModel, child: const CreateEventContent()), + return PopScope( + child: ColoredBox( + color: EventlyAppTheme.kWhite, + child: SafeArea( + bottom: false, + child: Scaffold( + body: ChangeNotifierProvider.value(value: createEventViewModel, child: const CreateEventContent()), + ), ), ), ); diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 9ab2f504ae..659efec014 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -6,7 +6,9 @@ import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; import 'package:evently/screens/custom_widgets/steps_indicator.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/enums.dart'; import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/screen_responsive.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; @@ -207,7 +209,13 @@ class _PriceScreenState extends State { onPressContinue: () { homeViewModel.nextPage(); }, - onPressSaveDraft: () {}, + onPressSaveDraft: () { + final navigator = Navigator.of(context); + provider.saveAsDraft( + onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), + uploadStep: UploadStep.price, + ); + }, isContinueEnable: provider.isFreeDrop == FreeDrop.unselected ? false : provider.isFreeDrop == FreeDrop.yes diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index d0346a6ccc..8f60346477 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -92,6 +92,9 @@ abstract class LocalDataSource { /// this method is used to save draft from details screen Future saveEventFromPerks({required Events events}); + + /// this method is used to save draft from details screen + Future saveEventFromPrice({required Events events}); } @LazySingleton(as: LocalDataSource) @@ -249,4 +252,14 @@ class LocalDataSourceImpl extends LocalDataSource { throw CacheFailure(LocaleKeys.get_error.tr()); } } + + @override + Future saveEventFromPrice({required Events events}) async { + try { + await database.eventsDao.updateNFTFromPrice(events.id!, events.numberOfTickets, events.price, events.isFreeDrops, events.denom, events.step); + return true; + } catch (_) { + throw CacheFailure(LocaleKeys.get_error.tr()); + } + } } diff --git a/evently/lib/services/third_party_services/database.g.dart b/evently/lib/services/third_party_services/database.g.dart index 3e1308b38b..ac1d0a0403 100644 --- a/evently/lib/services/third_party_services/database.g.dart +++ b/evently/lib/services/third_party_services/database.g.dart @@ -244,10 +244,12 @@ class _$EventsDao extends EventsDao { String numberOfTickets, String price, String isFreeDrops, + String denom, + String step, ) async { await _queryAdapter.queryNoReturn( - 'UPDATE events SET numberOfTickets = ?2, price = ?3, isFreeDrops = ?4 WHERE id = ?1', - arguments: [id, numberOfTickets, price, isFreeDrops]); + 'UPDATE events SET numberOfTickets = ?2, price = ?3, isFreeDrops = ?4, denom = ?5, step = ?6 WHERE id = ?1', + arguments: [id, numberOfTickets, price, isFreeDrops, denom, step]); } @override diff --git a/evently/lib/services/third_party_services/event_dao.dart b/evently/lib/services/third_party_services/event_dao.dart index ca56a30dc2..de4d997d99 100644 --- a/evently/lib/services/third_party_services/event_dao.dart +++ b/evently/lib/services/third_party_services/event_dao.dart @@ -34,11 +34,13 @@ abstract class EventsDao { String step, ); - @Query('UPDATE events SET numberOfTickets = :numberOfTickets, price = :price, isFreeDrops = :isFreeDrops WHERE id = :id') + @Query('UPDATE events SET numberOfTickets = :numberOfTickets, price = :price, isFreeDrops = :isFreeDrops, denom = :denom, step = :step WHERE id = :id') Future updateNFTFromPrice( int id, String numberOfTickets, String price, String isFreeDrops, + String denom, + String step, ); } diff --git a/evently/lib/utils/enums.dart b/evently/lib/utils/enums.dart index 255631625c..94a62d15ba 100644 --- a/evently/lib/utils/enums.dart +++ b/evently/lib/utils/enums.dart @@ -7,3 +7,13 @@ extension ToUploadStepPar on String { }, orElse: () => UploadStep.none); //return null if not found } } + +enum FreeDrop { yes, no, unselected } + +extension ToFreeDrop on String { + FreeDrop toFreeDrop() { + return FreeDrop.values.firstWhere((e) { + return e.toString().toLowerCase() == toLowerCase(); + }, orElse: () => FreeDrop.unselected); //return null if not found + } +} From 9255e38b6d0f5d1e50a8063901747f044d87aa91 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 21 May 2024 16:55:54 +0500 Subject: [PATCH 124/161] feat: on continue save missing data --- evently/lib/evently_provider.dart | 6 +++--- evently/lib/screens/create_event.dart | 11 +++++----- evently/lib/screens/detail_screen.dart | 26 +++++++++++++++++++----- evently/lib/screens/overview_screen.dart | 10 ++++++--- evently/lib/screens/perks_screen.dart | 4 ++++ evently/lib/screens/price_screen.dart | 5 ++++- evently/lib/utils/constants.dart | 3 --- 7 files changed, 45 insertions(+), 20 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 67466ccdc1..6a5af201d6 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -34,7 +34,7 @@ class EventlyProvider extends ChangeNotifier { ///* overview screen variable String _eventName = ''; String _hostName = ''; - String? _thumbnail; + String _thumbnail = ''; bool _isOverviewEnable = false; String? get thumbnail => _thumbnail; @@ -55,7 +55,7 @@ class EventlyProvider extends ChangeNotifier { } set setThumbnail(String? file) { - _thumbnail = file; + _thumbnail = file!; checkIsOverEnable(); notifyListeners(); } @@ -66,7 +66,7 @@ class EventlyProvider extends ChangeNotifier { } checkIsOverEnable() { - setOverviewEnable = thumbnail != null && eventName.length >= kMinEventName && hostName.length >= kMinHostName; + setOverviewEnable = thumbnail!.isNotEmpty && eventName.isNotEmpty && hostName.isNotEmpty; } ///* detail screen diff --git a/evently/lib/screens/create_event.dart b/evently/lib/screens/create_event.dart index 6e4766abbe..745fc78971 100644 --- a/evently/lib/screens/create_event.dart +++ b/evently/lib/screens/create_event.dart @@ -62,11 +62,12 @@ class _CreateEventState extends State { @override Widget build(BuildContext context) { - return PopScope( - child: ColoredBox( - color: EventlyAppTheme.kWhite, - child: SafeArea( - bottom: false, + return ColoredBox( + color: EventlyAppTheme.kWhite, + child: SafeArea( + bottom: false, + child: PopScope( + canPop: false, child: Scaffold( body: ChangeNotifierProvider.value(value: createEventViewModel, child: const CreateEventContent()), ), diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 60a3631eb7..3764607252 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -56,7 +56,7 @@ class _DetailsScreenState extends State { onTap: () async { _showDatePicker(onSelected: (DateTime? val) { if (val == null) return; - provider.setStartDate = DateFormat("dd-MM-yyyy").format(val); + provider.setStartDate = _dateFormatter(val); }); }, child: EventlyTextField( @@ -73,7 +73,7 @@ class _DetailsScreenState extends State { onTap: () { _showDatePicker(onSelected: (DateTime? val) { if (val == null) return; - provider.setEndDate = DateFormat("dd-MM-yyyy").format(val); + provider.setEndDate = _dateFormatter(val); }); }, child: EventlyTextField( @@ -97,7 +97,9 @@ class _DetailsScreenState extends State { onTap: () { _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { if (timeOfDay == null) return; - provider.setStartTime = '${timeOfDay.hour}:${timeOfDay.minute}'; + final currentTime = DateTime.now(); + final time = currentTime.copyWith(hour: timeOfDay.hour, minute: timeOfDay.minute); + provider.setStartTime = _timerFormatter(time); }); }, child: EventlyTextField( @@ -117,7 +119,9 @@ class _DetailsScreenState extends State { onTap: () { _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { if (timeOfDay == null) return; - provider.setEndTime = '${timeOfDay.hour}:${timeOfDay.minute}'; + final currentTime = DateTime.now(); + final time = currentTime.copyWith(hour: timeOfDay.hour, minute: timeOfDay.minute); + provider.setEndTime = _timerFormatter(time); }); }, child: EventlyTextField( @@ -162,6 +166,10 @@ class _DetailsScreenState extends State { const VerticalSpace(80), BottomButtons( onPressContinue: () { + provider.saveAsDraft( + onCompleted: () => {}, + uploadStep: UploadStep.price, + ); createEventViewModel.nextPage(); }, onPressSaveDraft: () { @@ -198,7 +206,15 @@ class _DetailsScreenState extends State { showDatePicker( context: context, firstDate: DateTime.now(), - lastDate: DateTime.now(), + lastDate: DateTime(2099, 12), ).then((value) => onSelected(value!)); } + + _timerFormatter(DateTime dateTime) { + return DateFormat('hh:mm a').format(dateTime); + } + + _dateFormatter(DateTime dateTime) { + return DateFormat.yMMMMd('en_US').format(dateTime); + } } diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 76fd3280f7..b22957d460 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -91,7 +91,7 @@ class _OverViewScreenState extends State { dashPattern: const [10, 6], color: EventlyAppTheme.kTextDarkPurple, strokeWidth: 3.h, - child: provider.thumbnail != null + child: provider.thumbnail!.isNotEmpty ? SizedBox( height: 180, width: double.infinity, @@ -99,7 +99,7 @@ class _OverViewScreenState extends State { alignment: Alignment.center, children: [ CachedNetworkImage( - fit: BoxFit.fill, + fit: BoxFit.contain, imageUrl: provider.thumbnail!, errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), progressIndicatorBuilder: (context, _, progress) { @@ -110,7 +110,7 @@ class _OverViewScreenState extends State { onTap: () => provider.pickThumbnail(), child: SvgPicture.asset( SVGUtils.kSvgUpload, - color: EventlyAppTheme.kWhite, + color: Colors.white, ), ), ], @@ -145,6 +145,10 @@ class _OverViewScreenState extends State { const VerticalSpace(80), BottomButtons( onPressContinue: () { + provider.saveAsDraft( + onCompleted: () => {}, + uploadStep: UploadStep.overView, + ); createEventViewModel.nextPage(); }, onPressSaveDraft: () { diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index 03f14088de..52f3cd9b00 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -114,6 +114,10 @@ class _PerksScreenState extends State { padding: const EdgeInsets.symmetric(horizontal: 20), child: BottomButtons( onPressContinue: () { + provider.saveAsDraft( + onCompleted: () => {}, + uploadStep: UploadStep.perks, + ); createEventViewModel.nextPage(); }, onPressSaveDraft: () { diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 659efec014..3769de13a6 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -1,6 +1,5 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; -import 'package:evently/models/events.dart'; import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; @@ -207,6 +206,10 @@ class _PriceScreenState extends State { padding: EdgeInsets.symmetric(horizontal: 20.w), child: BottomButtons( onPressContinue: () { + provider.saveAsDraft( + onCompleted: () => {}, + uploadStep: UploadStep.price, + ); homeViewModel.nextPage(); }, onPressSaveDraft: () { diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index eec19a237c..b6946f8251 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -28,9 +28,6 @@ const int kEthIntBase = 1000000000000000000; final List imageAllowedExts = ["png", "jpg", "jpeg", "heif"]; const kPylons = "Pylons"; -///number constant -const kMinEventName = 9; -const kMinHostName = 9; const kErrProfileNotExist = 'profileDoesNotExist'; From dc6f3f03e2011a00dab161bef46833307e9aae20 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 21 May 2024 17:02:14 +0500 Subject: [PATCH 125/161] fix: causing duplication --- evently/lib/screens/detail_screen.dart | 5 +---- evently/lib/screens/overview_screen.dart | 4 ---- evently/lib/screens/perks_screen.dart | 5 +---- evently/lib/screens/price_screen.dart | 5 +---- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 3764607252..2a199fe9b4 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -166,10 +166,7 @@ class _DetailsScreenState extends State { const VerticalSpace(80), BottomButtons( onPressContinue: () { - provider.saveAsDraft( - onCompleted: () => {}, - uploadStep: UploadStep.price, - ); + createEventViewModel.nextPage(); }, onPressSaveDraft: () { diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index b22957d460..0ad9e1ce37 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -145,10 +145,6 @@ class _OverViewScreenState extends State { const VerticalSpace(80), BottomButtons( onPressContinue: () { - provider.saveAsDraft( - onCompleted: () => {}, - uploadStep: UploadStep.overView, - ); createEventViewModel.nextPage(); }, onPressSaveDraft: () { diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index 52f3cd9b00..06d3bc60bf 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -114,10 +114,7 @@ class _PerksScreenState extends State { padding: const EdgeInsets.symmetric(horizontal: 20), child: BottomButtons( onPressContinue: () { - provider.saveAsDraft( - onCompleted: () => {}, - uploadStep: UploadStep.perks, - ); + createEventViewModel.nextPage(); }, onPressSaveDraft: () { diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 3769de13a6..b77db24b91 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -206,10 +206,7 @@ class _PriceScreenState extends State { padding: EdgeInsets.symmetric(horizontal: 20.w), child: BottomButtons( onPressContinue: () { - provider.saveAsDraft( - onCompleted: () => {}, - uploadStep: UploadStep.price, - ); + homeViewModel.nextPage(); }, onPressSaveDraft: () { From 3d93f16797f946d6fe304160689d6b02f075da25 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 21 May 2024 17:13:57 +0500 Subject: [PATCH 126/161] fix: when started perks list empty --- evently/lib/evently_provider.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 6a5af201d6..afb6b1dd10 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -122,7 +122,7 @@ class EventlyProvider extends ChangeNotifier { } /// perks screen - final List _perks = []; + List _perks = []; int _selectedPerk = 0; List get perks => _perks; @@ -359,6 +359,8 @@ class EventlyProvider extends ChangeNotifier { _price = 0; _cookbookId = ''; + _perks = []; + notifyListeners(); } From 1ab1968971f786d84e4559ea06e97d41f0f51288 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 23 May 2024 11:02:34 +0500 Subject: [PATCH 127/161] feat: get publish event list --- evently/lib/evently_provider.dart | 11 ++-- evently/lib/models/events.dart | 2 + evently/lib/repository/repository.dart | 6 +- .../screens/event_hub/event_hub_screen.dart | 36 ++++++----- .../event_hub/event_hub_view_model.dart | 60 ++++++++----------- .../lib/screens/host_view_ticket_preview.dart | 54 +++++++++-------- .../datasources/local_datasource.dart | 15 ----- 7 files changed, 83 insertions(+), 101 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index afb6b1dd10..7e5ae92315 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -305,14 +305,15 @@ class EventlyProvider extends ChangeNotifier { return false; } scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); - final eventsFromRecipe = Events.fromRecipe(recipe); - GetIt.I.get().updatePublishedEventList(events: eventsFromRecipe); - deleteEvent(); + // final eventsFromRecipe = Events.fromRecipe(recipe); + // GetIt.I.get().updatePublishedEventList(events: eventsFromRecipe); + deleteEvent(event.id); return true; } - Future deleteEvent() async { - notifyListeners(); + Future deleteEvent(int? id) async { + if (id == null) return; + await repository.deleteEvent(id); } /// send createCookBook tx message to the wallet app diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index e4d9572878..65e21b28e6 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -75,6 +75,8 @@ class Events extends Equatable { price: map[kPrice]!, listOfPerks: map[kPerks]!, step: '', + cookbookID: map[kCookBookId]!, + recipeID: map[kRecipeId]!, ); } diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 7855273e53..8fb27a50d3 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -66,7 +66,7 @@ abstract class Repository { /// This method will delete draft from the local database /// Input: [id] the id of the nft which the user wants to delete /// Output: [bool] returns whether the operation is successful or not - Future> deleteNft(int id); + Future> deleteEvent(int id); /// This method will set the input in the cache /// Input: [key] the key against which the value is to be set, [value] the value that is to be set. @@ -194,9 +194,9 @@ class RepositoryImp implements Repository { } @override - Future> deleteNft(int id) async { + Future> deleteEvent(int id) async { try { - final bool result = await localDataSource.deleteNft(id); + final bool result = await localDataSource.deleteEvents(id); return Right(result); } on Exception catch (_) { return Left(CacheFailure(LocaleKeys.something_wrong.tr())); diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 57c68d93d3..5cb4d16a27 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -76,7 +76,6 @@ class _EventHubContentState extends State { color: EventlyAppTheme.kWhite, fontFamily: kUniversalFontFamily, ); - TextStyle subTitleStyle = TextStyle( fontSize: isTablet ? 12.sp : 15, fontWeight: FontWeight.w700, @@ -134,14 +133,9 @@ class _EventHubContentState extends State { isSelected: viewModel.selectedCollectionType == CollectionType.draft, ), getButton( - title: LocaleKeys.for_sale, - onTap: () => viewModel.changeSelectedCollection(CollectionType.forSale), - isSelected: viewModel.selectedCollectionType == CollectionType.forSale, - ), - getButton( - title: LocaleKeys.history, - onTap: () => viewModel.changeSelectedCollection(CollectionType.history), - isSelected: viewModel.selectedCollectionType == CollectionType.history, + title: LocaleKeys.published, + onTap: () => viewModel.changeSelectedCollection(CollectionType.publish), + isSelected: viewModel.selectedCollectionType == CollectionType.publish, ), ], ), @@ -177,15 +171,16 @@ class _EventHubContentState extends State { viewModel.viewType == ViewType.viewGrid ? Expanded( child: BuildGridView( - eventsList: viewModel.eventForDraftList, + eventsList: viewModel.selectedCollectionType == CollectionType.draft ? viewModel.getDraftList : viewModel.getPublishList, onEmptyList: (BuildContext context) => Padding( padding: EdgeInsets.symmetric(horizontal: 20.w), child: Align( - alignment: Alignment.centerLeft, - child: Text( - LocaleKeys.no_nft_created.tr(), - style: subTitleStyle, - )), + alignment: Alignment.topLeft, + child: Text( + LocaleKeys.no_nft_created.tr(), + style: subTitleStyle, + ), + ), ), calculateBannerPrice: ({required String currency, required String price}) { return ''; @@ -194,12 +189,15 @@ class _EventHubContentState extends State { ) : Expanded( child: BuildListView( - eventsList: viewModel.eventForDraftList, + eventsList: viewModel.selectedCollectionType == CollectionType.draft ? viewModel.getDraftList : viewModel.getPublishList, onEmptyList: (BuildContext context) => Padding( padding: EdgeInsets.symmetric(horizontal: 20.w), - child: Text( - LocaleKeys.no_nft_created.tr(), - style: subTitleStyle, + child: Align( + alignment: Alignment.topLeft, + child: Text( + LocaleKeys.no_nft_created.tr(), + style: subTitleStyle, + ), ), ), ), diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 3073068c6d..8a8dfacd85 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -9,7 +9,7 @@ import 'package:flutter/cupertino.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/pylons_sdk.dart'; -enum CollectionType { draft, forSale, history } +enum CollectionType { draft, publish } enum ViewType { viewGrid, viewList } @@ -19,36 +19,26 @@ class EventHubViewModel extends ChangeNotifier { final Repository repository; - List _eventPublishedList = []; + List _draftList = []; + List _publishList = []; - List get eventPublishedList => _eventPublishedList; + List get getDraftList => _draftList; + List get getPublishList => _publishList; - List _eventForDraftList = []; - - ViewType viewType = ViewType.viewGrid; - - void updateViewType(ViewType selectedViewType) { - viewType = selectedViewType; + set setDraftList(List eventList) { + _draftList = eventList; notifyListeners(); } - List get eventForDraftList => _eventForDraftList; - - List get eventForSaleList => _eventForDraftList; - - set setEventForDraftList(List eventDraftList) { - _eventForDraftList = eventDraftList; + set setPublishList(List eventList) { + _publishList = eventList; notifyListeners(); } - set setEventPublishList(List events) { - _eventPublishedList = events; - notifyListeners(); - } + ViewType viewType = ViewType.viewGrid; - void updatePublishedEventList({required Events events}) { - _eventPublishedList.add(events); - _eventForDraftList.add(events); + void updateViewType(ViewType selectedViewType) { + viewType = selectedViewType; notifyListeners(); } @@ -63,13 +53,8 @@ class EventHubViewModel extends ChangeNotifier { notifyListeners(); break; - case CollectionType.forSale: - _selectedCollectionType = CollectionType.forSale; - notifyListeners(); - break; - - case CollectionType.history: - _selectedCollectionType = CollectionType.history; + case CollectionType.publish: + _selectedCollectionType = CollectionType.publish; notifyListeners(); break; } @@ -102,10 +87,15 @@ class EventHubViewModel extends ChangeNotifier { if (recipesList.isEmpty) { return; } + + List eventsList = []; + for (final recipe in recipesList) { - final nft = Events.fromRecipe(recipe); - _eventPublishedList.add(nft); + final events = Events.fromRecipe(recipe); + eventsList.add(events); } + + setPublishList = eventsList; } Future getDraftsList() async { @@ -120,7 +110,8 @@ class EventHubViewModel extends ChangeNotifier { } List draftEvent = getEventResponse.getOrElse(() => []); - setEventForDraftList = draftEvent; + + setDraftList = draftEvent; loading.dismiss(); @@ -134,13 +125,14 @@ class EventHubViewModel extends ChangeNotifier { } Future deleteNft(int? id) async { - final deleteNftResponse = await repository.deleteNft(id!); + final deleteNftResponse = await repository.deleteEvent(id!); if (deleteNftResponse.isLeft()) { LocaleKeys.delete_error.tr().show(); return; } - eventForDraftList.removeWhere((element) => element.id == id); + + getDraftList.removeWhere((element) => element.id == id); notifyListeners(); } diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index 8ac54606b0..cfb12e50cd 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -3,7 +3,9 @@ import 'package:evently/evently_provider.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/main.dart'; import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; +import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/space_utils.dart'; @@ -149,34 +151,35 @@ class _HostTicketPreviewState extends State { ), ], ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'SEAT', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '6A', - style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ), - Text( - 'Room 3', - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ) - ], - ), - ], - ), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Text( + // 'SEAT', + // style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + // ), + // SizedBox(height: 1.h), + // Column( + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // '6A', + // style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + // ), + // Text( + // 'Room 3', + // style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + // ) + // ], + // ), + // ], + // ), ], ), ), + VerticalSpace(20.h), Padding( padding: EdgeInsets.only(left: 10.w, right: 30.h), child: Row( @@ -276,6 +279,7 @@ class _HostTicketPreviewState extends State { return; } + sl().changeSelectedCollection(CollectionType.publish); navigator.popUntil((route) { return route.settings.name == RouteUtil.kRouteEventHub; }); diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 8f60346477..97cd8acb80 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -54,11 +54,6 @@ abstract class LocalDataSource { /// Output: [int] returns id of the inserted document Future saveEvents(Events events); - /// This method will delete draft from the local database - /// Input: [id] the id of the draft which the user wants to delete - /// Output: [bool] returns whether the operation is successful or not - Future deleteNft(int id); - /// This method will delete the value from the cache /// Input: [key] the key of the value /// Output: [value] will return the value that is just removed @@ -184,16 +179,6 @@ class LocalDataSourceImpl extends LocalDataSource { } } - @override - Future deleteNft(int id) async { - try { - await database.eventsDao.delete(id); - return true; - } catch (e) { - throw CacheFailure(LocaleKeys.delete_error.tr()); - } - } - @override String deleteCacheString({required String key}) { return cacheManager.deleteString(key: key); From 914ec16cd6ceb3f5f3937b252034fb5c0ebb90ee Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 23 May 2024 11:24:42 +0500 Subject: [PATCH 128/161] feat: receipe name should be more than 8 characters --- evently/lib/evently_provider.dart | 7 +++---- evently/lib/screens/event_hub/event_hub_view_model.dart | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 7e5ae92315..f2fcf296b6 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -11,6 +11,7 @@ import 'package:evently/models/storage_response_model.dart'; import 'package:evently/repository/repository.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/di/di.dart'; import 'package:evently/utils/enums.dart'; import 'package:evently/utils/extension_util.dart'; import 'package:evently/utils/failure/failure.dart'; @@ -18,7 +19,6 @@ import 'package:evently/widgets/loading_with_progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:get_it/get_it.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/low_level.dart'; import 'services/third_party_services/quick_node.dart'; @@ -66,7 +66,7 @@ class EventlyProvider extends ChangeNotifier { } checkIsOverEnable() { - setOverviewEnable = thumbnail!.isNotEmpty && eventName.isNotEmpty && hostName.isNotEmpty; + setOverviewEnable = thumbnail!.isNotEmpty && eventName.length > 8 && hostName.isNotEmpty; } ///* detail screen @@ -305,8 +305,7 @@ class EventlyProvider extends ChangeNotifier { return false; } scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); - // final eventsFromRecipe = Events.fromRecipe(recipe); - // GetIt.I.get().updatePublishedEventList(events: eventsFromRecipe); + sl().updatePublishedEventList(); deleteEvent(event.id); return true; } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 8a8dfacd85..2bffc70541 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -140,4 +140,9 @@ class EventHubViewModel extends ChangeNotifier { repository.setCacheDynamicType(key: eventKey, value: events); repository.setCacheString(key: fromKey, value: kDraft); } + + updatePublishedEventList() { + getDraftsList(); + getRecipesList(); + } } From 257cd3b3d269aae2eec14e47f4c1eebe2b1bb101 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 23 May 2024 11:43:04 +0500 Subject: [PATCH 129/161] feat: publish list view style --- .../screens/event_hub/event_hub_screen.dart | 6 +- .../event_hub/widgets/events_list_tile.dart | 153 ++++++++++++++++++ evently/lib/utils/constants.dart | 11 ++ evently/lib/utils/evently_app_theme.dart | 3 + evently/lib/utils/extension_util.dart | 48 ++++++ 5 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 evently/lib/screens/event_hub/widgets/events_list_tile.dart diff --git a/evently/lib/screens/event_hub/event_hub_screen.dart b/evently/lib/screens/event_hub/event_hub_screen.dart index 5cb4d16a27..d1fd317b3e 100644 --- a/evently/lib/screens/event_hub/event_hub_screen.dart +++ b/evently/lib/screens/event_hub/event_hub_screen.dart @@ -6,6 +6,7 @@ import 'package:evently/models/events.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/screens/event_hub/widgets/delete_confirmation_dialog.dart'; import 'package:evently/screens/event_hub/widgets/drafts_more_bottomsheet.dart'; +import 'package:evently/screens/event_hub/widgets/events_list_tile.dart'; import 'package:evently/screens/event_hub/widgets/nfts_grid_view.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/di/di.dart'; @@ -202,7 +203,6 @@ class _EventHubContentState extends State { ), ), ), - const Spacer(), Padding(padding: EdgeInsets.symmetric(horizontal: 20.w), child: getCreateEventWidget()), ], ), @@ -283,7 +283,9 @@ class BuildListView extends StatelessWidget { child: DraftListTile(events: events, viewModel: viewModel), ); } else { - return const Placeholder(); + return NFTsListTile( + publishedEvents: events, + ); } }, ); diff --git a/evently/lib/screens/event_hub/widgets/events_list_tile.dart b/evently/lib/screens/event_hub/widgets/events_list_tile.dart new file mode 100644 index 0000000000..cbcfae5735 --- /dev/null +++ b/evently/lib/screens/event_hub/widgets/events_list_tile.dart @@ -0,0 +1,153 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:evently/evently_provider.dart'; +import 'package:evently/main.dart'; +import 'package:evently/models/events.dart'; +import 'package:evently/utils/constants.dart'; +import 'package:evently/utils/evently_app_theme.dart'; +import 'package:evently/utils/extension_util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:get_it/get_it.dart'; +import 'package:shimmer_animation/shimmer_animation.dart'; + +import '../../../generated/locale_keys.g.dart'; + +class NFTsListTile extends StatelessWidget { + final Events publishedEvents; + + const NFTsListTile({super.key, required this.publishedEvents}); + + EventlyProvider get _easelProvider => GetIt.I.get(); + + void buildBottomSheet({required BuildContext context}) { + + } + + Widget getPublishedCard({required BuildContext context}) { + return DecoratedBox( + decoration: BoxDecoration( + color: EventlyAppTheme.kWhite, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + offset: const Offset(0.0, 1.0), + blurRadius: 4.0, + ), + ], + ), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 15.h), + child: Row( + children: [ + SizedBox( + height: 45.h, + width: 45.w, + child: NftTypeBuilder( + onImage: (context) => buildCachedNetworkImage(publishedEvents.thumbnail), + ), + ), + SizedBox( + width: 10.w, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + publishedEvents.eventName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: EventlyAppTheme.titleStyle.copyWith(fontSize: isTablet ? 13.sp : 18.sp), + ), + SizedBox( + height: 6.h, + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 3.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(1.h), + color: EventlyAppTheme.kDarkGreen, + ), + child: Text( + LocaleKeys.published.tr(), + style: EventlyAppTheme.titleStyle.copyWith(color: EventlyAppTheme.kWhite, fontSize: isTablet ? 8.sp : 11.sp), + ), + ) + ], + ), + ), + SizedBox( + width: 10.w, + ), + InkWell( + + onTap: () => buildBottomSheet(context: context), + child: Padding( + padding: EdgeInsets.all(4.0.w), + child: SvgPicture.asset(SVGUtils.kSvgMoreOption), + ), + ), + SizedBox( + width: 10.w, + ), + ], + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + if (publishedEvents.price.isNotEmpty && double.parse(publishedEvents.price) > 0) + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Card( + elevation: 5, + margin: EdgeInsets.zero, + child: ClipRRect( + child: Banner( + key: Key("${publishedEvents.denom.getCoinWithProperDenomination(publishedEvents.price)} ${publishedEvents.denom.getAbbrev()}"), + color: EventlyAppTheme.kDarkGreen, + location: BannerLocation.topEnd, + message: "${publishedEvents.denom.getCoinWithProperDenomination(publishedEvents.price)} ${publishedEvents.denom.getAbbrev()}", + child: getPublishedCard(context: context), + ), + ), + ), + ) + else + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: getPublishedCard(context: context), + ), + ], + ); + } + + CachedNetworkImage buildCachedNetworkImage(String url) { + return CachedNetworkImage( + fit: BoxFit.fill, + imageUrl: url, + errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)), + placeholder: (context, url) => Shimmer(color: EventlyAppTheme.cardBackground, child: const SizedBox.expand()), + ); + } +} + +class NftTypeBuilder extends StatelessWidget { + final WidgetBuilder onImage; + + const NftTypeBuilder({ + super.key, + required this.onImage, + }); + + @override + Widget build(BuildContext context) { + return onImage(context); + } +} diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index b6946f8251..f5715190db 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -94,3 +94,14 @@ class PngUtils { static const kPhantom = "assets/images/phantom.png"; static const kDottedLine = "assets/images/dotted_line.png"; } + +/// Currency ABRR +const String kEmoneyAbb = "EEUR"; +const String kAGoricAbb = "run"; +const String kPYLNAbbrevation = 'PYLN'; +const String kStripeUSDABR = 'USD'; +const String kAgoricAbr = "RUN"; +const String kAtomAbr = "ATOM"; +const String kLunaAbr = "Luna"; +const String kEthereumAbr = "ETH"; + diff --git a/evently/lib/utils/evently_app_theme.dart b/evently/lib/utils/evently_app_theme.dart index e5020bf64a..9052ff7beb 100644 --- a/evently/lib/utils/evently_app_theme.dart +++ b/evently/lib/utils/evently_app_theme.dart @@ -27,6 +27,9 @@ class EventlyAppTheme { static const Color kLightRed = Color(0xFFEF4421); static const Color kDarkGreen = Color(0xFF3A8977); + static Color cardBackground = const Color(0xFFC4C4C4).withOpacity(0.2); + + static const String universalSansFamily = "UniversalSans"; static ThemeData theme(BuildContext context) => ThemeData( diff --git a/evently/lib/utils/extension_util.dart b/evently/lib/utils/extension_util.dart index 5cba9e0363..8be68412ea 100644 --- a/evently/lib/utils/extension_util.dart +++ b/evently/lib/utils/extension_util.dart @@ -1,7 +1,10 @@ import 'package:evently/main.dart'; +import 'package:evently/models/events.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'constants.dart'; + extension NavigatorKey on GlobalKey { void showMsg({required String message}) { ScaffoldMessenger.maybeOf(currentState!.context) @@ -63,3 +66,48 @@ extension ScaffoldStateHelper on ScaffoldMessengerState { } } +extension GetAbbrev on String { + String getAbbrev() { + switch (this) { + case kAgoricSymbol: + return kAgoricAbr; + case kPylonSymbol: + return kPYLNAbbrevation; + case kUsdSymbol: + return kStripeUSDABR; + case kEuroSymbol: + return kEmoneyAbb; + case kAtomSymbol: + return kAtomAbr; + case kEthereumSymbol: + return kEthereumAbr; + default: + return kPYLNAbbrevation; + } + } +} + +extension GetCoinWithProperDenomination on String { + String getCoinWithProperDenomination(String amount) { + if (this == kUsdSymbol) { + return (double.parse(amount) / kBigIntBase).toStringAsFixed(2); + } else if (this == kPylonSymbol) { + return (double.parse(amount) / kBigIntBase).toStringAsFixed(0); + } else { + return (double.parse(amount) / kEthIntBase).toStringAsFixed(2); + } + } + + String getEaselInputCoinWithDenomination(String amount) { + if (this == kUsdSymbol) { + return double.parse(amount).toStringAsFixed(2); + } else if (this == kPylonSymbol) { + return double.parse(amount).toStringAsFixed(0); + } else { + return double.parse(amount).toStringAsFixed(2); + } + } +} + + + From abdbd979e5cf79497fbc7ea58e3fd415cc0d4499 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 23 May 2024 12:41:24 +0500 Subject: [PATCH 130/161] feat: publish view in pylons wallet app --- .../handlers/create_recipe_handler.dart | 2 +- wallet/lib/model/event.dart | 72 +++-- wallet/lib/model/nft.dart | 269 ++++-------------- wallet/lib/utils/route_util.dart | 2 +- 4 files changed, 113 insertions(+), 232 deletions(-) diff --git a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart index 259e03eecb..993016d65b 100644 --- a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart +++ b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart @@ -39,7 +39,7 @@ class CreateRecipeHandler implements BaseHandler { final msgObj = pylons.MsgCreateRecipe.create()..mergeFromProto3Json(jsonMap); if (msgObj.cookbookId.contains('Evently')) { - final events = await Events.fromRecipeId(msgObj.cookbookId, msgObj.id); + final events = await Events.eventFromRecipeId(msgObj.cookbookId, msgObj.id); if (events != null) { await navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events); } diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index 60d41f4d97..7ad8af98c1 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -8,7 +8,28 @@ import '../modules/Pylonstech.pylons.pylons/module/client/pylons/recipe.pb.dart' enum FreeDrop { yes, no, unselected } class Events extends Equatable { + final int? id; + final String recipeID; + final String eventName; + final String hostName; + final String thumbnail; + final String startDate; + final String endDate; + final String startTime; + final String endTime; + final String location; + final String description; + final String numberOfTickets; + final String price; + final String listOfPerks; + final String isFreeDrops; + final String cookbookID; + final String step; + final String denom; + const Events({ + this.id, + ///* overview data this.eventName = '', this.hostName = '', @@ -29,31 +50,18 @@ class Events extends Equatable { this.numberOfTickets = '0', this.price = '', this.isFreeDrops = 'unselected', + this.denom = '', ///* other this.cookbookID = '', this.recipeID = '', - }); - final String eventName; - final String hostName; - final String thumbnail; - final String startDate; - final String endDate; - final String startTime; - final String endTime; - final String location; - final String description; - final String numberOfTickets; - final String price; - final String listOfPerks; - final String isFreeDrops; - final String cookbookID; - final String recipeID; + ///* for tracking where its save as draft + this.step = '', + }); factory Events.fromRecipe(Recipe recipe) { - Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); - + final Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); return Events( eventName: map[kEventName]!, hostName: map[kEventHostName]!, @@ -67,6 +75,8 @@ class Events extends Equatable { numberOfTickets: map[kNumberOfTickets]!, price: map[kPrice]!, listOfPerks: map[kPerks]!, + cookbookID: map[kCookBookId]!, + recipeID: map[kRecipeId]!, ); } @@ -83,9 +93,12 @@ class Events extends Equatable { case kEndTime: case kLocation: case kDescription: - case kPerks: case kNumberOfTickets: case kPrice: + case kPerks: + case kFreeDrop: + case kCookBookId: + case kRecipeId: attributeValues[attribute.key] = attribute.value; break; default: @@ -97,14 +110,15 @@ class Events extends Equatable { } @override - List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price, listOfPerks, isFreeDrops, cookbookID, recipeID]; + List get props => + [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price, listOfPerks, isFreeDrops, cookbookID, recipeID, step]; @override String toString() { - return 'Event{eventName: $eventName, hostName: $hostName, thumbnail: $thumbnail, startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime, location: $location, description: $description, numberOfTickets: $numberOfTickets, price: $price, tradePercentage: $listOfPerks, isFreeDrop: $isFreeDrops, cookbookID: $cookbookID, recipeID: $recipeID}'; + return 'Event{eventName: $eventName, hostName: $hostName, thumbnail: $thumbnail, startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime, location: $location, description: $description, numberOfTickets: $numberOfTickets, price: $price, listOfPerks: $listOfPerks, isFreeDrop: $isFreeDrops, cookbookID: $cookbookID, recipeID: $recipeID, step: $step}'; } - static Future fromRecipeId(String cookbookId, String recipeId) async { + static Future eventFromRecipeId(String cookbookId, String recipeId) async { final walletsStore = GetIt.I.get(); final recipeEither = await walletsStore.getRecipe(cookbookId, recipeId); @@ -203,10 +217,10 @@ const kCookBookId = "kCookBookId"; const kVersion = "v0.2.0"; const kUpylon = "upylon"; const kPylonSymbol = 'upylon'; -const String transferFeeAmount = '1'; +const transferFeeAmount = '1'; const kEventlyEvent = "Evently_Event"; const kExtraInfo = "extraInfo"; -const String costPerBlock = '0'; +const costPerBlock = '0'; class PerksModel { PerksModel({required this.name, required this.description}); @@ -224,6 +238,16 @@ class PerksModel { description: description, ); + factory PerksModel.fromJson(Map map) => PerksModel(name: map['name']!, description: map['description']!); + + Map toJson() { + final Map map = {}; + map['name'] = name; + map['description'] = description; + + return map; + } + @override String toString() { return 'PerksModel{name: $name, description: $description}'; diff --git a/wallet/lib/model/nft.dart b/wallet/lib/model/nft.dart index 1d0e7f153f..59bf9bf03d 100644 --- a/wallet/lib/model/nft.dart +++ b/wallet/lib/model/nft.dart @@ -95,8 +95,7 @@ class NFT extends Equatable { } static Future fromItem(Item item) async { - if (item.strings.where((strKeyValue) => strKeyValue.key == kName).isEmpty && - item.strings.where((strKeyValue) => strKeyValue.key == kNFTURL).isEmpty) { + if (item.strings.where((strKeyValue) => strKeyValue.key == kName).isEmpty && item.strings.where((strKeyValue) => strKeyValue.key == kNFTURL).isEmpty) { return null; } final walletsStore = GetIt.I.get(); @@ -105,63 +104,27 @@ class NFT extends Equatable { recipeID: item.recipeId, name: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kName).value, url: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kNFTURL).value.changeDomain(), - thumbnailUrl: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kThumbnailUrl, - orElse: () => StringKeyValue(key: kThumbnailUrl, value: "")) - .value - .changeDomain(), + thumbnailUrl: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kThumbnailUrl, orElse: () => StringKeyValue(key: kThumbnailUrl, value: "")).value.changeDomain(), description: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kDescription).value, fileSize: getFileSize(item), - creator: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kCreator, - orElse: () => StringKeyValue(key: kCreator, value: "")) - .value, - fileExtension: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kCreator, - orElse: () => StringKeyValue(key: kFileExtension, value: "")) - .value, - cid: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kCID, orElse: () => StringKeyValue(key: kCID, value: "")) - .value, - appType: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kAppType, - orElse: () => StringKeyValue(key: kAppType, value: "")) - .value, - width: item.longs - .firstWhere((longKeyValue) => longKeyValue.key == kWidth, - orElse: () => LongKeyValue(key: kWidth, value: Int64())) - .value - .toString(), - height: item.longs - .firstWhere((longKeyValue) => longKeyValue.key == kHeight, - orElse: () => LongKeyValue(key: kHeight, value: Int64())) - .value - .toString(), + creator: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kCreator, orElse: () => StringKeyValue(key: kCreator, value: "")).value, + fileExtension: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kCreator, orElse: () => StringKeyValue(key: kFileExtension, value: "")).value, + cid: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kCID, orElse: () => StringKeyValue(key: kCID, value: "")).value, + appType: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kAppType, orElse: () => StringKeyValue(key: kAppType, value: "")).value, + width: item.longs.firstWhere((longKeyValue) => longKeyValue.key == kWidth, orElse: () => LongKeyValue(key: kWidth, value: Int64())).value.toString(), + height: item.longs.firstWhere((longKeyValue) => longKeyValue.key == kHeight, orElse: () => LongKeyValue(key: kHeight, value: Int64())).value.toString(), itemID: item.id, cookbookID: item.cookbookId, owner: owner, ibcCoins: IBCCoins.upylon, assetType: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kNftFormat).value.toAssetTypeEnum(), - duration: item.longs - .firstWhere((longKeyValue) => longKeyValue.key == kDuration, - orElse: () => LongKeyValue(key: kDuration, value: Int64())) - .value - .toInt() - .toSeconds(), - hashtags: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kHashtags, - orElse: () => StringKeyValue(key: kHashtags, value: "")) - .value, - realWorld: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kRealWorld, - orElse: () => StringKeyValue(key: kRealWorld, value: "false")) - .value == - "true", + duration: item.longs.firstWhere((longKeyValue) => longKeyValue.key == kDuration, orElse: () => LongKeyValue(key: kDuration, value: Int64())).value.toInt().toSeconds(), + hashtags: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kHashtags, orElse: () => StringKeyValue(key: kHashtags, value: "")).value, + realWorld: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kRealWorld, orElse: () => StringKeyValue(key: kRealWorld, value: "false")).value == "true", ); } - static String getFileSize(Item item) => - item.strings.firstWhereOrNull((strKeyValue) => strKeyValue.key == kFileSize)?.value ?? ""; + static String getFileSize(Item item) => item.strings.firstWhereOrNull((strKeyValue) => strKeyValue.key == kFileSize)?.value ?? ""; static Future fromTrade(Trade trade) async { final walletsStore = GetIt.I.get(); @@ -176,38 +139,15 @@ class NFT extends Equatable { type: NftType.TYPE_TRADE, name: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kName).value, url: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kNFTURL).value.changeDomain(), - thumbnailUrl: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kThumbnailUrl, - orElse: () => StringKeyValue(key: kThumbnailUrl, value: "")) - .value - .changeDomain(), + thumbnailUrl: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kThumbnailUrl, orElse: () => StringKeyValue(key: kThumbnailUrl, value: "")).value.changeDomain(), description: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kDescription).value, fileSize: getFileSize(item), - creator: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kCreator, - orElse: () => StringKeyValue(key: kCreator, value: "")) - .value, - fileExtension: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kFileExtension, - orElse: () => StringKeyValue(key: kCreator, value: "")) - .value, - cid: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kCID, orElse: () => StringKeyValue(key: kCID, value: "")) - .value, - appType: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kAppType, - orElse: () => StringKeyValue(key: kAppType, value: "")) - .value, - width: item.longs - .firstWhere((longKeyValue) => longKeyValue.key == kWidth, - orElse: () => LongKeyValue(key: kWidth, value: Int64())) - .value - .toString(), - height: item.longs - .firstWhere((longKeyValue) => longKeyValue.key == kHeight, - orElse: () => LongKeyValue(key: kHeight, value: Int64())) - .value - .toString(), + creator: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kCreator, orElse: () => StringKeyValue(key: kCreator, value: "")).value, + fileExtension: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kFileExtension, orElse: () => StringKeyValue(key: kCreator, value: "")).value, + cid: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kCID, orElse: () => StringKeyValue(key: kCID, value: "")).value, + appType: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kAppType, orElse: () => StringKeyValue(key: kAppType, value: "")).value, + width: item.longs.firstWhere((longKeyValue) => longKeyValue.key == kWidth, orElse: () => LongKeyValue(key: kWidth, value: Int64())).value.toString(), + height: item.longs.firstWhere((longKeyValue) => longKeyValue.key == kHeight, orElse: () => LongKeyValue(key: kHeight, value: Int64())).value.toString(), price: trade.coinInputs.first.coins.first.amount, denom: trade.coinInputs.first.coins.first.denom, itemID: item.id, @@ -216,21 +156,9 @@ class NFT extends Equatable { tradeID: trade.id.toString(), ibcCoins: trade.coinInputs.first.coins.first.denom.toIBCCoinsEnum(), assetType: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kNftFormat).value.toAssetTypeEnum(), - duration: item.longs - .firstWhere((longKeyValue) => longKeyValue.key == kDuration, - orElse: () => LongKeyValue(key: kDuration, value: Int64())) - .value - .toInt() - .toSeconds(), - hashtags: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kHashtags, - orElse: () => StringKeyValue(key: kHashtags, value: "")) - .value, - realWorld: item.strings - .firstWhere((strKeyValue) => strKeyValue.key == kRealWorld, - orElse: () => StringKeyValue(key: kRealWorld, value: "false")) - .value == - "true", + duration: item.longs.firstWhere((longKeyValue) => longKeyValue.key == kDuration, orElse: () => LongKeyValue(key: kDuration, value: Int64())).value.toInt().toSeconds(), + hashtags: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kHashtags, orElse: () => StringKeyValue(key: kHashtags, value: "")).value, + realWorld: item.strings.firstWhere((strKeyValue) => strKeyValue.key == kRealWorld, orElse: () => StringKeyValue(key: kRealWorld, value: "false")).value == "true", ); } @@ -247,88 +175,31 @@ class NFT extends Equatable { type: NftType.TYPE_RECIPE, recipeID: recipe.id, cookbookID: recipe.cookbookId, - name: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kName, orElse: () => StringParam()) - .value ?? - "", - url: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kNFTURL, orElse: () => StringParam()) - .value - .changeDomain() ?? - "", - thumbnailUrl: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kThumbnailUrl, orElse: () => StringParam()) - .value - .changeDomain() ?? - "", - description: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kDescription, orElse: () => StringParam()) - .value ?? - "", - fileSize: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kFileSize, orElse: () => StringParam()) - .value ?? - "", - cid: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kCID, orElse: () => StringParam()) - .value ?? - "", - appType: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kAppType, orElse: () => StringParam()) - .value ?? - "", - creator: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kCreator, orElse: () => StringParam()) - .value ?? - "", - fileExtension: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kFileExtension, orElse: () => StringParam()) - .value ?? - "", - width: recipe.entries.itemOutputs.firstOrNull?.longs - .firstWhere((longKeyValue) => longKeyValue.key == kWidth, orElse: () => LongParam()) - .weightRanges - .firstOrNull - ?.upper - .toString() ?? - "0", - height: recipe.entries.itemOutputs.firstOrNull?.longs - .firstWhere((longKeyValue) => longKeyValue.key == kHeight, orElse: () => LongParam()) - .weightRanges - .firstOrNull - ?.upper - .toString() ?? - "0", + name: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kName, orElse: () => StringParam()).value ?? "", + url: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kNFTURL, orElse: () => StringParam()).value.changeDomain() ?? "", + thumbnailUrl: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kThumbnailUrl, orElse: () => StringParam()).value.changeDomain() ?? "", + description: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kDescription, orElse: () => StringParam()).value ?? "", + fileSize: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kFileSize, orElse: () => StringParam()).value ?? "", + cid: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kCID, orElse: () => StringParam()).value ?? "", + appType: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kAppType, orElse: () => StringParam()).value ?? "", + creator: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kCreator, orElse: () => StringParam()).value ?? "", + fileExtension: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kFileExtension, orElse: () => StringParam()).value ?? "", + width: recipe.entries.itemOutputs.firstOrNull?.longs.firstWhere((longKeyValue) => longKeyValue.key == kWidth, orElse: () => LongParam()).weightRanges.firstOrNull?.upper.toString() ?? "0", + height: recipe.entries.itemOutputs.firstOrNull?.longs.firstWhere((longKeyValue) => longKeyValue.key == kHeight, orElse: () => LongParam()).weightRanges.firstOrNull?.upper.toString() ?? "0", amountMinted: int.parse(recipe.entries.itemOutputs.firstOrNull?.amountMinted.toString() ?? "0"), quantity: recipe.entries.itemOutputs.firstOrNull?.quantity.toInt() ?? 0, tradePercentage: royalties == "0" ? kNone : "$royalties%", price: recipe.coinInputs.firstOrNull?.coins.firstOrNull?.amount ?? "0", denom: recipe.coinInputs.firstOrNull?.coins.firstOrNull?.denom ?? "", ibcCoins: recipe.coinInputs.firstOrNull?.coins.firstOrNull?.denom.toIBCCoinsEnum() ?? IBCCoins.upylon, - assetType: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kNftFormat, orElse: () => StringParam()) - .value - .toAssetTypeEnum() ?? - AssetType.Image, - duration: recipe.entries.itemOutputs.firstOrNull?.longs - .firstWhere((longKeyValue) => longKeyValue.key == kDuration, orElse: () => LongParam()) - .weightRanges - .firstOrNull - ?.upper - .toInt() - .toSeconds() ?? - "0", + assetType: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kNftFormat, orElse: () => StringParam()).value.toAssetTypeEnum() ?? AssetType.Image, + duration: + recipe.entries.itemOutputs.firstOrNull?.longs.firstWhere((longKeyValue) => longKeyValue.key == kDuration, orElse: () => LongParam()).weightRanges.firstOrNull?.upper.toInt().toSeconds() ?? + "0", createdAt: createdAt, isEnabled: recipe.enabled, - hashtags: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kHashtags, orElse: () => StringParam()) - .value ?? - "", - realWorld: recipe.entries.itemOutputs.firstOrNull?.strings - .firstWhere((strKeyValue) => strKeyValue.key == kRealWorld, - orElse: () => StringParam(key: kRealWorld, value: "false")) - .value == - "true", + hashtags: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kHashtags, orElse: () => StringParam()).value ?? "", + realWorld: recipe.entries.itemOutputs.firstOrNull?.strings.firstWhere((strKeyValue) => strKeyValue.key == kRealWorld, orElse: () => StringParam(key: kRealWorld, value: "false")).value == "true", ); } @@ -349,20 +220,6 @@ class NFT extends Equatable { return NFT.fromRecipe(recipeEither.toOption().toNullable()!); } - - static Future eventFromRecipeId(String cookbookId, String recipeId) async { - final walletsStore = GetIt.I.get(); - final recipeEither = await walletsStore.getRecipe(cookbookId, recipeId); - - if (recipeEither.isLeft()) { - return null; - } - - return NFT.fromRecipe(recipeEither.toOption().toNullable()!); - } - - - factory NFT.fromJson(Map json) { return NFT( name: json['name'].toString(), @@ -392,30 +249,30 @@ class NFT extends Equatable { } Map toJson() => { - "name" : name, - "type" : type.name, - "url" : url, - "thumbnailUrl" : thumbnailUrl, - "description" : description, - "fileSize" : fileSize, - "creator" : creator, - "fileExtension" : fileExtension, - "cid" : cid, - "appType": appType, - "width" : width, - "height" : height, - "price" : price, - "denom" : denom, - "itemID" : itemID, - "cookbookID": cookbookID, - "owner" : owner, - "tradeID": tradeID, - "ibcCoins": ibcCoins.name, - "assetType" : assetType.name, - "duration" : duration, - "hashtags" : hashtags, - "realWorld" : realWorld, - }; + "name": name, + "type": type.name, + "url": url, + "thumbnailUrl": thumbnailUrl, + "description": description, + "fileSize": fileSize, + "creator": creator, + "fileExtension": fileExtension, + "cid": cid, + "appType": appType, + "width": width, + "height": height, + "price": price, + "denom": denom, + "itemID": itemID, + "cookbookID": cookbookID, + "owner": owner, + "tradeID": tradeID, + "ibcCoins": ibcCoins.name, + "assetType": assetType.name, + "duration": duration, + "hashtags": hashtags, + "realWorld": realWorld, + }; @override List get props => [ diff --git a/wallet/lib/utils/route_util.dart b/wallet/lib/utils/route_util.dart index e654a5de99..9ed4204b75 100644 --- a/wallet/lib/utils/route_util.dart +++ b/wallet/lib/utils/route_util.dart @@ -114,7 +114,7 @@ class RouteUtil { case Routes.transactionHistory: return createRoute(const TransactionHistoryScreen()); case Routes.eventView: - if (settings.arguments != null && settings.arguments is NFT) { + if (settings.arguments != null && settings.arguments is Events) { return createRoute(EventPassViewScreen( key: ValueKey(settings.arguments), events: settings.arguments as Events, From a6bcd6fa44295d1718e03d04a44382e66f24f299 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 23 May 2024 15:11:47 +0500 Subject: [PATCH 131/161] evently implmentation on the wallet side for Events class --- easel/lib/utils/extension_util.dart | 2 +- evently/lib/evently_provider.dart | 3 +- evently/lib/models/events.dart | 1 - wallet/assets/images/svg/diamond.svg | 3 + wallet/lib/model/event.dart | 85 +++-------------- wallet/lib/pages/events/events_screen.dart | 103 +++++++++++++++++---- wallet/lib/utils/constants.dart | 2 + 7 files changed, 103 insertions(+), 96 deletions(-) create mode 100644 wallet/assets/images/svg/diamond.svg diff --git a/easel/lib/utils/extension_util.dart b/easel/lib/utils/extension_util.dart index 17acada114..720ca890f2 100644 --- a/easel/lib/utils/extension_util.dart +++ b/easel/lib/utils/extension_util.dart @@ -232,7 +232,7 @@ extension GetCoinWithProperDenomination on String { } } - String getEaselInputCoinWithDenomination(String amount) { + String getEaselInputCoinWithDenomination(String amount) { if (this == kUsdSymbol) { return double.parse(amount).toStringAsFixed(2); } else if (this == kPylonSymbol) { diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index f2fcf296b6..49839808dc 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -283,7 +283,7 @@ class EventlyProvider extends ChangeNotifier { description: description, numberOfTickets: numberOfTickets.toString(), price: price.toString(), - listOfPerks: perks.join(','), + listOfPerks: perks.map((e) => jsonEncode(e)).toList().toString(), cookbookID: _cookbookId!, recipeID: _recipeId, step: '', @@ -294,7 +294,6 @@ class EventlyProvider extends ChangeNotifier { recipeId: _recipeId, isFreeDrop: isFreeDrop, symbol: selectedDenom.symbol, - perksList: perks, price: price.toString(), ); diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 65e21b28e6..b9d32b2f1c 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -125,7 +125,6 @@ extension CreateRecipe on Events { required String recipeId, required FreeDrop isFreeDrop, required String symbol, - required List perksList, required String price, }) { return Recipe( diff --git a/wallet/assets/images/svg/diamond.svg b/wallet/assets/images/svg/diamond.svg new file mode 100644 index 0000000000..4e874f6959 --- /dev/null +++ b/wallet/assets/images/svg/diamond.svg @@ -0,0 +1,3 @@ + + + diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index 7ad8af98c1..76944bbe48 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -1,8 +1,7 @@ +import 'dart:convert'; import 'package:equatable/equatable.dart'; -import 'package:fixnum/fixnum.dart'; import 'package:get_it/get_it.dart'; import 'package:pylons_wallet/stores/wallet_store.dart'; -import '../modules/Pylonstech.pylons.pylons/module/client/cosmos/base/v1beta1/coin.pb.dart'; import '../modules/Pylonstech.pylons.pylons/module/client/pylons/recipe.pb.dart'; enum FreeDrop { yes, no, unselected } @@ -21,7 +20,7 @@ class Events extends Equatable { final String description; final String numberOfTickets; final String price; - final String listOfPerks; + final List? listOfPerks; final String isFreeDrops; final String cookbookID; final String step; @@ -44,7 +43,7 @@ class Events extends Equatable { this.description = '', ///* perks - this.listOfPerks = '', + this.listOfPerks, ///* price this.numberOfTickets = '0', @@ -62,6 +61,12 @@ class Events extends Equatable { factory Events.fromRecipe(Recipe recipe) { final Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); + + final List listOfPerks = []; + jsonDecode(map[kPerks]!).map((jsonMap) { + listOfPerks.add(PerksModel.fromJson(jsonMap as Map)); + }).toList(); + return Events( eventName: map[kEventName]!, hostName: map[kEventHostName]!, @@ -74,7 +79,7 @@ class Events extends Equatable { description: map[kDescription]!, numberOfTickets: map[kNumberOfTickets]!, price: map[kPrice]!, - listOfPerks: map[kPerks]!, + listOfPerks: listOfPerks, cookbookID: map[kCookBookId]!, recipeID: map[kRecipeId]!, ); @@ -130,74 +135,6 @@ class Events extends Equatable { } } -extension CreateRecipe on Events { - Recipe createRecipe({ - required String cookbookId, - required String recipeId, - required FreeDrop isFreeDrop, - required String symbol, - required List perksList, - required String price, - }) { - return Recipe( - cookbookId: cookbookId, - id: recipeId, - nodeVersion: Int64(1), - name: eventName.trim(), - description: description.trim(), - version: kVersion, - coinInputs: [ - if (isFreeDrop == FreeDrop.yes) - CoinInput() - else - CoinInput( - coins: [Coin(amount: price, denom: symbol)], - ) - ], - itemInputs: [], - costPerBlock: Coin(denom: kUpylon, amount: costPerBlock), - entries: EntriesList( - coinOutputs: [], - itemOutputs: [ - ItemOutput( - id: kEventlyEvent, - doubles: [], - longs: [], - strings: [ - StringParam(key: kEventName, value: eventName.trim()), - StringParam(key: kEventHostName, value: hostName.trim()), - StringParam(key: kThumbnail, value: thumbnail.trim()), - StringParam(key: kStartDate, value: startDate.trim()), - StringParam(key: kEndDate, value: endDate.trim()), - StringParam(key: kStartTime, value: startTime.trim()), - StringParam(key: kEndTime, value: endTime.trim()), - StringParam(key: kLocation, value: location.trim()), - StringParam(key: kDescription, value: description.trim()), - StringParam(key: kNumberOfTickets, value: numberOfTickets.trim()), - StringParam(key: kPrice, value: price.trim()), - StringParam(key: kPerks, value: listOfPerks.trim()), - StringParam(key: kFreeDrop, value: isFreeDrops), - StringParam(key: kCookBookId, value: cookbookID), - StringParam(key: kRecipeId, value: recipeID), - ], - mutableStrings: [], - transferFee: [Coin(denom: kPylonSymbol, amount: transferFeeAmount)], - tradeable: true, - amountMinted: Int64(), - ), - ], - itemModifyOutputs: [], - ), - outputs: [ - WeightedOutputs(entryIds: [kEventlyEvent], weight: Int64(1)) - ], - blockInterval: Int64(), - enabled: true, - extraInfo: kExtraInfo, - ); - } -} - /// Event String keys const kEventName = "kEventName"; const kEventHostName = "kEventHostName"; @@ -238,7 +175,7 @@ class PerksModel { description: description, ); - factory PerksModel.fromJson(Map map) => PerksModel(name: map['name']!, description: map['description']!); + factory PerksModel.fromJson(Map map) => PerksModel(name: map['name']!.toString(), description: map['description']!.toString()); Map toJson() { final Map map = {}; diff --git a/wallet/lib/pages/events/events_screen.dart b/wallet/lib/pages/events/events_screen.dart index c938c18544..c437035cf2 100644 --- a/wallet/lib/pages/events/events_screen.dart +++ b/wallet/lib/pages/events/events_screen.dart @@ -1,7 +1,10 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:pylons_wallet/components/space_widgets.dart'; import 'package:pylons_wallet/utils/constants.dart'; +import 'package:shimmer_animation/shimmer_animation.dart'; import '../../model/event.dart'; @@ -49,60 +52,124 @@ class _EventPassViewScreenState extends State { body: Column( children: [ Container( + margin: EdgeInsets.symmetric(horizontal: 20.w), + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 20.h), decoration: BoxDecoration(color: AppColors.kBlue), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(widget.events.eventName), + Text( + widget.events.eventName, + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 25.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), + VerticalSpace(20.h), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Date'), - Text(widget.events.startDate), + Text( + 'Date', + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + Text( + widget.events.startDate, + style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), ], ), Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Time'), - Text(widget.events.startDate), + Text( + 'Time', + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + Text( + widget.events.startDate, + style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), ], ) ], ), + VerticalSpace(20.h), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('LOCATION'), - Text(widget.events.location), + Text( + 'LOCATION', + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + Text( + widget.events.location, + style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), ], ), Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('SEAT'), - Text("no seat"), + Text( + 'SEAT', + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + Text( + "no seat", + style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), ], ) ], ), + VerticalSpace(20.h), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Date'), - Text(widget.events.startDate), + Text( + 'PERKS', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + SizedBox(height: 1.h), + Row( + children: [ + SvgPicture.asset(kDiamondIcon), + SizedBox(width: 5.w), + Text( + 'x ${widget.events.listOfPerks?.length}', + style: TextStyle(fontSize: 15.sp, color: AppColors.kWhite, fontWeight: FontWeight.bold), + ), + SizedBox(width: 5.w), + Text( + 'Redeem', + style: TextStyle(fontSize: 15.sp, color: AppColors.kGreenText, fontWeight: FontWeight.bold), + ) + ], + ) ], ), - Column( - children: [ - Text('Time'), - Text(widget.events.startDate), - ], - ) ], - ) + ), ], ), + ), + VerticalSpace(20.h), + CachedNetworkImage( + fit: BoxFit.fill, + imageUrl: widget.events.thumbnail, + errorWidget: (a, b, c) => const Center( + child: Icon( + Icons.error_outline, + color: AppColors.kWhite, + )), + placeholder: (context, url) => Shimmer(color: AppColors.kLightGray, child: const SizedBox.expand()), ) ], ), diff --git a/wallet/lib/utils/constants.dart b/wallet/lib/utils/constants.dart index ecde213025..7079f3a522 100644 --- a/wallet/lib/utils/constants.dart +++ b/wallet/lib/utils/constants.dart @@ -78,6 +78,7 @@ class AppColors { static Color kGreyColor = const Color.fromRGBO(141, 140, 140, 1); static Color kTransparentColor = Colors.transparent; static Color kGrey = Colors.grey; + static const Color kGreenText = Color(0xFF14FB00); } const double kIconSize = 24.0; @@ -333,6 +334,7 @@ const recipeIdKey = "recipeId"; /// Assets +const kDiamondIcon = "assets/images/svg/diamond.svg"; const kAlertIcon = "assets/images/icons/alert.svg"; const kUploadErrorIcon = "assets/images/icons/upload_error_background.svg"; const kSvgCloseButton = 'assets/images/svg/close_button.svg'; From 1c1b04def022b5fde8f16af0838439277673b771 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 23 May 2024 16:42:09 +0500 Subject: [PATCH 132/161] fix: flow retesting and fixed --- evently/lib/evently_provider.dart | 15 +++--- evently/lib/repository/repository.dart | 15 ++++++ .../event_hub/event_hub_view_model.dart | 8 ++-- .../widgets/delete_confirmation_dialog.dart | 2 +- .../event_hub/widgets/events_list_tile.dart | 5 +- .../datasources/local_datasource.dart | 35 ++++++++++++-- .../third_party_services/database.g.dart | 47 +++++++++++++++++-- .../third_party_services/event_dao.dart | 27 +++++++++-- 8 files changed, 129 insertions(+), 25 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 49839808dc..2fac808a01 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -272,6 +272,7 @@ class EventlyProvider extends ChangeNotifier { _recipeId = repository.autoGenerateEventlyId(); final event = Events( + id: id, eventName: eventName, hostName: hostName, thumbnail: thumbnail!, @@ -304,8 +305,8 @@ class EventlyProvider extends ChangeNotifier { return false; } scaffoldMessengerState?.show(message: LocaleKeys.recipe_created.tr()); - sl().updatePublishedEventList(); - deleteEvent(event.id); + await sl().updatePublishedEventList(); + await deleteEvent(event.id); return true; } @@ -395,19 +396,19 @@ class EventlyProvider extends ChangeNotifier { switch (uploadStep) { case UploadStep.overView: - await repository.saveEvents(saveEvent); + await repository.updateEvent(saveEvent); break; case UploadStep.detail: - await repository.saveFromDetail(saveEvent); + await repository.updateEvent(saveEvent); break; case UploadStep.perks: - await repository.saveFromPerks(saveEvent); + await repository.updateEvent(saveEvent); break; case UploadStep.price: - await repository.saveEventFromPrice(saveEvent); + await repository.updateEvent(saveEvent); break; case UploadStep.none: - // TODO: Handle this case. + break; } onCompleted(); diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 8fb27a50d3..2ba326306b 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -96,6 +96,8 @@ abstract class Repository { Future> saveFromPerks(Events events); Future> saveEventFromPrice(Events events); + + Future> updateEvent(Events events); } @LazySingleton(as: Repository) @@ -266,4 +268,17 @@ class RepositoryImp implements Repository { return Left(CacheFailure(LocaleKeys.get_error.tr())); } } + + @override + Future> updateEvent(Events events) async { + try { + final bool result = await localDataSource.updateEvent(events: events); + if (!result) { + return Left(CacheFailure(LocaleKeys.get_error.tr())); + } + return Right(result); + } on Exception catch (_) { + return Left(CacheFailure(LocaleKeys.get_error.tr())); + } + } } diff --git a/evently/lib/screens/event_hub/event_hub_view_model.dart b/evently/lib/screens/event_hub/event_hub_view_model.dart index 2bffc70541..1ab103fa89 100644 --- a/evently/lib/screens/event_hub/event_hub_view_model.dart +++ b/evently/lib/screens/event_hub/event_hub_view_model.dart @@ -124,7 +124,7 @@ class EventHubViewModel extends ChangeNotifier { notifyListeners(); } - Future deleteNft(int? id) async { + Future deleteEvents(int? id) async { final deleteNftResponse = await repository.deleteEvent(id!); if (deleteNftResponse.isLeft()) { @@ -141,8 +141,8 @@ class EventHubViewModel extends ChangeNotifier { repository.setCacheString(key: fromKey, value: kDraft); } - updatePublishedEventList() { - getDraftsList(); - getRecipesList(); + Future updatePublishedEventList() async { + await getDraftsList(); + await getRecipesList(); } } diff --git a/evently/lib/screens/event_hub/widgets/delete_confirmation_dialog.dart b/evently/lib/screens/event_hub/widgets/delete_confirmation_dialog.dart index 4873cde777..6cc9f10405 100644 --- a/evently/lib/screens/event_hub/widgets/delete_confirmation_dialog.dart +++ b/evently/lib/screens/event_hub/widgets/delete_confirmation_dialog.dart @@ -76,7 +76,7 @@ class DeleteDialog { clipperType: ClipperType.bottomLeftTopRight, onPressed: () async { Navigator.of(context).pop(); - creatorHubViewModel.deleteNft(events.id); + creatorHubViewModel.deleteEvents(events.id); }, cuttingHeight: 12.h, ), diff --git a/evently/lib/screens/event_hub/widgets/events_list_tile.dart b/evently/lib/screens/event_hub/widgets/events_list_tile.dart index cbcfae5735..df6d9d203a 100644 --- a/evently/lib/screens/event_hub/widgets/events_list_tile.dart +++ b/evently/lib/screens/event_hub/widgets/events_list_tile.dart @@ -21,9 +21,7 @@ class NFTsListTile extends StatelessWidget { EventlyProvider get _easelProvider => GetIt.I.get(); - void buildBottomSheet({required BuildContext context}) { - - } + void buildBottomSheet({required BuildContext context}) {} Widget getPublishedCard({required BuildContext context}) { return DecoratedBox( @@ -82,7 +80,6 @@ class NFTsListTile extends StatelessWidget { width: 10.w, ), InkWell( - onTap: () => buildBottomSheet(context: context), child: Padding( padding: EdgeInsets.all(4.0.w), diff --git a/evently/lib/services/datasources/local_datasource.dart b/evently/lib/services/datasources/local_datasource.dart index 97cd8acb80..07b2819f49 100644 --- a/evently/lib/services/datasources/local_datasource.dart +++ b/evently/lib/services/datasources/local_datasource.dart @@ -90,6 +90,8 @@ abstract class LocalDataSource { /// this method is used to save draft from details screen Future saveEventFromPrice({required Events events}); + + Future updateEvent({required Events events}); } @LazySingleton(as: LocalDataSource) @@ -212,7 +214,7 @@ class LocalDataSourceImpl extends LocalDataSource { @override Future saveEventFromDetail({required Events events}) async { try { - await database.eventsDao.updateNFTFromDetail( + await database.eventsDao.updateEventFromDetail( events.startDate, events.endDate, events.startTime, @@ -231,7 +233,7 @@ class LocalDataSourceImpl extends LocalDataSource { @override Future saveEventFromPerks({required Events events}) async { try { - await database.eventsDao.updateNFTFromPerks(events.listOfPerks, events.id!, events.step); + await database.eventsDao.updateEventFromPerks(events.listOfPerks, events.id!, events.step); return true; } catch (_) { throw CacheFailure(LocaleKeys.get_error.tr()); @@ -241,7 +243,34 @@ class LocalDataSourceImpl extends LocalDataSource { @override Future saveEventFromPrice({required Events events}) async { try { - await database.eventsDao.updateNFTFromPrice(events.id!, events.numberOfTickets, events.price, events.isFreeDrops, events.denom, events.step); + await database.eventsDao.updateEventFromPrice(events.id!, events.numberOfTickets, events.price, events.isFreeDrops, events.denom, events.step); + return true; + } catch (_) { + throw CacheFailure(LocaleKeys.get_error.tr()); + } + } + + @override + Future updateEvent({required Events events}) async { + try { + await database.eventsDao.updateEvent( + events.id!, + events.step, + events.eventName, + events.hostName, + events.thumbnail, + events.startDate, + events.endDate, + events.startTime, + events.endTime, + events.location, + events.description, + events.listOfPerks, + events.numberOfTickets, + events.price, + events.isFreeDrops, + events.denom, + ); return true; } catch (_) { throw CacheFailure(LocaleKeys.get_error.tr()); diff --git a/evently/lib/services/third_party_services/database.g.dart b/evently/lib/services/third_party_services/database.g.dart index ac1d0a0403..617d98b679 100644 --- a/evently/lib/services/third_party_services/database.g.dart +++ b/evently/lib/services/third_party_services/database.g.dart @@ -203,7 +203,7 @@ class _$EventsDao extends EventsDao { } @override - Future updateNFTFromDetail( + Future updateEventFromDetail( String startDate, String endDate, String startTime, @@ -228,7 +228,7 @@ class _$EventsDao extends EventsDao { } @override - Future updateNFTFromPerks( + Future updateEventFromPerks( String listOfPerks, int id, String step, @@ -239,7 +239,7 @@ class _$EventsDao extends EventsDao { } @override - Future updateNFTFromPrice( + Future updateEventFromPrice( int id, String numberOfTickets, String price, @@ -252,6 +252,47 @@ class _$EventsDao extends EventsDao { arguments: [id, numberOfTickets, price, isFreeDrops, denom, step]); } + @override + Future updateEvent( + int id, + String step, + String eventName, + String hostName, + String thumbnail, + String startDate, + String endDate, + String startTime, + String endTime, + String location, + String description, + String listOfPerks, + String numberOfTickets, + String price, + String isFreeDrops, + String denom, + ) async { + await _queryAdapter.queryNoReturn( + 'UPDATE events SET eventName = ?3, hostName = ?4, thumbnail = ?5, startDate = ?6, endDate = ?7, startTime = ?8, endTime = ?9, location = ?10, description = ?11, numberOfTickets = ?13, price = ?14, listOfPerks = ?12, isFreeDrops = ?15, step = ?2, denom = ?16 WHERE id = ?1', + arguments: [ + id, + step, + eventName, + hostName, + thumbnail, + startDate, + endDate, + startTime, + endTime, + location, + description, + listOfPerks, + numberOfTickets, + price, + isFreeDrops, + denom + ]); + } + @override Future insertEvents(Events events) { return _eventsInsertionAdapter.insertAndReturnId( diff --git a/evently/lib/services/third_party_services/event_dao.dart b/evently/lib/services/third_party_services/event_dao.dart index de4d997d99..80b801c8b3 100644 --- a/evently/lib/services/third_party_services/event_dao.dart +++ b/evently/lib/services/third_party_services/event_dao.dart @@ -16,7 +16,7 @@ abstract class EventsDao { Future delete(int id); @Query('UPDATE events SET startDate = :startDate, endDate= :endDate, startTime = :startTime, endTime = :endTime,location = :location, description = :description, step = :step WHERE id = :id') - Future updateNFTFromDetail( + Future updateEventFromDetail( String startDate, String endDate, String startTime, @@ -28,14 +28,14 @@ abstract class EventsDao { ); @Query('UPDATE events SET listOfPerks = :listOfPerks, step = :step WHERE id = :id') - Future updateNFTFromPerks( + Future updateEventFromPerks( String listOfPerks, int id, String step, ); @Query('UPDATE events SET numberOfTickets = :numberOfTickets, price = :price, isFreeDrops = :isFreeDrops, denom = :denom, step = :step WHERE id = :id') - Future updateNFTFromPrice( + Future updateEventFromPrice( int id, String numberOfTickets, String price, @@ -43,4 +43,25 @@ abstract class EventsDao { String denom, String step, ); + + @Query( + 'UPDATE events SET eventName = :eventName, hostName = :hostName, thumbnail = :thumbnail, startDate = :startDate, endDate = :endDate, startTime = :startTime, endTime = :endTime, location = :location, description = :description, numberOfTickets = :numberOfTickets, price = :price, listOfPerks = :listOfPerks, isFreeDrops = :isFreeDrops, step = :step, denom = :denom WHERE id = :id') + Future updateEvent( + int id, + String step, + String eventName, + String hostName, + String thumbnail, + String startDate, + String endDate, + String startTime, + String endTime, + String location, + String description, + String listOfPerks, + String numberOfTickets, + String price, + String isFreeDrops, + String denom, + ); } From 72681256da937a9ae42e35d0245d2e6868f2cc14 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Thu, 23 May 2024 16:42:52 +0500 Subject: [PATCH 133/161] fix: on wallet side --- wallet/lib/pages/events/events_screen.dart | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/wallet/lib/pages/events/events_screen.dart b/wallet/lib/pages/events/events_screen.dart index c437035cf2..158c2dbc27 100644 --- a/wallet/lib/pages/events/events_screen.dart +++ b/wallet/lib/pages/events/events_screen.dart @@ -160,16 +160,18 @@ class _EventPassViewScreenState extends State { ], ), ), - VerticalSpace(20.h), - CachedNetworkImage( - fit: BoxFit.fill, - imageUrl: widget.events.thumbnail, - errorWidget: (a, b, c) => const Center( - child: Icon( - Icons.error_outline, - color: AppColors.kWhite, - )), - placeholder: (context, url) => Shimmer(color: AppColors.kLightGray, child: const SizedBox.expand()), + Container( + margin: EdgeInsets.symmetric(horizontal: 20.w), + child: CachedNetworkImage( + fit: BoxFit.fill, + imageUrl: widget.events.thumbnail, + errorWidget: (a, b, c) => const Center( + child: Icon( + Icons.error_outline, + color: AppColors.kWhite, + )), + // placeholder: (context, url) => Shimmer(color: AppColors.kLightGray, child: const SizedBox.expand()), + ), ) ], ), From fa9202e07642ad212ac7977d0cc2240b332149c6 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Fri, 24 May 2024 12:37:40 +0500 Subject: [PATCH 134/161] feat: create build apk and configuration for testing --- evently/android/app/build.gradle | 10 ++++++++++ evently/android/app/src/main/AndroidManifest.xml | 1 + 2 files changed, 11 insertions(+) diff --git a/evently/android/app/build.gradle b/evently/android/app/build.gradle index 3fbaa808c7..8f00827493 100644 --- a/evently/android/app/build.gradle +++ b/evently/android/app/build.gradle @@ -51,11 +51,21 @@ android { versionName flutterVersionName } + signingConfigs { + debug { + storeFile file('keystore.jks') + storePassword 'tech.pylons' + keyAlias 'key_tech_pylon' + keyPassword 'tech.pylons' + } + } + buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } diff --git a/evently/android/app/src/main/AndroidManifest.xml b/evently/android/app/src/main/AndroidManifest.xml index be1012d065..f435665a8b 100644 --- a/evently/android/app/src/main/AndroidManifest.xml +++ b/evently/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + Date: Mon, 27 May 2024 15:32:13 +0500 Subject: [PATCH 135/161] feat: dynamic link sharing of event from non nft creation --- .../handlers/create_recipe_handler.dart | 3 +- .../pages/detailed_asset_view/owner_view.dart | 12 +- .../owner_view_view_model.dart | 17 ++ wallet/lib/pages/events/events_screen.dart | 54 +++- .../collection_screen/collection_screen.dart | 13 +- .../data_stores/remote_data_store.dart | 237 ++++++++++-------- .../lib/services/repository/repository.dart | 24 +- wallet/lib/utils/constants.dart | 2 + 8 files changed, 230 insertions(+), 132 deletions(-) diff --git a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart index 993016d65b..dc1888badd 100644 --- a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart +++ b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart @@ -11,6 +11,7 @@ import 'package:pylons_wallet/pages/home/home_provider.dart'; import 'package:pylons_wallet/providers/collections_tab_provider.dart'; import 'package:pylons_wallet/pylons_app.dart'; import 'package:pylons_wallet/stores/wallet_store.dart'; +import 'package:pylons_wallet/utils/constants.dart'; import 'package:pylons_wallet/utils/route_util.dart'; import 'package:pylons_wallet/modules/Pylonstech.pylons.pylons/module/export.dart' as pylons; @@ -38,7 +39,7 @@ class CreateRecipeHandler implements BaseHandler { if (shouldShowNFTPreview()) { final msgObj = pylons.MsgCreateRecipe.create()..mergeFromProto3Json(jsonMap); - if (msgObj.cookbookId.contains('Evently')) { + if (msgObj.cookbookId.contains(kEvently)) { final events = await Events.eventFromRecipeId(msgObj.cookbookId, msgObj.id); if (events != null) { await navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events); diff --git a/wallet/lib/pages/detailed_asset_view/owner_view.dart b/wallet/lib/pages/detailed_asset_view/owner_view.dart index 9cc4eb74b8..c21239642d 100644 --- a/wallet/lib/pages/detailed_asset_view/owner_view.dart +++ b/wallet/lib/pages/detailed_asset_view/owner_view.dart @@ -288,7 +288,7 @@ class _CollapsedBottomMenuState extends State<_CollapsedBottomMenu> { final ibcEnumCoins = viewModel.nft.ibcCoins; /// This change will reflect only for upylon ibcCoins - final coinWithDenom =ibcEnumCoins.getAbbrev() == constants.kPYLN_ABBREVATION + final coinWithDenom = ibcEnumCoins.getAbbrev() == constants.kPYLN_ABBREVATION ? "\$${ibcEnumCoins.pylnToCredit(viewModel.nft.ibcCoins.getCoinWithProperDenomination(viewModel.nft.price))} ${viewModel.nft.ibcCoins.getAbbrev()}" : "${ibcEnumCoins.getCoinWithProperDenomination(viewModel.nft.price)} ${ibcEnumCoins.getAbbrev()}"; @@ -334,9 +334,7 @@ class _CollapsedBottomMenuState extends State<_CollapsedBottomMenu> { children: [ if (viewModel.nft.type != NftType.TYPE_ITEM) Text( - viewModel.nft.price == "0" - ? LocaleKeys.free.tr() - : coinWithDenom, + viewModel.nft.price == "0" ? LocaleKeys.free.tr() : coinWithDenom, style: TextStyle(color: Colors.white, fontSize: 15.sp, fontWeight: FontWeight.bold), ) ], @@ -402,7 +400,7 @@ class __ExpandedBottomMenuState extends State<_ExpandedBottomMenu> { final ibcEnumCoins = viewModel.nft.ibcCoins; // This change will reflect only for upylon ibcCoins - final coinWithDenom = ibcEnumCoins.getAbbrev() == constants.kPYLN_ABBREVATION + final coinWithDenom = ibcEnumCoins.getAbbrev() == constants.kPYLN_ABBREVATION ? "\$${ibcEnumCoins.pylnToCredit(viewModel.nft.ibcCoins.getCoinWithProperDenomination(viewModel.nft.price))} ${viewModel.nft.ibcCoins.getAbbrev()}" : "${ibcEnumCoins.getCoinWithProperDenomination(viewModel.nft.price)} ${ibcEnumCoins.getAbbrev()}"; @@ -664,9 +662,7 @@ class __ExpandedBottomMenuState extends State<_ExpandedBottomMenu> { children: [ if (viewModel.nft.type != NftType.TYPE_ITEM) ...[ Text( - viewModel.nft.price == "0" - ? LocaleKeys.free.tr() - : coinWithDenom, + viewModel.nft.price == "0" ? LocaleKeys.free.tr() : coinWithDenom, style: TextStyle(color: Colors.white, fontSize: 15.sp, fontWeight: FontWeight.bold), ) ] diff --git a/wallet/lib/pages/detailed_asset_view/owner_view_view_model.dart b/wallet/lib/pages/detailed_asset_view/owner_view_view_model.dart index 232c7a990a..743ac368bb 100644 --- a/wallet/lib/pages/detailed_asset_view/owner_view_view_model.dart +++ b/wallet/lib/pages/detailed_asset_view/owner_view_view_model.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:just_audio/just_audio.dart'; import 'package:pylons_wallet/components/loading.dart'; import 'package:pylons_wallet/model/common.dart'; +import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/model/nft.dart'; import 'package:pylons_wallet/model/nft_ownership_history.dart'; import 'package:pylons_wallet/pages/detailed_asset_view/widgets/create_trade_bottom_sheet.dart'; @@ -32,6 +33,9 @@ class OwnerViewViewModel extends ChangeNotifier { final VideoPlayerHelper videoPlayerHelper; final ShareHelper shareHelper; + ///* for events + late Events events; + OwnerViewViewModel({ required this.repository, required this.walletsStore, @@ -473,6 +477,19 @@ class OwnerViewViewModel extends ChangeNotifier { }); } + Future shareEventsLink({required Size size}) async { + final address = accountPublicInfo.publicAddress; + + final link = await repository.createDynamicLinkForRecipeEventShare(address: address, events: events); + return link.fold((l) { + LocaleKeys.something_wrong.tr().show(); + return null; + }, (r) { + shareHelper.shareText(text: r, size: size); + return null; + }); + } + Future changeNFTEnabledDisableStatus({required bool enabled}) async { final response = await repository.enableDisableRecipe( cookBookId: CookbookId(nft.cookbookID), diff --git a/wallet/lib/pages/events/events_screen.dart b/wallet/lib/pages/events/events_screen.dart index 158c2dbc27..34144bad08 100644 --- a/wallet/lib/pages/events/events_screen.dart +++ b/wallet/lib/pages/events/events_screen.dart @@ -2,11 +2,12 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get_it/get_it.dart'; +import 'package:provider/provider.dart'; import 'package:pylons_wallet/components/space_widgets.dart'; +import 'package:pylons_wallet/model/event.dart'; +import 'package:pylons_wallet/pages/detailed_asset_view/owner_view_view_model.dart'; import 'package:pylons_wallet/utils/constants.dart'; -import 'package:shimmer_animation/shimmer_animation.dart'; - -import '../../model/event.dart'; class EventPassViewScreen extends StatefulWidget { const EventPassViewScreen({super.key, required this.events}); @@ -18,8 +19,31 @@ class EventPassViewScreen extends StatefulWidget { } class _EventPassViewScreenState extends State { + OwnerViewViewModel ownerViewViewModel = GetIt.I.get(); + + @override + void initState() { + super.initState(); + ownerViewViewModel.events = widget.events; + } + @override Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: ownerViewViewModel, + builder: (_, __) => const EventPassViewContent(), + ); + } +} + +class EventPassViewContent extends StatelessWidget { + const EventPassViewContent({ + super.key, + }); + + @override + Widget build(BuildContext context) { + final viewModel = context.watch(); return ColoredBox( color: AppColors.kBlack87, child: SafeArea( @@ -44,7 +68,13 @@ class _EventPassViewScreenState extends State { color: AppColors.kWhite, ), ), - SvgPicture.asset(shareIcon), + GestureDetector( + onTap: () { + final Size size = MediaQuery.of(context).size; + viewModel.shareEventsLink(size: size); + }, + child: SvgPicture.asset(shareIcon), + ), ], ), ), @@ -59,7 +89,7 @@ class _EventPassViewScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - widget.events.eventName, + viewModel.events.eventName, style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 25.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), VerticalSpace(20.h), @@ -74,7 +104,7 @@ class _EventPassViewScreenState extends State { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - widget.events.startDate, + viewModel.events.startDate, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -87,7 +117,7 @@ class _EventPassViewScreenState extends State { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - widget.events.startDate, + viewModel.events.startTime, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -106,7 +136,7 @@ class _EventPassViewScreenState extends State { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - widget.events.location, + viewModel.events.location, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -115,11 +145,11 @@ class _EventPassViewScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'SEAT', + 'PRICE', style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - "no seat", + viewModel.events.price == "0" ? "Free" : viewModel.events.price, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -143,7 +173,7 @@ class _EventPassViewScreenState extends State { SvgPicture.asset(kDiamondIcon), SizedBox(width: 5.w), Text( - 'x ${widget.events.listOfPerks?.length}', + 'x ${viewModel.events.listOfPerks?.length}', style: TextStyle(fontSize: 15.sp, color: AppColors.kWhite, fontWeight: FontWeight.bold), ), SizedBox(width: 5.w), @@ -164,7 +194,7 @@ class _EventPassViewScreenState extends State { margin: EdgeInsets.symmetric(horizontal: 20.w), child: CachedNetworkImage( fit: BoxFit.fill, - imageUrl: widget.events.thumbnail, + imageUrl: viewModel.events.thumbnail, errorWidget: (a, b, c) => const Center( child: Icon( Icons.error_outline, diff --git a/wallet/lib/pages/home/collection_screen/collection_screen.dart b/wallet/lib/pages/home/collection_screen/collection_screen.dart index 37fdabbc15..af79b539bf 100644 --- a/wallet/lib/pages/home/collection_screen/collection_screen.dart +++ b/wallet/lib/pages/home/collection_screen/collection_screen.dart @@ -7,16 +7,17 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:pylons_wallet/components/loading.dart'; import 'package:pylons_wallet/gen/assets.gen.dart'; +import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/model/nft.dart'; import 'package:pylons_wallet/pages/home/collection_screen/collection_view_model.dart'; import 'package:pylons_wallet/pages/home/collection_screen/widgets/creation_collection_sheet.dart'; import 'package:pylons_wallet/pages/home/collection_screen/widgets/show_recipe_json.dart'; import 'package:pylons_wallet/providers/items_provider.dart'; import 'package:pylons_wallet/providers/recipes_provider.dart'; +import 'package:pylons_wallet/pylons_app.dart'; import 'package:pylons_wallet/utils/constants.dart'; import 'package:pylons_wallet/utils/enums.dart'; import 'package:pylons_wallet/utils/route_util.dart'; - import '../../../providers/collections_tab_provider.dart'; import 'widgets/purchase_collection_sheet.dart'; import 'widgets/trades_collection_sheet.dart'; @@ -181,9 +182,17 @@ class NONNftCreations extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 16.w), itemBuilder: (context, index) => InkWell( onTap: () { + final recipe = viewModel.nonNFTRecipes[index]; + + if (recipe.cookbookId.contains(kEvently)) { + final events = Events.fromRecipe(recipe); + navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events); + return; + } + final showRecipeJsonDialog = ShowRecipeJsonDialog( context: context, - recipe: viewModel.nonNFTRecipes[index], + recipe: recipe, ); showRecipeJsonDialog.show(); }, diff --git a/wallet/lib/services/data_stores/remote_data_store.dart b/wallet/lib/services/data_stores/remote_data_store.dart index b0a2be6dac..27b375f826 100644 --- a/wallet/lib/services/data_stores/remote_data_store.dart +++ b/wallet/lib/services/data_stores/remote_data_store.dart @@ -17,6 +17,7 @@ import 'package:protobuf/protobuf.dart'; import 'package:pylons_wallet/ipc/handler/handler_factory.dart'; import 'package:pylons_wallet/model/amount.dart'; import 'package:pylons_wallet/model/balance.dart'; +import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/model/execution_list_by_recipe_response.dart'; import 'package:pylons_wallet/model/export.dart'; import 'package:pylons_wallet/model/nft.dart'; @@ -88,8 +89,7 @@ abstract class RemoteDataStore { /// Input: [StripeGetLoginBasedOnAddressRequest] the request parameters that are required for the login link /// Output : [StripeGetLoginBasedOnAddressResponse] contains the response for the login link /// else will through error - Future getLoginLinkBasedOnAddress( - {required StripeGetLoginBasedOnAddressRequest req}); + Future getLoginLinkBasedOnAddress({required StripeGetLoginBasedOnAddressRequest req}); /// This method will return the ibc coin info /// Input: [ibcHash] hash of the ibc coin @@ -117,8 +117,7 @@ abstract class RemoteDataStore { /// and [offset] is the last item's position /// Output: if successful will return [List][NotificationMessage] the list of the NotificationMessage /// else will throw error - Future> getAllNotificationMessages( - {required String walletAddress, required int limit, required int offset}); + Future> getAllNotificationMessages({required String walletAddress, required int limit, required int offset}); /// This method will update the recipe in the chain /// Input: [updateRecipeModel] contains the info regarding the recipe update @@ -292,8 +291,7 @@ abstract class RemoteDataStore { /// This method will create dynamic link for the nft share purchase /// Input : [address] the address & [itemId] the id of the item& [cookbookId] against which the invite link to be generated /// Output: [String] return the generated dynamic link else will throw error - Future createDynamicLinkForItemNftShare( - {required String address, required String itemId, required String cookbookId}); + Future createDynamicLinkForItemNftShare({required String address, required String itemId, required String cookbookId}); /// This method will create User account based on account public info /// Input: [publicInfo] contains info related to user chain address, [walletCreationModel] contains user entered data, [appCheckToken] the app specific token, [referralToken] the invitee user address @@ -358,6 +356,11 @@ abstract class RemoteDataStore { Future createTrade({required pylons.MsgCreateTrade msgCreateTrade}); Future> getPylonItem({required Address address}); + + /// This method will create dynamic link for the event share recipe + /// Input : [address] the address & [Event] against which the invite link to be generated + /// Output: [String] return the generated dynamic link else will throw error + Future createDynamicLinkForRecipeEventShare({required String address, required Events events}); } class RemoteDataStoreImp implements RemoteDataStore { @@ -525,17 +528,14 @@ class RemoteDataStoreImp implements RemoteDataStore { @override Future generateStripeRegistrationToken({required String address}) async { final baseEnv = getBaseEnv(); - final response = await httpClient - .get(Uri.parse("${baseEnv.baseStripeUrl}/generate-registration-token?address=$address")) - .timeout( + final response = await httpClient.get(Uri.parse("${baseEnv.baseStripeUrl}/generate-registration-token?address=$address")).timeout( timeOutDuration, ); log(response.body, name: "Stripe | generateStripeRegistrationToken"); if (response.statusCode == API_SUCCESS_CODE) { - return StripeGenerateRegistrationTokenResponse.fromJson( - json.decode(utf8.decode(response.bodyBytes)) as Map); + return StripeGenerateRegistrationTokenResponse.fromJson(json.decode(utf8.decode(response.bodyBytes)) as Map); } if (response.statusCode == API_ERROR_CODE && response.body == 'account already exists\n') { @@ -555,8 +555,7 @@ class RemoteDataStoreImp implements RemoteDataStore { .timeout(timeOutDuration); if (response.statusCode == API_SUCCESS_CODE) { - return StripeGenerateUpdateTokenResponse.fromJson( - json.decode(utf8.decode(response.bodyBytes)) as Map); + return StripeGenerateUpdateTokenResponse.fromJson(json.decode(utf8.decode(response.bodyBytes)) as Map); } log(response.body, name: "Stripe | generateUpdateToken"); @@ -585,8 +584,7 @@ class RemoteDataStoreImp implements RemoteDataStore { } @override - Future getAccountLinkBasedOnUpdateToken( - {required StripeUpdateAccountRequest req}) async { + Future getAccountLinkBasedOnUpdateToken({required StripeUpdateAccountRequest req}) async { final baseEnv = getBaseEnv(); final response = await httpClient .post( @@ -624,8 +622,7 @@ class RemoteDataStoreImp implements RemoteDataStore { } @override - Future getLoginLinkBasedOnAddress( - {required StripeGetLoginBasedOnAddressRequest req}) async { + Future getLoginLinkBasedOnAddress({required StripeGetLoginBasedOnAddressRequest req}) async { final baseEnv = getBaseEnv(); final response = await httpClient .post( @@ -637,8 +634,7 @@ class RemoteDataStoreImp implements RemoteDataStore { log(response.body, name: "Stripe | getLoginLinkBasedOnAddress"); if (response.statusCode == API_SUCCESS_CODE) { - return StripeGetLoginBasedOnAddressResponse.from( - json.decode(utf8.decode(response.bodyBytes)) as Map); + return StripeGetLoginBasedOnAddressResponse.from(json.decode(utf8.decode(response.bodyBytes)) as Map); } throw HandlerFactory.ERR_SOMETHING_WENT_WRONG; @@ -716,8 +712,7 @@ class RemoteDataStoreImp implements RemoteDataStore { for (final ibcHashResult in ibcHashResults) { final relatedBalance = response.balances.firstWhere((element) => element.denom == "ibc/${ibcHashResult.ibcHash}"); - balances.add(Balance( - denom: ibcHashResult.denomTrace.baseDenom.name, amount: Amount(Decimal.parse(relatedBalance.amount)))); + balances.add(Balance(denom: ibcHashResult.denomTrace.baseDenom.name, amount: Amount(Decimal.parse(relatedBalance.amount)))); } return balances; @@ -745,8 +740,7 @@ class RemoteDataStoreImp implements RemoteDataStore { } @override - Future> getAllNotificationMessages( - {required String walletAddress, required int limit, required int offset}) async { + Future> getAllNotificationMessages({required String walletAddress, required int limit, required int offset}) async { final baseApiUrl = getBaseEnv().baseMongoUrl; final uri = Uri.parse("$baseApiUrl/api/notifications/getAllNotifications/$walletAddress/$limit/$offset"); @@ -907,18 +901,20 @@ class RemoteDataStoreImp implements RemoteDataStore { @override Future getRecipe({required CookbookId cookBookId, required RecipeId recipeId}) async { - - try{ + try { const retry = RetryOptions(); - final response = await retry.retry(()async { - final pylons.QueryClient queryClient = getQueryClient(); - final request = pylons.QueryGetRecipeRequest.create() - ..cookbookId = cookBookId.toString() - ..id = recipeId.toString(); - - final response = await queryClient.recipe(request); - return response; - }, retryIf: (_) => true ,); + final response = await retry.retry( + () async { + final pylons.QueryClient queryClient = getQueryClient(); + final request = pylons.QueryGetRecipeRequest.create() + ..cookbookId = cookBookId.toString() + ..id = recipeId.toString(); + + final response = await queryClient.recipe(request); + return response; + }, + retryIf: (_) => true, + ); if (response.hasRecipe()) { return response.recipe; @@ -972,23 +968,25 @@ class RemoteDataStoreImp implements RemoteDataStore { @override Future getCookbookBasedOnId({required String cookBookId}) async { - try{ + try { const retry = RetryOptions(); - final response = await retry.retry(()async { - final pylons.QueryClient queryClient = getQueryClient(); - final request = pylons.QueryGetCookbookRequest.create()..id = cookBookId; + final response = await retry.retry( + () async { + final pylons.QueryClient queryClient = getQueryClient(); + final request = pylons.QueryGetCookbookRequest.create()..id = cookBookId; - final response = await queryClient.cookbook(request); - return response; - }, retryIf: (_) => true,); + final response = await queryClient.cookbook(request); + return response; + }, + retryIf: (_) => true, + ); if (response.hasCookbook()) { return response.cookbook; } throw CookBookNotFoundFailure(LocaleKeys.cookbook_not_found.tr()); - } catch (_) { throw CookBookNotFoundFailure(LocaleKeys.cookbook_not_found.tr()); } @@ -1010,16 +1008,14 @@ class RemoteDataStoreImp implements RemoteDataStore { } @override - Future getExecutionsByRecipeId( - {required String cookBookId, required String recipeId}) async { + Future getExecutionsByRecipeId({required String cookBookId, required String recipeId}) async { final pylons.QueryClient queryClient = getQueryClient(); final queryExecutionListByRecipe = pylons.QueryListExecutionsByRecipeRequest() ..cookbookId = cookBookId ..recipeId = recipeId; final response = await queryClient.listExecutionsByRecipe(queryExecutionListByRecipe); - return ExecutionListByRecipeResponse( - completedExecutions: response.completedExecutions, pendingExecutions: response.pendingExecutions); + return ExecutionListByRecipeResponse(completedExecutions: response.completedExecutions, pendingExecutions: response.pendingExecutions); } @override @@ -1063,8 +1059,7 @@ class RemoteDataStoreImp implements RemoteDataStore { } @override - Future updateFcmToken( - {required String address, required String fcmToken, required String appCheckToken}) async { + Future updateFcmToken({required String address, required String fcmToken, required String appCheckToken}) async { final baseApiUrl = getBaseEnv().baseMongoUrl; final uri = Uri.parse("$baseApiUrl/api/fcmtoken/update/$address/$fcmToken"); @@ -1088,9 +1083,7 @@ class RemoteDataStoreImp implements RemoteDataStore { final uri = Uri.parse("$baseApiUrl/api/notifications/markread"); - final response = await httpClient.post(uri, - headers: {FIREBASE_APP_CHECK_HEADER: appCheckToken, 'Content-type': 'application/json'}, - body: jsonEncode({kNotificationsIds: idsList})); + final response = await httpClient.post(uri, headers: {FIREBASE_APP_CHECK_HEADER: appCheckToken, 'Content-type': 'application/json'}, body: jsonEncode({kNotificationsIds: idsList})); if (response.statusCode == API_SUCCESS_CODE) { final historyMap = jsonDecode(response.body); @@ -1157,8 +1150,7 @@ class RemoteDataStoreImp implements RemoteDataStore { @override Future sendGoogleInAppPurchaseCoinsRequest(GoogleInAppPurchaseModel googleInAppPurchaseModel) async { - final msgGoogleInAPPPurchase = pylons.MsgGoogleInAppPurchaseGetCoins() - ..mergeFromProto3Json(googleInAppPurchaseModel.toJson()); + final msgGoogleInAPPPurchase = pylons.MsgGoogleInAppPurchaseGetCoins()..mergeFromProto3Json(googleInAppPurchaseModel.toJson()); final customTransactionSigningGateway = getCustomTransactionSigningGateway(); @@ -1224,31 +1216,27 @@ class RemoteDataStoreImp implements RemoteDataStore { @override Future createDynamicLinkForRecipeNftShare({required String address, required NFT nft}) async { - final updateText = nft.ibcCoins.getAbbrev() == constants.kPYLN_ABBREVATION? - "\$${nft.ibcCoins.pylnToCredit(nft.ibcCoins.getCoinWithProperDenomination(nft.price))}" + final updateText = nft.ibcCoins.getAbbrev() == constants.kPYLN_ABBREVATION + ? "\$${nft.ibcCoins.pylnToCredit(nft.ibcCoins.getCoinWithProperDenomination(nft.price))}" : "${nft.ibcCoins.getCoinWithProperDenomination(nft.price)} ${nft.ibcCoins.getAbbrev()}"; final dynamicLinkParams = DynamicLinkParameters( - link: Uri.parse("$bigDipperBaseLink?recipe_id=${nft.recipeID}&cookbook_id=${nft.cookbookID}&address=$address"), - uriPrefix: kDeepLink, - androidParameters: AndroidParameters( - packageName: packageName, - fallbackUrl: - Uri.parse("$bigDipperBaseLink?recipe_id=${nft.recipeID}&cookbook_id=${nft.cookbookID}&address=$address"), - ), - iosParameters: IOSParameters( - bundleId: bundleId, - fallbackUrl: - Uri.parse("$bigDipperBaseLink?recipe_id=${nft.recipeID}&cookbook_id=${nft.cookbookID}&address=$address"), - ), - navigationInfoParameters: const NavigationInfoParameters(forcedRedirectEnabled: true), - socialMetaTagParameters: SocialMetaTagParameters( - title: nft.name, - imageUrl: Uri.parse(nft.url), - description: '${nft.description} Price:${nft.price == "0" ? LocaleKeys.free.tr() :updateText}' - , - ) - ); + link: Uri.parse("$bigDipperBaseLink?recipe_id=${nft.recipeID}&cookbook_id=${nft.cookbookID}&address=$address"), + uriPrefix: kDeepLink, + androidParameters: AndroidParameters( + packageName: packageName, + fallbackUrl: Uri.parse("$bigDipperBaseLink?recipe_id=${nft.recipeID}&cookbook_id=${nft.cookbookID}&address=$address"), + ), + iosParameters: IOSParameters( + bundleId: bundleId, + fallbackUrl: Uri.parse("$bigDipperBaseLink?recipe_id=${nft.recipeID}&cookbook_id=${nft.cookbookID}&address=$address"), + ), + navigationInfoParameters: const NavigationInfoParameters(forcedRedirectEnabled: true), + socialMetaTagParameters: SocialMetaTagParameters( + title: nft.name, + imageUrl: Uri.parse(nft.url), + description: '${nft.description} Price:${nft.price == "0" ? LocaleKeys.free.tr() : updateText}', + )); final link = await dynamicLinksGenerator.buildShortLink( dynamicLinkParams, @@ -1280,9 +1268,7 @@ class RemoteDataStoreImp implements RemoteDataStore { final unsignedTransaction = UnsignedAlanTransaction(messages: [msgObj]); - final result = await customTransactionSigningGateway - .signTransaction(transaction: unsignedTransaction, walletLookupKey: walletLookupKey) - .mapError((error) { + final result = await customTransactionSigningGateway.signTransaction(transaction: unsignedTransaction, walletLookupKey: walletLookupKey).mapError((error) { throw error; }).flatMap( (signed) => customTransactionSigningGateway.broadcastTransaction( @@ -1328,9 +1314,7 @@ class RemoteDataStoreImp implements RemoteDataStore { final dynamicLinkParams = DynamicLinkParameters( link: Uri.parse("$bigDipperBaseLink?item_id=$itemId&cookbook_id=$cookbookId&address=$address"), uriPrefix: kDeepLink, - androidParameters: AndroidParameters( - packageName: packageName, - fallbackUrl: Uri.parse("$bigDipperBaseLink?item_id=$itemId&cookbook_id=$cookbookId&address=$address")), + androidParameters: AndroidParameters(packageName: packageName, fallbackUrl: Uri.parse("$bigDipperBaseLink?item_id=$itemId&cookbook_id=$cookbookId&address=$address")), iosParameters: const IOSParameters(bundleId: bundleId), ); @@ -1343,8 +1327,7 @@ class RemoteDataStoreImp implements RemoteDataStore { final dynamicLinkParams = DynamicLinkParameters( link: Uri.parse("$bigDipperBaseLink?trade_id=$tradeId&address=$address"), uriPrefix: kDeepLink, - androidParameters: AndroidParameters( - packageName: packageName, fallbackUrl: Uri.parse("$bigDipperBaseLink?trade_id=$tradeId&address=$address")), + androidParameters: AndroidParameters(packageName: packageName, fallbackUrl: Uri.parse("$bigDipperBaseLink?trade_id=$tradeId&address=$address")), iosParameters: const IOSParameters(bundleId: bundleId), ); @@ -1452,7 +1435,7 @@ class RemoteDataStoreImp implements RemoteDataStore { final OnLogError onLogError; @override - Future> getPylonItem({required Address address}) async{ + Future> getPylonItem({required Address address}) async { final baseApiUrl = getBaseEnv().baseApiUrl; final uri = Uri.parse("$baseApiUrl/pylons/items/${address.id}"); @@ -1465,15 +1448,43 @@ class RemoteDataStoreImp implements RemoteDataStore { final pylonItemsMap = jsonDecode(pylonItemsResponse.body); - final List pylonListItems= []; + final List pylonListItems = []; pylonItemsMap["items"].map((data) { - final pylonItems= PylonItems.fromJson(data as Map); - pylonListItems.add(pylonItems); + final pylonItems = PylonItems.fromJson(data as Map); + pylonListItems.add(pylonItems); }).toList(); return pylonListItems.toList(); } + + @override + Future createDynamicLinkForRecipeEventShare({required String address, required Events events}) async { + final updateText = '${events.eventName} is hosted by ${events.hostName} at location ${events.location} ticket price is ${events.price}'; + final dynamicLinkParams = DynamicLinkParameters( + link: Uri.parse("$bigDipperBaseLink?recipe_id=${events.recipeID}&cookbook_id=${events.cookbookID}&address=$address"), + uriPrefix: kDeepLink, + androidParameters: AndroidParameters( + packageName: packageName, + fallbackUrl: Uri.parse("$bigDipperBaseLink?recipe_id=${events.recipeID}&cookbook_id=${events.cookbookID}&address=$address"), + ), + iosParameters: IOSParameters( + bundleId: bundleId, + fallbackUrl: Uri.parse("$bigDipperBaseLink?recipe_id=${events.recipeID}&cookbook_id=${events.cookbookID}&address=$address"), + ), + navigationInfoParameters: const NavigationInfoParameters(forcedRedirectEnabled: true), + socialMetaTagParameters: SocialMetaTagParameters( + title: events.eventName, + imageUrl: Uri.parse(events.thumbnail), + description: '${events.description} Price:${events.price == "0" ? LocaleKeys.free.tr() : updateText}', + )); + + final link = await dynamicLinksGenerator.buildShortLink( + dynamicLinkParams, + shortLinkType: ShortDynamicLinkType.unguessable, + ); + return link.shortUrl.toString(); + } } class AppleInAppPurchaseModel { @@ -1495,8 +1506,7 @@ class AppleInAppPurchaseModel { receiptData = json['receiptDataBase64'] as String, creator = json['creator'] as String; - Map toJson() => - {"productId": productID, "purchaseId": purchaseID, "receiptDataBase64": receiptData, "creator": creator}; + Map toJson() => {"productId": productID, "purchaseId": purchaseID, "receiptDataBase64": receiptData, "creator": creator}; } class GoogleInAppPurchaseModel { @@ -1521,23 +1531,42 @@ class GoogleInAppPurchaseModel { signature = json['signature'] as String, creator = json['creator'] as String; - Map toJson() => { - "product_id": productID, - "purchase_token": purchaseToken, - "receipt_data_base64": getReceiptDataInBase64(), - "signature": signature, - "creator": creator - }; - - Map toJsonLocalRetry() => { - "product_id": productID, - "purchase_token": purchaseToken, - "receipt_data_base64": receiptData, - "signature": signature, - "creator": creator - }; + Map toJson() => {"product_id": productID, "purchase_token": purchaseToken, "receipt_data_base64": getReceiptDataInBase64(), "signature": signature, "creator": creator}; + + Map toJsonLocalRetry() => {"product_id": productID, "purchase_token": purchaseToken, "receipt_data_base64": receiptData, "signature": signature, "creator": creator}; String getReceiptDataInBase64() { return base64Url.encode(utf8.encode(jsonEncode(receiptData))); } } + +class EventlyDenom { + final String name; + final String symbol; + final String icon; + + EventlyDenom({required this.name, required this.symbol, required this.icon}); + + factory EventlyDenom.initial() { + return EventlyDenom(icon: '', name: '', symbol: ''); + } + + @override + String toString() { + return '{name: $name, symbol: $symbol, icon: $icon}'; + } + + Map toJson() { + final Map map = {}; + map['name'] = name; + map['symbol'] = symbol; + map['icon'] = icon; + return map; + } + + factory EventlyDenom.fromJson(Map json) => EventlyDenom( + name: json['name']!, + symbol: json['symbol']!, + icon: json['icon']!, + ); +} diff --git a/wallet/lib/services/repository/repository.dart b/wallet/lib/services/repository/repository.dart index f12b0f879d..3deae3e142 100644 --- a/wallet/lib/services/repository/repository.dart +++ b/wallet/lib/services/repository/repository.dart @@ -11,6 +11,7 @@ import 'package:internet_connection_checker/internet_connection_checker.dart'; import 'package:local_auth/local_auth.dart'; import 'package:pylons_wallet/components/loading.dart'; import 'package:pylons_wallet/model/balance.dart'; +import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/model/execution_list_by_recipe_response.dart'; import 'package:pylons_wallet/model/export.dart'; import 'package:pylons_wallet/model/nft.dart'; @@ -581,6 +582,11 @@ abstract class Repository { Future> cancelTrade({required TradeId tradeId, required Address address}); Future>> getPylonItem({required Address address}); + + /// This method will create dynamic link for the event share recipe + /// Input : [address] the address against which the invite link to be generated, [Events] the event whose link to be created + /// Output: [String] return the generated dynamic link else will return [Failure] + Future> createDynamicLinkForRecipeEventShare({required String address, required Events events}); } class RepositoryImp implements Repository { @@ -913,8 +919,7 @@ class RepositoryImp implements Repository { ); return Left(StripeFailure(result.error ?? GEN_PAYMENTRECEIPT_FAILED)); } - await saveTransactionRecord( - transactionHash: "", transactionStatus: TransactionStatus.Success, txLocalModel: localTransactionModel); + await saveTransactionRecord(transactionHash: "", transactionStatus: TransactionStatus.Success, txLocalModel: localTransactionModel); return Right(StripeGeneratePaymentReceiptResponse.from(result)); } on Exception catch (error) { await saveTransactionRecord( @@ -2450,13 +2455,22 @@ class RepositoryImp implements Repository { final BaseEnv Function() getBaseEnv; @override - Future>> getPylonItem({required Address address}) async{ + Future>> getPylonItem({required Address address}) async { try { - final response = await remoteDataStore.getPylonItem(address: address); - return Right(response); + final response = await remoteDataStore.getPylonItem(address: address); + return Right(response); } on Exception catch (e) { recordErrorInCrashlytics(e); return Left(ServerFailure(e.toString())); } } + + @override + Future> createDynamicLinkForRecipeEventShare({required String address, required Events events}) async { + try { + return Right(await remoteDataStore.createDynamicLinkForRecipeEventShare(address: address, events: events)); + } on Exception catch (_) { + return Left(FirebaseDynamicLinkFailure(LocaleKeys.dynamic_link_failure.tr())); + } + } } diff --git a/wallet/lib/utils/constants.dart b/wallet/lib/utils/constants.dart index 7079f3a522..3d005d1ede 100644 --- a/wallet/lib/utils/constants.dart +++ b/wallet/lib/utils/constants.dart @@ -501,3 +501,5 @@ const String drawerKey = "drawer_key"; const String kRemaining = 'remaining'; const kTotal = 'total'; const kFileExtension = "file_extension"; + +const kEvently = "Evently"; From b285ea5a18123cd67f07be1ef00d7a271cf28a9c Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 27 May 2024 16:19:00 +0500 Subject: [PATCH 136/161] feat: owner view --- wallet/lib/ipc/ipc_engine.dart | 88 +++++++++++++++++++++++++++++++++- wallet/lib/model/event.dart | 21 +++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/wallet/lib/ipc/ipc_engine.dart b/wallet/lib/ipc/ipc_engine.dart index 1f6d27f59f..88f690a77b 100644 --- a/wallet/lib/ipc/ipc_engine.dart +++ b/wallet/lib/ipc/ipc_engine.dart @@ -14,6 +14,7 @@ import 'package:pylons_wallet/ipc/handler/handler_factory.dart'; import 'package:pylons_wallet/ipc/models/sdk_ipc_message.dart'; import 'package:pylons_wallet/ipc/models/sdk_ipc_response.dart'; import 'package:pylons_wallet/ipc/widgets/sdk_approval_dialog.dart'; +import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/model/nft.dart'; import 'package:pylons_wallet/pages/detailed_asset_view/widgets/create_trade_bottom_sheet.dart'; import 'package:pylons_wallet/providers/account_provider.dart'; @@ -92,8 +93,17 @@ class IPCEngine { } Future handleLinksBasedOnUri(String initialLink) async { - if (_isEaselUniLink(initialLink)) { + if (_isEventlyUniLink(initialLink)) { + handleEventlyLink( + link: initialLink, + getEventFromRecipe: getEventsFromRecipe, + showOwnerView: (events) => navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events), + showCreateAccountView: (events) => showCreateAccountView, + showPurchaseView: (events) => showPurchaseView, + ); + } else if (_isEaselUniLink(initialLink)) { onLogEvent(AnalyticsEventEnum.easelLink); + handleEaselLink( link: initialLink, showOwnerView: (nullableNFT) => navigatorKey.currentState!.pushNamed( @@ -198,11 +208,56 @@ class IPCEngine { walletsStore.setStateUpdatedFlag(flag: true); } + Future handleEventlyLink({ + required String link, + required Future Function({ + required String cookbookId, + required String recipeId, + }) getEventFromRecipe, + required void Function(Events?) showOwnerView, + required void Function(Events?) showCreateAccountView, + required void Function(Events?) showPurchaseView, + }) async { + final queryParameters = Uri.parse(link).queryParameters; + + final recipeId = (queryParameters.containsKey(kRecipeIdKey)) ? queryParameters[kRecipeIdKey] ?? '' : ""; + final cookbookId = (queryParameters.containsKey(kCookbookIdKey)) ? queryParameters[kCookbookIdKey] ?? "" : ""; + final currentWallet = accountProvider.accountPublicInfo; + final address = (queryParameters.containsKey(kAddress)) ? queryParameters[kAddress] ?? "" : ""; + + if (currentWallet == null) { + repository.saveInviteeAddressFromDynamicLink(dynamicLink: address); + } + + final nullableEvents = await getEventFromRecipe(cookbookId: cookbookId, recipeId: recipeId); + + if (nullableEvents == null) { + return; + } + + if (isOwnerIsViewingEvent(nullableEvents, currentWallet)) { + showOwnerView(nullableEvents); + } else { + if (currentWallet != null) { + showPurchaseView(nullableEvents); + } else { + showCreateAccountView(nullableEvents); + } + } + + walletsStore.setStateUpdatedFlag(flag: true); + } + bool isOwnerIsViewing(NFT nullableNFT, AccountPublicInfo? currentWallet) { if (currentWallet == null) return false; return nullableNFT.ownerAddress == currentWallet.publicAddress; } + bool isOwnerIsViewingEvent(Events nullableEvents, AccountPublicInfo? currentWallet) { + if (currentWallet == null) return false; + return nullableEvents.ownerAddress == currentWallet.publicAddress; + } + bool getUserAcceptPolicies() => repository.getUserAcceptPolicies().getOrElse(() => false); Future _handleNFTTradeLink(String link) async { @@ -387,6 +442,12 @@ class IPCEngine { return queryParam.containsKey(kRecipeIdKey) && queryParam.containsKey(kCookbookIdKey); } + ///This method checks if the incoming link is generated from Easel + bool _isEventlyUniLink(String link) { + final queryParam = Uri.parse(link).queryParameters; + return queryParam.containsKey(kRecipeIdKey) && queryParam.containsKey(kCookbookIdKey) && link.contains(kEvently); + } + bool _isNFTViewUniLink(String link) { final queryParam = Uri.parse(link).queryParameters; return queryParam.containsKey(kItemIdKey) && queryParam.containsKey(kCookbookIdKey); @@ -452,6 +513,31 @@ class IPCEngine { return nft; } + /// This method get the NFT based on the cookbookId and the recipeId + /// Input : [cookbookId] the id of the cookbook, [recipeId] the id of the recipe + /// Output: returns [Events] if successful otherwise returns [null] + Future getEventsFromRecipe({required String cookbookId, required String recipeId}) async { + final walletsStore = GetIt.I.get(); + + final showLoader = Loading()..showLoading(); + + final recipeResult = await walletsStore.getRecipe(cookbookId, recipeId); + + showLoader.dismiss(); + + if (recipeResult.isLeft()) { + onLogEvent(AnalyticsEventEnum.nftNotExists); + LocaleKeys.nft_does_not_exists.tr().show(); + return null; + } + + final events = Events.fromRecipe(recipeResult.toOption().toNullable()!); + + await events.getOwnerAddress(); + + return events; + } + void dispose() { _sub.cancel(); } diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index 76944bbe48..83692cdc4b 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -25,8 +25,10 @@ class Events extends Equatable { final String cookbookID; final String step; final String denom; + String ownerAddress = ""; + String owner = ""; - const Events({ + Events({ this.id, ///* overview data @@ -57,8 +59,25 @@ class Events extends Equatable { ///* for tracking where its save as draft this.step = '', + + ///* + this.ownerAddress = "", + this.owner = '', }); + Future getOwnerAddress() async { + if (ownerAddress.isEmpty) { + final walletsStore = GetIt.I.get(); + final cookbook = await walletsStore.getCookbookById(cookbookID); + ownerAddress = cookbook?.creator ?? ""; + + if (owner.isEmpty) { + owner = await walletsStore.getAccountNameByAddress(ownerAddress); + } + } + return ownerAddress; + } + factory Events.fromRecipe(Recipe recipe) { final Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); From 0d6f0720e58f6378d38fa1a107d000d452891d3e Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 27 May 2024 16:29:13 +0500 Subject: [PATCH 137/161] feat: added route for event purchase view --- .../handlers/create_recipe_handler.dart | 2 +- wallet/lib/ipc/ipc_engine.dart | 6 +- .../lib/pages/events/event_purchase_view.dart | 204 ++++++++++++++++++ ...nts_screen.dart => events_owner_view.dart} | 8 +- .../collection_screen/collection_screen.dart | 2 +- wallet/lib/utils/route_util.dart | 26 ++- 6 files changed, 233 insertions(+), 15 deletions(-) create mode 100644 wallet/lib/pages/events/event_purchase_view.dart rename wallet/lib/pages/events/{events_screen.dart => events_owner_view.dart} (97%) diff --git a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart index dc1888badd..bda982348a 100644 --- a/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart +++ b/wallet/lib/ipc/handler/handlers/create_recipe_handler.dart @@ -42,7 +42,7 @@ class CreateRecipeHandler implements BaseHandler { if (msgObj.cookbookId.contains(kEvently)) { final events = await Events.eventFromRecipeId(msgObj.cookbookId, msgObj.id); if (events != null) { - await navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events); + await navigatorKey.currentState!.pushNamed(Routes.eventOwnerView.name, arguments: events); } } else { final nft = await NFT.fromRecipeId(msgObj.cookbookId, msgObj.id); diff --git a/wallet/lib/ipc/ipc_engine.dart b/wallet/lib/ipc/ipc_engine.dart index 88f690a77b..ab76e9779e 100644 --- a/wallet/lib/ipc/ipc_engine.dart +++ b/wallet/lib/ipc/ipc_engine.dart @@ -97,9 +97,9 @@ class IPCEngine { handleEventlyLink( link: initialLink, getEventFromRecipe: getEventsFromRecipe, - showOwnerView: (events) => navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events), - showCreateAccountView: (events) => showCreateAccountView, - showPurchaseView: (events) => showPurchaseView, + showOwnerView: (events) => navigatorKey.currentState!.pushNamed(Routes.eventOwnerView.name, arguments: events), + showCreateAccountView: (events) => {}, + showPurchaseView: (events) => navigatorKey.currentState!.pushNamed(Routes.eventPurchaseView.name, arguments: events), ); } else if (_isEaselUniLink(initialLink)) { onLogEvent(AnalyticsEventEnum.easelLink); diff --git a/wallet/lib/pages/events/event_purchase_view.dart b/wallet/lib/pages/events/event_purchase_view.dart new file mode 100644 index 0000000000..d546c85997 --- /dev/null +++ b/wallet/lib/pages/events/event_purchase_view.dart @@ -0,0 +1,204 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:pylons_wallet/components/space_widgets.dart'; +import 'package:pylons_wallet/model/event.dart'; +import 'package:pylons_wallet/utils/constants.dart'; + +class EventPurchaseView extends StatefulWidget { + const EventPurchaseView({super.key, required this.events}); + + final Events events; + + @override + State createState() => _EventPurchaseViewState(); +} + +class _EventPurchaseViewState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return EventPassViewContent( + events: widget.events, + ); + } +} + +class EventPassViewContent extends StatelessWidget { + const EventPassViewContent({ + super.key, + required this.events, + }); + + final Events events; + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: AppColors.kBlack87, + child: SafeArea( + child: Scaffold( + backgroundColor: AppColors.kBlack87, + appBar: AppBar( + backgroundColor: Colors.black, + flexibleSpace: Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Icon( + Icons.arrow_back_ios, + color: AppColors.kWhite, + ), + Text( + 'Event Pass', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w700, + color: AppColors.kWhite, + ), + ), + GestureDetector( + onTap: () {}, + child: SvgPicture.asset(shareIcon), + ), + ], + ), + ), + ), + body: Column( + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: 20.w), + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 20.h), + decoration: BoxDecoration(color: AppColors.kBlue), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + events.eventName, + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 25.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), + VerticalSpace(20.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Date', + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + Text( + events.startDate, + style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Time', + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + Text( + events.startTime, + style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), + ], + ) + ], + ), + VerticalSpace(20.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'LOCATION', + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + Text( + events.location, + style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'PRICE', + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + Text( + events.price == "0" ? "Free" : events.price, + style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), + ), + ], + ) + ], + ), + VerticalSpace(20.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'PERKS', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), + ), + SizedBox(height: 1.h), + Row( + children: [ + SvgPicture.asset(kDiamondIcon), + SizedBox(width: 5.w), + Text( + 'x ${events.listOfPerks?.length}', + style: TextStyle(fontSize: 15.sp, color: AppColors.kWhite, fontWeight: FontWeight.bold), + ), + SizedBox(width: 5.w), + Text( + 'Redeem', + style: TextStyle(fontSize: 15.sp, color: AppColors.kGreenText, fontWeight: FontWeight.bold), + ) + ], + ) + ], + ), + ], + ), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 20.w), + child: CachedNetworkImage( + fit: BoxFit.fill, + imageUrl: events.thumbnail, + errorWidget: (a, b, c) => const Center( + child: Icon( + Icons.error_outline, + color: AppColors.kWhite, + )), + // placeholder: (context, url) => Shimmer(color: AppColors.kLightGray, child: const SizedBox.expand()), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/wallet/lib/pages/events/events_screen.dart b/wallet/lib/pages/events/events_owner_view.dart similarity index 97% rename from wallet/lib/pages/events/events_screen.dart rename to wallet/lib/pages/events/events_owner_view.dart index 34144bad08..17cc0f0602 100644 --- a/wallet/lib/pages/events/events_screen.dart +++ b/wallet/lib/pages/events/events_owner_view.dart @@ -9,16 +9,16 @@ import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/pages/detailed_asset_view/owner_view_view_model.dart'; import 'package:pylons_wallet/utils/constants.dart'; -class EventPassViewScreen extends StatefulWidget { - const EventPassViewScreen({super.key, required this.events}); +class EventOwnerView extends StatefulWidget { + const EventOwnerView({super.key, required this.events}); final Events events; @override - State createState() => _EventPassViewScreenState(); + State createState() => _EventOwnerViewState(); } -class _EventPassViewScreenState extends State { +class _EventOwnerViewState extends State { OwnerViewViewModel ownerViewViewModel = GetIt.I.get(); @override diff --git a/wallet/lib/pages/home/collection_screen/collection_screen.dart b/wallet/lib/pages/home/collection_screen/collection_screen.dart index af79b539bf..dbbe7ef2db 100644 --- a/wallet/lib/pages/home/collection_screen/collection_screen.dart +++ b/wallet/lib/pages/home/collection_screen/collection_screen.dart @@ -186,7 +186,7 @@ class NONNftCreations extends StatelessWidget { if (recipe.cookbookId.contains(kEvently)) { final events = Events.fromRecipe(recipe); - navigatorKey.currentState!.pushNamed(Routes.eventView.name, arguments: events); + navigatorKey.currentState!.pushNamed(Routes.eventOwnerView.name, arguments: events); return; } diff --git a/wallet/lib/utils/route_util.dart b/wallet/lib/utils/route_util.dart index 9ed4204b75..313e12ca85 100644 --- a/wallet/lib/utils/route_util.dart +++ b/wallet/lib/utils/route_util.dart @@ -1,7 +1,8 @@ // ignore_for_file: cast_nullable_to_non_nullable import 'package:flutter/material.dart'; -import 'package:pylons_wallet/pages/events/events_screen.dart'; +import 'package:pylons_wallet/pages/events/event_purchase_view.dart'; +import 'package:pylons_wallet/pages/events/events_owner_view.dart'; import 'package:pylons_wallet/pages/settings/screens/general_screen/general_screen.dart'; import 'package:pylons_wallet/pages/settings/screens/recovery_screen/recovery_screen.dart'; @@ -113,9 +114,19 @@ class RouteUtil { return createRoute(const SizedBox()); case Routes.transactionHistory: return createRoute(const TransactionHistoryScreen()); - case Routes.eventView: + case Routes.eventOwnerView: if (settings.arguments != null && settings.arguments is Events) { - return createRoute(EventPassViewScreen( + return createRoute(EventOwnerView( + key: ValueKey(settings.arguments), + events: settings.arguments as Events, + )); + } + + return createRoute(const SizedBox()); + + case Routes.eventPurchaseView: + if (settings.arguments != null && settings.arguments is Events) { + return createRoute(EventPurchaseView( key: ValueKey(settings.arguments), events: settings.arguments as Events, )); @@ -158,7 +169,8 @@ enum Routes { transactionHistory, acceptPolicy, fallback, - eventView; + eventOwnerView, + eventPurchaseView; static Routes getAppRouteFromString(String routeName) { switch (routeName) { @@ -210,8 +222,10 @@ enum Routes { return localTransactionDetails; case 'transactionHistory': return transactionHistory; - case 'eventView': - return eventView; + case 'eventOwnerView': + return eventOwnerView; + case 'eventPurchaseView': + return eventPurchaseView; default: return fallback; } From ee33aec5d3a555f951697b90f3314d20b3be5455 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 27 May 2024 17:14:51 +0500 Subject: [PATCH 138/161] feat: mobile scanner --- wallet/android/gradle.properties | 1 + wallet/macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ wallet/pubspec.yaml | 1 + 3 files changed, 4 insertions(+) diff --git a/wallet/android/gradle.properties b/wallet/android/gradle.properties index ed508580fa..fb436b2b45 100644 --- a/wallet/android/gradle.properties +++ b/wallet/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx4608m android.useAndroidX=true android.enableJetifier=true +dev.steenbakker.mobile_scanner.useUnbundled=true diff --git a/wallet/macos/Flutter/GeneratedPluginRegistrant.swift b/wallet/macos/Flutter/GeneratedPluginRegistrant.swift index 330e80cb6c..b2bb0501ea 100644 --- a/wallet/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/wallet/macos/Flutter/GeneratedPluginRegistrant.swift @@ -21,6 +21,7 @@ import google_sign_in_ios import icloud_storage import in_app_purchase_storekit import just_audio +import mobile_scanner import package_info import path_provider_foundation import share_plus @@ -46,6 +47,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { IcloudStoragePlugin.register(with: registry.registrar(forPlugin: "IcloudStoragePlugin")) InAppPurchasePlugin.register(with: registry.registrar(forPlugin: "InAppPurchasePlugin")) JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) + MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) diff --git a/wallet/pubspec.yaml b/wallet/pubspec.yaml index 1173c6a9fc..93e92e403e 100644 --- a/wallet/pubspec.yaml +++ b/wallet/pubspec.yaml @@ -70,6 +70,7 @@ dependencies: just_audio: ^0.9.29 just_the_tooltip: 0.0.12 local_auth: ^2.1.2 + mobile_scanner: ^5.1.1 mobx: ^2.0.3 modal_bottom_sheet: ^3.0.0 model_viewer_plus: From 21100c81c9fe262fdab37772ee33101ce82ac482 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Mon, 27 May 2024 17:39:03 +0500 Subject: [PATCH 139/161] feat: basic mobile scanner --- wallet/lib/pages/events/mobile_scanner.dart | 66 +++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 wallet/lib/pages/events/mobile_scanner.dart diff --git a/wallet/lib/pages/events/mobile_scanner.dart b/wallet/lib/pages/events/mobile_scanner.dart new file mode 100644 index 0000000000..dcf11ccaf8 --- /dev/null +++ b/wallet/lib/pages/events/mobile_scanner.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; + +class MobileQrScanner extends StatefulWidget { + const MobileQrScanner({super.key}); + + @override + State createState() => _MobileQrScannerState(); +} + +class _MobileQrScannerState extends State { + Barcode? _barcode; + + Widget _buildBarcode(Barcode? value) { + if (value == null) { + return const Text( + 'Scan something!', + overflow: TextOverflow.fade, + style: TextStyle(color: Colors.white), + ); + } + + return Text( + value.displayValue ?? 'No display value.', + overflow: TextOverflow.fade, + style: const TextStyle(color: Colors.white), + ); + } + + void _handleBarcode(BarcodeCapture barcodes) { + if (mounted) { + setState(() { + _barcode = barcodes.barcodes.firstOrNull; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Simple scanner')), + backgroundColor: Colors.black, + body: Stack( + children: [ + MobileScanner( + onDetect: _handleBarcode, + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + alignment: Alignment.bottomCenter, + height: 100, + color: Colors.black.withOpacity(0.4), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded(child: Center(child: _buildBarcode(_barcode))), + ], + ), + ), + ), + ], + ), + ); + } +} From 80225ae04e6a2ae3509a072a2979d04d35564ab3 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 28 May 2024 12:11:46 +0500 Subject: [PATCH 140/161] feat: qr scanner, events fixation and events class updation for Coins input --- evently/lib/models/events.dart | 4 +- .../lib/screens/host_view_ticket_preview.dart | 378 +++++++++--------- wallet/assets/images/qr_overlay.png | Bin 0 -> 75845 bytes wallet/lib/model/event.dart | 9 + .../lib/pages/events/event_purchase_view.dart | 92 ++++- .../lib/pages/events/events_owner_view.dart | 1 + wallet/lib/utils/image_util.dart | 1 + wallet/pubspec.yaml | 1 + 8 files changed, 296 insertions(+), 190 deletions(-) create mode 100644 wallet/assets/images/qr_overlay.png diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index b9d32b2f1c..e3c485461f 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -135,9 +135,7 @@ extension CreateRecipe on Events { description: description.trim(), version: kVersion, coinInputs: [ - if (isFreeDrop == FreeDrop.yes) - CoinInput() - else + if (isFreeDrop == FreeDrop.no) CoinInput( coins: [Coin(amount: price, denom: symbol)], ) diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index cfb12e50cd..f4317feda7 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -6,6 +6,7 @@ import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; import 'package:evently/screens/event_hub/event_hub_view_model.dart'; import 'package:evently/utils/constants.dart'; import 'package:evently/utils/di/di.dart'; +import 'package:evently/utils/enums.dart'; import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/space_utils.dart'; @@ -27,221 +28,228 @@ class _HostTicketPreviewState extends State { @override Widget build(BuildContext context) { return ColoredBox( - color: EventlyAppTheme.kWhite, - child: SafeArea( - child: Scaffold( - bottomNavigationBar: Container( - padding: EdgeInsets.symmetric(horizontal: 30.w), - height: 110.h, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - BottomButtons( - onPressContinue: () { - onPublishPressed(); - }, - onPressSaveDraft: () {}, - isContinueEnable: true, - clipBtnTxt: LocaleKeys.publish.tr(), + color: EventlyAppTheme.kWhite, + child: Consumer( + builder: (_, provider, __) => SafeArea( + child: Scaffold( + bottomNavigationBar: Container( + padding: EdgeInsets.symmetric(horizontal: 30.w), + height: 110.h, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + BottomButtons( + onPressContinue: () { + onPublishPressed(); + }, + onPressSaveDraft: () { + final navigator = Navigator.of(context); + provider.saveAsDraft( + onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), + uploadStep: UploadStep.price, + ); + }, + isContinueEnable: true, + clipBtnTxt: LocaleKeys.publish.tr(), + ), + ], ), - ], - ), - ), - body: SingleChildScrollView( - child: Consumer( - builder: (_, provider, __) => ClipRRect( - borderRadius: BorderRadius.circular(10.r), - child: Container( - margin: EdgeInsets.symmetric(vertical: 20.w, horizontal: 20.w), - decoration: BoxDecoration( - image: const DecorationImage(image: AssetImage(PngUtils.kHostPreview), fit: BoxFit.fitHeight), + ), + body: SingleChildScrollView( + child: Consumer( + builder: (_, provider, __) => ClipRRect( borderRadius: BorderRadius.circular(10.r), - ), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(), - child: ClipRRect( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(14), - topLeft: Radius.circular(14), - ), - child: Stack( - alignment: Alignment.bottomLeft, - children: [ - Image.asset( - PngUtils.kPhantom, - fit: BoxFit.cover, - ), - Padding( - padding: EdgeInsets.only(bottom: 10.h, left: 10.w), - child: Text( - provider.eventName, - style: TextStyle(fontSize: 20.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w700), - ), - ) - ], - ), - ), + child: Container( + margin: EdgeInsets.symmetric(vertical: 20.w, horizontal: 20.w), + decoration: BoxDecoration( + image: const DecorationImage(image: AssetImage(PngUtils.kHostPreview), fit: BoxFit.fitHeight), + borderRadius: BorderRadius.circular(10.r), ), - VerticalSpace(10.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'DATE', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Text( - provider.startDate, - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ), - ], + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(), + child: ClipRRect( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(14), + topLeft: Radius.circular(14), + ), + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Image.asset( + PngUtils.kPhantom, + fit: BoxFit.cover, + ), + Padding( + padding: EdgeInsets.only(bottom: 10.h, left: 10.w), + child: Text( + provider.eventName, + style: TextStyle(fontSize: 20.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w700), + ), + ) + ], + ), ), - Column( + ), + VerticalSpace(10.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, children: [ - Text( - 'Time', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'DATE', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + provider.startDate, + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], ), - SizedBox(height: 1.h), - Text( - provider.startTime, - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Time', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + provider.startTime, + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], ), ], ), - ], - ), - ), - VerticalSpace(20.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( + ), + VerticalSpace(20.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, children: [ - Text( - 'LOCATION', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - ConstrainedBox( - constraints: BoxConstraints(maxWidth: 1.sw / 2.4), - child: Text( - provider.location, - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - maxLines: 2, - ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'LOCATION', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 1.sw / 2.4), + child: Text( + provider.location, + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + maxLines: 2, + ), + ), + ], ), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Text( + // 'SEAT', + // style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + // ), + // SizedBox(height: 1.h), + // Column( + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // '6A', + // style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + // ), + // Text( + // 'Room 3', + // style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + // ) + // ], + // ), + // ], + // ), ], ), - // Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.start, - // children: [ - // Text( - // 'SEAT', - // style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - // ), - // SizedBox(height: 1.h), - // Column( - // mainAxisAlignment: MainAxisAlignment.start, - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text( - // '6A', - // style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - // ), - // Text( - // 'Room 3', - // style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - // ) - // ], - // ), - // ], - // ), - ], - ), - ), - VerticalSpace(20.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( + ), + VerticalSpace(20.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, children: [ - Text( - 'PERKS', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ - SvgPicture.asset(SVGUtils.kDiamond), - SizedBox(width: 5.w), Text( - 'x ${provider.perks.length}', - style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), + 'PERKS', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), ), - SizedBox(width: 5.w), - Text( - 'Redeem', - style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kGreenText, fontWeight: FontWeight.bold), + SizedBox(height: 1.h), + Row( + children: [ + SvgPicture.asset(SVGUtils.kDiamond), + SizedBox(width: 5.w), + Text( + 'x ${provider.perks.length}', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), + ), + SizedBox(width: 5.w), + Text( + 'Redeem', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kGreenText, fontWeight: FontWeight.bold), + ) + ], ) ], - ) + ), ], ), - ], - ), - ), - VerticalSpace(16.h), - Image.asset(PngUtils.kDottedLine), - VerticalSpace(40.h), - Text( - 'Scan QR to enter', - style: TextStyle( - color: EventlyAppTheme.kWhite, - fontWeight: FontWeight.bold, - fontSize: 15.sp, - ), - ), - VerticalSpace(10.h), - Container( - decoration: const BoxDecoration(color: EventlyAppTheme.kBlack), - width: 338, - height: 338, + ), + VerticalSpace(16.h), + Image.asset(PngUtils.kDottedLine), + VerticalSpace(40.h), + Text( + 'Scan QR to enter', + style: TextStyle( + color: EventlyAppTheme.kWhite, + fontWeight: FontWeight.bold, + fontSize: 15.sp, + ), + ), + VerticalSpace(10.h), + Container( + decoration: const BoxDecoration(color: EventlyAppTheme.kBlack), + width: 338, + height: 338, + ), + SizedBox(height: 20.h), + ], ), - SizedBox(height: 20.h), - ], + ), ), ), ), ), ), - ), - ), - ); + )); } Future onPublishPressed() async { diff --git a/wallet/assets/images/qr_overlay.png b/wallet/assets/images/qr_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..2c41e9992e6d21a0ebbbb49f3f6d75a512c7be8c GIT binary patch literal 75845 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 map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); + final denom = recipe.coinInputs.isEmpty ? "" : _extractCoinValue(recipe.coinInputs[0].coins)[kDenom]; + final List listOfPerks = []; jsonDecode(map[kPerks]!).map((jsonMap) { listOfPerks.add(PerksModel.fromJson(jsonMap as Map)); @@ -101,6 +104,7 @@ class Events extends Equatable { listOfPerks: listOfPerks, cookbookID: map[kCookBookId]!, recipeID: map[kRecipeId]!, + denom: denom!, ); } @@ -133,6 +137,10 @@ class Events extends Equatable { return attributeValues; } + static Map _extractCoinValue(List coins) { + return {kDenom: coins[0].denom, kPrice: coins[0].amount}; + } + @override List get props => [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price, listOfPerks, isFreeDrops, cookbookID, recipeID, step]; @@ -177,6 +185,7 @@ const transferFeeAmount = '1'; const kEventlyEvent = "Evently_Event"; const kExtraInfo = "extraInfo"; const costPerBlock = '0'; +const kDenom = "kDenom"; class PerksModel { PerksModel({required this.name, required this.description}); diff --git a/wallet/lib/pages/events/event_purchase_view.dart b/wallet/lib/pages/events/event_purchase_view.dart index d546c85997..3787aa8c75 100644 --- a/wallet/lib/pages/events/event_purchase_view.dart +++ b/wallet/lib/pages/events/event_purchase_view.dart @@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:pylons_wallet/components/space_widgets.dart'; import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/utils/constants.dart'; @@ -30,15 +31,25 @@ class _EventPurchaseViewState extends State { } class EventPassViewContent extends StatelessWidget { - const EventPassViewContent({ + EventPassViewContent({ super.key, required this.events, }); final Events events; + final MobileScannerController controller = MobileScannerController( + formats: const [BarcodeFormat.qrCode], + ); + @override Widget build(BuildContext context) { + final scanWindow = Rect.fromCenter( + center: MediaQuery.sizeOf(context).center(Offset.zero), + width: 100, + height: 100, + ); + return ColoredBox( color: AppColors.kBlack87, child: SafeArea( @@ -65,7 +76,7 @@ class EventPassViewContent extends StatelessWidget { ), GestureDetector( onTap: () {}, - child: SvgPicture.asset(shareIcon), + child: Container(), ), ], ), @@ -73,6 +84,20 @@ class EventPassViewContent extends StatelessWidget { ), body: Column( children: [ + Stack( + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: 20.w), + height: 200.h, + child: MobileScanner( + controller: controller, + onDetect: (_) { + print(_); + }, + ), + ), + ], + ), Container( margin: EdgeInsets.symmetric(horizontal: 20.w), padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 20.h), @@ -202,3 +227,66 @@ class EventPassViewContent extends StatelessWidget { ); } } + +class ScannerOverlay extends CustomPainter { + const ScannerOverlay({ + required this.scanWindow, + this.borderRadius = 12.0, + }); + + final Rect scanWindow; + final double borderRadius; + + @override + void paint(Canvas canvas, Size size) { + // TODO: use `Offset.zero & size` instead of Rect.largest + // we need to pass the size to the custom paint widget + final backgroundPath = Path()..addRect(Rect.largest); + + final cutoutPath = Path() + ..addRRect( + RRect.fromRectAndCorners( + scanWindow, + topLeft: Radius.circular(borderRadius), + topRight: Radius.circular(borderRadius), + bottomLeft: Radius.circular(borderRadius), + bottomRight: Radius.circular(borderRadius), + ), + ); + + final backgroundPaint = Paint() + ..color = Colors.black.withOpacity(0.5) + ..style = PaintingStyle.fill + ..blendMode = BlendMode.dstOut; + + final backgroundWithCutout = Path.combine( + PathOperation.difference, + backgroundPath, + cutoutPath, + ); + + final borderPaint = Paint() + ..color = Colors.white + ..style = PaintingStyle.stroke + ..strokeWidth = 4.0; + + final borderRect = RRect.fromRectAndCorners( + scanWindow, + topLeft: Radius.circular(borderRadius), + topRight: Radius.circular(borderRadius), + bottomLeft: Radius.circular(borderRadius), + bottomRight: Radius.circular(borderRadius), + ); + + // First, draw the background, + // with a cutout area that is a bit larger than the scan window. + // Finally, draw the scan window itself. + canvas.drawPath(backgroundWithCutout, backgroundPaint); + canvas.drawRRect(borderRect, borderPaint); + } + + @override + bool shouldRepaint(ScannerOverlay oldDelegate) { + return scanWindow != oldDelegate.scanWindow || borderRadius != oldDelegate.borderRadius; + } +} diff --git a/wallet/lib/pages/events/events_owner_view.dart b/wallet/lib/pages/events/events_owner_view.dart index 17cc0f0602..d8a0f89887 100644 --- a/wallet/lib/pages/events/events_owner_view.dart +++ b/wallet/lib/pages/events/events_owner_view.dart @@ -194,6 +194,7 @@ class EventPassViewContent extends StatelessWidget { margin: EdgeInsets.symmetric(horizontal: 20.w), child: CachedNetworkImage( fit: BoxFit.fill, + width: double.infinity, imageUrl: viewModel.events.thumbnail, errorWidget: (a, b, c) => const Center( child: Icon( diff --git a/wallet/lib/utils/image_util.dart b/wallet/lib/utils/image_util.dart index dab7dc5a65..9370157809 100644 --- a/wallet/lib/utils/image_util.dart +++ b/wallet/lib/utils/image_util.dart @@ -28,6 +28,7 @@ class ImageUtil { static String LIKE_FULL = 'assets/images/icons/like_full.png'; static String SPLASH_SCREEN_BG = "assets/images/splash/Splash_Screen.jpg"; static String CLOSE_ICON = 'assets/images/icons/close.png'; + static String QR_OVERLAY = "assets/images/qr_overlay.png"; static List BG_IMAGES = [ Image.asset( diff --git a/wallet/pubspec.yaml b/wallet/pubspec.yaml index 93e92e403e..fd1d85f7ca 100644 --- a/wallet/pubspec.yaml +++ b/wallet/pubspec.yaml @@ -80,6 +80,7 @@ dependencies: provider: ^6.0.3 qr_flutter: ^4.0.0 + qr_scanner_overlay: ^0.0.2 retry: ^3.1.2 share_plus: ^6.0.0 shared_preferences: ^2.0.12 From a348b37d2ce9ace3aa3b089ee5a7d122794294cd Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 28 May 2024 12:23:14 +0500 Subject: [PATCH 141/161] feat: sharable link and denom added --- wallet/lib/model/event.dart | 7 ++-- .../lib/pages/events/event_purchase_view.dart | 9 ++--- .../lib/pages/events/events_owner_view.dart | 3 +- .../data_stores/remote_data_store.dart | 36 +++---------------- 4 files changed, 12 insertions(+), 43 deletions(-) diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index a0de629b8c..ad9d0414c6 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:equatable/equatable.dart'; import 'package:get_it/get_it.dart'; +import 'package:pylons_wallet/pages/home/currency_screen/model/ibc_coins.dart'; import 'package:pylons_wallet/stores/wallet_store.dart'; import '../modules/Pylonstech.pylons.pylons/module/client/cosmos/base/v1beta1/coin.pb.dart'; import '../modules/Pylonstech.pylons.pylons/module/client/pylons/recipe.pb.dart'; @@ -25,7 +26,7 @@ class Events extends Equatable { final String isFreeDrops; final String cookbookID; final String step; - final String denom; + final IBCCoins denom; String ownerAddress = ""; String owner = ""; @@ -52,7 +53,7 @@ class Events extends Equatable { this.numberOfTickets = '0', this.price = '', this.isFreeDrops = 'unselected', - this.denom = '', + this.denom = IBCCoins.upylon, ///* other this.cookbookID = '', @@ -104,7 +105,7 @@ class Events extends Equatable { listOfPerks: listOfPerks, cookbookID: map[kCookBookId]!, recipeID: map[kRecipeId]!, - denom: denom!, + denom: denom!.toIBCCoinsEnum(), ); } diff --git a/wallet/lib/pages/events/event_purchase_view.dart b/wallet/lib/pages/events/event_purchase_view.dart index 3787aa8c75..6e8c2b4b98 100644 --- a/wallet/lib/pages/events/event_purchase_view.dart +++ b/wallet/lib/pages/events/event_purchase_view.dart @@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:pylons_wallet/components/space_widgets.dart'; import 'package:pylons_wallet/model/event.dart'; +import 'package:pylons_wallet/pages/home/currency_screen/model/ibc_coins.dart'; import 'package:pylons_wallet/utils/constants.dart'; class EventPurchaseView extends StatefulWidget { @@ -44,12 +45,6 @@ class EventPassViewContent extends StatelessWidget { @override Widget build(BuildContext context) { - final scanWindow = Rect.fromCenter( - center: MediaQuery.sizeOf(context).center(Offset.zero), - width: 100, - height: 100, - ); - return ColoredBox( color: AppColors.kBlack87, child: SafeArea( @@ -166,7 +161,7 @@ class EventPassViewContent extends StatelessWidget { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - events.price == "0" ? "Free" : events.price, + events.price == "0" ? "Free" : '${events.price} ${events.denom.getName()}', style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], diff --git a/wallet/lib/pages/events/events_owner_view.dart b/wallet/lib/pages/events/events_owner_view.dart index d8a0f89887..62c275b0b0 100644 --- a/wallet/lib/pages/events/events_owner_view.dart +++ b/wallet/lib/pages/events/events_owner_view.dart @@ -7,6 +7,7 @@ import 'package:provider/provider.dart'; import 'package:pylons_wallet/components/space_widgets.dart'; import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/pages/detailed_asset_view/owner_view_view_model.dart'; +import 'package:pylons_wallet/pages/home/currency_screen/model/ibc_coins.dart'; import 'package:pylons_wallet/utils/constants.dart'; class EventOwnerView extends StatefulWidget { @@ -149,7 +150,7 @@ class EventPassViewContent extends StatelessWidget { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - viewModel.events.price == "0" ? "Free" : viewModel.events.price, + viewModel.events.price == "0" ? "Free" : '${viewModel.events.price} ${viewModel.events.denom.getName()}', style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], diff --git a/wallet/lib/services/data_stores/remote_data_store.dart b/wallet/lib/services/data_stores/remote_data_store.dart index 27b375f826..f66686523a 100644 --- a/wallet/lib/services/data_stores/remote_data_store.dart +++ b/wallet/lib/services/data_stores/remote_data_store.dart @@ -1460,7 +1460,10 @@ class RemoteDataStoreImp implements RemoteDataStore { @override Future createDynamicLinkForRecipeEventShare({required String address, required Events events}) async { - final updateText = '${events.eventName} is hosted by ${events.hostName} at location ${events.location} ticket price is ${events.price}'; + final updateText = events.denom.getAbbrev() == constants.kPYLN_ABBREVATION + ? "\$${events.denom.pylnToCredit(events.denom.getCoinWithProperDenomination(events.price))}" + : "${events.denom.getCoinWithProperDenomination(events.price)} ${events.denom.getAbbrev()}"; + final dynamicLinkParams = DynamicLinkParameters( link: Uri.parse("$bigDipperBaseLink?recipe_id=${events.recipeID}&cookbook_id=${events.cookbookID}&address=$address"), uriPrefix: kDeepLink, @@ -1539,34 +1542,3 @@ class GoogleInAppPurchaseModel { return base64Url.encode(utf8.encode(jsonEncode(receiptData))); } } - -class EventlyDenom { - final String name; - final String symbol; - final String icon; - - EventlyDenom({required this.name, required this.symbol, required this.icon}); - - factory EventlyDenom.initial() { - return EventlyDenom(icon: '', name: '', symbol: ''); - } - - @override - String toString() { - return '{name: $name, symbol: $symbol, icon: $icon}'; - } - - Map toJson() { - final Map map = {}; - map['name'] = name; - map['symbol'] = symbol; - map['icon'] = icon; - return map; - } - - factory EventlyDenom.fromJson(Map json) => EventlyDenom( - name: json['name']!, - symbol: json['symbol']!, - icon: json['icon']!, - ); -} From c1ae72f13f9f8edc94ae4e033d439f54a593b893 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 28 May 2024 12:56:22 +0500 Subject: [PATCH 142/161] feat: qr view for purchases --- .../pages/events/event_qr_code_screen.dart | 25 +++++++++++++++++++ .../lib/pages/events/events_owner_view.dart | 6 ++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 wallet/lib/pages/events/event_qr_code_screen.dart diff --git a/wallet/lib/pages/events/event_qr_code_screen.dart b/wallet/lib/pages/events/event_qr_code_screen.dart new file mode 100644 index 0000000000..74bf610501 --- /dev/null +++ b/wallet/lib/pages/events/event_qr_code_screen.dart @@ -0,0 +1,25 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +const jsonExecuteRecipe = ''' + { + "creator": "", + "cookbook_id": "", + "recipe_id": "", + "eventName": "", + "eventPrice": "", + "eventCurrency": "", + "coinInputsIndex": 0 + } + '''; +final jsonMap = jsonDecode(jsonExecuteRecipe) as Map; + +class EventQrCodeScreen extends StatelessWidget { + const EventQrCodeScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/wallet/lib/pages/events/events_owner_view.dart b/wallet/lib/pages/events/events_owner_view.dart index 62c275b0b0..0fc4986b3c 100644 --- a/wallet/lib/pages/events/events_owner_view.dart +++ b/wallet/lib/pages/events/events_owner_view.dart @@ -204,7 +204,11 @@ class EventPassViewContent extends StatelessWidget { )), // placeholder: (context, url) => Shimmer(color: AppColors.kLightGray, child: const SizedBox.expand()), ), - ) + ), + + + + ], ), ), From 83a29b3413c7610af7d59a60cdd149fd74d6a542 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 28 May 2024 14:46:34 +0500 Subject: [PATCH 143/161] feat: qr view for excuting recipe --- wallet/lib/model/event.dart | 29 +++++++ .../lib/pages/events/event_purchase_view.dart | 1 + .../pages/events/event_qr_code_screen.dart | 85 ++++++++++++++++++- .../lib/pages/events/events_owner_view.dart | 20 ++++- 4 files changed, 128 insertions(+), 7 deletions(-) diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index ad9d0414c6..07a5d56668 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -67,6 +67,35 @@ class Events extends Equatable { this.owner = '', }); + Map toJson() { + final Map map = {}; + + final perks = []; + + listOfPerks?.map((e) => perks.add(e.toJson())).toList(); + + map['id'] = id; + map['recipeID'] = recipeID; + map['eventName'] = eventName; + map['hostName'] = hostName; + map['thumbnail'] = thumbnail; + map['startDate'] = startDate; + map['endDate'] = endDate; + map['startTime'] = startTime; + map['endTime'] = endTime; + map['location'] = location; + map['description'] = description; + map['numberOfTickets'] = numberOfTickets; + map['price'] = price; + map['isFreeDrops'] = isFreeDrops; + map['cookbookID'] = cookbookID; + map['step'] = step; + map['denom'] = denom.toString(); + map['listOfPerks'] = perks; + + return map; + } + Future getOwnerAddress() async { if (ownerAddress.isEmpty) { final walletsStore = GetIt.I.get(); diff --git a/wallet/lib/pages/events/event_purchase_view.dart b/wallet/lib/pages/events/event_purchase_view.dart index 6e8c2b4b98..c5f3edc8b7 100644 --- a/wallet/lib/pages/events/event_purchase_view.dart +++ b/wallet/lib/pages/events/event_purchase_view.dart @@ -205,6 +205,7 @@ class EventPassViewContent extends StatelessWidget { Container( margin: EdgeInsets.symmetric(horizontal: 20.w), child: CachedNetworkImage( + width: double.infinity, fit: BoxFit.fill, imageUrl: events.thumbnail, errorWidget: (a, b, c) => const Center( diff --git a/wallet/lib/pages/events/event_qr_code_screen.dart b/wallet/lib/pages/events/event_qr_code_screen.dart index 74bf610501..78381bfdd7 100644 --- a/wallet/lib/pages/events/event_qr_code_screen.dart +++ b/wallet/lib/pages/events/event_qr_code_screen.dart @@ -1,6 +1,16 @@ import 'dart:convert'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:pylons_wallet/components/buttons/custom_paint_button.dart'; +import 'package:pylons_wallet/gen/assets.gen.dart'; +import 'package:pylons_wallet/generated/locale_keys.g.dart'; +import 'package:pylons_wallet/model/event.dart'; +import 'package:pylons_wallet/pages/detailed_asset_view/widgets/nft_image_asset.dart'; +import 'package:pylons_wallet/utils/constants.dart'; +import 'package:qr_flutter/qr_flutter.dart'; const jsonExecuteRecipe = ''' { @@ -15,11 +25,80 @@ const jsonExecuteRecipe = ''' '''; final jsonMap = jsonDecode(jsonExecuteRecipe) as Map; -class EventQrCodeScreen extends StatelessWidget { - const EventQrCodeScreen({super.key}); +class EventQrCodeScreen extends StatefulWidget { + const EventQrCodeScreen({ + super.key, + required this.events, + }); + + final Events events; + + @override + State createState() => _EventQrCodeScreenState(); +} + +class _EventQrCodeScreenState extends State { + GlobalKey renderObjectKey = GlobalKey(); @override Widget build(BuildContext context) { - return const Placeholder(); + return Material( + color: AppColors.kBlack, + child: Stack( + children: [ + NftImageWidget( + url: widget.events.thumbnail, + opacity: 0.5, + ), + Padding( + padding: EdgeInsets.only(left: 23.w, top: MediaQuery.of(context).viewPadding.top + 13.h), + child: GestureDetector( + onTap: () async { + Navigator.pop(context); + }, + child: SvgPicture.asset( + Assets.images.icons.back, + height: 25.h, + ), + ), + ), + ColoredBox( + color: AppColors.kBlack.withOpacity(0.5), + child: Align( + child: RepaintBoundary( + key: renderObjectKey, + child: QrImageView( + padding: EdgeInsets.zero, + data: jsonEncode(widget.events), + size: 200, + dataModuleStyle: const QrDataModuleStyle(color: AppColors.kWhite), + eyeStyle: const QrEyeStyle(eyeShape: QrEyeShape.square, color: AppColors.kWhite), + ), + ), + ), + ), + Align( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SvgPicture.asset(Assets.images.svg.qrSideBorder), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: EdgeInsets.only(bottom: 30.h), + child: CustomPaintButton( + title: LocaleKeys.done.tr(), + bgColor: AppColors.kWhite.withOpacity(0.3), + width: 280.w, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ) + ], + ), + ); } } diff --git a/wallet/lib/pages/events/events_owner_view.dart b/wallet/lib/pages/events/events_owner_view.dart index 0fc4986b3c..337c9cd810 100644 --- a/wallet/lib/pages/events/events_owner_view.dart +++ b/wallet/lib/pages/events/events_owner_view.dart @@ -5,8 +5,10 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get_it/get_it.dart'; import 'package:provider/provider.dart'; import 'package:pylons_wallet/components/space_widgets.dart'; +import 'package:pylons_wallet/gen/assets.gen.dart'; import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/pages/detailed_asset_view/owner_view_view_model.dart'; +import 'package:pylons_wallet/pages/events/event_qr_code_screen.dart'; import 'package:pylons_wallet/pages/home/currency_screen/model/ibc_coins.dart'; import 'package:pylons_wallet/utils/constants.dart'; @@ -205,10 +207,20 @@ class EventPassViewContent extends StatelessWidget { // placeholder: (context, url) => Shimmer(color: AppColors.kLightGray, child: const SizedBox.expand()), ), ), - - - - + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (_) => EventQrCodeScreen( + events: viewModel.events, + ), + ); + }, + child: SvgPicture.asset( + Assets.images.icons.qr, + height: 20.h, + ), + ), ], ), ), From b496b2fb18baa02f145f04f7a4c95d6cd3b82436 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Tue, 28 May 2024 16:11:58 +0500 Subject: [PATCH 144/161] fix: qr view is not scannable --- .../lib/pages/events/event_purchase_view.dart | 21 +++++++++++++++++-- .../pages/events/event_qr_code_screen.dart | 2 +- .../lib/pages/events/events_owner_view.dart | 3 ++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/wallet/lib/pages/events/event_purchase_view.dart b/wallet/lib/pages/events/event_purchase_view.dart index c5f3edc8b7..a70c9bbdcd 100644 --- a/wallet/lib/pages/events/event_purchase_view.dart +++ b/wallet/lib/pages/events/event_purchase_view.dart @@ -39,12 +39,29 @@ class EventPassViewContent extends StatelessWidget { final Events events; - final MobileScannerController controller = MobileScannerController( + late Rect scanWindow; + + MobileScannerController cameraController = MobileScannerController( formats: const [BarcodeFormat.qrCode], + autoStart: true, + detectionSpeed: DetectionSpeed.noDuplicates, ); + void _foundBarcode(BarcodeCapture barcodeCapture) { + debugPrint('$barcodeCapture'); + final String code = barcodeCapture.barcodes.last.displayValue ?? ''; + debugPrint('Code: $code'); + // context.read().add(ScannerTextEvent(scannedText: code)); + } + @override Widget build(BuildContext context) { + scanWindow = Rect.fromCenter( + center: MediaQuery.sizeOf(context).center(Offset(0, -130.h)), + width: 100.r, + height: 100.r, + ); + return ColoredBox( color: AppColors.kBlack87, child: SafeArea( @@ -85,12 +102,12 @@ class EventPassViewContent extends StatelessWidget { margin: EdgeInsets.symmetric(horizontal: 20.w), height: 200.h, child: MobileScanner( - controller: controller, onDetect: (_) { print(_); }, ), ), + ], ), Container( diff --git a/wallet/lib/pages/events/event_qr_code_screen.dart b/wallet/lib/pages/events/event_qr_code_screen.dart index 78381bfdd7..fda1326899 100644 --- a/wallet/lib/pages/events/event_qr_code_screen.dart +++ b/wallet/lib/pages/events/event_qr_code_screen.dart @@ -69,7 +69,7 @@ class _EventQrCodeScreenState extends State { key: renderObjectKey, child: QrImageView( padding: EdgeInsets.zero, - data: jsonEncode(widget.events), + data: jsonEncode(widget.events.toJson()), size: 200, dataModuleStyle: const QrDataModuleStyle(color: AppColors.kWhite), eyeStyle: const QrEyeStyle(eyeShape: QrEyeShape.square, color: AppColors.kWhite), diff --git a/wallet/lib/pages/events/events_owner_view.dart b/wallet/lib/pages/events/events_owner_view.dart index 337c9cd810..ead5c1fa8a 100644 --- a/wallet/lib/pages/events/events_owner_view.dart +++ b/wallet/lib/pages/events/events_owner_view.dart @@ -207,6 +207,7 @@ class EventPassViewContent extends StatelessWidget { // placeholder: (context, url) => Shimmer(color: AppColors.kLightGray, child: const SizedBox.expand()), ), ), + SizedBox(height: 10.h), GestureDetector( onTap: () { showDialog( @@ -218,7 +219,7 @@ class EventPassViewContent extends StatelessWidget { }, child: SvgPicture.asset( Assets.images.icons.qr, - height: 20.h, + height: 40.h, ), ), ], From 9a3eaab0daa3e62c6e3586946d9cc495cf4c53ec Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Wed, 29 May 2024 16:41:28 +0500 Subject: [PATCH 145/161] feat: purchasing event with coins integration --- evently/lib/evently_provider.dart | 5 +- evently/lib/models/events.dart | 4 +- wallet/lib/model/event.dart | 26 +++ .../lib/pages/events/event_purchase_view.dart | 153 +++++++++++++++--- .../pages/events/event_qr_code_screen.dart | 13 -- .../lib/pages/events/events_owner_view.dart | 7 +- .../home/currency_screen/model/ibc_coins.dart | 17 +- .../purchase_item_view_model.dart | 43 ++++- .../failure_manager_view_model.dart | 3 + .../data_stores/remote_data_store.dart | 1 + wallet/lib/stores/wallet_store.dart | 5 + wallet/lib/stores/wallet_store_imp.dart | 88 ++++++++++ wallet/lib/utils/constants.dart | 5 + wallet/lib/utils/enums.dart | 3 +- 14 files changed, 324 insertions(+), 49 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 2fac808a01..d4065cbb26 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -290,12 +290,15 @@ class EventlyProvider extends ChangeNotifier { step: '', ); + final String prices = isFreeDrop == FreeDrop.yes ? "0" : _selectedDenom.formatAmount(price: price.toString()); + + final recipe = event.createRecipe( cookbookId: _cookbookId!, recipeId: _recipeId, isFreeDrop: isFreeDrop, symbol: selectedDenom.symbol, - price: price.toString(), + price: prices, ); final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index e3c485461f..b9d32b2f1c 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -135,7 +135,9 @@ extension CreateRecipe on Events { description: description.trim(), version: kVersion, coinInputs: [ - if (isFreeDrop == FreeDrop.no) + if (isFreeDrop == FreeDrop.yes) + CoinInput() + else CoinInput( coins: [Coin(amount: price, denom: symbol)], ) diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index 07a5d56668..aa4ce6dbd4 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -96,6 +96,32 @@ class Events extends Equatable { return map; } + factory Events.fromJson(Map json) { + final List listOfPerks = []; + + json['listOfPerks'].map((jsonData) => listOfPerks.add(PerksModel.fromJson(jsonData as Map))).toList(); + + return Events( + recipeID: json['recipeID'] as String, + eventName: json['eventName'] as String, + hostName: json['hostName'] as String, + thumbnail: json['thumbnail'] as String, + startDate: json['startDate'] as String, + endDate: json['endDate'] as String, + startTime: json['startTime'] as String, + endTime: json['endTime'] as String, + location: json['location'] as String, + description: json['description'] as String, + numberOfTickets: json['numberOfTickets'] as String, + price: json['price'] as String, + isFreeDrops: json['isFreeDrops'] as String, + cookbookID: json['cookbookID'] as String, + step: json['step'] as String, + listOfPerks: listOfPerks, + denom: json['denom'].toString().toIBCCoinsEnumforEvent(), + ); + } + Future getOwnerAddress() async { if (ownerAddress.isEmpty) { final walletsStore = GetIt.I.get(); diff --git a/wallet/lib/pages/events/event_purchase_view.dart b/wallet/lib/pages/events/event_purchase_view.dart index a70c9bbdcd..3623944b72 100644 --- a/wallet/lib/pages/events/event_purchase_view.dart +++ b/wallet/lib/pages/events/event_purchase_view.dart @@ -1,12 +1,23 @@ +import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:provider/provider.dart'; +import 'package:pylons_wallet/components/loading.dart'; import 'package:pylons_wallet/components/space_widgets.dart'; import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/pages/home/currency_screen/model/ibc_coins.dart'; +import 'package:pylons_wallet/pages/purchase_item/purchase_item_view_model.dart'; +import 'package:pylons_wallet/pages/purchase_item/widgets/trade_receipt_dialog.dart'; +import 'package:pylons_wallet/pages/purchase_item/widgets/transaction_complete_dialog.dart'; +import 'package:pylons_wallet/pylons_app.dart'; import 'package:pylons_wallet/utils/constants.dart'; +import 'package:pylons_wallet/utils/dependency_injection/dependency_injection.dart'; +import 'package:pylons_wallet/utils/route_util.dart'; +import '../../modules/Pylonstech.pylons.pylons/module/client/pylons/execution.pb.dart'; class EventPurchaseView extends StatefulWidget { const EventPurchaseView({super.key, required this.events}); @@ -23,35 +34,129 @@ class _EventPurchaseViewState extends State { super.initState(); } + final viewModel = sl(); + @override Widget build(BuildContext context) { - return EventPassViewContent( - events: widget.events, + return ChangeNotifierProvider.value( + value: viewModel, + child: EventPassViewContent( + events: widget.events, + ), ); } } -class EventPassViewContent extends StatelessWidget { - EventPassViewContent({ +class EventPassViewContent extends StatefulWidget { + const EventPassViewContent({ super.key, required this.events, }); final Events events; + @override + State createState() => _EventPassViewContentState(); +} + +class _EventPassViewContentState extends State { late Rect scanWindow; MobileScannerController cameraController = MobileScannerController( formats: const [BarcodeFormat.qrCode], - autoStart: true, detectionSpeed: DetectionSpeed.noDuplicates, ); - void _foundBarcode(BarcodeCapture barcodeCapture) { - debugPrint('$barcodeCapture'); - final String code = barcodeCapture.barcodes.last.displayValue ?? ''; - debugPrint('Code: $code'); - // context.read().add(ScannerTextEvent(scannedText: code)); + bool isCapture = false; + + Future executeRecipe(BuildContext context) async { + final provider = context.read(); + final ibcEnumCoins = provider.getEvents.denom; + + if (ibcEnumCoins == IBCCoins.ustripeusd) { + // stripePaymentForRecipe(context, provider.getEvents); + print('needed to work for this'); + } else { + paymentByCoins(); + } + } + + Future paymentByCoins() async { + final navigator = Navigator.of(navigatorKey.currentState!.overlay!.context); + final provider = context.read(); + final executionResponse = await provider.paymentForEventRecipe(); + + navigator.pop(); + if (!executionResponse.success) { + executionResponse.error.show(); + navigator.pushNamed(Routes.transactionFailure.name); + return; + } + + showTransactionCompleteDialog(execution: executionResponse.data!); + } + + void showTransactionCompleteDialog({required Execution execution}) { + final viewModel = context.read(); + + var price = double.parse(viewModel.nft.price); + final fee = double.parse(viewModel.nft.price) * 0.1; + price = price - fee; + + final txId = execution.hasId() ? execution.id : ""; + + final txTime = getTransactionTimeStamp(execution.hasTxTime() ? execution.txTime.toInt() : null); + + final model = viewModel.createTradeReciptModel( + fee: fee, + price: price, + txId: txId, + txTime: txTime, + ); + + final TradeCompleteDialog tradeCompleteDialog = TradeCompleteDialog( + model: model, + context: context, + onBackPressed: () { + showReceiptDialog(model); + }, + ); + tradeCompleteDialog.show(); + } + + void showReceiptDialog(TradeReceiptModel model) { + final TradeReceiptDialog tradeReceiptDialog = TradeReceiptDialog(context: context, model: model); + tradeReceiptDialog.show(); + } + + String getTransactionTimeStamp(int? time) { + final formatter = DateFormat('MMM dd yyyy HH:mm'); + if (time == null) { + return "${formatter.format(DateTime.now().toUtc())} $kUTC"; + } + + final int timeStamp = time * kDateConverterConstant; + final DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timeStamp, isUtc: true); + return "${formatter.format(dateTime)} $kUTC"; + } + + Future onDetect(BarcodeCapture barCodeCapture, PurchaseItemViewModel viewModel, BuildContext context) async { + if (isCapture) { + return; + } + if (!isCapture) { + setState(() => isCapture = true); + + final String? displayValue = barCodeCapture.barcodes.first.displayValue; + + final map = jsonDecode(displayValue!); + + final event = Events.fromJson(map as Map); + + viewModel.setEvents = event; + + await executeRecipe(context); + } } @override @@ -62,6 +167,11 @@ class EventPassViewContent extends StatelessWidget { height: 100.r, ); + final viewModel = context.watch(); + final coinWithDenom = widget.events.denom.getAbbrev() == kPYLN_ABBREVATION + ? "\$${widget.events.denom.pylnToCredit(widget.events.denom.getCoinWithProperDenomination(widget.events.price))} ${widget.events.denom.getAbbrev()}" + : "${widget.events.denom.getCoinWithProperDenomination(widget.events.price)} ${widget.events.denom.getAbbrev()}"; + return ColoredBox( color: AppColors.kBlack87, child: SafeArea( @@ -102,12 +212,13 @@ class EventPassViewContent extends StatelessWidget { margin: EdgeInsets.symmetric(horizontal: 20.w), height: 200.h, child: MobileScanner( - onDetect: (_) { - print(_); - }, + onDetect: (BarcodeCapture barCodeCapture) => onDetect( + barCodeCapture, + viewModel, + context, + ), ), ), - ], ), Container( @@ -118,7 +229,7 @@ class EventPassViewContent extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - events.eventName, + widget.events.eventName, style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 25.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), VerticalSpace(20.h), @@ -133,7 +244,7 @@ class EventPassViewContent extends StatelessWidget { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - events.startDate, + widget.events.startDate, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -146,7 +257,7 @@ class EventPassViewContent extends StatelessWidget { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - events.startTime, + widget.events.startTime, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -165,7 +276,7 @@ class EventPassViewContent extends StatelessWidget { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - events.location, + widget.events.location, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -178,7 +289,7 @@ class EventPassViewContent extends StatelessWidget { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - events.price == "0" ? "Free" : '${events.price} ${events.denom.getName()}', + coinWithDenom, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -202,7 +313,7 @@ class EventPassViewContent extends StatelessWidget { SvgPicture.asset(kDiamondIcon), SizedBox(width: 5.w), Text( - 'x ${events.listOfPerks?.length}', + 'x ${widget.events.listOfPerks?.length}', style: TextStyle(fontSize: 15.sp, color: AppColors.kWhite, fontWeight: FontWeight.bold), ), SizedBox(width: 5.w), @@ -224,7 +335,7 @@ class EventPassViewContent extends StatelessWidget { child: CachedNetworkImage( width: double.infinity, fit: BoxFit.fill, - imageUrl: events.thumbnail, + imageUrl: widget.events.thumbnail, errorWidget: (a, b, c) => const Center( child: Icon( Icons.error_outline, diff --git a/wallet/lib/pages/events/event_qr_code_screen.dart b/wallet/lib/pages/events/event_qr_code_screen.dart index fda1326899..b75d18e23a 100644 --- a/wallet/lib/pages/events/event_qr_code_screen.dart +++ b/wallet/lib/pages/events/event_qr_code_screen.dart @@ -12,19 +12,6 @@ import 'package:pylons_wallet/pages/detailed_asset_view/widgets/nft_image_asset. import 'package:pylons_wallet/utils/constants.dart'; import 'package:qr_flutter/qr_flutter.dart'; -const jsonExecuteRecipe = ''' - { - "creator": "", - "cookbook_id": "", - "recipe_id": "", - "eventName": "", - "eventPrice": "", - "eventCurrency": "", - "coinInputsIndex": 0 - } - '''; -final jsonMap = jsonDecode(jsonExecuteRecipe) as Map; - class EventQrCodeScreen extends StatefulWidget { const EventQrCodeScreen({ super.key, diff --git a/wallet/lib/pages/events/events_owner_view.dart b/wallet/lib/pages/events/events_owner_view.dart index ead5c1fa8a..5c1da9d385 100644 --- a/wallet/lib/pages/events/events_owner_view.dart +++ b/wallet/lib/pages/events/events_owner_view.dart @@ -47,6 +47,11 @@ class EventPassViewContent extends StatelessWidget { @override Widget build(BuildContext context) { final viewModel = context.watch(); + + final coinWithDenom = viewModel.events.denom.getAbbrev() == kPYLN_ABBREVATION + ? "\$${viewModel.events.denom.pylnToCredit(viewModel.events.denom.getCoinWithProperDenomination(viewModel.events.price))} ${viewModel.events.denom.getAbbrev()}" + : "${viewModel.events.denom.getCoinWithProperDenomination(viewModel.events.price)} ${viewModel.events.denom.getAbbrev()}"; + return ColoredBox( color: AppColors.kBlack87, child: SafeArea( @@ -152,7 +157,7 @@ class EventPassViewContent extends StatelessWidget { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - viewModel.events.price == "0" ? "Free" : '${viewModel.events.price} ${viewModel.events.denom.getName()}', + coinWithDenom, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], diff --git a/wallet/lib/pages/home/currency_screen/model/ibc_coins.dart b/wallet/lib/pages/home/currency_screen/model/ibc_coins.dart index 67b17b2659..52bfad7244 100644 --- a/wallet/lib/pages/home/currency_screen/model/ibc_coins.dart +++ b/wallet/lib/pages/home/currency_screen/model/ibc_coins.dart @@ -19,6 +19,16 @@ extension IBCCoinsPar on String { return e.toString().toLowerCase() == 'IBCCoins.$this'.toLowerCase(); }, orElse: () => IBCCoins.none); //return null if not found } + + IBCCoins toIBCCoinsEnumforEvent() { + if (this == kEthereumSymbol) { + return IBCCoins.weth_wei; + } + + return IBCCoins.values.firstWhere((e) { + return e.toString().toLowerCase() == toLowerCase(); + }, orElse: () => IBCCoins.none); //return null if not found + } } extension IBCCoinsDePar on IBCCoins { @@ -146,7 +156,7 @@ extension IBCCoinsDePar on IBCCoins { case IBCCoins.ustripeusd: return (double.parse(amount) / kBigIntBase).toStringAsFixed(2); case IBCCoins.upylon: - return (double.parse(amount) / kBigIntBase).toStringAsFixed(0); + return (double.parse(amount) / kBigIntBase).toStringAsFixed(2); case IBCCoins.weth_wei: return (double.parse(amount) / kEthIntBase).toStringAsFixed(2); } @@ -185,7 +195,6 @@ extension IBCCoinsDePar on IBCCoins { case IBCCoins.none: case IBCCoins.eeur: case IBCCoins.ujuno: - case IBCCoins.uatom: return "${(double.parse(amount) / kBigIntBase).toStringAsFixed(kCurrencyDecimalLength)} ${getAbbrev()}"; case IBCCoins.upylon: @@ -197,11 +206,9 @@ extension IBCCoinsDePar on IBCCoins { } } - String pylnToCredit(String amount) { - return( double.parse(amount)/10).toStringAsFixed(2); + return (double.parse(amount) / 10).toStringAsFixed(2); } - } SizedBox getIconFromAsset(String ibcCoinIcon) => SizedBox( diff --git a/wallet/lib/pages/purchase_item/purchase_item_view_model.dart b/wallet/lib/pages/purchase_item/purchase_item_view_model.dart index 8110e75021..c8f948f4dd 100644 --- a/wallet/lib/pages/purchase_item/purchase_item_view_model.dart +++ b/wallet/lib/pages/purchase_item/purchase_item_view_model.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:convert'; - import 'package:dartz/dartz.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; @@ -8,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:just_audio/just_audio.dart'; import 'package:pylons_wallet/components/loading.dart'; import 'package:pylons_wallet/ipc/models/sdk_ipc_response.dart'; +import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/model/nft.dart'; import 'package:pylons_wallet/model/nft_ownership_history.dart'; import 'package:pylons_wallet/modules/Pylonstech.pylons.pylons/module/client/pylons/execution.pb.dart'; @@ -147,6 +147,33 @@ class PurchaseItemViewModel extends ChangeNotifier { return response; } + Future> paymentForEventRecipe() async { + const jsonExecuteRecipe = ''' + { + "creator": "", + "cookbook_id": "", + "recipe_id": "", + "eventName": "", + "eventPrice": "", + "eventCurrency": "", + "coinInputsIndex": 0 + } + '''; + final jsonMap = jsonDecode(jsonExecuteRecipe) as Map; + + jsonMap[kCookbookIdKey] = getEvents.cookbookID; + jsonMap[kRecipeIdKey] = getEvents.recipeID; + jsonMap[kEventNam] = getEvents.eventName; + jsonMap[kEventPrice] = getEvents.denom.getCoinWithProperDenomination(getEvents.price); + jsonMap[kEventCurrency] = getEvents.denom.getAbbrev(); + + final showLoader = Loading()..showLoading(); + + final response = await walletsStore.executeRecipeForEvent(jsonMap); + showLoader.dismiss(); + return response; + } + Future paymentForTrade() async { final showLoader = Loading()..showLoading(); const json = ''' @@ -494,8 +521,7 @@ class PurchaseItemViewModel extends ChangeNotifier { }); } - Future> shouldShowSwipeToBuy( - {required String selectedDenom, required double requiredAmount}) async { + Future> shouldShowSwipeToBuy({required String selectedDenom, required double requiredAmount}) async { final walletAddress = accountPublicInfo!.publicAddress; final balancesEither = await repository.getBalance(walletAddress); @@ -511,8 +537,7 @@ class PurchaseItemViewModel extends ChangeNotifier { return const Right(true); } - final mappedBalances = - balancesEither.getOrElse(() => []).where((element) => element.denom == selectedDenom).toList(); + final mappedBalances = balancesEither.getOrElse(() => []).where((element) => element.denom == selectedDenom).toList(); if (mappedBalances.isEmpty) { return const Right(false); } @@ -603,7 +628,7 @@ class PurchaseItemViewModel extends ChangeNotifier { TradeReceiptModel createTradeReciptModel({ required double price, - required double fee, + required double fee, required String txTime, required String txId, }) => @@ -619,4 +644,10 @@ class PurchaseItemViewModel extends ChangeNotifier { nftName: nft.name, transactionID: txId, ); + + Events _event = Events(); + + set setEvents(Events event) => _event = event; + + Events get getEvents => _event; } diff --git a/wallet/lib/pages/transaction_failure_manager/failure_manager_view_model.dart b/wallet/lib/pages/transaction_failure_manager/failure_manager_view_model.dart index 38d3ea4a4b..bb94a2247a 100644 --- a/wallet/lib/pages/transaction_failure_manager/failure_manager_view_model.dart +++ b/wallet/lib/pages/transaction_failure_manager/failure_manager_view_model.dart @@ -54,6 +54,9 @@ class FailureManagerViewModel extends ChangeNotifier { case TransactionTypeEnum.Unknown: LocaleKeys.something_wrong.tr().show(); break; + case TransactionTypeEnum.BuyEvent: + LocaleKeys.something_wrong.tr().show(); + break; } } diff --git a/wallet/lib/services/data_stores/remote_data_store.dart b/wallet/lib/services/data_stores/remote_data_store.dart index f66686523a..1bf33dc676 100644 --- a/wallet/lib/services/data_stores/remote_data_store.dart +++ b/wallet/lib/services/data_stores/remote_data_store.dart @@ -1112,6 +1112,7 @@ class RemoteDataStoreImp implements RemoteDataStore { @override Future getAppCheckToken() async { + return "6B189291-D2AE-439B-AA5D-E2B99679C01E"; final String? token = await firebaseAppCheck.getToken(); if (token == null || token.isEmpty) { diff --git a/wallet/lib/stores/wallet_store.dart b/wallet/lib/stores/wallet_store.dart index d2ac2b9e5c..0a2ce90430 100644 --- a/wallet/lib/stores/wallet_store.dart +++ b/wallet/lib/stores/wallet_store.dart @@ -37,6 +37,11 @@ abstract class WalletsStore { /// Output : [Execution] of the transaction (data field - idk that this is - this is a mess) Future> executeRecipe(Map json); + /// Input : [Map] containing the info related to the execution of recipe for event + /// Output : [Execution] of the transaction (data field - idk that this is - this is a mess) + Future> executeRecipeForEvent(Map json); + + /// This method is for create Trade /// MsgCreateTrade proto /// request fields: {String creator, list coinInputs, List itemInputs, List coinOutputs, List itemOutputs, String extraInfo} diff --git a/wallet/lib/stores/wallet_store_imp.dart b/wallet/lib/stores/wallet_store_imp.dart index ba90fb20ff..8448bf17f1 100644 --- a/wallet/lib/stores/wallet_store_imp.dart +++ b/wallet/lib/stores/wallet_store_imp.dart @@ -483,6 +483,94 @@ class WalletsStoreImp implements WalletsStore { ); } + @override + Future> executeRecipeForEvent(Map json) async { + final networkInfo = GetIt.I.get(); + + final LocalTransactionModel localTransactionModel = createInitialLocalTransactionModel( + transactionTypeEnum: TransactionTypeEnum.BuyEvent, + transactionData: jsonEncode(json), + transactionDescription: "${LocaleKeys.bought_nft.tr()} ${json[kEventNam] ?? ""}", + transactionCurrency: "${json[kEventCurrency] ?? ""}", + transactionPrice: "${json[kEventPrice] ?? ""}", + ); + + if (!await networkInfo.isConnected) { + await saveTransactionRecord( + transactionHash: "", + transactionStatus: TransactionStatus.Failed, + txLocalModel: localTransactionModel, + ); + return SdkIpcResponse.failure( + sender: '', + error: LocaleKeys.no_internet.tr(), + errorCode: HandlerFactory.ERR_SOMETHING_WENT_WRONG, + ); + } + + json.remove(kEventNam); + json.remove(kEventCurrency); + json.remove(kEventPrice); + + final msgObj = pylons.MsgExecuteRecipe.create()..mergeFromProto3Json(json); + msgObj.creator = accountProvider.accountPublicInfo!.publicAddress; + final sdkResponse = await _signAndBroadcast(msgObj); + if (!sdkResponse.success) { + await saveTransactionRecord( + transactionHash: "", + transactionStatus: TransactionStatus.Failed, + txLocalModel: localTransactionModel, + ); + return SdkIpcResponse.failure( + error: sdkResponse.error, + sender: sdkResponse.sender, + errorCode: sdkResponse.errorCode, + ); + } + + final executionEither = await repository.getExecutionsByRecipeId( + recipeId: json.containsKey(kRecipeIdKey) ? json[kRecipeIdKey].toString() : json[kRecipeIdMap].toString(), + cookBookId: json.containsKey(kCookbookIdKey) ? json[kCookbookIdKey].toString() : json[kCookbookIdMap].toString(), + ); + if (executionEither.isLeft()) { + await saveTransactionRecord( + transactionHash: "", + transactionStatus: TransactionStatus.Failed, + txLocalModel: localTransactionModel, + ); + return SdkIpcResponse.failure( + error: sdkResponse.error, + sender: sdkResponse.sender, + errorCode: sdkResponse.errorCode, + ); + } + + if (executionEither.toOption().toNullable()!.completedExecutions.isEmpty) { + await saveTransactionRecord( + transactionHash: "", + transactionStatus: TransactionStatus.Failed, + txLocalModel: localTransactionModel, + ); + return SdkIpcResponse.failure( + error: sdkResponse.error, + sender: sdkResponse.sender, + errorCode: sdkResponse.errorCode, + ); + } + + await saveTransactionRecord( + transactionHash: sdkResponse.data.toString(), + transactionStatus: TransactionStatus.Success, + txLocalModel: localTransactionModel, + ); + return SdkIpcResponse.success( + data: executionEither.toOption().toNullable()!.completedExecutions.last, + sender: sdkResponse.sender, + transaction: sdkResponse.data.toString(), + ); + } + + @override Future fulfillTrade(Map json) async { final msgObj = pylons.MsgFulfillTrade.create()..mergeFromProto3Json(json); diff --git a/wallet/lib/utils/constants.dart b/wallet/lib/utils/constants.dart index 3d005d1ede..6987bb8380 100644 --- a/wallet/lib/utils/constants.dart +++ b/wallet/lib/utils/constants.dart @@ -451,6 +451,11 @@ const String kCookbookID = "cookbookID"; const String kPaymentInfosMap = "paymentInfos"; const String kItemAlreadyOwned = "itemAlreadyOwned"; +const kEventNam = "eventName"; +const kEventPrice = "eventPrice"; +const kEventCurrency = "eventCurrency"; + + class AnalyticsScreenEvents { static String mainLanding = "MainLandingScreen"; static String createKey = "CreateKeyScreen"; diff --git a/wallet/lib/utils/enums.dart b/wallet/lib/utils/enums.dart index 348497b2fc..e54d96269b 100644 --- a/wallet/lib/utils/enums.dart +++ b/wallet/lib/utils/enums.dart @@ -25,7 +25,8 @@ enum TransactionTypeEnum { AppleInAppCoinsRequest, GoogleInAppCoinsRequest, BuyProduct, - Unknown + Unknown, + BuyEvent, } enum TransactionStatus { From 3690f8464ff1071f9c86b87609f16bfe44bdad96 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Fri, 31 May 2024 11:30:30 +0500 Subject: [PATCH 146/161] fix: some issue while testing --- evently/i18n/de.json | 3 ++- evently/i18n/en-US.json | 3 ++- evently/i18n/es.json | 3 ++- evently/i18n/ru-RU.json | 3 ++- evently/lib/generated/locale_keys.g.dart | 1 + evently/lib/models/denom.dart | 2 +- evently/lib/viewmodels/create_event_viewmodel.dart | 1 + wallet/lib/model/event.dart | 7 +++++-- wallet/lib/pages/events/event_purchase_view.dart | 1 + 9 files changed, 17 insertions(+), 7 deletions(-) diff --git a/evently/i18n/de.json b/evently/i18n/de.json index c1082e519e..ecf3116545 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -73,5 +73,6 @@ "event_name" : "Event Name", "are_you_sure_you_want_to_delete": "Möchten Sie diesen Entwurf wirklich löschen?", "published": "Veröffentlicht", - "delete": "Löschen" + "delete": "Löschen", + "event_preview" : "event_preview" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 777c9749cf..0c91f363d6 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -70,5 +70,6 @@ "event_name" : "Event Name", "are_you_sure_you_want_to_delete": "Are you sure you want to delete this draft?", "published": "Published", - "delete": "Delete" + "delete": "Delete", + "event_preview" : "event_preview" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index ecbd2db646..0ccaa01230 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -71,5 +71,6 @@ "event_name" : "Event Name", "are_you_sure_you_want_to_delete": "¿Seguro que quieres eliminar este Plan Preliminar?", "published": "Publicado", - "delete": "Borrar" + "delete": "Borrar", + "event_preview" : "event_preview" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 1c347bb983..0a9945448a 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -71,5 +71,6 @@ "event_name" : "Event Name", "are_you_sure_you_want_to_delete": "Вы уверены, что хотите удалить этот черновик?", "published": "изданный", - "delete": "удалять" + "delete": "удалять", + "event_preview" : "event_preview" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index 48bceccb94..06eb4dd784 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -71,5 +71,6 @@ abstract class LocaleKeys { static const are_you_sure_you_want_to_delete = 'are_you_sure_you_want_to_delete'; static const published = 'published'; static const delete = 'delete'; + static const event_preview = 'event_preview'; } diff --git a/evently/lib/models/denom.dart b/evently/lib/models/denom.dart index 1f4cb66b36..b08ababff7 100644 --- a/evently/lib/models/denom.dart +++ b/evently/lib/models/denom.dart @@ -91,7 +91,7 @@ class Denom { return map; } - factory Denom.fromJson(Map json) => Denom( + factory Denom.fromJson(Map json) => Denom( name: json['name']!, symbol: json['symbol']!, icon: json['icon']!, diff --git a/evently/lib/viewmodels/create_event_viewmodel.dart b/evently/lib/viewmodels/create_event_viewmodel.dart index b898ed55f3..c39aa4fb7b 100644 --- a/evently/lib/viewmodels/create_event_viewmodel.dart +++ b/evently/lib/viewmodels/create_event_viewmodel.dart @@ -23,6 +23,7 @@ class CreateEventViewModel extends ChangeNotifier { LocaleKeys.detail.tr(), LocaleKeys.perks.tr(), LocaleKeys.price.tr(), + LocaleKeys.event_preview.trim(), ]; Events? events; diff --git a/wallet/lib/model/event.dart b/wallet/lib/model/event.dart index aa4ce6dbd4..86dbdf7a45 100644 --- a/wallet/lib/model/event.dart +++ b/wallet/lib/model/event.dart @@ -138,7 +138,7 @@ class Events extends Equatable { factory Events.fromRecipe(Recipe recipe) { final Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); - final denom = recipe.coinInputs.isEmpty ? "" : _extractCoinValue(recipe.coinInputs[0].coins)[kDenom]; + final denom = _extractCoinValue(recipe.coinInputs[0].coins)[kDenom] ?? ""; final List listOfPerks = []; jsonDecode(map[kPerks]!).map((jsonMap) { @@ -160,7 +160,7 @@ class Events extends Equatable { listOfPerks: listOfPerks, cookbookID: map[kCookBookId]!, recipeID: map[kRecipeId]!, - denom: denom!.toIBCCoinsEnum(), + denom: denom.isEmpty ? IBCCoins.upylon : denom.toIBCCoinsEnum(), ); } @@ -194,6 +194,9 @@ class Events extends Equatable { } static Map _extractCoinValue(List coins) { + if (coins.isEmpty) { + return {}; + } return {kDenom: coins[0].denom, kPrice: coins[0].amount}; } diff --git a/wallet/lib/pages/events/event_purchase_view.dart b/wallet/lib/pages/events/event_purchase_view.dart index 3623944b72..29beec63bb 100644 --- a/wallet/lib/pages/events/event_purchase_view.dart +++ b/wallet/lib/pages/events/event_purchase_view.dart @@ -333,6 +333,7 @@ class _EventPassViewContentState extends State { Container( margin: EdgeInsets.symmetric(horizontal: 20.w), child: CachedNetworkImage( + height: 160.h, width: double.infinity, fit: BoxFit.fill, imageUrl: widget.events.thumbnail, From a3df2cc10eea2ee9a3eb8c9db4beec296476923e Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Fri, 31 May 2024 11:30:58 +0500 Subject: [PATCH 147/161] fix: app check debubug token --- wallet/lib/services/data_stores/remote_data_store.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/wallet/lib/services/data_stores/remote_data_store.dart b/wallet/lib/services/data_stores/remote_data_store.dart index 1bf33dc676..f66686523a 100644 --- a/wallet/lib/services/data_stores/remote_data_store.dart +++ b/wallet/lib/services/data_stores/remote_data_store.dart @@ -1112,7 +1112,6 @@ class RemoteDataStoreImp implements RemoteDataStore { @override Future getAppCheckToken() async { - return "6B189291-D2AE-439B-AA5D-E2B99679C01E"; final String? token = await firebaseAppCheck.getToken(); if (token == null || token.isEmpty) { From d918ed4eace52a52ae45b4447897d4c466d2a474 Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Fri, 31 May 2024 12:57:10 +0500 Subject: [PATCH 148/161] fix: code cleaning --- .../assets/images/text_field_bottom_left.png | Bin 0 -> 407 bytes .../assets/images/text_field_top_right.png | Bin 0 -> 411 bytes evently/lib/evently_provider.dart | 3 +- .../custom_widgets/bottom_buttons.dart | 6 +- evently/lib/screens/detail_screen.dart | 52 ++-- .../lib/screens/host_view_ticket_preview.dart | 277 ++++++++---------- evently/lib/screens/overview_screen.dart | 12 +- evently/lib/screens/perks_screen.dart | 2 +- evently/lib/screens/price_screen.dart | 2 - evently/lib/utils/constants.dart | 6 +- evently/lib/widgets/evently_text_field.dart | 13 +- 11 files changed, 179 insertions(+), 194 deletions(-) create mode 100644 evently/assets/images/text_field_bottom_left.png create mode 100644 evently/assets/images/text_field_top_right.png diff --git a/evently/assets/images/text_field_bottom_left.png b/evently/assets/images/text_field_bottom_left.png new file mode 100644 index 0000000000000000000000000000000000000000..5151a60f913479ccae527e6d0078f1f1bde9e548 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^i-1^@gAGV-YyAWy88{0(B8wRqxP?KOkzv*x37{Zj zage(c!@6@aFM%AEbVpxD28NCO+VVL5JkQ)(sovoBIAg`q}uya2h}Jr>uLw`Pp-H3L6-l zT3D!oT8^BllijdU<@BcY0+0Kd&89r5{CBrh)-Wl!Y^O$7()?vFD<#_YGAnKVZywz9 zUh!Bnv(l#5?25+j7qz=}nAeOAYU&P6X| zj( zy<_{{f3KYP-@n$mEgbQaHtBpn&&MfvuOxQ+ zR^~H?zL&55=n;50RW`vaQNpgKMp$W2v0>kKgO(20p75rYj-z*yOSB!Gjx37l$y4>{ kh?o=2DWs$#KqVBzxK$!^rQ_89k)YuAboFyt=akR{05HaitpET3 literal 0 HcmV?d00001 diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index d4065cbb26..0e13b3f409 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -136,6 +136,7 @@ class EventlyProvider extends ChangeNotifier { set setPerks(PerksModel perksModel) { _perks.add(perksModel); + setSelectedPerks = _perks.length - 1; notifyListeners(); } @@ -146,6 +147,7 @@ class EventlyProvider extends ChangeNotifier { removePerks(int index) { _perks.removeAt(index); + setSelectedPerks = _perks.length - 1; notifyListeners(); } @@ -292,7 +294,6 @@ class EventlyProvider extends ChangeNotifier { final String prices = isFreeDrop == FreeDrop.yes ? "0" : _selectedDenom.formatAmount(price: price.toString()); - final recipe = event.createRecipe( cookbookId: _cookbookId!, recipeId: _recipeId, diff --git a/evently/lib/screens/custom_widgets/bottom_buttons.dart b/evently/lib/screens/custom_widgets/bottom_buttons.dart index 6c8809be16..52e95671f0 100644 --- a/evently/lib/screens/custom_widgets/bottom_buttons.dart +++ b/evently/lib/screens/custom_widgets/bottom_buttons.dart @@ -41,7 +41,11 @@ class BottomButtons extends StatelessWidget { const VerticalSpace(10), Center( child: InkWell( - onTap: onPressSaveDraft, + onTap: () { + if (isContinueEnable) { + onPressSaveDraft(); + } + }, child: Text( LocaleKeys.save_draft.tr(), style: TextStyle(color: EventlyAppTheme.kTextGrey02, fontSize: 14.sp, fontWeight: FontWeight.w700), diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 2a199fe9b4..ec4d57a16a 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -5,7 +5,9 @@ import 'package:evently/screens/custom_widgets/bottom_buttons.dart'; import 'package:evently/screens/custom_widgets/page_app_bar.dart'; import 'package:evently/screens/custom_widgets/step_labels.dart'; import 'package:evently/screens/custom_widgets/steps_indicator.dart'; +import 'package:evently/utils/constants.dart'; import 'package:evently/utils/enums.dart'; +import 'package:evently/utils/evently_app_theme.dart'; import 'package:evently/utils/route_util.dart'; import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; @@ -60,11 +62,12 @@ class _DetailsScreenState extends State { }); }, child: EventlyTextField( - enable: false, - label: LocaleKeys.start_date.tr(), - controller: TextEditingController(text: provider.startDate), - textCapitalization: TextCapitalization.sentences, - ), + enable: false, + label: LocaleKeys.start_date.tr(), + controller: TextEditingController(text: provider.startDate), + textCapitalization: TextCapitalization.sentences, + imageBackground: PngUtils.textFieldBottomLeft, + inputTextColor: EventlyAppTheme.kTextDarkBlue), ), ), HorizontalSpace(20.w), @@ -77,14 +80,15 @@ class _DetailsScreenState extends State { }); }, child: EventlyTextField( - enable: false, - label: LocaleKeys.end_date.tr(), - controller: TextEditingController(text: provider.endDate), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, - ), + enable: false, + label: LocaleKeys.end_date.tr(), + controller: TextEditingController(text: provider.endDate), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + imageBackground: PngUtils.textFieldTopRight, + inputTextColor: EventlyAppTheme.kTextDarkBlue), ), ), ], @@ -103,14 +107,15 @@ class _DetailsScreenState extends State { }); }, child: EventlyTextField( - enable: false, - label: LocaleKeys.start_time.tr(), - controller: TextEditingController(text: provider.startTime), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, - ), + enable: false, + label: LocaleKeys.start_time.tr(), + controller: TextEditingController(text: provider.startTime), + textCapitalization: TextCapitalization.sentences, + validator: (value) { + return null; + }, + imageBackground: PngUtils.textFieldBottomLeft, + inputTextColor: EventlyAppTheme.kTextDarkBlue), ), ), HorizontalSpace(20.w), @@ -132,6 +137,8 @@ class _DetailsScreenState extends State { validator: (value) { return null; }, + imageBackground: PngUtils.textFieldTopRight, + inputTextColor: EventlyAppTheme.kTextDarkBlue, ), ), ), @@ -163,10 +170,9 @@ class _DetailsScreenState extends State { textCapitalization: TextCapitalization.sentences, noOfLines: 4, ), - const VerticalSpace(80), + const VerticalSpace(40), BottomButtons( onPressContinue: () { - createEventViewModel.nextPage(); }, onPressSaveDraft: () { diff --git a/evently/lib/screens/host_view_ticket_preview.dart b/evently/lib/screens/host_view_ticket_preview.dart index f4317feda7..dbb76c4349 100644 --- a/evently/lib/screens/host_view_ticket_preview.dart +++ b/evently/lib/screens/host_view_ticket_preview.dart @@ -34,6 +34,13 @@ class _HostTicketPreviewState extends State { child: Scaffold( bottomNavigationBar: Container( padding: EdgeInsets.symmetric(horizontal: 30.w), + decoration: BoxDecoration(color: EventlyAppTheme.kWhite, boxShadow: [ + BoxShadow( + color: EventlyAppTheme.kGrey01.withOpacity(0.1), + // offset: Offset(0, 0), + // blurRadius: 20, + spreadRadius: 1) + ]), height: 110.h, child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -62,185 +69,145 @@ class _HostTicketPreviewState extends State { borderRadius: BorderRadius.circular(10.r), child: Container( margin: EdgeInsets.symmetric(vertical: 20.w, horizontal: 20.w), - decoration: BoxDecoration( - image: const DecorationImage(image: AssetImage(PngUtils.kHostPreview), fit: BoxFit.fitHeight), - borderRadius: BorderRadius.circular(10.r), - ), - child: Column( + child: Stack( children: [ - Padding( - padding: const EdgeInsets.only(), - child: ClipRRect( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(14), - topLeft: Radius.circular(14), - ), - child: Stack( - alignment: Alignment.bottomLeft, - children: [ - Image.asset( - PngUtils.kPhantom, - fit: BoxFit.cover, + Image.asset(PngUtils.kHostPreview), + Column( + children: [ + Padding( + padding: const EdgeInsets.only(), + child: ClipRRect( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(14), + topLeft: Radius.circular(14), + ), + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Image.asset( + PngUtils.kPhantom, + fit: BoxFit.cover, + ), + Padding( + padding: EdgeInsets.only(bottom: 10.h, left: 10.w), + child: Text( + provider.eventName, + style: TextStyle(fontSize: 20.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w700), + ), + ) + ], ), - Padding( - padding: EdgeInsets.only(bottom: 10.h, left: 10.w), - child: Text( - provider.eventName, - style: TextStyle(fontSize: 20.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.w700), - ), - ) - ], - ), - ), - ), - VerticalSpace(10.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'DATE', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Text( - provider.startDate, - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - ), - ], ), - Column( + ), + VerticalSpace(10.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, children: [ - Text( - 'Time', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'DATE', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + provider.startDate, + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], ), - SizedBox(height: 1.h), - Text( - provider.startTime, - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Time', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + Text( + provider.startTime, + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + ), + ], ), ], ), - ], - ), - ), - VerticalSpace(20.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( + ), + VerticalSpace(20.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, children: [ - Text( - 'LOCATION', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - ConstrainedBox( - constraints: BoxConstraints(maxWidth: 1.sw / 2.4), - child: Text( - provider.location, - style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - maxLines: 2, - ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'LOCATION', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), + ), + SizedBox(height: 1.h), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 1.sw / 2.4), + child: Text( + provider.location, + style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), + maxLines: 2, + ), + ), + ], ), ], ), - // Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.start, - // children: [ - // Text( - // 'SEAT', - // style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - // ), - // SizedBox(height: 1.h), - // Column( - // mainAxisAlignment: MainAxisAlignment.start, - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text( - // '6A', - // style: TextStyle(fontSize: 30.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - // ), - // Text( - // 'Room 3', - // style: TextStyle(fontSize: 15.sp, fontWeight: FontWeight.bold, color: EventlyAppTheme.kWhite), - // ) - // ], - // ), - // ], - // ), - ], - ), - ), - VerticalSpace(20.h), - Padding( - padding: EdgeInsets.only(left: 10.w, right: 30.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( + ), + VerticalSpace(20.h), + Padding( + padding: EdgeInsets.only(left: 10.w, right: 30.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, children: [ - Text( - 'PERKS', - style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), - ), - SizedBox(height: 1.h), - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ - SvgPicture.asset(SVGUtils.kDiamond), - SizedBox(width: 5.w), Text( - 'x ${provider.perks.length}', - style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), + 'PERKS', + style: TextStyle(fontSize: 11.sp, fontWeight: FontWeight.w400, color: EventlyAppTheme.kWhite), ), - SizedBox(width: 5.w), - Text( - 'Redeem', - style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kGreenText, fontWeight: FontWeight.bold), + SizedBox(height: 1.h), + Row( + children: [ + SvgPicture.asset(SVGUtils.kDiamond), + SizedBox(width: 5.w), + Text( + 'x ${provider.perks.length}', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kWhite, fontWeight: FontWeight.bold), + ), + SizedBox(width: 5.w), + Text( + 'Redeem', + style: TextStyle(fontSize: 15.sp, color: EventlyAppTheme.kGreenText, fontWeight: FontWeight.bold), + ) + ], ) ], - ) + ), ], ), - ], - ), - ), - VerticalSpace(16.h), - Image.asset(PngUtils.kDottedLine), - VerticalSpace(40.h), - Text( - 'Scan QR to enter', - style: TextStyle( - color: EventlyAppTheme.kWhite, - fontWeight: FontWeight.bold, - fontSize: 15.sp, - ), - ), - VerticalSpace(10.h), - Container( - decoration: const BoxDecoration(color: EventlyAppTheme.kBlack), - width: 338, - height: 338, + ), + VerticalSpace(30.h), + Image.asset(PngUtils.kDottedLine), + ], ), - SizedBox(height: 20.h), ], ), ), diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 0ad9e1ce37..8c5bd9ee0b 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -148,11 +148,13 @@ class _OverViewScreenState extends State { createEventViewModel.nextPage(); }, onPressSaveDraft: () { - final navigator = Navigator.of(context); - provider.saveAsDraft( - onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), - uploadStep: UploadStep.overView, - ); + if (provider.isOverviewEnable) { + final navigator = Navigator.of(context); + provider.saveAsDraft( + onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), + uploadStep: UploadStep.overView, + ); + } }, isContinueEnable: provider.isOverviewEnable, ) diff --git a/evently/lib/screens/perks_screen.dart b/evently/lib/screens/perks_screen.dart index 06d3bc60bf..2584f0678b 100644 --- a/evently/lib/screens/perks_screen.dart +++ b/evently/lib/screens/perks_screen.dart @@ -121,7 +121,7 @@ class _PerksScreenState extends State { final navigator = Navigator.of(context); provider.saveAsDraft( onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), - uploadStep: UploadStep.perks, + uploadStep:provider.perks.isEmpty ? UploadStep.detail : UploadStep.perks, ); }, isContinueEnable: true, diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index b77db24b91..267ad84545 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -17,7 +17,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; - import '../generated/locale_keys.g.dart'; class PriceScreen extends StatefulWidget { @@ -206,7 +205,6 @@ class _PriceScreenState extends State { padding: EdgeInsets.symmetric(horizontal: 20.w), child: BottomButtons( onPressContinue: () { - homeViewModel.nextPage(); }, onPressSaveDraft: () { diff --git a/evently/lib/utils/constants.dart b/evently/lib/utils/constants.dart index f5715190db..06a65a7540 100644 --- a/evently/lib/utils/constants.dart +++ b/evently/lib/utils/constants.dart @@ -28,7 +28,6 @@ const int kEthIntBase = 1000000000000000000; final List imageAllowedExts = ["png", "jpg", "jpeg", "heif"]; const kPylons = "Pylons"; - const kErrProfileNotExist = 'profileDoesNotExist'; const cookbookName = "Evently Cookbook"; @@ -93,6 +92,10 @@ class PngUtils { /// i need this variable to be used static const kPhantom = "assets/images/phantom.png"; static const kDottedLine = "assets/images/dotted_line.png"; + + ///text field background + static const textFieldBottomLeft = "assets/images/text_field_bottom_left.png"; + static const textFieldTopRight = "assets/images/text_field_top_right.png"; } /// Currency ABRR @@ -104,4 +107,3 @@ const String kAgoricAbr = "RUN"; const String kAtomAbr = "ATOM"; const String kLunaAbr = "Luna"; const String kEthereumAbr = "ETH"; - diff --git a/evently/lib/widgets/evently_text_field.dart b/evently/lib/widgets/evently_text_field.dart index 62ed55450f..6f3d989080 100644 --- a/evently/lib/widgets/evently_text_field.dart +++ b/evently/lib/widgets/evently_text_field.dart @@ -18,6 +18,8 @@ class EventlyTextField extends StatelessWidget { this.textCapitalization = TextCapitalization.none, this.onChanged, this.enable, + this.imageBackground, + this.inputTextColor, }); final String label; @@ -32,6 +34,9 @@ class EventlyTextField extends StatelessWidget { final ValueChanged? onChanged; final bool? enable; + final String? imageBackground; + final Color? inputTextColor; + @override Widget build(BuildContext context) { return Column( @@ -47,13 +52,13 @@ class EventlyTextField extends StatelessWidget { children: [ ScreenResponsive( mobileScreen: (context) => Image.asset( - noOfLines == 1 ? PngUtils.kTextFieldSingleLine : PngUtils.kTextFieldMultiLine, + imageBackground ?? (noOfLines == 1 ? PngUtils.kTextFieldSingleLine : PngUtils.kTextFieldMultiLine), height: noOfLines == 1 ? 40.h : 120.h, width: 1.sw, fit: BoxFit.fill, ), tabletScreen: (context) => Image.asset( - noOfLines == 1 ? PngUtils.kTextFieldSingleLine : PngUtils.kTextFieldMultiLine, + imageBackground ?? (noOfLines == 1 ? PngUtils.kTextFieldSingleLine : PngUtils.kTextFieldMultiLine), height: noOfLines == 1 ? 32.h : 110.h, width: 1.sw, fit: BoxFit.fill, @@ -73,7 +78,7 @@ class EventlyTextField extends StatelessWidget { child: TextFormField( enabled: enable, onChanged: onChanged, - style: TextStyle(fontSize: noOfLines == 1 ? 18.sp : 15, fontWeight: FontWeight.w400, color: EventlyAppTheme.kTextGrey), + style: TextStyle(fontSize: noOfLines == 1 ? 18.sp : 15, fontWeight: FontWeight.w400, color: inputTextColor ?? EventlyAppTheme.kTextGrey02), controller: controller, validator: validator, minLines: noOfLines, @@ -83,7 +88,7 @@ class EventlyTextField extends StatelessWidget { inputFormatters: inputFormatters, decoration: InputDecoration( hintText: hint, - hintStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400, color: EventlyAppTheme.kTextGrey), + hintStyle: TextStyle(fontSize: 15, fontWeight: FontWeight.w400, color: inputTextColor ?? EventlyAppTheme.kTextGrey02), border: const OutlineInputBorder(borderSide: BorderSide.none), floatingLabelBehavior: FloatingLabelBehavior.always, contentPadding: EdgeInsets.fromLTRB(10.w, 0.h, 10.w, 0.h), From 1f0cbfe33537f367010b526cf0a6dbba2e97ea2a Mon Sep 17 00:00:00 2001 From: AhsanAli13503 Date: Fri, 31 May 2024 15:31:33 +0500 Subject: [PATCH 149/161] fix: fixes --- .../lib/pages/events/event_purchase_view.dart | 4 +- .../lib/pages/events/events_owner_view.dart | 82 ++++++++++++------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/wallet/lib/pages/events/event_purchase_view.dart b/wallet/lib/pages/events/event_purchase_view.dart index 29beec63bb..8d6a516504 100644 --- a/wallet/lib/pages/events/event_purchase_view.dart +++ b/wallet/lib/pages/events/event_purchase_view.dart @@ -74,8 +74,8 @@ class _EventPassViewContentState extends State { final ibcEnumCoins = provider.getEvents.denom; if (ibcEnumCoins == IBCCoins.ustripeusd) { - // stripePaymentForRecipe(context, provider.getEvents); - print('needed to work for this'); + //@TODO + // needed to work on this flow } else { paymentByCoins(); } diff --git a/wallet/lib/pages/events/events_owner_view.dart b/wallet/lib/pages/events/events_owner_view.dart index 5c1da9d385..70800aaa25 100644 --- a/wallet/lib/pages/events/events_owner_view.dart +++ b/wallet/lib/pages/events/events_owner_view.dart @@ -1,11 +1,15 @@ import 'package:cached_network_image/cached_network_image.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get_it/get_it.dart'; import 'package:provider/provider.dart'; import 'package:pylons_wallet/components/space_widgets.dart'; import 'package:pylons_wallet/gen/assets.gen.dart'; +import 'package:pylons_wallet/generated/locale_keys.g.dart'; import 'package:pylons_wallet/model/event.dart'; import 'package:pylons_wallet/pages/detailed_asset_view/owner_view_view_model.dart'; import 'package:pylons_wallet/pages/events/event_qr_code_screen.dart'; @@ -58,30 +62,62 @@ class EventPassViewContent extends StatelessWidget { child: Scaffold( backgroundColor: AppColors.kBlack87, appBar: AppBar( + automaticallyImplyLeading: false, backgroundColor: Colors.black, flexibleSpace: Padding( padding: EdgeInsets.symmetric(horizontal: 20.w), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Icon( - Icons.arrow_back_ios, - color: AppColors.kWhite, + Expanded( + child: Container( + alignment: Alignment.centerLeft, + child: const Icon( + Icons.arrow_back_ios, + color: AppColors.kWhite, + ), + ), ), - Text( - 'Event Pass', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w700, - color: AppColors.kWhite, + Expanded( + child: Container( + alignment: Alignment.center, + child: Text( + 'Event Pass', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w700, + color: AppColors.kWhite, + ), + ), ), ), - GestureDetector( - onTap: () { - final Size size = MediaQuery.of(context).size; - viewModel.shareEventsLink(size: size); - }, - child: SvgPicture.asset(shareIcon), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + final Size size = MediaQuery.of(context).size; + viewModel.shareEventsLink(size: size); + }, + child: SvgPicture.asset(shareIcon), + ), + SizedBox(width: 20.w), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (_) => EventQrCodeScreen( + events: viewModel.events, + ), + ); + }, + child: SvgPicture.asset( + Assets.images.icons.qr, + ), + ), + ], + ), ), ], ), @@ -157,7 +193,7 @@ class EventPassViewContent extends StatelessWidget { style: Theme.of(context).textTheme.displayLarge?.copyWith(fontSize: 11.sp, fontWeight: FontWeight.w400, color: AppColors.kWhite), ), Text( - coinWithDenom, + viewModel.events.price == "0" ? LocaleKeys.free.tr() : coinWithDenom, style: Theme.of(context).textTheme.labelSmall?.copyWith(fontSize: 15.sp, fontWeight: FontWeight.w700, color: AppColors.kWhite), ), ], @@ -213,20 +249,6 @@ class EventPassViewContent extends StatelessWidget { ), ), SizedBox(height: 10.h), - GestureDetector( - onTap: () { - showDialog( - context: context, - builder: (_) => EventQrCodeScreen( - events: viewModel.events, - ), - ); - }, - child: SvgPicture.asset( - Assets.images.icons.qr, - height: 40.h, - ), - ), ], ), ), From 1acd025a792eab30843017cfce55a4c70126c431 Mon Sep 17 00:00:00 2001 From: gram-incolo Date: Fri, 7 Jun 2024 23:27:12 +0300 Subject: [PATCH 150/161] fix the event length issue --- evently/lib/evently_provider.dart | 39 +++++++++++++++++++++-------- evently/lib/main.dart | 7 ++++-- evently/lib/models/events.dart | 41 ++++++++++++++++++++++++------- 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/evently/lib/evently_provider.dart b/evently/lib/evently_provider.dart index 0e13b3f409..2cf50625e3 100644 --- a/evently/lib/evently_provider.dart +++ b/evently/lib/evently_provider.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; + import 'package:dartz/dartz.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; @@ -21,6 +22,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:injectable/injectable.dart'; import 'package:pylons_sdk/low_level.dart'; + import 'services/third_party_services/quick_node.dart'; @LazySingleton() @@ -66,7 +68,8 @@ class EventlyProvider extends ChangeNotifier { } checkIsOverEnable() { - setOverviewEnable = thumbnail!.isNotEmpty && eventName.length > 8 && hostName.isNotEmpty; + setOverviewEnable = + thumbnail!.isNotEmpty && eventName.length > 4 && hostName.isNotEmpty; } ///* detail screen @@ -190,9 +193,11 @@ class EventlyProvider extends ChangeNotifier { notifyListeners(); } - final StreamController _uploadProgressController = StreamController.broadcast(); + final StreamController _uploadProgressController = + StreamController.broadcast(); - Stream get uploadProgressStream => _uploadProgressController.stream; + Stream get uploadProgressStream => + _uploadProgressController.stream; void pickThumbnail() async { final pickedFile = await repository.pickFile(); @@ -207,11 +212,15 @@ class EventlyProvider extends ChangeNotifier { if (result.path.isEmpty) return; - final loading = LoadingProgress()..showLoadingWithProgress(message: LocaleKeys.uploading.tr()); + final loading = LoadingProgress() + ..showLoadingWithProgress(message: LocaleKeys.uploading.tr()); Either response; response = await repository.uploadFileUsingQuickNode( - uploadIPFSInput: UploadIPFSInput(fileName: result.fileName, filePath: result.path, contentType: QuickNode.getContentType(result.extension)), + uploadIPFSInput: UploadIPFSInput( + fileName: result.fileName, + filePath: result.path, + contentType: QuickNode.getContentType(result.extension)), onUploadProgressCallback: (value) { _uploadProgressController.sink.add(value); }, @@ -223,7 +232,8 @@ class EventlyProvider extends ChangeNotifier { return; } - final fileUploadResponse = response.getOrElse(() => StorageResponseModel.initial()); + final fileUploadResponse = + response.getOrElse(() => StorageResponseModel.initial()); loading.dismiss(); setThumbnail = "$ipfsDomain/${fileUploadResponse.value?.cid}"; @@ -234,7 +244,10 @@ class EventlyProvider extends ChangeNotifier { String? _cookbookId; String? get cookbookId => _cookbookId; - bool showStripeDialog() => !stripeAccountExists && _selectedDenom.symbol == kUsdSymbol && isFreeDrop == FreeDrop.no; + bool showStripeDialog() => + !stripeAccountExists && + _selectedDenom.symbol == kUsdSymbol && + isFreeDrop == FreeDrop.no; ///* this method is used to get the profile Future> getProfile() async { @@ -243,7 +256,10 @@ class EventlyProvider extends ChangeNotifier { if (sdkResponse.success) { stripeAccountExists = sdkResponse.data!.stripeExists; - supportedDenomList = Denom.availableDenoms.where((Denom e) => sdkResponse.data!.supportedCoins.contains(e.symbol)).toList(); + supportedDenomList = Denom.availableDenoms + .where( + (Denom e) => sdkResponse.data!.supportedCoins.contains(e.symbol)) + .toList(); if (supportedDenomList.isNotEmpty && selectedDenom.symbol.isEmpty) { _selectedDenom = supportedDenomList.first; @@ -292,7 +308,9 @@ class EventlyProvider extends ChangeNotifier { step: '', ); - final String prices = isFreeDrop == FreeDrop.yes ? "0" : _selectedDenom.formatAmount(price: price.toString()); + final String prices = isFreeDrop == FreeDrop.yes + ? "0" + : _selectedDenom.formatAmount(price: price.toString()); final recipe = event.createRecipe( cookbookId: _cookbookId!, @@ -302,7 +320,8 @@ class EventlyProvider extends ChangeNotifier { price: prices, ); - final response = await PylonsWallet.instance.txCreateRecipe(recipe, requestResponse: false); + final response = await PylonsWallet.instance + .txCreateRecipe(recipe, requestResponse: false); if (!response.success) { scaffoldMessengerState?.show(message: "$kErrRecipe ${response.error}"); diff --git a/evently/lib/main.dart b/evently/lib/main.dart index 2d9ea8ad0b..76cde65d6c 100644 --- a/evently/lib/main.dart +++ b/evently/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:ui'; + import 'package:easy_localization/easy_localization.dart'; import 'package:evently/evently_provider.dart'; import 'package:evently/screens/buyer_status_screen.dart'; @@ -65,7 +66,8 @@ class MyApp extends StatelessWidget { builder: (context, widget) { ScreenUtil.init(context); return MediaQuery( - data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), + data: MediaQuery.of(context) + .copyWith(textScaler: TextScaler.noScaling), child: widget!, ); }, @@ -89,6 +91,7 @@ class MyApp extends StatelessWidget { } bool _getIsCurrentDeviceTablet() { - final MediaQueryData mediaQuery = MediaQueryData.fromView(PlatformDispatcher.instance.implicitView!); + final MediaQueryData mediaQuery = + MediaQueryData.fromView(PlatformDispatcher.instance.implicitView!); return mediaQuery.size.shortestSide >= tabletMinWidth; } diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index b9d32b2f1c..b7d9a9a70e 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -1,8 +1,8 @@ import 'package:equatable/equatable.dart'; import 'package:evently/utils/enums.dart'; +import 'package:fixnum/fixnum.dart'; import 'package:floor/floor.dart'; import 'package:pylons_sdk/low_level.dart'; -import 'package:fixnum/fixnum.dart'; @entity class Events extends Equatable { @@ -60,7 +60,8 @@ class Events extends Equatable { }); factory Events.fromRecipe(Recipe recipe) { - Map map = _extractAttributeValues(recipe.entries.itemOutputs[0].strings); + Map map = + _extractAttributeValues(recipe.entries.itemOutputs[0].strings); return Events( eventName: map[kEventName]!, hostName: map[kEventHostName]!, @@ -80,7 +81,8 @@ class Events extends Equatable { ); } - static Map _extractAttributeValues(List attributes) { + static Map _extractAttributeValues( + List attributes) { final Map attributeValues = {}; for (final attribute in attributes) { switch (attribute.key) { @@ -110,8 +112,24 @@ class Events extends Equatable { } @override - List get props => - [eventName, hostName, thumbnail, startDate, endDate, startTime, endTime, location, description, numberOfTickets, price, listOfPerks, isFreeDrops, cookbookID, recipeID, step]; + List get props => [ + eventName, + hostName, + thumbnail, + startDate, + endDate, + startTime, + endTime, + location, + description, + numberOfTickets, + price, + listOfPerks, + isFreeDrops, + cookbookID, + recipeID, + step + ]; @override String toString() { @@ -131,7 +149,7 @@ extension CreateRecipe on Events { cookbookId: cookbookId, id: recipeId, nodeVersion: Int64(1), - name: eventName.trim(), + name: eventName.trim() + hostName.trim(), description: description.trim(), version: kVersion, coinInputs: [ @@ -216,17 +234,22 @@ class PerksModel { final String name; final String description; - factory PerksModel.updateName({required String name, required PerksModel perksModel}) => PerksModel( + factory PerksModel.updateName( + {required String name, required PerksModel perksModel}) => + PerksModel( name: name, description: perksModel.description, ); - factory PerksModel.updateDescription({required String description, required PerksModel perksModel}) => PerksModel( + factory PerksModel.updateDescription( + {required String description, required PerksModel perksModel}) => + PerksModel( name: perksModel.name, description: description, ); - factory PerksModel.fromJson(Map map) => PerksModel(name: map['name'] ?? '', description: map['description'] ?? ''); + factory PerksModel.fromJson(Map map) => PerksModel( + name: map['name'] ?? '', description: map['description'] ?? ''); Map toJson() { Map map = {}; From 835ab480d62e269b07daf1146322b282d9f4d9f0 Mon Sep 17 00:00:00 2001 From: Girish Date: Sun, 9 Jun 2024 15:41:55 +0300 Subject: [PATCH 151/161] Update evently/lib/models/events.dart Co-authored-by: kjawadDeveloper2 <90063570+kjawadDeveloper2@users.noreply.github.com> --- evently/lib/models/events.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index b7d9a9a70e..95905e60ce 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -249,7 +249,7 @@ class PerksModel { ); factory PerksModel.fromJson(Map map) => PerksModel( - name: map['name'] ?? '', description: map['description'] ?? ''); + name: map['name'] ?? '', description: map['description'] ?? '',); Map toJson() { Map map = {}; From a3caab573c79985b6d12ceed439957449f4e01a8 Mon Sep 17 00:00:00 2001 From: Girish Date: Sun, 9 Jun 2024 15:42:01 +0300 Subject: [PATCH 152/161] Update evently/lib/models/events.dart Co-authored-by: kjawadDeveloper2 <90063570+kjawadDeveloper2@users.noreply.github.com> --- evently/lib/models/events.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evently/lib/models/events.dart b/evently/lib/models/events.dart index 95905e60ce..5a17f9fe74 100644 --- a/evently/lib/models/events.dart +++ b/evently/lib/models/events.dart @@ -242,7 +242,7 @@ class PerksModel { ); factory PerksModel.updateDescription( - {required String description, required PerksModel perksModel}) => + {required String description, required PerksModel perksModel,}) => PerksModel( name: perksModel.name, description: description, From 389af51f8a912f31c54efb39306ae340d4741c90 Mon Sep 17 00:00:00 2001 From: Raviramnani1 Date: Fri, 14 Jun 2024 16:23:24 +0530 Subject: [PATCH 153/161] demo demo --- evently/android/app/build.gradle | 4 ++-- evently/pubspec.lock | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/evently/android/app/build.gradle b/evently/android/app/build.gradle index 8f00827493..73a1423a38 100644 --- a/evently/android/app/build.gradle +++ b/evently/android/app/build.gradle @@ -52,7 +52,7 @@ android { } signingConfigs { - debug { + release { storeFile file('keystore.jks') storePassword 'tech.pylons' keyAlias 'key_tech_pylon' @@ -61,7 +61,7 @@ android { } buildTypes { - release { + debug { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug diff --git a/evently/pubspec.lock b/evently/pubspec.lock index ce96ed57ce..af0ac58ecf 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -529,10 +529,10 @@ packages: dependency: "direct main" description: name: injectable - sha256: "3d98967224a5fdd4094a61bf53ed9616c3fbcf3e090bf83e7cb7d436d0c20041" + sha256: "3c8355a29d11ff28c0311bed754649761f345ef7a13ff66a714380954af51226" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" injectable_generator: dependency: "direct main" description: @@ -545,10 +545,10 @@ packages: dependency: transitive description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -577,26 +577,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -1101,10 +1101,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" timing: dependency: transitive description: @@ -1245,10 +1245,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" watcher: dependency: transitive description: From d6fa3bbfa30942178e93ce3de13cf44453c923ed Mon Sep 17 00:00:00 2001 From: Raviramnani1 Date: Tue, 18 Jun 2024 18:22:35 +0530 Subject: [PATCH 154/161] Date validation date validation feature for event creation --- evently/lib/screens/detail_screen.dart | 359 ++++++++++++++----------- 1 file changed, 206 insertions(+), 153 deletions(-) diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index ec4d57a16a..5d62fde20b 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -13,8 +13,10 @@ import 'package:evently/utils/space_utils.dart'; import 'package:evently/viewmodels/create_event_viewmodel.dart'; import 'package:evently/widgets/evently_text_field.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; +import 'package:intl/intl.dart'; class DetailsScreen extends StatefulWidget { const DetailsScreen({super.key}); @@ -24,6 +26,8 @@ class DetailsScreen extends StatefulWidget { } class _DetailsScreenState extends State { + final GlobalKey scaffoldMessengerKey = GlobalKey(); + @override void initState() { super.initState(); @@ -32,192 +36,241 @@ class _DetailsScreenState extends State { @override Widget build(BuildContext context) { final createEventViewModel = context.watch(); + final provider = context.watch(); - return Scaffold( - body: SingleChildScrollView( - child: Consumer( - builder: (_, provider, __) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const VerticalSpace(20), - MyStepsIndicator(currentStep: createEventViewModel.currentStep), - StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), - const VerticalSpace(20), - PageAppBar(onPressBack: () { - createEventViewModel.previousPage(); - }), - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: GestureDetector( - onTap: () async { - _showDatePicker(onSelected: (DateTime? val) { - if (val == null) return; - provider.setStartDate = _dateFormatter(val); - }); - }, - child: EventlyTextField( + return ScaffoldMessenger( + key: scaffoldMessengerKey, + child: Scaffold( + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const VerticalSpace(20), + MyStepsIndicator(currentStep: createEventViewModel.currentStep), + StepLabels(currentPage: createEventViewModel.currentPage, currentStep: createEventViewModel.currentStep), + const VerticalSpace(20), + PageAppBar(onPressBack: () { + createEventViewModel.previousPage(); + }), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 15.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () async { + _showDatePicker(onSelected: (DateTime? val) { + if (val == null) return; + provider.setStartDate = _formatDateToIso(val); + // Validate dates only if both dates are set + if (provider.endDate.isNotEmpty) { + _validateDates(provider); + } + }); + }, + child: EventlyTextField( enable: false, label: LocaleKeys.start_date.tr(), - controller: TextEditingController(text: provider.startDate), + controller: TextEditingController(text: _formatDateDisplay(provider.startDate)), textCapitalization: TextCapitalization.sentences, imageBackground: PngUtils.textFieldBottomLeft, - inputTextColor: EventlyAppTheme.kTextDarkBlue), + inputTextColor: EventlyAppTheme.kTextDarkBlue, + ), + ), ), - ), - HorizontalSpace(20.w), - Expanded( - child: GestureDetector( - onTap: () { - _showDatePicker(onSelected: (DateTime? val) { - if (val == null) return; - provider.setEndDate = _dateFormatter(val); - }); - }, - child: EventlyTextField( + HorizontalSpace(20.w), + Expanded( + child: GestureDetector( + onTap: () { + _showDatePicker(onSelected: (DateTime? val) { + if (val == null) return; + provider.setEndDate = _formatDateToIso(val); + // Validate dates only if both dates are set + if (provider.startDate.isNotEmpty) { + _validateDates(provider); + } + }); + }, + child: EventlyTextField( enable: false, label: LocaleKeys.end_date.tr(), - controller: TextEditingController(text: provider.endDate), + controller: TextEditingController(text: _formatDateDisplay(provider.endDate)), textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, imageBackground: PngUtils.textFieldTopRight, - inputTextColor: EventlyAppTheme.kTextDarkBlue), + inputTextColor: EventlyAppTheme.kTextDarkBlue, + ), + ), ), - ), - ], - ), - VerticalSpace(20.h), - Row( - children: [ - Expanded( - child: GestureDetector( - onTap: () { - _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { - if (timeOfDay == null) return; - final currentTime = DateTime.now(); - final time = currentTime.copyWith(hour: timeOfDay.hour, minute: timeOfDay.minute); - provider.setStartTime = _timerFormatter(time); - }); - }, - child: EventlyTextField( + ], + ), + VerticalSpace(20.h), + Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + _showTimePicker(onSelected: (TimeOfDay? timeOfDay) { + if (timeOfDay == null) return; + final currentTime = DateTime.now(); + final time = currentTime.copyWith(hour: timeOfDay.hour, minute: timeOfDay.minute); + provider.setStartTime = _formatTimeToIso(time); + }); + }, + child: EventlyTextField( enable: false, label: LocaleKeys.start_time.tr(), controller: TextEditingController(text: provider.startTime), textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, imageBackground: PngUtils.textFieldBottomLeft, - inputTextColor: EventlyAppTheme.kTextDarkBlue), + inputTextColor: EventlyAppTheme.kTextDarkBlue, + ), + ), ), - ), - HorizontalSpace(20.w), - Expanded( - child: GestureDetector( - onTap: () { - _showTimerPicker(onSelected: (TimeOfDay? timeOfDay) { - if (timeOfDay == null) return; - final currentTime = DateTime.now(); - final time = currentTime.copyWith(hour: timeOfDay.hour, minute: timeOfDay.minute); - provider.setEndTime = _timerFormatter(time); - }); - }, - child: EventlyTextField( - enable: false, - label: LocaleKeys.end_time.tr(), - controller: TextEditingController(text: provider.endTime), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; + HorizontalSpace(20.w), + Expanded( + child: GestureDetector( + onTap: () { + _showTimePicker(onSelected: (TimeOfDay? timeOfDay) { + if (timeOfDay == null) return; + final currentTime = DateTime.now(); + final time = currentTime.copyWith(hour: timeOfDay.hour, minute: timeOfDay.minute); + provider.setEndTime = _formatTimeToIso(time); + }); }, - imageBackground: PngUtils.textFieldTopRight, - inputTextColor: EventlyAppTheme.kTextDarkBlue, + child: EventlyTextField( + enable: false, + label: LocaleKeys.end_time.tr(), + controller: TextEditingController(text: provider.endTime), + textCapitalization: TextCapitalization.sentences, + imageBackground: PngUtils.textFieldTopRight, + inputTextColor: EventlyAppTheme.kTextDarkBlue, + ), ), ), - ), - ], - ), - VerticalSpace(20.h), - EventlyTextField( - onChanged: (_) => provider.setLocation = _, - label: LocaleKeys.location.tr(), - hint: LocaleKeys.search_location.tr(), - controller: TextEditingController(text: provider.location) - ..selection = TextSelection.fromPosition( - TextPosition(offset: provider.location.length), - ), - textCapitalization: TextCapitalization.sentences, - validator: (value) { - return null; - }, - ), - VerticalSpace(20.h), - EventlyTextField( - onChanged: (_) => provider.setDescription = _, - label: LocaleKeys.description.tr(), - hint: LocaleKeys.what_event_for.tr(), - controller: TextEditingController(text: provider.description) - ..selection = TextSelection.fromPosition( - TextPosition(offset: provider.description.length), - ), - textCapitalization: TextCapitalization.sentences, - noOfLines: 4, - ), - const VerticalSpace(40), - BottomButtons( - onPressContinue: () { - createEventViewModel.nextPage(); - }, - onPressSaveDraft: () { - final navigator = Navigator.of(context); - provider.saveAsDraft( - onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), - uploadStep: UploadStep.detail, - ); - }, - isContinueEnable: provider.startDate.isNotEmpty && - provider.endDate.isNotEmpty && - provider.startTime.isNotEmpty && - provider.endTime.isNotEmpty && - provider.description.isNotEmpty && - provider.location.isNotEmpty, - ), - ], + ], + ), + VerticalSpace(20.h), + EventlyTextField( + onChanged: (val) => provider.setLocation = val, + label: LocaleKeys.location.tr(), + hint: LocaleKeys.search_location.tr(), + controller: TextEditingController(text: provider.location) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.location.length), + ), + textCapitalization: TextCapitalization.sentences, + ), + VerticalSpace(20.h), + EventlyTextField( + onChanged: (val) => provider.setDescription = val, + label: LocaleKeys.description.tr(), + hint: LocaleKeys.what_event_for.tr(), + controller: TextEditingController(text: provider.description) + ..selection = TextSelection.fromPosition( + TextPosition(offset: provider.description.length), + ), + textCapitalization: TextCapitalization.sentences, + noOfLines: 4, + ), + const VerticalSpace(40), + BottomButtons( + onPressContinue: () { + if (_validateDates(provider)) { + createEventViewModel.nextPage(); + } else { + _showSnackBarWithPostFrameCallback('End date cannot be before start date!'); + } + }, + onPressSaveDraft: () { + final navigator = Navigator.of(context); + if (_validateDates(provider)) { + provider.saveAsDraft( + onCompleted: () => navigator.popUntil((route) => route.settings.name == RouteUtil.kRouteEventHub), + uploadStep: UploadStep.detail, + ); + } else { + _showSnackBarWithPostFrameCallback('End date cannot be before start date!'); + } + }, + isContinueEnable: provider.startDate.isNotEmpty && + provider.endDate.isNotEmpty && + provider.startTime.isNotEmpty && + provider.endTime.isNotEmpty && + provider.description.isNotEmpty && + provider.location.isNotEmpty, + ), + ], + ), ), - ), - ], + ], + ), ), - )), + ), ); } - _showTimerPicker({required ValueChanged onSelected}) { - showTimePicker( - initialTime: TimeOfDay.now(), - context: context, - ).then((value) => onSelected(value!)); - } - _showDatePicker({required ValueChanged onSelected}) { showDatePicker( context: context, firstDate: DateTime.now(), lastDate: DateTime(2099, 12), - ).then((value) => onSelected(value!)); + ).then((value) => onSelected(value)); + } + + _showTimePicker({required ValueChanged onSelected}) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((value) => onSelected(value)); } - _timerFormatter(DateTime dateTime) { - return DateFormat('hh:mm a').format(dateTime); + String _formatDateToIso(DateTime? dateTime) { + if (dateTime == null) return ''; + return DateFormat('yyyy-MM-dd').format(dateTime); + } + + String _formatDateDisplay(String? date) { + if (date == null || date.isEmpty) return ''; + try { + final DateTime parsedDate = DateFormat('yyyy-MM-dd').parse(date); + return DateFormat.yMMMMd('en_US').format(parsedDate); + } catch (e) { + return ''; + } + } + + String _formatTimeToIso(DateTime? dateTime) { + if (dateTime == null) return ''; + return DateFormat('HH:mm').format(dateTime); + } + + bool _validateDates(EventlyProvider provider) { + if (provider.startDate.isEmpty || provider.endDate.isEmpty) { + return true; // Dates are not set yet, so no validation needed + } + + try { + final DateTime start = DateFormat('yyyy-MM-dd').parse(provider.startDate); + final DateTime end = DateFormat('yyyy-MM-dd').parse(provider.endDate); + if (end.isBefore(start)) { + return false; // End date is before start date + } + } catch (e) { + return false; // Invalid date format + } + + return true; // Dates are valid } - _dateFormatter(DateTime dateTime) { - return DateFormat.yMMMMd('en_US').format(dateTime); + void _showSnackBarWithPostFrameCallback(String message) { + SchedulerBinding.instance!.addPostFrameCallback((_) { + scaffoldMessengerKey.currentState!.showSnackBar(SnackBar( + content: Text(message), + duration: Duration(seconds: 2), + )); + }); } } From a473c35bfcc9d1f9ef45ab871cb4d64e3d524c1c Mon Sep 17 00:00:00 2001 From: Raviramnani1 <64575750+Raviramnani1@users.noreply.github.com> Date: Fri, 21 Jun 2024 20:12:26 +0530 Subject: [PATCH 155/161] Update build.gradle --- evently/android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evently/android/app/build.gradle b/evently/android/app/build.gradle index 73a1423a38..63ae673595 100644 --- a/evently/android/app/build.gradle +++ b/evently/android/app/build.gradle @@ -52,7 +52,7 @@ android { } signingConfigs { - release { + debug { storeFile file('keystore.jks') storePassword 'tech.pylons' keyAlias 'key_tech_pylon' @@ -61,7 +61,7 @@ android { } buildTypes { - debug { + release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug From fa4428278458ff8ab420d3aae6649c989b85a8d7 Mon Sep 17 00:00:00 2001 From: raviramnani Date: Sat, 27 Jul 2024 13:57:34 +0530 Subject: [PATCH 156/161] Buy_now set Next_public_api_key env variable as BaseURL --- buy-now/pages/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buy-now/pages/index.tsx b/buy-now/pages/index.tsx index d32e04bd22..321b295d54 100644 --- a/buy-now/pages/index.tsx +++ b/buy-now/pages/index.tsx @@ -138,7 +138,7 @@ export default function EaselBuyMainPage({ export async function getServerSideProps({ res, query }: any): Promise { const recipeId: string = query?.recipe_id ?? ""; const cookbookId: string = query?.cookbook_id ?? ""; - const baseURL: string = "https://api.pylons.nodestake.top"; + const baseURL: string = process.env.NEXT_PUBLIC_API_KEY ?? ""; if (!recipeId || !cookbookId) { return { redirect: { From 678211ed428dc836593f34c5f1f0917f4b676a6d Mon Sep 17 00:00:00 2001 From: raviramnani Date: Tue, 30 Jul 2024 15:15:07 +0530 Subject: [PATCH 157/161] Added check for number of tickets to be less than 1ooo in price_screen.dart --- evently/lib/screens/price_screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evently/lib/screens/price_screen.dart b/evently/lib/screens/price_screen.dart index 267ad84545..d955b4570e 100644 --- a/evently/lib/screens/price_screen.dart +++ b/evently/lib/screens/price_screen.dart @@ -217,8 +217,8 @@ class _PriceScreenState extends State { isContinueEnable: provider.isFreeDrop == FreeDrop.unselected ? false : provider.isFreeDrop == FreeDrop.yes - ? provider.numberOfTickets > 0 - : provider.numberOfTickets > 0 && provider.price > 0, + ? (provider.numberOfTickets > 0 && provider.numberOfTickets <= 1000) + : (provider.numberOfTickets > 0 && provider.numberOfTickets <= 1000 && provider.price > 0), ), ) ], From 7c68564419cc69045a6f49d5dd66b3d29a2c3009 Mon Sep 17 00:00:00 2001 From: raviramnani Date: Tue, 30 Jul 2024 15:21:51 +0530 Subject: [PATCH 158/161] solved thumbnail box bottom overflown by 13 pixel error in overview_screen.dart --- evently/lib/screens/overview_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evently/lib/screens/overview_screen.dart b/evently/lib/screens/overview_screen.dart index 8c5bd9ee0b..568270aca3 100644 --- a/evently/lib/screens/overview_screen.dart +++ b/evently/lib/screens/overview_screen.dart @@ -120,7 +120,7 @@ class _OverViewScreenState extends State { onTap: () => provider.pickThumbnail(), child: Container( width: double.infinity, - height: 180, + height: 200, padding: EdgeInsets.symmetric(vertical: 20.w), child: Column( children: [ From 8a5bb7abb2f0e54985aa55eb4fb23194f7a2dd40 Mon Sep 17 00:00:00 2001 From: raviramnani Date: Tue, 30 Jul 2024 15:50:00 +0530 Subject: [PATCH 159/161] Added time validation if the date is same in detail_screen.dart --- evently/lib/screens/detail_screen.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/evently/lib/screens/detail_screen.dart b/evently/lib/screens/detail_screen.dart index 5d62fde20b..2e87493559 100644 --- a/evently/lib/screens/detail_screen.dart +++ b/evently/lib/screens/detail_screen.dart @@ -181,7 +181,7 @@ class _DetailsScreenState extends State { if (_validateDates(provider)) { createEventViewModel.nextPage(); } else { - _showSnackBarWithPostFrameCallback('End date cannot be before start date!'); + _showSnackBarWithPostFrameCallback('End date and time cannot be before start date and time!'); } }, onPressSaveDraft: () { @@ -192,7 +192,7 @@ class _DetailsScreenState extends State { uploadStep: UploadStep.detail, ); } else { - _showSnackBarWithPostFrameCallback('End date cannot be before start date!'); + _showSnackBarWithPostFrameCallback('End date and time cannot be before start date and time!'); } }, isContinueEnable: provider.startDate.isNotEmpty && @@ -258,6 +258,15 @@ class _DetailsScreenState extends State { if (end.isBefore(start)) { return false; // End date is before start date } + // Check times if the dates are the same + if (provider.startDate.compareTo(provider.endDate) == 0) { + final DateTime startTime = DateFormat('HH:mm').parse(provider.startTime); + final DateTime endTime = DateFormat('HH:mm').parse(provider.endTime); + + if (endTime.isBefore(startTime)) { + return false; // End time is before start time on the same day + } + } } catch (e) { return false; // Invalid date format } From e600ffc9d40b68f36a303011eb4dbc49a87f3657 Mon Sep 17 00:00:00 2001 From: raviramnani Date: Tue, 30 Jul 2024 16:17:34 +0530 Subject: [PATCH 160/161] updated docker file to support node 18 --- buy-now/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buy-now/Dockerfile b/buy-now/Dockerfile index 59b7ec4685..a047d7d027 100644 --- a/buy-now/Dockerfile +++ b/buy-now/Dockerfile @@ -1,5 +1,5 @@ # Install dependencies only when needed -FROM node:16-alpine AS deps +FROM node:18-alpine AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat WORKDIR /app From d217ce79a57ee9f727f8c66dd05e38e7e4a1116d Mon Sep 17 00:00:00 2001 From: raviramnani Date: Tue, 30 Jul 2024 16:20:31 +0530 Subject: [PATCH 161/161] updated docker file to support node 18 --- buy-now/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buy-now/Dockerfile b/buy-now/Dockerfile index a047d7d027..87b93969e9 100644 --- a/buy-now/Dockerfile +++ b/buy-now/Dockerfile @@ -10,7 +10,7 @@ RUN npm i --legacy-peer-deps # Rebuild the source code only when needed -FROM node:16-alpine AS builder +FROM node:18-alpine AS builder ENV GENERATE_SOURCEMAP false WORKDIR /app @@ -34,7 +34,7 @@ RUN npm run build # RUN npm run build # Production image, copy all the files and run next -FROM node:16-alpine AS runner +FROM node:18-alpine AS runner WORKDIR /app ENV NODE_ENV production