13 Commits

Author SHA1 Message Date
e74aba2d8b 💄 Optimized image browsing 2024-03-24 11:20:59 +08:00
41f456893f 🐛 Bug fixes 2024-03-24 11:07:36 +08:00
06b4cb863e 🐛 Fix both line in notification are subject 2024-03-24 01:36:24 +08:00
827f8f1175 🐛 Remove dark theme for a while to fix display issue 2024-03-24 01:33:47 +08:00
e54a506671 🗑️ Remove firebase to fix ios build 2024-03-24 01:29:23 +08:00
a1fd936702 Moment editor 2024-03-24 01:22:01 +08:00
048b8c0894 Image attachments 2024-03-24 00:13:52 +08:00
c19bb3730e Notification links 2024-03-23 23:41:45 +08:00
a3b4706ca2 Explore page 2024-03-23 23:05:04 +08:00
8bb9c960c1 💄 Optimized notification page
⬇️ Remove firebase deps
2024-03-23 20:56:32 +08:00
5da657a73c ♻️ Refactored and joined the Solar Network 2024-03-17 20:22:46 +08:00
f3da8f5349 💄 Optimized UX 2024-02-12 17:50:06 +08:00
ef0e7f02da 🚀 Launch iOS 2024-02-12 17:25:02 +08:00
85 changed files with 1371 additions and 1070 deletions

View File

@@ -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.

View File

@@ -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'
]
}

View File

@@ -9,7 +9,7 @@
"client_info": {
"mobilesdk_app_id": "1:659822066072:android:39e699282c97a7cfc013ed",
"android_client_info": {
"package_name": "studio.smartsheep.goatagent"
"package_name": "dev.solsynth.solaragent"
}
},
"oauth_client": [

View File

@@ -1,8 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="GoatAgent"
android:label="SolarAgent"
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon">
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"

View File

@@ -1,4 +1,4 @@
package studio.smartsheep.goatagent
package dev.solsynth.solaragent
import io.flutter.embedding.android.FlutterActivity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -9,7 +9,7 @@
"client_info": {
"mobilesdk_app_id": "1:659822066072:android:39e699282c97a7cfc013ed",
"android_client_info": {
"package_name": "studio.smartsheep.goatagent"
"package_name": "dev.solsynth.solaragent"
}
},
"oauth_client": [

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
</Workspace>

View File

@@ -9,7 +9,7 @@
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>studio.smartsheep.goatagent</string>
<string>dev.solsynth.solaragent</string>
<key>PROJECT_ID</key>
<string>smartsheep-hydrogen</string>
<key>STORAGE_BUCKET</key>

View File

@@ -1,187 +1,52 @@
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)
- 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`)
- 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"
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
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

View File

@@ -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 = SolarAgent;
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 = SolarAgent;
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 = SolarAgent;
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;

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -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
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 880 B

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,23 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Launch Screen.png",
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -3,34 +3,29 @@ 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;
Future<bool> pickClient() async {
@@ -43,7 +38,7 @@ class AuthGuard {
await pullProfiles();
return true;
} catch (e) {
logout();
signoff();
return false;
}
} else {
@@ -68,11 +63,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 +85,41 @@ 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 && client!.credentials.isExpired) {
await refreshToken();
}
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();
}

View File

@@ -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;
}
}

View File

@@ -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',
);
}

View File

@@ -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;
});
},
),
);
}
}
}

View File

@@ -1,51 +1,47 @@
import 'package:flutter/material.dart';
import 'package:goatagent/auth.dart';
import 'package:goatagent/firebase.dart';
import 'layouts/navigation.dart';
import 'package:solaragent/auth.dart';
import 'package:solaragent/router.dart';
import 'package:solaragent/widgets/navigation.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
try {
await initializeFirebase();
} catch (e) {
print(e);
}
await authClient.pickClient();
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) => SafeArea(
child: SafeArea(
child: Scaffold(
body: child,
bottomNavigationBar: const AgentNavigation(),
),
),
),
)
],
),
home: AgentNavigation(),
);
}
}

59
lib/models/author.dart Normal file
View 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
View 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,
};
}

View 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,
};
}

View 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
View File

@@ -0,0 +1,5 @@
import 'package:shared_preferences/shared_preferences.dart';
getPreferences() async {
return await SharedPreferences.getInstance();
}

27
lib/router.dart Normal file
View File

@@ -0,0 +1,27 @@
import 'package:go_router/go_router.dart';
import 'package:solaragent/screens/account.dart';
import 'package:solaragent/screens/explore.dart';
import 'package:solaragent/screens/notifications.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/moments',
builder: (context, state) => const MomentEditorScreen(),
),
],
);

View File

@@ -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'),
),

View File

@@ -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'),
),
],
),
),
],
),
]),
),
),
]),
),
);
}

View File

@@ -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(),
),
],
),
),
);
}
}

View File

@@ -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;
}

View File

@@ -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(),
),
),
),
);
}
}

97
lib/screens/explore.dart Normal file
View File

@@ -0,0 +1,97 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.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';
import 'package:solaragent/screens/publish/moment_editor.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;
if (isLastPage || result.data == null) {
paginationController.appendLastPage(feed);
} else {
final feed =
result.data?.map((x) => Feed.fromJson(x)).toList() ?? List.empty();
final nextPageKey = pageKey + feed.length;
paginationController.appendPage(feed, nextPageKey);
}
} else {
paginationController.error = utf8.decode(res.bodyBytes);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PagedListView<int, Feed>(
pagingController: paginationController,
builderDelegate: PagedChildBuilderDelegate<Feed>(
itemBuilder: (context, item, index) => FeedItem(
item: item,
),
),
),
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/moments");
},
);
} else {
return Container();
}
}),
);
}
@override
void dispose() {
paginationController.dispose();
super.dispose();
}
}

View File

@@ -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,
),
),
),
],
),
),
);

View File

@@ -0,0 +1,156 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:solaragent/auth.dart';
import 'package:solaragent/router.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;
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,
}),
);
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();
}
}
setState(() => isSubmitting = false);
}
@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?!"),
),
),
),
// 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"));
},
),
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();
}
}

87
lib/widgets/feed.dart Normal file
View File

@@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:solaragent/models/feed.dart';
import 'package:solaragent/widgets/image.dart';
class FeedItem extends StatelessWidget {
final Feed item;
const FeedItem({super.key, required this.item});
bool hasAttachments() =>
item.attachments != null && 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';
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
color: Colors.grey[50],
child: ListTile(
title: Text(item.author.name),
leading: CircleAvatar(
backgroundImage: NetworkImage(item.author.avatar),
),
subtitle: Text(
getDescription(item.author.description),
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
),
),
),
Markdown(
data: item.content,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
hasAttachments()
? Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 0.3, color: Color(0xffdedede))),
),
child: FlutterCarousel(
options: CarouselOptions(
height: 240.0,
viewportFraction: 1.0,
showIndicator: true,
slideIndicator: const CircularSlideIndicator(),
),
items: 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(),
],
);
}
}

30
lib/widgets/image.dart Normal file
View 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);
},
),
);
}
}

View File

@@ -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!();

View 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);
},
);
}
}

View 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(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,
),
),
);
}
}

View File

@@ -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.

View File

@@ -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);

View File

@@ -5,22 +5,16 @@
import FlutterMacOS
import Foundation
import firebase_core
import firebase_crashlytics
import firebase_messaging
import flutter_appauth
import flutter_secure_storage_macos
import package_info_plus
import path_provider_foundation
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"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

View File

@@ -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;
};

View File

@@ -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>

View File

@@ -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.

View File

@@ -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>

View File

@@ -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:
@@ -117,95 +109,31 @@ packages:
dependency: transitive
description:
name: ffi
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
sha256: "7e049e32a9d347616edb39542cf92cd53fdb4a99fb6af0a0bff327c14cd76445"
url: "https://pub.dev"
source: hosted
version: "2.25.4"
firebase_core_platform_interface:
version: "2.1.2"
file:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: "57e61d6010e253b36d38191cefd6199d7849152cdcd234b61ca290cdb278a0ba"
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:
dependency: transitive
description:
name: firebase_crashlytics_platform_interface
sha256: "225a54d834a118be262c1f1096d407515e35b99d9b474c987abdcff7663f2b81"
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:
dependency: transitive
description:
name: firebase_messaging_platform_interface
sha256: d464b255e922c7915dc4b0ebc305ebad4e1f130519bee3d6e568ef2ea1613a4b
url: "https://pub.dev"
source: hosted
version: "4.5.23"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
sha256: f3f71aeec719ec1fe2c99f75cd74d00d33f1c240cf1e402cc9d43883e84f935a
url: "https://pub.dev"
source: hosted
version: "3.6.4"
version: "7.0.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 +150,14 @@ 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_secure_storage:
dependency: "direct main"
description:
@@ -270,6 +206,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 +228,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 +252,18 @@ packages:
dependency: transitive
description:
name: image
sha256: "49a0d4b0c12402853d3f227fe7c315601b238d126aa4caa5dbb2dcf99421aa4a"
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.dev"
source: hosted
version: "4.1.6"
version: "4.1.7"
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 +280,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 +320,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
markdown:
dependency: transitive
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"
oauth2:
dependency: "direct main"
description:
@@ -396,10 +380,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 +464,75 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.7.4"
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:
@@ -545,26 +593,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 +633,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:
@@ -613,30 +661,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 +705,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 +742,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"

View File

@@ -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
@@ -35,17 +35,17 @@ 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
dev_dependencies:
flutter_test:
@@ -60,10 +60,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"

View File

@@ -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);

View File

@@ -18,18 +18,18 @@
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="The official Application of Goatworks Global Hydrogen Network">
<meta name="description" content="The official Application of Solarworks Global Hydrogen Network">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="goatagent">
<meta name="apple-mobile-web-app-title" content="solaragent">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>goatagent</title>
<title>solaragent</title>
<link rel="manifest" href="manifest.json">
<script>

View File

@@ -1,11 +1,11 @@
{
"name": "goatagent",
"short_name": "goatagent",
"name": "solaragent",
"short_name": "solaragent",
"start_url": ".",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#49509e",
"description": "The official Application of Goatworks Global Hydrogen Network",
"description": "The official Application of Solarworks Global Hydrogen Network",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [

View File

@@ -1,10 +1,10 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(goatagent LANGUAGES CXX)
project(solaragent 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")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.

View File

@@ -6,13 +6,10 @@
#include "generated_plugin_registrant.h"
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
firebase_core
flutter_secure_storage_windows
url_launcher_windows
)

View File

@@ -89,13 +89,13 @@ BEGIN
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "studio.smartsheep" "\0"
VALUE "FileDescription", "goatagent" "\0"
VALUE "CompanyName", "dev.solsynth" "\0"
VALUE "FileDescription", "solaragent" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "goatagent" "\0"
VALUE "LegalCopyright", "Copyright (C) 2024 studio.smartsheep. All rights reserved." "\0"
VALUE "OriginalFilename", "goatagent.exe" "\0"
VALUE "ProductName", "goatagent" "\0"
VALUE "InternalName", "solaragent" "\0"
VALUE "LegalCopyright", "Copyright (C) 2024 dev.solsynth. All rights reserved." "\0"
VALUE "OriginalFilename", "solaragent.exe" "\0"
VALUE "ProductName", "solaragent" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END

View File

@@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.Create(L"goatagent", origin, size)) {
if (!window.Create(L"solaragent", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);