Compare commits
	
		
			29 Commits
		
	
	
		
			1.0.0
			...
			91d238bc2f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 91d238bc2f | |||
| 217c20164f | |||
| 52304a7633 | |||
| a303f5c30c | |||
| 92354eee34 | |||
| c3df9b0f5b | |||
| 3705ab82f3 | |||
| f6429115a4 | |||
| 527655458f | |||
| b6a11dc858 | |||
| b520f27b90 | |||
| 7c8c1025e2 | |||
| cba4f19b17 | |||
| eb01942e2d | |||
| c4157c3e23 | |||
| b32a7216b5 | |||
| e74aba2d8b | |||
| 41f456893f | |||
| 06b4cb863e | |||
| 827f8f1175 | |||
| e54a506671 | |||
| a1fd936702 | |||
| 048b8c0894 | |||
| c19bb3730e | |||
| a3b4706ca2 | |||
| 8bb9c960c1 | |||
| 5da657a73c | |||
| f3da8f5349 | |||
| ef0e7f02da | 
							
								
								
									
										17
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,16 +1,3 @@ | ||||
| # goatagent | ||||
| # SolarAgent | ||||
|  | ||||
| The official Application of Goatworks Global Hydrogen Network | ||||
|  | ||||
| ## Getting Started | ||||
|  | ||||
| This project is a starting point for a Flutter application. | ||||
|  | ||||
| A few resources to get you started if this is your first Flutter project: | ||||
|  | ||||
| - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) | ||||
| - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) | ||||
|  | ||||
| For help getting started with Flutter development, view the | ||||
| [online documentation](https://docs.flutter.dev/), which offers tutorials, | ||||
| samples, guidance on mobile development, and a full API reference. | ||||
| The official Solar Network moblie application. | ||||
| @@ -23,7 +23,7 @@ if (flutterVersionName == null) { | ||||
| } | ||||
|  | ||||
| android { | ||||
|     namespace "studio.smartsheep.goatagent" | ||||
|     namespace "dev.solsynth.solaragent" | ||||
|     compileSdkVersion flutter.compileSdkVersion | ||||
|     ndkVersion flutter.ndkVersion | ||||
|  | ||||
| @@ -41,7 +41,7 @@ android { | ||||
|     } | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId "studio.smartsheep.goatagent" | ||||
|         applicationId "dev.solsynth.solaragent" | ||||
|         // You can update the following values to match your application needs. | ||||
|         // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. | ||||
|         minSdkVersion flutter.minSdkVersion | ||||
| @@ -50,7 +50,7 @@ android { | ||||
|         versionName flutterVersionName | ||||
|         manifestPlaceholders = [ | ||||
|                 applicationName      : 'android.app.Application', | ||||
|                 appAuthRedirectScheme: 'goatagent' | ||||
|                 appAuthRedirectScheme: 'solaragent' | ||||
|         ] | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,39 +0,0 @@ | ||||
| { | ||||
|   "project_info": { | ||||
|     "project_number": "659822066072", | ||||
|     "project_id": "smartsheep-hydrogen", | ||||
|     "storage_bucket": "smartsheep-hydrogen.appspot.com" | ||||
|   }, | ||||
|   "client": [ | ||||
|     { | ||||
|       "client_info": { | ||||
|         "mobilesdk_app_id": "1:659822066072:android:39e699282c97a7cfc013ed", | ||||
|         "android_client_info": { | ||||
|           "package_name": "studio.smartsheep.goatagent" | ||||
|         } | ||||
|       }, | ||||
|       "oauth_client": [ | ||||
|         { | ||||
|           "client_id": "659822066072-dde0aqiocn28bc1gk9p5k8oaqe1jpi0l.apps.googleusercontent.com", | ||||
|           "client_type": 3 | ||||
|         } | ||||
|       ], | ||||
|       "api_key": [ | ||||
|         { | ||||
|           "current_key": "AIzaSyBLPaAK4CVW9umXIdUoGOGHO42jKnwZkKo" | ||||
|         } | ||||
|       ], | ||||
|       "services": { | ||||
|         "appinvite_service": { | ||||
|           "other_platform_oauth_client": [ | ||||
|             { | ||||
|               "client_id": "659822066072-dde0aqiocn28bc1gk9p5k8oaqe1jpi0l.apps.googleusercontent.com", | ||||
|               "client_type": 3 | ||||
|             } | ||||
|           ] | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "configuration_version": "1" | ||||
| } | ||||
| @@ -1,8 +1,8 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <application | ||||
|         android:label="GoatAgent" | ||||
|         android:label="Solian" | ||||
|         android:name="${applicationName}" | ||||
|         android:icon="@mipmap/launcher_icon"> | ||||
|         android:icon="@mipmap/ic_launcher"> | ||||
|         <activity | ||||
|             android:name=".MainActivity" | ||||
|             android:exported="true" | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package studio.smartsheep.goatagent | ||||
| package dev.solsynth.solaragent | ||||
|  | ||||
| import io.flutter.embedding.android.FlutterActivity | ||||
|  | ||||
|   | ||||
| Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 2.9 KiB | 
| Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 4.3 KiB | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 6.8 KiB | 
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 9.6 KiB | 
| @@ -1,39 +0,0 @@ | ||||
| { | ||||
|   "project_info": { | ||||
|     "project_number": "659822066072", | ||||
|     "project_id": "smartsheep-hydrogen", | ||||
|     "storage_bucket": "smartsheep-hydrogen.appspot.com" | ||||
|   }, | ||||
|   "client": [ | ||||
|     { | ||||
|       "client_info": { | ||||
|         "mobilesdk_app_id": "1:659822066072:android:39e699282c97a7cfc013ed", | ||||
|         "android_client_info": { | ||||
|           "package_name": "studio.smartsheep.goatagent" | ||||
|         } | ||||
|       }, | ||||
|       "oauth_client": [ | ||||
|         { | ||||
|           "client_id": "659822066072-dde0aqiocn28bc1gk9p5k8oaqe1jpi0l.apps.googleusercontent.com", | ||||
|           "client_type": 3 | ||||
|         } | ||||
|       ], | ||||
|       "api_key": [ | ||||
|         { | ||||
|           "current_key": "AIzaSyBLPaAK4CVW9umXIdUoGOGHO42jKnwZkKo" | ||||
|         } | ||||
|       ], | ||||
|       "services": { | ||||
|         "appinvite_service": { | ||||
|           "other_platform_oauth_client": [ | ||||
|             { | ||||
|               "client_id": "659822066072-dde0aqiocn28bc1gk9p5k8oaqe1jpi0l.apps.googleusercontent.com", | ||||
|               "client_type": 3 | ||||
|             } | ||||
|           ] | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "configuration_version": "1" | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								image.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 69 KiB | 
							
								
								
									
										4
									
								
								ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
| </Workspace> | ||||
| @@ -1,30 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>API_KEY</key> | ||||
| 	<string>AIzaSyBQB4u2KKe1P5jMG_zWGiUFtpcjQKhG3jY</string> | ||||
| 	<key>GCM_SENDER_ID</key> | ||||
| 	<string>659822066072</string> | ||||
| 	<key>PLIST_VERSION</key> | ||||
| 	<string>1</string> | ||||
| 	<key>BUNDLE_ID</key> | ||||
| 	<string>studio.smartsheep.goatagent</string> | ||||
| 	<key>PROJECT_ID</key> | ||||
| 	<string>smartsheep-hydrogen</string> | ||||
| 	<key>STORAGE_BUCKET</key> | ||||
| 	<string>smartsheep-hydrogen.appspot.com</string> | ||||
| 	<key>IS_ADS_ENABLED</key> | ||||
| 	<false></false> | ||||
| 	<key>IS_ANALYTICS_ENABLED</key> | ||||
| 	<false></false> | ||||
| 	<key>IS_APPINVITE_ENABLED</key> | ||||
| 	<true></true> | ||||
| 	<key>IS_GCM_ENABLED</key> | ||||
| 	<true></true> | ||||
| 	<key>IS_SIGNIN_ENABLED</key> | ||||
| 	<true></true> | ||||
| 	<key>GOOGLE_APP_ID</key> | ||||
| 	<string>1:659822066072:ios:90dff099ef47fc8fc013ed</string> | ||||
| </dict> | ||||
| </plist> | ||||
							
								
								
									
										157
									
								
								ios/Podfile.lock
									
									
									
									
									
								
							
							
						
						| @@ -1,187 +1,58 @@ | ||||
| PODS: | ||||
|   - AppAuth (1.6.2): | ||||
|     - AppAuth/Core (= 1.6.2) | ||||
|     - AppAuth/ExternalUserAgent (= 1.6.2) | ||||
|   - AppAuth/Core (1.6.2) | ||||
|   - AppAuth/ExternalUserAgent (1.6.2): | ||||
|     - AppAuth/Core | ||||
|   - Firebase/CoreOnly (10.20.0): | ||||
|     - FirebaseCore (= 10.20.0) | ||||
|   - Firebase/Crashlytics (10.20.0): | ||||
|     - Firebase/CoreOnly | ||||
|     - FirebaseCrashlytics (~> 10.20.0) | ||||
|   - Firebase/Messaging (10.20.0): | ||||
|     - Firebase/CoreOnly | ||||
|     - FirebaseMessaging (~> 10.20.0) | ||||
|   - firebase_core (2.25.4): | ||||
|     - Firebase/CoreOnly (= 10.20.0) | ||||
|     - Flutter | ||||
|   - firebase_crashlytics (3.4.14): | ||||
|     - Firebase/Crashlytics (= 10.20.0) | ||||
|     - firebase_core | ||||
|     - Flutter | ||||
|   - firebase_messaging (14.7.15): | ||||
|     - Firebase/Messaging (= 10.20.0) | ||||
|     - firebase_core | ||||
|     - Flutter | ||||
|   - FirebaseCore (10.20.0): | ||||
|     - FirebaseCoreInternal (~> 10.0) | ||||
|     - GoogleUtilities/Environment (~> 7.12) | ||||
|     - GoogleUtilities/Logger (~> 7.12) | ||||
|   - FirebaseCoreExtension (10.21.0): | ||||
|     - FirebaseCore (~> 10.0) | ||||
|   - FirebaseCoreInternal (10.21.0): | ||||
|     - "GoogleUtilities/NSData+zlib (~> 7.8)" | ||||
|   - FirebaseCrashlytics (10.20.0): | ||||
|     - FirebaseCore (~> 10.5) | ||||
|     - FirebaseInstallations (~> 10.0) | ||||
|     - FirebaseSessions (~> 10.5) | ||||
|     - GoogleDataTransport (~> 9.2) | ||||
|     - GoogleUtilities/Environment (~> 7.8) | ||||
|     - nanopb (< 2.30910.0, >= 2.30908.0) | ||||
|     - PromisesObjC (~> 2.1) | ||||
|   - FirebaseInstallations (10.21.0): | ||||
|     - FirebaseCore (~> 10.0) | ||||
|     - GoogleUtilities/Environment (~> 7.8) | ||||
|     - GoogleUtilities/UserDefaults (~> 7.8) | ||||
|     - PromisesObjC (~> 2.1) | ||||
|   - FirebaseMessaging (10.20.0): | ||||
|     - FirebaseCore (~> 10.0) | ||||
|     - FirebaseInstallations (~> 10.0) | ||||
|     - GoogleDataTransport (~> 9.3) | ||||
|     - GoogleUtilities/AppDelegateSwizzler (~> 7.8) | ||||
|     - GoogleUtilities/Environment (~> 7.8) | ||||
|     - GoogleUtilities/Reachability (~> 7.8) | ||||
|     - GoogleUtilities/UserDefaults (~> 7.8) | ||||
|     - nanopb (< 2.30910.0, >= 2.30908.0) | ||||
|   - FirebaseSessions (10.21.0): | ||||
|     - FirebaseCore (~> 10.5) | ||||
|     - FirebaseCoreExtension (~> 10.0) | ||||
|     - FirebaseInstallations (~> 10.0) | ||||
|     - GoogleDataTransport (~> 9.2) | ||||
|     - GoogleUtilities/Environment (~> 7.10) | ||||
|     - nanopb (< 2.30910.0, >= 2.30908.0) | ||||
|     - PromisesSwift (~> 2.1) | ||||
|   - Flutter (1.0.0) | ||||
|   - flutter_appauth (0.0.1): | ||||
|     - AppAuth (= 1.6.2) | ||||
|     - Flutter | ||||
|   - flutter_secure_storage (6.0.0): | ||||
|     - Flutter | ||||
|   - GoogleDataTransport (9.3.0): | ||||
|     - GoogleUtilities/Environment (~> 7.7) | ||||
|     - nanopb (< 2.30910.0, >= 2.30908.0) | ||||
|     - PromisesObjC (< 3.0, >= 1.2) | ||||
|   - GoogleUtilities/AppDelegateSwizzler (7.12.0): | ||||
|     - GoogleUtilities/Environment | ||||
|     - GoogleUtilities/Logger | ||||
|     - GoogleUtilities/Network | ||||
|   - GoogleUtilities/Environment (7.12.0): | ||||
|     - PromisesObjC (< 3.0, >= 1.2) | ||||
|   - GoogleUtilities/Logger (7.12.0): | ||||
|     - GoogleUtilities/Environment | ||||
|   - GoogleUtilities/Network (7.12.0): | ||||
|     - GoogleUtilities/Logger | ||||
|     - "GoogleUtilities/NSData+zlib" | ||||
|     - GoogleUtilities/Reachability | ||||
|   - "GoogleUtilities/NSData+zlib (7.12.0)" | ||||
|   - GoogleUtilities/Reachability (7.12.0): | ||||
|     - GoogleUtilities/Logger | ||||
|   - GoogleUtilities/UserDefaults (7.12.0): | ||||
|     - GoogleUtilities/Logger | ||||
|   - nanopb (2.30909.1): | ||||
|     - nanopb/decode (= 2.30909.1) | ||||
|     - nanopb/encode (= 2.30909.1) | ||||
|   - nanopb/decode (2.30909.1) | ||||
|   - nanopb/encode (2.30909.1) | ||||
|   - image_picker_ios (0.0.1): | ||||
|     - Flutter | ||||
|   - package_info_plus (0.4.5): | ||||
|     - Flutter | ||||
|   - path_provider_foundation (0.0.1): | ||||
|     - Flutter | ||||
|     - FlutterMacOS | ||||
|   - PromisesObjC (2.3.1) | ||||
|   - PromisesSwift (2.3.1): | ||||
|     - PromisesObjC (= 2.3.1) | ||||
|   - shared_preferences_foundation (0.0.1): | ||||
|     - Flutter | ||||
|     - FlutterMacOS | ||||
|   - url_launcher_ios (0.0.1): | ||||
|     - Flutter | ||||
|   - webview_flutter_wkwebview (0.0.1): | ||||
|     - Flutter | ||||
|  | ||||
| DEPENDENCIES: | ||||
|   - firebase_core (from `.symlinks/plugins/firebase_core/ios`) | ||||
|   - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) | ||||
|   - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) | ||||
|   - Flutter (from `Flutter`) | ||||
|   - flutter_appauth (from `.symlinks/plugins/flutter_appauth/ios`) | ||||
|   - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) | ||||
|   - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) | ||||
|   - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) | ||||
|   - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) | ||||
|   - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) | ||||
|   - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) | ||||
|   - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) | ||||
|  | ||||
| SPEC REPOS: | ||||
|   trunk: | ||||
|     - AppAuth | ||||
|     - Firebase | ||||
|     - FirebaseCore | ||||
|     - FirebaseCoreExtension | ||||
|     - FirebaseCoreInternal | ||||
|     - FirebaseCrashlytics | ||||
|     - FirebaseInstallations | ||||
|     - FirebaseMessaging | ||||
|     - FirebaseSessions | ||||
|     - GoogleDataTransport | ||||
|     - GoogleUtilities | ||||
|     - nanopb | ||||
|     - PromisesObjC | ||||
|     - PromisesSwift | ||||
|  | ||||
| EXTERNAL SOURCES: | ||||
|   firebase_core: | ||||
|     :path: ".symlinks/plugins/firebase_core/ios" | ||||
|   firebase_crashlytics: | ||||
|     :path: ".symlinks/plugins/firebase_crashlytics/ios" | ||||
|   firebase_messaging: | ||||
|     :path: ".symlinks/plugins/firebase_messaging/ios" | ||||
|   Flutter: | ||||
|     :path: Flutter | ||||
|   flutter_appauth: | ||||
|     :path: ".symlinks/plugins/flutter_appauth/ios" | ||||
|   flutter_secure_storage: | ||||
|     :path: ".symlinks/plugins/flutter_secure_storage/ios" | ||||
|   image_picker_ios: | ||||
|     :path: ".symlinks/plugins/image_picker_ios/ios" | ||||
|   package_info_plus: | ||||
|     :path: ".symlinks/plugins/package_info_plus/ios" | ||||
|   path_provider_foundation: | ||||
|     :path: ".symlinks/plugins/path_provider_foundation/darwin" | ||||
|   shared_preferences_foundation: | ||||
|     :path: ".symlinks/plugins/shared_preferences_foundation/darwin" | ||||
|   url_launcher_ios: | ||||
|     :path: ".symlinks/plugins/url_launcher_ios/ios" | ||||
|   webview_flutter_wkwebview: | ||||
|     :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" | ||||
|  | ||||
| SPEC CHECKSUMS: | ||||
|   AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570 | ||||
|   Firebase: 10c8cb12fb7ad2ae0c09ffc86cd9c1ab392a0031 | ||||
|   firebase_core: a46c312d8bae4defa3d009b2aa7b5b413aeb394e | ||||
|   firebase_crashlytics: 3d12285fcbd865d576b9965bd4090cd8b68e11fd | ||||
|   firebase_messaging: 40d7dd2f3e88a6367c7326cf601f84633d477582 | ||||
|   FirebaseCore: 28045c1560a2600d284b9c45a904fe322dc890b6 | ||||
|   FirebaseCoreExtension: 1c044fd46e95036cccb29134757c499613f3f564 | ||||
|   FirebaseCoreInternal: 43c1788eaeee9d1b97caaa751af567ce11010d00 | ||||
|   FirebaseCrashlytics: 81530595edb6d99f1918f723a6c33766a24a4c86 | ||||
|   FirebaseInstallations: 390ea1d10a4d02b20c965cbfd527ee9b3b412acb | ||||
|   FirebaseMessaging: 06c414a21b122396a26847c523d5c370f8325df5 | ||||
|   FirebaseSessions: 80c2bbdd28166267b3d132debe5f7531efdb00bc | ||||
|   Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 | ||||
|   flutter_appauth: 0863b1f33110b410213e736aead4a6727303f509 | ||||
|   flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be | ||||
|   GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe | ||||
|   GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 | ||||
|   nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 | ||||
|   image_picker_ios: b545a5f16c0fa88e3ecbbce3ed4de45567a8ec18 | ||||
|   package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 | ||||
|   path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c | ||||
|   PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 | ||||
|   PromisesSwift: 28dca69a9c40779916ac2d6985a0192a5cb4a265 | ||||
|   url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812 | ||||
|   shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 | ||||
|   url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586 | ||||
|   webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36 | ||||
|  | ||||
| PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 | ||||
|   | ||||
| @@ -16,7 +16,6 @@ | ||||
| 		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 */; }; | ||||
| 		E82D4FD7CF8610AE06A29945 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B3FD85540C530EB623F244F0 /* GoogleService-Info.plist */; }; | ||||
| /* End PBXBuildFile section */ | ||||
|  | ||||
| /* Begin PBXContainerItemProxy section */ | ||||
| @@ -65,7 +64,6 @@ | ||||
| 		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||||
| 		AFF7DBFDB082C74F82791AB2 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; }; | ||||
| 		B2ED47C18F55E48A0603226E /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; }; | ||||
| 		B3FD85540C530EB623F244F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; }; | ||||
| 		B86346ED17835849683BBD52 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| 		D0F7456C6B52E05DA9E946D2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | ||||
| /* End PBXFileReference section */ | ||||
| @@ -127,7 +125,6 @@ | ||||
| 				331C8082294A63A400263BE5 /* RunnerTests */, | ||||
| 				B986742A69F7079E8EB387D9 /* Pods */, | ||||
| 				3D7BB3AACE7D51DB2A1773FD /* Frameworks */, | ||||
| 				B3FD85540C530EB623F244F0 /* GoogleService-Info.plist */, | ||||
| 			); | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| @@ -203,7 +200,6 @@ | ||||
| 				9705A1C41CF9048500538489 /* Embed Frameworks */, | ||||
| 				3B06AD1E1E4923F5004D2608 /* Thin Binary */, | ||||
| 				6735FEBC571714546BA26AEA /* [CP] Embed Pods Frameworks */, | ||||
| 				B4B724ADD0BB41C68032B791 /* [firebase_crashlytics] Crashlytics Upload Symbols */, | ||||
| 			); | ||||
| 			buildRules = ( | ||||
| 			); | ||||
| @@ -221,7 +217,7 @@ | ||||
| 			isa = PBXProject; | ||||
| 			attributes = { | ||||
| 				BuildIndependentTargetsInParallel = YES; | ||||
| 				LastUpgradeCheck = 1430; | ||||
| 				LastUpgradeCheck = 1510; | ||||
| 				ORGANIZATIONNAME = ""; | ||||
| 				TargetAttributes = { | ||||
| 					331C8080294A63A400263BE5 = { | ||||
| @@ -269,7 +265,6 @@ | ||||
| 				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, | ||||
| 				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, | ||||
| 				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, | ||||
| 				E82D4FD7CF8610AE06A29945 /* GoogleService-Info.plist in Resources */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| @@ -368,29 +363,6 @@ | ||||
| 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||||
| 			showEnvVarsInLog = 0; | ||||
| 		}; | ||||
| 		B4B724ADD0BB41C68032B791 /* [firebase_crashlytics] Crashlytics Upload Symbols */ = { | ||||
| 			isa = PBXShellScriptBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			inputFileListPaths = ( | ||||
| 			); | ||||
| 			inputPaths = ( | ||||
| 				"\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"", | ||||
| 				"\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/\"", | ||||
| 				"\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"", | ||||
| 				"\"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)\"", | ||||
| 				"\"$(PROJECT_DIR)/firebase_app_id_file.json\"", | ||||
| 			); | ||||
| 			name = "[firebase_crashlytics] Crashlytics Upload Symbols"; | ||||
| 			outputFileListPaths = ( | ||||
| 			); | ||||
| 			outputPaths = ( | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 			shellPath = /bin/sh; | ||||
| 			shellScript = "\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --flutter-project \"$PROJECT_DIR/firebase_app_id_file.json\" "; | ||||
| 		}; | ||||
| /* End PBXShellScriptBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXSourcesBuildPhase section */ | ||||
| @@ -505,14 +477,14 @@ | ||||
| 				DEVELOPMENT_TEAM = W7HPZ53V6B; | ||||
| 				ENABLE_BITCODE = NO; | ||||
| 				INFOPLIST_FILE = Runner/Info.plist; | ||||
| 				INFOPLIST_KEY_CFBundleDisplayName = GoatAgent; | ||||
| 				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; | ||||
| 				INFOPLIST_KEY_CFBundleDisplayName = Solian; | ||||
| 				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 				); | ||||
| 				MARKETING_VERSION = 1.0.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| @@ -529,7 +501,7 @@ | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent.RunnerTests; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; | ||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||||
| @@ -547,7 +519,7 @@ | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent.RunnerTests; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||||
| @@ -563,7 +535,7 @@ | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent.RunnerTests; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||||
| @@ -574,7 +546,7 @@ | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; | ||||
| 				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| @@ -632,7 +604,7 @@ | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; | ||||
| 				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| @@ -694,14 +666,14 @@ | ||||
| 				DEVELOPMENT_TEAM = W7HPZ53V6B; | ||||
| 				ENABLE_BITCODE = NO; | ||||
| 				INFOPLIST_FILE = Runner/Info.plist; | ||||
| 				INFOPLIST_KEY_CFBundleDisplayName = GoatAgent; | ||||
| 				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; | ||||
| 				INFOPLIST_KEY_CFBundleDisplayName = Solian; | ||||
| 				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 				); | ||||
| 				MARKETING_VERSION = 1.0.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||||
| @@ -721,14 +693,14 @@ | ||||
| 				DEVELOPMENT_TEAM = W7HPZ53V6B; | ||||
| 				ENABLE_BITCODE = NO; | ||||
| 				INFOPLIST_FILE = Runner/Info.plist; | ||||
| 				INFOPLIST_KEY_CFBundleDisplayName = GoatAgent; | ||||
| 				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; | ||||
| 				INFOPLIST_KEY_CFBundleDisplayName = Solian; | ||||
| 				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 				); | ||||
| 				MARKETING_VERSION = 1.0.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Scheme | ||||
|    LastUpgradeVersion = "1430" | ||||
|    LastUpgradeVersion = "1510" | ||||
|    version = "1.3"> | ||||
|    <BuildAction | ||||
|       parallelizeBuildables = "YES" | ||||
|   | ||||
| @@ -1,122 +1,122 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "size" : "20x20", | ||||
|       "idiom" : "iphone", | ||||
|       "filename" : "Icon-App-20x20@2x.png", | ||||
|       "scale" : "2x" | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "2x", | ||||
|       "size" : "20x20" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "20x20", | ||||
|       "idiom" : "iphone", | ||||
|       "filename" : "Icon-App-20x20@3x.png", | ||||
|       "scale" : "3x" | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "3x", | ||||
|       "size" : "20x20" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "29x29", | ||||
|       "idiom" : "iphone", | ||||
|       "filename" : "Icon-App-29x29@1x.png", | ||||
|       "scale" : "1x" | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "1x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "29x29", | ||||
|       "idiom" : "iphone", | ||||
|       "filename" : "Icon-App-29x29@2x.png", | ||||
|       "scale" : "2x" | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "2x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "29x29", | ||||
|       "idiom" : "iphone", | ||||
|       "filename" : "Icon-App-29x29@3x.png", | ||||
|       "scale" : "3x" | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "3x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "40x40", | ||||
|       "idiom" : "iphone", | ||||
|       "filename" : "Icon-App-40x40@2x.png", | ||||
|       "scale" : "2x" | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "2x", | ||||
|       "size" : "40x40" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "40x40", | ||||
|       "idiom" : "iphone", | ||||
|       "filename" : "Icon-App-40x40@3x.png", | ||||
|       "scale" : "3x" | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "3x", | ||||
|       "size" : "40x40" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "60x60", | ||||
|       "idiom" : "iphone", | ||||
|       "filename" : "Icon-App-60x60@2x.png", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "60x60", | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "2x", | ||||
|       "size" : "60x60" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "Icon-App-60x60@3x.png", | ||||
|       "scale" : "3x" | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "3x", | ||||
|       "size" : "60x60" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "20x20", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-20x20@1x.png", | ||||
|       "scale" : "1x" | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "1x", | ||||
|       "size" : "20x20" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "20x20", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-20x20@2x.png", | ||||
|       "scale" : "2x" | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "20x20" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "29x29", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-29x29@1x.png", | ||||
|       "scale" : "1x" | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "1x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "29x29", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-29x29@2x.png", | ||||
|       "scale" : "2x" | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "40x40", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-40x40@1x.png", | ||||
|       "scale" : "1x" | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "1x", | ||||
|       "size" : "40x40" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "40x40", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-40x40@2x.png", | ||||
|       "scale" : "2x" | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "40x40" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "76x76", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-76x76@1x.png", | ||||
|       "scale" : "1x" | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "1x", | ||||
|       "size" : "76x76" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "76x76", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-76x76@2x.png", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "83.5x83.5", | ||||
|       "idiom" : "ipad", | ||||
|       "filename" : "Icon-App-83.5x83.5@2x.png", | ||||
|       "scale" : "2x" | ||||
|       "scale" : "2x", | ||||
|       "size" : "76x76" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "Icon-App-83.5x83.5@2x.png", | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "83.5x83.5" | ||||
|     }, | ||||
|     { | ||||
|       "size" : "1024x1024", | ||||
|       "idiom" : "ios-marketing", | ||||
|       "filename" : "Icon-App-1024x1024@1x.png", | ||||
|       "scale" : "1x" | ||||
|       "idiom" : "ios-marketing", | ||||
|       "scale" : "1x", | ||||
|       "size" : "1024x1024" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "version" : 1, | ||||
|     "author" : "xcode" | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
|   | ||||
| Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 63 KiB | 
| Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 625 B | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.3 KiB | 
| Before Width: | Height: | Size: 880 B After Width: | Height: | Size: 958 B | 
| Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.2 KiB | 
| Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.7 KiB | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.3 KiB | 
| Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 5.6 KiB | 
| Before Width: | Height: | Size: 1.6 KiB | 
| Before Width: | Height: | Size: 3.2 KiB | 
| Before Width: | Height: | Size: 1.8 KiB | 
| Before Width: | Height: | Size: 3.7 KiB | 
| Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 5.6 KiB | 
| Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 8.9 KiB | 
| Before Width: | Height: | Size: 2.3 KiB | 
| Before Width: | Height: | Size: 4.6 KiB | 
| Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 3.1 KiB | 
| Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 7.4 KiB | 
| Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 8.0 KiB | 
| @@ -1,23 +1,23 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait 2.png", | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "LaunchImage.png", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait 1.png", | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "LaunchImage@2x.png", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait.png", | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "LaunchImage@3x.png", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "version" : 1, | ||||
|     "author" : "xcode" | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
|   | ||||
| Before Width: | Height: | Size: 68 B | 
| Before Width: | Height: | Size: 68 B | 
| Before Width: | Height: | Size: 68 B | 
| After Width: | Height: | Size: 108 KiB | 
| After Width: | Height: | Size: 108 KiB | 
| After Width: | Height: | Size: 108 KiB | 
| @@ -1,30 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>API_KEY</key> | ||||
| 	<string>AIzaSyBQB4u2KKe1P5jMG_zWGiUFtpcjQKhG3jY</string> | ||||
| 	<key>GCM_SENDER_ID</key> | ||||
| 	<string>659822066072</string> | ||||
| 	<key>PLIST_VERSION</key> | ||||
| 	<string>1</string> | ||||
| 	<key>BUNDLE_ID</key> | ||||
| 	<string>studio.smartsheep.goatagent</string> | ||||
| 	<key>PROJECT_ID</key> | ||||
| 	<string>smartsheep-hydrogen</string> | ||||
| 	<key>STORAGE_BUCKET</key> | ||||
| 	<string>smartsheep-hydrogen.appspot.com</string> | ||||
| 	<key>IS_ADS_ENABLED</key> | ||||
| 	<false></false> | ||||
| 	<key>IS_ANALYTICS_ENABLED</key> | ||||
| 	<false></false> | ||||
| 	<key>IS_APPINVITE_ENABLED</key> | ||||
| 	<true></true> | ||||
| 	<key>IS_GCM_ENABLED</key> | ||||
| 	<true></true> | ||||
| 	<key>IS_SIGNIN_ENABLED</key> | ||||
| 	<true></true> | ||||
| 	<key>GOOGLE_APP_ID</key> | ||||
| 	<string>1:659822066072:ios:90dff099ef47fc8fc013ed</string> | ||||
| </dict> | ||||
| </plist> | ||||
| @@ -7,7 +7,7 @@ | ||||
| 	<key>CFBundleDevelopmentRegion</key> | ||||
| 	<string>$(DEVELOPMENT_LANGUAGE)</string> | ||||
| 	<key>CFBundleDisplayName</key> | ||||
| 	<string>GoatAgent</string> | ||||
| 	<string>SolarAgent</string> | ||||
| 	<key>CFBundleExecutable</key> | ||||
| 	<string>$(EXECUTABLE_NAME)</string> | ||||
| 	<key>CFBundleIdentifier</key> | ||||
| @@ -15,7 +15,7 @@ | ||||
| 	<key>CFBundleInfoDictionaryVersion</key> | ||||
| 	<string>6.0</string> | ||||
| 	<key>CFBundleName</key> | ||||
| 	<string>GoatAgent</string> | ||||
| 	<string>SolarAgent</string> | ||||
| 	<key>CFBundlePackageType</key> | ||||
| 	<string>APPL</string> | ||||
| 	<key>CFBundleShortVersionString</key> | ||||
| @@ -29,10 +29,10 @@ | ||||
| 	<key>UIApplicationSupportsIndirectInputEvents</key> | ||||
| 	<true/> | ||||
| 	<key>LSApplicationQueriesSchemes</key> | ||||
|     <array> | ||||
|       <string>sms</string> | ||||
|       <string>tel</string> | ||||
|     </array> | ||||
| 	<array> | ||||
| 		<string>sms</string> | ||||
| 		<string>tel</string> | ||||
| 	</array> | ||||
| 	<key>UIBackgroundModes</key> | ||||
| 	<array> | ||||
| 		<string>fetch</string> | ||||
| @@ -55,5 +55,11 @@ | ||||
| 		<string>UIInterfaceOrientationLandscapeLeft</string> | ||||
| 		<string>UIInterfaceOrientationLandscapeRight</string> | ||||
| 	</array> | ||||
| 	<key>NSCameraUsageDescription</key> | ||||
| 	<string>Allow Solar access to your camera to take photo, so that you can attach image in your post.</string> | ||||
| 	<key>NSMicrophoneUsageDescription</key> | ||||
| 	<string>Allow Solar access to your microphone to record audio, so that you can attach image in your post.</string> | ||||
| 	<key>NSPhotoLibraryUsageDescription</key> | ||||
| 	<string>Allow Solar access to your photo library, so that you can attach image in your post.</string> | ||||
| </dict> | ||||
| </plist> | ||||
|   | ||||
| @@ -1,5 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict/> | ||||
| <dict> | ||||
| 	<key>aps-environment</key> | ||||
| 	<string>development</string> | ||||
| </dict> | ||||
| </plist> | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| { | ||||
|   "file_generated_by": "FlutterFire CLI", | ||||
|   "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", | ||||
|   "GOOGLE_APP_ID": "1:659822066072:ios:90dff099ef47fc8fc013ed", | ||||
|   "FIREBASE_PROJECT_ID": "smartsheep-hydrogen", | ||||
|   "GCM_SENDER_ID": "659822066072" | ||||
| } | ||||
| @@ -3,35 +3,31 @@ import 'dart:io'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_secure_storage/flutter_secure_storage.dart'; | ||||
| import 'package:goatagent/firebase.dart'; | ||||
| import 'package:goatagent/screens/auth.dart'; | ||||
| import 'package:solaragent/screens/auth.dart'; | ||||
| import 'package:oauth2/oauth2.dart' as oauth2; | ||||
|  | ||||
| final authClient = AuthGuard(); | ||||
|  | ||||
| class AuthGuard { | ||||
|   static final AuthGuard _singleton = AuthGuard._internal(); | ||||
|   AuthGuard(); | ||||
|  | ||||
|   final deviceEndpoint = | ||||
|       Uri.parse('https://id.smartsheep.studio/api/notifications/subscribe'); | ||||
|       Uri.parse('https://id.solsynth.dev/api/notifications/subscribe'); | ||||
|   final authorizationEndpoint = | ||||
|       Uri.parse('https://id.smartsheep.studio/auth/o/connect'); | ||||
|   final tokenEndpoint = | ||||
|       Uri.parse('https://id.smartsheep.studio/api/auth/token'); | ||||
|   final userinfoEndpoint = | ||||
|       Uri.parse('https://id.smartsheep.studio/api/users/me'); | ||||
|   final redirectUrl = Uri.parse('goatagent://auth'); | ||||
|       Uri.parse('https://id.solsynth.dev/auth/o/connect'); | ||||
|   final tokenEndpoint = Uri.parse('https://id.solsynth.dev/api/auth/token'); | ||||
|   final userinfoEndpoint = Uri.parse('https://id.solsynth.dev/api/users/me'); | ||||
|   final redirectUrl = Uri.parse('solaragent://auth'); | ||||
|  | ||||
|   static const clientId = "goatagent"; | ||||
|   static const clientId = "solaragent"; | ||||
|   static const clientSecret = "_F4%q2Eea3"; | ||||
|  | ||||
|   static const storage = FlutterSecureStorage(); | ||||
|   static const storageKey = "identity"; | ||||
|   static const profileKey = "profiles"; | ||||
|  | ||||
|   factory AuthGuard() { | ||||
|     return _singleton; | ||||
|   } | ||||
|  | ||||
|   oauth2.Client? client; | ||||
|   DateTime? lastRefreshedAt; | ||||
|  | ||||
|   Future<bool> pickClient() async { | ||||
|     if (await storage.containsKey(key: storageKey)) { | ||||
| @@ -43,7 +39,7 @@ class AuthGuard { | ||||
|         await pullProfiles(); | ||||
|         return true; | ||||
|       } catch (e) { | ||||
|         logout(); | ||||
|         signoff(); | ||||
|         return false; | ||||
|       } | ||||
|     } else { | ||||
| @@ -68,11 +64,6 @@ class AuthGuard { | ||||
|     var authorizationUrl = grant.getAuthorizationUrl(redirectUrl); | ||||
|  | ||||
|     if (Platform.isAndroid || Platform.isIOS) { | ||||
|       // Let Goatpass know it is embed in an app | ||||
|       authorizationUrl = authorizationUrl.replace( | ||||
|           queryParameters: {"embedded": "yes"} | ||||
|             ..addAll(authorizationUrl.queryParameters)); | ||||
|  | ||||
|       // Use WebView to get authorization url | ||||
|       var responseUrl = await Navigator.of(context, rootNavigator: true).push( | ||||
|         MaterialPageRoute( | ||||
| @@ -95,57 +86,47 @@ class AuthGuard { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<void> login(BuildContext context) async { | ||||
|     try { | ||||
|       client = await createClient(context); | ||||
|       storage.write(key: storageKey, value: client!.credentials.toJson()); | ||||
|   Future<void> refreshToken() async { | ||||
|     if (client != null) { | ||||
|       var credentials = await client?.credentials.refresh( | ||||
|           identifier: clientId, secret: clientSecret, basicAuth: false); | ||||
|  | ||||
|       await pullProfiles(); | ||||
|       await subscribeNotify(); | ||||
|     } catch (e) { | ||||
|       print(e); | ||||
|       storage.write(key: storageKey, value: credentials!.toJson()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<void> subscribeNotify() async { | ||||
|     if (client == null) { | ||||
|       return; | ||||
|     } | ||||
|   Future<void> signin(BuildContext context) async { | ||||
|     client = await createClient(context); | ||||
|     storage.write(key: storageKey, value: client!.credentials.toJson()); | ||||
|  | ||||
|     var token = await initializeFirebaseMessaging(); | ||||
|     if (token == null) { | ||||
|       print("failed to initialize firebase messaging..."); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     var response = await client!.post( | ||||
|       deviceEndpoint, | ||||
|       headers: {"Content-Type": "application/json"}, | ||||
|       body: jsonEncode({"device_id": token, "provider": "firebase"}), | ||||
|     ); | ||||
|     if (response.statusCode != 200) { | ||||
|       print(response.body); | ||||
|     } | ||||
|     await pullProfiles(); | ||||
|   } | ||||
|  | ||||
|   void logout() { | ||||
|     try { | ||||
|       storage.delete(key: profileKey); | ||||
|       storage.delete(key: storageKey); | ||||
|     } catch (e) { | ||||
|       print(e); | ||||
|     } | ||||
|   void signoff() { | ||||
|     storage.delete(key: profileKey); | ||||
|     storage.delete(key: storageKey); | ||||
|   } | ||||
|  | ||||
|   Future<bool> isAuthorized() async { | ||||
|     const storage = FlutterSecureStorage(); | ||||
|     return await storage.containsKey(key: storageKey); | ||||
|     if (await storage.containsKey(key: storageKey)) { | ||||
|       if (client != null) { | ||||
|         if (lastRefreshedAt == null || | ||||
|             lastRefreshedAt! | ||||
|                 .add(const Duration(minutes: 3)) | ||||
|                 .isAfter(DateTime.now())) { | ||||
|           await refreshToken(); | ||||
|           lastRefreshedAt = DateTime.now(); | ||||
|         } | ||||
|       } | ||||
|       return true; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<dynamic> readProfiles() async { | ||||
|   Future<dynamic> getProfiles() async { | ||||
|     const storage = FlutterSecureStorage(); | ||||
|     return jsonDecode(await storage.read(key: profileKey) ?? "{}"); | ||||
|   } | ||||
|  | ||||
|   AuthGuard._internal(); | ||||
| } | ||||
|   | ||||
| @@ -1,43 +0,0 @@ | ||||
| import 'dart:ui'; | ||||
|  | ||||
| import 'package:firebase_crashlytics/firebase_crashlytics.dart'; | ||||
| import 'package:firebase_messaging/firebase_messaging.dart'; | ||||
| import 'package:firebase_core/firebase_core.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'firebase_options.dart'; | ||||
|  | ||||
| Future<bool> initializeFirebase() async { | ||||
|   await Firebase.initializeApp( | ||||
|     options: DefaultFirebaseOptions.currentPlatform, | ||||
|   ); | ||||
|  | ||||
|   await FirebaseMessaging.instance.requestPermission( | ||||
|     alert: true, | ||||
|     announcement: false, | ||||
|     badge: true, | ||||
|     carPlay: false, | ||||
|     criticalAlert: false, | ||||
|     provisional: false, | ||||
|     sound: true, | ||||
|   ); | ||||
|  | ||||
|   FlutterError.onError = (errorDetails) { | ||||
|     FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails); | ||||
|   }; | ||||
|   PlatformDispatcher.instance.onError = (error, stack) { | ||||
|     FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); | ||||
|     return true; | ||||
|   }; | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| Future<String?> initializeFirebaseMessaging() async { | ||||
|   try { | ||||
|     final fcmToken = await FirebaseMessaging.instance.getToken(); | ||||
|     return fcmToken; | ||||
|   } catch (e) { | ||||
|     print("failed to setup firebase messaging: $e"); | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| @@ -1,81 +0,0 @@ | ||||
| // File generated by FlutterFire CLI. | ||||
| // ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members | ||||
| import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; | ||||
| import 'package:flutter/foundation.dart' | ||||
|     show defaultTargetPlatform, kIsWeb, TargetPlatform; | ||||
|  | ||||
| /// Default [FirebaseOptions] for use with your Firebase apps. | ||||
| /// | ||||
| /// Example: | ||||
| /// ```dart | ||||
| /// import 'firebase_options.dart'; | ||||
| /// // ... | ||||
| /// await Firebase.initializeApp( | ||||
| ///   options: DefaultFirebaseOptions.currentPlatform, | ||||
| /// ); | ||||
| /// ``` | ||||
| class DefaultFirebaseOptions { | ||||
|   static FirebaseOptions get currentPlatform { | ||||
|     if (kIsWeb) { | ||||
|       return web; | ||||
|     } | ||||
|     switch (defaultTargetPlatform) { | ||||
|       case TargetPlatform.android: | ||||
|         return android; | ||||
|       case TargetPlatform.iOS: | ||||
|         return ios; | ||||
|       case TargetPlatform.macOS: | ||||
|         return macos; | ||||
|       case TargetPlatform.windows: | ||||
|         throw UnsupportedError( | ||||
|           'DefaultFirebaseOptions have not been configured for windows - ' | ||||
|           'you can reconfigure this by running the FlutterFire CLI again.', | ||||
|         ); | ||||
|       case TargetPlatform.linux: | ||||
|         throw UnsupportedError( | ||||
|           'DefaultFirebaseOptions have not been configured for linux - ' | ||||
|           'you can reconfigure this by running the FlutterFire CLI again.', | ||||
|         ); | ||||
|       default: | ||||
|         throw UnsupportedError( | ||||
|           'DefaultFirebaseOptions are not supported for this platform.', | ||||
|         ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static const FirebaseOptions web = FirebaseOptions( | ||||
|     apiKey: 'AIzaSyCjTtCsk3Jo98gDiU2Zh8hX2Y-r2CCsT6g', | ||||
|     appId: '1:659822066072:web:2fbe2e4dc1186e00c013ed', | ||||
|     messagingSenderId: '659822066072', | ||||
|     projectId: 'smartsheep-hydrogen', | ||||
|     authDomain: 'smartsheep-hydrogen.firebaseapp.com', | ||||
|     storageBucket: 'smartsheep-hydrogen.appspot.com', | ||||
|     measurementId: 'G-8HVJ5TVQG8', | ||||
|   ); | ||||
|  | ||||
|   static const FirebaseOptions android = FirebaseOptions( | ||||
|     apiKey: 'AIzaSyBLPaAK4CVW9umXIdUoGOGHO42jKnwZkKo', | ||||
|     appId: '1:659822066072:android:39e699282c97a7cfc013ed', | ||||
|     messagingSenderId: '659822066072', | ||||
|     projectId: 'smartsheep-hydrogen', | ||||
|     storageBucket: 'smartsheep-hydrogen.appspot.com', | ||||
|   ); | ||||
|  | ||||
|   static const FirebaseOptions ios = FirebaseOptions( | ||||
|     apiKey: 'AIzaSyBQB4u2KKe1P5jMG_zWGiUFtpcjQKhG3jY', | ||||
|     appId: '1:659822066072:ios:90dff099ef47fc8fc013ed', | ||||
|     messagingSenderId: '659822066072', | ||||
|     projectId: 'smartsheep-hydrogen', | ||||
|     storageBucket: 'smartsheep-hydrogen.appspot.com', | ||||
|     iosBundleId: 'studio.smartsheep.goatagent', | ||||
|   ); | ||||
|  | ||||
|   static const FirebaseOptions macos = FirebaseOptions( | ||||
|     apiKey: 'AIzaSyBQB4u2KKe1P5jMG_zWGiUFtpcjQKhG3jY', | ||||
|     appId: '1:659822066072:ios:17efa96a78467043c013ed', | ||||
|     messagingSenderId: '659822066072', | ||||
|     projectId: 'smartsheep-hydrogen', | ||||
|     storageBucket: 'smartsheep-hydrogen.appspot.com', | ||||
|     iosBundleId: 'studio.smartsheep.goatagent.RunnerTests', | ||||
|   ); | ||||
| } | ||||
| @@ -1,114 +0,0 @@ | ||||
| import 'package:firebase_messaging/firebase_messaging.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:goatagent/screens/account.dart'; | ||||
| import 'package:goatagent/screens/dashboard.dart'; | ||||
| import 'package:goatagent/screens/notifications.dart'; | ||||
|  | ||||
| class AgentNavigation extends StatefulWidget { | ||||
|   AgentNavigation({super.key}); | ||||
|  | ||||
|   static const items = [ | ||||
|     {'label': 'Dashboard', 'icon': Icon(Icons.home)}, | ||||
|     {'label': 'Notifications', 'icon': Icon(Icons.notifications)}, | ||||
|     {'label': 'Account', 'icon': Icon(Icons.account_circle)}, | ||||
|   ]; | ||||
|  | ||||
|   final bottomDestinations = items | ||||
|       .map((element) => BottomNavigationBarItem( | ||||
|             icon: element["icon"] as Widget, | ||||
|             label: element["label"] as String, | ||||
|           )) | ||||
|       .toList(); | ||||
|   final railDestinations = items | ||||
|       .map((element) => NavigationRailDestination( | ||||
|             icon: element["icon"] as Widget, | ||||
|             label: Text(element["label"] as String), | ||||
|           )) | ||||
|       .toList(); | ||||
|  | ||||
|   @override | ||||
|   State<AgentNavigation> createState() => _AgentNavigationState(); | ||||
| } | ||||
|  | ||||
| class _AgentNavigationState extends State<AgentNavigation> { | ||||
|   int _view = 0; | ||||
|  | ||||
|   Future<void> initMessage(BuildContext context) async { | ||||
|     void navigate() { | ||||
|       setState(() { | ||||
|         _view = 1; | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     RemoteMessage? initialMessage = | ||||
|         await FirebaseMessaging.instance.getInitialMessage(); | ||||
|  | ||||
|     if (initialMessage != null) { | ||||
|       navigate(); | ||||
|     } | ||||
|  | ||||
|     FirebaseMessaging.onMessageOpenedApp.listen((event) { | ||||
|       navigate(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     initMessage(context); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final isLargeScreen = MediaQuery.of(context).size.width > 640; | ||||
|  | ||||
|     final content = Stack( | ||||
|       children: [ | ||||
|         Offstage( | ||||
|           offstage: _view != 0, | ||||
|           child: const DashboardScreen(), | ||||
|         ), | ||||
|         Offstage( | ||||
|           offstage: _view != 1, | ||||
|           child: const NotificationScreen(), | ||||
|         ), | ||||
|         Offstage( | ||||
|           offstage: _view != 2, | ||||
|           child: const AccountScreen(), | ||||
|         ) | ||||
|       ], | ||||
|     ); | ||||
|  | ||||
|     if (isLargeScreen) { | ||||
|       return Row(children: <Widget>[ | ||||
|         NavigationRail( | ||||
|           selectedIndex: _view, | ||||
|           groupAlignment: 0, | ||||
|           onDestinationSelected: (int index) { | ||||
|             setState(() { | ||||
|               _view = index; | ||||
|             }); | ||||
|           }, | ||||
|           labelType: NavigationRailLabelType.all, | ||||
|           destinations: widget.railDestinations, | ||||
|         ), | ||||
|         const VerticalDivider(thickness: 1, width: 1), | ||||
|         Expanded(child: content), | ||||
|       ]); | ||||
|     } else { | ||||
|       return Scaffold( | ||||
|         body: content, | ||||
|         bottomNavigationBar: BottomNavigationBar( | ||||
|           currentIndex: _view, | ||||
|           showUnselectedLabels: false, | ||||
|           items: widget.bottomDestinations, | ||||
|           onTap: (index) { | ||||
|             setState(() { | ||||
|               _view = index; | ||||
|             }); | ||||
|           }, | ||||
|         ), | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1,51 +1,59 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:goatagent/auth.dart'; | ||||
| import 'package:goatagent/firebase.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/router.dart'; | ||||
| import 'package:solaragent/widgets/navigation.dart'; | ||||
| import 'package:sentry_flutter/sentry_flutter.dart'; | ||||
|  | ||||
| import 'layouts/navigation.dart'; | ||||
| Future<void> main() async { | ||||
|   await SentryFlutter.init( | ||||
|     (options) { | ||||
|       options.dsn = | ||||
|           'https://a629d9164a60a28ac855c52f4f543722@o4506965897117696.ingest.us.sentry.io/4506965900001280'; | ||||
|       options.tracesSampleRate = 1.0; | ||||
|     }, | ||||
|     appRunner: () async { | ||||
|       WidgetsFlutterBinding.ensureInitialized(); | ||||
|  | ||||
| void main() async { | ||||
|   WidgetsFlutterBinding.ensureInitialized(); | ||||
|       await authClient.pickClient(); | ||||
|  | ||||
|   try { | ||||
|     await initializeFirebase(); | ||||
|   } catch (e) { | ||||
|     print(e); | ||||
|   } | ||||
|  | ||||
|   await AuthGuard().pickClient(); | ||||
|  | ||||
|   runApp(const GoatAgent()); | ||||
|       runApp(const SolarAgent()); | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| class GoatAgent extends StatelessWidget { | ||||
|   const GoatAgent({super.key}); | ||||
| class SolarAgent extends StatelessWidget { | ||||
|   const SolarAgent({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return MaterialApp( | ||||
|       title: 'GoatAgent', | ||||
|     return MaterialApp.router( | ||||
|       title: 'SolarAgent', | ||||
|       theme: ThemeData( | ||||
|         brightness: Brightness.light, | ||||
|         colorScheme: ColorScheme.fromSwatch( | ||||
|             primarySwatch: Colors.indigo, | ||||
|             accentColor: Colors.indigoAccent, | ||||
|             backgroundColor: Colors.white, | ||||
|             brightness: Brightness.light, | ||||
|           primarySwatch: Colors.indigo, | ||||
|           accentColor: Colors.indigoAccent, | ||||
|           backgroundColor: Colors.white, | ||||
|           brightness: Brightness.light, | ||||
|         ), | ||||
|         useMaterial3: true, | ||||
|       ), | ||||
|       darkTheme: ThemeData( | ||||
|         brightness: Brightness.dark, | ||||
|         colorScheme: ColorScheme.fromSwatch( | ||||
|             primarySwatch: Colors.indigo, | ||||
|             accentColor: Colors.indigoAccent, | ||||
|             backgroundColor: Colors.black, | ||||
|             brightness: Brightness.dark, | ||||
|         ), | ||||
|         useMaterial3: true, | ||||
|       routerConfig: router, | ||||
|       builder: (context, child) => Overlay( | ||||
|         initialEntries: [ | ||||
|           OverlayEntry( | ||||
|             builder: (context) => Container( | ||||
|               color: Colors.white, | ||||
|               child: SafeArea( | ||||
|                 bottom: false, | ||||
|                 child: Scaffold( | ||||
|                   body: child, | ||||
|                   bottomNavigationBar: const AgentNavigation(), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|       home: AgentNavigation(), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										59
									
								
								lib/models/author.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,59 @@ | ||||
| class Author { | ||||
|   int id; | ||||
|   DateTime createdAt; | ||||
|   DateTime updatedAt; | ||||
|   DateTime? deletedAt; | ||||
|   String name; | ||||
|   String nick; | ||||
|   String avatar; | ||||
|   String banner; | ||||
|   String description; | ||||
|   String emailAddress; | ||||
|   int powerLevel; | ||||
|   int externalId; | ||||
|  | ||||
|   Author({ | ||||
|     required this.id, | ||||
|     required this.createdAt, | ||||
|     required this.updatedAt, | ||||
|     this.deletedAt, | ||||
|     required this.name, | ||||
|     required this.nick, | ||||
|     required this.avatar, | ||||
|     required this.banner, | ||||
|     required this.description, | ||||
|     required this.emailAddress, | ||||
|     required this.powerLevel, | ||||
|     required this.externalId, | ||||
|   }); | ||||
|  | ||||
|   factory Author.fromJson(Map<String, dynamic> json) => Author( | ||||
|         id: json["id"], | ||||
|         createdAt: DateTime.parse(json["created_at"]), | ||||
|         updatedAt: DateTime.parse(json["updated_at"]), | ||||
|         deletedAt: json["deleted_at"], | ||||
|         name: json["name"], | ||||
|         nick: json["nick"], | ||||
|         avatar: json["avatar"], | ||||
|         banner: json["banner"], | ||||
|         description: json["description"], | ||||
|         emailAddress: json["email_address"], | ||||
|         powerLevel: json["power_level"], | ||||
|         externalId: json["external_id"], | ||||
|       ); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
|         "id": id, | ||||
|         "created_at": createdAt.toIso8601String(), | ||||
|         "updated_at": updatedAt.toIso8601String(), | ||||
|         "deleted_at": deletedAt, | ||||
|         "name": name, | ||||
|         "nick": nick, | ||||
|         "avatar": avatar, | ||||
|         "banner": banner, | ||||
|         "description": description, | ||||
|         "email_address": emailAddress, | ||||
|         "power_level": powerLevel, | ||||
|         "external_id": externalId, | ||||
|       }; | ||||
| } | ||||
							
								
								
									
										154
									
								
								lib/models/feed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,154 @@ | ||||
| import 'package:solaragent/models/author.dart'; | ||||
|  | ||||
| class Feed { | ||||
|   int id; | ||||
|   DateTime createdAt; | ||||
|   DateTime updatedAt; | ||||
|   DateTime? deletedAt; | ||||
|   String alias; | ||||
|   String title; | ||||
|   String description; | ||||
|   String content; | ||||
|   String modelType; | ||||
|   int commentCount; | ||||
|   int reactionCount; | ||||
|   int authorId; | ||||
|   int? realmId; | ||||
|   Author author; | ||||
|   List<Attachment>? attachments; | ||||
|   Map<String, dynamic>? reactionList; | ||||
|  | ||||
|   Feed({ | ||||
|     required this.id, | ||||
|     required this.createdAt, | ||||
|     required this.updatedAt, | ||||
|     this.deletedAt, | ||||
|     required this.alias, | ||||
|     required this.title, | ||||
|     required this.description, | ||||
|     required this.content, | ||||
|     required this.modelType, | ||||
|     required this.commentCount, | ||||
|     required this.reactionCount, | ||||
|     required this.authorId, | ||||
|     this.realmId, | ||||
|     required this.author, | ||||
|     this.attachments, | ||||
|     this.reactionList, | ||||
|   }); | ||||
|  | ||||
|   factory Feed.fromJson(Map<String, dynamic> json) => Feed( | ||||
|         id: json["id"], | ||||
|         createdAt: DateTime.parse(json["created_at"]), | ||||
|         updatedAt: DateTime.parse(json["updated_at"]), | ||||
|         deletedAt: json["deleted_at"], | ||||
|         alias: json["alias"], | ||||
|         title: json["title"], | ||||
|         description: json["description"], | ||||
|         content: json["content"], | ||||
|         modelType: json["model_type"], | ||||
|         commentCount: json["comment_count"], | ||||
|         reactionCount: json["reaction_count"], | ||||
|         authorId: json["author_id"], | ||||
|         realmId: json["realm_id"], | ||||
|         author: Author.fromJson(json["author"]), | ||||
|         attachments: json["attachments"] != null | ||||
|             ? List<Attachment>.from( | ||||
|                 json["attachments"].map((x) => Attachment.fromJson(x))) | ||||
|             : List.empty(), | ||||
|         reactionList: json["reaction_list"], | ||||
|       ); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
|         "id": id, | ||||
|         "created_at": createdAt.toIso8601String(), | ||||
|         "updated_at": updatedAt.toIso8601String(), | ||||
|         "deleted_at": deletedAt, | ||||
|         "alias": alias, | ||||
|         "title": title, | ||||
|         "description": description, | ||||
|         "content": content, | ||||
|         "model_type": modelType, | ||||
|         "comment_count": commentCount, | ||||
|         "reaction_count": reactionCount, | ||||
|         "author_id": authorId, | ||||
|         "realm_id": realmId, | ||||
|         "author": author.toJson(), | ||||
|         "attachments": attachments == null | ||||
|             ? List.empty() | ||||
|             : List<dynamic>.from(attachments!.map((x) => x.toJson())), | ||||
|         "reaction_list": reactionList, | ||||
|       }; | ||||
| } | ||||
|  | ||||
| class Attachment { | ||||
|   int id; | ||||
|   DateTime createdAt; | ||||
|   DateTime updatedAt; | ||||
|   DateTime? deletedAt; | ||||
|   String fileId; | ||||
|   int filesize; | ||||
|   String filename; | ||||
|   String mimetype; | ||||
|   int type; | ||||
|   String externalUrl; | ||||
|   Author author; | ||||
|   int? articleId; | ||||
|   int? momentId; | ||||
|   int? commentId; | ||||
|   int? authorId; | ||||
|  | ||||
|   Attachment({ | ||||
|     required this.id, | ||||
|     required this.createdAt, | ||||
|     required this.updatedAt, | ||||
|     this.deletedAt, | ||||
|     required this.fileId, | ||||
|     required this.filesize, | ||||
|     required this.filename, | ||||
|     required this.mimetype, | ||||
|     required this.type, | ||||
|     required this.externalUrl, | ||||
|     required this.author, | ||||
|     this.articleId, | ||||
|     this.momentId, | ||||
|     this.commentId, | ||||
|     this.authorId, | ||||
|   }); | ||||
|  | ||||
|   factory Attachment.fromJson(Map<String, dynamic> json) => Attachment( | ||||
|         id: json["id"], | ||||
|         createdAt: DateTime.parse(json["created_at"]), | ||||
|         updatedAt: DateTime.parse(json["updated_at"]), | ||||
|         deletedAt: json["deleted_at"], | ||||
|         fileId: json["file_id"], | ||||
|         filesize: json["filesize"], | ||||
|         filename: json["filename"], | ||||
|         mimetype: json["mimetype"], | ||||
|         type: json["type"], | ||||
|         externalUrl: json["external_url"], | ||||
|         author: Author.fromJson(json["author"]), | ||||
|         articleId: json["article_id"], | ||||
|         momentId: json["moment_id"], | ||||
|         commentId: json["comment_id"], | ||||
|         authorId: json["author_id"], | ||||
|       ); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
|         "id": id, | ||||
|         "created_at": createdAt.toIso8601String(), | ||||
|         "updated_at": updatedAt.toIso8601String(), | ||||
|         "deleted_at": deletedAt, | ||||
|         "file_id": fileId, | ||||
|         "filesize": filesize, | ||||
|         "filename": filename, | ||||
|         "mimetype": mimetype, | ||||
|         "type": type, | ||||
|         "external_url": externalUrl, | ||||
|         "author": author.toJson(), | ||||
|         "article_id": articleId, | ||||
|         "moment_id": momentId, | ||||
|         "comment_id": commentId, | ||||
|         "author_id": authorId, | ||||
|       }; | ||||
| } | ||||
							
								
								
									
										79
									
								
								lib/models/notification.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,79 @@ | ||||
| class Notification { | ||||
|   int id; | ||||
|   DateTime createdAt; | ||||
|   DateTime updatedAt; | ||||
|   DateTime? deletedAt; | ||||
|   String subject; | ||||
|   String content; | ||||
|   List<Link>? links; | ||||
|   bool isImportant; | ||||
|   DateTime? readAt; | ||||
|   int senderId; | ||||
|   int recipientId; | ||||
|  | ||||
|   Notification({ | ||||
|     required this.id, | ||||
|     required this.createdAt, | ||||
|     required this.updatedAt, | ||||
|     this.deletedAt, | ||||
|     required this.subject, | ||||
|     required this.content, | ||||
|     this.links, | ||||
|     required this.isImportant, | ||||
|     this.readAt, | ||||
|     required this.senderId, | ||||
|     required this.recipientId, | ||||
|   }); | ||||
|  | ||||
|   factory Notification.fromJson(Map<String, dynamic> json) => Notification( | ||||
|         id: json["id"], | ||||
|         createdAt: DateTime.parse(json["created_at"]), | ||||
|         updatedAt: DateTime.parse(json["updated_at"]), | ||||
|         deletedAt: json["deleted_at"], | ||||
|         subject: json["subject"], | ||||
|         content: json["content"], | ||||
|         links: json["links"] != null | ||||
|             ? List<Link>.from(json["links"].map((x) => Link.fromJson(x))) | ||||
|             : List.empty(), | ||||
|         isImportant: json["is_important"], | ||||
|         readAt: json["read_at"], | ||||
|         senderId: json["sender_id"], | ||||
|         recipientId: json["recipient_id"], | ||||
|       ); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
|         "id": id, | ||||
|         "created_at": createdAt.toIso8601String(), | ||||
|         "updated_at": updatedAt.toIso8601String(), | ||||
|         "deleted_at": deletedAt, | ||||
|         "subject": subject, | ||||
|         "content": content, | ||||
|         "links": links != null | ||||
|             ? List<dynamic>.from(links!.map((x) => x.toJson())) | ||||
|             : List.empty(), | ||||
|         "is_important": isImportant, | ||||
|         "read_at": readAt, | ||||
|         "sender_id": senderId, | ||||
|         "recipient_id": recipientId, | ||||
|       }; | ||||
| } | ||||
|  | ||||
| class Link { | ||||
|   String label; | ||||
|   String url; | ||||
|  | ||||
|   Link({ | ||||
|     required this.label, | ||||
|     required this.url, | ||||
|   }); | ||||
|  | ||||
|   factory Link.fromJson(Map<String, dynamic> json) => Link( | ||||
|         label: json["label"], | ||||
|         url: json["url"], | ||||
|       ); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
|         "label": label, | ||||
|         "url": url, | ||||
|       }; | ||||
| } | ||||
							
								
								
									
										17
									
								
								lib/models/pagination.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,17 @@ | ||||
| class PaginationResult { | ||||
|   int count; | ||||
|   List<dynamic>? data; | ||||
|  | ||||
|   PaginationResult({ | ||||
|     required this.count, | ||||
|     this.data, | ||||
|   }); | ||||
|  | ||||
|   factory PaginationResult.fromJson(Map<String, dynamic> json) => | ||||
|       PaginationResult(count: json["count"], data: json["data"]); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
|         "count": count, | ||||
|         "data": data, | ||||
|       }; | ||||
| } | ||||
							
								
								
									
										5
									
								
								lib/preferences.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| getPreferences() async { | ||||
|   return await SharedPreferences.getInstance(); | ||||
| } | ||||
							
								
								
									
										43
									
								
								lib/router.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,43 @@ | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
| import 'package:solaragent/screens/account.dart'; | ||||
| import 'package:solaragent/screens/explore.dart'; | ||||
| import 'package:solaragent/screens/notifications.dart'; | ||||
| import 'package:solaragent/screens/posts/screen.dart'; | ||||
| import 'package:solaragent/screens/publish/comment_editor.dart'; | ||||
| import 'package:solaragent/screens/publish/moment_editor.dart'; | ||||
|  | ||||
| final router = GoRouter( | ||||
|   routes: [ | ||||
|     GoRoute( | ||||
|       path: '/', | ||||
|       builder: (context, state) => const ExploreScreen(), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/notifications', | ||||
|       builder: (context, state) => const NotificationScreen(), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/account', | ||||
|       builder: (context, state) => const AccountScreen(), | ||||
|     ), | ||||
|  | ||||
|     GoRoute( | ||||
|       path: '/post/new/moments', | ||||
|       builder: (context, state) => const MomentEditorScreen(), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/post/new/comments', | ||||
|       builder: (context, state) => | ||||
|           CommentEditorScreen(parent: state.extra as Feed), | ||||
|     ), | ||||
|  | ||||
|     GoRoute( | ||||
|       path: '/post/:modelType/:alias', | ||||
|       builder: (context, state) => PostScreen( | ||||
|         modelType: state.pathParameters['modelType'] as String, | ||||
|         alias: state.pathParameters['alias'] as String, | ||||
|       ), | ||||
|     ), | ||||
|   ], | ||||
| ); | ||||
| @@ -16,10 +16,10 @@ class AboutScreen extends StatelessWidget { | ||||
|           mainAxisAlignment: MainAxisAlignment.center, | ||||
|           crossAxisAlignment: CrossAxisAlignment.center, | ||||
|           children: [ | ||||
|             Text('GoatAgent', | ||||
|             Text('SolarAgent', | ||||
|                 style: Theme.of(context).textTheme.headlineMedium), | ||||
|             Text('Goatworks Official Mobile Helper', | ||||
|                 style: Theme.of(context).textTheme.bodyLarge), | ||||
|             Text('Solar Networks Official Mobile Application', | ||||
|                 style: Theme.of(context).textTheme.bodyMedium), | ||||
|             const SizedBox(height: 20), | ||||
|             FutureBuilder( | ||||
|               future: PackageInfo.fromPlatform(), | ||||
| @@ -37,7 +37,7 @@ class AboutScreen extends StatelessWidget { | ||||
|             const SizedBox(height: 10), | ||||
|             MaterialButton( | ||||
|               onPressed: () async { | ||||
|                 await launchUrl(Uri.parse('https://smartsheep.studio')); | ||||
|                 await launchUrl(Uri.parse('https://solsynth.dev')); | ||||
|               }, | ||||
|               child: const Text('Official Website'), | ||||
|             ), | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:goatagent/auth.dart'; | ||||
| import 'package:goatagent/screens/about.dart'; | ||||
| import 'package:goatagent/widgets/name_card.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/screens/about.dart'; | ||||
| import 'package:solaragent/widgets/name_card.dart'; | ||||
|  | ||||
| class AccountScreen extends StatefulWidget { | ||||
|   const AccountScreen({super.key}); | ||||
| @@ -17,7 +16,7 @@ class _AccountScreenState extends State<AccountScreen> { | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     AuthGuard().isAuthorized().then((value) { | ||||
|     authClient.isAuthorized().then((value) { | ||||
|       setState(() { | ||||
|         isAuthorized = value; | ||||
|       }); | ||||
| @@ -27,38 +26,38 @@ class _AccountScreenState extends State<AccountScreen> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       body: SafeArea( | ||||
|         child: Padding( | ||||
|           padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20), | ||||
|           child: Column(children: [ | ||||
|             Padding( | ||||
|               padding: const EdgeInsets.only(top: 20), | ||||
|               child: NameCard( | ||||
|                 onLogin: () async { | ||||
|                   await AuthGuard().login(context); | ||||
|                   var authorized = await AuthGuard().isAuthorized(); | ||||
|                   setState(() { | ||||
|                     isAuthorized = authorized; | ||||
|                   }); | ||||
|                 }, | ||||
|               ), | ||||
|       body: Padding( | ||||
|         padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20), | ||||
|         child: Column(children: [ | ||||
|           Padding( | ||||
|             padding: const EdgeInsets.only(top: 20), | ||||
|             child: NameCard( | ||||
|               onLogin: () async { | ||||
|                 await authClient.signin(context); | ||||
|                 var authorized = await authClient.isAuthorized(); | ||||
|                 setState(() { | ||||
|                   isAuthorized = authorized; | ||||
|                 }); | ||||
|               }, | ||||
|             ), | ||||
|             Padding( | ||||
|               padding: const EdgeInsets.only(top: 5), | ||||
|               child: Wrap( | ||||
|                 spacing: 5, | ||||
|                 children: [ | ||||
|                   FutureBuilder( | ||||
|                     future: AuthGuard().isAuthorized(), | ||||
|                     builder: (BuildContext context, AsyncSnapshot<bool> snapshot) { | ||||
|                       if (snapshot.hasData && snapshot.data == true) { | ||||
|                         return Card( | ||||
|                           elevation: 0, | ||||
|                           child: InkWell( | ||||
|           ), | ||||
|           Padding( | ||||
|             padding: const EdgeInsets.only(top: 5), | ||||
|             child: Wrap( | ||||
|               spacing: 5, | ||||
|               children: [ | ||||
|                 FutureBuilder( | ||||
|                   future: authClient.isAuthorized(), | ||||
|                   builder: | ||||
|                       (BuildContext context, AsyncSnapshot<bool> snapshot) { | ||||
|                     return (snapshot.hasData && snapshot.data == true) | ||||
|                         ? InkWell( | ||||
|                             borderRadius: | ||||
|                                 const BorderRadius.all(Radius.circular(40)), | ||||
|                             splashColor: Colors.indigo.withAlpha(30), | ||||
|                             onTap: () async { | ||||
|                               AuthGuard().logout(); | ||||
|                               var authorized = await AuthGuard().isAuthorized(); | ||||
|                               authClient.signoff(); | ||||
|                               var authorized = await authClient.isAuthorized(); | ||||
|                               setState(() { | ||||
|                                 isAuthorized = authorized; | ||||
|                               }); | ||||
| @@ -67,33 +66,29 @@ class _AccountScreenState extends State<AccountScreen> { | ||||
|                               leading: Icon(Icons.logout), | ||||
|                               title: Text('Logout'), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ); | ||||
|                       } else { | ||||
|                         return Container(); | ||||
|                       } | ||||
|                     }, | ||||
|                   ), | ||||
|                   Card( | ||||
|                     elevation: 0, | ||||
|                     child: InkWell( | ||||
|                       splashColor: Colors.indigo.withAlpha(30), | ||||
|                       onTap: () { | ||||
|                         Navigator.push(context, MaterialPageRoute( | ||||
|                           ) | ||||
|                         : Container(); | ||||
|                   }, | ||||
|                 ), | ||||
|                 InkWell( | ||||
|                   borderRadius: const BorderRadius.all(Radius.circular(40)), | ||||
|                   splashColor: Colors.indigo.withAlpha(30), | ||||
|                   onTap: () { | ||||
|                     Navigator.push( | ||||
|                         context, | ||||
|                         MaterialPageRoute( | ||||
|                           builder: (context) => const AboutScreen(), | ||||
|                         )); | ||||
|                       }, | ||||
|                       child: const ListTile( | ||||
|                         leading: Icon(Icons.info_outline), | ||||
|                         title: Text('About'), | ||||
|                       ), | ||||
|                     ), | ||||
|                   }, | ||||
|                   child: const ListTile( | ||||
|                     leading: Icon(Icons.info_outline), | ||||
|                     title: Text('About'), | ||||
|                   ), | ||||
|                 ], | ||||
|               ), | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ]), | ||||
|         ), | ||||
|           ), | ||||
|         ]), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -1,32 +0,0 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:webview_flutter/webview_flutter.dart'; | ||||
|  | ||||
| class ApplicationScreen extends StatelessWidget { | ||||
|   final Uri link; | ||||
|   final String title; | ||||
|  | ||||
|   const ApplicationScreen({super.key, required this.link, required this.title}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     ScaffoldMessenger.of(context).showSnackBar(const SnackBar( | ||||
|       content: Text("Swipe from left to back to dashboard."), | ||||
|     )); | ||||
|  | ||||
|     return Scaffold( | ||||
|       body: SafeArea( | ||||
|         child: Stack( | ||||
|           children: [ | ||||
|             WebViewWidget( | ||||
|               controller: WebViewController() | ||||
|                 ..setJavaScriptMode(JavaScriptMode.unrestricted) | ||||
|                 ..setBackgroundColor(Colors.white) | ||||
|                 ..loadRequest(link) | ||||
|                 ..clearCache(), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -11,7 +11,7 @@ class AuthorizationScreen extends StatelessWidget { | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: const Text('Connect with Goatpass'), | ||||
|         title: const Text('Sign in'), | ||||
|       ), | ||||
|       body: Stack(children: [ | ||||
|         WebViewWidget( | ||||
| @@ -20,10 +20,10 @@ class AuthorizationScreen extends StatelessWidget { | ||||
|             ..setBackgroundColor(Colors.white) | ||||
|             ..setNavigationDelegate(NavigationDelegate( | ||||
|               onNavigationRequest: (NavigationRequest request) { | ||||
|                 if (request.url.startsWith('goatagent://auth')) { | ||||
|                 if (request.url.startsWith('solaragent://auth')) { | ||||
|                   Navigator.of(context).pop(request.url); | ||||
|                   return NavigationDecision.prevent; | ||||
|                 } else if (request.url.startsWith("https://id.smartsheep.studio/auth/register")) { | ||||
|                 } else if (request.url.startsWith("https://solsynth.dev/auth/sign-up")) { | ||||
|                   launchUrl(Uri.parse(request.url)); | ||||
|                   return NavigationDecision.prevent; | ||||
|                 } | ||||
|   | ||||
| @@ -1,90 +0,0 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:goatagent/screens/application.dart'; | ||||
| import 'package:http/http.dart' as http; | ||||
|  | ||||
| class DashboardScreen extends StatefulWidget { | ||||
|   const DashboardScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<DashboardScreen> createState() => _DashboardScreenState(); | ||||
| } | ||||
|  | ||||
| class _DashboardScreenState extends State<DashboardScreen> { | ||||
|   final client = http.Client(); | ||||
|   final directoryEndpoint = | ||||
|       Uri.parse('https://id.smartsheep.studio/.well-known'); | ||||
|  | ||||
|   List<dynamic> directory = List.empty(); | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     _pullDirectory(); | ||||
|   } | ||||
|  | ||||
|   Future<void> _pullDirectory() async { | ||||
|     var response = await client.get(directoryEndpoint); | ||||
|     if (response.statusCode == 200) { | ||||
|       setState(() { | ||||
|         directory = jsonDecode(utf8.decode(response.bodyBytes))["directory"]; | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       body: SafeArea( | ||||
|         child: Padding( | ||||
|           padding: const EdgeInsets.only(left: 20, right: 20, top: 30), | ||||
|           child: GridView.count( | ||||
|             crossAxisCount: 2, | ||||
|             children: directory.map((element) { | ||||
|               return Card( | ||||
|                 child: InkWell( | ||||
|                   onTap: () async { | ||||
|                     var link = element["link"]; | ||||
|                     Navigator.of(context, rootNavigator: true) | ||||
|                         .push(MaterialPageRoute( | ||||
|                       builder: (context) => ApplicationScreen( | ||||
|                         link: Uri.parse(link), | ||||
|                         title: element["name"], | ||||
|                       ), | ||||
|                     )); | ||||
|                   }, | ||||
|                   splashColor: Colors.indigo.withAlpha(30), | ||||
|                   child: Padding( | ||||
|                     padding: const EdgeInsets.all(10), | ||||
|                     child: Wrap( | ||||
|                       spacing: 8.0, | ||||
|                       children: [ | ||||
|                         Padding( | ||||
|                           padding: const EdgeInsets.only(left: 6, top: 2), | ||||
|                           child: Card( | ||||
|                             shape: const CircleBorder(), | ||||
|                             elevation: 0, | ||||
|                             color: Colors.indigo.withAlpha(70), | ||||
|                             child: const Padding( | ||||
|                               padding: EdgeInsets.all(16), | ||||
|                               child: Icon(Icons.apps), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ), | ||||
|                         ListTile( | ||||
|                           title: Text(element['name']), | ||||
|                           subtitle: Text(element['description']), | ||||
|                         ), | ||||
|                       ], | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ); | ||||
|             }).toList(), | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										103
									
								
								lib/screens/explore.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,103 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
| import 'package:solaragent/models/pagination.dart'; | ||||
| import 'package:http/http.dart' as http; | ||||
| import 'package:solaragent/router.dart'; | ||||
| import 'package:solaragent/widgets/feed.dart'; | ||||
|  | ||||
| class ExploreScreen extends StatefulWidget { | ||||
|   const ExploreScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<ExploreScreen> createState() => _ExploreScreenState(); | ||||
| } | ||||
|  | ||||
| class _ExploreScreenState extends State<ExploreScreen> { | ||||
|   static const pageSize = 5; | ||||
|  | ||||
|   final client = http.Client(); | ||||
|  | ||||
|   final PagingController<int, Feed> paginationController = | ||||
|       PagingController(firstPageKey: 0); | ||||
|  | ||||
|   List<Feed> feed = List.empty(); | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     paginationController.addPageRequestListener((pageKey) { | ||||
|       pullFeed(pageKey); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   Future<void> pullFeed(int pageKey) async { | ||||
|     var offset = pageKey; | ||||
|     var take = pageSize; | ||||
|  | ||||
|     var uri = | ||||
|         Uri.parse('https://co.solsynth.dev/api/feed?take=$take&offset=$offset'); | ||||
|  | ||||
|     var res = await client.get(uri); | ||||
|     if (res.statusCode == 200) { | ||||
|       final result = | ||||
|           PaginationResult.fromJson(jsonDecode(utf8.decode(res.bodyBytes))); | ||||
|       final isLastPage = (result.count - pageKey) < pageSize; | ||||
|       final items = | ||||
|           result.data?.map((x) => Feed.fromJson(x)).toList() ?? List.empty(); | ||||
|       if (isLastPage || result.data == null) { | ||||
|         paginationController.appendLastPage(items); | ||||
|       } else { | ||||
|         final nextPageKey = pageKey + items.length; | ||||
|         paginationController.appendPage(items, nextPageKey); | ||||
|       } | ||||
|     } else { | ||||
|       paginationController.error = utf8.decode(res.bodyBytes); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       body: RefreshIndicator( | ||||
|         onRefresh: () async { | ||||
|           paginationController.refresh(); | ||||
|         }, | ||||
|         child: PagedListView<int, Feed>( | ||||
|           pagingController: paginationController, | ||||
|           builderDelegate: PagedChildBuilderDelegate<Feed>( | ||||
|             itemBuilder: (context, item, index) => FeedItem( | ||||
|               item: item, | ||||
|               brief: true, | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|       floatingActionButton: FutureBuilder( | ||||
|           future: authClient.isAuthorized(), | ||||
|           builder: (context, snapshot) { | ||||
|             if (snapshot.hasData && snapshot.data == true) { | ||||
|               return FloatingActionButton( | ||||
|                 child: const Icon(Icons.edit), | ||||
|                 onPressed: () { | ||||
|                   router.push("/post/new/moments").then((value) { | ||||
|                     if (value == true) paginationController.refresh(); | ||||
|                   }); | ||||
|                 }, | ||||
|               ); | ||||
|             } else { | ||||
|               return Container(); | ||||
|             } | ||||
|           }), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     paginationController.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| } | ||||
| @@ -1,7 +1,10 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:goatagent/auth.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/models/notification.dart' as model; | ||||
| import 'package:solaragent/models/pagination.dart'; | ||||
| import 'package:solaragent/widgets/notification.dart'; | ||||
|  | ||||
| class NotificationScreen extends StatefulWidget { | ||||
|   const NotificationScreen({super.key}); | ||||
| @@ -11,84 +14,90 @@ class NotificationScreen extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _NotificationScreenState extends State<NotificationScreen> { | ||||
|   final notificationEndpoint = Uri.parse( | ||||
|       'https://id.smartsheep.studio/api/notifications?skip=0&take=20'); | ||||
|  | ||||
|   List<dynamic> notifications = List.empty(); | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     _pullNotifications(); | ||||
|     pullNotifications(); | ||||
|   } | ||||
|  | ||||
|   Future<void> _pullNotifications() async { | ||||
|     if (await AuthGuard().isAuthorized()) { | ||||
|       await AuthGuard().pullProfiles(); | ||||
|       var profiles = await AuthGuard().readProfiles(); | ||||
|       setState(() { | ||||
|         notifications = profiles['notifications']; | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<void> _markAsRead(element) async { | ||||
|     if (AuthGuard().client != null) { | ||||
|       var id = element['id']; | ||||
|   Future<void> pullNotifications() async { | ||||
|     if (await authClient.isAuthorized()) { | ||||
|       var uri = | ||||
|           Uri.parse('https://id.smartsheep.studio/api/notifications/$id/read'); | ||||
|       await AuthGuard().client!.put(uri); | ||||
|           Uri.parse('https://id.solsynth.dev/api/notifications?skip=0&take=25'); | ||||
|       var res = await authClient.client!.get(uri); | ||||
|       if (res.statusCode == 200) { | ||||
|         final result = | ||||
|             PaginationResult.fromJson(jsonDecode(utf8.decode(res.bodyBytes))); | ||||
|         setState(() { | ||||
|           notifications = | ||||
|               result.data?.map((x) => model.Notification.fromJson(x)).toList() ?? | ||||
|                   List.empty(); | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       body: SafeArea( | ||||
|         child: Padding( | ||||
|           padding: const EdgeInsets.only(left: 20, right: 20, top: 30), | ||||
|           child: RefreshIndicator( | ||||
|             onRefresh: _pullNotifications, | ||||
|             child: CustomScrollView( | ||||
|               slivers: [ | ||||
|                 notifications.isEmpty | ||||
|                     ? const SliverToBoxAdapter( | ||||
|                         child: Card( | ||||
|                           child: Padding( | ||||
|                             padding: EdgeInsets.all(10), | ||||
|                             child: ListTile( | ||||
|                               leading: Icon(Icons.check), | ||||
|                               title: Text('You\'re done!'), | ||||
|                               subtitle: Text( | ||||
|                                   'There are no notifications unread for you.'), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ), | ||||
|                       ) | ||||
|                     : SliverList.builder( | ||||
|                         itemCount: notifications.length, | ||||
|                         itemBuilder: (BuildContext context, int index) { | ||||
|                           var element = notifications[index]; | ||||
|                           return Dismissible( | ||||
|                               key: Key('notification-$index'), | ||||
|                               onDismissed: (direction) { | ||||
|                                 var subject = element["subject"]; | ||||
|                                 _markAsRead(element).then((value) { | ||||
|                                   ScaffoldMessenger.of(context).showSnackBar( | ||||
|                                       SnackBar( | ||||
|                                           content: | ||||
|                                               Text('「$subject」 mark as read'))); | ||||
|                                 }); | ||||
|                               }, | ||||
|                               child: ListTile( | ||||
|                                 title: Text(element["subject"]), | ||||
|                                 subtitle: Text(element["content"]), | ||||
|                               )); | ||||
|                         }, | ||||
|                       ), | ||||
|               ], | ||||
|       body: RefreshIndicator( | ||||
|         onRefresh: pullNotifications, | ||||
|         child: CustomScrollView( | ||||
|           slivers: [ | ||||
|             // Title | ||||
|             SliverToBoxAdapter( | ||||
|               child: Padding( | ||||
|                 padding: const EdgeInsets.only(left: 10, right: 10, top: 20), | ||||
|                 child: ListTile( | ||||
|                   title: Text( | ||||
|                     'Notifications', | ||||
|                     style: Theme.of(context).textTheme.headlineSmall, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|             // Content | ||||
|             notifications.isEmpty | ||||
|                 ? SliverToBoxAdapter( | ||||
|                     child: Container( | ||||
|                       padding: const EdgeInsets.symmetric(horizontal: 10), | ||||
|                       color: Colors.grey[50], | ||||
|                       child: const ListTile( | ||||
|                         leading: Icon(Icons.check), | ||||
|                         title: Text('You\'re done!'), | ||||
|                         subtitle: Text( | ||||
|                           'There are no notifications unread for you.', | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                   ) | ||||
|                 : SliverList.builder( | ||||
|                     itemCount: notifications.length, | ||||
|                     itemBuilder: (BuildContext context, int index) { | ||||
|                       var element = notifications[index]; | ||||
|                       return NotificationItem( | ||||
|                         index: index, | ||||
|                         item: element, | ||||
|                         onDismiss: () => setState(() { | ||||
|                           notifications.removeAt(index); | ||||
|                         }), | ||||
|                       ); | ||||
|                     }, | ||||
|                   ), | ||||
|             // Tips | ||||
|             SliverToBoxAdapter( | ||||
|               child: Container( | ||||
|                 padding: const EdgeInsets.only(top: 12), | ||||
|                 child: Text( | ||||
|                   "Pull to refresh, swipe to dismiss", | ||||
|                   textAlign: TextAlign.center, | ||||
|                   style: Theme.of(context).textTheme.bodySmall, | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   | ||||
							
								
								
									
										64
									
								
								lib/screens/posts/screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,64 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
| import 'package:solaragent/widgets/feed.dart'; | ||||
|  | ||||
| class PostScreen extends StatefulWidget { | ||||
|   final Client client = Client(); | ||||
|  | ||||
|   final String modelType; | ||||
|   final String alias; | ||||
|  | ||||
|   PostScreen({super.key, required this.modelType, required this.alias}); | ||||
|  | ||||
|   @override | ||||
|   State<PostScreen> createState() => _PostScreenState(); | ||||
| } | ||||
|  | ||||
| class _PostScreenState extends State<PostScreen> { | ||||
|   Future<Feed?> pullPost(BuildContext context) async { | ||||
|     var uri = Uri.parse( | ||||
|       "https://co.solsynth.dev/api/p/${widget.modelType}s/${widget.alias}", | ||||
|     ); | ||||
|     var res = await widget.client.get(uri); | ||||
|     if (res.statusCode != 200) { | ||||
|       var err = utf8.decode(res.bodyBytes); | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         SnackBar(content: Text("Something went wrong... $err")), | ||||
|       ); | ||||
|       return null; | ||||
|     } else { | ||||
|       return Feed.fromJson(jsonDecode(utf8.decode(res.bodyBytes))); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Widget buildItem(BuildContext context, Feed item) { | ||||
|     return FeedItem( | ||||
|       item: item, | ||||
|       brief: false, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar(title: const Text("Post")), | ||||
|       body: FutureBuilder( | ||||
|         future: pullPost(context), | ||||
|         builder: (context, snapshot) { | ||||
|           if (snapshot.hasData && snapshot.data != null) { | ||||
|             return SingleChildScrollView( | ||||
|               child: buildItem(context, snapshot.data!), | ||||
|             ); | ||||
|           } else { | ||||
|             return const Center( | ||||
|               child: CircularProgressIndicator(), | ||||
|             ); | ||||
|           } | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										212
									
								
								lib/screens/publish/attachment_list.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,212 @@ | ||||
| import 'dart:convert'; | ||||
| import 'dart:io'; | ||||
| import 'dart:math' as math; | ||||
|  | ||||
| import 'package:crypto/crypto.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:image_picker/image_picker.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
|  | ||||
| class AttachmentList extends StatefulWidget { | ||||
|   final List<Attachment> current; | ||||
|   final void Function(List<Attachment> data) onUpdate; | ||||
|  | ||||
|   const AttachmentList( | ||||
|       {super.key, required this.current, required this.onUpdate}); | ||||
|  | ||||
|   @override | ||||
|   State<AttachmentList> createState() => _AttachmentListState(); | ||||
| } | ||||
|  | ||||
| class _AttachmentListState extends State<AttachmentList> { | ||||
|   final imagePicker = ImagePicker(); | ||||
|  | ||||
|   bool isSubmitting = false; | ||||
|  | ||||
|   List<Attachment> attachments = List.empty(growable: true); | ||||
|  | ||||
|   Future<void> pickImageToUpload(BuildContext context) async { | ||||
|     if (!await authClient.isAuthorized()) return; | ||||
|  | ||||
|     final image = await imagePicker.pickImage(source: ImageSource.gallery); | ||||
|     if (image == null) return; | ||||
|  | ||||
|     final file = File(image.path); | ||||
|     final hashcode = await calculateSha256(file); | ||||
|  | ||||
|     try { | ||||
|       await uploadAttachment(file, hashcode); | ||||
|     } catch (err) { | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         SnackBar(content: Text("Something went wrong... $err")), | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<void> uploadAttachment(File file, String hashcode) async { | ||||
|     var req = MultipartRequest( | ||||
|       'POST', | ||||
|       Uri.parse('https://co.solsynth.dev/api/attachments'), | ||||
|     ); | ||||
|     req.files.add(await MultipartFile.fromPath('attachment', file.path)); | ||||
|     req.fields['hashcode'] = hashcode; | ||||
|  | ||||
|     setState(() => isSubmitting = true); | ||||
|     var res = await authClient.client!.send(req); | ||||
|     if (res.statusCode == 200) { | ||||
|       var result = Attachment.fromJson( | ||||
|         jsonDecode(utf8.decode(await res.stream.toBytes()))["info"], | ||||
|       ); | ||||
|       setState(() => attachments.add(result)); | ||||
|       widget.onUpdate(attachments); | ||||
|     } else { | ||||
|       throw Exception(utf8.decode(await res.stream.toBytes())); | ||||
|     } | ||||
|     setState(() => isSubmitting = false); | ||||
|   } | ||||
|  | ||||
|   Future<void> disposeAttachment( | ||||
|       BuildContext context, Attachment item, int index) async { | ||||
|     var req = MultipartRequest( | ||||
|       'DELETE', | ||||
|       Uri.parse('https://co.solsynth.dev/api/attachments/${item.id}'), | ||||
|     ); | ||||
|  | ||||
|     setState(() => isSubmitting = true); | ||||
|     var res = await authClient.client!.send(req); | ||||
|     if (res.statusCode == 200) { | ||||
|       setState(() => attachments.removeAt(index)); | ||||
|       widget.onUpdate(attachments); | ||||
|     } else { | ||||
|       final err = utf8.decode(await res.stream.toBytes()); | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         SnackBar(content: Text("Something went wrong... $err")), | ||||
|       ); | ||||
|     } | ||||
|     setState(() => isSubmitting = false); | ||||
|   } | ||||
|  | ||||
|   Future<String> calculateSha256(File file) async { | ||||
|     final bytes = await file.readAsBytes(); | ||||
|     final digest = sha256.convert(bytes); | ||||
|     return digest.toString(); | ||||
|   } | ||||
|  | ||||
|   String getFileName(Attachment item) { | ||||
|     return item.filename.replaceAll(RegExp(r'\.[^/.]+$'), ''); | ||||
|   } | ||||
|  | ||||
|   String getFileType(Attachment item) { | ||||
|     switch (item.type) { | ||||
|       case 1: | ||||
|         return 'Photo'; | ||||
|       case 2: | ||||
|         return 'Video'; | ||||
|       case 3: | ||||
|         return 'Audio'; | ||||
|       default: | ||||
|         return 'Others'; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   String formatBytes(int bytes, {int decimals = 2}) { | ||||
|     if (bytes == 0) return '0 Bytes'; | ||||
|     const k = 1024; | ||||
|     final dm = decimals < 0 ? 0 : decimals; | ||||
|     final sizes = [ | ||||
|       'Bytes', | ||||
|       'KiB', | ||||
|       'MiB', | ||||
|       'GiB', | ||||
|       'TiB', | ||||
|       'PiB', | ||||
|       'EiB', | ||||
|       'ZiB', | ||||
|       'YiB' | ||||
|     ]; | ||||
|     final i = (math.log(bytes) / math.log(k)).floor().toInt(); | ||||
|     return '${(bytes / math.pow(k, i)).toStringAsFixed(dm)} ${sizes[i]}'; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     attachments = widget.current; | ||||
|     super.initState(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Column( | ||||
|       children: [ | ||||
|         Container( | ||||
|           padding: const EdgeInsets.only(left: 10, right: 10, top: 20), | ||||
|           child: Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|             children: [ | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric( | ||||
|                   horizontal: 8.0, | ||||
|                   vertical: 12.0, | ||||
|                 ), | ||||
|                 child: Text( | ||||
|                   'Attachments', | ||||
|                   style: Theme.of(context).textTheme.headlineSmall, | ||||
|                 ), | ||||
|               ), | ||||
|               FutureBuilder( | ||||
|                 future: authClient.isAuthorized(), | ||||
|                 builder: (context, snapshot) { | ||||
|                   if (snapshot.hasData && snapshot.data == true) { | ||||
|                     return Tooltip( | ||||
|                       message: "Add a photo", | ||||
|                       child: TextButton( | ||||
|                         onPressed: isSubmitting | ||||
|                             ? null | ||||
|                             : () => pickImageToUpload(context), | ||||
|                         style: TextButton.styleFrom( | ||||
|                           shape: const CircleBorder(), | ||||
|                         ), | ||||
|                         child: const Icon(Icons.add_photo_alternate), | ||||
|                       ), | ||||
|                     ); | ||||
|                   } else { | ||||
|                     return Container(); | ||||
|                   } | ||||
|                 }, | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|         isSubmitting ? const LinearProgressIndicator() : Container(), | ||||
|         Expanded( | ||||
|           child: ListView.separated( | ||||
|             itemCount: attachments.length, | ||||
|             itemBuilder: (context, index) { | ||||
|               var element = attachments[index]; | ||||
|               return Container( | ||||
|                 padding: const EdgeInsets.only(left: 8.0), | ||||
|                 child: ListTile( | ||||
|                   title: Text(getFileName(element)), | ||||
|                   subtitle: Text( | ||||
|                     "${getFileType(element)} · ${formatBytes(element.filesize)}", | ||||
|                   ), | ||||
|                   trailing: TextButton( | ||||
|                     style: TextButton.styleFrom( | ||||
|                       shape: const CircleBorder(), | ||||
|                       foregroundColor: Colors.red, | ||||
|                     ), | ||||
|                     child: const Icon(Icons.delete), | ||||
|                     onPressed: () => disposeAttachment(context, element, index), | ||||
|                   ), | ||||
|                 ), | ||||
|               ); | ||||
|             }, | ||||
|             separatorBuilder: (context, index) => const Divider(), | ||||
|           ), | ||||
|         ), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										169
									
								
								lib/screens/publish/comment_editor.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,169 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
| import 'package:solaragent/router.dart'; | ||||
| import 'package:url_launcher/url_launcher.dart'; | ||||
|  | ||||
| class CommentEditorScreen extends StatefulWidget { | ||||
|   final Feed parent; | ||||
|  | ||||
|   const CommentEditorScreen({super.key, required this.parent}); | ||||
|  | ||||
|   @override | ||||
|   State<CommentEditorScreen> createState() => _CommentEditorScreenState(); | ||||
| } | ||||
|  | ||||
| class _CommentEditorScreenState extends State<CommentEditorScreen> { | ||||
|   final contentController = TextEditingController(); | ||||
|  | ||||
|   bool isSubmitting = false; | ||||
|  | ||||
|   bool showRecommendationBanner = true; | ||||
|  | ||||
|   Future<void> postComment() async { | ||||
|     if (!await authClient.isAuthorized()) return; | ||||
|  | ||||
|     var dataset = "${widget.parent.modelType}s"; | ||||
|     var alias = widget.parent.alias; | ||||
|  | ||||
|     var uri = Uri.parse( | ||||
|       "https://co.solsynth.dev/api/p/$dataset/$alias/comments", | ||||
|     ); | ||||
|  | ||||
|     setState(() => isSubmitting = true); | ||||
|     var res = await authClient.client!.post( | ||||
|       uri, | ||||
|       headers: <String, String>{ | ||||
|         'Content-Type': 'application/json', | ||||
|       }, | ||||
|       body: jsonEncode(<String, dynamic>{ | ||||
|         'content': contentController.value.text, | ||||
|       }), | ||||
|     ); | ||||
|     if (res.statusCode != 200) { | ||||
|       var message = utf8.decode(res.bodyBytes); | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         SnackBar(content: Text("Something went wrong... $message")), | ||||
|       ); | ||||
|     } else { | ||||
|       if (router.canPop()) { | ||||
|         router.pop(true); | ||||
|       } | ||||
|     } | ||||
|     setState(() => isSubmitting = false); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: const Text("Leave a comment"), | ||||
|         actions: <Widget>[ | ||||
|           TextButton( | ||||
|             onPressed: !isSubmitting ? postComment : null, | ||||
|             child: const Text('POST'), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|       body: Column( | ||||
|         children: [ | ||||
|           // Loading indicator | ||||
|           isSubmitting ? const LinearProgressIndicator() : Container(), | ||||
|           // Userinfo | ||||
|           FutureBuilder( | ||||
|               future: authClient.getProfiles(), | ||||
|               builder: (context, snapshot) { | ||||
|                 if (snapshot.hasData) { | ||||
|                   var userinfo = snapshot.data; | ||||
|                   return Container( | ||||
|                     color: Colors.grey[50], | ||||
|                     margin: const EdgeInsets.only(bottom: 12), | ||||
|                     child: ListTile( | ||||
|                       title: Text(userinfo["nick"]), | ||||
|                       subtitle: const Text("You will post this post as"), | ||||
|                       leading: CircleAvatar( | ||||
|                         backgroundImage: NetworkImage(userinfo["picture"]), | ||||
|                       ), | ||||
|                     ), | ||||
|                   ); | ||||
|                 } else { | ||||
|                   return Container(); | ||||
|                 } | ||||
|               }), | ||||
|           // Editor | ||||
|           Expanded( | ||||
|             child: Container( | ||||
|               padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), | ||||
|               child: TextField( | ||||
|                 maxLines: null, | ||||
|                 autofocus: true, | ||||
|                 autocorrect: true, | ||||
|                 keyboardType: TextInputType.multiline, | ||||
|                 controller: contentController, | ||||
|                 decoration: const InputDecoration.collapsed( | ||||
|                   hintText: "What do you want to say?", | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|           // Recommend website banner | ||||
|           showRecommendationBanner | ||||
|               ? FutureBuilder( | ||||
|                   future: SharedPreferences.getInstance(), | ||||
|                   builder: (context, snapshot) { | ||||
|                     if (snapshot.hasData && | ||||
|                         snapshot.data?.getBool( | ||||
|                                 "editor.hide_website_recommendation") == | ||||
|                             null) { | ||||
|                       snapshot.data | ||||
|                           ?.remove("editor.hide_website_recommendation"); | ||||
|                       return MaterialBanner( | ||||
|                         padding: const EdgeInsets.all(20), | ||||
|                         content: const Text( | ||||
|                           'SolarAgent still in early stage development. Some features isn\'t available. We recommend use our website, also optimized for moblie!', | ||||
|                         ), | ||||
|                         leading: const Icon(Icons.construction), | ||||
|                         backgroundColor: const Color(0xFFE0E0E0), | ||||
|                         actions: <Widget>[ | ||||
|                           TextButton( | ||||
|                             child: const Text('OPEN'), | ||||
|                             onPressed: () async { | ||||
|                               await launchUrl( | ||||
|                                 Uri.parse("https://co.solsynth.dev"), | ||||
|                                 mode: LaunchMode.externalApplication, | ||||
|                               ); | ||||
|                             }, | ||||
|                           ), | ||||
|                           TextButton( | ||||
|                             child: const Text('DISMISS'), | ||||
|                             onPressed: () async { | ||||
|                               await snapshot.data?.setBool( | ||||
|                                 "editor.hide_website_recommendation", | ||||
|                                 true, | ||||
|                               ); | ||||
|                               setState(() { | ||||
|                                 showRecommendationBanner = false; | ||||
|                               }); | ||||
|                             }, | ||||
|                           ), | ||||
|                         ], | ||||
|                       ); | ||||
|                     } else { | ||||
|                       return Container(); | ||||
|                     } | ||||
|                   }) | ||||
|               : Container(), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     contentController.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										191
									
								
								lib/screens/publish/moment_editor.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,191 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
| import 'package:solaragent/router.dart'; | ||||
| import 'package:solaragent/screens/publish/attachment_list.dart'; | ||||
| import 'package:url_launcher/url_launcher.dart'; | ||||
|  | ||||
| class MomentEditorScreen extends StatefulWidget { | ||||
|   const MomentEditorScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<MomentEditorScreen> createState() => _MomentEditorScreenState(); | ||||
| } | ||||
|  | ||||
| class _MomentEditorScreenState extends State<MomentEditorScreen> { | ||||
|   final contentController = TextEditingController(); | ||||
|  | ||||
|   bool isSubmitting = false; | ||||
|  | ||||
|   bool showRecommendationBanner = true; | ||||
|  | ||||
|   List<Attachment> attachments = List.empty(growable: true); | ||||
|  | ||||
|   Future<void> postMoment() async { | ||||
|     if (!await authClient.isAuthorized()) return; | ||||
|  | ||||
|     setState(() => isSubmitting = true); | ||||
|     var res = await authClient.client!.post( | ||||
|       Uri.parse("https://co.solsynth.dev/api/p/moments"), | ||||
|       headers: <String, String>{ | ||||
|         'Content-Type': 'application/json', | ||||
|       }, | ||||
|       body: jsonEncode(<String, dynamic>{ | ||||
|         'content': contentController.value.text, | ||||
|         'attachments': attachments, | ||||
|       }), | ||||
|     ); | ||||
|     if (res.statusCode != 200) { | ||||
|       var message = utf8.decode(res.bodyBytes); | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         SnackBar(content: Text("Something went wrong... $message")), | ||||
|       ); | ||||
|     } else { | ||||
|       if (router.canPop()) { | ||||
|         router.pop(true); | ||||
|       } | ||||
|     } | ||||
|     setState(() => isSubmitting = false); | ||||
|   } | ||||
|  | ||||
|   void viewAttachments(BuildContext context) { | ||||
|     showModalBottomSheet( | ||||
|       context: context, | ||||
|       builder: (context) => AttachmentList( | ||||
|         current: attachments, | ||||
|         onUpdate: (value) => attachments = value, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: const Text("Record a moment"), | ||||
|         actions: <Widget>[ | ||||
|           TextButton( | ||||
|             onPressed: !isSubmitting ? postMoment : null, | ||||
|             child: const Text('POST'), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|       body: Column( | ||||
|         children: [ | ||||
|           // Loading indicator | ||||
|           isSubmitting ? const LinearProgressIndicator() : Container(), | ||||
|           // Userinfo | ||||
|           FutureBuilder( | ||||
|               future: authClient.getProfiles(), | ||||
|               builder: (context, snapshot) { | ||||
|                 if (snapshot.hasData) { | ||||
|                   var userinfo = snapshot.data; | ||||
|                   return Container( | ||||
|                     color: Colors.grey[50], | ||||
|                     margin: const EdgeInsets.only(bottom: 12), | ||||
|                     child: ListTile( | ||||
|                       title: Text(userinfo["nick"]), | ||||
|                       subtitle: const Text("You will post this post as"), | ||||
|                       leading: CircleAvatar( | ||||
|                         backgroundImage: NetworkImage(userinfo["picture"]), | ||||
|                       ), | ||||
|                     ), | ||||
|                   ); | ||||
|                 } else { | ||||
|                   return Container(); | ||||
|                 } | ||||
|               }), | ||||
|           // Editor | ||||
|           Expanded( | ||||
|             child: Container( | ||||
|               padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), | ||||
|               child: TextField( | ||||
|                 maxLines: null, | ||||
|                 autofocus: true, | ||||
|                 autocorrect: true, | ||||
|                 keyboardType: TextInputType.multiline, | ||||
|                 controller: contentController, | ||||
|                 decoration: const InputDecoration.collapsed( | ||||
|                   hintText: "What's happened?!", | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|           // Toolbar | ||||
|           Container( | ||||
|             decoration: const BoxDecoration( | ||||
|               border: Border( | ||||
|                 top: BorderSide(width: 0.3, color: Color(0xffdedede)), | ||||
|               ), | ||||
|             ), | ||||
|             child: Row( | ||||
|               children: [ | ||||
|                 TextButton( | ||||
|                   style: TextButton.styleFrom(shape: const CircleBorder()), | ||||
|                   child: const Icon(Icons.camera_alt), | ||||
|                   onPressed: () => viewAttachments(context), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           // Recommend website banner | ||||
|           showRecommendationBanner | ||||
|               ? FutureBuilder( | ||||
|                   future: SharedPreferences.getInstance(), | ||||
|                   builder: (context, snapshot) { | ||||
|                     if (snapshot.hasData && | ||||
|                         snapshot.data?.getBool( | ||||
|                                 "editor.hide_website_recommendation") == | ||||
|                             null) { | ||||
|                       snapshot.data | ||||
|                           ?.remove("editor.hide_website_recommendation"); | ||||
|                       return MaterialBanner( | ||||
|                         padding: const EdgeInsets.all(20), | ||||
|                         content: const Text( | ||||
|                           'SolarAgent still in early stage development. Some features isn\'t available. We recommend use our website, also optimized for moblie!', | ||||
|                         ), | ||||
|                         leading: const Icon(Icons.construction), | ||||
|                         backgroundColor: const Color(0xFFE0E0E0), | ||||
|                         actions: <Widget>[ | ||||
|                           TextButton( | ||||
|                             child: const Text('OPEN'), | ||||
|                             onPressed: () async { | ||||
|                               await launchUrl( | ||||
|                                 Uri.parse("https://co.solsynth.dev"), | ||||
|                                 mode: LaunchMode.externalApplication, | ||||
|                               ); | ||||
|                             }, | ||||
|                           ), | ||||
|                           TextButton( | ||||
|                             child: const Text('DISMISS'), | ||||
|                             onPressed: () async { | ||||
|                               await snapshot.data?.setBool( | ||||
|                                 "editor.hide_website_recommendation", | ||||
|                                 true, | ||||
|                               ); | ||||
|                               setState(() { | ||||
|                                 showRecommendationBanner = false; | ||||
|                               }); | ||||
|                             }, | ||||
|                           ), | ||||
|                         ], | ||||
|                       ); | ||||
|                     } else { | ||||
|                       return Container(); | ||||
|                     } | ||||
|                   }) | ||||
|               : Container(), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     contentController.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										177
									
								
								lib/widgets/feed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,177 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_carousel_widget/flutter_carousel_widget.dart'; | ||||
| import 'package:flutter_markdown/flutter_markdown.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
| import 'package:solaragent/router.dart'; | ||||
| import 'package:solaragent/widgets/image.dart'; | ||||
| import 'package:solaragent/widgets/posts/comments.dart'; | ||||
| import 'package:solaragent/widgets/posts/content/article.dart'; | ||||
| import 'package:solaragent/widgets/posts/content/moment.dart'; | ||||
| import 'package:solaragent/widgets/posts/reactions.dart'; | ||||
|  | ||||
| class FeedItem extends StatefulWidget { | ||||
|   final Feed item; | ||||
|   final bool? brief; | ||||
|  | ||||
|   const FeedItem({super.key, required this.item, this.brief}); | ||||
|  | ||||
|   @override | ||||
|   State<FeedItem> createState() => _FeedItemState(); | ||||
| } | ||||
|  | ||||
| class _FeedItemState extends State<FeedItem> { | ||||
|   int reactionCount = 0; | ||||
|   Map<String, dynamic>? reactionList; | ||||
|  | ||||
|   void viewComments(BuildContext context) { | ||||
|     showModalBottomSheet( | ||||
|       context: context, | ||||
|       isScrollControlled: true, | ||||
|       builder: (context) => CommentList(parent: widget.item), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   void viewReactions(BuildContext context) { | ||||
|     showModalBottomSheet( | ||||
|       context: context, | ||||
|       builder: (context) => ReactionList( | ||||
|         parent: widget.item, | ||||
|         onReact: (symbol, num) { | ||||
|           setState(() { | ||||
|             if (!reactionList!.containsKey(symbol)) { | ||||
|               reactionList![symbol] = 0; | ||||
|             } | ||||
|  | ||||
|             reactionCount += num; | ||||
|             reactionList![symbol] += num; | ||||
|           }); | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   bool hasAttachments() => | ||||
|       widget.item.modelType == "moment" && | ||||
|       widget.item.attachments != null && | ||||
|       widget.item.attachments!.isNotEmpty; | ||||
|  | ||||
|   String getDescription(String desc) => | ||||
|       desc.isEmpty ? "No description yet." : desc; | ||||
|  | ||||
|   String getFileUrl(String fileId) => | ||||
|       'https://co.solsynth.dev/api/attachments/o/$fileId'; | ||||
|  | ||||
|   Widget buildContent() { | ||||
|     switch (widget.item.modelType) { | ||||
|       case "article": | ||||
|         return ArticleContent(item: widget.item, brief: widget.brief ?? false); | ||||
|       default: | ||||
|         return MomentContent(item: widget.item, brief: widget.brief ?? false); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Widget buildItem(BuildContext context) { | ||||
|     return Column( | ||||
|       children: [ | ||||
|         Container( | ||||
|           color: Colors.grey[50], | ||||
|           child: ListTile( | ||||
|             title: Text(widget.item.author.name), | ||||
|             leading: CircleAvatar( | ||||
|               backgroundImage: NetworkImage(widget.item.author.avatar), | ||||
|             ), | ||||
|             subtitle: Text( | ||||
|               getDescription(widget.item.author.description), | ||||
|               overflow: TextOverflow.ellipsis, | ||||
|               maxLines: 1, | ||||
|               softWrap: false, | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         buildContent(), | ||||
|         hasAttachments() | ||||
|             ? Container( | ||||
|                 decoration: const BoxDecoration( | ||||
|                   border: Border( | ||||
|                       top: BorderSide(width: 0.3, color: Color(0xffdedede))), | ||||
|                 ), | ||||
|                 child: FlutterCarousel( | ||||
|                   options: CarouselOptions( | ||||
|                     height: 360.0, | ||||
|                     viewportFraction: 1.0, | ||||
|                     showIndicator: true, | ||||
|                     slideIndicator: const CircularSlideIndicator(), | ||||
|                   ), | ||||
|                   items: widget.item.attachments?.map((x) { | ||||
|                     return Builder( | ||||
|                       builder: (BuildContext context) { | ||||
|                         return SizedBox( | ||||
|                           width: MediaQuery.of(context).size.width, | ||||
|                           child: InkWell( | ||||
|                             child: Image.network( | ||||
|                               getFileUrl(x.fileId), | ||||
|                               fit: BoxFit.cover, | ||||
|                             ), | ||||
|                             onTap: () { | ||||
|                               Navigator.push(context, | ||||
|                                   MaterialPageRoute(builder: (_) { | ||||
|                                 return ImageLightbox( | ||||
|                                   url: getFileUrl(x.fileId), | ||||
|                                 ); | ||||
|                               })); | ||||
|                             }, | ||||
|                           ), | ||||
|                         ); | ||||
|                       }, | ||||
|                     ); | ||||
|                   }).toList(), | ||||
|                 ), | ||||
|               ) | ||||
|             : Container(), | ||||
|         Container( | ||||
|           padding: const EdgeInsets.symmetric(horizontal: 8), | ||||
|           decoration: const BoxDecoration( | ||||
|             border: Border( | ||||
|               top: BorderSide(width: 0.3, color: Color(0xffdedede)), | ||||
|             ), | ||||
|           ), | ||||
|           child: Row( | ||||
|             children: [ | ||||
|               TextButton.icon( | ||||
|                 icon: const Icon(Icons.comment), | ||||
|                 label: Text(widget.item.commentCount.toString()), | ||||
|                 onPressed: () => viewComments(context), | ||||
|               ), | ||||
|               TextButton.icon( | ||||
|                 icon: const Icon(Icons.emoji_emotions), | ||||
|                 label: Text(reactionCount.toString()), | ||||
|                 style: TextButton.styleFrom(foregroundColor: Colors.teal), | ||||
|                 onPressed: () => viewReactions(context), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     reactionCount = widget.item.reactionCount; | ||||
|     reactionList = widget.item.reactionList ?? <String, dynamic>{}; | ||||
|     super.initState(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return (widget.brief ?? false) | ||||
|         ? GestureDetector( | ||||
|             child: buildItem(context), | ||||
|             onTap: () => router.push( | ||||
|               "/post/${widget.item.modelType}/${widget.item.alias}", | ||||
|             ), | ||||
|           ) | ||||
|         : buildItem(context); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										30
									
								
								lib/widgets/image.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class ImageLightbox extends StatelessWidget { | ||||
|   final String url; | ||||
|  | ||||
|   const ImageLightbox({super.key, required this.url}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       body: GestureDetector( | ||||
|         child: Center( | ||||
|           child: SizedBox( | ||||
|             height: MediaQuery.of(context).size.height, | ||||
|             width: MediaQuery.of(context).size.width, | ||||
|             child: InteractiveViewer( | ||||
|               boundaryMargin: const EdgeInsets.all(128), | ||||
|               minScale: 0.1, | ||||
|               maxScale: 16.0, | ||||
|               child: Image.network(url, fit: BoxFit.contain), | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         onTap: () { | ||||
|           Navigator.pop(context); | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,9 +1,5 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_secure_storage/flutter_secure_storage.dart'; | ||||
|  | ||||
| import '../auth.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
|  | ||||
| class NameCard extends StatelessWidget { | ||||
|   const NameCard({super.key, this.onLogin, this.onCheck}); | ||||
| @@ -12,8 +8,8 @@ class NameCard extends StatelessWidget { | ||||
|   final void Function()? onCheck; | ||||
|  | ||||
|   Future<CircleAvatar> _getAvatar() async { | ||||
|     if (await AuthGuard().isAuthorized()) { | ||||
|       final profiles = await AuthGuard().readProfiles(); | ||||
|     if (await authClient.isAuthorized()) { | ||||
|       final profiles = await authClient.getProfiles(); | ||||
|       return CircleAvatar(backgroundImage: NetworkImage(profiles["picture"])); | ||||
|     } else { | ||||
|       return const CircleAvatar(child: Icon(Icons.account_circle)); | ||||
| @@ -21,8 +17,8 @@ class NameCard extends StatelessWidget { | ||||
|   } | ||||
|  | ||||
|   Future<Column> _getDescribe() async { | ||||
|     if (await AuthGuard().isAuthorized()) { | ||||
|       final profiles = await AuthGuard().readProfiles(); | ||||
|     if (await authClient.isAuthorized()) { | ||||
|       final profiles = await authClient.getProfiles(); | ||||
|       return Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
| @@ -59,7 +55,7 @@ class NameCard extends StatelessWidget { | ||||
|       child: InkWell( | ||||
|         splashColor: Colors.indigo.withAlpha(30), | ||||
|         onTap: () async { | ||||
|           if (await AuthGuard().isAuthorized() && onCheck != null) { | ||||
|           if (await authClient.isAuthorized() && onCheck != null) { | ||||
|             onCheck!(); | ||||
|           } else if (onLogin != null) { | ||||
|             onLogin!(); | ||||
|   | ||||
							
								
								
									
										38
									
								
								lib/widgets/navigation.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,38 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:solaragent/router.dart'; | ||||
|  | ||||
| class AgentNavigation extends StatefulWidget { | ||||
|   const AgentNavigation({super.key}); | ||||
|  | ||||
|   static const List<(String, NavigationDestination)> destinations = [ | ||||
|     ('/', NavigationDestination(icon: Icon(Icons.explore), label: 'Explore')), | ||||
|     ('/notifications', NavigationDestination(icon: Icon(Icons.notifications), label: 'Notifications')), | ||||
|     ('/account', NavigationDestination(icon: Icon(Icons.account_circle), label: 'Account')), | ||||
|   ]; | ||||
|  | ||||
|   @override | ||||
|   State<AgentNavigation> createState() => _AgentNavigationState(); | ||||
| } | ||||
|  | ||||
| class _AgentNavigationState extends State<AgentNavigation> { | ||||
|   int currentPage = 0; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return NavigationBar( | ||||
|       selectedIndex: currentPage, | ||||
|       destinations: AgentNavigation.destinations | ||||
|           .map(((String, NavigationDestination) e) => e.$2) | ||||
|           .toList(), | ||||
|       onDestinationSelected: (index) { | ||||
|         router.go(AgentNavigation.destinations[index].$1); | ||||
|         setState(() => currentPage = index); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										94
									
								
								lib/widgets/notification.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,94 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:solaragent/models/notification.dart' as model; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:url_launcher/url_launcher.dart'; | ||||
|  | ||||
| class NotificationItem extends StatelessWidget { | ||||
|   final int index; | ||||
|   final model.Notification item; | ||||
|   final void Function()? onDismiss; | ||||
|  | ||||
|   const NotificationItem( | ||||
|       {super.key, required this.index, required this.item, this.onDismiss}); | ||||
|  | ||||
|   bool hasLinks() => item.links != null && item.links!.isNotEmpty; | ||||
|  | ||||
|   void showLinks(BuildContext context) { | ||||
|     if (!hasLinks()) return; | ||||
|  | ||||
|     showModalBottomSheet<void>( | ||||
|       context: context, | ||||
|       builder: (BuildContext context) { | ||||
|         return ListView.builder( | ||||
|             padding: const EdgeInsets.all(8), | ||||
|             itemCount: item.links!.length, | ||||
|             itemBuilder: (BuildContext context, int index) { | ||||
|               var element = item.links![index]; | ||||
|               return InkWell( | ||||
|                 borderRadius: const BorderRadius.all( | ||||
|                   Radius.circular(64), | ||||
|                 ), | ||||
|                 onTap: () async { | ||||
|                   await launchUrl(Uri.parse(element.url)); | ||||
|                 }, | ||||
|                 child: ListTile(title: Text(element.label)), | ||||
|               ); | ||||
|             }); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Future<void> markAsRead(model.Notification element) async { | ||||
|     if (authClient.client != null) { | ||||
|       var id = element.id; | ||||
|       var uri = Uri.parse('https://id.solsynth.dev/api/notifications/$id/read'); | ||||
|       await authClient.client!.put(uri); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Dismissible( | ||||
|       key: Key('notification-$index'), | ||||
|       onDismissed: (direction) { | ||||
|         markAsRead(item).then((value) { | ||||
|           ScaffoldMessenger.of(context).showSnackBar( | ||||
|             SnackBar( | ||||
|               content: RichText( | ||||
|                 text: TextSpan(children: [ | ||||
|                   TextSpan( | ||||
|                     text: item.subject, | ||||
|                     style: const TextStyle(fontWeight: FontWeight.bold), | ||||
|                   ), | ||||
|                   const TextSpan( | ||||
|                     text: " is marked as read", | ||||
|                   ) | ||||
|                 ]), | ||||
|               ), | ||||
|             ), | ||||
|           ); | ||||
|         }); | ||||
|         if (onDismiss != null) { | ||||
|           onDismiss!(); | ||||
|         } | ||||
|       }, | ||||
|       background: Container( | ||||
|         color: Colors.grey, | ||||
|       ), | ||||
|       child: Container( | ||||
|         padding: const EdgeInsets.only(left: 10), | ||||
|         child: ListTile( | ||||
|           title: Text(item.subject), | ||||
|           subtitle: Text(item.content), | ||||
|           trailing: hasLinks() | ||||
|               ? TextButton( | ||||
|                   onPressed: () => showLinks(context), | ||||
|                   style: TextButton.styleFrom(shape: const CircleBorder()), | ||||
|                   child: const Icon(Icons.more_vert), | ||||
|                 ) | ||||
|               : null, | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										130
									
								
								lib/widgets/posts/comments.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,130 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
| import 'package:solaragent/models/pagination.dart'; | ||||
| import 'package:solaragent/router.dart'; | ||||
| import 'package:solaragent/widgets/feed.dart'; | ||||
|  | ||||
| class CommentList extends StatefulWidget { | ||||
|   final Feed parent; | ||||
|  | ||||
|   const CommentList({super.key, required this.parent}); | ||||
|  | ||||
|   @override | ||||
|   State<CommentList> createState() => _CommentListState(); | ||||
| } | ||||
|  | ||||
| class _CommentListState extends State<CommentList> { | ||||
|   static const pageSize = 5; | ||||
|  | ||||
|   final client = Client(); | ||||
|  | ||||
|   final PagingController<int, Feed> paginationController = | ||||
|       PagingController(firstPageKey: 0); | ||||
|  | ||||
|   List<Feed> feed = List.empty(); | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     paginationController.addPageRequestListener((pageKey) { | ||||
|       pullFeed(pageKey); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   Future<void> pullFeed(int pageKey) async { | ||||
|     var offset = pageKey; | ||||
|     var take = pageSize; | ||||
|  | ||||
|     var dataset = "${widget.parent.modelType}s"; | ||||
|     var alias = widget.parent.alias; | ||||
|  | ||||
|     var uri = Uri.parse( | ||||
|       'https://co.solsynth.dev/api/p/$dataset/$alias/comments?take=$take&offset=$offset', | ||||
|     ); | ||||
|  | ||||
|     var res = await client.get(uri); | ||||
|     if (res.statusCode == 200) { | ||||
|       final result = | ||||
|           PaginationResult.fromJson(jsonDecode(utf8.decode(res.bodyBytes))); | ||||
|       final isLastPage = (result.count - pageKey) < pageSize; | ||||
|       final items = | ||||
|           result.data?.map((x) => Feed.fromJson(x)).toList() ?? List.empty(); | ||||
|       if (isLastPage || result.data == null) { | ||||
|         paginationController.appendLastPage(items); | ||||
|       } else { | ||||
|         final nextPageKey = pageKey + items.length; | ||||
|         paginationController.appendPage(items, nextPageKey); | ||||
|       } | ||||
|     } else { | ||||
|       paginationController.error = utf8.decode(res.bodyBytes); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Column( | ||||
|       children: [ | ||||
|         Container( | ||||
|           padding: const EdgeInsets.only(left: 10, right: 10, top: 20), | ||||
|           child: Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|             children: [ | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric( | ||||
|                   horizontal: 8.0, | ||||
|                   vertical: 12.0, | ||||
|                 ), | ||||
|                 child: Text( | ||||
|                   'Comments', | ||||
|                   style: Theme.of(context).textTheme.headlineSmall, | ||||
|                 ), | ||||
|               ), | ||||
|               FutureBuilder( | ||||
|                 future: authClient.isAuthorized(), | ||||
|                 builder: (context, snapshot) { | ||||
|                   if (snapshot.hasData && snapshot.data == true) { | ||||
|                     return TextButton.icon( | ||||
|                       icon: const Icon(Icons.edit), | ||||
|                       label: const Text("LEAVE COMMENT"), | ||||
|                       onPressed: () { | ||||
|                         router | ||||
|                             .push("/post/new/comments", extra: widget.parent) | ||||
|                             .then((value) { | ||||
|                           if (value == true) paginationController.refresh(); | ||||
|                         }); | ||||
|                       }, | ||||
|                     ); | ||||
|                   } else { | ||||
|                     return Container(); | ||||
|                   } | ||||
|                 }, | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|         Expanded( | ||||
|           child: PagedListView<int, Feed>( | ||||
|             pagingController: paginationController, | ||||
|             builderDelegate: PagedChildBuilderDelegate<Feed>( | ||||
|               itemBuilder: (context, item, index) => FeedItem( | ||||
|                 item: item, | ||||
|                 brief: true, | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     paginationController.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										47
									
								
								lib/widgets/posts/content/article.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,47 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_markdown/flutter_markdown.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
| import 'package:markdown/markdown.dart' as md; | ||||
| import 'package:url_launcher/url_launcher.dart'; | ||||
| import 'package:url_launcher/url_launcher_string.dart'; | ||||
|  | ||||
| class ArticleContent extends StatelessWidget { | ||||
|   final Feed item; | ||||
|   final bool brief; | ||||
|  | ||||
|   const ArticleContent({super.key, required this.item, required this.brief}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return brief | ||||
|         ? ListTile( | ||||
|             title: Text(item.title), | ||||
|             subtitle: Text(item.description), | ||||
|           ) | ||||
|         : Column( | ||||
|             children: [ | ||||
|               ListTile( | ||||
|                 title: Text(item.title), | ||||
|                 subtitle: Text(item.description), | ||||
|               ), | ||||
|               const Divider(color: Color(0xffefefef)), | ||||
|               Markdown( | ||||
|                   selectable: !brief, | ||||
|                   data: item.content, | ||||
|                   shrinkWrap: true, | ||||
|                   physics: const NeverScrollableScrollPhysics(), | ||||
|                   extensionSet: md.ExtensionSet( | ||||
|                     md.ExtensionSet.gitHubFlavored.blockSyntaxes, | ||||
|                     md.ExtensionSet.gitHubFlavored.inlineSyntaxes, | ||||
|                   ), | ||||
|                   onTapLink: (text, href, title) async { | ||||
|                     if (href == null) return; | ||||
|                     await launchUrlString( | ||||
|                       href, | ||||
|                       mode: LaunchMode.externalApplication, | ||||
|                     ); | ||||
|                   }), | ||||
|             ], | ||||
|           ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										20
									
								
								lib/widgets/posts/content/moment.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,20 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_markdown/flutter_markdown.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
|  | ||||
| class MomentContent extends StatelessWidget { | ||||
|   final Feed item; | ||||
|   final bool brief; | ||||
|  | ||||
|   const MomentContent({super.key, required this.brief, required this.item}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Markdown( | ||||
|       selectable: !brief, | ||||
|       data: item.content, | ||||
|       shrinkWrap: true, | ||||
|       physics: const NeverScrollableScrollPhysics(), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										175
									
								
								lib/widgets/posts/reactions.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,175 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:solaragent/auth.dart'; | ||||
| import 'package:solaragent/models/feed.dart'; | ||||
|  | ||||
| class ReactionList extends StatefulWidget { | ||||
|   static const Map<String, Map<String, dynamic>> emojis = { | ||||
|     'thumb_up': {'icon': '👍', 'attitude': 1}, | ||||
|     'clap': {'icon': '👏', 'attitude': 1} | ||||
|   }; | ||||
|  | ||||
|   final Feed parent; | ||||
|   final void Function(String symbol, int num) onReact; | ||||
|  | ||||
|   const ReactionList({super.key, required this.parent, required this.onReact}); | ||||
|  | ||||
|   @override | ||||
|   State<ReactionList> createState() => _ReactionListState(); | ||||
| } | ||||
|  | ||||
| class _ReactionListState extends State<ReactionList> { | ||||
|   bool isSubmitting = false; | ||||
|  | ||||
|   void viewReactMenu(BuildContext context) { | ||||
|     showModalBottomSheet( | ||||
|       context: context, | ||||
|       builder: (context) { | ||||
|         return ListView.builder( | ||||
|             padding: const EdgeInsets.all(8), | ||||
|             itemCount: ReactionList.emojis.length, | ||||
|             itemBuilder: (BuildContext context, int index) { | ||||
|               var element = ReactionList.emojis.entries.toList()[index]; | ||||
|               return InkWell( | ||||
|                 borderRadius: const BorderRadius.all( | ||||
|                   Radius.circular(64), | ||||
|                 ), | ||||
|                 onTap: () async { | ||||
|                   await doReact(element.key, element.value['attitude']); | ||||
|                 }, | ||||
|                 child: ListTile( | ||||
|                   title: Text(element.value['icon']), | ||||
|                   subtitle: Text( | ||||
|                     ":${element.key}:", | ||||
|                     style: const TextStyle(fontFamily: "monospace"), | ||||
|                   ), | ||||
|                 ), | ||||
|               ); | ||||
|             }); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Future<void> doReact(String symbol, int attitude) async { | ||||
|     if (!await authClient.isAuthorized()) return; | ||||
|  | ||||
|     var dataset = "${widget.parent.modelType}s"; | ||||
|     var alias = widget.parent.id; | ||||
|  | ||||
|     var uri = Uri.parse( | ||||
|       "https://co.solsynth.dev/api/p/$dataset/$alias/react", | ||||
|     ); | ||||
|  | ||||
|     setState(() => isSubmitting = true); | ||||
|     var res = await authClient.client!.post( | ||||
|       uri, | ||||
|       headers: <String, String>{ | ||||
|         'Content-Type': 'application/json', | ||||
|       }, | ||||
|       body: jsonEncode(<String, dynamic>{ | ||||
|         'symbol': symbol, | ||||
|         'attitude': attitude, | ||||
|       }), | ||||
|     ); | ||||
|     if (res.statusCode == 201) { | ||||
|       widget.onReact(symbol, 1); | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         const SnackBar( | ||||
|           content: Text("Your reaction has been added onto this post."), | ||||
|         ), | ||||
|       ); | ||||
|     } else if (res.statusCode == 204) { | ||||
|       widget.onReact(symbol, -1); | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         const SnackBar( | ||||
|           content: Text("Your reaction has been removed from this post."), | ||||
|         ), | ||||
|       ); | ||||
|     } else { | ||||
|       var message = utf8.decode(res.bodyBytes); | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         SnackBar(content: Text("Something went wrong... $message")), | ||||
|       ); | ||||
|     } | ||||
|     setState(() => isSubmitting = false); | ||||
|   } | ||||
|  | ||||
|   List<MapEntry<String, dynamic>> getReactionEntries() => | ||||
|       widget.parent.reactionList?.entries.toList() ?? List.empty(); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Column( | ||||
|       children: [ | ||||
|         // Title | ||||
|         Container( | ||||
|           padding: const EdgeInsets.only(left: 10, right: 10, top: 20), | ||||
|           child: Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|             children: [ | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric( | ||||
|                   horizontal: 8.0, | ||||
|                   vertical: 12.0, | ||||
|                 ), | ||||
|                 child: Text( | ||||
|                   'Reactions', | ||||
|                   style: Theme.of(context).textTheme.headlineSmall, | ||||
|                 ), | ||||
|               ), | ||||
|               FutureBuilder( | ||||
|                 future: authClient.isAuthorized(), | ||||
|                 builder: (context, snapshot) { | ||||
|                   if (snapshot.hasData && snapshot.data == true) { | ||||
|                     return TextButton.icon( | ||||
|                       icon: const Icon(Icons.add_reaction), | ||||
|                       label: const Text("REACT"), | ||||
|                       onPressed: | ||||
|                           isSubmitting ? null : () => viewReactMenu(context), | ||||
|                     ); | ||||
|                   } else { | ||||
|                     return Container(); | ||||
|                   } | ||||
|                 }, | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|         // Loading indicator | ||||
|         isSubmitting ? const LinearProgressIndicator() : Container(), | ||||
|         // Data list | ||||
|         Expanded( | ||||
|           child: ListView.separated( | ||||
|             itemCount: getReactionEntries().length, | ||||
|             itemBuilder: (BuildContext context, int index) { | ||||
|               var element = getReactionEntries()[index]; | ||||
|               return InkWell( | ||||
|                   onTap: isSubmitting | ||||
|                       ? null | ||||
|                       : () { | ||||
|                           doReact( | ||||
|                             element.key, | ||||
|                             ReactionList.emojis[element.key]!['attitude'], | ||||
|                           ); | ||||
|                         }, | ||||
|                   child: Container( | ||||
|                     padding: const EdgeInsets.symmetric(horizontal: 6.0), | ||||
|                     child: ListTile( | ||||
|                       title: Text( | ||||
|                         "${ReactionList.emojis[element.key]!['icon']} x${element.value.toString()}", | ||||
|                       ), | ||||
|                       subtitle: Text( | ||||
|                         ":${element.key}:", | ||||
|                         style: const TextStyle(fontFamily: "monospace"), | ||||
|                       ), | ||||
|                     ), | ||||
|                   )); | ||||
|             }, | ||||
|             separatorBuilder: (context, index) => const Divider(), | ||||
|           ), | ||||
|         ), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -4,10 +4,10 @@ project(runner LANGUAGES CXX) | ||||
|  | ||||
| # The name of the executable created for the application. Change this to change | ||||
| # the on-disk name of your application. | ||||
| set(BINARY_NAME "goatagent") | ||||
| set(BINARY_NAME "solaragent") | ||||
| # The unique GTK application identifier for this application. See: | ||||
| # https://wiki.gnome.org/HowDoI/ChooseApplicationID | ||||
| set(APPLICATION_ID "studio.smartsheep.goatagent") | ||||
| set(APPLICATION_ID "dev.solsynth.solaragent") | ||||
|  | ||||
| # Explicitly opt in to modern CMake behaviors to avoid warnings with recent | ||||
| # versions of CMake. | ||||
|   | ||||
| @@ -6,13 +6,21 @@ | ||||
|  | ||||
| #include "generated_plugin_registrant.h" | ||||
|  | ||||
| #include <file_selector_linux/file_selector_plugin.h> | ||||
| #include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h> | ||||
| #include <sentry_flutter/sentry_flutter_plugin.h> | ||||
| #include <url_launcher_linux/url_launcher_plugin.h> | ||||
|  | ||||
| void fl_register_plugins(FlPluginRegistry* registry) { | ||||
|   g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); | ||||
|   file_selector_plugin_register_with_registrar(file_selector_linux_registrar); | ||||
|   g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); | ||||
|   flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); | ||||
|   g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); | ||||
|   sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); | ||||
|   g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); | ||||
|   url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); | ||||
|   | ||||
| @@ -3,7 +3,9 @@ | ||||
| # | ||||
|  | ||||
| list(APPEND FLUTTER_PLUGIN_LIST | ||||
|   file_selector_linux | ||||
|   flutter_secure_storage_linux | ||||
|   sentry_flutter | ||||
|   url_launcher_linux | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) { | ||||
|   if (use_header_bar) { | ||||
|     GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); | ||||
|     gtk_widget_show(GTK_WIDGET(header_bar)); | ||||
|     gtk_header_bar_set_title(header_bar, "goatagent"); | ||||
|     gtk_header_bar_set_title(header_bar, "solaragent"); | ||||
|     gtk_header_bar_set_show_close_button(header_bar, TRUE); | ||||
|     gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); | ||||
|   } else { | ||||
|     gtk_window_set_title(window, "goatagent"); | ||||
|     gtk_window_set_title(window, "solaragent"); | ||||
|   } | ||||
|  | ||||
|   gtk_window_set_default_size(window, 1280, 720); | ||||
|   | ||||
| @@ -5,22 +5,20 @@ | ||||
| import FlutterMacOS | ||||
| import Foundation | ||||
|  | ||||
| import firebase_core | ||||
| import firebase_crashlytics | ||||
| import firebase_messaging | ||||
| import flutter_appauth | ||||
| import file_selector_macos | ||||
| import flutter_secure_storage_macos | ||||
| import package_info_plus | ||||
| import path_provider_foundation | ||||
| import sentry_flutter | ||||
| import shared_preferences_foundation | ||||
| import url_launcher_macos | ||||
|  | ||||
| func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | ||||
|   FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) | ||||
|   FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) | ||||
|   FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) | ||||
|   FlutterAppauthPlugin.register(with: registry.registrar(forPlugin: "FlutterAppauthPlugin")) | ||||
|   FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) | ||||
|   FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) | ||||
|   FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) | ||||
|   PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) | ||||
|   SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) | ||||
|   SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) | ||||
|   UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) | ||||
| } | ||||
|   | ||||
| @@ -65,7 +65,7 @@ | ||||
| 		331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; | ||||
| 		333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; | ||||
| 		335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; | ||||
| 		33CC10ED2044A3C60003C045 /* goatagent.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = goatagent.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| 		33CC10ED2044A3C60003C045 /* solaragent.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = solaragent.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| 		33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | ||||
| 		33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; }; | ||||
| 		33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; | ||||
| @@ -134,7 +134,7 @@ | ||||
| 		33CC10EE2044A3C60003C045 /* Products */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				33CC10ED2044A3C60003C045 /* goatagent.app */, | ||||
| 				33CC10ED2044A3C60003C045 /* solaragent.app */, | ||||
| 				331C80D5294CF71000263BE5 /* RunnerTests.xctest */, | ||||
| 			); | ||||
| 			name = Products; | ||||
| @@ -220,7 +220,7 @@ | ||||
| 			); | ||||
| 			name = Runner; | ||||
| 			productName = Runner; | ||||
| 			productReference = 33CC10ED2044A3C60003C045 /* goatagent.app */; | ||||
| 			productReference = 33CC10ED2044A3C60003C045 /* solaragent.app */; | ||||
| 			productType = "com.apple.product-type.application"; | ||||
| 		}; | ||||
| /* End PBXNativeTarget section */ | ||||
| @@ -388,10 +388,10 @@ | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent.RunnerTests; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/goatagent.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/goatagent"; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/solaragent.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/solaragent"; | ||||
| 			}; | ||||
| 			name = Debug; | ||||
| 		}; | ||||
| @@ -402,10 +402,10 @@ | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent.RunnerTests; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/goatagent.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/goatagent"; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/solaragent.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/solaragent"; | ||||
| 			}; | ||||
| 			name = Release; | ||||
| 		}; | ||||
| @@ -416,10 +416,10 @@ | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent.RunnerTests; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/goatagent.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/goatagent"; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/solaragent.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/solaragent"; | ||||
| 			}; | ||||
| 			name = Profile; | ||||
| 		}; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ | ||||
|             <BuildableReference | ||||
|                BuildableIdentifier = "primary" | ||||
|                BlueprintIdentifier = "33CC10EC2044A3C60003C045" | ||||
|                BuildableName = "goatagent.app" | ||||
|                BuildableName = "solaragent.app" | ||||
|                BlueprintName = "Runner" | ||||
|                ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|             </BuildableReference> | ||||
| @@ -31,7 +31,7 @@ | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "33CC10EC2044A3C60003C045" | ||||
|             BuildableName = "goatagent.app" | ||||
|             BuildableName = "solaragent.app" | ||||
|             BlueprintName = "Runner" | ||||
|             ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|          </BuildableReference> | ||||
| @@ -65,7 +65,7 @@ | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "33CC10EC2044A3C60003C045" | ||||
|             BuildableName = "goatagent.app" | ||||
|             BuildableName = "solaragent.app" | ||||
|             BlueprintName = "Runner" | ||||
|             ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|          </BuildableReference> | ||||
| @@ -82,7 +82,7 @@ | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "33CC10EC2044A3C60003C045" | ||||
|             BuildableName = "goatagent.app" | ||||
|             BuildableName = "solaragent.app" | ||||
|             BlueprintName = "Runner" | ||||
|             ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|   | ||||
| Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 63 KiB | 
| Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 6.0 KiB | 
| Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 480 B | 
| Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 13 KiB | 
| Before Width: | Height: | Size: 967 B After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 29 KiB | 
| Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.5 KiB | 
| @@ -5,10 +5,10 @@ | ||||
| // 'flutter create' template. | ||||
|  | ||||
| // The application's name. By default this is also the title of the Flutter window. | ||||
| PRODUCT_NAME = goatagent | ||||
| PRODUCT_NAME = solaragent | ||||
|  | ||||
| // The application's bundle identifier | ||||
| PRODUCT_BUNDLE_IDENTIFIER = studio.smartsheep.goatagent | ||||
| PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solaragent | ||||
|  | ||||
| // The copyright displayed in application information | ||||
| PRODUCT_COPYRIGHT = Copyright © 2024 studio.smartsheep. All rights reserved. | ||||
| PRODUCT_COPYRIGHT = Copyright © 2024 dev.solsynth. All rights reserved. | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| 	<key>PLIST_VERSION</key> | ||||
| 	<string>1</string> | ||||
| 	<key>BUNDLE_ID</key> | ||||
| 	<string>studio.smartsheep.goatagent.RunnerTests</string> | ||||
| 	<string>dev.solsynth.solaragent.RunnerTests</string> | ||||
| 	<key>PROJECT_ID</key> | ||||
| 	<string>smartsheep-hydrogen</string> | ||||
| 	<key>STORAGE_BUCKET</key> | ||||
|   | ||||
							
								
								
									
										414
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						| @@ -1,14 +1,6 @@ | ||||
| # Generated by pub | ||||
| # See https://dart.dev/tools/pub/glossary#lockfile | ||||
| packages: | ||||
|   _flutterfire_internals: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: _flutterfire_internals | ||||
|       sha256: "1a52f1afae8ab7ac4741425114713bdbba802f1ce1e0648e167ffcc6e05e96cf" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.3.21" | ||||
|   archive: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -89,8 +81,16 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.1.1" | ||||
|   crypto: | ||||
|   cross_file: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: cross_file | ||||
|       sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.3.3+8" | ||||
|   crypto: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: crypto | ||||
|       sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab | ||||
| @@ -117,95 +117,79 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: ffi | ||||
|       sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" | ||||
|       sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.0" | ||||
|   firebase_core: | ||||
|     version: "2.1.2" | ||||
|   file: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: file | ||||
|       sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "7.0.0" | ||||
|   file_picker: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: firebase_core | ||||
|       sha256: "7e049e32a9d347616edb39542cf92cd53fdb4a99fb6af0a0bff327c14cd76445" | ||||
|       name: file_picker | ||||
|       sha256: d1d0ac3966b36dc3e66eeefb40280c17feb87fa2099c6e22e6a1fc959327bd03 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.25.4" | ||||
|   firebase_core_platform_interface: | ||||
|     version: "8.0.0+1" | ||||
|   file_selector_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: firebase_core_platform_interface | ||||
|       sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 | ||||
|       name: file_selector_linux | ||||
|       sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "5.0.0" | ||||
|   firebase_core_web: | ||||
|     version: "0.9.2+1" | ||||
|   file_selector_macos: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: firebase_core_web | ||||
|       sha256: "57e61d6010e253b36d38191cefd6199d7849152cdcd234b61ca290cdb278a0ba" | ||||
|       name: file_selector_macos | ||||
|       sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.11.4" | ||||
|   firebase_crashlytics: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: firebase_crashlytics | ||||
|       sha256: efd096e4c3d2c568e128505b6e4ce5f5d5a1629f700a4d6fee6bd25b85937dde | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.4.14" | ||||
|   firebase_crashlytics_platform_interface: | ||||
|     version: "0.9.3+3" | ||||
|   file_selector_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: firebase_crashlytics_platform_interface | ||||
|       sha256: "225a54d834a118be262c1f1096d407515e35b99d9b474c987abdcff7663f2b81" | ||||
|       name: file_selector_platform_interface | ||||
|       sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.6.21" | ||||
|   firebase_messaging: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: firebase_messaging | ||||
|       sha256: "9c97b20c012542252a8853f11334efd833ddae83551fe37d27f87d885c655038" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "14.7.15" | ||||
|   firebase_messaging_platform_interface: | ||||
|     version: "2.6.2" | ||||
|   file_selector_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: firebase_messaging_platform_interface | ||||
|       sha256: d464b255e922c7915dc4b0ebc305ebad4e1f130519bee3d6e568ef2ea1613a4b | ||||
|       name: file_selector_windows | ||||
|       sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.5.23" | ||||
|   firebase_messaging_web: | ||||
|     version: "0.9.3+1" | ||||
|   fixnum: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: firebase_messaging_web | ||||
|       sha256: f3f71aeec719ec1fe2c99f75cd74d00d33f1c240cf1e402cc9d43883e84f935a | ||||
|       name: fixnum | ||||
|       sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.6.4" | ||||
|     version: "1.1.0" | ||||
|   flutter: | ||||
|     dependency: "direct main" | ||||
|     description: flutter | ||||
|     source: sdk | ||||
|     version: "0.0.0" | ||||
|   flutter_appauth: | ||||
|   flutter_carousel_widget: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_appauth | ||||
|       sha256: "4ad7d446888b1486642226db6aa04ffb4651d76388715aba6e6e572ebeb915c3" | ||||
|       name: flutter_carousel_widget | ||||
|       sha256: "37b9e55e4cafffe358152b016db24153e756152aa07c4214cfe6ee902cd69a01" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.0.4" | ||||
|   flutter_appauth_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: flutter_appauth_platform_interface | ||||
|       sha256: "44feaa7058191b5d3cd7c9ff195262725773643121bcada172d49c2ddcff71cb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.0.0" | ||||
|     version: "2.2.0" | ||||
|   flutter_launcher_icons: | ||||
|     dependency: "direct dev" | ||||
|     description: | ||||
| @@ -222,6 +206,22 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.3" | ||||
|   flutter_markdown: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_markdown | ||||
|       sha256: "87e11b9df25a42e2db315b8b7a51fae8e66f57a4b2f50ec4b822d0fa155e6b52" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.6.22" | ||||
|   flutter_plugin_android_lifecycle: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: flutter_plugin_android_lifecycle | ||||
|       sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.17" | ||||
|   flutter_secure_storage: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -270,6 +270,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.0.0" | ||||
|   flutter_staggered_grid_view: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: flutter_staggered_grid_view | ||||
|       sha256: "19e7abb550c96fbfeb546b23f3ff356ee7c59a019a651f8f102a4ba9b7349395" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.7.0" | ||||
|   flutter_test: | ||||
|     dependency: "direct dev" | ||||
|     description: flutter | ||||
| @@ -284,10 +292,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: go_router | ||||
|       sha256: "07ee2436909f749d606f53521dc1725dd738dc5196e5ff815bc254253c594075" | ||||
|       sha256: "7ecb2f391edbca5473db591b48555a8912dde60edd0fb3013bd6743033b2d3f8" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "13.1.0" | ||||
|     version: "13.2.1" | ||||
|   http: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -308,10 +316,82 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image | ||||
|       sha256: "49a0d4b0c12402853d3f227fe7c315601b238d126aa4caa5dbb2dcf99421aa4a" | ||||
|       sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.1.6" | ||||
|     version: "4.1.7" | ||||
|   image_picker: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: image_picker | ||||
|       sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.7" | ||||
|   image_picker_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image_picker_android | ||||
|       sha256: "39f2bfe497e495450c81abcd44b62f56c2a36a37a175da7d137b4454977b51b1" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.8.9+3" | ||||
|   image_picker_for_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image_picker_for_web | ||||
|       sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.0.2" | ||||
|   image_picker_ios: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image_picker_ios | ||||
|       sha256: "917a5cadd67d052554cfb258595e54217de53fac5b52939426e26319a02e6297" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.8.9+2" | ||||
|   image_picker_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image_picker_linux | ||||
|       sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.1+1" | ||||
|   image_picker_macos: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image_picker_macos | ||||
|       sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.1+1" | ||||
|   image_picker_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image_picker_platform_interface | ||||
|       sha256: "3d2c323daea9d60608f1caf30be32a938916f4975434b8352e6f73dae496da38" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.9.4" | ||||
|   image_picker_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image_picker_windows | ||||
|       sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.1+1" | ||||
|   infinite_scroll_pagination: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: infinite_scroll_pagination | ||||
|       sha256: b68bce20752fcf36c7739e60de4175494f74e99e9a69b4dd2fe3a1dd07a7f16a | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.0.0" | ||||
|   js: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -328,6 +408,30 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.8.1" | ||||
|   leak_tracker: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: leak_tracker | ||||
|       sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "10.0.4" | ||||
|   leak_tracker_flutter_testing: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: leak_tracker_flutter_testing | ||||
|       sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.1" | ||||
|   leak_tracker_testing: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: leak_tracker_testing | ||||
|       sha256: d4c8f568c60af6b6daa74c80fc04411765769882600f6bf9cd4b391c96de42ce | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.3" | ||||
|   lints: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -344,30 +448,46 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.2.0" | ||||
|   markdown: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: markdown | ||||
|       sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "7.2.2" | ||||
|   matcher: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: matcher | ||||
|       sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" | ||||
|       sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.12.16" | ||||
|     version: "0.12.16+1" | ||||
|   material_color_utilities: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: material_color_utilities | ||||
|       sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" | ||||
|       sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.5.0" | ||||
|     version: "0.8.0" | ||||
|   meta: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: meta | ||||
|       sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e | ||||
|       sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.10.0" | ||||
|     version: "1.12.0" | ||||
|   mime: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: mime | ||||
|       sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.5" | ||||
|   oauth2: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -396,10 +516,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: path | ||||
|       sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" | ||||
|       sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.8.3" | ||||
|     version: "1.9.0" | ||||
|   path_provider: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -480,11 +600,91 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.7.4" | ||||
|   sentry: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: sentry | ||||
|       sha256: a460aa48568d47140dd0557410b624d344ffb8c05555107ac65035c1097cf1ad | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "7.18.0" | ||||
|   sentry_flutter: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: sentry_flutter | ||||
|       sha256: "3d0d1d4e0e407d276ae8128d123263ccbc37e988bae906765efd6f37d544f4c6" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "7.18.0" | ||||
|   shared_preferences: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: shared_preferences | ||||
|       sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.2.2" | ||||
|   shared_preferences_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shared_preferences_android | ||||
|       sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.2.1" | ||||
|   shared_preferences_foundation: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shared_preferences_foundation | ||||
|       sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.3.5" | ||||
|   shared_preferences_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shared_preferences_linux | ||||
|       sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.3.2" | ||||
|   shared_preferences_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shared_preferences_platform_interface | ||||
|       sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.3.2" | ||||
|   shared_preferences_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shared_preferences_web | ||||
|       sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.2.2" | ||||
|   shared_preferences_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shared_preferences_windows | ||||
|       sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.3.2" | ||||
|   sky_engine: | ||||
|     dependency: transitive | ||||
|     description: flutter | ||||
|     source: sdk | ||||
|     version: "0.0.99" | ||||
|   sliver_tools: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: sliver_tools | ||||
|       sha256: eae28220badfb9d0559207badcbbc9ad5331aac829a88cb0964d330d2a4636a6 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.12" | ||||
|   source_span: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -493,6 +693,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.10.0" | ||||
|   sprintf: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: sprintf | ||||
|       sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "7.0.0" | ||||
|   stack_trace: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -545,26 +753,26 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: url_launcher | ||||
|       sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c | ||||
|       sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.2.4" | ||||
|     version: "6.2.5" | ||||
|   url_launcher_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: url_launcher_android | ||||
|       sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" | ||||
|       sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.2.2" | ||||
|     version: "6.3.0" | ||||
|   url_launcher_ios: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: url_launcher_ios | ||||
|       sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" | ||||
|       sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.2.4" | ||||
|     version: "6.2.5" | ||||
|   url_launcher_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -585,10 +793,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: url_launcher_platform_interface | ||||
|       sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f | ||||
|       sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.3.1" | ||||
|     version: "2.3.2" | ||||
|   url_launcher_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -605,6 +813,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.1.1" | ||||
|   uuid: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: uuid | ||||
|       sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.3.3" | ||||
|   vector_math: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -613,30 +829,38 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.4" | ||||
|   vm_service: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: vm_service | ||||
|       sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "13.0.0" | ||||
|   web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: web | ||||
|       sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 | ||||
|       sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.3.0" | ||||
|     version: "0.4.2" | ||||
|   webview_flutter: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: webview_flutter | ||||
|       sha256: d81b68e88cc353e546afb93fb38958e3717282c5ac6e5d3be4a4aef9fc3c1413 | ||||
|       sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.5.0" | ||||
|     version: "4.7.0" | ||||
|   webview_flutter_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_android | ||||
|       sha256: "4ea3c4e1b8ed590162b15b8a61b41b1ef3ff179a314627c16ce40c086d94b8af" | ||||
|       sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.14.0" | ||||
|     version: "3.16.0" | ||||
|   webview_flutter_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -649,18 +873,18 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_wkwebview | ||||
|       sha256: b99ca8d8bae9c6b43d568218691aa537fb0aeae1d7d34eadf112a6aa36d26506 | ||||
|       sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.11.0" | ||||
|     version: "3.13.0" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: win32 | ||||
|       sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" | ||||
|       sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "5.2.0" | ||||
|     version: "5.3.0" | ||||
|   xdg_directories: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -686,5 +910,5 @@ packages: | ||||
|     source: hosted | ||||
|     version: "3.1.2" | ||||
| sdks: | ||||
|   dart: ">=3.2.6 <4.0.0" | ||||
|   flutter: ">=3.16.6" | ||||
|   dart: ">=3.3.0 <4.0.0" | ||||
|   flutter: ">=3.19.0" | ||||
|   | ||||
							
								
								
									
										24
									
								
								pubspec.yaml
									
									
									
									
									
								
							
							
						
						| @@ -1,5 +1,5 @@ | ||||
| name: goatagent | ||||
| description: "The official Application of Goatworks Global Hydrogen Network" | ||||
| name: solaragent | ||||
| description: "The official Application of Solarworks Global Hydrogen Network" | ||||
| # The following line prevents the package from being accidentally published to | ||||
| # pub.dev using `flutter pub publish`. This is preferred for private packages. | ||||
| publish_to: 'none' # Remove this line if you wish to publish to pub.dev | ||||
| @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev | ||||
| # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html | ||||
| # In Windows, build-name is used as the major, minor, and patch parts | ||||
| # of the product and file versions while build-number is used as the build suffix. | ||||
| version: 1.0.0+1 | ||||
| version: 2.0.0+1 | ||||
|  | ||||
| environment: | ||||
|   sdk: '>=3.2.6 <4.0.0' | ||||
| @@ -35,17 +35,22 @@ dependencies: | ||||
|   # The following adds the Cupertino Icons font to your application. | ||||
|   # Use with the CupertinoIcons class for iOS style icons. | ||||
|   cupertino_icons: ^1.0.2 | ||||
|   firebase_messaging: ^14.7.15 | ||||
|   firebase_core: ^2.25.4 | ||||
|   firebase_crashlytics: ^3.4.14 | ||||
|   flutter_appauth: ^6.0.4 | ||||
|   flutter_secure_storage: ^9.0.0 | ||||
|   oauth2: ^2.0.2 | ||||
|   http: ^1.2.0 | ||||
|   webview_flutter: ^4.5.0 | ||||
|   go_router: ^13.1.0 | ||||
|   go_router: ^13.2.1 | ||||
|   package_info_plus: ^5.0.1 | ||||
|   url_launcher: ^6.2.4 | ||||
|   shared_preferences: ^2.2.2 | ||||
|   flutter_markdown: ^0.6.22 | ||||
|   infinite_scroll_pagination: ^4.0.0 | ||||
|   flutter_carousel_widget: ^2.2.0 | ||||
|   image_picker: ^1.0.7 | ||||
|   sentry_flutter: ^7.18.0 | ||||
|   crypto: ^3.0.3 | ||||
|   file_picker: ^8.0.0+1 | ||||
|   markdown: ^7.2.2 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
| @@ -60,10 +65,11 @@ dev_dependencies: | ||||
|   flutter_launcher_icons: "^0.13.1" | ||||
|  | ||||
| flutter_launcher_icons: | ||||
|   android: "launcher_icon" | ||||
|   android: true | ||||
|   ios: true | ||||
|   image_path: "image.png" | ||||
|   min_sdk_android: 21 # android min sdk min:16, default 21 | ||||
|   remove_alpha_ios: true | ||||
|   web: | ||||
|     generate: true | ||||
|     image_path: "image.png" | ||||
|   | ||||
| @@ -8,12 +8,12 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_test/flutter_test.dart'; | ||||
|  | ||||
| import 'package:goatagent/main.dart'; | ||||
| import 'package:solaragent/main.dart'; | ||||
|  | ||||
| void main() { | ||||
|   testWidgets('Counter increments smoke test', (WidgetTester tester) async { | ||||
|     // Build our app and trigger a frame. | ||||
|     await tester.pumpWidget(const GoatAgent()); | ||||
|     await tester.pumpWidget(const SolarAgent()); | ||||
|  | ||||
|     // Verify that our counter starts at 0. | ||||
|     expect(find.text('0'), findsOneWidget); | ||||
|   | ||||