diff --git a/.env b/.env new file mode 100644 index 00000000..b85f7c1b --- /dev/null +++ b/.env @@ -0,0 +1 @@ +KEY="Enter KEY Here"; \ No newline at end of file diff --git a/.github/workflows/fortify.yml b/.github/workflows/fortify.yml new file mode 100644 index 00000000..fa29f460 --- /dev/null +++ b/.github/workflows/fortify.yml @@ -0,0 +1,98 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +################################################################################################################################################ +# Fortify lets you build secure software fast with an appsec platform that automates testing throughout the DevSecOps pipeline. Fortify static,# +# dynamic, interactive, and runtime security testing is available on premises or as a service. To learn more about Fortify, start a free trial # +# or contact our sales team, visit microfocus.com/appsecurity. # +# # +# Use this workflow template as a basis for integrating Fortify on Demand Static Application Security Testing(SAST) into your GitHub workflows.# +# This template demonstrates the steps to prepare the code+dependencies, initiate a scan, download results once complete and import into # +# GitHub Security Code Scanning Alerts. Existing customers should review inputs and environment variables below to configure scanning against # +# an existing application in your Fortify on Demand tenant. Additional information is available in the comments throughout the workflow, the # +# documentation for the Fortify actions used, and the Fortify on Demand / ScanCentral Client product documentation. If you need additional # +# assistance with configuration, feel free to create a help ticket in the Fortify on Demand portal. # +################################################################################################################################################ + +name: Fortify on Demand Scan + +# TODO: Customize trigger events based on your DevSecOps processes and typical FoD SAST scan time +on: + workflow_dispatch: + push: + branches: [ "master" ] + schedule: + - cron: '28 12 * * 1' + +jobs: + FoD-SAST-Scan: + # Use the appropriate runner for building your source code. + # TODO: Use a Windows runner for .NET projects that use msbuild. Additional changes to RUN commands will be required to switch to Windows syntax. + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + # Check out source code + - name: Check Out Source Code + uses: actions/checkout@v3 + + # Java is required to run the various Fortify utilities. + # When scanning a Java application, please use the appropriate Java version for building your application. + - name: Setup Java + uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: 'temurin' + + # Prepare source+dependencies for upload. The default example is for a Maven project that uses pom.xml. + # TODO: Update PACKAGE_OPTS based on the ScanCentral Client documentation for your project's included tech stack(s). Helpful hints: + # ScanCentral Client will download dependencies for maven (-bt mvn) and gradle (-bt gradle). + # ScanCentral Client can download dependencies for msbuild projects (-bt msbuild); however, you must convert the workflow to use a Windows runner. + # ScanCentral has additional options that should be set for PHP and Python projects + # For other build tools, add your build commands to download necessary dependencies and prepare according to Fortify on Demand Packaging documentation. + # ScanCentral Client documentation is located at https://www.microfocus.com/documentation/fortify-software-security-center/ + - name: Download Fortify ScanCentral Client + uses: fortify/gha-setup-scancentral-client@5b7382f8234fb9840958c49d5f32ae854115f9f3 + - name: Package Code + Dependencies + run: scancentral package $PACKAGE_OPTS -o package.zip + env: + PACKAGE_OPTS: "-bt mvn" + + # Start Fortify on Demand SAST scan and wait until results complete. For more information on FoDUploader commands, see https://github.com/fod-dev/fod-uploader-java + # TODO: Update ENV variables for your application and create the necessary GitHub Secrets. Helpful hints: + # Credentials and release ID should be obtained from your FoD tenant (either Personal Access Token or API Key can be used). + # Automated Audit preference should be configured for the release's Static Scan Settings in the Fortify on Demand portal. + - name: Download Fortify on Demand Universal CI Tool + uses: fortify/gha-setup-fod-uploader@6e6bb8a33cb476e240929fa8ebc739ff110e7433 + - name: Perform SAST Scan + run: java -jar $FOD_UPLOAD_JAR -z package.zip -aurl $FOD_API_URL -purl $FOD_URL -rid "$FOD_RELEASE_ID" -tc "$FOD_TENANT" -uc "$FOD_USER" "$FOD_PAT" $FOD_UPLOADER_OPTS -n "$FOD_UPLOADER_NOTES" + env: + FOD_URL: "https://ams.fortify.com/" + FOD_API_URL: "https://api.ams.fortify.com/" + FOD_TENANT: ${{ secrets.FOD_TENANT }} + FOD_USER: ${{ secrets.FOD_USER }} + FOD_PAT: ${{ secrets.FOD_PAT }} + FOD_RELEASE_ID: ${{ secrets.FOD_RELEASE_ID }} + FOD_UPLOADER_OPTS: "-ep 2 -pp 0 -I 1 -apf" + FOD_UPLOADER_NOTES: 'Triggered by GitHub Actions (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})' + + # Once scan completes, pull SAST issues from Fortify on Demand and generate SARIF output. + - name: Export results to GitHub-optimized SARIF + uses: fortify/gha-export-vulnerabilities@fcb374411cff9809028c911dabb8b57dbdae623b + with: + fod_base_url: "https://ams.fortify.com/" + fod_tenant: ${{ secrets.FOD_TENANT }} + fod_user: ${{ secrets.FOD_USER }} + fod_password: ${{ secrets.FOD_PAT }} + fod_release_id: ${{ secrets.FOD_RELEASE_ID }} + + # Import Fortify on Demand results to GitHub Security Code Scanning + - name: Import Results + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: ./gh-fortify-sast.sarif diff --git a/.gitignore b/.gitignore index a77c7941..766b7dd8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ .buildlog/ .history .svn/ +*.env # IntelliJ related *.iml diff --git a/Changelog.md b/Changelog.md index aa4ab7c2..0d0c5b8c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,25 @@ ## Unreleased +## 2.4.1 + +- Move map page outside utilities +- Implement deep links for map page +- Fix Android 11+ url open issue +- Remove Google Maps library (due to iOS limitations) + +## 2.4.0 + +- Implement Buy and Sell + +## 2.3.0 + +- Revert 5 year b'day changes + +## 2.2.2 (reverted) + +- 5 year b'day changes + ## v2.2.1 - Add a comment in Mess Menu page for hostel councils diff --git a/README.md b/README.md index 7424ac03..1e020fcd 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,12 @@ For help getting started with Flutter, view online ## Getting Started 1. Follow instructions at [Flutter Website](https://flutter.io/) to download and setup flutter and its dependencies -2. Clone this repo -3. Run `flutter packages get` to get all the dependency packages -4. Run `flutter packages pub run build_runner build --delete-conflicting-outputs` to generate code for JSON Serailizing and Retrofit ApiClient -5. Run `flutter run` to run the app on any device/simulator/emulator. -6. \[_Optional but recommended_\] Use either Visual Studio Code or Android Studio to code/debug. +2. We use Flutter version 2.10.5 +3. Clone this repo +4. Run `flutter packages get` to get all the dependency packages +5. Run `flutter packages pub run build_runner build --delete-conflicting-outputs` to generate code for JSON Serializing and Retrofit ApiClient +6. Run `flutter run` to run the app on any device/simulator/emulator. +7. \[_Optional but recommended_\] Use either Visual Studio Code or Android Studio to code/debug. ## pod install error on M1 mac diff --git a/android/app/build.gradle b/android/app/build.gradle index a726db33..94263f61 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -43,9 +43,9 @@ android { defaultConfig { applicationId "app.insti.flutter" minSdkVersion 26 - targetSdkVersion 31 - versionCode 25 - versionName "2.2.1" + targetSdkVersion 33 + versionCode 32 + versionName "2.4.4" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 64803787..beffafec 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ + + - - - + +


or

@@ -128,6 +145,23 @@ + + \ No newline at end of file diff --git a/assets/login/login_dark.html b/assets/login/login_dark.html index 015c4946..e04eef28 100644 --- a/assets/login/login_dark.html +++ b/assets/login/login_dark.html @@ -4,6 +4,7 @@ @@ -112,12 +128,14 @@
- +


or

@@ -132,6 +150,22 @@
+ \ No newline at end of file diff --git a/ios/GoogleService-Info.plist b/ios/GoogleService-Info.plist new file mode 100644 index 00000000..62c69483 --- /dev/null +++ b/ios/GoogleService-Info.plist @@ -0,0 +1,38 @@ + + + + + CLIENT_ID + 259853447628-c8oqskpps37cp7utc9eg66cfverl1uou.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.259853447628-c8oqskpps37cp7utc9eg66cfverl1uou + ANDROID_CLIENT_ID + 259853447628-6hcftl72mg4ku9qvigs89f2etit8096f.apps.googleusercontent.com + API_KEY + AIzaSyBh5H5f1gSyCjV7Gp3UnKAsT8ORR3IpMcY + GCM_SENDER_ID + 259853447628 + PLIST_VERSION + 1 + BUNDLE_ID + app.instiapp.flutter + PROJECT_ID + astral-theory-207617 + STORAGE_BUCKET + astral-theory-207617.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:259853447628:ios:9e225f255f93748e70ed69 + DATABASE_URL + https://astral-theory-207617.firebaseio.com + + \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index 1e8c3c90..469e02f8 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -38,4 +38,12 @@ post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) end + + installer.generated_projects.each do |project| + project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' + end + end + end end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index afd73b88..8d6394be 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 08BB3402B20078142410C626 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1613304FEB16BF453B8A7EF4 /* Pods_Runner.framework */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 157FE497287E14F8006DA854 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 157FE496287E14F8006DA854 /* GoogleService-Info.plist */; }; 15B703D427F86A110040D0AD /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15B703D327F86A110040D0AD /* WidgetKit.framework */; }; 15B703D627F86A110040D0AD /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15B703D527F86A110040D0AD /* SwiftUI.framework */; }; 15B703D927F86A110040D0AD /* MessMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15B703D827F86A110040D0AD /* MessMenu.swift */; }; @@ -25,6 +24,8 @@ 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 */; }; + BC299CE12A7FBEC400BD2070 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = BC299CE02A7FBEC400BD2070 /* GoogleService-Info.plist */; }; + BC299CE22A7FBEC400BD2070 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = BC299CE02A7FBEC400BD2070 /* GoogleService-Info.plist */; }; D66CA1E527777987001E7434 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66CA1E427777987001E7434 /* File.swift */; }; /* End PBXBuildFile section */ @@ -56,7 +57,6 @@ 1114E73EA8E0A36542EECFD9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 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 = ""; }; - 157FE496287E14F8006DA854 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../../Downloads/GoogleService-Info.plist"; sourceTree = ""; }; 15B703D227F86A110040D0AD /* MessMenuExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MessMenuExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 15B703D327F86A110040D0AD /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; 15B703D527F86A110040D0AD /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; @@ -68,7 +68,6 @@ 1613304FEB16BF453B8A7EF4 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 4969AAA02206311F0042C827 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; - 6282C9FDD83A58E14F90EEFA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -80,6 +79,7 @@ 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 = ""; }; + BC299CE02A7FBEC400BD2070 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; }; D66CA1E327777987001E7434 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; D66CA1E427777987001E7434 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; E1D2A8C0A5C935E6E3B684F8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; @@ -122,11 +122,6 @@ 1A7F164AB156C93E0D101431 /* Frameworks */ = { isa = PBXGroup; children = ( - 151E6B8B2881F73800EDF6D4 /* FirebaseCore.framework */, - 151E6B882881F6DB00EDF6D4 /* FirebaseMessaging.framework */, - 151E6B852881F69500EDF6D4 /* awesome_notifications.framework */, - 151E6B822881F67800EDF6D4 /* firebase_messaging.framework */, - 151E6B7E2881F5EA00EDF6D4 /* FirebaseMessaging.framework */, 15B703D327F86A110040D0AD /* WidgetKit.framework */, 15B703D527F86A110040D0AD /* SwiftUI.framework */, 1613304FEB16BF453B8A7EF4 /* Pods_Runner.framework */, @@ -171,7 +166,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 157FE496287E14F8006DA854 /* GoogleService-Info.plist */, + BC299CE02A7FBEC400BD2070 /* GoogleService-Info.plist */, 4969AAA02206311F0042C827 /* Runner.entitlements */, 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, @@ -236,7 +231,6 @@ 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 15B703E327F86A130040D0AD /* Embed App Extensions */, 744BFA914FCE4568FA012E0D /* [CP] Embed Pods Frameworks */, - E055E411A43BF9A37D7E2439 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -302,6 +296,7 @@ buildActionMask = 2147483647; files = ( 15B703DC27F86A130040D0AD /* Assets.xcassets in Resources */, + BC299CE22A7FBEC400BD2070 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -311,9 +306,9 @@ files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + BC299CE12A7FBEC400BD2070 /* GoogleService-Info.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - 157FE497287E14F8006DA854 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -372,15 +367,16 @@ "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", "${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework", - "${BUILT_PRODUCTS_DIR}/OrderedSet/OrderedSet.framework", "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework", + "${BUILT_PRODUCTS_DIR}/camera_avfoundation/camera_avfoundation.framework", "${BUILT_PRODUCTS_DIR}/device_calendar/device_calendar.framework", + "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework", "${BUILT_PRODUCTS_DIR}/flutter_dynamic_icon/flutter_dynamic_icon.framework", "${BUILT_PRODUCTS_DIR}/flutter_image_compress/flutter_image_compress.framework", - "${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework", "${BUILT_PRODUCTS_DIR}/flutter_keyboard_visibility/flutter_keyboard_visibility.framework", + "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework", "${BUILT_PRODUCTS_DIR}/flutter_native_timezone/flutter_native_timezone.framework", "${BUILT_PRODUCTS_DIR}/home_widget/home_widget.framework", "${BUILT_PRODUCTS_DIR}/image_picker_ios/image_picker_ios.framework", @@ -391,13 +387,11 @@ "${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework", "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", "${BUILT_PRODUCTS_DIR}/share/share.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences_ios/shared_preferences_ios.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework", "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", "${BUILT_PRODUCTS_DIR}/uni_links/uni_links.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", - "${BUILT_PRODUCTS_DIR}/video_player_avfoundation/video_player_avfoundation.framework", - "${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework", - "${BUILT_PRODUCTS_DIR}/webview_flutter_wkwebview/webview_flutter_wkwebview.framework", + "${BUILT_PRODUCTS_DIR}/webview_pro_wkwebview/webview_pro_wkwebview.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -410,15 +404,16 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mantle.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OrderedSet.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/camera_avfoundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_calendar.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_dynamic_icon.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_image_compress.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_keyboard_visibility.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_native_timezone.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/home_widget.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker_ios.framework", @@ -429,13 +424,11 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/uni_links.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player_avfoundation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/webview_flutter_wkwebview.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/webview_pro_wkwebview.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -456,24 +449,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; - E055E411A43BF9A37D7E2439 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -683,7 +658,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -693,7 +668,7 @@ }; 249021D4217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = 1114E73EA8E0A36542EECFD9 /* Pods-Runner.profile.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -701,7 +676,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 5; + CURRENT_PROJECT_VERSION = 15; DEVELOPMENT_TEAM = 5WT47589CF; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -715,7 +690,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 2.1.3; + MARKETING_VERSION = 2.4.3; PRODUCT_BUNDLE_IDENTIFIER = app.instiapp.flutter; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -774,7 +749,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -825,7 +800,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; @@ -844,7 +819,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 5; + CURRENT_PROJECT_VERSION = 15; DEVELOPMENT_TEAM = 5WT47589CF; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -858,7 +833,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 2.1.3; + MARKETING_VERSION = 2.4.3; PRODUCT_BUNDLE_IDENTIFIER = app.instiapp.flutter; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -879,7 +854,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 5; + CURRENT_PROJECT_VERSION = 15; DEVELOPMENT_TEAM = 5WT47589CF; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -893,7 +868,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 2.1.3; + MARKETING_VERSION = 2.4.3; PRODUCT_BUNDLE_IDENTIFIER = app.instiapp.flutter; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/ios/Runner/AppDelegate.m b/ios/Runner/AppDelegate.m index f6875ebf..2d273ccf 100644 --- a/ios/Runner/AppDelegate.m +++ b/ios/Runner/AppDelegate.m @@ -1,13 +1,13 @@ #include "AppDelegate.h" #include "GeneratedPluginRegistrant.h" -#import "GoogleMaps/GoogleMaps.h" +//#import "GoogleMaps/GoogleMaps.h" //#import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GMSServices provideAPIKey:@"AIzaSyAryoUjEhGTWvgrInX1rDAR3D3DEXD5Z7M"]; + // [GMSServices provideAPIKey:@"AIzaSyAryoUjEhGTWvgrInX1rDAR3D3DEXD5Z7M"]; [GeneratedPluginRegistrant registerWithRegistry:self]; // Override point for customization after application launch. return [super application:application didFinishLaunchingWithOptions:launchOptions]; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index ebf35ecb..645fdbcf 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -27,11 +27,11 @@ NSCalendarsUsageDescription To add your interested/going events to your phone calendar NSCameraUsageDescription - This app needs camera access to scan QR codes + This app needs camera access to scan QR codes and to upload product images NSLocationAlwaysUsageDescription - + This app uses your location for navigation in instimap NSLocationWhenInUseUsageDescription - + This app uses your location for navigation in instimap NSMicrophoneUsageDescription NSPhotoLibraryUsageDescription diff --git a/lib/generated_plugin_registrant.dart b/lib/generated_plugin_registrant.dart index 3ba095a4..aba977a9 100644 --- a/lib/generated_plugin_registrant.dart +++ b/lib/generated_plugin_registrant.dart @@ -5,6 +5,7 @@ // ignore_for_file: directives_ordering // ignore_for_file: lines_longer_than_80_chars +import 'package:camera_web/camera_web.dart'; import 'package:device_info_plus_web/device_info_plus_web.dart'; import 'package:firebase_core_web/firebase_core_web.dart'; import 'package:firebase_messaging_web/firebase_messaging_web.dart'; @@ -21,6 +22,7 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart'; // ignore: public_member_api_docs void registerPlugins(Registrar registrar) { + CameraPlugin.registerWith(registrar); DeviceInfoPlusPlugin.registerWith(registrar); FirebaseCoreWeb.registerWith(registrar); FirebaseMessagingWeb.registerWith(registrar); diff --git a/lib/main.dart b/lib/main.dart index d9df0718..d8a6104e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,11 @@ -import 'dart:async'; + import 'dart:async'; import 'package:InstiApp/src/routes/aboutpage.dart'; import 'package:InstiApp/src/routes/bodypage.dart'; +import 'package:InstiApp/src/routes/buynsell_categories.dart'; +import 'package:InstiApp/src/routes/buynsell_createpost.dart'; +import 'package:InstiApp/src/routes/buynsell_info.dart'; +import 'package:InstiApp/src/routes/buynsell_page.dart'; import 'package:InstiApp/src/routes/calendarpage.dart'; import 'package:InstiApp/src/routes/communitydetails.dart'; import 'package:InstiApp/src/routes/communitypostpage.dart'; @@ -47,7 +51,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:uni_links/uni_links.dart'; import 'package:firebase_core/firebase_core.dart'; - +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:awesome_notifications/awesome_notifications.dart'; void main() async { @@ -56,6 +60,7 @@ void main() async { await Firebase.initializeApp(); InstiAppBloc bloc = InstiAppBloc(wholeAppKey: key); FirebaseMessaging.onBackgroundMessage(sendMessage); + await dotenv.load(fileName: ".env"); AwesomeNotifications().initialize( 'resource://drawable/ic_launcher_foreground', @@ -273,6 +278,17 @@ class MyAppState extends State with WidgetsBindingObserver { isBody: true, entityID: temp.split("/putentity/body/")[1], cookie: widget.bloc.getSessionIdHeader())); + } else if (temp.startsWith("/map/")) { + return _buildRoute( + settings, + MapPage( + location:temp.split("/map/")[1])); + }else if (temp.startsWith("/buyandsell/info")) { + return _buildRoute( + settings, + BuyAndSellInfoPage( + post: widget.bloc.buynSellPostBloc + .getBuynSellPost(temp.split("/buyandsell/info")[1]))); } else { switch (settings.name) { case "/": @@ -304,10 +320,23 @@ class MyAppState extends State with WidgetsBindingObserver { return _buildRoute(settings, ChatPage()); case "/groups": return _buildRoute(settings, CommunityPage()); + case "/buynsell": + return _buildRoute(settings, Sellpage()); case "/explore": return _buildRoute(settings, ExplorePage()); case "/calendar": return _buildRoute(settings, CalendarPage()); + case "/buyandsell": + return _buildRoute(settings, Sellpage()); + case "/buyandsell/category": + return _buildRoute(settings, BuyAndSellCategoryPage()); + // case "/buyandsell/info": + // return _buildRoute(settings, Buyandsell_information()); + case "/buyandsell/createPost": + return _buildRoute(settings, BuyAndSellForm()); + // case "/buyandsell/giveinfo": + // return _buildRoute(settings, Buyandsell_information()); + // case "/complaints": // return _buildRoute(settings, ComplaintsPage()); // case "/newcomplaint": @@ -315,6 +344,7 @@ class MyAppState extends State with WidgetsBindingObserver { case "/putentity/event": return _buildRoute(settings, EventForm(cookie: widget.bloc.getSessionIdHeader())); + case "/map": // return _buildRoute(settings, NativeMapPage()); return _buildRoute(settings, MapPage()); @@ -366,7 +396,6 @@ class MyAppState extends State with WidgetsBindingObserver { void handleAppLink(Uri? uri) { if (uri == null) return; - // print(uri.pathSegments); String routeName = "/"; if (uri.pathSegments.length == 1) { routeName = { @@ -382,6 +411,7 @@ class MyAppState extends State with WidgetsBindingObserver { "view-post": "/groups", "discussions": "/groups", "group": "/group/${uri.pathSegments[1]}", + "map": "/map/${uri.pathSegments[1]}", }[uri.pathSegments[0]] ?? routeName; } diff --git a/lib/src/api/apiclient.dart b/lib/src/api/apiclient.dart index b46b9921..8aab5af5 100644 --- a/lib/src/api/apiclient.dart +++ b/lib/src/api/apiclient.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:InstiApp/src/api/model/UserTag.dart'; import 'package:InstiApp/src/api/model/achievements.dart'; import 'package:InstiApp/src/api/model/body.dart'; +import 'package:InstiApp/src/api/model/buynsellPost.dart'; import 'package:InstiApp/src/api/model/community.dart'; import 'package:InstiApp/src/api/model/communityPost.dart'; import 'package:InstiApp/src/api/model/event.dart'; @@ -42,10 +43,10 @@ import 'package:dio/dio.dart'; import 'model/offersecret.dart'; part 'apiclient.g.dart'; -// @rt.RestApi(baseUrl: "http://192.168.230.89:8000/api") +//@rt.RestApi(baseUrl: "http://192.168.1.103:8000/api") // @rt.RestApi(baseUrl: "http://10.105.177.150/api") -@rt.RestApi(baseUrl: "https://2ddd-103-21-126-76.ngrok-free.app/api") -// @rt.RestApi(baseUrl: "https://0ac7-103-21-125-80.in.ngrok.io/api") + @rt.RestApi(baseUrl: "https://gymkhana.iitb.ac.in/instiapp/api") +// @rt.RestApi(baseUrl: "https://272c-2405-201-5004-3c2f-d836-b028-6ac-ad9.ngrok-free.app/api") abstract class InstiAppApi { factory InstiAppApi(Dio dio, {String baseUrl}) = _InstiAppApi; @@ -382,4 +383,25 @@ abstract class InstiAppApi { @rt.POST('/achievements-offer') Future createAchievement( sessionId, @rt.Body() OfferedAchievements offeredAchievements); + +//Buy & Sell + @rt.GET('/buy/products') + Future> getBuynSellPosts( + @rt.Header("Cookie") String sessionId); + + @rt.GET('/buy/products/{id}') + Future getBuynSellPost( + @rt.Header("Cookie") String sessionId, @rt.Path() String id); + + @rt.DELETE('/buy/products/{id}') + Future deleteBuynSellPost( + @rt.Header("Cookie") String sessionId, @rt.Path() String id); + + @rt.PUT('/buy/products/{id}') + Future updateBuynSellPost(@rt.Header("Cookie") String sessionId, + @rt.Path() String id, @rt.Body() BuynSellPost post); + + @rt.POST("/buy/products") + Future createBuynSellPost( + @rt.Header("Cookie") String sessionId, @rt.Body() BuynSellPost post); } diff --git a/lib/src/api/apiclient.g.dart b/lib/src/api/apiclient.g.dart index a2f981f9..70241935 100644 --- a/lib/src/api/apiclient.g.dart +++ b/lib/src/api/apiclient.g.dart @@ -8,7 +8,7 @@ part of 'apiclient.dart'; class _InstiAppApi implements InstiAppApi { _InstiAppApi(this._dio, {this.baseUrl}) { - baseUrl ??= 'https://2ddd-103-21-126-76.ngrok-free.app/api'; + baseUrl ??= 'https://gymkhana.iitb.ac.in/instiapp/api'; } final Dio _dio; @@ -1334,6 +1334,95 @@ class _InstiAppApi implements InstiAppApi { return value; } + @override + Future> getBuynSellPosts(sessionId) async { + const _extra = {}; + final queryParameters = {}; + final _headers = {r'Cookie': sessionId}; + _headers.removeWhere((k, v) => v == null); + final _data = {}; + final _result = await _dio.fetch>( + _setStreamType>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose(_dio.options, '/buy/products', + queryParameters: queryParameters, data: _data) + .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); + var value = _result.data! + .map((dynamic i) => BuynSellPost.fromJson(i as Map)) + .toList(); + return value; + } + + @override + Future getBuynSellPost(sessionId, id) async { + const _extra = {}; + final queryParameters = {}; + final _headers = {r'Cookie': sessionId}; + _headers.removeWhere((k, v) => v == null); + final _data = {}; + final _result = await _dio.fetch>( + _setStreamType( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose(_dio.options, '/buy/products/${id}', + queryParameters: queryParameters, data: _data) + .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); + final value = BuynSellPost.fromJson(_result.data!); + return value; + } + + @override + Future deleteBuynSellPost(sessionId, id) async { + const _extra = {}; + final queryParameters = {}; + final _headers = {r'Cookie': sessionId}; + _headers.removeWhere((k, v) => v == null); + final _data = {}; + final _result = await _dio.fetch>( + _setStreamType( + Options(method: 'DELETE', headers: _headers, extra: _extra) + .compose(_dio.options, '/buy/products/${id}', + queryParameters: queryParameters, data: _data) + .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); + final value = BuynSellPost.fromJson(_result.data!); + return value; + } + + @override + Future updateBuynSellPost(sessionId, id, post) async { + const _extra = {}; + final queryParameters = {}; + final _headers = {r'Cookie': sessionId}; + _headers.removeWhere((k, v) => v == null); + final _data = {}; + _data.addAll(post.toJson()); + final _result = await _dio.fetch>( + _setStreamType( + Options(method: 'PUT', headers: _headers, extra: _extra) + .compose(_dio.options, '/buy/products/${id}', + queryParameters: queryParameters, data: _data) + .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); + final value = BuynSellPost.fromJson(_result.data!); + return value; + } + + @override + Future createBuynSellPost(sessionId, post) async { + const _extra = {}; + final queryParameters = {}; + final _headers = {r'Cookie': sessionId}; + _headers.removeWhere((k, v) => v == null); + final _data = {}; + _data.addAll(post.toJson()); + final _result = await _dio.fetch>( + _setStreamType( + Options(method: 'POST', headers: _headers, extra: _extra) + .compose(_dio.options, '/buy/products', + queryParameters: queryParameters, data: _data) + .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); + final value = BuynSellPost.fromJson(_result.data!); + return value; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/src/api/model/buynsellPost.dart b/lib/src/api/model/buynsellPost.dart new file mode 100644 index 00000000..6e5cc5d6 --- /dev/null +++ b/lib/src/api/model/buynsellPost.dart @@ -0,0 +1,109 @@ +import 'package:InstiApp/src/api/model/user.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'buynsellPost.g.dart'; + +@JsonSerializable() +class BuynSellPost { + @JsonKey(name: "id") + String? id; + + @JsonKey(name: "str_id") + String? buysellPostStrId; + + @JsonKey(name: "name") + String? name; + + @JsonKey(name: "description") + String? description; + + @JsonKey(name: "product_image") + List? imageUrl; + + @JsonKey(name: "brand") + String? brand; + + @JsonKey(name: "warranty") + bool? warranty; + + @JsonKey(name: "packaging") + bool? packaging; + + @JsonKey(name: "condition") + String? condition; + + @JsonKey(name: "action") + String? action; + + @JsonKey(name: "status") + bool? status; + + @JsonKey(name: "deleted") + bool? deleted; + + @JsonKey(name: "price") + int? price; + + @JsonKey(name: "negotiable") + bool? negotiable; + + @JsonKey(name: "contact_details") + String? contactDetails; + + @JsonKey(name: "time_of_creation") + String? timeOfCreation; + + @JsonKey(name: "category") + String? category; + + @JsonKey(name: "user") + User? user; + + @JsonKey(ignore: true) + int? postedMinutes; + + @JsonKey(ignore: true) + String? timeBefore; + + @override + String toString() { + return 'BuySellPost{id:$id, content:$name}'; + } + + BuynSellPost({ + this.id, + this.buysellPostStrId, + this.name, + this.description, + this.imageUrl, + this.brand, + this.warranty, + this.packaging, + this.condition, + this.action, + this.status, + this.deleted, + this.price, + this.timeOfCreation, + this.timeBefore, + }) { + if (timeOfCreation != null) { + postedMinutes = + DateTime.now().difference(DateTime.parse(timeOfCreation!)).inMinutes; + if (postedMinutes! > 1440) { + timeBefore = "${postedMinutes! ~/ 1440} Days Ago"; + } else if (postedMinutes! > 60) { + timeBefore = "${postedMinutes! ~/ 60} Hours Ago"; + } else { + timeBefore = "${postedMinutes!} Minutes Ago"; + } + } + } + + factory BuynSellPost.fromJson(Map json) => + _$BuynSellPostFromJson(json); + + Map toJson() => _$BuynSellPostToJson(this); + + getBuynSellPost(String s) {} +} diff --git a/lib/src/api/model/buynsellPost.g.dart b/lib/src/api/model/buynsellPost.g.dart new file mode 100644 index 00000000..d39af909 --- /dev/null +++ b/lib/src/api/model/buynsellPost.g.dart @@ -0,0 +1,54 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'buynsellPost.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BuynSellPost _$BuynSellPostFromJson(Map json) => BuynSellPost( + id: json['id'] as String?, + buysellPostStrId: json['str_id'] as String?, + name: json['name'] as String?, + description: json['description'] as String?, + imageUrl: (json['product_image'] as List?) + ?.map((e) => e as String) + .toList(), + brand: json['brand'] as String?, + warranty: json['warranty'] as bool?, + packaging: json['packaging'] as bool?, + condition: json['condition'] as String?, + action: json['action'] as String?, + status: json['status'] as bool?, + deleted: json['deleted'] as bool?, + price: json['price'] as int?, + timeOfCreation: json['time_of_creation'] as String?, + ) + ..negotiable = json['negotiable'] as bool? + ..contactDetails = json['contact_details'] as String? + ..category = json['category'] as String? + ..user = json['user'] == null + ? null + : User.fromJson(json['user'] as Map); + +Map _$BuynSellPostToJson(BuynSellPost instance) => + { + 'id': instance.id, + 'str_id': instance.buysellPostStrId, + 'name': instance.name, + 'description': instance.description, + 'product_image': instance.imageUrl, + 'brand': instance.brand, + 'warranty': instance.warranty, + 'packaging': instance.packaging, + 'condition': instance.condition, + 'action': instance.action, + 'status': instance.status, + 'deleted': instance.deleted, + 'price': instance.price, + 'negotiable': instance.negotiable, + 'contact_details': instance.contactDetails, + 'time_of_creation': instance.timeOfCreation, + 'category': instance.category, + 'user': instance.user, + }; diff --git a/lib/src/blocs/buynsell_post_bloc.dart b/lib/src/blocs/buynsell_post_bloc.dart new file mode 100644 index 00000000..c2a30421 --- /dev/null +++ b/lib/src/blocs/buynsell_post_bloc.dart @@ -0,0 +1,45 @@ +import 'package:InstiApp/src/api/model/buynsellPost.dart'; +import 'package:InstiApp/src/blocs/ia_bloc.dart'; +import 'package:rxdart/rxdart.dart'; + +enum BnSType { All } + +class BuynSellPostBloc { + final String storageID = "BuynSellPost"; + + InstiAppBloc bloc; + + List _buynsellPosts = []; + + ValueStream> get buynsellposts => _buynsellSubject.stream; + final _buynsellSubject = BehaviorSubject>(); + + String query = ""; + + BuynSellPostBloc(this.bloc); + + get buynsellpost => null; + + Future getBuynSellPost(String id) async { + return await bloc.client.getBuynSellPost(bloc.getSessionIdHeader(), id); + } + + Future deleteBuynSellPost(String id) async { + return await bloc.client.deleteBuynSellPost(bloc.getSessionIdHeader(), id); + } + + Future updateBuynSellPost(BuynSellPost post) async { + await bloc.client + .updateBuynSellPost(bloc.getSessionIdHeader(), post.id!, post); + } + + Future refresh({BnSType type = BnSType.All}) async { + _buynsellPosts = + (await bloc.client.getBuynSellPosts(bloc.getSessionIdHeader())); + _buynsellSubject.add(_buynsellPosts); + } + + Future createBuynSellPost(BuynSellPost post) async { + await bloc.client.createBuynSellPost(bloc.getSessionIdHeader(), post); + } +} diff --git a/lib/src/blocs/ia_bloc.dart b/lib/src/blocs/ia_bloc.dart index bbfc42f5..a181b3c2 100644 --- a/lib/src/blocs/ia_bloc.dart +++ b/lib/src/blocs/ia_bloc.dart @@ -17,6 +17,7 @@ import 'package:InstiApp/src/api/response/alumni_login_response.dart'; import 'package:InstiApp/src/api/response/getencr_response.dart'; import 'package:InstiApp/src/blocs/ach_to_vefiry_bloc.dart'; import 'package:InstiApp/src/blocs/blog_bloc.dart'; +import 'package:InstiApp/src/blocs/buynsell_post_bloc.dart'; import 'package:InstiApp/src/blocs/calendar_bloc.dart'; import 'package:InstiApp/src/blocs/community_bloc.dart'; import 'package:InstiApp/src/blocs/community_post_bloc.dart'; @@ -130,6 +131,7 @@ class InstiAppBloc { late VerifyBloc bodyAchBloc; late CommunityBloc communityBloc; late CommunityPostBloc communityPostBloc; + late BuynSellPostBloc buynSellPostBloc; // actual current state Session? currSession; var _hostels = []; @@ -274,6 +276,7 @@ class InstiAppBloc { messCalendarBloc = MessCalendarBloc(this); communityBloc = CommunityBloc(this); communityPostBloc = CommunityPostBloc(this); + buynSellPostBloc = BuynSellPostBloc(this); _initNotificationBatch(); } diff --git a/lib/src/drawer.dart b/lib/src/drawer.dart index 2f3313d7..4b11944b 100644 --- a/lib/src/drawer.dart +++ b/lib/src/drawer.dart @@ -172,20 +172,29 @@ class _NavDrawerState extends State { highlight: indexSnapshot.data == 15, selected: indexSnapshot.data == 15, ), - 9: NavListTile( - icon: Icons.verified_outlined, - title: "Achievements", + 7: NavListTile( + icon: Icons.map_outlined, + title: "Map", onTap: () { - changeSelection(9, drawerState!); - navigateNamed('/achievements'); + changeSelection(8, drawerState!); + navigateNamed('/map'); }, - highlight: indexSnapshot.data == 9, - selected: indexSnapshot.data == 9, + highlight: indexSnapshot.data == 8, + selected: indexSnapshot.data == 8, + ), + 17: NavListTile( + icon: Icons.currency_rupee_outlined, + title: "Buy and Sell", + onTap: () { + changeSelection(17, drawerState!); + navigateNamed('/buyandsell'); + }, + highlight: indexSnapshot.data == 17, + selected: indexSnapshot.data == 17, ), 8: NavExpansionTile( title: "Utilities", - initiallyExpanded: indexSnapshot.data == 8 || - indexSnapshot.data == 11 || + initiallyExpanded: indexSnapshot.data == 11 || indexSnapshot.data == 12 || indexSnapshot.data == 7, leading: Icons.construction_outlined, @@ -200,16 +209,6 @@ class _NavDrawerState extends State { highlight: indexSnapshot.data == 7, selected: indexSnapshot.data == 7, ), - NavListTile( - icon: Icons.map_outlined, - title: "Map", - onTap: () { - changeSelection(8, drawerState!); - navigateNamed('/map'); - }, - highlight: indexSnapshot.data == 8, - selected: indexSnapshot.data == 8, - ), NavListTile( icon: Icons.link_outlined, title: "Quick Links", @@ -232,6 +231,16 @@ class _NavDrawerState extends State { ), ], ), + 9: NavListTile( + icon: Icons.verified_outlined, + title: "Achievements", + onTap: () { + changeSelection(9, drawerState!); + navigateNamed('/achievements'); + }, + highlight: indexSnapshot.data == 9, + selected: indexSnapshot.data == 9, + ), // 13: NavListTile( // icon: Icons.query_stats, // title: "FAQs", @@ -667,7 +676,8 @@ class MNavigatorObserver extends NavigatorObserver { "/messcalendar": 14, "/messcalendar/qr": 14, "/groups": 15, - "/InSeek": 16, + //"/InSeek": 16, + "/buyandsell": 17, }; static Map routeToName = { @@ -693,6 +703,7 @@ class MNavigatorObserver extends NavigatorObserver { "/messcalendar": "Mess Calendar", "/messcalendar/qr": "Show Mess QR", "/groups": "Groups", + "/buyandsell": "Buy and Sell", "n/a": "", }; diff --git a/lib/src/routes/achievement_form.dart b/lib/src/routes/achievement_form.dart index 2a9c7097..baa507fd 100644 --- a/lib/src/routes/achievement_form.dart +++ b/lib/src/routes/achievement_form.dart @@ -242,33 +242,7 @@ class _CreateAchievementPage extends State { : NestedScrollView( headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverPersistentHeader( - floating: true, - pinned: true, - delegate: _SliverTabBarDelegate( - child: PreferredSize( - preferredSize: Size.fromHeight(72), - child: Material( - elevation: 4.0, - child: TabBar( - labelColor: theme.colorScheme.secondary, - unselectedLabelColor: theme.disabledColor, - tabs: [ - Tab( - text: "Associations", - icon: - Icon(Icons.work_outline_outlined)), - Tab( - text: "Events", - icon: Icon(Icons.event_outlined)), - ], - ), - ), - ), - ), - ), - ]; + return []; }, body: TabBarView( // These are the contents of the tab views, below the tabs. @@ -1304,6 +1278,7 @@ class _QRViewExampleState extends State { } } +// ignore: unused_element class _SliverTabBarDelegate extends SliverPersistentHeaderDelegate { final PreferredSize child; diff --git a/lib/src/routes/buynsell_categories.dart b/lib/src/routes/buynsell_categories.dart new file mode 100644 index 00000000..15995f70 --- /dev/null +++ b/lib/src/routes/buynsell_categories.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart'; +import 'package:InstiApp/src/drawer.dart'; +import 'package:InstiApp/src/utils/common_widgets.dart'; +import 'package:InstiApp/src/utils/title_with_backbutton.dart'; + +import '../utils/title_with_backbutton.dart'; + +double screen_h = 0, screen_w = 0; + +class ScreenArguments { + final String title; + ScreenArguments(this.title); +} + +// widget for generic category button +class CategoryButton extends StatelessWidget { + final String name; + CategoryButton(this.name); + + Widget build(BuildContext context) { + return Expanded( + child: Center( + child: TextButton( + onPressed: () { + Navigator.of(context).pushNamed( + "/buyandsell/createPost", + arguments: ScreenArguments( + name, + ), + ); + }, + child: Column( + children: [ + Image.asset('assets/buynsell/' + name + '.png'), + Text( + name, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ), + ); + } +} + +//widget for category page +class BuyAndSellCategoryPage extends StatelessWidget { + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + BuyAndSellCategoryPage({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) { + var theme = Theme.of(context); + return Scaffold( + key: _scaffoldKey, + drawer: NavDrawer(), + bottomNavigationBar: MyBottomAppBar( + child: new Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Icon( + Icons.menu_outlined, + semanticLabel: "Show navigation drawer", + ), + onPressed: () { + _scaffoldKey.currentState?.openDrawer(); + }, + ), + ], + ), + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(10.0, 45.0, 0, 10), + child: Row( + children: [ + Center( + child: TitleWithBackButton( + child: Text( + "Choose Category", + style: theme.textTheme.headline4, + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(14.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CategoryButton("Electronics"), + CategoryButton("Stationary"), + CategoryButton("Bicycle") + ], + ), + ), + Padding( + padding: const EdgeInsets.all(14.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CategoryButton("Books"), + CategoryButton("Daily Needs"), + CategoryButton("Acessories") + ], + ), + ), + Padding( + padding: const EdgeInsets.all(14.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CategoryButton("Sports"), + CategoryButton("Miscellaneous"), + Expanded(child: Container()), + ], + ), + ), + ]), + ), + ); + } +} diff --git a/lib/src/routes/buynsell_createpost.dart b/lib/src/routes/buynsell_createpost.dart new file mode 100644 index 00000000..dff24329 --- /dev/null +++ b/lib/src/routes/buynsell_createpost.dart @@ -0,0 +1,874 @@ +import 'dart:io'; + +import 'package:InstiApp/src/api/model/buynsellPost.dart'; +import 'package:InstiApp/src/api/model/user.dart'; +import 'package:InstiApp/src/api/response/image_upload_response.dart'; +import 'package:flutter/material.dart'; + +import 'package:image_picker/image_picker.dart'; +import 'package:InstiApp/src/drawer.dart'; +import 'package:InstiApp/src/utils/common_widgets.dart'; + +import '../bloc_provider.dart'; +import '../utils/title_with_backbutton.dart'; +import 'package:flutter/services.dart'; + + +class NavigateArguments { + final BuynSellPost? post; + + NavigateArguments({this.post}); +} + +class BuyAndSellForm extends StatefulWidget { + _BuyAndSellFormState createState() => _BuyAndSellFormState(); +} + +class _BuyAndSellFormState extends State { + BuynSellPost bnsPost = BuynSellPost(); + ActionChoices? _option; + + final _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + BuynSellPost currRequest = BuynSellPost(); + + File? _imageFile; + List imageFiles = []; + + List actionChoices = [ + ActionChoices(value: "sell", text: "Sell"), + ActionChoices(value: "giveaway", text: "Give Away") + ]; + + Widget _buildPreview() { + if (_imageFile == null) { + return Container(); + } + return Image.file( + _imageFile!, + height: 200, + width: 140, + ); + } + + @override + void initState() { + super.initState(); + _option = actionChoices[0]; + bnsPost.action = _option!.value; + bnsPost.warranty = false; + bnsPost.negotiable = false; + bnsPost.packaging = false; + } + + @override + Widget build(BuildContext context) { + var bloc = BlocProvider.of(context)!.bloc; + var theme = Theme.of(context); + User? profile = bloc.currSession?.profile; + + return Scaffold( + key: _scaffoldKey, + drawer: NavDrawer(), + bottomNavigationBar: MyBottomAppBar( + child: new Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Icon( + Icons.menu_outlined, + semanticLabel: "Show navigation drawer", + ), + onPressed: () { + _scaffoldKey.currentState?.openDrawer(); + }, + ), + ], + ), + ), + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TitleWithBackButton( + + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Create Post", + style: theme.textTheme.headline4, + ),SizedBox(height: 20,), + Text("Your Name and LDAP will be visible by default",style:TextStyle(color: Colors.red),maxLines: 2,) + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 4.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 3, + child: Padding( + padding: + EdgeInsets.fromLTRB(0.0, 0.0, 16.0, 0.0), + child: Text( + 'Choose the action you want to take', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + ), + const Expanded(flex: 1, child: SizedBox()), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DropdownButtonFormField( + value: _option, + onChanged: (value) { + setState(() { + _option = value!; + bnsPost.action = value.value; + }); + }, + items: actionChoices + .map>( + (ActionChoices value) { + return DropdownMenuItem( + value: value, + child: Text(value.text), + ); + }).toList(), + decoration: const InputDecoration( + labelText: "Sell/Giveaway", + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric( + vertical: 1, horizontal: 10), + ), + validator: (value) { + if (value == null) { + return 'Please select an option'; + } + return null; + }, + ), + ], + ), + ), + const Expanded(flex: 1, child: SizedBox()), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 2, + child: Text( + 'Name of the item', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + decoration: const InputDecoration( + hintText: 'Enter item name', + contentPadding: EdgeInsets.symmetric( + vertical: 1, horizontal: 10), + border: OutlineInputBorder()), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter item name'; + } + return null; + }, + onChanged: (value) { + bnsPost.name = value; + }, + ), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 8.0), + child: Row( + children: [ + const Expanded( + flex: 2, + child: Text( + 'Price of the item', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + decoration: const InputDecoration( + hintText: 'Enter Price', + contentPadding: EdgeInsets.symmetric( + vertical: 1, horizontal: 10), + border: OutlineInputBorder()), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter Price'; + } + return null; + }, + onChanged: (value) { + bnsPost.price = int.tryParse(value); + }, + keyboardType: TextInputType.number, + ), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 2, + child: Text( + 'Is Price Negotiable?', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text('Yes'), + Radio( + value: true, + groupValue: bnsPost.negotiable, + onChanged: (value) { + setState(() { + bnsPost.negotiable = value; + }); + }, + ), + const Text('No'), + Radio( + value: false, + groupValue: bnsPost.negotiable, + onChanged: (value) { + setState(() { + bnsPost.negotiable = value; + }); + }, + ), + // const Text('No'), + ], + ) + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 2, + child: Text( + 'Description', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + keyboardType: TextInputType.multiline, + maxLines: null, + decoration: InputDecoration( + contentPadding: EdgeInsets.symmetric( + vertical: 10, horizontal: 10), + border: OutlineInputBorder(), + hintText: "Enter Description", + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter item description'; + } + return null; + }, + onChanged: (value) { + bnsPost.description = value; + }, + ), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 2, + child: Text( + 'Brand Name', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + decoration: const InputDecoration( + hintText: 'Enter brand name', + contentPadding: EdgeInsets.symmetric( + vertical: 1, horizontal: 10), + border: OutlineInputBorder()), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter Brand name'; + } + return null; + }, + onChanged: (value) { + bnsPost.brand = value; + }, + ), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 2, + child: Text( + 'Within the Warranty?', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text('Yes'), + Radio( + value: true, + groupValue: bnsPost.warranty, + onChanged: (value) { + setState(() { + bnsPost.warranty = value; + }); + }, + ), + const Text('No'), + Radio( + value: false, + groupValue: bnsPost.warranty, + onChanged: (value) { + setState(() { + bnsPost.warranty = value; + }); + }, + ), + // const Text('No'), + ], + ) + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 2, + child: Text( + 'Original Packaging?', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text('Yes'), + Radio( + value: true, + groupValue: bnsPost.packaging, + onChanged: (value) { + setState(() { + bnsPost.packaging = value; + }); + }, + ), + const Text('No'), + Radio( + value: false, + groupValue: bnsPost.packaging, + onChanged: (value) { + setState(() { + bnsPost.packaging = value; + }); + }, + ), + // const Text('No'), + ], + ) + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 4.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 4, + child: Text( + 'Condition of the item', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 8, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + decoration: const InputDecoration( + hintText: 'Rating/10.', + contentPadding: EdgeInsets.symmetric( + vertical: 1, horizontal: 10), + border: OutlineInputBorder()), + validator: (value) { + if (value == null || + value.isEmpty || + (int.tryParse(value) ?? -1) > 10 || + (int.tryParse(value) ?? -1) < 0) { + return 'Please enter rating from 0-10'; + } + return null; + }, + onChanged: (value) { + bnsPost.condition = value; + }, + keyboardType: TextInputType.number, + ), + ], + ), + ), + // const Expanded( + // flex: 4, + // child: SizedBox(), + // ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 2, + child: Text( + 'Phone Number', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), + LengthLimitingTextInputFormatter(10), + ], + decoration: const InputDecoration( + hintText: 'Enter 10 digits contact no.', + contentPadding: EdgeInsets.symmetric(vertical: 20, horizontal: 10), + border: OutlineInputBorder(), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter contact no.'; + } + return null; + }, + onChanged: (value) { + bnsPost.contactDetails = value; + }, + ), + + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + // child: Row( + // children: [ + // const Expanded( + // flex: 2, + // child: Text( + // 'Status', + // style: TextStyle( + // fontWeight: FontWeight.bold, + // fontSize: 16.0, + // ), + // ), + // ), + // Expanded( + // flex: 4, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // children: [ + // const Text('Open'), + // Radio( + // value: true, + // groupValue: _itemStatus, + // onChanged: (value) { + // setState(() { + // bnsPost.status = value; + // }); + // }, + // ), + // const Text('Closed'), + // Radio( + // value: false, + // groupValue: _itemStatus, + // onChanged: (value) { + // setState(() { + // bnsPost.status = value; + // }); + // }, + // ), + // // const Text('No'), + // ], + // ) + // ], + // ), + // ), + // ], + // ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), + child: Row( + children: [ + const Expanded( + flex: 3, + child: Text( + 'Attach Image', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + const Expanded(flex: 1, child: SizedBox()), + Expanded( + flex: 4, + child: Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + ElevatedButton( + onPressed: () => _pickImage( + ImageSource.camera), + child: const Icon(Icons.camera)), + const SizedBox(width: 16), + ElevatedButton( + onPressed: () => _pickImage( + ImageSource.gallery), + child: const Icon( + Icons.attach_file)), + const SizedBox(height: 16), + ], + ), + _buildPreview(), + ], + ), + ], + ), + ), + const Expanded(flex: 1, child: SizedBox()) + ], + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + ...(bnsPost.imageUrl ?? []) + .asMap() + .entries + .map((e) => _buildImageUrl( + e.value, + e.key, + )), + ...imageFiles + .asMap() + .entries + .map((e) => _buildImageFile( + e.value, + e.key, + )), + ], + )), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + child: const DefaultTextStyle( + style: TextStyle(color: Colors.blue), + child: Text('Cancel'), + ), + style: ElevatedButton.styleFrom( + primary: Colors.white, + ), + onPressed: () { + Navigator.pushNamed(context, '/buyandsell'); + }, + ), + const SizedBox(width: 20), + ElevatedButton( + onPressed: isButtonDisabled + ? null + : () async { + setState(() { + isButtonDisabled = true; + }); + if (_formKey.currentState!.validate()) { + bnsPost.user = profile; + if (bnsPost.imageUrl == null) + bnsPost.imageUrl = []; + for (int i = 0; + i < imageFiles.length; + i++) { + ImageUploadResponse resp = + await bloc.client.uploadImage( + bloc.getSessionIdHeader(), + imageFiles[i]); + bnsPost.imageUrl! + .add(resp.pictureURL!); + } + // ignore: avoid_print + print(bnsPost.imageUrl); + + bloc.buynSellPostBloc + .createBuynSellPost(bnsPost); + Navigator.pushNamed( + context, '/buyandsell'); + } + setState(() { + isButtonDisabled = false; + }); + }, + child: const Text('Submit'), + ), + const SizedBox(width: 20), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); + } + + void _pickImage(ImageSource source) async { + final ImagePicker _picker = ImagePicker(); + final XFile? pi = await _picker.pickImage(source: source); + + if (pi != null) { + // ImageUploadResponse resp = await bloc.client + // .uploadImage( + // bloc.getSessionIdHeader(), File(pi.path)); + // print(resp.pictureURL); + if (await pi.length() / 1000000 <= 10) { + setState(() { + imageFiles.add(File(pi.path)); + }); + } else { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text("Image size should be less than 10MB"), + )); + } + } + } + + Widget _buildImageUrl(String url, int index) { + return Stack( + children: [ + Image.network( + url, + height: MediaQuery.of(context).size.height / 7.5, + width: MediaQuery.of(context).size.height / 7.5, + fit: BoxFit.scaleDown, + ), + Positioned( + right: 0, + top: 0, + child: Container( + child: IconButton( + icon: Icon(Icons.close), + onPressed: () { + setState(() { + bnsPost.imageUrl!.removeAt(index); + }); + }, + ), + ), + ), + ], + ); + } + + Widget _buildImageFile(File file, int index) { + return Stack( + children: [ + Image.file( + file, + height: MediaQuery.of(context).size.height / 7.5, + width: MediaQuery.of(context).size.height / 7.5, + fit: BoxFit.scaleDown, + ), + Positioned( + right: 0, + top: 0, + child: Container( + child: IconButton( + icon: Icon(Icons.close), + onPressed: () { + setState(() { + imageFiles.removeAt(index); + }); + }, + ), + ), + ), + ], + ); + } + + bool isButtonDisabled = false; + + void handleTap() { + if (!isButtonDisabled) { + setState(() { + isButtonDisabled = true; + }); + Future.delayed(Duration(seconds: 10), () { + setState(() { + isButtonDisabled = false; + }); + }); + } + } +} + +class ActionChoices { + String value; + String text; + + ActionChoices({required this.value, required this.text}); +} diff --git a/lib/src/routes/buynsell_info.dart b/lib/src/routes/buynsell_info.dart new file mode 100644 index 00000000..145649e5 --- /dev/null +++ b/lib/src/routes/buynsell_info.dart @@ -0,0 +1,409 @@ +import 'package:InstiApp/src/utils/common_widgets.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +import '../api/model/buynsellPost.dart'; + +class BuyAndSellInfoPage extends StatefulWidget { + final Future post; + + BuyAndSellInfoPage({required this.post}); + + static void navigateWith( + BuildContext context, BuynSellPost bloc, BuynSellPost post) { + Navigator.push( + context, + MaterialPageRoute( + settings: RouteSettings( + name: "/${post.id ?? ""}", + ), + builder: (context) => BuyAndSellInfoPage( + post: bloc.getBuynSellPost(post.id ?? ""), + ), + ), + ); + } + + @override + State createState() => _BuyAndSellInfoPageState(); +} + +class _BuyAndSellInfoPageState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + + BuynSellPost? bnsPost; + + @override + void initState() { + super.initState(); + widget.post.then((bnsPost) { + if (this.mounted) { + setState(() { + this.bnsPost = bnsPost; + }); + } + }); + } + + @override + Widget build(BuildContext context) { + List? imageList = bnsPost?.imageUrl; + double screen_wr = MediaQuery.of(context).size.width; + double screen_hr = MediaQuery.of(context).size.height; + double x, y; + + var theme = Theme.of(context); + + screen_hr >= screen_wr ? x = 0.35 : x = 1; + screen_hr >= screen_wr ? y = 0.9 : y = 0.8; + var screen_w = screen_wr * y; + var screen_h = screen_hr * x; + + return Scaffold( + bottomNavigationBar: MyBottomAppBar( + shape: RoundedNotchedRectangle(), + child: new Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Icon( + Icons.menu_outlined, + color: Colors.blue.withOpacity(0), + semanticLabel: "Show navigation drawer", + ), + onPressed: () { + _scaffoldKey.currentState?.openDrawer(); + }, + ), + ], + ), + ), + body: SingleChildScrollView( + child: Column(children: [ + Container( + padding: EdgeInsets.only(top: 32, left: 16), + alignment: Alignment.topLeft, + child: Container( + padding: EdgeInsets.only(top: 32, left: 16), + alignment: Alignment.topLeft, + child: DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.blueAccent, + width: 2.0, + ), + ), + child: IconButton( + icon: Icon(Icons.arrow_back_ios_outlined, + color: Colors.blueAccent), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.fromLTRB(screen_w * 0.1, 15, 0, 0), + child: SizedBox( + height: screen_h / 1.2, + width: screen_w / 1, + child: ImageCarousel(imageList), + ), + ), + Spacer(), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.fromLTRB(screen_w * 0.1, 11, 0, 0), + child: Container( + width: screen_w, + child: Text(bnsPost?.brand ?? "", + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.headline6?.copyWith( + fontWeight: FontWeight.w100, fontSize: 20)), + ) + // style: TextStyle( + // fontSize: myfont / 1.3, fontWeight: FontWeight.w100), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.fromLTRB(screen_w * 0.1, 3, 0, 0), + child: Text( + '${bnsPost?.user?.userName ?? ""} (${bnsPost?.user?.userLDAPId ?? ""})', + ), + ), + ], + ), + + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: screen_w * 0.9, + margin: EdgeInsets.fromLTRB(screen_w * 0.1, 3, 0, 0), + child: Text(bnsPost?.name ?? "", + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.headline5?.copyWith( + fontWeight: FontWeight.bold, + // fontSize: 30, + fontSize: 30, + ) + // style: TextStyle( + // fontSize: myfont * 1.5, fontWeight: FontWeight.w700), + )), + ], + ), + Row(mainAxisAlignment: MainAxisAlignment.start, children: [ + Container( + margin: EdgeInsets.fromLTRB(screen_w * 0.1, 5, 0, 0), + child: Text("Condition - " + (bnsPost?.condition ?? '0') + '/10', + style: theme.textTheme.headline6 + ?.copyWith(fontSize: 15, fontWeight: FontWeight.w500) + // style: TextStyle(fontSize: myfont, fontWeight: FontWeight.w100), + ), + ) + ]), + Column(children: [ + Container( + width: screen_w * 0.9, + height: screen_h * 0.5, + margin: EdgeInsets.fromLTRB(0, 8, 0, 0), + child: Text(bnsPost?.description ?? "", + maxLines: 10, + overflow: TextOverflow.ellipsis, + softWrap: false, + style: theme.textTheme.bodySmall?.copyWith(fontSize: 13) + // style: TextStyle( + // fontSize: myfont * 0.75, fontWeight: FontWeight.w100), + ), + ), + SizedBox( + height: screen_h * 0.07, + ), + SizedBox( + width: screen_w, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + 'Phone number - ' + + (bnsPost?.contactDetails ?? ""), + style: theme.textTheme.headline4?.copyWith( + fontSize: 20, fontWeight: FontWeight.w400), + ), + ], + ), + Container( + child: Row( + children: [ + Text( + "Negotiable - " + + ((bnsPost?.negotiable ?? false) + ? "Yes" + : "No"), + style: theme.textTheme.headline4?.copyWith( + fontSize: 20, fontWeight: FontWeight.w400), + ), + ], + ), + ), + Container( + child: Text( + (bnsPost?.action == 'giveaway' + ? "GiveAway" + : "Price - ₹" + + (bnsPost?.price ?? 0).toString()), + style: theme.textTheme.headline4?.copyWith( + fontSize: 20, fontWeight: FontWeight.w400), + textAlign: TextAlign.left, + ), + ) + ], + ), + ), + Spacer(), + ], + ), + ), + ]), + + // Add more widgets below the image card + ]), + ), + ); + } +} + +class ImageCarousel extends StatefulWidget { + final List? imageList; + + ImageCarousel(this.imageList); + + @override + _ImageCarouselState createState() => _ImageCarouselState(); +} + +class _ImageCarouselState extends State { + int _currentIndex = 0; + + @override + Widget build(BuildContext context) { + double screen_wr = MediaQuery.of(context).size.width; + double screen_hr = MediaQuery.of(context).size.height; + double x, y; + + screen_hr >= screen_wr ? x = 0.35 : x = 1; + if (0.5 <= screen_hr / screen_wr && screen_hr / screen_wr <= 1) { + x = 0.8; + } + screen_hr >= screen_wr ? y = 0.9 : y = 0.8; + var screen_w = screen_wr * y; + var screen_h = screen_hr * x; + + if (widget.imageList == null || widget.imageList!.isEmpty) { + return Container( + child: Center(child: Image.asset('assets/buynsell/DevcomLogo.png')), + ); + } + + return Row( + children: [ + Expanded( + flex: 3, + child: Row( + children: [ + Container( + child: SizedBox( + height: screen_h * 0.7, + width: screen_w * 0.6, + child: PageView.builder( + itemCount: widget.imageList?.length, + onPageChanged: (index) { + setState(() { + _currentIndex = index; + }); + }, + itemBuilder: (context, index) { + return GestureDetector( + onTap: () { + setState(() { + _currentIndex = index; + }); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(15.0), + child: CachedNetworkImage( + imageUrl: widget.imageList?[index] ?? "", + fit: BoxFit.fitHeight, + ), + ), + ); + }, + ), + ), + ), + SizedBox(height: 10), + SizedBox(height: 10), + ], + ), + ), + Expanded( + child: ListView.builder( + itemCount: widget.imageList?.length, + itemBuilder: (context, index) { + return GestureDetector( + onTap: () { + setState(() { + _currentIndex = index; + }); + }, + child: Container( + margin: EdgeInsets.fromLTRB(0, 10, 10, screen_h * 0.005), + child: Container( + margin: EdgeInsets.symmetric(vertical: 5), + decoration: BoxDecoration( + border: Border.all( + color: _currentIndex == index + ? Colors.blue + : Colors.transparent, + width: 1.75, + style: BorderStyle.solid, + ), + borderRadius: BorderRadius.circular(10), + ), + child: SizedBox( + height: screen_h * 0.25, + width: screen_w * 0.1, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: CachedNetworkImage( + imageUrl: widget.imageList?[index] ?? "", + width: 80, + height: 80, + fit: BoxFit.cover, + ), + ), + ), + ), + ), + ); + }, + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildDotIndicator(), + ), + ], + ); + } + + List _buildDotIndicator() { + List dots = []; + for (int i = 0; i < (widget.imageList?.length ?? 0); i++) { + dots.add( + Padding( + padding: const EdgeInsets.all(5.0), + child: GestureDetector( + onTap: () { + setState(() { + _currentIndex = i; + }); + }, + child: Container( + width: 6, + height: 6, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: _currentIndex == i ? Colors.blueGrey : Colors.grey, + ), + ), + ), + ), + ); + } + return dots; + } +} diff --git a/lib/src/routes/buynsell_page.dart b/lib/src/routes/buynsell_page.dart new file mode 100644 index 00000000..687b28a3 --- /dev/null +++ b/lib/src/routes/buynsell_page.dart @@ -0,0 +1,459 @@ +import 'package:InstiApp/src/api/model/buynsellPost.dart'; +import 'package:InstiApp/src/bloc_provider.dart'; +import 'package:InstiApp/src/blocs/buynsell_post_bloc.dart'; +import 'package:InstiApp/src/drawer.dart'; +import 'package:InstiApp/src/utils/common_widgets.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +import '../api/model/user.dart'; +import '../utils/title_with_backbutton.dart'; + +class BuySellPage extends StatefulWidget { + BuySellPage({Key? key}) : super(key: key); + //final GlobalKey _scaffoldKey = GlobalKey(); + + @override + State createState() => _BuySellPageState(); +} + +class _BuySellPageState extends State { + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + home: Sellpage(), + ); + } +} + +class Sellpage extends StatefulWidget { + const Sellpage({Key? key}) : super(key: key); + + @override + State createState() => _SellpageState(); +} + +class _SellpageState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + + BnSType bnstype = BnSType.All; + bool firstBuild = true; + bool MyPosts = false; + + late double x; + + @override + void initState() { + super.initState(); + } + + Widget build(BuildContext context) { + BuynSellPostBloc buynSellPostBloc = + BlocProvider.of(context)!.bloc.buynSellPostBloc; + + User? profile = BlocProvider.of(context)!.bloc.currSession?.profile; + if (firstBuild) { + buynSellPostBloc.refresh(); + } + + double screen_wr = MediaQuery.of(context).size.width; + double screen_hr = MediaQuery.of(context).size.height; + double y; + var bloc = BlocProvider.of(context)!.bloc; + var theme = Theme.of(context); + bool isLoggedIn = bloc.currSession != null; + + screen_hr >= screen_wr ? x = 0.35 : x = 0.80; + if (1 >= screen_hr / screen_wr && screen_hr / screen_wr >= 0.5) { + x = 0.35; + } + screen_hr >= screen_wr ? y = 0.9 : y = 0.5; + double screen_w = screen_wr * y; + double screen_h = 270; + + double myfont = ((18 / 274.4) * screen_h); + return Scaffold( + key: _scaffoldKey, + drawer: NavDrawer(), + bottomNavigationBar: MyBottomAppBar( + shape: RoundedNotchedRectangle(), + notchMargin: 4.0, + child: new Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Icon( + Icons.menu_outlined, + semanticLabel: "Show navigation drawer", + ), + onPressed: () { + _scaffoldKey.currentState?.openDrawer(); + }, + ), + ], + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, + floatingActionButton: isLoggedIn + ? FloatingActionButton.extended( + icon: Icon(Icons.add_outlined), + label: Text("Add Item"), + onPressed: () { + Navigator.of(context).pushNamed("/buyandsell/category"); + }, + ) + : SizedBox( + height: 0, + width: 0, + ), + body: SafeArea( + child: !isLoggedIn + ? Container( + alignment: Alignment.center, + padding: EdgeInsets.all(50), + child: Column( + children: [ + Icon( + Icons.cloud, + size: 200, + color: Colors.grey[600], + ), + Text( + "Login To View Buy and Sell Posts", + style: theme.textTheme.headline5, + textAlign: TextAlign.center, + ) + ], + crossAxisAlignment: CrossAxisAlignment.center, + ), + ) + : SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TitleWithBackButton( + child: Text( + "Buy & Sell (Beta)", + style: theme.textTheme.headline4, + ), + ), + Center( + child: Column( + children: [ + Row(children: [ + Expanded( + child: Container( + padding: EdgeInsets.fromLTRB(10, 10, 5, 10), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(15)), + primary: theme.cardColor, + side: BorderSide( + width: 2, + color: MyPosts + ? theme.cardColor + : Colors.blue, + )), + // color: !MyPosts + // ? theme.bottomAppBarColor + // : theme.cardColor, + onPressed: () => { + setState(() { + MyPosts = false; + }), + buynSellPostBloc.refresh() + }, + child: Text( + "All Posts", + style: theme.textTheme.headline6, + )), + )), + Expanded( + child: Container( + padding: EdgeInsets.fromLTRB(5, 10, 10, 10), + child: ElevatedButton( + child: Text("Your Posts", + style: theme.textTheme.headline6), + onPressed: () => { + setState(() { + MyPosts = true; + }), + buynSellPostBloc.refresh() + }, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15)), + primary: theme.cardColor, + side: BorderSide( + width: 2, + color: !MyPosts + ? theme.cardColor + : Colors.blue, + )), + // color: MyPosts + // ? theme.bottomAppBarColor + // : theme.cardColor, + ), + )), + ]), + StreamBuilder>( + stream: buynSellPostBloc.buynsellposts, + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + return ListView.builder( + primary: false, + shrinkWrap: true, + itemCount: MyPosts + ? (snapshot.hasData + ? snapshot.data! + .where((post) => + post.user?.userID == + profile?.userID && + post.deleted != true) + .length + : 0) + : (snapshot.hasData + ? snapshot.data! + .where( + (post) => post.deleted != true) + .length + : 0), + itemBuilder: (_, index) { + if (!snapshot.hasData) { + return Center( + child: + CircularProgressIndicatorExtended( + label: Text("Loading..."), + )); + } + return _buildContent(screen_h, screen_w, + index, myfont, context, snapshot); + }, + ); + }), + ], + )), + ], + )), + )); + } + + Widget _buildContent(double screen_h, double screen_w, int index, + double myfont, BuildContext context, AsyncSnapshot snapshot) { + List posts = snapshot.data!; + var theme = Theme.of(context); + var bloc = BlocProvider.of(context)!.bloc; + User? profile = bloc.currSession?.profile; + if (MyPosts) { + posts = posts + .where((post) => + post.user?.userID == profile?.userID && post.deleted != true) + .toList(); + } else { + posts = snapshot.data; + posts = posts.where((post) => post.deleted != true).toList(); + } + + return Center( + child: (SizedBox( + height: screen_h * 0.7, + width: screen_w * 1.2, + child: Card( + color: theme.cardColor, + margin: EdgeInsets.symmetric(horizontal: 25, vertical: 10), + child: InkWell( + onTap: () { + Navigator.of(context) + .pushNamed("/buyandsell/info" + (posts[index].id ?? "")); + }, + child: SizedBox( + child: Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topRight: Radius.circular(10), + topLeft: Radius.circular(10)), + child: Row( + children: [ + Center( + child: Container( + padding: EdgeInsets.fromLTRB(0, 0, 5, 0), + height: screen_h, + width: screen_h * 0.20 / 0.43, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(10), + bottomLeft: Radius.circular(10)), + ), + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + topLeft: Radius.circular(10)), + child: CachedNetworkImage( + imageUrl: posts[index].imageUrl?[0] ?? '', + placeholder: (context, url) => Image.asset( + 'assets/buynsell/DevcomLogo.png', + fit: BoxFit.fill, + ), + errorWidget: (context, url, error) => + new Image.asset( + 'assets/buynsell/DevcomLogo.png', + fit: BoxFit.fill, + ), + fit: BoxFit.cover, + ), + ), + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.fromLTRB(0, 0, 10, 0), + margin: + EdgeInsets.fromLTRB(screen_h * 0.20 / 0.43, 11, 0, 50), + child: Text( + posts[index].name ?? "", + style: theme.textTheme.headline6, + //style: TextStyle( + // fontSize: (myfont.toInt()).toDouble(), + // fontWeight: FontWeight.w600), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + Container( + margin: + EdgeInsets.fromLTRB(screen_h * 0.20 / 0.43, 105, 10, 0), + child: Text( + (posts[index].brand ?? "").length <= 10 + ? posts[index].brand ?? "" + : (posts[index].brand ?? "").substring(0, 10) + '...', + style: theme.textTheme.bodyText2, + maxLines: 1, + ), + ), + Container( + margin: EdgeInsets.fromLTRB( + screen_w * 0.7, 110, screen_h * 0.04 / 1.5, 1), + child: MyPosts + ? ClipRRect( + borderRadius: BorderRadius.circular(5), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.red, + ), + onPressed: () { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text("Delete Item"), + content: const Text( + "Are you sure you want to delete this item?"), + actions: [ + TextButton( + onPressed: () { + posts[index].deleted = true; + bloc.buynSellPostBloc + .updateBuynSellPost(posts[index]); + Navigator.of(ctx).pop(); + bloc.buynSellPostBloc.refresh(); + }, + child: Text("Delete", + maxLines: 1, + style: TextStyle( + fontSize: + 12.5 / 338 * screen_w)), + ), + ], + ), + ); + }, + child: Text("Delete", + maxLines: 1, + style: TextStyle( + fontSize: 12.5 / 338 * screen_w)), + )) + : Row( + children: [ + Spacer(), + Text( + (posts[index].action == 'giveaway' + ? "GiveAway" + : "₹" + + (posts[index].price ?? 0).toString()), + style: theme.textTheme.bodyText1, + + // style: + // TextStyle(fontSize: w, fontWeight: FontWeight.w800), + ) + ], + )), + Container( + child: MyPosts + ? Container() + : Row( + children: [ + Icon( + Icons.access_time, + size: ((myfont / 18 * 12).toInt()).toDouble(), + ), + Text(' ' + (posts[index].timeBefore ?? ""), + style: theme.textTheme.bodyText1!.copyWith( + fontWeight: FontWeight.bold, + ) + //theme.textTheme.labelSmall + // style: TextStyle( + // fontWeight: FontWeight.w600, + // fontSize: ((myfont / 19 * 12).toInt()).toDouble()), + ), + ], + ), + margin: + EdgeInsets.fromLTRB(screen_h * 0.20 / 0.43, 135, 0, 0), + ), + Container( + child: Text( + "Condition: " + (posts[index].condition ?? "") + "/10", + style: //TextStyle( + theme.textTheme.bodyText2, + // fontSize: ((myfont / 18 * 12).toInt()).toDouble()), + ), + margin: + EdgeInsets.fromLTRB(screen_h * 0.20 / 0.43, 35, 0, 0)), + Container( + padding: EdgeInsets.fromLTRB(0, 13, 10, 0), + child: Text( + (posts[index].description ?? ""), maxLines: 2, + + overflow: TextOverflow.ellipsis, + style: theme.textTheme.bodyText2, + // style: TextStyle( + // fontWeight: FontWeight.w500, + // fontSize: ((myfont / 16 * 12).toInt()).toDouble()), + ), + margin: + EdgeInsets.fromLTRB(screen_h * 0.20 / 0.43, 43, 0, 0)), + Row( + children: [ + Spacer(), + Container(), + ], + ) + ], + )), + ), + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: BorderSide(color: Colors.blue), + ), + ), + )), + ); + } +} diff --git a/lib/src/routes/communitydetails.dart b/lib/src/routes/communitydetails.dart index e2273858..a517f5e4 100644 --- a/lib/src/routes/communitydetails.dart +++ b/lib/src/routes/communitydetails.dart @@ -296,27 +296,27 @@ class CommunityAboutSectionState extends State { mainAxisSize: MainAxisSize.min, children: [ Container( - child: TabBar( - tabs: [ - Tab( - child: Text( - "About", - style: theme.textTheme.bodyText1, - ), - ), - Tab( - child: Text( - "Members", - style: theme.textTheme.bodyText1, + child: TabBar( + tabs: [ + Tab( + child: Text( + "About", + style: theme.textTheme.bodyText1, + ), ), - ) - ], - onTap: (index) { - setState(() { - _selectedIndex = index; - }); - }, - ), + Tab( + child: Text( + "Members", + style: theme.textTheme.bodyText1, + ), + ) + ], + onTap: (index) { + setState(() { + _selectedIndex = index; + }); + }, + ), ), IndexedStack( children: [ @@ -474,8 +474,8 @@ class CommunityPostSection extends StatefulWidget { class _CommunityPostSectionState extends State { bool firstBuild = true; - // final Community? community; - + // final Community? community; + //String id=community.id; CPType cpType = CPType.All; _CommunityPostSectionState(); diff --git a/lib/src/routes/communitypostpage.dart b/lib/src/routes/communitypostpage.dart index d36feab0..38429493 100644 --- a/lib/src/routes/communitypostpage.dart +++ b/lib/src/routes/communitypostpage.dart @@ -24,6 +24,7 @@ class CommunityPostPage extends StatefulWidget { CommunityPost communityPost) { Navigator.push( context, + MaterialPageRoute( settings: RouteSettings( name: "/post/${communityPost.id ?? ""}", diff --git a/lib/src/routes/loginpage.dart b/lib/src/routes/loginpage.dart index c654d638..097f6404 100644 --- a/lib/src/routes/loginpage.dart +++ b/lib/src/routes/loginpage.dart @@ -151,39 +151,40 @@ class _LoginPageState extends State { : webview.WebView( javascriptMode: webview.JavascriptMode.unrestricted, initialUrl: loginurl, - onPageStarted: (url) async { - if (url.startsWith(successUrl)) { - var uri = Uri.parse(url); - var code = uri.queryParameters['code']; - - setState(() { - loading = true; - }); - await login( - code ?? "", "https://www.insti.app/login-android.html"); - setState(() { - loading = false; - }); - } else if (url.startsWith(guestUrl)) { - setState(() { - loading = true; - }); - Navigator.of(context) - .pushNamedAndRemoveUntil(_bloc!.homepageName, (r) => false); - } else if (url.startsWith(alumniUrl)) { - // print(alumniUrl); - setState(() { - loading = true; - }); - Navigator.of(context).pushNamedAndRemoveUntil( - _bloc!.alumniLoginPage, (r) => false); - } - }, - onPageFinished: (url) {}, + onPageStarted: checkPageUrl, + onPageFinished: checkPageUrl, gestureNavigationEnabled: true, ); } + Future checkPageUrl(String url) async { + if (url.startsWith(successUrl)) { + var uri = Uri.parse(url); + var code = uri.queryParameters['code']; + + setState(() { + loading = true; + }); + await login(code ?? "", "https://www.insti.app/login-android.html"); + setState(() { + loading = false; + }); + } else if (url.startsWith(guestUrl)) { + setState(() { + loading = true; + }); + Navigator.of(context) + .pushNamedAndRemoveUntil(_bloc!.homepageName, (r) => false); + } else if (url.startsWith(alumniUrl)) { + // print(alumniUrl); + setState(() { + loading = true; + }); + Navigator.of(context) + .pushNamedAndRemoveUntil(_bloc!.alumniLoginPage, (r) => false); + } + } + Future startLoginPageServer() async { server = jag.Jaguar(port: 9399, multiThread: true); server?.addRoute(serveFlutterAssets(prefix: "login/")); diff --git a/lib/src/routes/mappage.dart b/lib/src/routes/mappage.dart index 45244658..8267a3bb 100644 --- a/lib/src/routes/mappage.dart +++ b/lib/src/routes/mappage.dart @@ -7,6 +7,8 @@ import 'package:jaguar/jaguar.dart' as jag; import 'package:flutter_webview_pro/webview_flutter.dart' as webview; class MapPage extends StatefulWidget { + final String? location; + MapPage({this.location}); @override _MapPageState createState() => _MapPageState(); } @@ -15,7 +17,8 @@ class _MapPageState extends State { late jag.Jaguar server; final String hostUrl = "www.insti.app"; - final String mapUrl = "https://www.insti.app/map/?sandbox=true"; + // final String mapUrl = "https://www.insti.app/map/?sandbox=true"; + String mapUrl = "https://www.insti.app/map/?sandbox=true"; StreamSubscription? onUrlChangedSub; webview.WebViewController? webViewController; @@ -27,6 +30,8 @@ class _MapPageState extends State { @override void initState() { + mapUrl = + ("https://www.insti.app/map/${Uri.encodeComponent(widget.location ?? "")}?sandbox=true"); super.initState(); } @@ -77,12 +82,7 @@ class _MapPageState extends State { onWebViewCreated: (webview.WebViewController webViewController) { this.webViewController = webViewController; }, - navigationDelegate: (webview.NavigationRequest request) { - if (request.url.startsWith(mapUrl)) { - return webview.NavigationDecision.prevent; - } - return webview.NavigationDecision.navigate; - }, + zoomEnabled: false, geolocationEnabled: true, ), ); diff --git a/lib/src/routes/qr_encryption.dart b/lib/src/routes/qr_encryption.dart new file mode 100644 index 00000000..9884ec12 --- /dev/null +++ b/lib/src/routes/qr_encryption.dart @@ -0,0 +1,26 @@ +import 'dart:convert'; + +import 'package:encrypt/encrypt.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +import '../api/model/user.dart'; + +class QREncryption { + String? number; + QREncryption(User user) { + number = user.userRollNumber; + } + String Encrypt() { + final time = DateTime.now().toString(); + final message = number! + ',' + time; + + String? encryption_key = dotenv.env['KEY']; + final key = Key.fromBase64(encryption_key!); + final b64key = Key.fromBase64(base64Url.encode(key.bytes)); + final fernet = Fernet(b64key); + final encrypter = Encrypter(fernet); + final encrypted = encrypter.encrypt(message); + + return encrypted.base64; + } +} diff --git a/lib/src/routes/qrpage.dart b/lib/src/routes/qrpage.dart index 63c0566a..939a7b60 100644 --- a/lib/src/routes/qrpage.dart +++ b/lib/src/routes/qrpage.dart @@ -7,10 +7,11 @@ import 'package:InstiApp/src/drawer.dart'; import 'package:InstiApp/src/utils/common_widgets.dart'; import 'package:InstiApp/src/utils/title_with_backbutton.dart'; import 'package:flutter/material.dart'; -// import 'package:flutter_html/shims/dart_ui_real.dart'; -// import 'package:intl/intl.dart'; import 'package:qr_flutter/qr_flutter.dart'; +import '../api/model/user.dart'; +import 'qr_encryption.dart'; + class QRPage extends StatefulWidget { const QRPage({Key? key}) : super(key: key); @@ -20,11 +21,11 @@ class QRPage extends StatefulWidget { class _QRPageState extends State { GlobalKey _scaffoldKey = GlobalKey(); - - String? qrString; + String qrString = ""; bool first = true; bool loading = true; bool error = false; + User? profile; @override void initState() { @@ -32,7 +33,8 @@ class _QRPageState extends State { } void getQRString(bloc) async { - String qr = await bloc.getQRString(); + final qr_encryption = QREncryption(profile!); + String qr = (qr_encryption.Encrypt()).toString(); if (qr == "Error") { setState(() { error = true; @@ -40,7 +42,7 @@ class _QRPageState extends State { }); } else { setState(() { - qrString = qr; + qrString = (qr_encryption.Encrypt()).toString(); loading = false; }); } @@ -50,7 +52,7 @@ class _QRPageState extends State { Widget build(BuildContext context) { var theme = Theme.of(context); var bloc = BlocProvider.of(context)!.bloc; - + profile = bloc.currSession?.profile; if (first && bloc.currSession != null) { getQRString(bloc); first = false; @@ -143,6 +145,8 @@ class _QRPageState extends State { data: '${qrString}', size: MediaQuery.of(context).size.width / 2, foregroundColor: Colors.black, + embeddedImage: AssetImage( + 'assets/buynsell/DevcomLogo.png'), ), ), ], diff --git a/lib/src/routes/settingspage.dart b/lib/src/routes/settingspage.dart index d197139f..d9c29e6e 100644 --- a/lib/src/routes/settingspage.dart +++ b/lib/src/routes/settingspage.dart @@ -172,8 +172,8 @@ class _SettingsPageState extends State { // "/complaints": "Complaints/Suggestions", "/map": "Map", // "/settings": "Settings", - //TODO: Change to communities "/groups": "Insight Discussion Forum", + //"/groups": "Communities", }.entries.map((entry) { return DropdownMenuItem( value: entry.key, diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 6d5c33d8..c4dd34a7 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -12,7 +12,7 @@ import flutter_local_notifications import flutter_native_timezone import location import package_info_plus_macos -import path_provider_macos +import path_provider_foundation import shared_preferences_foundation import sqflite import url_launcher_macos diff --git a/pubspec.yaml b/pubspec.yaml index ac61e339..2c9dd81b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ description: Flutter App for Indian Institute of Technology, Bombay # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # Read more about versioning at semver.org. -version: 2.2.1+25 +version: 2.4.1+29 environment: sdk: '>=2.15.0 <3.0.0' @@ -15,7 +15,7 @@ environment: dependencies: flutter: sdk: flutter - + camera: ^0.9.4+4 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. # cupertino_icons: ^1.0.1 @@ -31,8 +31,8 @@ dependencies: share: ^2.0.4 photo_view: ^0.13.0 flutter_calendar_carousel: 2.1.0 - google_maps_flutter: ^2.1.1 - flutter_google_places: ^0.3.0 + # google_maps_flutter: ^2.1.1 + # flutter_google_places: ^0.3.0 location: ^4.3.0 flutter_material_color_picker: ^1.1.0+2 flutter_typeahead: ^3.2.4 @@ -47,7 +47,7 @@ dependencies: flutter_dynamic_icon: ^2.0.0 device_calendar: ^4.2.0 dropdown_search: ^2.0.1 - multi_select_flutter: ^4.0.0 + multi_select_flutter: 4.0.0 retrofit: ^3.0.0 logger: any dio: ^4.0.4 @@ -56,6 +56,7 @@ dependencies: jaguar_flutter_asset: ^3.0.0 flutter_webview_pro: 3.0.1+3 home_widget: ^0.1.5 + flutter_dotenv: ^5.1.0 permission_handler: ^8.3.0 qr_flutter: ^4.0.0 @@ -64,6 +65,7 @@ dependencies: awesome_notifications: ^0.6.21 flutter_linkify: ^5.0.2 upgrader: ^4.11.1 + encrypt: ^5.0.1 dev_dependencies: flutter_test: @@ -88,6 +90,8 @@ flutter: - assets/communities/emojis/ - assets/map/assets/ - assets/map/assets/map/ + - assets/buynsell/ + - .env fonts: - family: IBMPlexSans fonts: