Compare commits

..

29 Commits

Author SHA1 Message Date
LittleSheep
a61c889c6c ⬆️ Upgrade flutter deps 2025-09-03 00:31:47 +08:00
LittleSheep
0dd3221a56 🐛 Fix oidc on web close #175 2025-09-02 00:36:06 +08:00
LittleSheep
66918521f8 More search filter 2025-09-02 00:01:36 +08:00
LittleSheep
bb1846e462 🐛 Fix post creator style broke 2025-09-01 23:17:54 +08:00
LittleSheep
a976a6eaf4 💄 Optimize the post item reaction made effect 2025-09-01 23:10:24 +08:00
LittleSheep
4252f66fd3 💄 Optimize some designs 2025-09-01 23:05:11 +08:00
LittleSheep
f2d780b48f 💄 Optimize of account profile card 2025-09-01 21:37:59 +08:00
LittleSheep
300541f9bb ♻️ Replaced picker 2025-09-01 01:00:52 +08:00
LittleSheep
43787bb813 💄 Optimize attachment previewer in editing 2025-09-01 00:45:35 +08:00
LittleSheep
3417c51a3b Preview for SND files 2025-09-01 00:34:55 +08:00
LittleSheep
f98e603e82 🐛 Dozens of bug fixes 2025-09-01 00:07:19 +08:00
LittleSheep
c9b71701c8 🚀 Launch 3.2.0+129 2025-08-26 01:33:57 +08:00
LittleSheep
28e98488f1 🌐 Localize new stuff 2025-08-26 01:24:58 +08:00
LittleSheep
b4d476613e 🐛 Optimzation and bug fixes 2025-08-26 01:18:54 +08:00
LittleSheep
b48a1aac44 Search messages!
♻️ Optimize messages loading, syncing
2025-08-26 01:05:30 +08:00
LittleSheep
596d212593 🐛 Fix account name localization 2025-08-26 00:17:34 +08:00
LittleSheep
54f290327e Copyable file ID 2025-08-26 00:08:50 +08:00
LittleSheep
16f248ceab 💄 Optimize file saving 2025-08-26 00:03:43 +08:00
LittleSheep
856d811187 🐛 Fix notification tap in system wide 2025-08-25 23:14:59 +08:00
LittleSheep
d07b194c04 🐛 Fixes 2025-08-25 20:44:35 +08:00
LittleSheep
2554b58be6 Show automated account in pfc 2025-08-25 20:31:43 +08:00
LittleSheep
a627b5838e 👔 Disable pin reply 2025-08-25 19:57:30 +08:00
LittleSheep
c479a9f381 Show social credits 2025-08-25 19:56:34 +08:00
LittleSheep
02057e663b Real previewing chat 2025-08-25 19:36:48 +08:00
LittleSheep
6501594100 Event dairy 2025-08-25 18:31:57 +08:00
LittleSheep
c6599edc3d 💄 Serval changes to optimize UX 2025-08-25 18:03:50 +08:00
LittleSheep
709a0620b6 Show pinned posts on realms, publishers 2025-08-25 17:09:24 +08:00
LittleSheep
f9b2a96c7c Pin post 2025-08-25 16:55:06 +08:00
LittleSheep
4dca6189cb 💄 Optimize post category subscribe loading state 2025-08-25 16:17:19 +08:00
65 changed files with 2449 additions and 931 deletions

View File

@@ -386,6 +386,7 @@
"postSettings": "Settings", "postSettings": "Settings",
"postPublisherUnselected": "Publisher Unspecified", "postPublisherUnselected": "Publisher Unspecified",
"postType": "Post Type", "postType": "Post Type",
"postTypePost": "Post",
"articleAttachmentHint": "Attachments must be uploaded and inserted into the article body to be visible.", "articleAttachmentHint": "Attachments must be uploaded and inserted into the article body to be visible.",
"postVisibility": "Post Visibility", "postVisibility": "Post Visibility",
"postVisibilityPublic": "Public", "postVisibilityPublic": "Public",
@@ -883,6 +884,7 @@
"stellarProgram": "Stellar Program", "stellarProgram": "Stellar Program",
"socialCredits": "Social Credits", "socialCredits": "Social Credits",
"credits": "Credits", "credits": "Credits",
"creditsStatus": "Credits Status",
"socialCreditsDescription": "Social Credit is a way for Solar Network to evaluate users. It is calculated based on their behavior and interactions. With a base score of 100, higher scores indicate a user's credibility within the community. Scores change over time to reflect a user's recent behavior. Users with higher credit ratings enjoy more benefits, while users with lower credit ratings may have some functionality restricted.", "socialCreditsDescription": "Social Credit is a way for Solar Network to evaluate users. It is calculated based on their behavior and interactions. With a base score of 100, higher scores indicate a user's credibility within the community. Scores change over time to reflect a user's recent behavior. Users with higher credit ratings enjoy more benefits, while users with lower credit ratings may have some functionality restricted.",
"socialCreditsLevelPoor": "Poor", "socialCreditsLevelPoor": "Poor",
"socialCreditsLevelNormal": "Normal", "socialCreditsLevelNormal": "Normal",
@@ -926,5 +928,46 @@
"newSecretGenerated": "New Secret Generated", "newSecretGenerated": "New Secret Generated",
"copySecretHint": "Please copy this secret and store it somewhere safe. You will not be able to see it again.", "copySecretHint": "Please copy this secret and store it somewhere safe. You will not be able to see it again.",
"expiresIn": "Expires In (seconds)", "expiresIn": "Expires In (seconds)",
"isOidc": "OIDC Compliant" "isOidc": "OIDC Compliant",
"pinPost": "Pin Post",
"unpinPost": "Unpin Post",
"pinnedPost": "Pinned",
"publisherPage": "Publisher Page",
"realmPage": "Realm Page",
"replyPage": "Reply Page",
"pinPostPublisherHint": "Pin this post to your publisher page",
"pinPostRealmHint": "Pin this post to the realm page",
"pinPostRealmDisabledHint": "This post doesn't belong to any realm",
"pinPostReplyHint": "Pin this post to the reply page",
"pinPostReplyDisabledHint": "This post is not a reply",
"pin": "Pin",
"unpinPostHint": "Are you sure you want to unpin this post?",
"all": "All",
"statusPresent": "Present",
"accountAutomated": "Automated",
"chatBreakClearButton": "Clear",
"chatBreak5m": "5m",
"chatBreak10m": "10m",
"chatBreak15m": "15m",
"chatBreak30m": "30m",
"chatBreakCustomMinutes": "Custom (minutes)",
"errorGeneric": "Error: {}",
"searchMessages": "Search Messages",
"messagesCount": "{} messages",
"dotSeparator": "·",
"roleValidationHint": "Role must be between 0 and 100",
"searchMessagesHint": "Search messages...",
"searchLinks": "Links",
"searchAttachments": "Attachments",
"noMessagesFound": "No messages found",
"openInBrowser": "Open in Browser",
"highlightPost": "Highlight Post",
"filters": "Filters",
"apply": "Apply",
"pubName": "Pub Name",
"realm": "Realm",
"shuffle": "Shuffle",
"pinned": "Pinned",
"noResultsFound": "No results found",
"toggleFilters": "Toggle filters"
} }

View File

@@ -855,5 +855,9 @@
"newSecretGenerated": "已生成新密钥", "newSecretGenerated": "已生成新密钥",
"copySecretHint": "请复制此密钥并将其存放在安全的地方。您将无法再次看到它。", "copySecretHint": "请复制此密钥并将其存放在安全的地方。您将无法再次看到它。",
"expiresIn": "过期时间(秒)", "expiresIn": "过期时间(秒)",
"isOidc": "OIDC 兼容" "isOidc": "OIDC 兼容",
"statusPresent": "至今",
"accountAutomated": "机器人",
"openInBrowser": "在浏览器中打开",
"highlightPost": "精选帖子"
} }

View File

@@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>12.0</string> <string>13.0</string>
</dict> </dict>
</plist> </plist>

View File

@@ -40,83 +40,85 @@ PODS:
- file_picker (0.0.1): - file_picker (0.0.1):
- DKImagePickerController/PhotoGallery - DKImagePickerController/PhotoGallery
- Flutter - Flutter
- Firebase/CoreOnly (12.0.0): - file_saver (0.0.1):
- FirebaseCore (~> 12.0.0) - Flutter
- Firebase/Crashlytics (12.0.0): - Firebase/CoreOnly (12.2.0):
- FirebaseCore (~> 12.2.0)
- Firebase/Crashlytics (12.2.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseCrashlytics (~> 12.0.0) - FirebaseCrashlytics (~> 12.2.0)
- Firebase/Messaging (12.0.0): - Firebase/Messaging (12.2.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseMessaging (~> 12.0.0) - FirebaseMessaging (~> 12.2.0)
- firebase_analytics (12.0.0): - firebase_analytics (12.0.1):
- firebase_core - firebase_core
- FirebaseAnalytics (= 12.0.0) - FirebaseAnalytics (= 12.2.0)
- Flutter - Flutter
- firebase_core (4.0.0): - firebase_core (4.1.0):
- Firebase/CoreOnly (= 12.0.0) - Firebase/CoreOnly (= 12.2.0)
- Flutter - Flutter
- firebase_crashlytics (5.0.0): - firebase_crashlytics (5.0.1):
- Firebase/Crashlytics (= 12.0.0) - Firebase/Crashlytics (= 12.2.0)
- firebase_core - firebase_core
- Flutter - Flutter
- firebase_messaging (16.0.0): - firebase_messaging (16.0.1):
- Firebase/Messaging (= 12.0.0) - Firebase/Messaging (= 12.2.0)
- firebase_core - firebase_core
- Flutter - Flutter
- FirebaseAnalytics (12.0.0): - FirebaseAnalytics (12.2.0):
- FirebaseAnalytics/Default (= 12.0.0) - FirebaseAnalytics/Default (= 12.2.0)
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- FirebaseAnalytics/Default (12.0.0): - FirebaseAnalytics/Default (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- GoogleAppMeasurement/Default (= 12.0.0) - GoogleAppMeasurement/Default (= 12.2.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- FirebaseCore (12.0.0): - FirebaseCore (12.2.0):
- FirebaseCoreInternal (~> 12.0.0) - FirebaseCoreInternal (~> 12.2.0)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1) - GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreExtension (12.0.0): - FirebaseCoreExtension (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseCoreInternal (12.0.0): - FirebaseCoreInternal (12.2.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseCrashlytics (12.0.0): - FirebaseCrashlytics (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- FirebaseRemoteConfigInterop (~> 12.0.0) - FirebaseRemoteConfigInterop (~> 12.2.0)
- FirebaseSessions (~> 12.0.0) - FirebaseSessions (~> 12.2.0)
- GoogleDataTransport (~> 10.1) - GoogleDataTransport (~> 10.1)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4) - PromisesObjC (~> 2.4)
- FirebaseInstallations (12.0.0): - FirebaseInstallations (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4) - PromisesObjC (~> 2.4)
- FirebaseMessaging (12.0.0): - FirebaseMessaging (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- GoogleDataTransport (~> 10.1) - GoogleDataTransport (~> 10.1)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Reachability (~> 8.1) - GoogleUtilities/Reachability (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- FirebaseRemoteConfigInterop (12.0.0) - FirebaseRemoteConfigInterop (12.2.0)
- FirebaseSessions (12.0.0): - FirebaseSessions (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseCoreExtension (~> 12.0.0) - FirebaseCoreExtension (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- GoogleDataTransport (~> 10.1) - GoogleDataTransport (~> 10.1)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1)
@@ -145,33 +147,33 @@ PODS:
- flutter_udid (0.0.1): - flutter_udid (0.0.1):
- Flutter - Flutter
- SAMKeychain - SAMKeychain
- flutter_webrtc (1.0.0): - flutter_webrtc (1.1.0):
- Flutter - Flutter
- WebRTC-SDK (= 137.7151.02) - WebRTC-SDK (= 137.7151.03)
- gal (1.0.0): - gal (1.0.0):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- GoogleAdsOnDeviceConversion (2.1.0): - GoogleAdsOnDeviceConversion (2.3.0):
- GoogleUtilities/Logger (~> 8.1) - GoogleUtilities/Logger (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Core (12.0.0): - GoogleAppMeasurement/Core (12.2.0):
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Default (12.0.0): - GoogleAppMeasurement/Default (12.2.0):
- GoogleAdsOnDeviceConversion (= 2.1.0) - GoogleAdsOnDeviceConversion (= 2.3.0)
- GoogleAppMeasurement/Core (= 12.0.0) - GoogleAppMeasurement/Core (= 12.2.0)
- GoogleAppMeasurement/IdentitySupport (= 12.0.0) - GoogleAppMeasurement/IdentitySupport (= 12.2.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- GoogleAppMeasurement/IdentitySupport (12.0.0): - GoogleAppMeasurement/IdentitySupport (12.2.0):
- GoogleAppMeasurement/Core (= 12.0.0) - GoogleAppMeasurement/Core (= 12.2.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
@@ -215,7 +217,7 @@ PODS:
- livekit_client (2.5.0): - livekit_client (2.5.0):
- Flutter - Flutter
- flutter_webrtc - flutter_webrtc
- WebRTC-SDK (= 137.7151.02) - WebRTC-SDK (= 137.7151.03)
- local_auth_darwin (0.0.1): - local_auth_darwin (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -248,9 +250,9 @@ PODS:
- record_ios (1.1.0): - record_ios (1.1.0):
- Flutter - Flutter
- SAMKeychain (1.5.3) - SAMKeychain (1.5.3)
- SDWebImage (5.21.1): - SDWebImage (5.21.2):
- SDWebImage/Core (= 5.21.1) - SDWebImage/Core (= 5.21.2)
- SDWebImage/Core (5.21.1) - SDWebImage/Core (5.21.2)
- share_plus (0.0.1): - share_plus (0.0.1):
- Flutter - Flutter
- shared_preferences_foundation (0.0.1): - shared_preferences_foundation (0.0.1):
@@ -295,7 +297,7 @@ PODS:
- Flutter - Flutter
- wakelock_plus (0.0.1): - wakelock_plus (0.0.1):
- Flutter - Flutter
- WebRTC-SDK (137.7151.02) - WebRTC-SDK (137.7151.03)
DEPENDENCIES: DEPENDENCIES:
- Alamofire - Alamofire
@@ -303,6 +305,7 @@ DEPENDENCIES:
- croppy (from `.symlinks/plugins/croppy/ios`) - croppy (from `.symlinks/plugins/croppy/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
- file_saver (from `.symlinks/plugins/file_saver/ios`)
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
@@ -381,6 +384,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/device_info_plus/ios" :path: ".symlinks/plugins/device_info_plus/ios"
file_picker: file_picker:
:path: ".symlinks/plugins/file_picker/ios" :path: ".symlinks/plugins/file_picker/ios"
file_saver:
:path: ".symlinks/plugins/file_saver/ios"
firebase_analytics: firebase_analytics:
:path: ".symlinks/plugins/firebase_analytics/ios" :path: ".symlinks/plugins/firebase_analytics/ios"
firebase_core: firebase_core:
@@ -464,21 +469,22 @@ SPEC CHECKSUMS:
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Firebase: 800d487043c0557d9faed71477a38d9aafb08a41 file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
firebase_analytics: cd56fc56f75c1df30a6ff5290cd56e230996a76d Firebase: 26f6f8d460603af3df970ad505b16b15f5e2e9a1
firebase_core: 633e1851ffe1b9ab875f6467a4f574c79cef02e4 firebase_analytics: 111ff65791a430356bd6c7e4d7339537fc6a15ae
firebase_crashlytics: 2c6c1a17900a38081d938330e9f48e60ec5b255d firebase_core: 3ff52146406557dddd01d570e807e203ec7e1302
firebase_messaging: d17feef781edc84ebefe62624fb384358ad96361 firebase_crashlytics: 3637078b718a52dc9fb4d64e37c969e86b87ff6f
FirebaseAnalytics: 6d790cd1b159b4eb61a99948df0934ce505a34f7 firebase_messaging: 3dcc998dd98e1e54af75d0cccae8606eba43553c
FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a FirebaseAnalytics: e04e23bc070e3014aa5cf4980f9df7ce5cd79ec8
FirebaseCoreExtension: 639afb3de6abd611952be78a794c54a47fa0f361 FirebaseCore: 311c48a147ad4a0ab7febbaed89e8025c67510cd
FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55 FirebaseCoreExtension: 73af080c22a2f7b44cefa391dc08f7e4ee162cb5
FirebaseCrashlytics: db75aa0cab8d00f68406fa247c32fe17ade884d7 FirebaseCoreInternal: 56ea29f3dad2894f81b060f706f9d53509b6ed3b
FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988 FirebaseCrashlytics: f83cbf176d5c637ade108c0aacf1ccbd5ec499bf
FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde FirebaseInstallations: 3e884b01feabdf67582a80f3250425a00979b4ed
FirebaseRemoteConfigInterop: bfa0ea72ba3dc5af739777296424e46bd6f42613 FirebaseMessaging: 43ec73bbfedd0c385a849bb91593ab4ad4b9e48e
FirebaseSessions: 4e784acda213108aafef536535cdfc03504acc42 FirebaseRemoteConfigInterop: 0896fd52ab72586a355c8f389ff85aaa9e5375e1
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 FirebaseSessions: f4692789e770bec66ce17d772c0e9561c4f11737
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9 flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99 flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619 flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
@@ -487,16 +493,16 @@ SPEC CHECKSUMS:
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544 flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9 flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
flutter_webrtc: 6f7da106613d52ade777d5b4875a43f48c28b457 flutter_webrtc: b0b2e04411747142962164a1cfa43a1af9a0afac
gal: baecd024ebfd13c441269ca7404792a7152fde89 gal: baecd024ebfd13c441269ca7404792a7152fde89
GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64 GoogleAdsOnDeviceConversion: 9090c435cde08903e8dd1ba2c77fbec9e46d9afe
GoogleAppMeasurement: 8f6ab04ad6ae493b53fcf56bd26323fb2f1384f3 GoogleAppMeasurement: 09f341dfa8527d1612a09cbfe809a242c0b737af
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486 irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
Kingfisher: ff0d31a1f07bdff6a1ebb3ba08b8e6e567b6500c Kingfisher: ff0d31a1f07bdff6a1ebb3ba08b8e6e567b6500c
livekit_client: e3b79b99405428aac439b6b76a254cd9a11dbbfb livekit_client: f810c81bbbc229a84f60b09e66603ac4e93f7599
local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19 local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854 media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474 media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
@@ -512,7 +518,7 @@ SPEC CHECKSUMS:
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00 receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
record_ios: f75fa1d57f840012775c0e93a38a7f3ceea1a374 record_ios: f75fa1d57f840012775c0e93a38a7f3ceea1a374
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
SDWebImage: f29024626962457f3470184232766516dee8dfea SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418 sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
@@ -524,7 +530,7 @@ SPEC CHECKSUMS:
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12 volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
WebRTC-SDK: d20de357dcbf7c9696b124b39f3ff62125107e4b WebRTC-SDK: 69d4e56b0b4b27d788e87bab9b9a1326ed05b1e3
PODFILE CHECKSUM: c818292390b02fa379036ea099713a332bd7193f PODFILE CHECKSUM: c818292390b02fa379036ea099713a332bd7193f

View File

@@ -853,7 +853,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@@ -897,6 +897,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -915,6 +916,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -931,6 +933,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1078,7 +1081,7 @@
INFOPLIST_FILE = SolianShareExtension/Info.plist; INFOPLIST_FILE = SolianShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension; INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -1121,7 +1124,7 @@
INFOPLIST_FILE = SolianShareExtension/Info.plist; INFOPLIST_FILE = SolianShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension; INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -1161,7 +1164,7 @@
INFOPLIST_FILE = SolianShareExtension/Info.plist; INFOPLIST_FILE = SolianShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension; INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -1348,7 +1351,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1399,7 +1402,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;

View File

@@ -68,6 +68,34 @@ class AppDatabase extends _$AppDatabase {
return (delete(chatMessages)..where((m) => m.id.equals(id))).go(); return (delete(chatMessages)..where((m) => m.id.equals(id))).go();
} }
Future<int> getTotalMessagesForRoom(String roomId) {
return (select(chatMessages)..where((m) => m.roomId.equals(roomId))).get().then((list) => list.length);
}
Future<List<LocalChatMessage>> searchMessages(
String roomId,
String query,
) async {
var selectStatement = select(chatMessages)
..where((m) => m.roomId.equals(roomId));
if (query.isNotEmpty) {
selectStatement =
selectStatement
..where((m) => m.content.like('%${query.toLowerCase()}%'));
}
final messages =
await (selectStatement
..orderBy([(m) => OrderingTerm.desc(m.createdAt)]))
.get();
return messages.map((msg) => companionToMessage(msg)).toList();
}
// Convert between Drift and model objects // Convert between Drift and model objects
ChatMessagesCompanion messageToCompanion(LocalChatMessage message) { ChatMessagesCompanion messageToCompanion(LocalChatMessage message) {
return ChatMessagesCompanion( return ChatMessagesCompanion(

View File

@@ -8,7 +8,7 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -30,7 +30,6 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
@@ -52,7 +51,6 @@ void main() async {
} }
try { try {
await langdetect.initLangDetect();
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
if (kIsWeb || !Platform.isLinux) { if (kIsWeb || !Platform.isLinux) {
@@ -169,12 +167,12 @@ class IslandApp extends HookConsumerWidget {
final theme = ref.watch(themeProvider); final theme = ref.watch(themeProvider);
void handleMessage(RemoteMessage notification) { void handleMessage(RemoteMessage notification) {
if (notification.data['action_uri'] != null) { if (notification.data['meta']?['action_uri'] != null) {
var uri = notification.data['action_uri'] as String; var uri = notification.data['meta']['action_uri'] as String;
if (uri.startsWith('/')) { if (uri.startsWith('/')) {
// In-app routes // In-app routes
final router = ref.read(routerProvider); final router = ref.read(routerProvider);
router.go(notification.data['action_uri']); router.push(notification.data['meta']['action_uri']);
} else { } else {
// External links // External links
launchUrlString(uri); launchUrlString(uri);
@@ -186,27 +184,6 @@ class IslandApp extends HookConsumerWidget {
if (!kIsWeb && Platform.isLinux) { if (!kIsWeb && Platform.isLinux) {
return null; return null;
} }
const channel = MethodChannel('dev.solsynth.solian/notifications');
Future<void> handleInitialLink() async {
final String? link = await channel.invokeMethod('initialLink');
if (link != null) {
final router = ref.read(routerProvider);
router.go(link);
}
}
if (!kIsWeb && Platform.isAndroid) {
handleInitialLink();
}
channel.setMethodCallHandler((call) async {
if (call.method == 'newLink') {
final String link = call.arguments;
final router = ref.read(routerProvider);
router.go(link);
}
});
// When the app is opened from a terminated state. // When the app is opened from a terminated state.
FirebaseMessaging.instance.getInitialMessage().then((message) { FirebaseMessaging.instance.getInitialMessage().then((message) {

View File

@@ -71,6 +71,8 @@ sealed class SnAccountProfile with _$SnAccountProfile {
SnAccountBadge? activeBadge, SnAccountBadge? activeBadge,
required int experience, required int experience,
required int level, required int level,
@Default(100) double socialCredits,
@Default(0) int socialCreditsLevel,
required double levelingProgress, required double levelingProgress,
required SnCloudFile? picture, required SnCloudFile? picture,
required SnCloudFile? background, required SnCloudFile? background,

View File

@@ -613,7 +613,7 @@ as String,
/// @nodoc /// @nodoc
mixin _$SnAccountProfile { mixin _$SnAccountProfile {
String get id; String get firstName; String get middleName; String get lastName; String get bio; String get gender; String get pronouns; String get location; String get timeZone; DateTime? get birthday;@ProfileLinkConverter() List<ProfileLink> get links; DateTime? get lastSeenAt; SnAccountBadge? get activeBadge; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get firstName; String get middleName; String get lastName; String get bio; String get gender; String get pronouns; String get location; String get timeZone; DateTime? get birthday;@ProfileLinkConverter() List<ProfileLink> get links; DateTime? get lastSeenAt; SnAccountBadge? get activeBadge; int get experience; int get level; double get socialCredits; int get socialCreditsLevel; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
/// Create a copy of SnAccountProfile /// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -626,16 +626,16 @@ $SnAccountProfileCopyWith<SnAccountProfile> get copyWith => _$SnAccountProfileCo
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&const DeepCollectionEquality().equals(other.links, links)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&const DeepCollectionEquality().equals(other.links, links)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.socialCredits, socialCredits) || other.socialCredits == socialCredits)&&(identical(other.socialCreditsLevel, socialCreditsLevel) || other.socialCreditsLevel == socialCreditsLevel)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,const DeepCollectionEquality().hash(links),lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]); int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,const DeepCollectionEquality().hash(links),lastSeenAt,activeBadge,experience,level,socialCredits,socialCreditsLevel,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
@override @override
String toString() { String toString() {
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, links: $links, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, links: $links, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, socialCredits: $socialCredits, socialCreditsLevel: $socialCreditsLevel, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
@@ -646,7 +646,7 @@ abstract mixin class $SnAccountProfileCopyWith<$Res> {
factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl; factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday,@ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday,@ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double socialCredits, int socialCreditsLevel, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -663,7 +663,7 @@ class _$SnAccountProfileCopyWithImpl<$Res>
/// Create a copy of SnAccountProfile /// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? links = null,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? links = null,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? socialCredits = null,Object? socialCreditsLevel = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
@@ -680,6 +680,8 @@ as List<ProfileLink>,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : last
as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable
as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable
as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable
as int,socialCredits: null == socialCredits ? _self.socialCredits : socialCredits // ignore: cast_nullable_to_non_nullable
as double,socialCreditsLevel: null == socialCreditsLevel ? _self.socialCreditsLevel : socialCreditsLevel // ignore: cast_nullable_to_non_nullable
as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : levelingProgress // ignore: cast_nullable_to_non_nullable as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : levelingProgress // ignore: cast_nullable_to_non_nullable
as double,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable as double,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
@@ -817,10 +819,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double socialCredits, int socialCreditsLevel, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnAccountProfile() when $default != null: case _SnAccountProfile() when $default != null:
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.socialCredits,_that.socialCreditsLevel,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return orElse(); return orElse();
} }
@@ -838,10 +840,10 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double socialCredits, int socialCreditsLevel, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnAccountProfile(): case _SnAccountProfile():
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);} return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.socialCredits,_that.socialCreditsLevel,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -855,10 +857,10 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double socialCredits, int socialCreditsLevel, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnAccountProfile() when $default != null: case _SnAccountProfile() when $default != null:
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.socialCredits,_that.socialCreditsLevel,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return null; return null;
} }
@@ -870,7 +872,7 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
@JsonSerializable() @JsonSerializable()
class _SnAccountProfile implements SnAccountProfile { class _SnAccountProfile implements SnAccountProfile {
const _SnAccountProfile({required this.id, this.firstName = '', this.middleName = '', this.lastName = '', this.bio = '', this.gender = '', this.pronouns = '', this.location = '', this.timeZone = '', this.birthday, @ProfileLinkConverter() final List<ProfileLink> links = const [], this.lastSeenAt, this.activeBadge, required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.createdAt, required this.updatedAt, required this.deletedAt}): _links = links; const _SnAccountProfile({required this.id, this.firstName = '', this.middleName = '', this.lastName = '', this.bio = '', this.gender = '', this.pronouns = '', this.location = '', this.timeZone = '', this.birthday, @ProfileLinkConverter() final List<ProfileLink> links = const [], this.lastSeenAt, this.activeBadge, required this.experience, required this.level, this.socialCredits = 100, this.socialCreditsLevel = 0, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.createdAt, required this.updatedAt, required this.deletedAt}): _links = links;
factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => _$SnAccountProfileFromJson(json); factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => _$SnAccountProfileFromJson(json);
@override final String id; @override final String id;
@@ -894,6 +896,8 @@ class _SnAccountProfile implements SnAccountProfile {
@override final SnAccountBadge? activeBadge; @override final SnAccountBadge? activeBadge;
@override final int experience; @override final int experience;
@override final int level; @override final int level;
@override@JsonKey() final double socialCredits;
@override@JsonKey() final int socialCreditsLevel;
@override final double levelingProgress; @override final double levelingProgress;
@override final SnCloudFile? picture; @override final SnCloudFile? picture;
@override final SnCloudFile? background; @override final SnCloudFile? background;
@@ -915,16 +919,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&const DeepCollectionEquality().equals(other._links, _links)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&const DeepCollectionEquality().equals(other._links, _links)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.socialCredits, socialCredits) || other.socialCredits == socialCredits)&&(identical(other.socialCreditsLevel, socialCreditsLevel) || other.socialCreditsLevel == socialCreditsLevel)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,const DeepCollectionEquality().hash(_links),lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]); int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,const DeepCollectionEquality().hash(_links),lastSeenAt,activeBadge,experience,level,socialCredits,socialCreditsLevel,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
@override @override
String toString() { String toString() {
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, links: $links, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, links: $links, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, socialCredits: $socialCredits, socialCreditsLevel: $socialCreditsLevel, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
@@ -935,7 +939,7 @@ abstract mixin class _$SnAccountProfileCopyWith<$Res> implements $SnAccountProfi
factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl; factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday,@ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday,@ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double socialCredits, int socialCreditsLevel, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -952,7 +956,7 @@ class __$SnAccountProfileCopyWithImpl<$Res>
/// Create a copy of SnAccountProfile /// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? links = null,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? links = null,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? socialCredits = null,Object? socialCreditsLevel = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_SnAccountProfile( return _then(_SnAccountProfile(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
@@ -969,6 +973,8 @@ as List<ProfileLink>,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : last
as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable
as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable
as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable
as int,socialCredits: null == socialCredits ? _self.socialCredits : socialCredits // ignore: cast_nullable_to_non_nullable
as double,socialCreditsLevel: null == socialCreditsLevel ? _self.socialCreditsLevel : socialCreditsLevel // ignore: cast_nullable_to_non_nullable
as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : levelingProgress // ignore: cast_nullable_to_non_nullable as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : levelingProgress // ignore: cast_nullable_to_non_nullable
as double,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable as double,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable

View File

@@ -86,6 +86,8 @@ _SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
), ),
experience: (json['experience'] as num).toInt(), experience: (json['experience'] as num).toInt(),
level: (json['level'] as num).toInt(), level: (json['level'] as num).toInt(),
socialCredits: (json['social_credits'] as num?)?.toDouble() ?? 100,
socialCreditsLevel: (json['social_credits_level'] as num?)?.toInt() ?? 0,
levelingProgress: (json['leveling_progress'] as num).toDouble(), levelingProgress: (json['leveling_progress'] as num).toDouble(),
picture: picture:
json['picture'] == null json['picture'] == null
@@ -128,6 +130,8 @@ Map<String, dynamic> _$SnAccountProfileToJson(_SnAccountProfile instance) =>
'active_badge': instance.activeBadge?.toJson(), 'active_badge': instance.activeBadge?.toJson(),
'experience': instance.experience, 'experience': instance.experience,
'level': instance.level, 'level': instance.level,
'social_credits': instance.socialCredits,
'social_credits_level': instance.socialCreditsLevel,
'leveling_progress': instance.levelingProgress, 'leveling_progress': instance.levelingProgress,
'picture': instance.picture?.toJson(), 'picture': instance.picture?.toJson(),
'background': instance.background?.toJson(), 'background': instance.background?.toJson(),

View File

@@ -54,7 +54,7 @@ sealed class SnEventCalendarEntry with _$SnEventCalendarEntry {
const factory SnEventCalendarEntry({ const factory SnEventCalendarEntry({
required DateTime date, required DateTime date,
required SnCheckInResult? checkInResult, required SnCheckInResult? checkInResult,
required List<dynamic> statuses, required List<SnAccountStatus> statuses,
}) = _SnEventCalendarEntry; }) = _SnEventCalendarEntry;
factory SnEventCalendarEntry.fromJson(Map<String, dynamic> json) => factory SnEventCalendarEntry.fromJson(Map<String, dynamic> json) =>

View File

@@ -861,7 +861,7 @@ as String,
/// @nodoc /// @nodoc
mixin _$SnEventCalendarEntry { mixin _$SnEventCalendarEntry {
DateTime get date; SnCheckInResult? get checkInResult; List<dynamic> get statuses; DateTime get date; SnCheckInResult? get checkInResult; List<SnAccountStatus> get statuses;
/// Create a copy of SnEventCalendarEntry /// Create a copy of SnEventCalendarEntry
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -894,7 +894,7 @@ abstract mixin class $SnEventCalendarEntryCopyWith<$Res> {
factory $SnEventCalendarEntryCopyWith(SnEventCalendarEntry value, $Res Function(SnEventCalendarEntry) _then) = _$SnEventCalendarEntryCopyWithImpl; factory $SnEventCalendarEntryCopyWith(SnEventCalendarEntry value, $Res Function(SnEventCalendarEntry) _then) = _$SnEventCalendarEntryCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
DateTime date, SnCheckInResult? checkInResult, List<dynamic> statuses DateTime date, SnCheckInResult? checkInResult, List<SnAccountStatus> statuses
}); });
@@ -916,7 +916,7 @@ class _$SnEventCalendarEntryCopyWithImpl<$Res>
date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable
as DateTime,checkInResult: freezed == checkInResult ? _self.checkInResult : checkInResult // ignore: cast_nullable_to_non_nullable as DateTime,checkInResult: freezed == checkInResult ? _self.checkInResult : checkInResult // ignore: cast_nullable_to_non_nullable
as SnCheckInResult?,statuses: null == statuses ? _self.statuses : statuses // ignore: cast_nullable_to_non_nullable as SnCheckInResult?,statuses: null == statuses ? _self.statuses : statuses // ignore: cast_nullable_to_non_nullable
as List<dynamic>, as List<SnAccountStatus>,
)); ));
} }
/// Create a copy of SnEventCalendarEntry /// Create a copy of SnEventCalendarEntry
@@ -1010,7 +1010,7 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( DateTime date, SnCheckInResult? checkInResult, List<dynamic> statuses)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( DateTime date, SnCheckInResult? checkInResult, List<SnAccountStatus> statuses)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnEventCalendarEntry() when $default != null: case _SnEventCalendarEntry() when $default != null:
return $default(_that.date,_that.checkInResult,_that.statuses);case _: return $default(_that.date,_that.checkInResult,_that.statuses);case _:
@@ -1031,7 +1031,7 @@ return $default(_that.date,_that.checkInResult,_that.statuses);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime date, SnCheckInResult? checkInResult, List<dynamic> statuses) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime date, SnCheckInResult? checkInResult, List<SnAccountStatus> statuses) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnEventCalendarEntry(): case _SnEventCalendarEntry():
return $default(_that.date,_that.checkInResult,_that.statuses);} return $default(_that.date,_that.checkInResult,_that.statuses);}
@@ -1048,7 +1048,7 @@ return $default(_that.date,_that.checkInResult,_that.statuses);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( DateTime date, SnCheckInResult? checkInResult, List<dynamic> statuses)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( DateTime date, SnCheckInResult? checkInResult, List<SnAccountStatus> statuses)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnEventCalendarEntry() when $default != null: case _SnEventCalendarEntry() when $default != null:
return $default(_that.date,_that.checkInResult,_that.statuses);case _: return $default(_that.date,_that.checkInResult,_that.statuses);case _:
@@ -1063,13 +1063,13 @@ return $default(_that.date,_that.checkInResult,_that.statuses);case _:
@JsonSerializable() @JsonSerializable()
class _SnEventCalendarEntry implements SnEventCalendarEntry { class _SnEventCalendarEntry implements SnEventCalendarEntry {
const _SnEventCalendarEntry({required this.date, required this.checkInResult, required final List<dynamic> statuses}): _statuses = statuses; const _SnEventCalendarEntry({required this.date, required this.checkInResult, required final List<SnAccountStatus> statuses}): _statuses = statuses;
factory _SnEventCalendarEntry.fromJson(Map<String, dynamic> json) => _$SnEventCalendarEntryFromJson(json); factory _SnEventCalendarEntry.fromJson(Map<String, dynamic> json) => _$SnEventCalendarEntryFromJson(json);
@override final DateTime date; @override final DateTime date;
@override final SnCheckInResult? checkInResult; @override final SnCheckInResult? checkInResult;
final List<dynamic> _statuses; final List<SnAccountStatus> _statuses;
@override List<dynamic> get statuses { @override List<SnAccountStatus> get statuses {
if (_statuses is EqualUnmodifiableListView) return _statuses; if (_statuses is EqualUnmodifiableListView) return _statuses;
// ignore: implicit_dynamic_type // ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_statuses); return EqualUnmodifiableListView(_statuses);
@@ -1109,7 +1109,7 @@ abstract mixin class _$SnEventCalendarEntryCopyWith<$Res> implements $SnEventCal
factory _$SnEventCalendarEntryCopyWith(_SnEventCalendarEntry value, $Res Function(_SnEventCalendarEntry) _then) = __$SnEventCalendarEntryCopyWithImpl; factory _$SnEventCalendarEntryCopyWith(_SnEventCalendarEntry value, $Res Function(_SnEventCalendarEntry) _then) = __$SnEventCalendarEntryCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
DateTime date, SnCheckInResult? checkInResult, List<dynamic> statuses DateTime date, SnCheckInResult? checkInResult, List<SnAccountStatus> statuses
}); });
@@ -1131,7 +1131,7 @@ class __$SnEventCalendarEntryCopyWithImpl<$Res>
date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable
as DateTime,checkInResult: freezed == checkInResult ? _self.checkInResult : checkInResult // ignore: cast_nullable_to_non_nullable as DateTime,checkInResult: freezed == checkInResult ? _self.checkInResult : checkInResult // ignore: cast_nullable_to_non_nullable
as SnCheckInResult?,statuses: null == statuses ? _self._statuses : statuses // ignore: cast_nullable_to_non_nullable as SnCheckInResult?,statuses: null == statuses ? _self._statuses : statuses // ignore: cast_nullable_to_non_nullable
as List<dynamic>, as List<SnAccountStatus>,
)); ));
} }

View File

@@ -87,7 +87,10 @@ _SnEventCalendarEntry _$SnEventCalendarEntryFromJson(
: SnCheckInResult.fromJson( : SnCheckInResult.fromJson(
json['check_in_result'] as Map<String, dynamic>, json['check_in_result'] as Map<String, dynamic>,
), ),
statuses: json['statuses'] as List<dynamic>, statuses:
(json['statuses'] as List<dynamic>)
.map((e) => SnAccountStatus.fromJson(e as Map<String, dynamic>))
.toList(),
); );
Map<String, dynamic> _$SnEventCalendarEntryToJson( Map<String, dynamic> _$SnEventCalendarEntryToJson(
@@ -95,5 +98,5 @@ Map<String, dynamic> _$SnEventCalendarEntryToJson(
) => <String, dynamic>{ ) => <String, dynamic>{
'date': instance.date.toIso8601String(), 'date': instance.date.toIso8601String(),
'check_in_result': instance.checkInResult?.toJson(), 'check_in_result': instance.checkInResult?.toJson(),
'statuses': instance.statuses, 'statuses': instance.statuses.map((e) => e.toJson()).toList(),
}; };

View File

@@ -27,6 +27,7 @@ sealed class SnPost with _$SnPost {
@Default(0) int upvotes, @Default(0) int upvotes,
@Default(0) int downvotes, @Default(0) int downvotes,
@Default(0) int repliesCount, @Default(0) int repliesCount,
int? pinMode,
String? threadedPostId, String? threadedPostId,
SnPost? threadedPost, SnPost? threadedPost,
String? repliedPostId, String? repliedPostId,

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$SnPost { mixin _$SnPost {
String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; String? get slug; int get type; Map<String, dynamic>? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; String? get realmId; SnRealm? get realm; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; Map<String, bool> get reactionsMade; List<dynamic> get reactions; List<SnPostTag> get tags; List<SnPostCategory> get categories; List<dynamic> get collections; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get isTruncated; String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; String? get slug; int get type; Map<String, dynamic>? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; int? get pinMode; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; String? get realmId; SnRealm? get realm; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; Map<String, bool> get reactionsMade; List<dynamic> get reactions; List<SnPostTag> get tags; List<SnPostCategory> get categories; List<dynamic> get collections; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get isTruncated;
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $SnPostCopyWith<SnPost> get copyWith => _$SnPostCopyWithImpl<SnPost>(this as SnP
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactionsCount, reactionsCount)&&const DeepCollectionEquality().equals(other.reactionsMade, reactionsMade)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactionsCount, reactionsCount)&&const DeepCollectionEquality().equals(other.reactionsMade, reactionsMade)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactionsCount),const DeepCollectionEquality().hash(reactionsMade),const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),createdAt,updatedAt,deletedAt,isTruncated]); int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactionsCount),const DeepCollectionEquality().hash(reactionsMade),const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),createdAt,updatedAt,deletedAt,isTruncated]);
@override @override
String toString() { String toString() {
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)'; return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)';
} }
@@ -48,7 +48,7 @@ abstract mixin class $SnPostCopyWith<$Res> {
factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl; factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
}); });
@@ -65,7 +65,7 @@ class _$SnPostCopyWithImpl<$Res>
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
@@ -83,7 +83,8 @@ as int,viewsTotal: null == viewsTotal ? _self.viewsTotal : viewsTotal // ignore:
as int,upvotes: null == upvotes ? _self.upvotes : upvotes // ignore: cast_nullable_to_non_nullable as int,upvotes: null == upvotes ? _self.upvotes : upvotes // ignore: cast_nullable_to_non_nullable
as int,downvotes: null == downvotes ? _self.downvotes : downvotes // ignore: cast_nullable_to_non_nullable as int,downvotes: null == downvotes ? _self.downvotes : downvotes // ignore: cast_nullable_to_non_nullable
as int,repliesCount: null == repliesCount ? _self.repliesCount : repliesCount // ignore: cast_nullable_to_non_nullable as int,repliesCount: null == repliesCount ? _self.repliesCount : repliesCount // ignore: cast_nullable_to_non_nullable
as int,threadedPostId: freezed == threadedPostId ? _self.threadedPostId : threadedPostId // ignore: cast_nullable_to_non_nullable as int,pinMode: freezed == pinMode ? _self.pinMode : pinMode // ignore: cast_nullable_to_non_nullable
as int?,threadedPostId: freezed == threadedPostId ? _self.threadedPostId : threadedPostId // ignore: cast_nullable_to_non_nullable
as String?,threadedPost: freezed == threadedPost ? _self.threadedPost : threadedPost // ignore: cast_nullable_to_non_nullable as String?,threadedPost: freezed == threadedPost ? _self.threadedPost : threadedPost // ignore: cast_nullable_to_non_nullable
as SnPost?,repliedPostId: freezed == repliedPostId ? _self.repliedPostId : repliedPostId // ignore: cast_nullable_to_non_nullable as SnPost?,repliedPostId: freezed == repliedPostId ? _self.repliedPostId : repliedPostId // ignore: cast_nullable_to_non_nullable
as String?,repliedPost: freezed == repliedPost ? _self.repliedPost : repliedPost // ignore: cast_nullable_to_non_nullable as String?,repliedPost: freezed == repliedPost ? _self.repliedPost : repliedPost // ignore: cast_nullable_to_non_nullable
@@ -242,10 +243,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnPost() when $default != null: case _SnPost() when $default != null:
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _: return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _:
return orElse(); return orElse();
} }
@@ -263,10 +264,10 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPost(): case _SnPost():
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);} return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -280,10 +281,10 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPost() when $default != null: case _SnPost() when $default != null:
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _: return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _:
return null; return null;
} }
@@ -295,7 +296,7 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
@JsonSerializable() @JsonSerializable()
class _SnPost implements SnPost { class _SnPost implements SnPost {
const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.slug, this.type = 0, final Map<String, dynamic>? meta, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, this.realmId, this.realm, final List<SnCloudFile> attachments = const [], required this.publisher, final Map<String, int> reactionsCount = const {}, final Map<String, bool> reactionsMade = const {}, final List<dynamic> reactions = const [], final List<SnPostTag> tags = const [], final List<SnPostCategory> categories = const [], final List<dynamic> collections = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactionsMade = reactionsMade,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections; const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.slug, this.type = 0, final Map<String, dynamic>? meta, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.pinMode, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, this.realmId, this.realm, final List<SnCloudFile> attachments = const [], required this.publisher, final Map<String, int> reactionsCount = const {}, final Map<String, bool> reactionsMade = const {}, final List<dynamic> reactions = const [], final List<SnPostTag> tags = const [], final List<SnPostCategory> categories = const [], final List<dynamic> collections = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactionsMade = reactionsMade,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections;
factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json); factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json);
@override final String id; @override final String id;
@@ -322,6 +323,7 @@ class _SnPost implements SnPost {
@override@JsonKey() final int upvotes; @override@JsonKey() final int upvotes;
@override@JsonKey() final int downvotes; @override@JsonKey() final int downvotes;
@override@JsonKey() final int repliesCount; @override@JsonKey() final int repliesCount;
@override final int? pinMode;
@override final String? threadedPostId; @override final String? threadedPostId;
@override final SnPost? threadedPost; @override final SnPost? threadedPost;
@override final String? repliedPostId; @override final String? repliedPostId;
@@ -398,16 +400,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactionsCount, _reactionsCount)&&const DeepCollectionEquality().equals(other._reactionsMade, _reactionsMade)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactionsCount, _reactionsCount)&&const DeepCollectionEquality().equals(other._reactionsMade, _reactionsMade)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(_meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactionsCount),const DeepCollectionEquality().hash(_reactionsMade),const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),createdAt,updatedAt,deletedAt,isTruncated]); int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(_meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactionsCount),const DeepCollectionEquality().hash(_reactionsMade),const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),createdAt,updatedAt,deletedAt,isTruncated]);
@override @override
String toString() { String toString() {
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)'; return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)';
} }
@@ -418,7 +420,7 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl; factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
}); });
@@ -435,7 +437,7 @@ class __$SnPostCopyWithImpl<$Res>
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
return _then(_SnPost( return _then(_SnPost(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
@@ -453,7 +455,8 @@ as int,viewsTotal: null == viewsTotal ? _self.viewsTotal : viewsTotal // ignore:
as int,upvotes: null == upvotes ? _self.upvotes : upvotes // ignore: cast_nullable_to_non_nullable as int,upvotes: null == upvotes ? _self.upvotes : upvotes // ignore: cast_nullable_to_non_nullable
as int,downvotes: null == downvotes ? _self.downvotes : downvotes // ignore: cast_nullable_to_non_nullable as int,downvotes: null == downvotes ? _self.downvotes : downvotes // ignore: cast_nullable_to_non_nullable
as int,repliesCount: null == repliesCount ? _self.repliesCount : repliesCount // ignore: cast_nullable_to_non_nullable as int,repliesCount: null == repliesCount ? _self.repliesCount : repliesCount // ignore: cast_nullable_to_non_nullable
as int,threadedPostId: freezed == threadedPostId ? _self.threadedPostId : threadedPostId // ignore: cast_nullable_to_non_nullable as int,pinMode: freezed == pinMode ? _self.pinMode : pinMode // ignore: cast_nullable_to_non_nullable
as int?,threadedPostId: freezed == threadedPostId ? _self.threadedPostId : threadedPostId // ignore: cast_nullable_to_non_nullable
as String?,threadedPost: freezed == threadedPost ? _self.threadedPost : threadedPost // ignore: cast_nullable_to_non_nullable as String?,threadedPost: freezed == threadedPost ? _self.threadedPost : threadedPost // ignore: cast_nullable_to_non_nullable
as SnPost?,repliedPostId: freezed == repliedPostId ? _self.repliedPostId : repliedPostId // ignore: cast_nullable_to_non_nullable as SnPost?,repliedPostId: freezed == repliedPostId ? _self.repliedPostId : repliedPostId // ignore: cast_nullable_to_non_nullable
as String?,repliedPost: freezed == repliedPost ? _self.repliedPost : repliedPost // ignore: cast_nullable_to_non_nullable as String?,repliedPost: freezed == repliedPost ? _self.repliedPost : repliedPost // ignore: cast_nullable_to_non_nullable

View File

@@ -29,6 +29,7 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
upvotes: (json['upvotes'] as num?)?.toInt() ?? 0, upvotes: (json['upvotes'] as num?)?.toInt() ?? 0,
downvotes: (json['downvotes'] as num?)?.toInt() ?? 0, downvotes: (json['downvotes'] as num?)?.toInt() ?? 0,
repliesCount: (json['replies_count'] as num?)?.toInt() ?? 0, repliesCount: (json['replies_count'] as num?)?.toInt() ?? 0,
pinMode: (json['pin_mode'] as num?)?.toInt(),
threadedPostId: json['threaded_post_id'] as String?, threadedPostId: json['threaded_post_id'] as String?,
threadedPost: threadedPost:
json['threaded_post'] == null json['threaded_post'] == null
@@ -109,6 +110,7 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
'upvotes': instance.upvotes, 'upvotes': instance.upvotes,
'downvotes': instance.downvotes, 'downvotes': instance.downvotes,
'replies_count': instance.repliesCount, 'replies_count': instance.repliesCount,
'pin_mode': instance.pinMode,
'threaded_post_id': instance.threadedPostId, 'threaded_post_id': instance.threadedPostId,
'threaded_post': instance.threadedPost?.toJson(), 'threaded_post': instance.threadedPost?.toJson(),
'replied_post_id': instance.repliedPostId, 'replied_post_id': instance.repliedPostId,

View File

@@ -1,11 +1,9 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
part 'translate.freezed.dart'; part 'translate.freezed.dart';
part 'translate.g.dart'; part 'translate.g.dart';
@@ -29,10 +27,17 @@ Future<String> translateString(Ref ref, TranslateQuery query) async {
@riverpod @riverpod
String? detectStringLanguage(Ref ref, String text) { String? detectStringLanguage(Ref ref, String text) {
try { bool isChinese(String text) {
return langdetect.detectLangs(text).firstOrNull?.lang; final chineseRegex = RegExp(r'[\u4e00-\u9fff]');
} catch (err) { return chineseRegex.hasMatch(text);
log('[Language] Unable to detect text\'s language: $text');
return null;
} }
bool isEnglish(String text) {
final englishRegex = RegExp(r'[a-zA-Z]');
return englishRegex.hasMatch(text) && !isChinese(text);
}
if (isChinese(text)) return "zh";
if (isEnglish(text)) return "en";
return null;
} }

View File

@@ -38,6 +38,7 @@ import 'package:island/screens/chat/chat.dart';
import 'package:island/screens/chat/room.dart'; import 'package:island/screens/chat/room.dart';
import 'package:island/screens/chat/room_detail.dart'; import 'package:island/screens/chat/room_detail.dart';
import 'package:island/screens/chat/call.dart'; import 'package:island/screens/chat/call.dart';
import 'package:island/screens/chat/search_messages_screen.dart';
import 'package:island/screens/creators/hub.dart'; import 'package:island/screens/creators/hub.dart';
import 'package:island/screens/creators/posts/post_manage_list.dart'; import 'package:island/screens/creators/posts/post_manage_list.dart';
import 'package:island/screens/creators/stickers/stickers.dart'; import 'package:island/screens/creators/stickers/stickers.dart';
@@ -555,6 +556,14 @@ final routerProvider = Provider<GoRouter>((ref) {
return ChatDetailScreen(id: id); return ChatDetailScreen(id: id);
}, },
), ),
GoRoute(
name: 'searchMessages',
path: '/chat/:id/search',
builder: (context, state) {
final id = state.pathParameters['id']!;
return SearchMessagesScreen(roomId: id);
},
),
], ],
), ),

View File

@@ -68,6 +68,7 @@ class AccountScreen extends HookConsumerWidget {
body: SingleChildScrollView( body: SingleChildScrollView(
padding: getTabbedPadding(context), padding: getTabbedPadding(context),
child: Column( child: Column(
spacing: 4,
children: <Widget>[ children: <Widget>[
Card( Card(
child: Column( child: Column(
@@ -112,20 +113,22 @@ class AccountScreen extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.baseline, crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic, textBaseline: TextBaseline.alphabetic,
children: [ children: [
AccountName( Flexible(
account: user.value!, child: AccountName(
style: TextStyle( account: user.value!,
fontSize: 16, style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16,
fontWeight: FontWeight.bold,
),
), ),
), ),
Text('@${user.value!.name}'), Flexible(child: Text('@${user.value!.name}')),
], ],
), ),
Text( Text(
(user.value!.profile.bio.isNotEmpty) (user.value!.profile.bio.isNotEmpty)
? user.value!.profile.bio ? user.value!.profile.bio
: 'No description yet.', : 'descriptionNone'.tr(),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@@ -158,8 +161,16 @@ class AccountScreen extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Icon(Symbols.draw, size: 28).padding(bottom: 8), Icon(Symbols.draw, size: 28).padding(bottom: 8),
Text('creatorHub').tr().fontSize(16).bold(), Text(
Text('creatorHubDescription').tr(), 'creatorHub',
maxLines: 1,
overflow: TextOverflow.ellipsis,
).tr().fontSize(16).bold(),
Text(
'creatorHubDescription',
maxLines: 2,
overflow: TextOverflow.ellipsis,
).tr(),
], ],
).padding(horizontal: 16, vertical: 12), ).padding(horizontal: 16, vertical: 12),
onTap: () { onTap: () {
@@ -176,8 +187,16 @@ class AccountScreen extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Icon(Symbols.code, size: 28).padding(bottom: 8), Icon(Symbols.code, size: 28).padding(bottom: 8),
Text('developerPortal').tr().fontSize(16).bold(), Text(
Text('developerPortalDescription').tr(), 'developerPortal',
maxLines: 1,
overflow: TextOverflow.ellipsis,
).tr().fontSize(16).bold(),
Text(
'developerPortalDescription',
maxLines: 2,
overflow: TextOverflow.ellipsis,
).tr(),
], ],
).padding(horizontal: 16, vertical: 12), ).padding(horizontal: 16, vertical: 12),
onTap: () { onTap: () {

View File

@@ -95,8 +95,24 @@ class LevelingScreen extends HookConsumerWidget {
title: Text('levelingProgress'.tr()), title: Text('levelingProgress'.tr()),
bottom: TabBar( bottom: TabBar(
tabs: [ tabs: [
Tab(text: 'leveling'.tr()), Tab(
Tab(text: 'stellarProgram'.tr()), child: Text(
'leveling'.tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
Tab(
child: Text(
'stellarProgram'.tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
], ],
), ),
), ),

View File

@@ -14,7 +14,6 @@ import 'package:island/screens/account/me/settings_connections.dart';
import 'package:island/screens/account/me/settings_contacts.dart'; import 'package:island/screens/account/me/settings_contacts.dart';
import 'package:island/screens/auth/captcha.dart'; import 'package:island/screens/auth/captcha.dart';
import 'package:island/screens/auth/login.dart'; import 'package:island/screens/auth/login.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/account_devices.dart'; import 'package:island/widgets/account/account_devices.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
@@ -57,7 +56,6 @@ class AccountSettingsScreen extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final isDesktop = final isDesktop =
!kIsWeb && (Platform.isWindows || Platform.isMacOS || Platform.isLinux); !kIsWeb && (Platform.isWindows || Platform.isMacOS || Platform.isLinux);
final isWide = isWideScreen(context);
Future<void> requestAccountDeletion() async { Future<void> requestAccountDeletion() async {
final confirm = await showConfirmAlert( final confirm = await showConfirmAlert(
@@ -440,51 +438,19 @@ class AccountSettingsScreen extends HookConsumerWidget {
// Create a responsive layout based on screen width // Create a responsive layout based on screen width
Widget buildSettingsList() { Widget buildSettingsList() {
if (isWide) { return Column(
// Two-column layout for wide screens crossAxisAlignment: CrossAxisAlignment.start,
return Row( children: [
crossAxisAlignment: CrossAxisAlignment.start, _SettingsSection(
children: [ title: 'accountSecurityTitle',
Expanded( children: securitySettings,
child: Column( ),
crossAxisAlignment: CrossAxisAlignment.start, _SettingsSection(
children: [ title: 'accountDangerZoneTitle',
_SettingsSection( children: dangerZoneSettings,
title: 'accountSecurityTitle', ),
children: securitySettings, ],
), );
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_SettingsSection(
title: 'accountDangerZoneTitle',
children: dangerZoneSettings,
),
],
),
),
],
);
} else {
// Single column layout for narrow screens
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_SettingsSection(
title: 'accountSecurityTitle',
children: securitySettings,
),
_SettingsSection(
title: 'accountDangerZoneTitle',
children: dangerZoneSettings,
),
],
);
}
} }
return AppScaffold( return AppScaffold(

View File

@@ -1,10 +1,12 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/auth.dart'; import 'package:island/models/auth.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/screens/account/me/account_settings.dart'; import 'package:island/screens/account/me/account_settings.dart';
import 'package:island/screens/auth/oidc.native.dart'; import 'package:island/screens/auth/oidc.native.dart';
@@ -16,6 +18,7 @@ import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart'; import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:url_launcher/url_launcher_string.dart';
// Helper function to get provider icon and localized name // Helper function to get provider icon and localized name
Widget getProviderIcon(String provider, {double size = 24, Color? color}) { Widget getProviderIcon(String provider, {double size = 24, Color? color}) {
@@ -165,9 +168,7 @@ class AccountConnectionNewSheet extends HookConsumerWidget {
scopes: [AppleIDAuthorizationScopes.email], scopes: [AppleIDAuthorizationScopes.email],
webAuthenticationOptions: WebAuthenticationOptions( webAuthenticationOptions: WebAuthenticationOptions(
clientId: 'dev.solsynth.solarpass', clientId: 'dev.solsynth.solarpass',
redirectUri: Uri.parse( redirectUri: Uri.parse('https://id.solian.app/auth/callback'),
'https://id.solian.app/auth/callback/apple',
),
), ),
); );
@@ -195,17 +196,25 @@ class AccountConnectionNewSheet extends HookConsumerWidget {
case 'github': case 'github':
case 'discord': case 'discord':
case 'afdian': case 'afdian':
await Navigator.of(context, rootNavigator: true).push( if (kIsWeb) {
MaterialPageRoute( final serverUrl = ref.watch(serverUrlProvider);
builder: final accessToken = ref.watch(tokenProvider);
(context) => OidcScreen( launchUrlString(
provider: selectedProvider.value.toLowerCase(), '$serverUrl/id/auth/login/${selectedProvider.value}?tk=${accessToken!.token}',
title: );
'Connect with ${selectedProvider.value.capitalizeEachWord()}', } else {
), await Navigator.of(context, rootNavigator: true).push(
), MaterialPageRoute(
); builder:
if (context.mounted) Navigator.pop(context, true); (context) => OidcScreen(
provider: selectedProvider.value.toLowerCase(),
title:
'Connect with ${selectedProvider.value.capitalizeEachWord()}',
),
),
);
if (context.mounted) Navigator.pop(context, true);
}
break; break;
default: default:
showSnackBar('accountConnectionAddError'.tr()); showSnackBar('accountConnectionAddError'.tr());

View File

@@ -2,6 +2,7 @@ import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
@@ -279,6 +280,36 @@ class AccountProfileScreen extends HookConsumerWidget {
if (data.profile.lastName.isNotEmpty) Text(data.profile.lastName), if (data.profile.lastName.isNotEmpty) Text(data.profile.lastName),
], ],
), ),
Tooltip(
message: 'creditsStatus'.tr(),
child: Row(
spacing: 6,
children: [
Icon(Symbols.star, size: 17, fill: 1).padding(right: 2),
Text('${data.profile.socialCredits.toStringAsFixed(2)} pts'),
Text('·').bold(),
switch (data.profile.socialCreditsLevel) {
-1 => Text('socialCreditsLevelPoor').tr(),
0 => Text('socialCreditsLevelNormal').tr(),
1 => Text('socialCreditsLevelGood').tr(),
2 => Text('socialCreditsLevelExcellent').tr(),
_ => Text('unknown').tr(),
},
],
),
),
InkWell(
child: Row(
spacing: 6,
children: [
Icon(Symbols.fingerprint, size: 17, fill: 1).padding(right: 2),
Text(data.id),
],
),
onTap: () {
Clipboard.setData(ClipboardData(text: data.id));
},
),
]; ];
} }

View File

@@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/widgets/account/account_pfc.dart';
import 'package:island/widgets/account/account_picker.dart'; import 'package:island/widgets/account/account_picker.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
@@ -99,7 +100,10 @@ class RelationshipListTile extends StatelessWidget {
return ListTile( return ListTile(
contentPadding: const EdgeInsets.only(left: 16, right: 12), contentPadding: const EdgeInsets.only(left: 16, right: 12),
leading: ProfilePictureWidget(fileId: account.profile.picture?.id), leading: AccountPfcGestureDetector(
uname: account.name,
child: ProfilePictureWidget(fileId: account.profile.picture?.id),
),
title: Row( title: Row(
spacing: 6, spacing: 6,
children: [ children: [

View File

@@ -700,45 +700,48 @@ class _LoginLookupScreen extends HookConsumerWidget {
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
onSubmitted: isBusy.value ? null : (_) => performNewTicket(), onSubmitted: isBusy.value ? null : (_) => performNewTicket(),
).padding(horizontal: 7), ).padding(horizontal: 7),
Row( if (!kIsWeb)
spacing: 6, Row(
crossAxisAlignment: CrossAxisAlignment.center, spacing: 6,
children: <Widget>[ crossAxisAlignment: CrossAxisAlignment.center,
Text("loginOr").tr().fontSize(11).opacity(0.85), children: <Widget>[
const Gap(8), Text("loginOr").tr().fontSize(11).opacity(0.85),
Spacer(), const Gap(8),
IconButton.filledTonal( Spacer(),
onPressed: () => withOidc('github'), IconButton.filledTonal(
padding: EdgeInsets.zero, onPressed: () => withOidc('github'),
icon: getProviderIcon( padding: EdgeInsets.zero,
"github", icon: getProviderIcon(
size: 16, "github",
color: Theme.of(context).colorScheme.onPrimaryContainer, size: 16,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
tooltip: 'GitHub',
), ),
tooltip: 'GitHub', IconButton.filledTonal(
), onPressed: () => withOidc('google'),
IconButton.filledTonal( padding: EdgeInsets.zero,
onPressed: () => withOidc('google'), icon: getProviderIcon(
padding: EdgeInsets.zero, "google",
icon: getProviderIcon( size: 16,
"google", color: Theme.of(context).colorScheme.onPrimaryContainer,
size: 16, ),
color: Theme.of(context).colorScheme.onPrimaryContainer, tooltip: 'Google',
), ),
tooltip: 'Google', IconButton.filledTonal(
), onPressed: withApple,
IconButton.filledTonal( padding: EdgeInsets.zero,
onPressed: withApple, icon: getProviderIcon(
padding: EdgeInsets.zero, "apple",
icon: getProviderIcon( size: 16,
"apple", color: Theme.of(context).colorScheme.onPrimaryContainer,
size: 16, ),
color: Theme.of(context).colorScheme.onPrimaryContainer, tooltip: 'Apple Account',
), ),
tooltip: 'Apple Account', ],
), ).padding(horizontal: 8, vertical: 8)
], else
).padding(horizontal: 8, vertical: 8), const Gap(12),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [

View File

@@ -4,6 +4,7 @@ import "dart:developer" as developer;
import "dart:io"; import "dart:io";
import "package:dio/dio.dart"; import "package:dio/dio.dart";
import "package:easy_localization/easy_localization.dart"; import "package:easy_localization/easy_localization.dart";
import "package:file_picker/file_picker.dart";
import "package:flutter/foundation.dart"; import "package:flutter/foundation.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:go_router/go_router.dart"; import "package:go_router/go_router.dart";
@@ -72,6 +73,207 @@ class _AppLifecycleObserver extends WidgetsBindingObserver {
} }
} }
class _PublicRoomPreview extends HookConsumerWidget {
final String id;
final SnChatRoom room;
const _PublicRoomPreview({required this.id, required this.room});
@override
Widget build(BuildContext context, WidgetRef ref) {
final messages = ref.watch(messagesNotifierProvider(id));
final messagesNotifier = ref.read(messagesNotifierProvider(id).notifier);
final scrollController = useScrollController();
final listController = useMemoized(() => ListController(), []);
var isLoading = false;
// Add scroll listener for pagination
useEffect(() {
void onScroll() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
if (isLoading) return;
isLoading = true;
messagesNotifier.loadMore().then((_) => isLoading = false);
}
}
scrollController.addListener(onScroll);
return () => scrollController.removeListener(onScroll);
}, [scrollController]);
Widget chatMessageListWidget(List<LocalChatMessage> messageList) =>
SuperListView.builder(
listController: listController,
padding: EdgeInsets.symmetric(vertical: 16),
controller: scrollController,
reverse: true, // Show newest messages at the bottom
itemCount: messageList.length,
findChildIndexCallback: (key) {
final valueKey = key as ValueKey;
final messageId = valueKey.value as String;
return messageList.indexWhere((m) => m.id == messageId);
},
extentEstimation: (_, _) => 40,
itemBuilder: (context, index) {
final message = messageList[index];
final nextMessage =
index < messageList.length - 1 ? messageList[index + 1] : null;
final isLastInGroup =
nextMessage == null ||
nextMessage.senderId != message.senderId ||
nextMessage.createdAt
.difference(message.createdAt)
.inMinutes
.abs() >
3;
return MessageItem(
message: message,
isCurrentUser: false, // User is not a member, so not current user
onAction: null, // No actions allowed in preview mode
onJump: (_) {}, // No jump functionality in preview
progress: null,
showAvatar: isLastInGroup,
);
},
);
final compactHeader = isWideScreen(context);
Widget comfortHeaderWidget() => Column(
spacing: 4,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 26,
width: 26,
child:
(room.type == 1 && room.picture?.id == null)
? SplitAvatarWidget(
filesId:
room.members!
.map((e) => e.account.profile.picture?.id)
.toList(),
)
: room.picture?.id != null
? ProfilePictureWidget(
fileId: room.picture?.id,
fallbackIcon: Symbols.chat,
)
: CircleAvatar(
child: Text(
room.name![0].toUpperCase(),
style: const TextStyle(fontSize: 12),
),
),
),
Text(
(room.type == 1 && room.name == null)
? room.members!.map((e) => e.account.nick).join(', ')
: room.name!,
).fontSize(15),
],
);
Widget compactHeaderWidget() => Row(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 26,
width: 26,
child:
(room.type == 1 && room.picture?.id == null)
? SplitAvatarWidget(
filesId:
room.members!
.map((e) => e.account.profile.picture?.id)
.toList(),
)
: room.picture?.id != null
? ProfilePictureWidget(
fileId: room.picture?.id,
fallbackIcon: Symbols.chat,
)
: CircleAvatar(
child: Text(
room.name![0].toUpperCase(),
style: const TextStyle(fontSize: 12),
),
),
),
Text(
(room.type == 1 && room.name == null)
? room.members!.map((e) => e.account.nick).join(', ')
: room.name!,
).fontSize(19),
],
);
return AppScaffold(
appBar: AppBar(
leading: !compactHeader ? const Center(child: PageBackButton()) : null,
automaticallyImplyLeading: false,
toolbarHeight: compactHeader ? null : 64,
title: compactHeader ? compactHeaderWidget() : comfortHeaderWidget(),
actions: [
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () {
context.pushNamed('chatDetail', pathParameters: {'id': id});
},
),
const Gap(8),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: messages.when(
data:
(messageList) =>
messageList.isEmpty
? Center(child: Text('No messages yet'.tr()))
: chatMessageListWidget(messageList),
loading: () => const Center(child: CircularProgressIndicator()),
error:
(error, _) => ResponseErrorWidget(
error: error,
onRetry: () => messagesNotifier.loadInitial(),
),
),
),
// Join button at the bottom for public rooms
Container(
padding: const EdgeInsets.all(16),
child: FilledButton.tonalIcon(
onPressed: () async {
try {
showLoadingModal(context);
final apiClient = ref.read(apiClientProvider);
await apiClient.post('/sphere/chat/${room.id}/members/me');
ref.invalidate(chatroomIdentityProvider(id));
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
}
},
label: Text('chatJoin').tr(),
icon: const Icon(Icons.add),
),
),
],
),
);
}
}
@riverpod @riverpod
class MessagesNotifier extends _$MessagesNotifier { class MessagesNotifier extends _$MessagesNotifier {
late final Dio _apiClient; late final Dio _apiClient;
@@ -82,6 +284,9 @@ class MessagesNotifier extends _$MessagesNotifier {
final Map<String, LocalChatMessage> _pendingMessages = {}; final Map<String, LocalChatMessage> _pendingMessages = {};
final Map<String, Map<int, double>> _fileUploadProgress = {}; final Map<String, Map<int, double>> _fileUploadProgress = {};
int? _totalCount; int? _totalCount;
String? _searchQuery;
bool? _withLinks;
bool? _withAttachments;
late final String _roomId; late final String _roomId;
int _currentPage = 0; int _currentPage = 0;
@@ -96,28 +301,42 @@ class MessagesNotifier extends _$MessagesNotifier {
_database = ref.watch(databaseProvider); _database = ref.watch(databaseProvider);
final room = await ref.watch(chatroomProvider(roomId).future); final room = await ref.watch(chatroomProvider(roomId).future);
final identity = await ref.watch(chatroomIdentityProvider(roomId).future); final identity = await ref.watch(chatroomIdentityProvider(roomId).future);
if (room == null || identity == null) {
throw Exception('Room or identity not found'); if (room == null) {
throw Exception('Room not found');
} }
_room = room; _room = room;
_identity = identity;
// Allow building even if identity is null for public rooms
if (identity != null) {
_identity = identity;
}
developer.log( developer.log(
'MessagesNotifier built for room $roomId', 'MessagesNotifier built for room $roomId',
name: 'MessagesNotifier', name: 'MessagesNotifier',
); );
ref.listen(appLifecycleStateProvider, (_, next) { // Only setup sync and lifecycle listeners if user is a member
if (next.hasValue && next.value == AppLifecycleState.resumed) { if (identity != null) {
developer.log( ref.listen(appLifecycleStateProvider, (_, next) {
'App resumed, syncing messages', if (next.hasValue && next.value == AppLifecycleState.resumed) {
name: 'MessagesNotifier', developer.log(
); 'App resumed, syncing messages',
syncMessages(); name: 'MessagesNotifier',
} );
}); syncMessages();
}
});
}
return await loadInitial(); loadInitial();
return [];
}
List<LocalChatMessage> _sortMessages(List<LocalChatMessage> messages) {
messages.sort((a, b) => b.createdAt.compareTo(a.createdAt));
return messages;
} }
Future<List<LocalChatMessage>> _getCachedMessages({ Future<List<LocalChatMessage>> _getCachedMessages({
@@ -128,13 +347,32 @@ class MessagesNotifier extends _$MessagesNotifier {
'Getting cached messages from offset $offset, take $take', 'Getting cached messages from offset $offset, take $take',
name: 'MessagesNotifier', name: 'MessagesNotifier',
); );
final dbMessages = await _database.getMessagesForRoom( final List<LocalChatMessage> dbMessages;
_roomId, if (_searchQuery != null && _searchQuery!.isNotEmpty) {
offset: offset, dbMessages = await _database.searchMessages(_roomId, _searchQuery ?? '');
limit: take, } else {
); final chatMessagesFromDb = await _database.getMessagesForRoom(
final dbLocalMessages = _roomId,
dbMessages.map(_database.companionToMessage).toList(); offset: offset,
limit: take,
);
dbMessages =
chatMessagesFromDb.map(_database.companionToMessage).toList();
}
List<LocalChatMessage> filteredMessages = dbMessages;
if (_withLinks == true) {
filteredMessages =
filteredMessages.where((msg) => _hasLink(msg)).toList();
}
if (_withAttachments == true) {
filteredMessages =
filteredMessages.where((msg) => _hasAttachment(msg)).toList();
}
final dbLocalMessages = filteredMessages;
if (offset == 0) { if (offset == 0) {
final pendingForRoom = final pendingForRoom =
@@ -143,7 +381,7 @@ class MessagesNotifier extends _$MessagesNotifier {
.toList(); .toList();
final allMessages = [...pendingForRoom, ...dbLocalMessages]; final allMessages = [...pendingForRoom, ...dbLocalMessages];
allMessages.sort((a, b) => b.createdAt.compareTo(a.createdAt)); _sortMessages(allMessages); // Use the helper function
final uniqueMessages = <LocalChatMessage>[]; final uniqueMessages = <LocalChatMessage>[];
final seenIds = <String>{}; final seenIds = <String>{};
@@ -218,7 +456,7 @@ class MessagesNotifier extends _$MessagesNotifier {
_isSyncing = true; _isSyncing = true;
developer.log('Starting message sync', name: 'MessagesNotifier'); developer.log('Starting message sync', name: 'MessagesNotifier');
ref.read(isSyncingProvider.notifier).state = true; Future.microtask(() => ref.read(isSyncingProvider.notifier).state = true);
try { try {
final dbMessages = await _database.getMessagesForRoom( final dbMessages = await _database.getMessagesForRoom(
_room.id, _room.id,
@@ -279,7 +517,9 @@ class MessagesNotifier extends _$MessagesNotifier {
showErrorAlert(err); showErrorAlert(err);
} finally { } finally {
developer.log('Finished message sync', name: 'MessagesNotifier'); developer.log('Finished message sync', name: 'MessagesNotifier');
ref.read(isSyncingProvider.notifier).state = false; Future.microtask(
() => ref.read(isSyncingProvider.notifier).state = false,
);
_isSyncing = false; _isSyncing = false;
} }
} }
@@ -290,7 +530,9 @@ class MessagesNotifier extends _$MessagesNotifier {
bool synced = false, bool synced = false,
}) async { }) async {
try { try {
if (offset == 0 && !synced) { if (offset == 0 &&
!synced &&
(_searchQuery == null || _searchQuery!.isEmpty)) {
_fetchAndCacheMessages(offset: 0, take: take).catchError((_) { _fetchAndCacheMessages(offset: 0, take: take).catchError((_) {
return <LocalChatMessage>[]; return <LocalChatMessage>[];
}); });
@@ -305,7 +547,11 @@ class MessagesNotifier extends _$MessagesNotifier {
return localMessages; return localMessages;
} }
return await _fetchAndCacheMessages(offset: offset, take: take); if (_searchQuery == null || _searchQuery!.isEmpty) {
return await _fetchAndCacheMessages(offset: offset, take: take);
} else {
return []; // If searching, and no local messages, don't fetch from network
}
} catch (e) { } catch (e) {
final localMessages = await _getCachedMessages( final localMessages = await _getCachedMessages(
offset: offset, offset: offset,
@@ -319,13 +565,15 @@ class MessagesNotifier extends _$MessagesNotifier {
} }
} }
Future<List<LocalChatMessage>> loadInitial() async { Future<void> loadInitial() async {
developer.log('Loading initial messages', name: 'MessagesNotifier'); developer.log('Loading initial messages', name: 'MessagesNotifier');
syncMessages(); if (_searchQuery == null || _searchQuery!.isEmpty) {
syncMessages();
}
final messages = await _getCachedMessages(offset: 0, take: 100); final messages = await _getCachedMessages(offset: 0, take: 100);
_currentPage = 0; _currentPage = 0;
_hasMore = messages.length == _pageSize; _hasMore = messages.length == _pageSize;
return messages; state = AsyncValue.data(messages);
} }
Future<void> loadMore() async { Future<void> loadMore() async {
@@ -344,7 +592,9 @@ class MessagesNotifier extends _$MessagesNotifier {
_hasMore = false; _hasMore = false;
} }
state = AsyncValue.data([...currentMessages, ...newMessages]); state = AsyncValue.data(
_sortMessages([...currentMessages, ...newMessages]),
);
} catch (err, stackTrace) { } catch (err, stackTrace) {
developer.log( developer.log(
'Error loading more messages', 'Error loading more messages',
@@ -455,10 +705,13 @@ class MessagesNotifier extends _$MessagesNotifier {
final currentMessages = state.value ?? []; final currentMessages = state.value ?? [];
if (editingTo != null) { if (editingTo != null) {
final newMessages = currentMessages final newMessages =
.where((m) => m.id != localMessage.id) // remove pending message currentMessages
.map((m) => m.id == editingTo.id ? updatedMessage : m) // update original message .where((m) => m.id != localMessage.id) // remove pending message
.toList(); .map(
(m) => m.id == editingTo.id ? updatedMessage : m,
) // update original message
.toList();
state = AsyncValue.data(newMessages); state = AsyncValue.data(newMessages);
} else { } else {
final newMessages = final newMessages =
@@ -566,7 +819,7 @@ class MessagesNotifier extends _$MessagesNotifier {
} }
return m; return m;
}).toList(); }).toList();
state = AsyncValue.data(newMessages); state = AsyncValue.data(_sortMessages(newMessages));
showErrorAlert(e); showErrorAlert(e);
} }
} }
@@ -626,7 +879,7 @@ class MessagesNotifier extends _$MessagesNotifier {
if (index >= 0) { if (index >= 0) {
final newList = [...currentMessages]; final newList = [...currentMessages];
newList[index] = updatedMessage; newList[index] = updatedMessage;
state = AsyncValue.data(newList); state = AsyncValue.data(_sortMessages(newList));
} }
} }
@@ -686,6 +939,20 @@ class MessagesNotifier extends _$MessagesNotifier {
} }
} }
void searchMessages(String query, {bool? withLinks, bool? withAttachments}) {
_searchQuery = query.trim();
_withLinks = withLinks;
_withAttachments = withAttachments;
loadInitial();
}
void clearSearch() {
_searchQuery = null;
_withLinks = null;
_withAttachments = null;
loadInitial();
}
Future<LocalChatMessage?> fetchMessageById(String messageId) async { Future<LocalChatMessage?> fetchMessageById(String messageId) async {
developer.log( developer.log(
'Fetching message by id $messageId', 'Fetching message by id $messageId',
@@ -715,6 +982,18 @@ class MessagesNotifier extends _$MessagesNotifier {
rethrow; rethrow;
} }
} }
bool _hasLink(LocalChatMessage message) {
final content = message.toRemoteMessage().content;
if (content == null) return false;
final urlRegex = RegExp(r'https?://[^\s/$.?#].[^\s]*');
return urlRegex.hasMatch(content);
}
bool _hasAttachment(LocalChatMessage message) {
final remoteMessage = message.toRemoteMessage();
return remoteMessage.attachments.isNotEmpty;
}
} }
class ChatRoomScreen extends HookConsumerWidget { class ChatRoomScreen extends HookConsumerWidget {
@@ -734,57 +1013,77 @@ class ChatRoomScreen extends HookConsumerWidget {
); );
} else if (chatIdentity.value == null) { } else if (chatIdentity.value == null) {
// Identity was not found, user was not joined // Identity was not found, user was not joined
return AppScaffold( return chatRoom.when(
appBar: AppBar(leading: const PageBackButton()), data: (room) {
body: Center( if (room!.isPublic) {
child: // Show public room preview with messages but no input
ConstrainedBox( return _PublicRoomPreview(id: id, room: room);
constraints: const BoxConstraints(maxWidth: 280), } else {
child: Column( // Show regular "not joined" screen for private rooms
crossAxisAlignment: CrossAxisAlignment.center, return AppScaffold(
mainAxisAlignment: MainAxisAlignment.center, appBar: AppBar(leading: const PageBackButton()),
children: [ body: Center(
Icon( child:
chatRoom.value?.isCommunity == true ConstrainedBox(
? Symbols.person_add constraints: const BoxConstraints(maxWidth: 280),
: Symbols.person_remove, child: Column(
size: 36, crossAxisAlignment: CrossAxisAlignment.center,
fill: 1, mainAxisAlignment: MainAxisAlignment.center,
).padding(bottom: 4), children: [
Text('chatNotJoined').tr(), Icon(
if (chatRoom.value?.isCommunity != true) room.isCommunity == true
Text( ? Symbols.person_add
'chatUnableJoin', : Symbols.person_remove,
textAlign: TextAlign.center, size: 36,
).tr().bold() fill: 1,
else ).padding(bottom: 4),
FilledButton.tonalIcon( Text('chatNotJoined').tr(),
onPressed: () async { if (room.isCommunity != true)
try { Text(
showLoadingModal(context); 'chatUnableJoin',
final apiClient = ref.read(apiClientProvider); textAlign: TextAlign.center,
if (chatRoom.value == null) { ).tr().bold()
hideLoadingModal(context); else
return; FilledButton.tonalIcon(
} onPressed: () async {
try {
await apiClient.post( showLoadingModal(context);
'/sphere/chat/${chatRoom.value!.id}/members/me', final apiClient = ref.read(apiClientProvider);
); await apiClient.post(
ref.invalidate(chatroomIdentityProvider(id)); '/sphere/chat/${room.id}/members/me',
} catch (err) { );
showErrorAlert(err); ref.invalidate(chatroomIdentityProvider(id));
} finally { } catch (err) {
if (context.mounted) hideLoadingModal(context); showErrorAlert(err);
} } finally {
}, if (context.mounted) {
label: Text('chatJoin').tr(), hideLoadingModal(context);
icon: const Icon(Icons.add), }
).padding(top: 8), }
], },
), label: Text('chatJoin').tr(),
).center(), icon: const Icon(Icons.add),
), ).padding(top: 8),
],
),
).center(),
),
);
}
},
loading:
() => AppScaffold(
appBar: AppBar(leading: const PageBackButton()),
body: CircularProgressIndicator().center(),
),
error:
(error, _) => AppScaffold(
appBar: AppBar(leading: const PageBackButton()),
body: ResponseErrorWidget(
error: error,
onRetry: () => ref.refresh(chatroomProvider(id)),
),
),
); );
} }
@@ -953,26 +1252,32 @@ class ChatRoomScreen extends HookConsumerWidget {
}, [id]); }, [id]);
Future<void> pickPhotoMedia() async { Future<void> pickPhotoMedia() async {
final result = await ref final result = await FilePicker.platform.pickFiles(
.watch(imagePickerProvider) type: FileType.image,
.pickMultiImage(requestFullMetadata: true); allowMultiple: true,
if (result.isEmpty) return; allowCompression: false,
);
if (result == null || result.count == 0) return;
attachments.value = [ attachments.value = [
...attachments.value, ...attachments.value,
...result.map( ...result.files.map(
(e) => UniversalFile(data: e, type: UniversalFileType.image), (e) => UniversalFile(data: e, type: UniversalFileType.image),
), ),
]; ];
} }
Future<void> pickVideoMedia() async { Future<void> pickVideoMedia() async {
final result = await ref final result = await FilePicker.platform.pickFiles(
.watch(imagePickerProvider) type: FileType.video,
.pickVideo(source: ImageSource.gallery); allowMultiple: true,
if (result == null) return; allowCompression: false,
);
if (result == null || result.count == 0) return;
attachments.value = [ attachments.value = [
...attachments.value, ...attachments.value,
UniversalFile(data: result, type: UniversalFileType.video), ...result.files.map(
(e) => UniversalFile(data: e, type: UniversalFileType.video),
),
]; ];
} }
@@ -1089,6 +1394,8 @@ class ChatRoomScreen extends HookConsumerWidget {
], ],
); );
const messageKeyPrefix = 'message-';
Widget chatMessageListWidget(List<LocalChatMessage> messageList) => Widget chatMessageListWidget(List<LocalChatMessage> messageList) =>
SuperListView.builder( SuperListView.builder(
listController: listController, listController: listController,
@@ -1098,7 +1405,9 @@ class ChatRoomScreen extends HookConsumerWidget {
itemCount: messageList.length, itemCount: messageList.length,
findChildIndexCallback: (key) { findChildIndexCallback: (key) {
final valueKey = key as ValueKey; final valueKey = key as ValueKey;
final messageId = valueKey.value as String; final messageId = (valueKey.value as String).substring(
messageKeyPrefix.length,
);
return messageList.indexWhere((m) => m.id == messageId); return messageList.indexWhere((m) => m.id == messageId);
}, },
extentEstimation: (_, _) => 40, extentEstimation: (_, _) => 40,
@@ -1115,10 +1424,13 @@ class ChatRoomScreen extends HookConsumerWidget {
.abs() > .abs() >
3; 3;
final key = ValueKey('$messageKeyPrefix${message.id}');
return chatIdentity.when( return chatIdentity.when(
skipError: true, skipError: true,
data: data:
(identity) => MessageItem( (identity) => MessageItem(
key: key,
message: message, message: message,
isCurrentUser: identity?.id == message.senderId, isCurrentUser: identity?.id == message.senderId,
onAction: (action) { onAction: (action) {
@@ -1161,6 +1473,7 @@ class ChatRoomScreen extends HookConsumerWidget {
), ),
loading: loading:
() => MessageItem( () => MessageItem(
key: key,
message: message, message: message,
isCurrentUser: false, isCurrentUser: false,
onAction: null, onAction: null,
@@ -1168,7 +1481,7 @@ class ChatRoomScreen extends HookConsumerWidget {
showAvatar: false, showAvatar: false,
onJump: (_) {}, onJump: (_) {},
), ),
error: (_, _) => const SizedBox.shrink(), error: (_, _) => SizedBox.shrink(key: key),
); );
}, },
); );
@@ -1549,7 +1862,7 @@ class _ChatInput extends HookConsumerWidget {
children: [ children: [
IconButton( IconButton(
tooltip: 'stickers'.tr(), tooltip: 'stickers'.tr(),
icon: const Icon(Symbols.emoji_symbols), icon: const Icon(Symbols.add_reaction),
onPressed: () { onPressed: () {
final size = MediaQuery.of(context).size; final size = MediaQuery.of(context).size;
showStickerPickerPopover( showStickerPickerPopover(
@@ -1659,8 +1972,13 @@ class _ChatInput extends HookConsumerWidget {
horizontal: 12, horizontal: 12,
vertical: 4, vertical: 4,
), ),
counterText:
messageController.text.length > 1024
? '${messageController.text.length}/4096'
: null,
), ),
maxLines: null, maxLines: 3,
minLines: 1,
onTapOutside: onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(), (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),

View File

@@ -6,7 +6,7 @@ part of 'room.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$messagesNotifierHash() => r'32afe6ea24086d869cc47bd3389c8fd734409ca0'; String _$messagesNotifierHash() => r'fc3b66dfb8dd3fc55d142dae5c5e7bdc67eca5d4';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

View File

@@ -9,6 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/chat.dart'; import 'package:island/models/chat.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/screens/chat/chat.dart'; import 'package:island/screens/chat/chat.dart';
import 'package:island/widgets/account/account_pfc.dart';
import 'package:island/widgets/account/account_picker.dart'; import 'package:island/widgets/account/account_picker.dart';
import 'package:island/widgets/account/status.dart'; import 'package:island/widgets/account/status.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
@@ -19,10 +20,17 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:island/pods/database.dart';
part 'room_detail.freezed.dart'; part 'room_detail.freezed.dart';
part 'room_detail.g.dart'; part 'room_detail.g.dart';
@riverpod
Future<int> totalMessagesCount(Ref ref, String roomId) async {
final database = ref.watch(databaseProvider);
return database.getTotalMessagesForRoom(roomId);
}
class ChatDetailScreen extends HookConsumerWidget { class ChatDetailScreen extends HookConsumerWidget {
final String id; final String id;
const ChatDetailScreen({super.key, required this.id}); const ChatDetailScreen({super.key, required this.id});
@@ -31,6 +39,7 @@ class ChatDetailScreen extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final roomState = ref.watch(chatroomProvider(id)); final roomState = ref.watch(chatroomProvider(id));
final roomIdentity = ref.watch(chatroomIdentityProvider(id)); final roomIdentity = ref.watch(chatroomIdentityProvider(id));
final totalMessages = ref.watch(totalMessagesCountProvider(id));
const kNotifyLevelText = [ const kNotifyLevelText = [
'chatNotifyLevelAll', 'chatNotifyLevelAll',
@@ -131,7 +140,7 @@ class ChatDetailScreen extends HookConsumerWidget {
const Text('chatBreakDescription').tr(), const Text('chatBreakDescription').tr(),
const Gap(16), const Gap(16),
ListTile( ListTile(
title: const Text('Clear').tr(), title: const Text('chatBreakClearButton').tr(),
subtitle: const Text('chatBreakClear').tr(), subtitle: const Text('chatBreakClear').tr(),
leading: const Icon(Icons.notifications_active), leading: const Icon(Icons.notifications_active),
onTap: () { onTap: () {
@@ -143,8 +152,10 @@ class ChatDetailScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
title: const Text('5m'), title: const Text('chatBreak5m').tr(),
subtitle: const Text('chatBreakHour').tr(args: ['5m']), subtitle: const Text(
'chatBreakHour',
).tr(args: ['chatBreak5m'.tr()]),
leading: const Icon(Symbols.circle), leading: const Icon(Symbols.circle),
onTap: () { onTap: () {
setChatBreak(now.add(const Duration(minutes: 5))); setChatBreak(now.add(const Duration(minutes: 5)));
@@ -155,8 +166,10 @@ class ChatDetailScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
title: const Text('10m'), title: const Text('chatBreak10m').tr(),
subtitle: const Text('chatBreakHour').tr(args: ['10m']), subtitle: const Text(
'chatBreakHour',
).tr(args: ['chatBreak10m'.tr()]),
leading: const Icon(Symbols.circle), leading: const Icon(Symbols.circle),
onTap: () { onTap: () {
setChatBreak(now.add(const Duration(minutes: 10))); setChatBreak(now.add(const Duration(minutes: 10)));
@@ -167,8 +180,10 @@ class ChatDetailScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
title: const Text('15m'), title: const Text('chatBreak15m').tr(),
subtitle: const Text('chatBreakHour').tr(args: ['15m']), subtitle: const Text(
'chatBreakHour',
).tr(args: ['chatBreak15m'.tr()]),
leading: const Icon(Symbols.timer_3), leading: const Icon(Symbols.timer_3),
onTap: () { onTap: () {
setChatBreak(now.add(const Duration(minutes: 15))); setChatBreak(now.add(const Duration(minutes: 15)));
@@ -179,8 +194,10 @@ class ChatDetailScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
title: const Text('30m'), title: const Text('chatBreak30m').tr(),
subtitle: const Text('chatBreakHour').tr(args: ['30m']), subtitle: const Text(
'chatBreakHour',
).tr(args: ['chatBreak30m'.tr()]),
leading: const Icon(Symbols.timer), leading: const Icon(Symbols.timer),
onTap: () { onTap: () {
setChatBreak(now.add(const Duration(minutes: 30))); setChatBreak(now.add(const Duration(minutes: 30)));
@@ -194,8 +211,8 @@ class ChatDetailScreen extends HookConsumerWidget {
TextField( TextField(
controller: durationController, controller: durationController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Custom (minutes)'.tr(), labelText: 'chatBreakCustomMinutes'.tr(),
hintText: 'Enter minutes'.tr(), hintText: 'chatBreakEnterMinutes'.tr(),
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: const Icon(Icons.check), icon: const Icon(Icons.check),
@@ -238,7 +255,10 @@ class ChatDetailScreen extends HookConsumerWidget {
return AppScaffold( return AppScaffold(
body: roomState.when( body: roomState.when(
loading: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => Center(child: Text('Error: $error')), error:
(error, _) => Center(
child: Text('errorGeneric'.tr(args: [error.toString()])),
),
data: data:
(currentRoom) => CustomScrollView( (currentRoom) => CustomScrollView(
slivers: [ slivers: [
@@ -358,6 +378,36 @@ class ChatDetailScreen extends HookConsumerWidget {
: const Text('chatBreakNone').tr(), : const Text('chatBreakNone').tr(),
onTap: () => showChatBreakDialog(), onTap: () => showChatBreakDialog(),
), ),
ListTile(
contentPadding: EdgeInsets.symmetric(
horizontal: 24,
),
leading: const Icon(Icons.search),
trailing: const Icon(Symbols.chevron_right),
title: const Text('searchMessages').tr(),
subtitle: totalMessages.when(
data:
(count) => Text(
'messagesCount'.tr(
args: [count.toString()],
),
),
loading:
() => const CircularProgressIndicator(),
error:
(err, stack) => Text(
'errorGeneric'.tr(
args: [err.toString()],
),
),
),
onTap: () {
context.pushNamed(
'searchMessages',
pathParameters: {'id': id},
);
},
),
], ],
), ),
error: (_, _) => const SizedBox.shrink(), error: (_, _) => const SizedBox.shrink(),
@@ -666,8 +716,11 @@ class _ChatMemberListSheet extends HookConsumerWidget {
final member = data.items[index]; final member = data.items[index];
return ListTile( return ListTile(
contentPadding: EdgeInsets.only(left: 16, right: 12), contentPadding: EdgeInsets.only(left: 16, right: 12),
leading: ProfilePictureWidget( leading: AccountPfcGestureDetector(
fileId: member.account.profile.picture?.id, uname: member.account.name,
child: ProfilePictureWidget(
fileId: member.account.profile.picture?.id,
),
), ),
title: Row( title: Row(
spacing: 6, spacing: 6,
@@ -848,7 +901,7 @@ class _ChatMemberRoleSheet extends HookConsumerWidget {
try { try {
final newRole = int.parse(roleController.text); final newRole = int.parse(roleController.text);
if (newRole < 0 || newRole > 100) { if (newRole < 0 || newRole > 100) {
throw 'Role must be between 0 and 100'; throw 'roleValidationHint'.tr();
} }
final apiClient = ref.read(apiClientProvider); final apiClient = ref.read(apiClientProvider);

View File

@@ -6,8 +6,8 @@ part of 'room_detail.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$chatMemberListNotifierHash() => String _$totalMessagesCountHash() =>
r'3ea30150278523e9f6b23f9200ea9a9fbae9c973'; r'a15c03461f25c2d4d39c0926509bf626ae2550a6';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -30,6 +30,128 @@ class _SystemHash {
} }
} }
/// See also [totalMessagesCount].
@ProviderFor(totalMessagesCount)
const totalMessagesCountProvider = TotalMessagesCountFamily();
/// See also [totalMessagesCount].
class TotalMessagesCountFamily extends Family<AsyncValue<int>> {
/// See also [totalMessagesCount].
const TotalMessagesCountFamily();
/// See also [totalMessagesCount].
TotalMessagesCountProvider call(String roomId) {
return TotalMessagesCountProvider(roomId);
}
@override
TotalMessagesCountProvider getProviderOverride(
covariant TotalMessagesCountProvider provider,
) {
return call(provider.roomId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'totalMessagesCountProvider';
}
/// See also [totalMessagesCount].
class TotalMessagesCountProvider extends AutoDisposeFutureProvider<int> {
/// See also [totalMessagesCount].
TotalMessagesCountProvider(String roomId)
: this._internal(
(ref) => totalMessagesCount(ref as TotalMessagesCountRef, roomId),
from: totalMessagesCountProvider,
name: r'totalMessagesCountProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$totalMessagesCountHash,
dependencies: TotalMessagesCountFamily._dependencies,
allTransitiveDependencies:
TotalMessagesCountFamily._allTransitiveDependencies,
roomId: roomId,
);
TotalMessagesCountProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.roomId,
}) : super.internal();
final String roomId;
@override
Override overrideWith(
FutureOr<int> Function(TotalMessagesCountRef provider) create,
) {
return ProviderOverride(
origin: this,
override: TotalMessagesCountProvider._internal(
(ref) => create(ref as TotalMessagesCountRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
roomId: roomId,
),
);
}
@override
AutoDisposeFutureProviderElement<int> createElement() {
return _TotalMessagesCountProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is TotalMessagesCountProvider && other.roomId == roomId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, roomId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin TotalMessagesCountRef on AutoDisposeFutureProviderRef<int> {
/// The parameter `roomId` of this provider.
String get roomId;
}
class _TotalMessagesCountProviderElement
extends AutoDisposeFutureProviderElement<int>
with TotalMessagesCountRef {
_TotalMessagesCountProviderElement(super.provider);
@override
String get roomId => (origin as TotalMessagesCountProvider).roomId;
}
String _$chatMemberListNotifierHash() =>
r'3ea30150278523e9f6b23f9200ea9a9fbae9c973';
abstract class _$ChatMemberListNotifier abstract class _$ChatMemberListNotifier
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnChatMember>> { extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnChatMember>> {
late final String roomId; late final String roomId;

View File

@@ -0,0 +1,139 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/screens/chat/room.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/chat/message_item.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
class SearchMessagesScreen extends HookConsumerWidget {
final String roomId;
const SearchMessagesScreen({super.key, required this.roomId});
@override
Widget build(BuildContext context, WidgetRef ref) {
final searchController = useTextEditingController();
final withLinks = useState(false);
final withAttachments = useState(false);
final messagesNotifier = ref.read(
messagesNotifierProvider(roomId).notifier,
);
final messages = ref.watch(messagesNotifierProvider(roomId));
useEffect(() {
// Clear search when screen is disposed
return () {
messagesNotifier.clearSearch();
};
}, []);
return AppScaffold(
appBar: AppBar(title: const Text('searchMessages').tr()),
body: Column(
children: [
Column(
children: [
TextField(
controller: searchController,
decoration: InputDecoration(
hintText: 'searchMessagesHint'.tr(),
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.only(
left: 16,
right: 16,
top: 12,
bottom: 16,
),
suffix: IconButton(
iconSize: 18,
visualDensity: VisualDensity.compact,
icon: const Icon(Icons.clear),
onPressed: () {
searchController.clear();
messagesNotifier.clearSearch();
},
),
),
onChanged: (query) {
messagesNotifier.searchMessages(
query,
withLinks: withLinks.value,
withAttachments: withAttachments.value,
);
},
),
Row(
children: [
Expanded(
child: CheckboxListTile(
secondary: const Icon(Symbols.link),
title: const Text('searchLinks').tr(),
value: withLinks.value,
onChanged: (bool? value) {
withLinks.value = value!;
messagesNotifier.searchMessages(
searchController.text,
withLinks: withLinks.value,
withAttachments: withAttachments.value,
);
},
),
),
Expanded(
child: CheckboxListTile(
secondary: const Icon(Symbols.file_copy),
title: const Text('searchAttachments').tr(),
value: withAttachments.value,
onChanged: (bool? value) {
withAttachments.value = value!;
messagesNotifier.searchMessages(
searchController.text,
withLinks: withLinks.value,
withAttachments: withAttachments.value,
);
},
),
),
],
),
],
),
const Divider(height: 1),
Expanded(
child: messages.when(
data:
(messageList) =>
messageList.isEmpty
? Center(child: Text('noMessagesFound'.tr()))
: SuperListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16),
reverse: true, // Show newest messages at the bottom
itemCount: messageList.length,
itemBuilder: (context, index) {
final message = messageList[index];
// Simplified MessageItem for search results, no grouping logic
return MessageItem(
message: message,
isCurrentUser:
false, // Or determine based on actual user
onAction: null,
onJump: (_) {},
progress: null,
showAvatar: true,
);
},
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => Center(child: Text('errorGeneric'.tr(args: [error.toString()]))),
),
),
],
),
);
}
}

View File

@@ -27,7 +27,9 @@ class AppDetailScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final tabController = useTabController(initialLength: 2); final tabController = useTabController(initialLength: 2);
final appData = ref.watch(customAppProvider(publisherName, projectId, appId)); final appData = ref.watch(
customAppProvider(publisherName, projectId, appId),
);
return AppScaffold( return AppScaffold(
appBar: AppBar( appBar: AppBar(
@@ -35,23 +37,43 @@ class AppDetailScreen extends HookConsumerWidget {
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Symbols.edit), icon: const Icon(Symbols.edit),
onPressed: appData.value == null onPressed:
? null appData.value == null
: () { ? null
context.pushNamed( : () {
'developerAppEdit', context.pushNamed(
pathParameters: { 'developerAppEdit',
'name': publisherName, pathParameters: {
'projectId': projectId, 'name': publisherName,
'id': appId, 'projectId': projectId,
}, 'id': appId,
); },
}, );
},
), ),
], ],
bottom: TabBar( bottom: TabBar(
controller: tabController, controller: tabController,
tabs: [Tab(text: 'overview'.tr()), Tab(text: 'secrets'.tr())], tabs: [
Tab(
child: Text(
'overview'.tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
Tab(
child: Text(
'secrets'.tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
],
), ),
), ),
body: appData.when( body: appData.when(
@@ -70,12 +92,14 @@ class AppDetailScreen extends HookConsumerWidget {
); );
}, },
loading: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => ResponseErrorWidget( error:
error: err, (err, stack) => ResponseErrorWidget(
onRetry: () => ref.invalidate( error: err,
customAppProvider(publisherName, projectId, appId), onRetry:
), () => ref.invalidate(
), customAppProvider(publisherName, projectId, appId),
),
),
), ),
); );
} }
@@ -98,12 +122,13 @@ class _AppOverview extends StatelessWidget {
children: [ children: [
Container( Container(
color: Theme.of(context).colorScheme.surfaceContainer, color: Theme.of(context).colorScheme.surfaceContainer,
child: app.background != null child:
? CloudFileWidget( app.background != null
item: app.background!, ? CloudFileWidget(
fit: BoxFit.cover, item: app.background!,
) fit: BoxFit.cover,
: const SizedBox.shrink(), )
: const SizedBox.shrink(),
), ),
Positioned( Positioned(
left: 20, left: 20,

View File

@@ -52,7 +52,26 @@ class BotDetailScreen extends HookConsumerWidget {
], ],
bottom: TabBar( bottom: TabBar(
controller: tabController, controller: tabController,
tabs: [Tab(text: 'overview'.tr()), Tab(text: 'keys'.tr())], tabs: [
Tab(
child: Text(
'overview'.tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
Tab(
child: Text(
'keys'.tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
],
), ),
), ),
body: botData.when( body: botData.when(

View File

@@ -58,7 +58,26 @@ class ProjectDetailScreen extends HookConsumerWidget {
], ],
bottom: TabBar( bottom: TabBar(
controller: tabController, controller: tabController,
tabs: [Tab(text: 'customApps'.tr()), Tab(text: 'bots'.tr())], tabs: [
Tab(
child: Text(
'customApps'.tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
Tab(
child: Text(
'bots'.tr(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
],
), ),
), ),
body: TabBarView( body: TabBarView(

View File

@@ -143,8 +143,26 @@ class ArticlesScreen extends ConsumerWidget {
bottom: TabBar( bottom: TabBar(
isScrollable: true, isScrollable: true,
tabs: [ tabs: [
const Tab(text: 'All'), Tab(
...feeds.map((feed) => Tab(text: feed.title)), child: Text(
'All',
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
...feeds.map(
(feed) => Tab(
child: Text(
feed.title,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
),
], ],
), ),
), ),

View File

@@ -151,7 +151,9 @@ class PostCategoryDetailScreen extends HookConsumerWidget {
(error, _) => Text( (error, _) => Text(
'Error loading subscription status', 'Error loading subscription status',
), ),
loading: () => CircularProgressIndicator(), loading:
() =>
CircularProgressIndicator().center(),
), ),
], ],
).padding(horizontal: 24, vertical: 16), ).padding(horizontal: 24, vertical: 16),
@@ -220,7 +222,9 @@ class PostCategoryDetailScreen extends HookConsumerWidget {
(error, _) => Text( (error, _) => Text(
'Error loading subscription status', 'Error loading subscription status',
), ),
loading: () => CircularProgressIndicator(), loading:
() =>
CircularProgressIndicator().center(),
), ),
], ],
).padding(horizontal: 24, vertical: 16), ).padding(horizontal: 24, vertical: 16),

View File

@@ -1,5 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
@@ -7,6 +9,7 @@ import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_item.dart';
import 'package:island/widgets/response.dart'; import 'package:island/widgets/response.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
import 'package:styled_widget/styled_widget.dart';
final postSearchNotifierProvider = StateNotifierProvider.autoDispose< final postSearchNotifierProvider = StateNotifierProvider.autoDispose<
PostSearchNotifier, PostSearchNotifier,
@@ -18,6 +21,13 @@ class PostSearchNotifier
final AutoDisposeRef ref; final AutoDisposeRef ref;
static const int _pageSize = 20; static const int _pageSize = 20;
String _currentQuery = ''; String _currentQuery = '';
String? _pubName;
String? _realm;
int? _type;
List<String>? _categories;
List<String>? _tags;
bool _shuffle = false;
bool? _pinned;
bool _isLoading = false; bool _isLoading = false;
PostSearchNotifier(this.ref) : super(const AsyncValue.loading()) { PostSearchNotifier(this.ref) : super(const AsyncValue.loading()) {
@@ -26,11 +36,38 @@ class PostSearchNotifier
); );
} }
Future<void> search(String query) async { Future<void> search(
String query, {
String? pubName,
String? realm,
int? type,
List<String>? categories,
List<String>? tags,
bool shuffle = false,
bool? pinned,
}) async {
if (_isLoading) return; if (_isLoading) return;
_currentQuery = query.trim(); _currentQuery = query.trim();
if (_currentQuery.isEmpty) { _pubName = pubName;
_realm = realm;
_type = type;
_categories = categories;
_tags = tags;
_shuffle = shuffle;
_pinned = pinned;
// Allow search even with empty query if any filters are applied
final hasFilters =
pubName != null ||
realm != null ||
type != null ||
categories != null ||
tags != null ||
shuffle ||
pinned != null;
if (_currentQuery.isEmpty && !hasFilters) {
state = AsyncValue.data( state = AsyncValue.data(
CursorPagingData(items: [], hasMore: false, nextCursor: null), CursorPagingData(items: [], hasMore: false, nextCursor: null),
); );
@@ -57,6 +94,13 @@ class PostSearchNotifier
'offset': offset, 'offset': offset,
'take': _pageSize, 'take': _pageSize,
'vector': false, 'vector': false,
if (_pubName != null) 'pub': _pubName,
if (_realm != null) 'realm': _realm,
if (_type != null) 'type': _type,
if (_tags != null) 'tags': _tags,
if (_categories != null) 'categories': _categories,
if (_shuffle) 'shuffle': true,
if (_pinned != null) 'pinned': _pinned,
}, },
); );
@@ -80,100 +124,269 @@ class PostSearchNotifier
} }
} }
class PostSearchScreen extends ConsumerStatefulWidget { class PostSearchScreen extends HookConsumerWidget {
const PostSearchScreen({super.key}); const PostSearchScreen({super.key});
@override @override
ConsumerState<PostSearchScreen> createState() => _PostSearchScreenState(); Widget build(BuildContext context, WidgetRef ref) {
} final searchController = useTextEditingController();
final debounce = useMemoized(() => Duration(milliseconds: 500));
final debounceTimer = useRef<Timer?>(null);
final showFilters = useState(false);
final pubNameController = useTextEditingController();
final realmController = useTextEditingController();
final typeValue = useState<int?>(null);
final selectedCategories = useState<List<String>>([]);
final selectedTags = useState<List<String>>([]);
final shuffleValue = useState(false);
final pinnedValue = useState<bool?>(null);
class _PostSearchScreenState extends ConsumerState<PostSearchScreen> { useEffect(() {
final _searchController = TextEditingController(); return () {
final _debounce = Duration(milliseconds: 500); searchController.dispose();
Timer? _debounceTimer; pubNameController.dispose();
realmController.dispose();
debounceTimer.value?.cancel();
};
}, []);
@override void onSearchChanged(String query) {
void dispose() { if (debounceTimer.value?.isActive ?? false) debounceTimer.value!.cancel();
_searchController.dispose();
_debounceTimer?.cancel();
super.dispose();
}
void _onSearchChanged(String query) { debounceTimer.value = Timer(debounce, () {
if (_debounceTimer?.isActive ?? false) _debounceTimer!.cancel(); ref.read(postSearchNotifierProvider.notifier).search(query);
});
}
_debounceTimer = Timer(_debounce, () { void onSearchWithFilters(String query) {
ref.read(postSearchNotifierProvider.notifier).search(query); if (debounceTimer.value?.isActive ?? false) debounceTimer.value!.cancel();
});
} debounceTimer.value = Timer(debounce, () {
ref
.read(postSearchNotifierProvider.notifier)
.search(
query,
pubName:
pubNameController.text.isNotEmpty
? pubNameController.text
: null,
realm:
realmController.text.isNotEmpty ? realmController.text : null,
type: typeValue.value,
categories:
selectedCategories.value.isNotEmpty
? selectedCategories.value
: null,
tags: selectedTags.value.isNotEmpty ? selectedTags.value : null,
shuffle: shuffleValue.value,
pinned: pinnedValue.value,
);
});
}
void toggleFilters() {
showFilters.value = !showFilters.value;
}
void applyFilters() {
onSearchWithFilters(searchController.text);
}
void clearFilters() {
pubNameController.clear();
realmController.clear();
typeValue.value = null;
selectedCategories.value = [];
selectedTags.value = [];
shuffleValue.value = false;
pinnedValue.value = null;
onSearchChanged(searchController.text);
}
Widget buildFilterPanel() {
return Card(
margin: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'filters'.tr(),
style: Theme.of(context).textTheme.titleMedium,
).padding(left: 4),
Row(
children: [
TextButton(
onPressed: applyFilters,
child: Text('apply'.tr()),
),
TextButton(
onPressed: clearFilters,
child: Text('clear'.tr()),
),
],
),
],
),
SizedBox(height: 16),
TextField(
controller: pubNameController,
decoration: InputDecoration(
labelText: 'pubName'.tr(),
border: OutlineInputBorder(),
),
onChanged:
(value) => onSearchWithFilters(searchController.text),
),
SizedBox(height: 8),
TextField(
controller: realmController,
decoration: InputDecoration(
labelText: 'realm'.tr(),
border: OutlineInputBorder(),
),
onChanged:
(value) => onSearchWithFilters(searchController.text),
),
SizedBox(height: 8),
Row(
children: [
Checkbox(
value: shuffleValue.value,
onChanged: (value) {
shuffleValue.value = value ?? false;
onSearchWithFilters(searchController.text);
},
),
Text('shuffle'.tr()),
],
),
Row(
children: [
Checkbox(
value: pinnedValue.value ?? false,
onChanged: (value) {
pinnedValue.value = value;
onSearchWithFilters(searchController.text);
},
),
Text('pinned'.tr()),
],
),
// TODO: Add dropdown for type selection
// TODO: Add multi-select for categories and tags
],
),
),
);
}
@override
Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
isNoBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: TextField( title: Row(
controller: _searchController, children: [
decoration: InputDecoration( Expanded(
hintText: 'Search posts...', child: TextField(
border: InputBorder.none, controller: searchController,
hintStyle: TextStyle( decoration: InputDecoration(
color: Theme.of(context).appBarTheme.foregroundColor, hintText: 'search'.tr(),
border: InputBorder.none,
hintStyle: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor,
),
),
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor,
),
onChanged: onSearchChanged,
onSubmitted: (value) {
onSearchWithFilters(value);
},
autofocus: true,
),
), ),
), IconButton(
style: TextStyle( icon: Icon(
color: Theme.of(context).appBarTheme.foregroundColor, showFilters.value
), ? Icons.filter_alt
onChanged: _onSearchChanged, : Icons.filter_alt_outlined,
onSubmitted: (value) { ),
ref.read(postSearchNotifierProvider.notifier).search(value); onPressed: toggleFilters,
}, tooltip: 'toggleFilters'.tr(),
autofocus: true, ),
],
), ),
), ),
body: Consumer( body: Consumer(
builder: (context, ref, child) { builder: (context, ref, child) {
final searchState = ref.watch(postSearchNotifierProvider); final searchState = ref.watch(postSearchNotifierProvider);
return searchState.when( return CustomScrollView(
data: (data) { slivers: [
if (data.items.isEmpty && _searchController.text.isNotEmpty) { if (showFilters.value)
return const Center(child: Text('No results found')); SliverToBoxAdapter(
} child: Center(
child: ConstrainedBox(
return ListView.builder( constraints: const BoxConstraints(maxWidth: 600),
padding: EdgeInsets.zero, child: buildFilterPanel(),
itemCount: data.items.length + (data.hasMore ? 1 : 0), ),
itemBuilder: (context, index) { ),
if (index >= data.items.length) { ),
ref searchState.when(
.read(postSearchNotifierProvider.notifier) data: (data) {
.fetch(cursor: data.nextCursor); if (data.items.isEmpty && searchController.text.isNotEmpty) {
return const Center(child: CircularProgressIndicator()); return SliverFillRemaining(
child: Center(child: Text('noResultsFound'.tr())),
);
} }
final post = data.items[index]; return SliverList(
return Center( delegate: SliverChildBuilderDelegate((context, index) {
child: ConstrainedBox( if (index >= data.items.length) {
constraints: BoxConstraints(maxWidth: 600), ref
child: Card( .read(postSearchNotifierProvider.notifier)
margin: EdgeInsets.symmetric( .fetch(cursor: data.nextCursor);
horizontal: 8, return Center(child: CircularProgressIndicator());
vertical: 4, }
final post = data.items[index];
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 600),
child: Card(
margin: EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
child: PostActionableItem(
item: post,
borderRadius: 8,
),
),
), ),
child: PostActionableItem(item: post, borderRadius: 8), );
), }, childCount: data.items.length + (data.hasMore ? 1 : 0)),
),
); );
}, },
); loading:
}, () => SliverFillRemaining(
loading: () => const Center(child: CircularProgressIndicator()), child: Center(child: CircularProgressIndicator()),
error: ),
(error, stack) => ResponseErrorWidget( error:
error: error, (error, stack) => SliverFillRemaining(
onRetry: () => ref.invalidate(postSearchNotifierProvider), child: ResponseErrorWidget(
), error: error,
onRetry:
() => ref.invalidate(postSearchNotifierProvider),
),
),
),
],
); );
}, },
), ),

View File

@@ -288,7 +288,11 @@ class PublisherProfileScreen extends HookConsumerWidget {
controller: categoryTabController, controller: categoryTabController,
dividerColor: Colors.transparent, dividerColor: Colors.transparent,
splashBorderRadius: const BorderRadius.all(Radius.circular(8)), splashBorderRadius: const BorderRadius.all(Radius.circular(8)),
tabs: [Tab(text: 'All'), Tab(text: 'Posts'), Tab(text: 'Articles')], tabs: [
Tab(text: 'all'.tr()),
Tab(text: 'postTypePost'.tr()),
Tab(text: 'postArticle'.tr()),
],
), ),
); );
@@ -345,12 +349,14 @@ class PublisherProfileScreen extends HookConsumerWidget {
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [
SliverGap(16), SliverGap(16),
SliverPostList(pubName: name, pinned: true),
SliverToBoxAdapter( SliverToBoxAdapter(
child: publisherCategoryTabWidget(), child: publisherCategoryTabWidget(),
), ),
SliverPostList( SliverPostList(
key: ValueKey(categoryTab.value), key: ValueKey(categoryTab.value),
pubName: name, pubName: name,
pinned: false,
type: switch (categoryTab.value) { type: switch (categoryTab.value) {
1 => 0, 1 => 0,
2 => 1, 2 => 1,
@@ -433,10 +439,12 @@ class PublisherProfileScreen extends HookConsumerWidget {
child: publisherVerificationWidget(data), child: publisherVerificationWidget(data),
), ),
SliverToBoxAdapter(child: publisherBioWidget(data)), SliverToBoxAdapter(child: publisherBioWidget(data)),
SliverPostList(pubName: name, pinned: true),
SliverToBoxAdapter(child: publisherCategoryTabWidget()), SliverToBoxAdapter(child: publisherCategoryTabWidget()),
SliverPostList( SliverPostList(
key: ValueKey(categoryTab.value), key: ValueKey(categoryTab.value),
pubName: name, pubName: name,
pinned: false,
type: switch (categoryTab.value) { type: switch (categoryTab.value) {
1 => 0, 1 => 0,
2 => 1, 2 => 1,

View File

@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:island/models/chat.dart'; import 'package:island/models/chat.dart';
import 'package:island/services/color.dart'; import 'package:island/services/color.dart';
import 'package:island/services/responsive.dart'; import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/account_pfc.dart';
import 'package:island/widgets/account/status.dart'; import 'package:island/widgets/account/status.dart';
import 'package:island/widgets/post/post_list.dart'; import 'package:island/widgets/post/post_list.dart';
import 'package:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
@@ -244,7 +245,10 @@ class RealmDetailScreen extends HookConsumerWidget {
Flexible( Flexible(
flex: 3, flex: 3,
child: CustomScrollView( child: CustomScrollView(
slivers: [SliverPostList(realm: slug)], slivers: [
SliverPostList(realm: slug, pinned: true),
SliverPostList(realm: slug, pinned: false),
],
), ),
), ),
Flexible( Flexible(
@@ -359,7 +363,8 @@ class RealmDetailScreen extends HookConsumerWidget {
SliverToBoxAdapter( SliverToBoxAdapter(
child: realmChatRoomListWidget(realm), child: realmChatRoomListWidget(realm),
), ),
SliverPostList(realm: slug), SliverPostList(realm: slug, pinned: true),
SliverPostList(realm: slug, pinned: false),
], ],
), ),
), ),
@@ -654,8 +659,11 @@ class _RealmMemberListSheet extends HookConsumerWidget {
final member = data.items[index]; final member = data.items[index];
return ListTile( return ListTile(
contentPadding: EdgeInsets.only(left: 16, right: 12), contentPadding: EdgeInsets.only(left: 16, right: 12),
leading: ProfilePictureWidget( leading: AccountPfcGestureDetector(
fileId: member.account!.profile.picture?.id, uname: member.account!.name,
child: ProfilePictureWidget(
fileId: member.account!.profile.picture?.id,
),
), ),
title: Row( title: Row(
spacing: 6, spacing: 6,

9
lib/utils/format.dart Normal file
View File

@@ -0,0 +1,9 @@
String formatFileSize(int bytes) {
if (bytes <= 0) return '0 B';
if (bytes < 1024) return '$bytes B';
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(2)} KB';
if (bytes < 1024 * 1024 * 1024) {
return '${(bytes / (1024 * 1024)).toStringAsFixed(2)} MB';
}
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
}

View File

@@ -37,11 +37,28 @@ class AccountName extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
spacing: 4, spacing: 4,
children: [ children: [
Flexible(child: Text(account.nick, style: nameStyle)), Flexible(
child: Text(
account.nick,
style: nameStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (account.perkSubscription != null) if (account.perkSubscription != null)
StellarMembershipMark(membership: account.perkSubscription!), StellarMembershipMark(membership: account.perkSubscription!),
if (account.profile.verification != null) if (account.profile.verification != null)
VerificationMark(mark: account.profile.verification!), VerificationMark(mark: account.profile.verification!),
if (account.automatedId != null)
Tooltip(
message: 'accountAutomated'.tr(),
child: Icon(
Symbols.smart_toy,
size: 16,
color: nameStyle.color,
fill: 1,
),
),
], ],
); );
} }
@@ -141,7 +158,7 @@ class VerificationStatusCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Icon( Icon(
mark.type == 4 mark.type == 4
@@ -152,7 +169,7 @@ class VerificationStatusCard extends StatelessWidget {
size: 32, size: 32,
color: kVerificationMarkColors[mark.type], color: kVerificationMarkColors[mark.type],
fill: 1, fill: 1,
), ).alignment(Alignment.centerLeft),
const Gap(8), const Gap(8),
Text(mark.title ?? 'No title').bold(), Text(mark.title ?? 'No title').bold(),
Text(mark.description ?? 'descriptionNone'.tr()), Text(mark.description ?? 'descriptionNone'.tr()),

View File

@@ -1,6 +1,7 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:flutter_popup_card/flutter_popup_card.dart'; import 'package:flutter_popup_card/flutter_popup_card.dart';
@@ -74,27 +75,75 @@ class AccountProfileCard extends HookConsumerWidget {
uname: data.name, uname: data.name,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
), ),
if (data.profile.timeZone.isNotEmpty) Tooltip(
Row( message: 'creditsStatus'.tr(),
child: Row(
spacing: 6, spacing: 6,
children: [ children: [
Icon( Icon(
Symbols.alarm, Symbols.star,
size: 17, size: 17,
fill: 1, fill: 1,
).padding(right: 2), ).padding(right: 2),
Text( Text(
getTzInfo( '${data.profile.socialCredits.toStringAsFixed(2)} pts',
data.profile.timeZone,
).$2.formatCustomGlobal('HH:mm'),
).fontSize(12),
Text(
getTzInfo(
data.profile.timeZone,
).$1.formatOffsetLocal(),
).fontSize(12), ).fontSize(12),
switch (data.profile.socialCreditsLevel) {
-1 => Text('socialCreditsLevelPoor').tr(),
0 => Text('socialCreditsLevelNormal').tr(),
1 => Text('socialCreditsLevelGood').tr(),
2 => Text('socialCreditsLevelExcellent').tr(),
_ => Text('unknown').tr(),
}.fontSize(12),
], ],
).padding(top: 2), ),
),
if (data.automatedId != null)
Row(
spacing: 6,
children: [
Icon(
Symbols.smart_toy,
size: 17,
fill: 1,
).padding(right: 2),
Text('accountAutomated').tr().fontSize(12),
],
),
if (data.profile.timeZone.isNotEmpty && !kIsWeb)
() {
try {
final tzInfo = getTzInfo(data.profile.timeZone);
return Row(
spacing: 6,
children: [
Icon(
Symbols.alarm,
size: 17,
fill: 1,
).padding(right: 2),
Text(
tzInfo.$2.formatCustomGlobal('HH:mm'),
).fontSize(12),
Text(
tzInfo.$1.formatOffsetLocal(),
).fontSize(12),
],
).padding(top: 2);
} catch (e) {
return Row(
spacing: 6,
children: [
Icon(
Symbols.alarm,
size: 17,
fill: 1,
).padding(right: 2),
Text('timezoneNotFound'.tr()).fontSize(12),
],
).padding(top: 2);
}
}(),
if (data.badges.isNotEmpty) if (data.badges.isNotEmpty)
BadgeList(badges: data.badges).padding(top: 12), BadgeList(badges: data.badges).padding(top: 12),
LevelingProgressCard( LevelingProgressCard(

View File

@@ -2,7 +2,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart';
import 'package:island/models/activity.dart'; import 'package:island/models/activity.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:island/widgets/account/event_details_widget.dart'; import 'package:island/widgets/account/event_details_widget.dart';
import 'package:table_calendar/table_calendar.dart'; import 'package:table_calendar/table_calendar.dart';
@@ -87,24 +89,56 @@ class EventCalendarWidget extends HookConsumerWidget {
return Center(child: Text(text)); return Center(child: Text(text));
}, },
markerBuilder: (context, day, events) { markerBuilder: (context, day, events) {
var checkInResult = final checkInResult =
events.whereType<SnCheckInResult>().firstOrNull; events.whereType<SnCheckInResult>().firstOrNull;
final statuses = events.whereType<SnAccountStatus>().toList();
final textColor =
isSameDay(selectedDay.value, day)
? Colors.white
: isSameDay(DateTime.now(), day)
? Colors.white
: Theme.of(context).colorScheme.onSurface;
final shadow =
isSameDay(selectedDay.value, day) ||
isSameDay(DateTime.now(), day)
? [
Shadow(
color: Colors.black.withOpacity(0.5),
offset: const Offset(0, 1),
blurRadius: 4,
),
]
: null;
if (checkInResult != null) { if (checkInResult != null) {
return Positioned( return Positioned(
top: 32, top: 32,
child: Text( child: Row(
'checkInResultT${checkInResult.level}'.tr(), spacing: 2,
style: TextStyle( children: [
fontSize: 9, Text(
color: 'checkInResultT${checkInResult.level}'.tr(),
isSameDay(selectedDay.value, day) style: TextStyle(
? Theme.of(context).colorScheme.onPrimaryContainer fontSize: 9,
: isSameDay(DateTime.now(), day) color: textColor,
? Theme.of( shadows: shadow,
context, ),
).colorScheme.onSecondaryContainer ),
: Theme.of(context).colorScheme.onSurface, if (statuses.isNotEmpty) ...[
), Icon(
switch (statuses.first.attitude) {
0 => Symbols.sentiment_satisfied,
2 => Symbols.sentiment_dissatisfied,
_ => Symbols.sentiment_neutral,
},
size: 12,
color: textColor,
shadows: shadow,
),
],
],
), ),
); );
} }

View File

@@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:island/models/activity.dart'; import 'package:island/models/activity.dart';
import 'package:island/services/time.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -53,6 +54,33 @@ class EventDetailsWidget extends StatelessWidget {
), ),
], ],
).padding(top: 8), ).padding(top: 8),
if (event!.statuses.isNotEmpty) ...[
const Gap(16),
Text('statusLabel').tr().fontSize(16).bold(),
],
for (final status in event!.statuses) ...[
Row(
spacing: 8,
children: [
Icon(switch (status.attitude) {
0 => Symbols.sentiment_satisfied,
2 => Symbols.sentiment_dissatisfied,
_ => Symbols.sentiment_neutral,
}),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(status.label),
Text(
'${status.createdAt.formatSystem()} - ${status.clearedAt?.formatSystem() ?? 'present'.tr()}',
).fontSize(11).opacity(0.8),
],
),
),
],
).padding(vertical: 8),
],
], ],
), ),
if (event?.checkInResult == null && (event?.statuses.isEmpty ?? true)) if (event?.checkInResult == null && (event?.statuses.isEmpty ?? true))

View File

@@ -60,7 +60,9 @@ class AccountStatusCreationWidget extends HookConsumerWidget {
spacing: 4, spacing: 4,
children: [ children: [
Icon(Symbols.keyboard_arrow_up), Icon(Symbols.keyboard_arrow_up),
Text('statusCreateHint').tr(), Expanded(
child: Text('statusCreateHint', maxLines: 1).tr(),
),
], ],
), ),
).opacity(0.85), ).opacity(0.85),

View File

@@ -10,6 +10,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/file.dart'; import 'package:island/models/file.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/services/file.dart'; import 'package:island/services/file.dart';
import 'package:island/utils/format.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/widgets/content/sheet.dart';
@@ -284,6 +285,13 @@ class AttachmentPreview extends HookConsumerWidget {
Builder( Builder(
key: ValueKey(item.hashCode), key: ValueKey(item.hashCode),
builder: (context) { builder: (context) {
final fallbackIcon = switch (item.type) {
UniversalFileType.video => Symbols.video_file,
UniversalFileType.audio => Symbols.audio_file,
UniversalFileType.image => Symbols.image,
_ => Symbols.insert_drive_file,
};
if (item.isOnCloud) { if (item.isOnCloud) {
return CloudFileWidget(item: item.data); return CloudFileWidget(item: item.data);
} else if (item.data is XFile) { } else if (item.data is XFile) {
@@ -309,9 +317,23 @@ class AttachmentPreview extends HookConsumerWidget {
: Image.file(File(file.path)); : Image.file(File(file.path));
default: default:
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Symbols.document_scanner), Icon(fallbackIcon),
const Gap(6),
Text(file.name), Text(file.name),
FutureBuilder(
future: file.length(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final size = snapshot.data as int;
return Text(
formatFileSize(size),
).fontSize(11);
}
return const SizedBox.shrink();
},
),
], ],
); );
} }
@@ -321,7 +343,14 @@ class AttachmentPreview extends HookConsumerWidget {
return Image.memory(item.data); return Image.memory(item.data);
default: default:
return Column( return Column(
children: [const Icon(Symbols.document_scanner)], mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(fallbackIcon),
const Gap(6),
Text(
formatFileSize(item.data.length),
).fontSize(11),
],
); );
} }
} }

View File

@@ -1,9 +1,12 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui'; import 'dart:ui';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:file_saver/file_saver.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_blurhash/flutter_blurhash.dart'; import 'package:flutter_blurhash/flutter_blurhash.dart';
@@ -14,6 +17,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/file.dart'; import 'package:island/models/file.dart';
import 'package:island/pods/config.dart'; import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/utils/format.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/sensitive.dart'; import 'package:island/widgets/content/sensitive.dart';
@@ -321,7 +325,7 @@ class CloudFileZoomIn extends HookConsumerWidget {
Future<void> saveToGallery() async { Future<void> saveToGallery() async {
try { try {
// Show loading indicator // Show loading indicator
showSnackBar('Saving image to gallery...'); showSnackBar('Saving image...');
// Get the image URL // Get the image URL
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
@@ -339,25 +343,23 @@ class CloudFileZoomIn extends HookConsumerWidget {
filePath, filePath,
queryParameters: {'original': true}, queryParameters: {'original': true},
); );
await Gal.putImage(filePath, album: 'Solar Network'); if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
// Save to gallery
// Show success message await Gal.putImage(filePath, album: 'Solar Network');
showSnackBar('Image saved to gallery'); // Show success message
showSnackBar('Image saved to gallery');
} else {
await FileSaver.instance.saveFile(
name: item.name.isEmpty ? '${item.id}.$extName' : item.name,
file: File(filePath),
);
showSnackBar('Image saved to $filePath');
}
} catch (e) { } catch (e) {
showErrorAlert(e); showErrorAlert(e);
} }
} }
String formatFileSize(int bytes) {
if (bytes <= 0) return '0 B';
if (bytes < 1024) return '$bytes B';
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(2)} KB';
if (bytes < 1024 * 1024 * 1024) {
return '${(bytes / (1024 * 1024)).toStringAsFixed(2)} MB';
}
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
}
void showInfoSheet() { void showInfoSheet() {
final theme = Theme.of(context); final theme = Theme.of(context);
final exifData = item.fileMeta?['exif'] as Map<String, dynamic>? ?? {}; final exifData = item.fileMeta?['exif'] as Map<String, dynamic>? ?? {};
@@ -437,7 +439,24 @@ class CloudFileZoomIn extends HookConsumerWidget {
).padding(horizontal: 24, vertical: 16), ).padding(horizontal: 24, vertical: 16),
const Divider(height: 1), const Divider(height: 1),
ListTile( ListTile(
leading: const Icon(Icons.file_present), leading: const Icon(Symbols.tag),
title: Text('ID').tr(),
subtitle: Text(
item.id,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
trailing: IconButton(
icon: const Icon(Icons.copy),
onPressed: () {
Clipboard.setData(ClipboardData(text: item.id));
showSnackBar('File ID copied to clipboard');
},
),
),
ListTile(
leading: const Icon(Symbols.file_present),
title: Text('Name').tr(), title: Text('Name').tr(),
subtitle: Text( subtitle: Text(
item.name, item.name,
@@ -623,6 +642,10 @@ class CloudFileZoomIn extends HookConsumerWidget {
); );
} }
final shadow = [
Shadow(color: Colors.black54, blurRadius: 5.0, offset: Offset(1.0, 1.0)),
];
return DismissiblePage( return DismissiblePage(
isFullScreen: true, isFullScreen: true,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@@ -660,22 +683,17 @@ class CloudFileZoomIn extends HookConsumerWidget {
children: [ children: [
Row( Row(
children: [ children: [
IconButton( if (!kIsWeb)
icon: Icon( IconButton(
Icons.save_alt, icon: Icon(
color: Colors.white, Icons.save_alt,
shadows: [ color: Colors.white,
Shadow( shadows: shadow,
color: Colors.black54, ),
blurRadius: 5.0, onPressed: () async {
offset: Offset(1.0, 1.0), saveToGallery();
), },
],
), ),
onPressed: () async {
saveToGallery();
},
),
IconButton( IconButton(
onPressed: () { onPressed: () {
showOriginal.value = !showOriginal.value; showOriginal.value = !showOriginal.value;
@@ -683,29 +701,13 @@ class CloudFileZoomIn extends HookConsumerWidget {
icon: Icon( icon: Icon(
showOriginal.value ? Symbols.hd : Symbols.sd, showOriginal.value ? Symbols.hd : Symbols.sd,
color: Colors.white, color: Colors.white,
shadows: [ shadows: shadow,
Shadow(
color: Colors.black54,
blurRadius: 5.0,
offset: Offset(1.0, 1.0),
),
],
), ),
), ),
], ],
), ),
IconButton( IconButton(
icon: Icon( icon: Icon(Icons.close, color: Colors.white, shadows: shadow),
Icons.close,
color: Colors.white,
shadows: [
Shadow(
color: Colors.black54,
blurRadius: 5.0,
offset: Offset(1.0, 1.0),
),
],
),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
], ],
@@ -722,26 +724,24 @@ class CloudFileZoomIn extends HookConsumerWidget {
icon: Icon( icon: Icon(
Icons.info_outline, Icons.info_outline,
color: Colors.white, color: Colors.white,
shadows: [ shadows: shadow,
Shadow(
color: Colors.black54,
blurRadius: 5.0,
offset: Offset(1.0, 1.0),
),
],
), ),
onPressed: showInfoSheet, onPressed: showInfoSheet,
), ),
Spacer(), Spacer(),
IconButton( IconButton(
icon: Icon(Icons.remove, color: Colors.white), icon: Icon(
Icons.remove,
color: Colors.white,
shadows: shadow,
),
onPressed: () { onPressed: () {
photoViewController.scale = photoViewController.scale =
(photoViewController.scale ?? 1) - 0.05; (photoViewController.scale ?? 1) - 0.05;
}, },
), ),
IconButton( IconButton(
icon: Icon(Icons.add, color: Colors.white), icon: Icon(Icons.add, color: Colors.white, shadows: shadow),
onPressed: () { onPressed: () {
photoViewController.scale = photoViewController.scale =
(photoViewController.scale ?? 1) + 0.05; (photoViewController.scale ?? 1) + 0.05;
@@ -752,13 +752,7 @@ class CloudFileZoomIn extends HookConsumerWidget {
icon: Icon( icon: Icon(
Icons.rotate_left, Icons.rotate_left,
color: Colors.white, color: Colors.white,
shadows: [ shadows: shadow,
Shadow(
color: Colors.black54,
blurRadius: 5.0,
offset: Offset(1.0, 1.0),
),
],
), ),
onPressed: () { onPressed: () {
rotation.value = (rotation.value - 1) % 4; rotation.value = (rotation.value - 1) % 4;

View File

@@ -1,15 +1,19 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/file.dart'; import 'package:island/models/file.dart';
import 'package:island/pods/config.dart'; import 'package:island/pods/config.dart';
import 'package:island/services/time.dart'; import 'package:island/services/time.dart';
import 'package:island/utils/format.dart';
import 'package:island/widgets/content/audio.dart'; import 'package:island/widgets/content/audio.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'image.dart'; import 'image.dart';
import 'video.dart'; import 'video.dart';
@@ -60,7 +64,45 @@ class CloudFileWidget extends HookConsumerWidget {
child: UniversalAudio(uri: uri, filename: item.name), child: UniversalAudio(uri: uri, filename: item.name),
), ),
), ),
_ => Text('Unable render for ${item.mimeType}'), _ => Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Symbols.insert_drive_file,
size: 48,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
const Gap(8),
Text(
item.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
Text(
formatFileSize(item.size),
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const Gap(8),
TextButton.icon(
onPressed: () {
launchUrlString(
'https://fs.solian.app/files/${item.id}',
mode: LaunchMode.externalApplication,
);
},
icon: const Icon(Symbols.launch),
label: Text('openInBrowser').tr(),
),
],
).padding(all: 8),
}; };
if (heroTag != null) { if (heroTag != null) {

View File

@@ -1,6 +1,7 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -388,26 +389,32 @@ class ComposeLogic {
} }
static Future<void> pickPhotoMedia(WidgetRef ref, ComposeState state) async { static Future<void> pickPhotoMedia(WidgetRef ref, ComposeState state) async {
final result = await ref final result = await FilePicker.platform.pickFiles(
.watch(imagePickerProvider) type: FileType.image,
.pickMultiImage(requestFullMetadata: true); allowMultiple: true,
if (result.isEmpty) return; allowCompression: false,
);
if (result == null || result.count == 0) return;
state.attachments.value = [ state.attachments.value = [
...state.attachments.value, ...state.attachments.value,
...result.map( ...result.files.map(
(e) => UniversalFile(data: e, type: UniversalFileType.image), (e) => UniversalFile(data: e, type: UniversalFileType.image),
), ),
]; ];
} }
static Future<void> pickVideoMedia(WidgetRef ref, ComposeState state) async { static Future<void> pickVideoMedia(WidgetRef ref, ComposeState state) async {
final result = await ref final result = await FilePicker.platform.pickFiles(
.watch(imagePickerProvider) type: FileType.video,
.pickVideo(source: ImageSource.gallery); allowMultiple: true,
if (result == null) return; allowCompression: false,
);
if (result == null || result.count == 0) return;
state.attachments.value = [ state.attachments.value = [
...state.attachments.value, ...state.attachments.value,
UniversalFile(data: result, type: UniversalFileType.video), ...result.files.map(
(e) => UniversalFile(data: e, type: UniversalFileType.video),
),
]; ];
} }

View File

@@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -105,7 +106,7 @@ class PostFeaturedList extends HookConsumerWidget {
spacing: 8, spacing: 8,
children: [ children: [
const Icon(Symbols.highlight), const Icon(Symbols.highlight),
Text('Highlight Posts'), const Text('highlightPost').tr(),
Spacer(), Spacer(),
IconButton( IconButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,

View File

@@ -18,6 +18,7 @@ import 'package:island/screens/posts/compose.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/markdown.dart'; import 'package:island/widgets/content/markdown.dart';
import 'package:island/widgets/post/post_item_screenshot.dart'; import 'package:island/widgets/post/post_item_screenshot.dart';
import 'package:island/widgets/post/post_pin_sheet.dart';
import 'package:island/widgets/post/post_shared.dart'; import 'package:island/widgets/post/post_shared.dart';
import 'package:island/widgets/safety/abuse_report_helper.dart'; import 'package:island/widgets/safety/abuse_report_helper.dart';
import 'package:island/widgets/share/share_sheet.dart'; import 'package:island/widgets/share/share_sheet.dart';
@@ -202,6 +203,45 @@ class PostActionableItem extends HookConsumerWidget {
); );
}, },
), ),
if (isAuthor && item.pinMode == null)
MenuAction(
title: 'pinPost'.tr(),
image: MenuImage.icon(Symbols.keep),
callback: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => PostPinSheet(post: item),
).then((value) {
if (value is int) {
onUpdate?.call(item.copyWith(pinMode: value));
}
});
},
)
else if (isAuthor && item.pinMode != null)
MenuAction(
title: 'unpinPost'.tr(),
image: MenuImage.icon(Symbols.keep_off),
callback: () {
showConfirmAlert('unpinPostHint'.tr(), 'unpinPost'.tr()).then(
(confirm) async {
if (confirm) {
final client = ref.watch(apiClientProvider);
try {
if (context.mounted) showLoadingModal(context);
await client.delete('/sphere/posts/${item.id}/pin');
onUpdate?.call(item.copyWith(pinMode: null));
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
}
}
},
);
},
),
MenuSeparator(), MenuSeparator(),
MenuAction( MenuAction(
title: 'share'.tr(), title: 'share'.tr(),
@@ -300,7 +340,14 @@ class PostItem extends HookConsumerWidget {
final delta = isRemoving ? -1 : 1; final delta = isRemoving ? -1 : 1;
final reactionsCount = Map<String, int>.from(item.reactionsCount); final reactionsCount = Map<String, int>.from(item.reactionsCount);
reactionsCount[symbol] = (reactionsCount[symbol] ?? 0) + delta; reactionsCount[symbol] = (reactionsCount[symbol] ?? 0) + delta;
onUpdate?.call(item.copyWith(reactionsCount: reactionsCount)); final reactionsMade = Map<String, bool>.from(item.reactionsMade);
reactionsMade[symbol] = delta == 1 ? true : false;
onUpdate?.call(
item.copyWith(
reactionsCount: reactionsCount,
reactionsMade: reactionsMade,
),
);
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
}); });
reacting.value = false; reacting.value = false;

View File

@@ -10,6 +10,7 @@ import 'package:island/widgets/alert.dart';
import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_item.dart';
import 'package:island/widgets/post/post_shared.dart'; import 'package:island/widgets/post/post_shared.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:super_context_menu/super_context_menu.dart'; import 'package:super_context_menu/super_context_menu.dart';
class PostItemCreator extends HookConsumerWidget { class PostItemCreator extends HookConsumerWidget {
@@ -33,7 +34,7 @@ class PostItemCreator extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final renderingPadding = final renderingPadding =
padding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 16); padding ?? const EdgeInsets.symmetric(horizontal: 8, vertical: 8);
return ContextMenuWidget( return ContextMenuWidget(
menuProvider: (_) { menuProvider: (_) {
@@ -97,18 +98,22 @@ class PostItemCreator extends HookConsumerWidget {
context.goNamed('postDetail', pathParameters: {'id': item.id}); context.goNamed('postDetail', pathParameters: {'id': item.id});
} }
}, },
child: Padding( child: Column(
padding: renderingPadding, crossAxisAlignment: CrossAxisAlignment.start,
child: Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, Gap(renderingPadding.vertical),
children: [ PostHeader(item: item, renderingPadding: renderingPadding),
PostHeader(item: item), PostBody(item: item, renderingPadding: renderingPadding),
PostBody(item: item), ReferencedPostWidget(
ReferencedPostWidget(item: item), item: item,
const Gap(16), renderingPadding: renderingPadding,
_buildAnalyticsSection(context), ),
], const Gap(16),
), _buildAnalyticsSection(
context,
).padding(horizontal: renderingPadding.horizontal),
Gap(renderingPadding.vertical),
],
), ),
), ),
), ),

View File

@@ -21,6 +21,7 @@ class PostListNotifier extends _$PostListNotifier
int? type, int? type,
List<String>? categories, List<String>? categories,
List<String>? tags, List<String>? tags,
bool? pinned,
bool shuffle = false, bool shuffle = false,
}) { }) {
return fetch(cursor: null); return fetch(cursor: null);
@@ -40,6 +41,7 @@ class PostListNotifier extends _$PostListNotifier
if (tags != null) 'tags': tags, if (tags != null) 'tags': tags,
if (categories != null) 'categories': categories, if (categories != null) 'categories': categories,
if (shuffle) 'shuffle': true, if (shuffle) 'shuffle': true,
if (pinned != null) 'pinned': pinned,
}; };
final response = await client.get( final response = await client.get(
@@ -77,6 +79,7 @@ class SliverPostList extends HookConsumerWidget {
final List<String>? categories; final List<String>? categories;
final List<String>? tags; final List<String>? tags;
final bool shuffle; final bool shuffle;
final bool? pinned;
final PostItemType itemType; final PostItemType itemType;
final Color? backgroundColor; final Color? backgroundColor;
final EdgeInsets? padding; final EdgeInsets? padding;
@@ -93,6 +96,7 @@ class SliverPostList extends HookConsumerWidget {
this.categories, this.categories,
this.tags, this.tags,
this.shuffle = false, this.shuffle = false,
this.pinned,
this.itemType = PostItemType.regular, this.itemType = PostItemType.regular,
this.backgroundColor, this.backgroundColor,
this.padding, this.padding,
@@ -104,33 +108,19 @@ class SliverPostList extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final provider = postListNotifierProvider(
pubName: pubName,
realm: realm,
type: type,
categories: categories,
tags: tags,
shuffle: shuffle,
pinned: pinned,
);
return PagingHelperSliverView( return PagingHelperSliverView(
provider: postListNotifierProvider( provider: provider,
pubName: pubName, futureRefreshable: provider.future,
realm: realm, notifierRefreshable: provider.notifier,
type: type,
categories: categories,
tags: tags,
shuffle: shuffle,
),
futureRefreshable:
postListNotifierProvider(
pubName: pubName,
realm: realm,
type: type,
categories: categories,
tags: tags,
shuffle: shuffle,
).future,
notifierRefreshable:
postListNotifierProvider(
pubName: pubName,
realm: realm,
type: type,
categories: categories,
tags: tags,
shuffle: shuffle,
).notifier,
contentBuilder: contentBuilder:
(data, widgetCount, endItemView) => SliverList.builder( (data, widgetCount, endItemView) => SliverList.builder(
itemCount: widgetCount, itemCount: widgetCount,

View File

@@ -6,7 +6,7 @@ part of 'post_list.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$postListNotifierHash() => r'faa0b939fae56367ff120ce63d9deb17b1995c9c'; String _$postListNotifierHash() => r'3c0a8154ded4bcd8f5456f7a4ea2e542f57efa85';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -36,6 +36,7 @@ abstract class _$PostListNotifier
late final int? type; late final int? type;
late final List<String>? categories; late final List<String>? categories;
late final List<String>? tags; late final List<String>? tags;
late final bool? pinned;
late final bool shuffle; late final bool shuffle;
FutureOr<CursorPagingData<SnPost>> build({ FutureOr<CursorPagingData<SnPost>> build({
@@ -44,6 +45,7 @@ abstract class _$PostListNotifier
int? type, int? type,
List<String>? categories, List<String>? categories,
List<String>? tags, List<String>? tags,
bool? pinned,
bool shuffle = false, bool shuffle = false,
}); });
} }
@@ -65,6 +67,7 @@ class PostListNotifierFamily
int? type, int? type,
List<String>? categories, List<String>? categories,
List<String>? tags, List<String>? tags,
bool? pinned,
bool shuffle = false, bool shuffle = false,
}) { }) {
return PostListNotifierProvider( return PostListNotifierProvider(
@@ -73,6 +76,7 @@ class PostListNotifierFamily
type: type, type: type,
categories: categories, categories: categories,
tags: tags, tags: tags,
pinned: pinned,
shuffle: shuffle, shuffle: shuffle,
); );
} }
@@ -87,6 +91,7 @@ class PostListNotifierFamily
type: provider.type, type: provider.type,
categories: provider.categories, categories: provider.categories,
tags: provider.tags, tags: provider.tags,
pinned: provider.pinned,
shuffle: provider.shuffle, shuffle: provider.shuffle,
); );
} }
@@ -120,6 +125,7 @@ class PostListNotifierProvider
int? type, int? type,
List<String>? categories, List<String>? categories,
List<String>? tags, List<String>? tags,
bool? pinned,
bool shuffle = false, bool shuffle = false,
}) : this._internal( }) : this._internal(
() => () =>
@@ -129,6 +135,7 @@ class PostListNotifierProvider
..type = type ..type = type
..categories = categories ..categories = categories
..tags = tags ..tags = tags
..pinned = pinned
..shuffle = shuffle, ..shuffle = shuffle,
from: postListNotifierProvider, from: postListNotifierProvider,
name: r'postListNotifierProvider', name: r'postListNotifierProvider',
@@ -144,6 +151,7 @@ class PostListNotifierProvider
type: type, type: type,
categories: categories, categories: categories,
tags: tags, tags: tags,
pinned: pinned,
shuffle: shuffle, shuffle: shuffle,
); );
@@ -159,6 +167,7 @@ class PostListNotifierProvider
required this.type, required this.type,
required this.categories, required this.categories,
required this.tags, required this.tags,
required this.pinned,
required this.shuffle, required this.shuffle,
}) : super.internal(); }) : super.internal();
@@ -167,6 +176,7 @@ class PostListNotifierProvider
final int? type; final int? type;
final List<String>? categories; final List<String>? categories;
final List<String>? tags; final List<String>? tags;
final bool? pinned;
final bool shuffle; final bool shuffle;
@override @override
@@ -179,6 +189,7 @@ class PostListNotifierProvider
type: type, type: type,
categories: categories, categories: categories,
tags: tags, tags: tags,
pinned: pinned,
shuffle: shuffle, shuffle: shuffle,
); );
} }
@@ -195,6 +206,7 @@ class PostListNotifierProvider
..type = type ..type = type
..categories = categories ..categories = categories
..tags = tags ..tags = tags
..pinned = pinned
..shuffle = shuffle, ..shuffle = shuffle,
from: from, from: from,
name: null, name: null,
@@ -206,6 +218,7 @@ class PostListNotifierProvider
type: type, type: type,
categories: categories, categories: categories,
tags: tags, tags: tags,
pinned: pinned,
shuffle: shuffle, shuffle: shuffle,
), ),
); );
@@ -228,6 +241,7 @@ class PostListNotifierProvider
other.type == type && other.type == type &&
other.categories == categories && other.categories == categories &&
other.tags == tags && other.tags == tags &&
other.pinned == pinned &&
other.shuffle == shuffle; other.shuffle == shuffle;
} }
@@ -239,6 +253,7 @@ class PostListNotifierProvider
hash = _SystemHash.combine(hash, type.hashCode); hash = _SystemHash.combine(hash, type.hashCode);
hash = _SystemHash.combine(hash, categories.hashCode); hash = _SystemHash.combine(hash, categories.hashCode);
hash = _SystemHash.combine(hash, tags.hashCode); hash = _SystemHash.combine(hash, tags.hashCode);
hash = _SystemHash.combine(hash, pinned.hashCode);
hash = _SystemHash.combine(hash, shuffle.hashCode); hash = _SystemHash.combine(hash, shuffle.hashCode);
return _SystemHash.finish(hash); return _SystemHash.finish(hash);
@@ -264,6 +279,9 @@ mixin PostListNotifierRef
/// The parameter `tags` of this provider. /// The parameter `tags` of this provider.
List<String>? get tags; List<String>? get tags;
/// The parameter `pinned` of this provider.
bool? get pinned;
/// The parameter `shuffle` of this provider. /// The parameter `shuffle` of this provider.
bool get shuffle; bool get shuffle;
} }
@@ -289,6 +307,8 @@ class _PostListNotifierProviderElement
@override @override
List<String>? get tags => (origin as PostListNotifierProvider).tags; List<String>? get tags => (origin as PostListNotifierProvider).tags;
@override @override
bool? get pinned => (origin as PostListNotifierProvider).pinned;
@override
bool get shuffle => (origin as PostListNotifierProvider).shuffle; bool get shuffle => (origin as PostListNotifierProvider).shuffle;
} }

View File

@@ -0,0 +1,124 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
class PostPinSheet extends HookConsumerWidget {
final SnPost post;
const PostPinSheet({super.key, required this.post});
@override
Widget build(BuildContext context, WidgetRef ref) {
final mode = useState(0);
Future<void> pinPost() async {
try {
showLoadingModal(context);
final client = ref.watch(apiClientProvider);
await client.post(
'/sphere/posts/${post.id}/pin',
data: {'mode': mode.value},
);
if (context.mounted) Navigator.of(context).pop(mode.value);
} catch (e) {
showErrorAlert(e);
} finally {
if (context.mounted) hideLoadingModal(context);
}
}
return SheetScaffold(
titleText: 'pinPost'.tr(),
heightFactor: 0.6,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Publisher page pin option (always available)
ListTile(
leading: Radio<int>(
value: 0,
groupValue: mode.value,
onChanged: (value) {
mode.value = value!;
},
),
title: Text('publisherPage'.tr()),
subtitle: Text('pinPostPublisherHint'.tr()),
onTap: () {
mode.value = 0;
},
),
// Realm page pin option (show always, but disabled when not available)
ListTile(
leading: Radio<int>(
value: 1,
groupValue: mode.value,
onChanged:
post.realmId != null && post.realmId!.isNotEmpty
? (value) {
mode.value = value!;
}
: null,
),
title: Text('realmPage'.tr()),
subtitle:
post.realmId != null && post.realmId!.isNotEmpty
? Text('pinPostRealmHint'.tr())
: Text('pinPostRealmDisabledHint'.tr()),
onTap:
post.realmId != null && post.realmId!.isNotEmpty
? () {
mode.value = 1;
}
: null,
enabled: post.realmId != null && post.realmId!.isNotEmpty,
),
// Reply page pin option (show always, but disabled when not available)
// Disabled for now because im being lazy
// ListTile(
// leading: Radio<int>(
// value: 2,
// groupValue: mode.value,
// onChanged:
// post.repliedPostId != null && post.repliedPostId!.isNotEmpty
// ? (value) {
// mode.value = value!;
// }
// : null,
// ),
// title: Text('replyPage'.tr()),
// subtitle:
// post.repliedPostId != null && post.repliedPostId!.isNotEmpty
// ? Text('pinPostReplyHint'.tr())
// : Text('pinPostReplyDisabledHint'.tr()),
// onTap:
// post.repliedPostId != null && post.repliedPostId!.isNotEmpty
// ? () {
// mode.value = 2;
// }
// : null,
// enabled:
// post.repliedPostId != null && post.repliedPostId!.isNotEmpty,
// ),
const SizedBox(height: 16),
// Pin button
FilledButton.icon(
onPressed: pinPost,
icon: const Icon(Symbols.keep),
label: Text('pin'.tr()),
).padding(horizontal: 24),
],
),
);
}
}

View File

@@ -545,107 +545,119 @@ class PostHeader extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Column(
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 12,
children: [ children: [
GestureDetector( if (item.pinMode != null)
onTap: Row(
isInteractive spacing: 4,
? () {
context.pushNamed(
'publisherProfile',
pathParameters: {'name': item.publisher.name},
);
}
: null,
child: ProfilePictureWidget(
file: item.publisher.picture,
radius: 16,
borderRadius: item.publisher.type == 0 ? null : 6,
),
),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( const Icon(Symbols.keep, size: 15, fill: 1),
crossAxisAlignment: CrossAxisAlignment.center, Text('pinnedPost').tr().fontSize(13),
spacing: 4, ],
).opacity(0.8).padding(horizontal: 8, bottom: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 12,
children: [
GestureDetector(
onTap:
isInteractive
? () {
context.pushNamed(
'publisherProfile',
pathParameters: {'name': item.publisher.name},
);
}
: null,
child: ProfilePictureWidget(
file: item.publisher.picture,
radius: 16,
borderRadius: item.publisher.type == 0 ? null : 6,
),
),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(item.publisher.nick).bold(), Row(
if (item.publisher.verification != null) crossAxisAlignment: CrossAxisAlignment.center,
VerificationMark(mark: item.publisher.verification!), spacing: 4,
if (item.realm == null) children: [
Text('@${item.publisher.name}').fontSize(11) Text(item.publisher.nick).bold(),
else if (item.publisher.verification != null)
...([ VerificationMark(mark: item.publisher.verification!),
const Icon(Symbols.arrow_right, size: 14), if (item.realm == null)
Flexible( Text('@${item.publisher.name}').fontSize(11)
child: InkWell( else
child: Row( ...([
mainAxisSize: MainAxisSize.min, const Icon(Symbols.arrow_right, size: 14),
spacing: 5, Flexible(
children: [ child: InkWell(
Flexible( child: Row(
child: Text( mainAxisSize: MainAxisSize.min,
item.realm!.name, spacing: 5,
maxLines: 1, children: [
overflow: TextOverflow.ellipsis, Flexible(
), child: Text(
), item.realm!.name,
ProfilePictureWidget( maxLines: 1,
file: item.realm!.picture, overflow: TextOverflow.ellipsis,
fallbackIcon: Symbols.group, ),
radius: 9, ),
ProfilePictureWidget(
file: item.realm!.picture,
fallbackIcon: Symbols.group,
radius: 9,
),
],
), ),
onTap: () {
GoRouter.of(context).pushNamed(
'realmDetail',
pathParameters: {'slug': item.realm!.slug},
);
},
),
),
]),
],
),
Row(
spacing: 6,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
!isFullPost && isRelativeTime
? (item.publishedAt ?? item.createdAt)!
.formatRelative(context)
: (item.publishedAt ?? item.createdAt)!
.formatSystem(),
).fontSize(10),
if (item.editedAt != null)
Text(
'editedAt'.tr(
args: [
!isFullPost && isRelativeTime
? item.editedAt!.formatRelative(context)
: item.editedAt!.formatSystem(),
], ],
), ),
onTap: () { ).fontSize(10),
GoRouter.of(context).pushNamed( if (item.visibility != 0)
'realmDetail', Text(
pathParameters: {'slug': item.realm!.slug}, PostVisibilityHelpers.getVisibilityText(
); item.visibility,
}, ).tr(),
), ).fontSize(10),
), ],
]), ),
], ],
), ),
Row( ),
spacing: 6, if (trailing != null) trailing!,
crossAxisAlignment: CrossAxisAlignment.end, ],
children: [
Text(
!isFullPost && isRelativeTime
? (item.publishedAt ?? item.createdAt)!.formatRelative(
context,
)
: (item.publishedAt ?? item.createdAt)!.formatSystem(),
).fontSize(10),
if (item.editedAt != null)
Text(
'editedAt'.tr(
args: [
!isFullPost && isRelativeTime
? item.editedAt!.formatRelative(context)
: item.editedAt!.formatSystem(),
],
),
).fontSize(10),
if (item.visibility != 0)
Text(
PostVisibilityHelpers.getVisibilityText(
item.visibility,
).tr(),
).fontSize(10),
],
),
],
),
), ),
if (trailing != null) trailing!,
], ],
).padding(horizontal: renderingPadding.horizontal, bottom: 4); ).padding(horizontal: renderingPadding.horizontal, bottom: 4);
} }
@@ -750,7 +762,9 @@ class PostBody extends ConsumerWidget {
).padding(bottom: 4), ).padding(bottom: 4),
MarkdownTextContent( MarkdownTextContent(
content: content:
item.isTruncated ? '${item.content!}...' : item.content!, item.isTruncated
? '${item.content!}...'
: item.content ?? '',
isSelectable: isTextSelectable, isSelectable: isTextSelectable,
), ),
if (translationSection != null) translationSection!, if (translationSection != null) translationSection!,

View File

@@ -25,7 +25,7 @@ class PostShuffleScreen extends HookConsumerWidget {
return cardSwiperController.dispose; return cardSwiperController.dispose;
}, []); }, []);
const kBottomControlHeight = 64.0; const kBottomControlHeight = 80.0;
return AppScaffold( return AppScaffold(
appBar: AppBar(title: const Text('postShuffle').tr()), appBar: AppBar(title: const Text('postShuffle').tr()),

View File

@@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <bitsdojo_window_linux/bitsdojo_window_plugin.h> #include <bitsdojo_window_linux/bitsdojo_window_plugin.h>
#include <file_saver/file_saver_plugin.h>
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <flutter_platform_alert/flutter_platform_alert_plugin.h> #include <flutter_platform_alert/flutter_platform_alert_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h> #include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
@@ -28,6 +29,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar = g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin");
bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar); bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar);
g_autoptr(FlPluginRegistrar) file_saver_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin");
file_saver_plugin_register_with_registrar(file_saver_registrar);
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar); file_selector_plugin_register_with_registrar(file_selector_linux_registrar);

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
bitsdojo_window_linux bitsdojo_window_linux
file_saver
file_selector_linux file_selector_linux
flutter_platform_alert flutter_platform_alert
flutter_secure_storage_linux flutter_secure_storage_linux

View File

@@ -9,6 +9,7 @@ import bitsdojo_window_macos
import connectivity_plus import connectivity_plus
import device_info_plus import device_info_plus
import file_picker import file_picker
import file_saver
import file_selector_macos import file_selector_macos
import firebase_analytics import firebase_analytics
import firebase_core import firebase_core
@@ -45,6 +46,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FirebaseAnalyticsPlugin")) FirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FirebaseAnalyticsPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))

View File

@@ -9,87 +9,89 @@ PODS:
- FlutterMacOS - FlutterMacOS
- file_picker (0.0.1): - file_picker (0.0.1):
- FlutterMacOS - FlutterMacOS
- file_saver (0.0.1):
- FlutterMacOS
- file_selector_macos (0.0.1): - file_selector_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- Firebase/CoreOnly (12.0.0): - Firebase/CoreOnly (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- Firebase/Crashlytics (12.0.0): - Firebase/Crashlytics (12.2.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseCrashlytics (~> 12.0.0) - FirebaseCrashlytics (~> 12.2.0)
- Firebase/Messaging (12.0.0): - Firebase/Messaging (12.2.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseMessaging (~> 12.0.0) - FirebaseMessaging (~> 12.2.0)
- firebase_analytics (12.0.0): - firebase_analytics (12.0.1):
- firebase_core - firebase_core
- FirebaseAnalytics (= 12.0.0) - FirebaseAnalytics (= 12.2.0)
- FlutterMacOS - FlutterMacOS
- firebase_core (4.0.0): - firebase_core (4.1.0):
- Firebase/CoreOnly (~> 12.0.0) - Firebase/CoreOnly (~> 12.2.0)
- FlutterMacOS - FlutterMacOS
- firebase_crashlytics (5.0.0): - firebase_crashlytics (5.0.1):
- Firebase/CoreOnly (~> 12.0.0) - Firebase/CoreOnly (~> 12.2.0)
- Firebase/Crashlytics (~> 12.0.0) - Firebase/Crashlytics (~> 12.2.0)
- firebase_core - firebase_core
- FlutterMacOS - FlutterMacOS
- firebase_messaging (16.0.0): - firebase_messaging (16.0.1):
- Firebase/CoreOnly (~> 12.0.0) - Firebase/CoreOnly (~> 12.2.0)
- Firebase/Messaging (~> 12.0.0) - Firebase/Messaging (~> 12.2.0)
- firebase_core - firebase_core
- FlutterMacOS - FlutterMacOS
- FirebaseAnalytics (12.0.0): - FirebaseAnalytics (12.2.0):
- FirebaseAnalytics/Default (= 12.0.0) - FirebaseAnalytics/Default (= 12.2.0)
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- FirebaseAnalytics/Default (12.0.0): - FirebaseAnalytics/Default (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- GoogleAppMeasurement/Default (= 12.0.0) - GoogleAppMeasurement/Default (= 12.2.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- FirebaseCore (12.0.0): - FirebaseCore (12.2.0):
- FirebaseCoreInternal (~> 12.0.0) - FirebaseCoreInternal (~> 12.2.0)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1) - GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreExtension (12.0.0): - FirebaseCoreExtension (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseCoreInternal (12.0.0): - FirebaseCoreInternal (12.2.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseCrashlytics (12.0.0): - FirebaseCrashlytics (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- FirebaseRemoteConfigInterop (~> 12.0.0) - FirebaseRemoteConfigInterop (~> 12.2.0)
- FirebaseSessions (~> 12.0.0) - FirebaseSessions (~> 12.2.0)
- GoogleDataTransport (~> 10.1) - GoogleDataTransport (~> 10.1)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4) - PromisesObjC (~> 2.4)
- FirebaseInstallations (12.0.0): - FirebaseInstallations (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4) - PromisesObjC (~> 2.4)
- FirebaseMessaging (12.0.0): - FirebaseMessaging (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- GoogleDataTransport (~> 10.1) - GoogleDataTransport (~> 10.1)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Reachability (~> 8.1) - GoogleUtilities/Reachability (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- FirebaseRemoteConfigInterop (12.0.0) - FirebaseRemoteConfigInterop (12.2.0)
- FirebaseSessions (12.0.0): - FirebaseSessions (12.2.0):
- FirebaseCore (~> 12.0.0) - FirebaseCore (~> 12.2.0)
- FirebaseCoreExtension (~> 12.0.0) - FirebaseCoreExtension (~> 12.2.0)
- FirebaseInstallations (~> 12.0.0) - FirebaseInstallations (~> 12.2.0)
- GoogleDataTransport (~> 10.1) - GoogleDataTransport (~> 10.1)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1)
@@ -107,30 +109,30 @@ PODS:
- flutter_udid (0.0.1): - flutter_udid (0.0.1):
- FlutterMacOS - FlutterMacOS
- SAMKeychain - SAMKeychain
- flutter_webrtc (1.0.0): - flutter_webrtc (1.1.0):
- FlutterMacOS - FlutterMacOS
- WebRTC-SDK (= 137.7151.02) - WebRTC-SDK (= 137.7151.03)
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- gal (1.0.0): - gal (1.0.0):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- GoogleAppMeasurement/Core (12.0.0): - GoogleAppMeasurement/Core (12.2.0):
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Default (12.0.0): - GoogleAppMeasurement/Default (12.2.0):
- GoogleAdsOnDeviceConversion (= 2.1.0) - GoogleAdsOnDeviceConversion (= 2.3.0)
- GoogleAppMeasurement/Core (= 12.0.0) - GoogleAppMeasurement/Core (= 12.2.0)
- GoogleAppMeasurement/IdentitySupport (= 12.0.0) - GoogleAppMeasurement/IdentitySupport (= 12.2.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- GoogleAppMeasurement/IdentitySupport (12.0.0): - GoogleAppMeasurement/IdentitySupport (12.2.0):
- GoogleAppMeasurement/Core (= 12.0.0) - GoogleAppMeasurement/Core (= 12.2.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1) - GoogleUtilities/Network (~> 8.1)
@@ -171,7 +173,7 @@ PODS:
- livekit_client (2.5.0): - livekit_client (2.5.0):
- flutter_webrtc - flutter_webrtc
- FlutterMacOS - FlutterMacOS
- WebRTC-SDK (= 137.7151.02) - WebRTC-SDK (= 137.7151.03)
- local_auth_darwin (0.0.1): - local_auth_darwin (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -241,7 +243,7 @@ PODS:
- FlutterMacOS - FlutterMacOS
- wakelock_plus (0.0.1): - wakelock_plus (0.0.1):
- FlutterMacOS - FlutterMacOS
- WebRTC-SDK (137.7151.02) - WebRTC-SDK (137.7151.03)
DEPENDENCIES: DEPENDENCIES:
- bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`) - bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`)
@@ -249,6 +251,7 @@ DEPENDENCIES:
- croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`) - croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
- file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`)
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
- firebase_analytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos`) - firebase_analytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos`)
- firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`) - firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`)
@@ -315,6 +318,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
file_picker: file_picker:
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
file_saver:
:path: Flutter/ephemeral/.symlinks/plugins/file_saver/macos
file_selector_macos: file_selector_macos:
:path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
firebase_analytics: firebase_analytics:
@@ -384,34 +389,35 @@ SPEC CHECKSUMS:
croppy: d9bfc8c02f3cd1851f669a421df298a474b78f43 croppy: d9bfc8c02f3cd1851f669a421df298a474b78f43
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76 device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31 file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
Firebase: 800d487043c0557d9faed71477a38d9aafb08a41 Firebase: 26f6f8d460603af3df970ad505b16b15f5e2e9a1
firebase_analytics: 53f0dc87ad10f56a6df8746da60d8a5fe41f886f firebase_analytics: efe6e51156f4565f3791d99072e8e3b0fcca0e91
firebase_core: eeea10f64026b68cd0bc3dee079ab4717e22909e firebase_core: a8d3b82b0a87bd1d0ebc21e686b37e939c56e6e1
firebase_crashlytics: 7be1dacc38809971354def57193b280636a3d51a firebase_crashlytics: fdbe67a1229a9e583ebf2b155541491aa83927bb
firebase_messaging: 5eefcd5bde556bfacdd9968e11c52f39032dfbe5 firebase_messaging: 6fb526705903e2e56e38a6ff56b43668b052b01b
FirebaseAnalytics: 6d790cd1b159b4eb61a99948df0934ce505a34f7 FirebaseAnalytics: e04e23bc070e3014aa5cf4980f9df7ce5cd79ec8
FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a FirebaseCore: 311c48a147ad4a0ab7febbaed89e8025c67510cd
FirebaseCoreExtension: 639afb3de6abd611952be78a794c54a47fa0f361 FirebaseCoreExtension: 73af080c22a2f7b44cefa391dc08f7e4ee162cb5
FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55 FirebaseCoreInternal: 56ea29f3dad2894f81b060f706f9d53509b6ed3b
FirebaseCrashlytics: db75aa0cab8d00f68406fa247c32fe17ade884d7 FirebaseCrashlytics: f83cbf176d5c637ade108c0aacf1ccbd5ec499bf
FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988 FirebaseInstallations: 3e884b01feabdf67582a80f3250425a00979b4ed
FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde FirebaseMessaging: 43ec73bbfedd0c385a849bb91593ab4ad4b9e48e
FirebaseRemoteConfigInterop: bfa0ea72ba3dc5af739777296424e46bd6f42613 FirebaseRemoteConfigInterop: 0896fd52ab72586a355c8f389ff85aaa9e5375e1
FirebaseSessions: 4e784acda213108aafef536535cdfc03504acc42 FirebaseSessions: f4692789e770bec66ce17d772c0e9561c4f11737
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284 flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54 flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54
flutter_timezone: d59eea86178cbd7943cd2431cc2eaa9850f935d8 flutter_timezone: d59eea86178cbd7943cd2431cc2eaa9850f935d8
flutter_udid: d26e455e8c06174e6aff476e147defc6cae38495 flutter_udid: d26e455e8c06174e6aff476e147defc6cae38495
flutter_webrtc: 0d70bd8782c19bde286dc52f766eebbea26de201 flutter_webrtc: 1ce7fe9a42f085286378355a575e682edd7f114d
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
gal: baecd024ebfd13c441269ca7404792a7152fde89 gal: baecd024ebfd13c441269ca7404792a7152fde89
GoogleAppMeasurement: 8f6ab04ad6ae493b53fcf56bd26323fb2f1384f3 GoogleAppMeasurement: 09f341dfa8527d1612a09cbfe809a242c0b737af
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
livekit_client: 0b0515e03858b86a7c14cc7fd6f772331f6ee84c livekit_client: 5a5c0f1081978542bbf9a986c7ac9bffcdb73906
local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19 local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19
media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65 media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65
media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758 media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758
@@ -434,7 +440,7 @@ SPEC CHECKSUMS:
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
volume_controller: 5c068e6d085c80dadd33fc2c918d2114b775b3dd volume_controller: 5c068e6d085c80dadd33fc2c918d2114b775b3dd
wakelock_plus: 21ddc249ac4b8d018838dbdabd65c5976c308497 wakelock_plus: 21ddc249ac4b8d018838dbdabd65c5976c308497
WebRTC-SDK: d20de357dcbf7c9696b124b39f3ff62125107e4b WebRTC-SDK: 69d4e56b0b4b27d788e87bab9b9a1326ed05b1e3
PODFILE CHECKSUM: 346bfb2deb41d4a6ebd6f6799f92188bde2d246f PODFILE CHECKSUM: 346bfb2deb41d4a6ebd6f6799f92188bde2d246f

View File

@@ -586,7 +586,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
@@ -674,7 +674,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx; SDKROOT = macosx;
@@ -724,7 +724,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;

View File

@@ -13,10 +13,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _flutterfire_internals name: _flutterfire_internals
sha256: bb84ee51e527053dd8e25ecc9f97a6abfdc19130fb4d883e4e8585e23e7e6dd8 sha256: "948f7d74f41dd6f2d563ea9f4c21d7ea764f8e047d2b24138974c19c24d37eb6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.60" version: "1.3.61"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
@@ -569,6 +569,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.3.2" version: "10.3.2"
file_saver:
dependency: "direct main"
description:
name: file_saver
sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
file_selector_linux: file_selector_linux:
dependency: transitive dependency: transitive
description: description:
@@ -605,34 +613,34 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_analytics name: firebase_analytics
sha256: "07146e89e11302c6b07e3465c2c556ebcdd0053a3c5b1aa9bfd3203b778e5b4c" sha256: dde9d6a7b69b07551a77cfb913c81c64804f7602b07541328322c321e73f2a0e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "12.0.0" version: "12.0.1"
firebase_analytics_platform_interface: firebase_analytics_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_analytics_platform_interface name: firebase_analytics_platform_interface
sha256: "27e81a0efc821bec6cba64abc1083b91c8ddbad28eeb4c6f6b7c78a59d06f259" sha256: "4008d82a58edcbedec34a7b39f457eed24181cb9c89782c104828c42e4c859b2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "5.0.1"
firebase_analytics_web: firebase_analytics_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_analytics_web name: firebase_analytics_web
sha256: "7d87f47462042a7d9125e3123db2783bc72917d85e2719d4cb6aeaec209605e1" sha256: db2a2e8803f5471a5f89b4abacae95ae27e0644f77526879fb81a2c1abc12b5f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.0+1"
firebase_core: firebase_core:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_core name: firebase_core
sha256: "6b343e6f7b72a4f32d7ce8df8c9a28d8f54b4ac20d7c6500f3e8b3969afca457" sha256: "967dae9a65f69377beb9f4ab292ea63ce5befa1ce24682cab1b69ca4b7a46927"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.1.0"
firebase_core_platform_interface: firebase_core_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -645,50 +653,50 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: firebase_core_web name: firebase_core_web
sha256: "5d28b14dd32282fb7ce2b22b897362453755b6b8541d491127dc72b755bb7b16" sha256: f7ee08febc1c4451588ce58ffcf28edaee857e9a196fee88b85deb889990094a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.1.0"
firebase_crashlytics: firebase_crashlytics:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_crashlytics name: firebase_crashlytics
sha256: "95b6871850b1a7e3b09c284c59a0c71fafcad3eee8ac1b6f06aaf8979290cbb8" sha256: f2e175a967712ee1f616ab8843390891a315428ba497ce3d256d4c46f32db6f8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "5.0.1"
firebase_crashlytics_platform_interface: firebase_crashlytics_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_crashlytics_platform_interface name: firebase_crashlytics_platform_interface
sha256: ba5b7a916f1ebedc6db35b33abdc618f202fc25e0792088dfba698e19fec9c09 sha256: b49b90af4a1fd8f30b58abd90af88371969bea51b62838a4f4e737c2098b725e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.8.11" version: "3.8.12"
firebase_messaging: firebase_messaging:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_messaging name: firebase_messaging
sha256: "10272b553a49c13a6cedfd00121047157521f82a5d3f2a1706b9dd28342cc482" sha256: aad5dcdea5698499b70d74d5a53b1f6a9972f85f97225e4b7ac006dd8d4f9bac
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "16.0.0" version: "16.0.1"
firebase_messaging_platform_interface: firebase_messaging_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_messaging_platform_interface name: firebase_messaging_platform_interface
sha256: b846a305feb3f74ee3f0aace447f65a4696bc6550bc828ecf5a84a1b77473d16 sha256: "825bc11767bf50a43dccf49b3026f847ec31d0f176139bfc48d662cc128b5014"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.7.0" version: "4.7.1"
firebase_messaging_web: firebase_messaging_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_messaging_web name: firebase_messaging_web
sha256: "28714749880f7242c5fb3b1ee6c66b41f61453f02ae348b43c82957df80b87ae" sha256: db8dbdd79921245c4de02407e33cae2d1868683be18a5ba948d2af5311e3ef5d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.0.1"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@@ -701,10 +709,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: fl_chart name: fl_chart
sha256: "577aeac8ca414c25333334d7c4bb246775234c0e44b38b10a82b559dd4d764e7" sha256: d3f82f4a38e33ba23d05a08ff304d7d8b22d2a59a5503f20bd802966e915db89
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "1.1.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@@ -878,14 +886,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
flutter_langdetect:
dependency: "direct main"
description:
name: flutter_langdetect
sha256: "93bd865c7d5723eac614744abb32234ee4f593505a293bc17ef097bd55fbdf38"
url: "https://pub.dev"
source: hosted
version: "0.0.2"
flutter_launcher_icons: flutter_launcher_icons:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -959,10 +959,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: "6382ce712ff69b0f719640ce957559dde459e55ecd433c767e06d139ddf16cab" sha256: b0694b7fb1689b0e6cc193b3f1fcac6423c4f93c74fb20b806c6b6f196db0c31
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.29" version: "2.0.30"
flutter_popup_card: flutter_popup_card:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1070,21 +1070,21 @@ packages:
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_webrtc: flutter_webrtc:
dependency: "direct main" dependency: transitive
description: description:
name: flutter_webrtc name: flutter_webrtc
sha256: "69095ba39b83da3de48286dfc0769aa8e9f10491f70058dc8d8ecc960ef7a260" sha256: "945d0a38b90fbca8257eadb167d8fb9fa7075d9a1939fd2953c10054454d1de2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "1.1.0"
font_awesome_flutter: font_awesome_flutter:
dependency: transitive dependency: transitive
description: description:
name: font_awesome_flutter name: font_awesome_flutter
sha256: b738e35f8bb4957896c34957baf922f99c5d415b38ddc8b070d14b7fa95715d4 sha256: "27af5982e6c510dec1ba038eff634fa284676ee84e3fd807225c80c4ad869177"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.9.1" version: "10.10.0"
freezed: freezed:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -1145,18 +1145,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: go_router name: go_router
sha256: ced3fdc143c1437234ac3b8e985f3286cf138968bb83ca9a6f94d22f2951c6b9 sha256: eb059dfe59f08546e9787f895bd01652076f996bcbf485a8609ef990419ad227
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "16.2.0" version: "16.2.1"
google_fonts: google_fonts:
dependency: "direct main" dependency: "direct main"
description: description:
name: google_fonts name: google_fonts
sha256: df9763500dadba0155373e9cb44e202ce21bd9ed5de6bdbd05c5854e86839cb8 sha256: ebc94ed30fd13cefd397cb1658b593f21571f014b7d1197eeb41fb95f05d899a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.0" version: "6.3.1"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@@ -1249,10 +1249,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: image_picker_android name: image_picker_android
sha256: e83b2b05141469c5e19d77e1dfa11096b6b1567d09065b2265d7c6904560050c sha256: "28f3987ca0ec702d346eae1d90eda59603a2101b52f1e234ded62cff1d5cfa6e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.13" version: "0.8.13+1"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
@@ -1361,26 +1361,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.9" version: "11.0.1"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.9" version: "3.0.10"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
lint: lint:
dependency: transitive dependency: transitive
description: description:
@@ -1401,10 +1401,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: livekit_client name: livekit_client
sha256: b3db2d8afa8d1dbe4fd8dfc965fc9d661cb51a8d864ad199919575ce919a40fb sha256: "011affc0fca22b2f9b0e8827219dad9948f84f2bf057980693de13039de904c7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.0+hotfix.1" version: "2.5.0+hotfix.3"
local_auth: local_auth:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1417,10 +1417,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: local_auth_android name: local_auth_android
sha256: "316503f6772dea9c0c038bb7aac4f68ab00112d707d258c770f7fc3c250a2d88" sha256: "48924f4a8b3cc45994ad5993e2e232d3b00788a305c1bf1c7db32cef281ce9a3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.51" version: "1.0.52"
local_auth_darwin: local_auth_darwin:
dependency: transitive dependency: transitive
description: description:
@@ -1449,10 +1449,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: logger name: logger
sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f" sha256: "55d6c23a6c15db14920e037fe7e0dc32e7cdaf3b64b4b25df2d541b5b6b81c0c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "2.6.1"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@@ -1713,10 +1713,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.17" version: "2.2.18"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
@@ -1753,10 +1753,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: petitparser name: petitparser
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.0" version: "7.0.1"
photo_view: photo_view:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1921,10 +1921,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: record_ios name: record_ios
sha256: "895c9467faec72d8e718a3142b51114958f42f18053836a8b484a74f9372f51a" sha256: "13e241ed9cbc220534a40ae6b66222e21288db364d96dd66fb762ebd3cb77c71"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.2"
record_linux: record_linux:
dependency: transitive dependency: transitive
description: description:
@@ -2041,10 +2041,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: screen_brightness_android name: screen_brightness_android
sha256: fb5fa43cb89d0c9b8534556c427db1e97e46594ac5d66ebdcf16063b773d54ed sha256: d34f5321abd03bc3474f4c381f53d189117eba0b039eac1916aa92cca5fd0a96
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
screen_brightness_platform_interface: screen_brightness_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -2105,10 +2105,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e" sha256: a2608114b1ffdcbc9c120eb71a0e207c71da56202852d4aab8a5e30a82269e74
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.11" version: "2.4.12"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
@@ -2246,10 +2246,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sqflite_android name: sqflite_android
sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.1" version: "2.4.2+2"
sqflite_common: sqflite_common:
dependency: transitive dependency: transitive
description: description:
@@ -2398,10 +2398,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.4" version: "0.7.6"
textfield_tags: textfield_tags:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -2496,10 +2496,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: "0aedad096a85b49df2e4725fa32118f9fa580f3b14af7a2d2221896a02cd5656" sha256: "69ee86740f2847b9a4ba6cffa74ed12ce500bbe2b07f3dc1e643439da60637b7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.17" version: "6.3.18"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
@@ -2576,18 +2576,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_graphics_compiler name: vector_graphics_compiler
sha256: ca81fdfaf62a5ab45d7296614aea108d2c7d0efca8393e96174bf4d51e6725b0 sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.18" version: "1.1.19"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.2.0"
very_good_infinite_list: very_good_infinite_list:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -2608,10 +2608,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "15.0.0" version: "15.0.2"
volume_controller: volume_controller:
dependency: transitive dependency: transitive
description: description:
@@ -2640,10 +2640,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: watcher name: watcher
sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a" sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.1.3"
waveform_flutter: waveform_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -2712,10 +2712,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: xml name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.5.0" version: "6.6.1"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
@@ -2725,5 +2725,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.8.0 <4.0.0" dart: ">=3.9.0 <4.0.0"
flutter: ">=3.32.0" flutter: ">=3.32.0"

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 3.2.0+128 version: 3.2.0+129
environment: environment:
sdk: ^3.7.2 sdk: ^3.7.2
@@ -39,7 +39,7 @@ dependencies:
flutter_hooks: ^0.21.3+1 flutter_hooks: ^0.21.3+1
hooks_riverpod: ^2.6.1 hooks_riverpod: ^2.6.1
bitsdojo_window: ^0.1.6 bitsdojo_window: ^0.1.6
go_router: ^16.2.0 go_router: ^16.2.1
styled_widget: ^0.4.1 styled_widget: ^0.4.1
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
flutter_riverpod: ^2.6.1 flutter_riverpod: ^2.6.1
@@ -53,7 +53,7 @@ dependencies:
flutter_highlight: ^0.7.0 flutter_highlight: ^0.7.0
uuid: ^4.5.1 uuid: ^4.5.1
url_launcher: ^6.3.2 url_launcher: ^6.3.2
google_fonts: ^6.3.0 google_fonts: ^6.3.1
gap: ^3.0.1 gap: ^3.0.1
cached_network_image: ^3.4.1 cached_network_image: ^3.4.1
web: ^1.1.1 web: ^1.1.1
@@ -76,12 +76,12 @@ dependencies:
file_picker: ^10.3.2 file_picker: ^10.3.2
riverpod_annotation: ^2.6.1 riverpod_annotation: ^2.6.1
image_picker_platform_interface: ^2.11.0 image_picker_platform_interface: ^2.11.0
image_picker_android: ^0.8.13 image_picker_android: ^0.8.13+1
super_context_menu: ^0.9.1 super_context_menu: ^0.9.1
modal_bottom_sheet: ^3.0.0 modal_bottom_sheet: ^3.0.0
firebase_messaging: ^16.0.0 firebase_messaging: ^16.0.1
flutter_udid: ^4.0.0 flutter_udid: ^4.0.0
firebase_core: ^4.0.0 firebase_core: ^4.1.0
web_socket_channel: ^3.0.3 web_socket_channel: ^3.0.3
material_symbols_icons: ^4.2867.0 material_symbols_icons: ^4.2867.0
drift: ^2.28.1 drift: ^2.28.1
@@ -103,8 +103,7 @@ dependencies:
gal: ^2.3.2 gal: ^2.3.2
dismissible_page: ^1.0.2 dismissible_page: ^1.0.2
super_sliver_list: ^0.4.1 super_sliver_list: ^0.4.1
flutter_webrtc: ^1.0.0 livekit_client: ^2.5.0+hotfix.3
livekit_client: ^2.5.0+hotfix.1
pasteboard: ^0.4.0 pasteboard: ^0.4.0
flutter_colorpicker: ^1.1.0 flutter_colorpicker: ^1.1.0
record: ^6.1.1 record: ^6.1.1
@@ -114,7 +113,7 @@ dependencies:
flutter_popup_card: ^0.0.6 flutter_popup_card: ^0.0.6
timezone: ^0.10.1 timezone: ^0.10.1
flutter_timezone: ^4.1.1 flutter_timezone: ^4.1.1
fl_chart: ^1.0.0 fl_chart: ^1.1.0
sign_in_with_apple: ^7.0.1 sign_in_with_apple: ^7.0.1
flutter_svg: ^2.2.0 flutter_svg: ^2.2.0
native_exif: ^0.6.2 native_exif: ^0.6.2
@@ -131,14 +130,14 @@ dependencies:
mime: ^2.0.0 mime: ^2.0.0
html2md: ^1.3.2 html2md: ^1.3.2
flutter_typeahead: ^5.2.0 flutter_typeahead: ^5.2.0
flutter_langdetect: ^0.0.2
waveform_flutter: ^1.2.0 waveform_flutter: ^1.2.0
flutter_app_update: ^3.2.2 flutter_app_update: ^3.2.2
firebase_crashlytics: ^5.0.0 firebase_crashlytics: ^5.0.1
firebase_analytics: ^12.0.0 firebase_analytics: ^12.0.1
material_color_utilities: ^0.11.1 material_color_utilities: ^0.11.1
screenshot: ^3.0.0 screenshot: ^3.0.0
flutter_card_swiper: ^7.0.2 flutter_card_swiper: ^7.0.2
file_saver: ^0.3.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -8,6 +8,7 @@
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h> #include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
#include <connectivity_plus/connectivity_plus_windows_plugin.h> #include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_saver/file_saver_plugin.h>
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <firebase_core/firebase_core_plugin_c_api.h> #include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h> #include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
@@ -35,6 +36,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar( ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FileSaverPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSaverPlugin"));
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
FirebaseCorePluginCApiRegisterWithRegistrar( FirebaseCorePluginCApiRegisterWithRegistrar(

View File

@@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
bitsdojo_window_windows bitsdojo_window_windows
connectivity_plus connectivity_plus
file_saver
file_selector_windows file_selector_windows
firebase_core firebase_core
flutter_inappwebview_windows flutter_inappwebview_windows