Compare commits
46 Commits
537e49f1a4
...
3.3.0+136
| Author | SHA1 | Date | |
|---|---|---|---|
|
c6594ea2ce
|
|||
|
3bec6e683e
|
|||
|
83e92e2eed
|
|||
|
a83b929d42
|
|||
|
9423affa75
|
|||
|
cda23db609
|
|||
|
61074bc5a3
|
|||
|
5feafa9255
|
|||
|
e604577c1f
|
|||
|
af0ddd1273
|
|||
|
8a6bb34808
|
|||
|
4ef8445c77
|
|||
|
ec39ad6ca3
|
|||
|
eabb3154f1
|
|||
|
910bf20eef
|
|||
|
5efa9b2ae8
|
|||
|
dd3e39e891
|
|||
|
b6896ded23
|
|||
|
f28a73ff9c
|
|||
|
a014b64235
|
|||
|
7e0e7c20d7
|
|||
|
389fa515ba
|
|||
|
681ead02eb
|
|||
|
8d1c145b0b
|
|||
|
51b4754182
|
|||
|
8a2b321701
|
|||
|
f685a7a249
|
|||
|
76009147e9
|
|||
|
ce12f28e56
|
|||
|
3604373a1e
|
|||
|
9704a4c2c7
|
|||
|
67def56ad1
|
|||
|
1be33916af
|
|||
|
e8ff1bfd22
|
|||
|
3ae56f3d89
|
|||
|
707143e998
|
|||
|
1fd34eb2a3
|
|||
|
d7ca41e946
|
|||
|
ad9fb0719a
|
|||
|
e2d315afd4
|
|||
|
6124dbfd79
|
|||
|
5327f04ec0
|
|||
|
41c56a2319
|
|||
|
f9d033542e
|
|||
|
91784e65e6
|
|||
|
9d39c6a825
|
@@ -75,3 +75,4 @@ dependencies {
|
|||||||
flutter {
|
flutter {
|
||||||
source = "../.."
|
source = "../.."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,11 @@
|
|||||||
"reactionPositive": "Postive",
|
"reactionPositive": "Postive",
|
||||||
"reactionNegative": "Negative",
|
"reactionNegative": "Negative",
|
||||||
"reactionNeutral": "Neutral",
|
"reactionNeutral": "Neutral",
|
||||||
|
"customReaction": "Custom Reaction",
|
||||||
|
"customReactions": "Custom Reactions",
|
||||||
|
"stickerPlaceholder": "Sticker Placeholder",
|
||||||
|
"reactionAttitude": "Reaction Attitude",
|
||||||
|
"addReaction": "Add Reaction",
|
||||||
"connectionConnected": "Connected",
|
"connectionConnected": "Connected",
|
||||||
"connectionDisconnected": "Disconnected",
|
"connectionDisconnected": "Disconnected",
|
||||||
"connectionReconnecting": "Reconnecting",
|
"connectionReconnecting": "Reconnecting",
|
||||||
@@ -166,8 +171,8 @@
|
|||||||
"checkInResultLevel3": "Good Luck",
|
"checkInResultLevel3": "Good Luck",
|
||||||
"checkInResultLevel4": "Best Luck",
|
"checkInResultLevel4": "Best Luck",
|
||||||
"checkInActivityTitle": "{} checked in on {} and got a {}",
|
"checkInActivityTitle": "{} checked in on {} and got a {}",
|
||||||
"eventCalander": "Event Calander",
|
"eventCalendar": "Event Calendar",
|
||||||
"eventCalanderEmpty": "No events on that day.",
|
"eventCalendarEmpty": "No events on that day.",
|
||||||
"fortuneGraph": "Fortune Trend",
|
"fortuneGraph": "Fortune Trend",
|
||||||
"noFortuneData": "No fortune data available for this month.",
|
"noFortuneData": "No fortune data available for this month.",
|
||||||
"creatorHub": "Creator Hub",
|
"creatorHub": "Creator Hub",
|
||||||
@@ -474,7 +479,6 @@
|
|||||||
"accountProfileView": "View Profile",
|
"accountProfileView": "View Profile",
|
||||||
"unspecified": "Unspecified",
|
"unspecified": "Unspecified",
|
||||||
"added": "Added",
|
"added": "Added",
|
||||||
"preview": "Preview",
|
|
||||||
"togglePreview": "Toggle Preview",
|
"togglePreview": "Toggle Preview",
|
||||||
"subscribe": "Subscribe",
|
"subscribe": "Subscribe",
|
||||||
"unsubscribe": "Unsubscribe",
|
"unsubscribe": "Unsubscribe",
|
||||||
@@ -635,6 +639,10 @@
|
|||||||
"chatNotJoined": "You have not joined this chat yet.",
|
"chatNotJoined": "You have not joined this chat yet.",
|
||||||
"chatUnableJoin": "You can't join this chat due to it's access control settings.",
|
"chatUnableJoin": "You can't join this chat due to it's access control settings.",
|
||||||
"chatJoin": "Join the Chat",
|
"chatJoin": "Join the Chat",
|
||||||
|
"chatReplyingTo": "Replying to {}",
|
||||||
|
"chatForwarding": "Forwarding message",
|
||||||
|
"chatEditing": "Editing message",
|
||||||
|
"chatNoContent": "No content",
|
||||||
"realmJoin": "Join the Realm",
|
"realmJoin": "Join the Realm",
|
||||||
"realmJoinSuccess": "Successfully joined the realm.",
|
"realmJoinSuccess": "Successfully joined the realm.",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
@@ -1215,5 +1223,34 @@
|
|||||||
"resend": "Resend",
|
"resend": "Resend",
|
||||||
"fileInfoTitle": "File Information",
|
"fileInfoTitle": "File Information",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"info": "Info"
|
"info": "Info",
|
||||||
|
"noStickers": "No Stickers",
|
||||||
|
"noStickersInPack": "This pack does not contains stickers",
|
||||||
|
"noStickerPacks": "No Sticker Packs",
|
||||||
|
"refresh": "Refresh",
|
||||||
|
"spoiler": "Spoiler",
|
||||||
|
"activityHeatmap": "Activity Heatmap",
|
||||||
|
"custom": "Custom",
|
||||||
|
"usernameColor": "Username Color",
|
||||||
|
"colorType": "Color Type",
|
||||||
|
"plain": "Plain",
|
||||||
|
"gradient": "Gradient",
|
||||||
|
"colorValue": "Color Value",
|
||||||
|
"gradientDirection": "Gradient Direction",
|
||||||
|
"gradientDirectionToRight": "To Right",
|
||||||
|
"gradientDirectionToLeft": "To Left",
|
||||||
|
"gradientDirectionToBottom": "To Bottom",
|
||||||
|
"gradientDirectionToTop": "To Top",
|
||||||
|
"gradientDirectionToBottomRight": "To Bottom Right",
|
||||||
|
"gradientDirectionToBottomLeft": "To Bottom Left",
|
||||||
|
"gradientDirectionToTopRight": "To Top Right",
|
||||||
|
"gradientDirectionToTopLeft": "To Top Left",
|
||||||
|
"gradientColors": "Gradient Colors",
|
||||||
|
"color": "Color",
|
||||||
|
"addColor": "Add Color",
|
||||||
|
"preview": "Preview",
|
||||||
|
"availableWithYourPlan": "Available with your plan",
|
||||||
|
"upgradeRequired": "Upgrade required",
|
||||||
|
"settingsDisableAnimation": "Disable Animation",
|
||||||
|
"addTag": "Add Tag"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -940,7 +940,7 @@
|
|||||||
"editBot": "编辑机器人",
|
"editBot": "编辑机器人",
|
||||||
"botAutomatedBy": "由 {} 自动化",
|
"botAutomatedBy": "由 {} 自动化",
|
||||||
"botDetails": "机器人详情",
|
"botDetails": "机器人详情",
|
||||||
"overview": "总揽",
|
"overview": "总览",
|
||||||
"keys": "密钥",
|
"keys": "密钥",
|
||||||
"botNotFound": "机器人未找到。",
|
"botNotFound": "机器人未找到。",
|
||||||
"newBotKey": "新建密钥",
|
"newBotKey": "新建密钥",
|
||||||
|
|||||||
133
ios/Podfile.lock
133
ios/Podfile.lock
@@ -44,83 +44,83 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- file_saver (0.0.1):
|
- file_saver (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Firebase/CoreOnly (12.2.0):
|
- Firebase/CoreOnly (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- Firebase/Crashlytics (12.2.0):
|
- Firebase/Crashlytics (12.4.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseCrashlytics (~> 12.2.0)
|
- FirebaseCrashlytics (~> 12.4.0)
|
||||||
- Firebase/Messaging (12.2.0):
|
- Firebase/Messaging (12.4.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 12.2.0)
|
- FirebaseMessaging (~> 12.4.0)
|
||||||
- firebase_analytics (12.0.2):
|
- firebase_analytics (12.0.3):
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- FirebaseAnalytics (= 12.2.0)
|
- FirebaseAnalytics (= 12.4.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_core (4.1.1):
|
- firebase_core (4.2.0):
|
||||||
- Firebase/CoreOnly (= 12.2.0)
|
- Firebase/CoreOnly (= 12.4.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_crashlytics (5.0.2):
|
- firebase_crashlytics (5.0.3):
|
||||||
- Firebase/Crashlytics (= 12.2.0)
|
- Firebase/Crashlytics (= 12.4.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_messaging (16.0.2):
|
- firebase_messaging (16.0.3):
|
||||||
- Firebase/Messaging (= 12.2.0)
|
- Firebase/Messaging (= 12.4.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- FirebaseAnalytics (12.2.0):
|
- FirebaseAnalytics (12.4.0):
|
||||||
- FirebaseAnalytics/Default (= 12.2.0)
|
- FirebaseAnalytics/Default (= 12.4.0)
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.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.2.0):
|
- FirebaseAnalytics/Default (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.0)
|
||||||
- GoogleAppMeasurement/Default (= 12.2.0)
|
- GoogleAppMeasurement/Default (= 12.4.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.2.0):
|
- FirebaseCore (12.4.0):
|
||||||
- FirebaseCoreInternal (~> 12.2.0)
|
- FirebaseCoreInternal (~> 12.4.0)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/Logger (~> 8.1)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
- FirebaseCoreExtension (12.2.0):
|
- FirebaseCoreExtension (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseCoreInternal (12.2.0):
|
- FirebaseCoreInternal (12.4.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- FirebaseCrashlytics (12.2.0):
|
- FirebaseCrashlytics (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.0)
|
||||||
- FirebaseRemoteConfigInterop (~> 12.2.0)
|
- FirebaseRemoteConfigInterop (~> 12.4.0)
|
||||||
- FirebaseSessions (~> 12.2.0)
|
- FirebaseSessions (~> 12.4.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.2.0):
|
- FirebaseInstallations (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.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.2.0):
|
- FirebaseMessaging (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.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.2.0)
|
- FirebaseRemoteConfigInterop (12.4.0)
|
||||||
- FirebaseSessions (12.2.0):
|
- FirebaseSessions (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseCoreExtension (~> 12.2.0)
|
- FirebaseCoreExtension (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.0)
|
||||||
- GoogleDataTransport (~> 10.1)
|
- GoogleDataTransport (~> 10.1)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
@@ -157,27 +157,28 @@ PODS:
|
|||||||
- gal (1.0.0):
|
- gal (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- GoogleAdsOnDeviceConversion (2.3.0):
|
- GoogleAdsOnDeviceConversion (3.1.0):
|
||||||
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- 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.2.0):
|
- GoogleAppMeasurement/Core (12.4.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.2.0):
|
- GoogleAppMeasurement/Default (12.4.0):
|
||||||
- GoogleAdsOnDeviceConversion (= 2.3.0)
|
- GoogleAdsOnDeviceConversion (~> 3.1.0)
|
||||||
- GoogleAppMeasurement/Core (= 12.2.0)
|
- GoogleAppMeasurement/Core (= 12.4.0)
|
||||||
- GoogleAppMeasurement/IdentitySupport (= 12.2.0)
|
- GoogleAppMeasurement/IdentitySupport (= 12.4.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.2.0):
|
- GoogleAppMeasurement/IdentitySupport (12.4.0):
|
||||||
- GoogleAppMeasurement/Core (= 12.2.0)
|
- GoogleAppMeasurement/Core (= 12.4.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)
|
||||||
@@ -486,20 +487,20 @@ SPEC CHECKSUMS:
|
|||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||||
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||||
Firebase: 26f6f8d460603af3df970ad505b16b15f5e2e9a1
|
Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e
|
||||||
firebase_analytics: 8c78ce6224e0623152379d6cc7ef3d9098477b7e
|
firebase_analytics: 1d024068b1d4707d5ba7a42a12976ddf3316d835
|
||||||
firebase_core: dfc4bd142bee4bc53a5d482397ca322c2dd3165d
|
firebase_core: 744984dbbed8b3036abf34f0b98d80f130a7e464
|
||||||
firebase_crashlytics: e55dcf895eed0dd87c447dd5aff8db7f1bb8bbdb
|
firebase_crashlytics: f3a9a4338ab99b67042f64e9e22e1bf349cb44ed
|
||||||
firebase_messaging: 38c66c1184695b0c87abe51d40fc590718abed1a
|
firebase_messaging: 82c70650c426a0a14873e1acdb9ec2b443c4e8b4
|
||||||
FirebaseAnalytics: e04e23bc070e3014aa5cf4980f9df7ce5cd79ec8
|
FirebaseAnalytics: 0fc2b20091f0ddd21bf73397cf8f0eb5346dc24f
|
||||||
FirebaseCore: 311c48a147ad4a0ab7febbaed89e8025c67510cd
|
FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3
|
||||||
FirebaseCoreExtension: 73af080c22a2f7b44cefa391dc08f7e4ee162cb5
|
FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018
|
||||||
FirebaseCoreInternal: 56ea29f3dad2894f81b060f706f9d53509b6ed3b
|
FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6
|
||||||
FirebaseCrashlytics: f83cbf176d5c637ade108c0aacf1ccbd5ec499bf
|
FirebaseCrashlytics: a6ece278a837c7e88de2d9b5da0a3542f2342395
|
||||||
FirebaseInstallations: 3e884b01feabdf67582a80f3250425a00979b4ed
|
FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2
|
||||||
FirebaseMessaging: 43ec73bbfedd0c385a849bb91593ab4ad4b9e48e
|
FirebaseMessaging: d33971b7bb252745ea6cd31ab190d1a1df4b8ed5
|
||||||
FirebaseRemoteConfigInterop: 0896fd52ab72586a355c8f389ff85aaa9e5375e1
|
FirebaseRemoteConfigInterop: 1e31ec72b89c9924367c59bfb5ec9ab60d1d6766
|
||||||
FirebaseSessions: f4692789e770bec66ce17d772c0e9561c4f11737
|
FirebaseSessions: ba7c7a7ca8696a8d540eb3fe3800fbe98c79786d
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
|
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
|
||||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||||
@@ -512,8 +513,8 @@ SPEC CHECKSUMS:
|
|||||||
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
|
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
|
||||||
flutter_webrtc: c3e21fc0dcd9d8eb246ae4d5256fcbeb2f5ecd22
|
flutter_webrtc: c3e21fc0dcd9d8eb246ae4d5256fcbeb2f5ecd22
|
||||||
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
||||||
GoogleAdsOnDeviceConversion: 9090c435cde08903e8dd1ba2c77fbec9e46d9afe
|
GoogleAdsOnDeviceConversion: e03a386840803ea7eef3fd22a061930142c039c1
|
||||||
GoogleAppMeasurement: 09f341dfa8527d1612a09cbfe809a242c0b737af
|
GoogleAppMeasurement: 1e718274b7e015cefd846ac1fcf7820c70dc017d
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
||||||
|
|||||||
@@ -55,6 +55,19 @@ class ProfileLinkConverter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class UsernameColor with _$UsernameColor {
|
||||||
|
const factory UsernameColor({
|
||||||
|
@Default('plain') String type,
|
||||||
|
String? value,
|
||||||
|
String? direction,
|
||||||
|
List<String>? colors,
|
||||||
|
}) = _UsernameColor;
|
||||||
|
|
||||||
|
factory UsernameColor.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$UsernameColorFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class SnAccountProfile with _$SnAccountProfile {
|
sealed class SnAccountProfile with _$SnAccountProfile {
|
||||||
const factory SnAccountProfile({
|
const factory SnAccountProfile({
|
||||||
@@ -79,6 +92,7 @@ sealed class SnAccountProfile with _$SnAccountProfile {
|
|||||||
required SnCloudFile? picture,
|
required SnCloudFile? picture,
|
||||||
required SnCloudFile? background,
|
required SnCloudFile? background,
|
||||||
required SnVerificationMark? verification,
|
required SnVerificationMark? verification,
|
||||||
|
UsernameColor? usernameColor,
|
||||||
required DateTime createdAt,
|
required DateTime createdAt,
|
||||||
required DateTime updatedAt,
|
required DateTime updatedAt,
|
||||||
required DateTime? deletedAt,
|
required DateTime? deletedAt,
|
||||||
|
|||||||
@@ -622,10 +622,284 @@ as String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$UsernameColor {
|
||||||
|
|
||||||
|
String get type; String? get value; String? get direction; List<String>? get colors;
|
||||||
|
/// Create a copy of UsernameColor
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$UsernameColorCopyWith<UsernameColor> get copyWith => _$UsernameColorCopyWithImpl<UsernameColor>(this as UsernameColor, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this UsernameColor to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is UsernameColor&&(identical(other.type, type) || other.type == type)&&(identical(other.value, value) || other.value == value)&&(identical(other.direction, direction) || other.direction == direction)&&const DeepCollectionEquality().equals(other.colors, colors));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,type,value,direction,const DeepCollectionEquality().hash(colors));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'UsernameColor(type: $type, value: $value, direction: $direction, colors: $colors)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $UsernameColorCopyWith<$Res> {
|
||||||
|
factory $UsernameColorCopyWith(UsernameColor value, $Res Function(UsernameColor) _then) = _$UsernameColorCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String type, String? value, String? direction, List<String>? colors
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$UsernameColorCopyWithImpl<$Res>
|
||||||
|
implements $UsernameColorCopyWith<$Res> {
|
||||||
|
_$UsernameColorCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final UsernameColor _self;
|
||||||
|
final $Res Function(UsernameColor) _then;
|
||||||
|
|
||||||
|
/// Create a copy of UsernameColor
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? value = freezed,Object? direction = freezed,Object? colors = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,value: freezed == value ? _self.value : value // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,direction: freezed == direction ? _self.direction : direction // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,colors: freezed == colors ? _self.colors : colors // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<String>?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [UsernameColor].
|
||||||
|
extension UsernameColorPatterns on UsernameColor {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _UsernameColor value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _UsernameColor() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _UsernameColor value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _UsernameColor():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _UsernameColor value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _UsernameColor() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String type, String? value, String? direction, List<String>? colors)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _UsernameColor() when $default != null:
|
||||||
|
return $default(_that.type,_that.value,_that.direction,_that.colors);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String type, String? value, String? direction, List<String>? colors) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _UsernameColor():
|
||||||
|
return $default(_that.type,_that.value,_that.direction,_that.colors);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String type, String? value, String? direction, List<String>? colors)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _UsernameColor() when $default != null:
|
||||||
|
return $default(_that.type,_that.value,_that.direction,_that.colors);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _UsernameColor implements UsernameColor {
|
||||||
|
const _UsernameColor({this.type = 'plain', this.value, this.direction, final List<String>? colors}): _colors = colors;
|
||||||
|
factory _UsernameColor.fromJson(Map<String, dynamic> json) => _$UsernameColorFromJson(json);
|
||||||
|
|
||||||
|
@override@JsonKey() final String type;
|
||||||
|
@override final String? value;
|
||||||
|
@override final String? direction;
|
||||||
|
final List<String>? _colors;
|
||||||
|
@override List<String>? get colors {
|
||||||
|
final value = _colors;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_colors is EqualUnmodifiableListView) return _colors;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Create a copy of UsernameColor
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$UsernameColorCopyWith<_UsernameColor> get copyWith => __$UsernameColorCopyWithImpl<_UsernameColor>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$UsernameColorToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _UsernameColor&&(identical(other.type, type) || other.type == type)&&(identical(other.value, value) || other.value == value)&&(identical(other.direction, direction) || other.direction == direction)&&const DeepCollectionEquality().equals(other._colors, _colors));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,type,value,direction,const DeepCollectionEquality().hash(_colors));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'UsernameColor(type: $type, value: $value, direction: $direction, colors: $colors)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$UsernameColorCopyWith<$Res> implements $UsernameColorCopyWith<$Res> {
|
||||||
|
factory _$UsernameColorCopyWith(_UsernameColor value, $Res Function(_UsernameColor) _then) = __$UsernameColorCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String type, String? value, String? direction, List<String>? colors
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$UsernameColorCopyWithImpl<$Res>
|
||||||
|
implements _$UsernameColorCopyWith<$Res> {
|
||||||
|
__$UsernameColorCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _UsernameColor _self;
|
||||||
|
final $Res Function(_UsernameColor) _then;
|
||||||
|
|
||||||
|
/// Create a copy of UsernameColor
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? value = freezed,Object? direction = freezed,Object? colors = freezed,}) {
|
||||||
|
return _then(_UsernameColor(
|
||||||
|
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,value: freezed == value ? _self.value : value // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,direction: freezed == direction ? _self.direction : direction // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,colors: freezed == colors ? _self._colors : colors // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<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 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;
|
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; UsernameColor? get usernameColor; 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)
|
||||||
@@ -638,16 +912,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.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));
|
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.usernameColor, usernameColor) || other.usernameColor == usernameColor)&&(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,socialCredits,socialCreditsLevel,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,usernameColor,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, socialCredits: $socialCredits, socialCreditsLevel: $socialCreditsLevel, 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, usernameColor: $usernameColor, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -658,11 +932,11 @@ 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 socialCredits, int socialCreditsLevel, 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, UsernameColor? usernameColor, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$SnAccountBadgeCopyWith<$Res>? get activeBadge;$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnVerificationMarkCopyWith<$Res>? get verification;
|
$SnAccountBadgeCopyWith<$Res>? get activeBadge;$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnVerificationMarkCopyWith<$Res>? get verification;$UsernameColorCopyWith<$Res>? get usernameColor;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -675,7 +949,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? 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,}) {
|
@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? usernameColor = 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
|
||||||
@@ -698,7 +972,8 @@ as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : lev
|
|||||||
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
|
||||||
as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
|
as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
|
||||||
as SnVerificationMark?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
as SnVerificationMark?,usernameColor: freezed == usernameColor ? _self.usernameColor : usernameColor // ignore: cast_nullable_to_non_nullable
|
||||||
|
as UsernameColor?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,
|
as DateTime?,
|
||||||
@@ -752,6 +1027,18 @@ $SnVerificationMarkCopyWith<$Res>? get verification {
|
|||||||
return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
|
return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
|
||||||
return _then(_self.copyWith(verification: value));
|
return _then(_self.copyWith(verification: value));
|
||||||
});
|
});
|
||||||
|
}/// Create a copy of SnAccountProfile
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$UsernameColorCopyWith<$Res>? get usernameColor {
|
||||||
|
if (_self.usernameColor == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $UsernameColorCopyWith<$Res>(_self.usernameColor!, (value) {
|
||||||
|
return _then(_self.copyWith(usernameColor: value));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -831,10 +1118,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 socialCredits, int socialCreditsLevel, 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, UsernameColor? usernameColor, 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.socialCredits,_that.socialCreditsLevel,_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.usernameColor,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -852,10 +1139,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 socialCredits, int socialCreditsLevel, 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, UsernameColor? usernameColor, 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.socialCredits,_that.socialCreditsLevel,_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.usernameColor,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -869,10 +1156,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 socialCredits, int socialCreditsLevel, 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, UsernameColor? usernameColor, 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.socialCredits,_that.socialCreditsLevel,_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.usernameColor,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -884,7 +1171,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, 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;
|
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, this.usernameColor, 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;
|
||||||
@@ -914,6 +1201,7 @@ class _SnAccountProfile implements SnAccountProfile {
|
|||||||
@override final SnCloudFile? picture;
|
@override final SnCloudFile? picture;
|
||||||
@override final SnCloudFile? background;
|
@override final SnCloudFile? background;
|
||||||
@override final SnVerificationMark? verification;
|
@override final SnVerificationMark? verification;
|
||||||
|
@override final UsernameColor? usernameColor;
|
||||||
@override final DateTime createdAt;
|
@override final DateTime createdAt;
|
||||||
@override final DateTime updatedAt;
|
@override final DateTime updatedAt;
|
||||||
@override final DateTime? deletedAt;
|
@override final DateTime? deletedAt;
|
||||||
@@ -931,16 +1219,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.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));
|
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.usernameColor, usernameColor) || other.usernameColor == usernameColor)&&(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,socialCredits,socialCreditsLevel,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,usernameColor,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, socialCredits: $socialCredits, socialCreditsLevel: $socialCreditsLevel, 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, usernameColor: $usernameColor, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -951,11 +1239,11 @@ 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 socialCredits, int socialCreditsLevel, 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, UsernameColor? usernameColor, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@override $SnAccountBadgeCopyWith<$Res>? get activeBadge;@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnVerificationMarkCopyWith<$Res>? get verification;
|
@override $SnAccountBadgeCopyWith<$Res>? get activeBadge;@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnVerificationMarkCopyWith<$Res>? get verification;@override $UsernameColorCopyWith<$Res>? get usernameColor;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -968,7 +1256,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? 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,}) {
|
@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? usernameColor = 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
|
||||||
@@ -991,7 +1279,8 @@ as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : lev
|
|||||||
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
|
||||||
as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
|
as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
|
||||||
as SnVerificationMark?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
as SnVerificationMark?,usernameColor: freezed == usernameColor ? _self.usernameColor : usernameColor // ignore: cast_nullable_to_non_nullable
|
||||||
|
as UsernameColor?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,
|
as DateTime?,
|
||||||
@@ -1046,6 +1335,18 @@ $SnVerificationMarkCopyWith<$Res>? get verification {
|
|||||||
return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
|
return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
|
||||||
return _then(_self.copyWith(verification: value));
|
return _then(_self.copyWith(verification: value));
|
||||||
});
|
});
|
||||||
|
}/// Create a copy of SnAccountProfile
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$UsernameColorCopyWith<$Res>? get usernameColor {
|
||||||
|
if (_self.usernameColor == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $UsernameColorCopyWith<$Res>(_self.usernameColor!, (value) {
|
||||||
|
return _then(_self.copyWith(usernameColor: value));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,23 @@ _ProfileLink _$ProfileLinkFromJson(Map<String, dynamic> json) =>
|
|||||||
Map<String, dynamic> _$ProfileLinkToJson(_ProfileLink instance) =>
|
Map<String, dynamic> _$ProfileLinkToJson(_ProfileLink instance) =>
|
||||||
<String, dynamic>{'name': instance.name, 'url': instance.url};
|
<String, dynamic>{'name': instance.name, 'url': instance.url};
|
||||||
|
|
||||||
|
_UsernameColor _$UsernameColorFromJson(Map<String, dynamic> json) =>
|
||||||
|
_UsernameColor(
|
||||||
|
type: json['type'] as String? ?? 'plain',
|
||||||
|
value: json['value'] as String?,
|
||||||
|
direction: json['direction'] as String?,
|
||||||
|
colors:
|
||||||
|
(json['colors'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$UsernameColorToJson(_UsernameColor instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'type': instance.type,
|
||||||
|
'value': instance.value,
|
||||||
|
'direction': instance.direction,
|
||||||
|
'colors': instance.colors,
|
||||||
|
};
|
||||||
|
|
||||||
_SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
|
_SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
|
||||||
_SnAccountProfile(
|
_SnAccountProfile(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
@@ -113,6 +130,12 @@ _SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
|
|||||||
: SnVerificationMark.fromJson(
|
: SnVerificationMark.fromJson(
|
||||||
json['verification'] as Map<String, dynamic>,
|
json['verification'] as Map<String, dynamic>,
|
||||||
),
|
),
|
||||||
|
usernameColor:
|
||||||
|
json['username_color'] == null
|
||||||
|
? null
|
||||||
|
: UsernameColor.fromJson(
|
||||||
|
json['username_color'] as Map<String, dynamic>,
|
||||||
|
),
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
deletedAt:
|
deletedAt:
|
||||||
@@ -144,6 +167,7 @@ Map<String, dynamic> _$SnAccountProfileToJson(_SnAccountProfile instance) =>
|
|||||||
'picture': instance.picture?.toJson(),
|
'picture': instance.picture?.toJson(),
|
||||||
'background': instance.background?.toJson(),
|
'background': instance.background?.toJson(),
|
||||||
'verification': instance.verification?.toJson(),
|
'verification': instance.verification?.toJson(),
|
||||||
|
'username_color': instance.usernameColor?.toJson(),
|
||||||
'created_at': instance.createdAt.toIso8601String(),
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
'updated_at': instance.updatedAt.toIso8601String(),
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
|||||||
16
lib/models/autocomplete_response.dart
Normal file
16
lib/models/autocomplete_response.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'autocomplete_response.freezed.dart';
|
||||||
|
part 'autocomplete_response.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class AutocompleteSuggestion with _$AutocompleteSuggestion {
|
||||||
|
const factory AutocompleteSuggestion({
|
||||||
|
required String type,
|
||||||
|
required String keyword,
|
||||||
|
required dynamic data,
|
||||||
|
}) = _AutocompleteSuggestion;
|
||||||
|
|
||||||
|
factory AutocompleteSuggestion.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AutocompleteSuggestionFromJson(json);
|
||||||
|
}
|
||||||
277
lib/models/autocomplete_response.freezed.dart
Normal file
277
lib/models/autocomplete_response.freezed.dart
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// coverage:ignore-file
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'autocomplete_response.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// dart format off
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$AutocompleteSuggestion {
|
||||||
|
|
||||||
|
String get type; String get keyword; dynamic get data;
|
||||||
|
/// Create a copy of AutocompleteSuggestion
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$AutocompleteSuggestionCopyWith<AutocompleteSuggestion> get copyWith => _$AutocompleteSuggestionCopyWithImpl<AutocompleteSuggestion>(this as AutocompleteSuggestion, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this AutocompleteSuggestion to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is AutocompleteSuggestion&&(identical(other.type, type) || other.type == type)&&(identical(other.keyword, keyword) || other.keyword == keyword)&&const DeepCollectionEquality().equals(other.data, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,type,keyword,const DeepCollectionEquality().hash(data));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AutocompleteSuggestion(type: $type, keyword: $keyword, data: $data)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $AutocompleteSuggestionCopyWith<$Res> {
|
||||||
|
factory $AutocompleteSuggestionCopyWith(AutocompleteSuggestion value, $Res Function(AutocompleteSuggestion) _then) = _$AutocompleteSuggestionCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String type, String keyword, dynamic data
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$AutocompleteSuggestionCopyWithImpl<$Res>
|
||||||
|
implements $AutocompleteSuggestionCopyWith<$Res> {
|
||||||
|
_$AutocompleteSuggestionCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final AutocompleteSuggestion _self;
|
||||||
|
final $Res Function(AutocompleteSuggestion) _then;
|
||||||
|
|
||||||
|
/// Create a copy of AutocompleteSuggestion
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? keyword = null,Object? data = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,keyword: null == keyword ? _self.keyword : keyword // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
|
||||||
|
as dynamic,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [AutocompleteSuggestion].
|
||||||
|
extension AutocompleteSuggestionPatterns on AutocompleteSuggestion {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AutocompleteSuggestion value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _AutocompleteSuggestion() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AutocompleteSuggestion value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _AutocompleteSuggestion():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AutocompleteSuggestion value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _AutocompleteSuggestion() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String type, String keyword, dynamic data)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _AutocompleteSuggestion() when $default != null:
|
||||||
|
return $default(_that.type,_that.keyword,_that.data);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String type, String keyword, dynamic data) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _AutocompleteSuggestion():
|
||||||
|
return $default(_that.type,_that.keyword,_that.data);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String type, String keyword, dynamic data)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _AutocompleteSuggestion() when $default != null:
|
||||||
|
return $default(_that.type,_that.keyword,_that.data);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _AutocompleteSuggestion implements AutocompleteSuggestion {
|
||||||
|
const _AutocompleteSuggestion({required this.type, required this.keyword, required this.data});
|
||||||
|
factory _AutocompleteSuggestion.fromJson(Map<String, dynamic> json) => _$AutocompleteSuggestionFromJson(json);
|
||||||
|
|
||||||
|
@override final String type;
|
||||||
|
@override final String keyword;
|
||||||
|
@override final dynamic data;
|
||||||
|
|
||||||
|
/// Create a copy of AutocompleteSuggestion
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$AutocompleteSuggestionCopyWith<_AutocompleteSuggestion> get copyWith => __$AutocompleteSuggestionCopyWithImpl<_AutocompleteSuggestion>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$AutocompleteSuggestionToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AutocompleteSuggestion&&(identical(other.type, type) || other.type == type)&&(identical(other.keyword, keyword) || other.keyword == keyword)&&const DeepCollectionEquality().equals(other.data, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,type,keyword,const DeepCollectionEquality().hash(data));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AutocompleteSuggestion(type: $type, keyword: $keyword, data: $data)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$AutocompleteSuggestionCopyWith<$Res> implements $AutocompleteSuggestionCopyWith<$Res> {
|
||||||
|
factory _$AutocompleteSuggestionCopyWith(_AutocompleteSuggestion value, $Res Function(_AutocompleteSuggestion) _then) = __$AutocompleteSuggestionCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String type, String keyword, dynamic data
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$AutocompleteSuggestionCopyWithImpl<$Res>
|
||||||
|
implements _$AutocompleteSuggestionCopyWith<$Res> {
|
||||||
|
__$AutocompleteSuggestionCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _AutocompleteSuggestion _self;
|
||||||
|
final $Res Function(_AutocompleteSuggestion) _then;
|
||||||
|
|
||||||
|
/// Create a copy of AutocompleteSuggestion
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? keyword = null,Object? data = freezed,}) {
|
||||||
|
return _then(_AutocompleteSuggestion(
|
||||||
|
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,keyword: null == keyword ? _self.keyword : keyword // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
|
||||||
|
as dynamic,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// dart format on
|
||||||
23
lib/models/autocomplete_response.g.dart
Normal file
23
lib/models/autocomplete_response.g.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'autocomplete_response.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_AutocompleteSuggestion _$AutocompleteSuggestionFromJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
|
) => _AutocompleteSuggestion(
|
||||||
|
type: json['type'] as String,
|
||||||
|
keyword: json['keyword'] as String,
|
||||||
|
data: json['data'],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$AutocompleteSuggestionToJson(
|
||||||
|
_AutocompleteSuggestion instance,
|
||||||
|
) => <String, dynamic>{
|
||||||
|
'type': instance.type,
|
||||||
|
'keyword': instance.keyword,
|
||||||
|
'data': instance.data,
|
||||||
|
};
|
||||||
26
lib/models/heatmap.dart
Normal file
26
lib/models/heatmap.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'heatmap.freezed.dart';
|
||||||
|
part 'heatmap.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnHeatmap with _$SnHeatmap {
|
||||||
|
const factory SnHeatmap({
|
||||||
|
required String unit,
|
||||||
|
@JsonKey(name: 'period_start') required DateTime periodStart,
|
||||||
|
@JsonKey(name: 'period_end') required DateTime periodEnd,
|
||||||
|
required List<SnHeatmapItem> items,
|
||||||
|
}) = _SnHeatmap;
|
||||||
|
|
||||||
|
factory SnHeatmap.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnHeatmapFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnHeatmapItem with _$SnHeatmapItem {
|
||||||
|
const factory SnHeatmapItem({required DateTime date, required int count}) =
|
||||||
|
_SnHeatmapItem;
|
||||||
|
|
||||||
|
factory SnHeatmapItem.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnHeatmapItemFromJson(json);
|
||||||
|
}
|
||||||
546
lib/models/heatmap.freezed.dart
Normal file
546
lib/models/heatmap.freezed.dart
Normal file
@@ -0,0 +1,546 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// coverage:ignore-file
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'heatmap.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// dart format off
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnHeatmap {
|
||||||
|
|
||||||
|
String get unit;@JsonKey(name: 'period_start') DateTime get periodStart;@JsonKey(name: 'period_end') DateTime get periodEnd; List<SnHeatmapItem> get items;
|
||||||
|
/// Create a copy of SnHeatmap
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnHeatmapCopyWith<SnHeatmap> get copyWith => _$SnHeatmapCopyWithImpl<SnHeatmap>(this as SnHeatmap, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnHeatmap to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnHeatmap&&(identical(other.unit, unit) || other.unit == unit)&&(identical(other.periodStart, periodStart) || other.periodStart == periodStart)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&const DeepCollectionEquality().equals(other.items, items));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,unit,periodStart,periodEnd,const DeepCollectionEquality().hash(items));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnHeatmap(unit: $unit, periodStart: $periodStart, periodEnd: $periodEnd, items: $items)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnHeatmapCopyWith<$Res> {
|
||||||
|
factory $SnHeatmapCopyWith(SnHeatmap value, $Res Function(SnHeatmap) _then) = _$SnHeatmapCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String unit,@JsonKey(name: 'period_start') DateTime periodStart,@JsonKey(name: 'period_end') DateTime periodEnd, List<SnHeatmapItem> items
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnHeatmapCopyWithImpl<$Res>
|
||||||
|
implements $SnHeatmapCopyWith<$Res> {
|
||||||
|
_$SnHeatmapCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnHeatmap _self;
|
||||||
|
final $Res Function(SnHeatmap) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnHeatmap
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? unit = null,Object? periodStart = null,Object? periodEnd = null,Object? items = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
unit: null == unit ? _self.unit : unit // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,periodStart: null == periodStart ? _self.periodStart : periodStart // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,periodEnd: null == periodEnd ? _self.periodEnd : periodEnd // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnHeatmapItem>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnHeatmap].
|
||||||
|
extension SnHeatmapPatterns on SnHeatmap {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnHeatmap value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmap() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnHeatmap value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmap():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnHeatmap value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmap() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String unit, @JsonKey(name: 'period_start') DateTime periodStart, @JsonKey(name: 'period_end') DateTime periodEnd, List<SnHeatmapItem> items)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmap() when $default != null:
|
||||||
|
return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String unit, @JsonKey(name: 'period_start') DateTime periodStart, @JsonKey(name: 'period_end') DateTime periodEnd, List<SnHeatmapItem> items) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmap():
|
||||||
|
return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String unit, @JsonKey(name: 'period_start') DateTime periodStart, @JsonKey(name: 'period_end') DateTime periodEnd, List<SnHeatmapItem> items)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmap() when $default != null:
|
||||||
|
return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnHeatmap implements SnHeatmap {
|
||||||
|
const _SnHeatmap({required this.unit, @JsonKey(name: 'period_start') required this.periodStart, @JsonKey(name: 'period_end') required this.periodEnd, required final List<SnHeatmapItem> items}): _items = items;
|
||||||
|
factory _SnHeatmap.fromJson(Map<String, dynamic> json) => _$SnHeatmapFromJson(json);
|
||||||
|
|
||||||
|
@override final String unit;
|
||||||
|
@override@JsonKey(name: 'period_start') final DateTime periodStart;
|
||||||
|
@override@JsonKey(name: 'period_end') final DateTime periodEnd;
|
||||||
|
final List<SnHeatmapItem> _items;
|
||||||
|
@override List<SnHeatmapItem> get items {
|
||||||
|
if (_items is EqualUnmodifiableListView) return _items;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_items);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Create a copy of SnHeatmap
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnHeatmapCopyWith<_SnHeatmap> get copyWith => __$SnHeatmapCopyWithImpl<_SnHeatmap>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnHeatmapToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnHeatmap&&(identical(other.unit, unit) || other.unit == unit)&&(identical(other.periodStart, periodStart) || other.periodStart == periodStart)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&const DeepCollectionEquality().equals(other._items, _items));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,unit,periodStart,periodEnd,const DeepCollectionEquality().hash(_items));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnHeatmap(unit: $unit, periodStart: $periodStart, periodEnd: $periodEnd, items: $items)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnHeatmapCopyWith<$Res> implements $SnHeatmapCopyWith<$Res> {
|
||||||
|
factory _$SnHeatmapCopyWith(_SnHeatmap value, $Res Function(_SnHeatmap) _then) = __$SnHeatmapCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String unit,@JsonKey(name: 'period_start') DateTime periodStart,@JsonKey(name: 'period_end') DateTime periodEnd, List<SnHeatmapItem> items
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnHeatmapCopyWithImpl<$Res>
|
||||||
|
implements _$SnHeatmapCopyWith<$Res> {
|
||||||
|
__$SnHeatmapCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnHeatmap _self;
|
||||||
|
final $Res Function(_SnHeatmap) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnHeatmap
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? unit = null,Object? periodStart = null,Object? periodEnd = null,Object? items = null,}) {
|
||||||
|
return _then(_SnHeatmap(
|
||||||
|
unit: null == unit ? _self.unit : unit // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,periodStart: null == periodStart ? _self.periodStart : periodStart // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,periodEnd: null == periodEnd ? _self.periodEnd : periodEnd // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnHeatmapItem>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnHeatmapItem {
|
||||||
|
|
||||||
|
DateTime get date; int get count;
|
||||||
|
/// Create a copy of SnHeatmapItem
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnHeatmapItemCopyWith<SnHeatmapItem> get copyWith => _$SnHeatmapItemCopyWithImpl<SnHeatmapItem>(this as SnHeatmapItem, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnHeatmapItem to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnHeatmapItem&&(identical(other.date, date) || other.date == date)&&(identical(other.count, count) || other.count == count));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,date,count);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnHeatmapItem(date: $date, count: $count)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnHeatmapItemCopyWith<$Res> {
|
||||||
|
factory $SnHeatmapItemCopyWith(SnHeatmapItem value, $Res Function(SnHeatmapItem) _then) = _$SnHeatmapItemCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
DateTime date, int count
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnHeatmapItemCopyWithImpl<$Res>
|
||||||
|
implements $SnHeatmapItemCopyWith<$Res> {
|
||||||
|
_$SnHeatmapItemCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnHeatmapItem _self;
|
||||||
|
final $Res Function(SnHeatmapItem) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnHeatmapItem
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? date = null,Object? count = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,count: null == count ? _self.count : count // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnHeatmapItem].
|
||||||
|
extension SnHeatmapItemPatterns on SnHeatmapItem {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnHeatmapItem value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmapItem() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnHeatmapItem value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmapItem():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnHeatmapItem value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmapItem() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( DateTime date, int count)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmapItem() when $default != null:
|
||||||
|
return $default(_that.date,_that.count);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime date, int count) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmapItem():
|
||||||
|
return $default(_that.date,_that.count);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( DateTime date, int count)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnHeatmapItem() when $default != null:
|
||||||
|
return $default(_that.date,_that.count);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnHeatmapItem implements SnHeatmapItem {
|
||||||
|
const _SnHeatmapItem({required this.date, required this.count});
|
||||||
|
factory _SnHeatmapItem.fromJson(Map<String, dynamic> json) => _$SnHeatmapItemFromJson(json);
|
||||||
|
|
||||||
|
@override final DateTime date;
|
||||||
|
@override final int count;
|
||||||
|
|
||||||
|
/// Create a copy of SnHeatmapItem
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnHeatmapItemCopyWith<_SnHeatmapItem> get copyWith => __$SnHeatmapItemCopyWithImpl<_SnHeatmapItem>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnHeatmapItemToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnHeatmapItem&&(identical(other.date, date) || other.date == date)&&(identical(other.count, count) || other.count == count));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,date,count);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnHeatmapItem(date: $date, count: $count)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnHeatmapItemCopyWith<$Res> implements $SnHeatmapItemCopyWith<$Res> {
|
||||||
|
factory _$SnHeatmapItemCopyWith(_SnHeatmapItem value, $Res Function(_SnHeatmapItem) _then) = __$SnHeatmapItemCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
DateTime date, int count
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnHeatmapItemCopyWithImpl<$Res>
|
||||||
|
implements _$SnHeatmapItemCopyWith<$Res> {
|
||||||
|
__$SnHeatmapItemCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnHeatmapItem _self;
|
||||||
|
final $Res Function(_SnHeatmapItem) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnHeatmapItem
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? date = null,Object? count = null,}) {
|
||||||
|
return _then(_SnHeatmapItem(
|
||||||
|
date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,count: null == count ? _self.count : count // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// dart format on
|
||||||
37
lib/models/heatmap.g.dart
Normal file
37
lib/models/heatmap.g.dart
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'heatmap.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_SnHeatmap _$SnHeatmapFromJson(Map<String, dynamic> json) => _SnHeatmap(
|
||||||
|
unit: json['unit'] as String,
|
||||||
|
periodStart: DateTime.parse(json['period_start'] as String),
|
||||||
|
periodEnd: DateTime.parse(json['period_end'] as String),
|
||||||
|
items:
|
||||||
|
(json['items'] as List<dynamic>)
|
||||||
|
.map((e) => SnHeatmapItem.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnHeatmapToJson(_SnHeatmap instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'unit': instance.unit,
|
||||||
|
'period_start': instance.periodStart.toIso8601String(),
|
||||||
|
'period_end': instance.periodEnd.toIso8601String(),
|
||||||
|
'items': instance.items.map((e) => e.toJson()).toList(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_SnHeatmapItem _$SnHeatmapItemFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnHeatmapItem(
|
||||||
|
date: DateTime.parse(json['date'] as String),
|
||||||
|
count: (json['count'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnHeatmapItemToJson(_SnHeatmapItem instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'date': instance.date.toIso8601String(),
|
||||||
|
'count': instance.count,
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
import 'package:island/models/post_category.dart';
|
import 'package:island/models/post_category.dart';
|
||||||
import 'package:island/models/post_tag.dart';
|
import 'package:island/models/post_tag.dart';
|
||||||
@@ -142,3 +143,21 @@ sealed class SnPostAward with _$SnPostAward {
|
|||||||
factory SnPostAward.fromJson(Map<String, dynamic> json) =>
|
factory SnPostAward.fromJson(Map<String, dynamic> json) =>
|
||||||
_$SnPostAwardFromJson(json);
|
_$SnPostAwardFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnPostReaction with _$SnPostReaction {
|
||||||
|
const factory SnPostReaction({
|
||||||
|
required String id,
|
||||||
|
required String symbol,
|
||||||
|
required int attitude,
|
||||||
|
required String postId,
|
||||||
|
required String accountId,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
@Default(null) SnAccount? account,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
}) = _SnPostReaction;
|
||||||
|
|
||||||
|
factory SnPostReaction.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnPostReactionFromJson(json);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1900,4 +1900,309 @@ as DateTime?,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnPostReaction {
|
||||||
|
|
||||||
|
String get id; String get symbol; int get attitude; String get postId; String get accountId; DateTime get createdAt; DateTime get updatedAt; SnAccount? get account; DateTime? get deletedAt;
|
||||||
|
/// Create a copy of SnPostReaction
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPostReactionCopyWith<SnPostReaction> get copyWith => _$SnPostReactionCopyWithImpl<SnPostReaction>(this as SnPostReaction, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnPostReaction to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostReaction&&(identical(other.id, id) || other.id == id)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude)&&(identical(other.postId, postId) || other.postId == postId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.account, account) || other.account == account)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,symbol,attitude,postId,accountId,createdAt,updatedAt,account,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnPostReaction(id: $id, symbol: $symbol, attitude: $attitude, postId: $postId, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, account: $account, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnPostReactionCopyWith<$Res> {
|
||||||
|
factory $SnPostReactionCopyWith(SnPostReaction value, $Res Function(SnPostReaction) _then) = _$SnPostReactionCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$SnAccountCopyWith<$Res>? get account;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnPostReactionCopyWithImpl<$Res>
|
||||||
|
implements $SnPostReactionCopyWith<$Res> {
|
||||||
|
_$SnPostReactionCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnPostReaction _self;
|
||||||
|
final $Res Function(SnPostReaction) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnPostReaction
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? symbol = null,Object? attitude = null,Object? postId = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? account = freezed,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,symbol: null == symbol ? _self.symbol : symbol // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,attitude: null == attitude ? _self.attitude : attitude // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
/// Create a copy of SnPostReaction
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get account {
|
||||||
|
if (_self.account == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
|
||||||
|
return _then(_self.copyWith(account: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnPostReaction].
|
||||||
|
extension SnPostReactionPatterns on SnPostReaction {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnPostReaction value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostReaction() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnPostReaction value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostReaction():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnPostReaction value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostReaction() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostReaction() when $default != null:
|
||||||
|
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_that.deletedAt);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostReaction():
|
||||||
|
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_that.deletedAt);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostReaction() when $default != null:
|
||||||
|
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_that.deletedAt);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnPostReaction implements SnPostReaction {
|
||||||
|
const _SnPostReaction({required this.id, required this.symbol, required this.attitude, required this.postId, required this.accountId, required this.createdAt, required this.updatedAt, this.account = null, this.deletedAt});
|
||||||
|
factory _SnPostReaction.fromJson(Map<String, dynamic> json) => _$SnPostReactionFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final String symbol;
|
||||||
|
@override final int attitude;
|
||||||
|
@override final String postId;
|
||||||
|
@override final String accountId;
|
||||||
|
@override final DateTime createdAt;
|
||||||
|
@override final DateTime updatedAt;
|
||||||
|
@override@JsonKey() final SnAccount? account;
|
||||||
|
@override final DateTime? deletedAt;
|
||||||
|
|
||||||
|
/// Create a copy of SnPostReaction
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnPostReactionCopyWith<_SnPostReaction> get copyWith => __$SnPostReactionCopyWithImpl<_SnPostReaction>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnPostReactionToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostReaction&&(identical(other.id, id) || other.id == id)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude)&&(identical(other.postId, postId) || other.postId == postId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.account, account) || other.account == account)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,symbol,attitude,postId,accountId,createdAt,updatedAt,account,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnPostReaction(id: $id, symbol: $symbol, attitude: $attitude, postId: $postId, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, account: $account, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnPostReactionCopyWith<$Res> implements $SnPostReactionCopyWith<$Res> {
|
||||||
|
factory _$SnPostReactionCopyWith(_SnPostReaction value, $Res Function(_SnPostReaction) _then) = __$SnPostReactionCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@override $SnAccountCopyWith<$Res>? get account;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnPostReactionCopyWithImpl<$Res>
|
||||||
|
implements _$SnPostReactionCopyWith<$Res> {
|
||||||
|
__$SnPostReactionCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnPostReaction _self;
|
||||||
|
final $Res Function(_SnPostReaction) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnPostReaction
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? symbol = null,Object? attitude = null,Object? postId = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? account = freezed,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_SnPostReaction(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,symbol: null == symbol ? _self.symbol : symbol // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,attitude: null == attitude ? _self.attitude : attitude // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPostReaction
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get account {
|
||||||
|
if (_self.account == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
|
||||||
|
return _then(_self.copyWith(account: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// dart format on
|
// dart format on
|
||||||
|
|||||||
@@ -230,3 +230,35 @@ Map<String, dynamic> _$SnPostAwardToJson(_SnPostAward instance) =>
|
|||||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_SnPostReaction _$SnPostReactionFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnPostReaction(
|
||||||
|
id: json['id'] as String,
|
||||||
|
symbol: json['symbol'] as String,
|
||||||
|
attitude: (json['attitude'] as num).toInt(),
|
||||||
|
postId: json['post_id'] as String,
|
||||||
|
accountId: json['account_id'] as String,
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
account:
|
||||||
|
json['account'] == null
|
||||||
|
? null
|
||||||
|
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
deletedAt:
|
||||||
|
json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnPostReactionToJson(_SnPostReaction instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'symbol': instance.symbol,
|
||||||
|
'attitude': instance.attitude,
|
||||||
|
'post_id': instance.postId,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'account': instance.account?.toJson(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
|||||||
@@ -320,16 +320,6 @@ class MessagesNotifier extends _$MessagesNotifier {
|
|||||||
final response = MessageSyncResponse.fromJson(resp.data);
|
final response = MessageSyncResponse.fromJson(resp.data);
|
||||||
talker.log('Sync response: ${response.messages.length} changes');
|
talker.log('Sync response: ${response.messages.length} changes');
|
||||||
for (final message in response.messages) {
|
for (final message in response.messages) {
|
||||||
switch (message.type) {
|
|
||||||
case "messages.update":
|
|
||||||
case "messages.update.links":
|
|
||||||
await receiveMessageUpdate(message);
|
|
||||||
break;
|
|
||||||
case "messages.delete":
|
|
||||||
await receiveMessageDeletion(message.id.toString());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Still need receive the message to show the history actions
|
|
||||||
await receiveMessage(message);
|
await receiveMessage(message);
|
||||||
}
|
}
|
||||||
} catch (err, stackTrace) {
|
} catch (err, stackTrace) {
|
||||||
@@ -705,6 +695,8 @@ class MessagesNotifier extends _$MessagesNotifier {
|
|||||||
remoteMessage.copyWith(
|
remoteMessage.copyWith(
|
||||||
id: targetId,
|
id: targetId,
|
||||||
meta: Map.of(remoteMessage.meta)..remove('message_id'),
|
meta: Map.of(remoteMessage.meta)..remove('message_id'),
|
||||||
|
type: 'text',
|
||||||
|
editedAt: remoteMessage.createdAt,
|
||||||
),
|
),
|
||||||
MessageStatus.sent,
|
MessageStatus.sent,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ part of 'messages_notifier.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$messagesNotifierHash() => r'639286fd8e4e0cfdef5be6cf5d80eea769007cb3';
|
String _$messagesNotifierHash() => r'6adefd9152cdd686c2a863964993f24c42d405b5';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const kAppDefaultPoolId = 'app_default_pool_id';
|
|||||||
const kAppMessageDisplayStyle = 'app_message_display_style';
|
const kAppMessageDisplayStyle = 'app_message_display_style';
|
||||||
const kAppThemeMode = 'app_theme_mode';
|
const kAppThemeMode = 'app_theme_mode';
|
||||||
const kMaterialYouToggleStoreKey = 'app_theme_material_you';
|
const kMaterialYouToggleStoreKey = 'app_theme_material_you';
|
||||||
|
const kAppDisableAnimation = 'app_disable_animation';
|
||||||
const kFeaturedPostsCollapsedId =
|
const kFeaturedPostsCollapsedId =
|
||||||
'featured_posts_collapsed_id'; // Key for storing the ID of the collapsed featured post
|
'featured_posts_collapsed_id'; // Key for storing the ID of the collapsed featured post
|
||||||
|
|
||||||
@@ -96,6 +97,7 @@ sealed class AppSettings with _$AppSettings {
|
|||||||
required String messageDisplayStyle,
|
required String messageDisplayStyle,
|
||||||
required String? themeMode,
|
required String? themeMode,
|
||||||
required bool useMaterial3,
|
required bool useMaterial3,
|
||||||
|
required bool disableAnimation,
|
||||||
}) = _AppSettings;
|
}) = _AppSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +124,7 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
|
|||||||
messageDisplayStyle: prefs.getString(kAppMessageDisplayStyle) ?? 'bubble',
|
messageDisplayStyle: prefs.getString(kAppMessageDisplayStyle) ?? 'bubble',
|
||||||
themeMode: prefs.getString(kAppThemeMode) ?? 'system',
|
themeMode: prefs.getString(kAppThemeMode) ?? 'system',
|
||||||
useMaterial3: prefs.getBool(kMaterialYouToggleStoreKey) ?? true,
|
useMaterial3: prefs.getBool(kMaterialYouToggleStoreKey) ?? true,
|
||||||
|
disableAnimation: prefs.getBool(kAppDisableAnimation) ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +276,12 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
|
|||||||
}
|
}
|
||||||
state = state.copyWith(customColors: value);
|
state = state.copyWith(customColors: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setDisableAnimation(bool value) {
|
||||||
|
final prefs = ref.read(sharedPreferencesProvider);
|
||||||
|
prefs.setBool(kAppDisableAnimation, value);
|
||||||
|
state = state.copyWith(disableAnimation: value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final updateInfoProvider =
|
final updateInfoProvider =
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ mixin _$AppSettings {
|
|||||||
ThemeColors? get customColors; Size? get windowSize;// The window size for desktop platforms
|
ThemeColors? get customColors; Size? get windowSize;// The window size for desktop platforms
|
||||||
double get windowOpacity;// The window opacity for desktop platforms
|
double get windowOpacity;// The window opacity for desktop platforms
|
||||||
double get cardTransparency;// The card background opacity
|
double get cardTransparency;// The card background opacity
|
||||||
String? get defaultPoolId; String get messageDisplayStyle; String? get themeMode; bool get useMaterial3;
|
String? get defaultPoolId; String get messageDisplayStyle; String? get themeMode; bool get useMaterial3; bool get disableAnimation;
|
||||||
/// Create a copy of AppSettings
|
/// Create a copy of AppSettings
|
||||||
/// 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)
|
||||||
@@ -301,16 +301,16 @@ $AppSettingsCopyWith<AppSettings> get copyWith => _$AppSettingsCopyWithImpl<AppS
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.useMaterial3, useMaterial3) || other.useMaterial3 == useMaterial3));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.useMaterial3, useMaterial3) || other.useMaterial3 == useMaterial3)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,useMaterial3);
|
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,useMaterial3,disableAnimation);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, useMaterial3: $useMaterial3)';
|
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, useMaterial3: $useMaterial3, disableAnimation: $disableAnimation)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ abstract mixin class $AppSettingsCopyWith<$Res> {
|
|||||||
factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl;
|
factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3
|
bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -338,7 +338,7 @@ class _$AppSettingsCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of AppSettings
|
/// Create a copy of AppSettings
|
||||||
/// 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? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? useMaterial3 = null,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? useMaterial3 = null,Object? disableAnimation = null,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
|
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
|
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -357,6 +357,7 @@ as double,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaul
|
|||||||
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
|
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
|
||||||
as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
|
as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,useMaterial3: null == useMaterial3 ? _self.useMaterial3 : useMaterial3 // ignore: cast_nullable_to_non_nullable
|
as String?,useMaterial3: null == useMaterial3 ? _self.useMaterial3 : useMaterial3 // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,disableAnimation: null == disableAnimation ? _self.disableAnimation : disableAnimation // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -451,10 +452,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _AppSettings() when $default != null:
|
case _AppSettings() when $default != null:
|
||||||
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3);case _:
|
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3,_that.disableAnimation);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -472,10 +473,10 @@ return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_tha
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _AppSettings():
|
case _AppSettings():
|
||||||
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3);}
|
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3,_that.disableAnimation);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -489,10 +490,10 @@ return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_tha
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _AppSettings() when $default != null:
|
case _AppSettings() when $default != null:
|
||||||
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3);case _:
|
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3,_that.disableAnimation);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -504,7 +505,7 @@ return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_tha
|
|||||||
|
|
||||||
|
|
||||||
class _AppSettings implements AppSettings {
|
class _AppSettings implements AppSettings {
|
||||||
const _AppSettings({required this.autoTranslate, required this.dataSavingMode, required this.soundEffects, required this.aprilFoolFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.customFonts, required this.appColorScheme, required this.customColors, required this.windowSize, required this.windowOpacity, required this.cardTransparency, required this.defaultPoolId, required this.messageDisplayStyle, required this.themeMode, required this.useMaterial3});
|
const _AppSettings({required this.autoTranslate, required this.dataSavingMode, required this.soundEffects, required this.aprilFoolFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.customFonts, required this.appColorScheme, required this.customColors, required this.windowSize, required this.windowOpacity, required this.cardTransparency, required this.defaultPoolId, required this.messageDisplayStyle, required this.themeMode, required this.useMaterial3, required this.disableAnimation});
|
||||||
|
|
||||||
|
|
||||||
@override final bool autoTranslate;
|
@override final bool autoTranslate;
|
||||||
@@ -528,6 +529,7 @@ class _AppSettings implements AppSettings {
|
|||||||
@override final String messageDisplayStyle;
|
@override final String messageDisplayStyle;
|
||||||
@override final String? themeMode;
|
@override final String? themeMode;
|
||||||
@override final bool useMaterial3;
|
@override final bool useMaterial3;
|
||||||
|
@override final bool disableAnimation;
|
||||||
|
|
||||||
/// Create a copy of AppSettings
|
/// Create a copy of AppSettings
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -539,16 +541,16 @@ _$AppSettingsCopyWith<_AppSettings> get copyWith => __$AppSettingsCopyWithImpl<_
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.useMaterial3, useMaterial3) || other.useMaterial3 == useMaterial3));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.useMaterial3, useMaterial3) || other.useMaterial3 == useMaterial3)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,useMaterial3);
|
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,useMaterial3,disableAnimation);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, useMaterial3: $useMaterial3)';
|
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, useMaterial3: $useMaterial3, disableAnimation: $disableAnimation)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -559,7 +561,7 @@ abstract mixin class _$AppSettingsCopyWith<$Res> implements $AppSettingsCopyWith
|
|||||||
factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl;
|
factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3
|
bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -576,7 +578,7 @@ class __$AppSettingsCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of AppSettings
|
/// Create a copy of AppSettings
|
||||||
/// 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? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? useMaterial3 = null,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? useMaterial3 = null,Object? disableAnimation = null,}) {
|
||||||
return _then(_AppSettings(
|
return _then(_AppSettings(
|
||||||
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
|
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
|
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -595,6 +597,7 @@ as double,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaul
|
|||||||
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
|
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
|
||||||
as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
|
as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,useMaterial3: null == useMaterial3 ? _self.useMaterial3 : useMaterial3 // ignore: cast_nullable_to_non_nullable
|
as String?,useMaterial3: null == useMaterial3 ? _self.useMaterial3 : useMaterial3 // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,disableAnimation: null == disableAnimation ? _self.disableAnimation : disableAnimation // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Map<String, dynamic> _$ThemeColorsToJson(_ThemeColors instance) =>
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$appSettingsNotifierHash() =>
|
String _$appSettingsNotifierHash() =>
|
||||||
r'3ba2cdce76f3c4fed84f4108341c88a0a971bf3a';
|
r'10ebd893c39d24ae588a4c0bf4144ee4656465a4';
|
||||||
|
|
||||||
/// See also [AppSettingsNotifier].
|
/// See also [AppSettingsNotifier].
|
||||||
@ProviderFor(AppSettingsNotifier)
|
@ProviderFor(AppSettingsNotifier)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dio_smart_retry/dio_smart_retry.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
@@ -109,6 +110,16 @@ final apiClientProvider = Provider<Dio>((ref) {
|
|||||||
printResponseData: false,
|
printResponseData: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
RetryInterceptor(
|
||||||
|
dio: dio,
|
||||||
|
retries: 3,
|
||||||
|
retryDelays: const [
|
||||||
|
Duration(milliseconds: 300),
|
||||||
|
Duration(milliseconds: 500),
|
||||||
|
Duration(milliseconds: 1000),
|
||||||
|
],
|
||||||
|
retryEvaluator: (err, _) => err.requestOptions.method == 'GET',
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return dio;
|
return dio;
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ ThemeData createAppTheme(Brightness brightness, AppSettings settings) {
|
|||||||
color: colorScheme.surfaceContainer.withOpacity(
|
color: colorScheme.surfaceContainer.withOpacity(
|
||||||
settings.cardTransparency,
|
settings.cardTransparency,
|
||||||
),
|
),
|
||||||
|
elevation: settings.cardTransparency <= 1 ? 0 : null,
|
||||||
),
|
),
|
||||||
pageTransitionsTheme: PageTransitionsTheme(
|
pageTransitionsTheme: PageTransitionsTheme(
|
||||||
builders: {
|
builders: {
|
||||||
|
|||||||
373
lib/route.dart
373
lib/route.dart
@@ -58,7 +58,6 @@ import 'package:island/screens/settings.dart';
|
|||||||
import 'package:island/screens/realm/realms.dart';
|
import 'package:island/screens/realm/realms.dart';
|
||||||
import 'package:island/screens/realm/realm_form.dart';
|
import 'package:island/screens/realm/realm_form.dart';
|
||||||
import 'package:island/screens/realm/realm_detail.dart';
|
import 'package:island/screens/realm/realm_detail.dart';
|
||||||
import 'package:island/screens/account/event_calendar.dart';
|
|
||||||
import 'package:island/screens/discovery/realms.dart';
|
import 'package:island/screens/discovery/realms.dart';
|
||||||
import 'package:island/screens/reports/report_detail.dart';
|
import 'package:island/screens/reports/report_detail.dart';
|
||||||
import 'package:island/screens/reports/report_list.dart';
|
import 'package:island/screens/reports/report_list.dart';
|
||||||
@@ -138,193 +137,6 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
path: '/logs',
|
path: '/logs',
|
||||||
builder: (context, state) => TalkerScreen(talker: talker),
|
builder: (context, state) => TalkerScreen(talker: talker),
|
||||||
),
|
),
|
||||||
GoRoute(
|
|
||||||
name: 'accountCalendar',
|
|
||||||
path: '/account/:name/calendar',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return EventCalanderScreen(name: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorHub',
|
|
||||||
path: '/creators',
|
|
||||||
builder: (context, state) => const CreatorHubScreen(),
|
|
||||||
routes: [
|
|
||||||
// Web Feed Routes
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorFeeds',
|
|
||||||
path: ':name/feeds',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return WebFeedListScreen(pubName: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorPosts',
|
|
||||||
path: ':name/posts',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return CreatorPostListScreen(pubName: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Poll list route
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorPolls',
|
|
||||||
path: ':name/polls',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return CreatorPollListScreen(pubName: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Poll routes
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorPollNew',
|
|
||||||
path: ':name/polls/new',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
// initialPollId left null for create; initialPublisher prefilled
|
|
||||||
return PollEditorScreen(initialPublisher: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorPollEdit',
|
|
||||||
path: ':name/polls/:id/edit',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
final id = state.pathParameters['id']!;
|
|
||||||
return PollEditorScreen(
|
|
||||||
initialPollId: id,
|
|
||||||
initialPublisher: name,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorStickers',
|
|
||||||
path: ':name/stickers',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return StickersScreen(pubName: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorNew',
|
|
||||||
path: 'new',
|
|
||||||
builder: (context, state) => const NewPublisherScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorEdit',
|
|
||||||
path: ':name/edit',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return EditPublisherScreen(name: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerHub',
|
|
||||||
path: '/developers',
|
|
||||||
builder:
|
|
||||||
(context, state) => DeveloperHubScreen(
|
|
||||||
initialPublisherName: state.uri.queryParameters['publisher'],
|
|
||||||
initialProjectId: state.uri.queryParameters['project'],
|
|
||||||
),
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerProjectNew',
|
|
||||||
path: ':name/projects/new',
|
|
||||||
builder:
|
|
||||||
(context, state) => NewProjectScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerProjectEdit',
|
|
||||||
path: ':name/projects/:id/edit',
|
|
||||||
builder:
|
|
||||||
(context, state) => EditProjectScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
id: state.pathParameters['id']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerProjectDetail',
|
|
||||||
path: ':name/projects/:projectId',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
final projectId = state.pathParameters['projectId']!;
|
|
||||||
// Redirect to hub with project selected
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
context.go(
|
|
||||||
'/developers?publisher=$name&project=$projectId',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return const SizedBox.shrink(); // Temporary placeholder
|
|
||||||
},
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerAppNew',
|
|
||||||
path: 'apps/new',
|
|
||||||
builder:
|
|
||||||
(context, state) => NewCustomAppScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerAppEdit',
|
|
||||||
path: 'apps/:id/edit',
|
|
||||||
builder:
|
|
||||||
(context, state) => EditAppScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
id: state.pathParameters['id']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerAppDetail',
|
|
||||||
path: 'apps/:appId',
|
|
||||||
builder:
|
|
||||||
(context, state) => AppDetailScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
appId: state.pathParameters['appId']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerBotNew',
|
|
||||||
path: 'bots/new',
|
|
||||||
builder:
|
|
||||||
(context, state) => NewBotScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerBotDetail',
|
|
||||||
path: 'bots/:botId',
|
|
||||||
builder:
|
|
||||||
(context, state) => BotDetailScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
botId: state.pathParameters['botId']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerBotEdit',
|
|
||||||
path: 'bots/:id/edit',
|
|
||||||
builder:
|
|
||||||
(context, state) => EditBotScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
id: state.pathParameters['id']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// Web articles
|
// Web articles
|
||||||
GoRoute(
|
GoRoute(
|
||||||
@@ -633,12 +445,195 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'accountProfile',
|
name: 'accountProfile',
|
||||||
path: '/account/:name',
|
path: '/accounts/:name',
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final name = state.pathParameters['name']!;
|
final name = state.pathParameters['name']!;
|
||||||
return AccountProfileScreen(name: name);
|
return AccountProfileScreen(name: name);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// Creator hub tab
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorHub',
|
||||||
|
path: '/creators',
|
||||||
|
builder: (context, state) => const CreatorHubScreen(),
|
||||||
|
routes: [
|
||||||
|
// Web Feed Routes
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorFeeds',
|
||||||
|
path: ':name/feeds',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return WebFeedListScreen(pubName: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorPosts',
|
||||||
|
path: ':name/posts',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return CreatorPostListScreen(pubName: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Poll list route
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorPolls',
|
||||||
|
path: ':name/polls',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return CreatorPollListScreen(pubName: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Poll routes
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorPollNew',
|
||||||
|
path: ':name/polls/new',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
// initialPollId left null for create; initialPublisher prefilled
|
||||||
|
return PollEditorScreen(initialPublisher: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorPollEdit',
|
||||||
|
path: ':name/polls/:id/edit',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
final id = state.pathParameters['id']!;
|
||||||
|
return PollEditorScreen(
|
||||||
|
initialPollId: id,
|
||||||
|
initialPublisher: name,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorStickers',
|
||||||
|
path: ':name/stickers',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return StickersScreen(pubName: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorNew',
|
||||||
|
path: 'new',
|
||||||
|
builder: (context, state) => const NewPublisherScreen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorEdit',
|
||||||
|
path: ':name/edit',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return EditPublisherScreen(name: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// Developer hub tab
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerHub',
|
||||||
|
path: '/developers',
|
||||||
|
builder:
|
||||||
|
(context, state) => DeveloperHubScreen(
|
||||||
|
initialPublisherName:
|
||||||
|
state.uri.queryParameters['publisher'],
|
||||||
|
initialProjectId: state.uri.queryParameters['project'],
|
||||||
|
),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerProjectNew',
|
||||||
|
path: ':name/projects/new',
|
||||||
|
builder:
|
||||||
|
(context, state) => NewProjectScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerProjectEdit',
|
||||||
|
path: ':name/projects/:id/edit',
|
||||||
|
builder:
|
||||||
|
(context, state) => EditProjectScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
id: state.pathParameters['id']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerProjectDetail',
|
||||||
|
path: ':name/projects/:projectId',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
final projectId = state.pathParameters['projectId']!;
|
||||||
|
// Redirect to hub with project selected
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
context.go(
|
||||||
|
'/developers?publisher=$name&project=$projectId',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return const SizedBox.shrink(); // Temporary placeholder
|
||||||
|
},
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerAppNew',
|
||||||
|
path: 'apps/new',
|
||||||
|
builder:
|
||||||
|
(context, state) => NewCustomAppScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerAppEdit',
|
||||||
|
path: 'apps/:id/edit',
|
||||||
|
builder:
|
||||||
|
(context, state) => EditAppScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
id: state.pathParameters['id']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerAppDetail',
|
||||||
|
path: 'apps/:appId',
|
||||||
|
builder:
|
||||||
|
(context, state) => AppDetailScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
appId: state.pathParameters['appId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerBotNew',
|
||||||
|
path: 'bots/new',
|
||||||
|
builder:
|
||||||
|
(context, state) => NewBotScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerBotDetail',
|
||||||
|
path: 'bots/:botId',
|
||||||
|
builder:
|
||||||
|
(context, state) => BotDetailScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
botId: state.pathParameters['botId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerBotEdit',
|
||||||
|
path: 'bots/:id/edit',
|
||||||
|
builder:
|
||||||
|
(context, state) => EditBotScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
id: state.pathParameters['id']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -149,132 +149,124 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed('leveling');
|
context.pushNamed('leveling');
|
||||||
},
|
},
|
||||||
).padding(horizontal: 12),
|
).padding(horizontal: 12),
|
||||||
|
if (!isWideScreen(context)) const SizedBox.shrink(),
|
||||||
|
if (!isWideScreen(context))
|
||||||
|
Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Icon(Symbols.draw, size: 28).padding(bottom: 8),
|
||||||
|
Text(
|
||||||
|
'creatorHub',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).tr().fontSize(16).bold(),
|
||||||
|
Text(
|
||||||
|
'creatorHubDescription',
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 16, vertical: 12),
|
||||||
|
onTap: () {
|
||||||
|
context.goNamed('creatorHub');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
).height(140),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Icon(Symbols.code, size: 28).padding(bottom: 8),
|
||||||
|
Text(
|
||||||
|
'developerPortal',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).tr().fontSize(16).bold(),
|
||||||
|
Text(
|
||||||
|
'developerPortalDescription',
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 16, vertical: 12),
|
||||||
|
onTap: () {
|
||||||
|
context.pushNamed('developerHub');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
).height(140),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 12),
|
||||||
const SizedBox.shrink(),
|
const SizedBox.shrink(),
|
||||||
Row(
|
SingleChildScrollView(
|
||||||
spacing: 8,
|
scrollDirection: Axis.horizontal,
|
||||||
children: [
|
child: Row(
|
||||||
Expanded(
|
spacing: 8,
|
||||||
child: Card(
|
children: [
|
||||||
|
Card(
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Icon(Symbols.draw, size: 28).padding(bottom: 8),
|
Icon(Symbols.settings, size: 20),
|
||||||
Text(
|
Text('appSettings').tr().fontSize(13).bold(),
|
||||||
'creatorHub',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
).tr().fontSize(16).bold(),
|
|
||||||
Text(
|
|
||||||
'creatorHubDescription',
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
).tr(),
|
|
||||||
],
|
|
||||||
).padding(horizontal: 16, vertical: 12),
|
|
||||||
onTap: () {
|
|
||||||
context.goNamed('creatorHub');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
).height(140),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Card(
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Icon(Symbols.code, size: 28).padding(bottom: 8),
|
|
||||||
Text(
|
|
||||||
'developerPortal',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
).tr().fontSize(16).bold(),
|
|
||||||
Text(
|
|
||||||
'developerPortalDescription',
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
).tr(),
|
|
||||||
],
|
|
||||||
).padding(horizontal: 16, vertical: 12),
|
|
||||||
onTap: () {
|
|
||||||
context.pushNamed('developerHub');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
).height(140),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padding(horizontal: 12),
|
|
||||||
const SizedBox.shrink(),
|
|
||||||
Row(
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Card(
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Icon(Symbols.settings, size: 28).padding(bottom: 8),
|
|
||||||
Text('appSettings').tr().fontSize(16).bold(),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('settings');
|
context.pushNamed('settings');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).height(120),
|
),
|
||||||
),
|
Card(
|
||||||
Expanded(
|
|
||||||
child: Card(
|
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(Symbols.person_edit, size: 20),
|
||||||
Symbols.person_edit,
|
Text('updateYourProfile').tr().fontSize(13).bold(),
|
||||||
size: 28,
|
|
||||||
).padding(bottom: 8),
|
|
||||||
Text('updateYourProfile').tr().fontSize(16).bold(),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('profileUpdate');
|
context.pushNamed('profileUpdate');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).height(120),
|
),
|
||||||
),
|
Card(
|
||||||
Expanded(
|
|
||||||
child: Card(
|
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(Symbols.manage_accounts, size: 20),
|
||||||
Symbols.manage_accounts,
|
Text('accountSettings').tr().fontSize(13).bold(),
|
||||||
size: 28,
|
|
||||||
).padding(bottom: 8),
|
|
||||||
Text('accountSettings').tr().fontSize(16).bold(),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('accountSettings');
|
context.pushNamed('accountSettings');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).height(120),
|
),
|
||||||
),
|
],
|
||||||
],
|
).padding(horizontal: 12),
|
||||||
).padding(horizontal: 12),
|
).height(48),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
leading: const Icon(Symbols.notifications),
|
leading: const Icon(Symbols.notifications),
|
||||||
@@ -290,7 +282,11 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('notifications');
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => const NotificationScreen(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import 'package:island/pods/userinfo.dart';
|
|||||||
import 'package:island/services/file.dart';
|
import 'package:island/services/file.dart';
|
||||||
import 'package:island/services/file_uploader.dart';
|
import 'package:island/services/file_uploader.dart';
|
||||||
import 'package:island/services/timezone.dart';
|
import 'package:island/services/timezone.dart';
|
||||||
|
import 'package:island/widgets/account/account_name.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';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
@@ -24,6 +25,17 @@ const kServerSupportedLanguages = {'en-US': 'en-us', 'zh-CN': 'zh-hans'};
|
|||||||
const kServerSupportedRegions = ['US', 'JP', 'CN'];
|
const kServerSupportedRegions = ['US', 'JP', 'CN'];
|
||||||
|
|
||||||
class UpdateProfileScreen extends HookConsumerWidget {
|
class UpdateProfileScreen extends HookConsumerWidget {
|
||||||
|
bool _isValidHexColor(String color) {
|
||||||
|
if (!color.startsWith('#')) return false;
|
||||||
|
if (color.length != 7) return false; // #RRGGBB format
|
||||||
|
try {
|
||||||
|
int.parse(color.substring(1), radix: 16);
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const UpdateProfileScreen({super.key});
|
const UpdateProfileScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -148,12 +160,39 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
text: user.value!.profile.timeZone,
|
text: user.value!.profile.timeZone,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Username color state
|
||||||
|
final usernameColorType = useState<String>(
|
||||||
|
user.value!.profile.usernameColor?.type ?? 'plain',
|
||||||
|
);
|
||||||
|
final usernameColorValue = useTextEditingController(
|
||||||
|
text: user.value!.profile.usernameColor?.value,
|
||||||
|
);
|
||||||
|
final usernameColorDirection = useTextEditingController(
|
||||||
|
text: user.value!.profile.usernameColor?.direction,
|
||||||
|
);
|
||||||
|
final usernameColorColors = useState<List<String>>(
|
||||||
|
user.value!.profile.usernameColor?.colors ?? [],
|
||||||
|
);
|
||||||
|
|
||||||
void updateProfile() async {
|
void updateProfile() async {
|
||||||
if (!formKeyProfile.currentState!.validate()) return;
|
if (!formKeyProfile.currentState!.validate()) return;
|
||||||
|
|
||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
try {
|
try {
|
||||||
final client = ref.watch(apiClientProvider);
|
final client = ref.watch(apiClientProvider);
|
||||||
|
final usernameColorData = {
|
||||||
|
'type': usernameColorType.value,
|
||||||
|
if (usernameColorType.value == 'plain' &&
|
||||||
|
usernameColorValue.text.isNotEmpty)
|
||||||
|
'value': usernameColorValue.text,
|
||||||
|
if (usernameColorType.value == 'gradient') ...{
|
||||||
|
if (usernameColorDirection.text.isNotEmpty)
|
||||||
|
'direction': usernameColorDirection.text,
|
||||||
|
'colors':
|
||||||
|
usernameColorColors.value.where((c) => c.isNotEmpty).toList(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
await client.patch(
|
await client.patch(
|
||||||
'/id/accounts/me/profile',
|
'/id/accounts/me/profile',
|
||||||
data: {
|
data: {
|
||||||
@@ -166,6 +205,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
'location': locationController.text,
|
'location': locationController.text,
|
||||||
'time_zone': timeZoneController.text,
|
'time_zone': timeZoneController.text,
|
||||||
'birthday': birthday.value?.toUtc().toIso8601String(),
|
'birthday': birthday.value?.toUtc().toIso8601String(),
|
||||||
|
'username_color': usernameColorData,
|
||||||
'links':
|
'links':
|
||||||
links.value
|
links.value
|
||||||
.where((e) => e.name.isNotEmpty && e.url.isNotEmpty)
|
.where((e) => e.name.isNotEmpty && e.url.isNotEmpty)
|
||||||
@@ -593,6 +633,320 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Text(
|
||||||
|
'usernameColor',
|
||||||
|
).tr().bold().fontSize(18).padding(top: 16),
|
||||||
|
Column(
|
||||||
|
spacing: 16,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Preview section
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceContainerHighest,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('preview').tr().bold().fontSize(14),
|
||||||
|
const Gap(8),
|
||||||
|
// Create a preview account with the current settings
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final previewAccount = user.value!.copyWith(
|
||||||
|
profile: user.value!.profile.copyWith(
|
||||||
|
usernameColor: UsernameColor(
|
||||||
|
type: usernameColorType.value,
|
||||||
|
value:
|
||||||
|
usernameColorType.value == 'plain' &&
|
||||||
|
usernameColorValue
|
||||||
|
.text
|
||||||
|
.isNotEmpty
|
||||||
|
? usernameColorValue.text
|
||||||
|
: null,
|
||||||
|
direction:
|
||||||
|
usernameColorType.value ==
|
||||||
|
'gradient' &&
|
||||||
|
usernameColorDirection
|
||||||
|
.text
|
||||||
|
.isNotEmpty
|
||||||
|
? usernameColorDirection.text
|
||||||
|
: null,
|
||||||
|
colors:
|
||||||
|
usernameColorType.value == 'gradient'
|
||||||
|
? usernameColorColors.value
|
||||||
|
.where((c) => c.isNotEmpty)
|
||||||
|
.toList()
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if user can use this color
|
||||||
|
final tier =
|
||||||
|
user.value!.perkSubscription?.identifier;
|
||||||
|
final canUseColor = switch (tier) {
|
||||||
|
'solian.stellar.primary' =>
|
||||||
|
usernameColorType.value == 'plain' &&
|
||||||
|
(kUsernamePlainColors.containsKey(
|
||||||
|
usernameColorValue.text,
|
||||||
|
) ||
|
||||||
|
(usernameColorValue.text.startsWith(
|
||||||
|
'#',
|
||||||
|
) &&
|
||||||
|
_isValidHexColor(
|
||||||
|
usernameColorValue.text,
|
||||||
|
))),
|
||||||
|
'solian.stellar.nova' =>
|
||||||
|
usernameColorType.value == 'plain',
|
||||||
|
'solian.stellar.supernova' => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
AccountName(
|
||||||
|
account: previewAccount,
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
ignorePermissions: true,
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
canUseColor
|
||||||
|
? Symbols.check_circle
|
||||||
|
: Symbols.error,
|
||||||
|
size: 16,
|
||||||
|
color:
|
||||||
|
canUseColor
|
||||||
|
? Colors.green
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
canUseColor
|
||||||
|
? 'availableWithYourPlan'.tr()
|
||||||
|
: 'upgradeRequired'.tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color:
|
||||||
|
canUseColor
|
||||||
|
? Colors.green
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DropdownButtonFormField2<String>(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'colorType'.tr(),
|
||||||
|
),
|
||||||
|
items: [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'plain',
|
||||||
|
child: Text('plain'.tr()),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'gradient',
|
||||||
|
child: Text('gradient'.tr()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
value: usernameColorType.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
usernameColorType.value = value ?? 'plain';
|
||||||
|
},
|
||||||
|
customButton: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Text(usernameColorType.value).tr()),
|
||||||
|
Icon(Symbols.arrow_drop_down),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (usernameColorType.value == 'plain')
|
||||||
|
Autocomplete<String>(
|
||||||
|
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||||
|
final options = kUsernamePlainColors.keys.toList();
|
||||||
|
if (textEditingValue.text == '') {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
return options.where(
|
||||||
|
(option) => option.toLowerCase().contains(
|
||||||
|
textEditingValue.text.toLowerCase(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSelected: (String selection) {
|
||||||
|
usernameColorValue.text = selection;
|
||||||
|
},
|
||||||
|
fieldViewBuilder: (
|
||||||
|
context,
|
||||||
|
controller,
|
||||||
|
focusNode,
|
||||||
|
onFieldSubmitted,
|
||||||
|
) {
|
||||||
|
// Initialize the controller with the current value
|
||||||
|
if (controller.text.isEmpty &&
|
||||||
|
usernameColorValue.text.isNotEmpty) {
|
||||||
|
controller.text = usernameColorValue.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextFormField(
|
||||||
|
controller: controller,
|
||||||
|
focusNode: focusNode,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'colorValue'.tr(),
|
||||||
|
hintText: 'e.g. red or #ff6600',
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
usernameColorValue.text = value;
|
||||||
|
},
|
||||||
|
onTapOutside:
|
||||||
|
(_) =>
|
||||||
|
FocusManager.instance.primaryFocus
|
||||||
|
?.unfocus(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (usernameColorType.value == 'gradient') ...[
|
||||||
|
DropdownButtonFormField2<String>(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'gradientDirection'.tr(),
|
||||||
|
),
|
||||||
|
items: [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'to right',
|
||||||
|
child: Text('gradientDirectionToRight'.tr()),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'to left',
|
||||||
|
child: Text('gradientDirectionToLeft'.tr()),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'to bottom',
|
||||||
|
child: Text('gradientDirectionToBottom'.tr()),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'to top',
|
||||||
|
child: Text('gradientDirectionToTop'.tr()),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'to bottom right',
|
||||||
|
child: Text(
|
||||||
|
'gradientDirectionToBottomRight'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'to bottom left',
|
||||||
|
child: Text('gradientDirectionToBottomLeft'.tr()),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'to top right',
|
||||||
|
child: Text('gradientDirectionToTopRight'.tr()),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'to top left',
|
||||||
|
child: Text('gradientDirectionToTopLeft'.tr()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
value:
|
||||||
|
usernameColorDirection.text.isNotEmpty
|
||||||
|
? usernameColorDirection.text
|
||||||
|
: 'to right',
|
||||||
|
onChanged: (value) {
|
||||||
|
usernameColorDirection.text = value ?? 'to right';
|
||||||
|
},
|
||||||
|
customButton: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
usernameColorDirection.text.isNotEmpty
|
||||||
|
? usernameColorDirection.text
|
||||||
|
: 'to right',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(Symbols.arrow_drop_down),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'gradientColors',
|
||||||
|
).tr().bold().fontSize(14).padding(top: 8),
|
||||||
|
Column(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
for (
|
||||||
|
var i = 0;
|
||||||
|
i < usernameColorColors.value.length;
|
||||||
|
i++
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
key: ValueKey(
|
||||||
|
usernameColorColors.value[i].hashCode,
|
||||||
|
),
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
initialValue:
|
||||||
|
usernameColorColors.value[i],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'color'.tr(),
|
||||||
|
hintText: 'e.g. #ff0000',
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
usernameColorColors.value[i] = value;
|
||||||
|
},
|
||||||
|
onTapOutside:
|
||||||
|
(_) =>
|
||||||
|
FocusManager.instance.primaryFocus
|
||||||
|
?.unfocus(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.delete),
|
||||||
|
onPressed: () {
|
||||||
|
usernameColorColors.value =
|
||||||
|
usernameColorColors.value
|
||||||
|
.whereIndexed(
|
||||||
|
(idx, _) => idx != i,
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
usernameColorColors.value = List.from(
|
||||||
|
usernameColorColors.value,
|
||||||
|
)..add('');
|
||||||
|
},
|
||||||
|
label: Text('addColor').tr(),
|
||||||
|
icon: const Icon(Symbols.add),
|
||||||
|
).padding(top: 8),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
Text('links').tr().bold().fontSize(18).padding(top: 16),
|
Text('links').tr().bold().fontSize(18).padding(top: 16),
|
||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
|
|||||||
@@ -97,9 +97,7 @@ class _AccountBasicInfo extends StatelessWidget {
|
|||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
SharePlus.instance.share(
|
SharePlus.instance.share(
|
||||||
ShareParams(
|
ShareParams(uri: Uri.parse('https://solian.app/@${data.name}')),
|
||||||
uri: Uri.parse('https://solian.app/@${data.name}'),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: const Icon(Symbols.share),
|
icon: const Icon(Symbols.share),
|
||||||
@@ -879,7 +877,7 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
child: Card(
|
child: Card(
|
||||||
child: FortuneGraphWidget(
|
child: FortuneGraphWidget(
|
||||||
events: accountEvents,
|
events: accountEvents,
|
||||||
eventCalanderUser: data.name,
|
eventCalandarUser: data.name,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1004,7 +1002,7 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
child: Card(
|
child: Card(
|
||||||
child: FortuneGraphWidget(
|
child: FortuneGraphWidget(
|
||||||
events: accountEvents,
|
events: accountEvents,
|
||||||
eventCalanderUser: data.name,
|
eventCalandarUser: data.name,
|
||||||
),
|
),
|
||||||
).padding(horizontal: 4),
|
).padding(horizontal: 4),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import "package:island/pods/chat/chat_subscribe.dart";
|
|||||||
import "package:island/pods/chat/messages_notifier.dart";
|
import "package:island/pods/chat/messages_notifier.dart";
|
||||||
import "package:island/pods/network.dart";
|
import "package:island/pods/network.dart";
|
||||||
import "package:island/pods/chat/chat_online_count.dart";
|
import "package:island/pods/chat/chat_online_count.dart";
|
||||||
|
import "package:island/pods/config.dart";
|
||||||
import "package:island/screens/chat/search_messages.dart";
|
import "package:island/screens/chat/search_messages.dart";
|
||||||
import "package:island/services/file_uploader.dart";
|
import "package:island/services/file_uploader.dart";
|
||||||
import "package:island/screens/chat/chat.dart";
|
import "package:island/screens/chat/chat.dart";
|
||||||
@@ -44,6 +45,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
final chatIdentity = ref.watch(chatroomIdentityProvider(id));
|
final chatIdentity = ref.watch(chatroomIdentityProvider(id));
|
||||||
final isSyncing = ref.watch(isSyncingProvider);
|
final isSyncing = ref.watch(isSyncingProvider);
|
||||||
final onlineCount = ref.watch(chatOnlineCountNotifierProvider(id));
|
final onlineCount = ref.watch(chatOnlineCountNotifierProvider(id));
|
||||||
|
final settings = ref.watch(appSettingsNotifierProvider);
|
||||||
|
|
||||||
final hasOnlineCount = onlineCount.hasValue;
|
final hasOnlineCount = onlineCount.hasValue;
|
||||||
|
|
||||||
@@ -535,7 +537,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
listController: listController,
|
listController: listController,
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
top: 16,
|
top: 16,
|
||||||
bottom: 80 + MediaQuery.of(context).padding.bottom,
|
bottom: MediaQuery.of(context).padding.bottom + 16,
|
||||||
),
|
),
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
reverse: true, // Show newest messages at the bottom
|
reverse: true, // Show newest messages at the bottom
|
||||||
@@ -543,7 +545,9 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
findChildIndexCallback: (key) {
|
findChildIndexCallback: (key) {
|
||||||
if (key is! ValueKey<String>) return null;
|
if (key is! ValueKey<String>) return null;
|
||||||
final messageId = key.value.substring(messageKeyPrefix.length);
|
final messageId = key.value.substring(messageKeyPrefix.length);
|
||||||
final index = messageList.indexWhere((m) => m.id == messageId);
|
final index = messageList.indexWhere(
|
||||||
|
(m) => (m.nonce ?? m.id) == messageId,
|
||||||
|
);
|
||||||
// Return null for invalid indices to let SuperListView handle it properly
|
// Return null for invalid indices to let SuperListView handle it properly
|
||||||
return index >= 0 ? index : null;
|
return index >= 0 ? index : null;
|
||||||
},
|
},
|
||||||
@@ -561,13 +565,14 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
.abs() >
|
.abs() >
|
||||||
3;
|
3;
|
||||||
|
|
||||||
final key = ValueKey('$messageKeyPrefix${message.id}');
|
// Use a stable animation key that doesn't change during message lifecycle
|
||||||
|
final key = Key('$messageKeyPrefix${message.nonce ?? message.id}');
|
||||||
|
|
||||||
return chatIdentity.when(
|
final messageWidget = chatIdentity.when(
|
||||||
skipError: true,
|
skipError: true,
|
||||||
data:
|
data:
|
||||||
(identity) => MessageItem(
|
(identity) => MessageItem(
|
||||||
key: key,
|
key: settings.disableAnimation ? key : null,
|
||||||
message: message,
|
message: message,
|
||||||
isCurrentUser: identity?.id == message.senderId,
|
isCurrentUser: identity?.id == message.senderId,
|
||||||
onAction: (action) {
|
onAction: (action) {
|
||||||
@@ -605,7 +610,6 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
loading:
|
loading:
|
||||||
() => MessageItem(
|
() => MessageItem(
|
||||||
key: key,
|
|
||||||
message: message,
|
message: message,
|
||||||
isCurrentUser: false,
|
isCurrentUser: false,
|
||||||
onAction: null,
|
onAction: null,
|
||||||
@@ -613,8 +617,29 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
showAvatar: false,
|
showAvatar: false,
|
||||||
onJump: (_) {},
|
onJump: (_) {},
|
||||||
),
|
),
|
||||||
error: (_, _) => SizedBox.shrink(key: key),
|
error: (_, _) => const SizedBox.shrink(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return settings.disableAnimation
|
||||||
|
? messageWidget
|
||||||
|
: TweenAnimationBuilder<double>(
|
||||||
|
key: key,
|
||||||
|
tween: Tween<double>(begin: 0.0, end: 1.0),
|
||||||
|
duration: Duration(
|
||||||
|
milliseconds: 400 + (index % 5) * 50,
|
||||||
|
), // Staggered delay
|
||||||
|
curve: Curves.easeOutCubic,
|
||||||
|
builder: (context, animationValue, child) {
|
||||||
|
return Transform.translate(
|
||||||
|
offset: Offset(
|
||||||
|
0,
|
||||||
|
20 * (1 - animationValue),
|
||||||
|
), // Slide up from bottom
|
||||||
|
child: Opacity(opacity: animationValue, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: messageWidget,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -687,91 +712,116 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
// Messages
|
// Messages and Input in Column
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: messages.when(
|
child: Column(
|
||||||
data:
|
children: [
|
||||||
(messageList) =>
|
Expanded(
|
||||||
messageList.isEmpty
|
child: AnimatedSwitcher(
|
||||||
? Center(child: Text('No messages yet'.tr()))
|
duration: const Duration(milliseconds: 300),
|
||||||
: chatMessageListWidget(messageList),
|
switchInCurve: Curves.easeOutCubic,
|
||||||
loading: () => const Center(child: CircularProgressIndicator()),
|
switchOutCurve: Curves.easeInCubic,
|
||||||
error:
|
transitionBuilder: (
|
||||||
(error, _) => ResponseErrorWidget(
|
Widget child,
|
||||||
error: error,
|
Animation<double> animation,
|
||||||
onRetry: () => messagesNotifier.loadInitial(),
|
) {
|
||||||
|
return SlideTransition(
|
||||||
|
position: Tween<Offset>(
|
||||||
|
begin: const Offset(0, 0.05),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(animation),
|
||||||
|
child: FadeTransition(opacity: animation, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: messages.when(
|
||||||
|
data:
|
||||||
|
(messageList) =>
|
||||||
|
messageList.isEmpty
|
||||||
|
? Center(
|
||||||
|
key: const ValueKey('empty-messages'),
|
||||||
|
child: Text('No messages yet'.tr()),
|
||||||
|
)
|
||||||
|
: chatMessageListWidget(messageList),
|
||||||
|
loading:
|
||||||
|
() => const Center(
|
||||||
|
key: ValueKey('loading-messages'),
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
error:
|
||||||
|
(error, _) => ResponseErrorWidget(
|
||||||
|
key: const ValueKey('error-messages'),
|
||||||
|
error: error,
|
||||||
|
onRetry: () => messagesNotifier.loadInitial(),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
chatRoom.when(
|
||||||
// Input
|
data:
|
||||||
Positioned(
|
(room) => Column(
|
||||||
bottom: 0,
|
mainAxisSize: MainAxisSize.min,
|
||||||
left: 0,
|
children: [
|
||||||
right: 0,
|
ChatInput(
|
||||||
child: chatRoom.when(
|
messageController: messageController,
|
||||||
data:
|
chatRoom: room!,
|
||||||
(room) => Column(
|
onSend: sendMessage,
|
||||||
mainAxisSize: MainAxisSize.min,
|
onClear: () {
|
||||||
children: [
|
if (messageEditingTo.value != null) {
|
||||||
ChatInput(
|
attachments.value.clear();
|
||||||
messageController: messageController,
|
messageController.clear();
|
||||||
chatRoom: room!,
|
}
|
||||||
onSend: sendMessage,
|
messageEditingTo.value = null;
|
||||||
onClear: () {
|
messageReplyingTo.value = null;
|
||||||
if (messageEditingTo.value != null) {
|
messageForwardingTo.value = null;
|
||||||
attachments.value.clear();
|
},
|
||||||
messageController.clear();
|
messageEditingTo: messageEditingTo.value,
|
||||||
}
|
messageReplyingTo: messageReplyingTo.value,
|
||||||
messageEditingTo.value = null;
|
messageForwardingTo: messageForwardingTo.value,
|
||||||
messageReplyingTo.value = null;
|
onPickFile: (bool isPhoto) {
|
||||||
messageForwardingTo.value = null;
|
if (isPhoto) {
|
||||||
},
|
pickPhotoMedia();
|
||||||
messageEditingTo: messageEditingTo.value,
|
} else {
|
||||||
messageReplyingTo: messageReplyingTo.value,
|
pickVideoMedia();
|
||||||
messageForwardingTo: messageForwardingTo.value,
|
}
|
||||||
onPickFile: (bool isPhoto) {
|
},
|
||||||
if (isPhoto) {
|
onPickAudio: pickAudioMedia,
|
||||||
pickPhotoMedia();
|
onPickGeneralFile: pickGeneralFile,
|
||||||
} else {
|
onLinkAttachment: linkAttachment,
|
||||||
pickVideoMedia();
|
attachments: attachments.value,
|
||||||
}
|
onUploadAttachment: uploadAttachment,
|
||||||
},
|
onDeleteAttachment: (index) async {
|
||||||
onPickAudio: pickAudioMedia,
|
final attachment = attachments.value[index];
|
||||||
onPickGeneralFile: pickGeneralFile,
|
if (attachment.isOnCloud && !attachment.isLink) {
|
||||||
onLinkAttachment: linkAttachment,
|
final client = ref.watch(apiClientProvider);
|
||||||
attachments: attachments.value,
|
await client.delete(
|
||||||
onUploadAttachment: uploadAttachment,
|
'/drive/files/${attachment.data.id}',
|
||||||
onDeleteAttachment: (index) async {
|
);
|
||||||
final attachment = attachments.value[index];
|
}
|
||||||
if (attachment.isOnCloud && !attachment.isLink) {
|
final clone = List.of(attachments.value);
|
||||||
final client = ref.watch(apiClientProvider);
|
clone.removeAt(index);
|
||||||
await client.delete(
|
attachments.value = clone;
|
||||||
'/drive/files/${attachment.data.id}',
|
},
|
||||||
);
|
onMoveAttachment: (idx, delta) {
|
||||||
}
|
if (idx + delta < 0 ||
|
||||||
final clone = List.of(attachments.value);
|
idx + delta >= attachments.value.length) {
|
||||||
clone.removeAt(index);
|
return;
|
||||||
attachments.value = clone;
|
}
|
||||||
},
|
final clone = List.of(attachments.value);
|
||||||
onMoveAttachment: (idx, delta) {
|
clone.insert(idx + delta, clone.removeAt(idx));
|
||||||
if (idx + delta < 0 ||
|
attachments.value = clone;
|
||||||
idx + delta >= attachments.value.length) {
|
},
|
||||||
return;
|
onAttachmentsChanged: (newAttachments) {
|
||||||
}
|
attachments.value = newAttachments;
|
||||||
final clone = List.of(attachments.value);
|
},
|
||||||
clone.insert(idx + delta, clone.removeAt(idx));
|
attachmentProgress: attachmentProgress.value,
|
||||||
attachments.value = clone;
|
),
|
||||||
},
|
Gap(MediaQuery.of(context).padding.bottom),
|
||||||
onAttachmentsChanged: (newAttachments) {
|
],
|
||||||
attachments.value = newAttachments;
|
|
||||||
},
|
|
||||||
attachmentProgress: attachmentProgress.value,
|
|
||||||
),
|
),
|
||||||
Gap(MediaQuery.of(context).padding.bottom),
|
error: (_, _) => const SizedBox.shrink(),
|
||||||
],
|
loading: () => const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
error: (_, _) => const SizedBox.shrink(),
|
],
|
||||||
loading: () => const SizedBox.shrink(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:gap/gap.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/models/publisher.dart';
|
import 'package:island/models/publisher.dart';
|
||||||
|
import 'package:island/models/heatmap.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/screens/creators/publishers_form.dart';
|
import 'package:island/screens/creators/publishers_form.dart';
|
||||||
import 'package:island/services/responsive.dart';
|
import 'package:island/services/responsive.dart';
|
||||||
@@ -18,6 +19,7 @@ import 'package:island/widgets/app_scaffold.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';
|
||||||
import 'package:island/widgets/response.dart';
|
import 'package:island/widgets/response.dart';
|
||||||
|
import 'package:island/widgets/activity_heatmap.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
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';
|
||||||
@@ -33,6 +35,14 @@ Future<SnPublisherStats?> publisherStats(Ref ref, String? uname) async {
|
|||||||
return SnPublisherStats.fromJson(resp.data);
|
return SnPublisherStats.fromJson(resp.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnHeatmap?> publisherHeatmap(Ref ref, String? uname) async {
|
||||||
|
if (uname == null) return null;
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
final resp = await apiClient.get('/sphere/publishers/$uname/heatmap');
|
||||||
|
return SnHeatmap.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
Future<SnPublisherMember?> publisherIdentity(Ref ref, String uname) async {
|
Future<SnPublisherMember?> publisherIdentity(Ref ref, String uname) async {
|
||||||
try {
|
try {
|
||||||
@@ -329,6 +339,10 @@ class CreatorHubScreen extends HookConsumerWidget {
|
|||||||
publisherStatsProvider(currentPublisher.value?.name),
|
publisherStatsProvider(currentPublisher.value?.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final publisherHeatmap = ref.watch(
|
||||||
|
publisherHeatmapProvider(currentPublisher.value?.name),
|
||||||
|
);
|
||||||
|
|
||||||
final publisherFeatures = ref.watch(
|
final publisherFeatures = ref.watch(
|
||||||
publisherFeaturesProvider(currentPublisher.value?.name),
|
publisherFeaturesProvider(currentPublisher.value?.name),
|
||||||
);
|
);
|
||||||
@@ -557,6 +571,7 @@ class CreatorHubScreen extends HookConsumerWidget {
|
|||||||
if (stats != null)
|
if (stats != null)
|
||||||
_PublisherStatsWidget(
|
_PublisherStatsWidget(
|
||||||
stats: stats,
|
stats: stats,
|
||||||
|
heatmap: publisherHeatmap.value,
|
||||||
).padding(horizontal: 12),
|
).padding(horizontal: 12),
|
||||||
buildNavigationWidget(true),
|
buildNavigationWidget(true),
|
||||||
],
|
],
|
||||||
@@ -567,6 +582,7 @@ class CreatorHubScreen extends HookConsumerWidget {
|
|||||||
if (stats != null)
|
if (stats != null)
|
||||||
_PublisherStatsWidget(
|
_PublisherStatsWidget(
|
||||||
stats: stats,
|
stats: stats,
|
||||||
|
heatmap: publisherHeatmap.value,
|
||||||
).padding(horizontal: 16),
|
).padding(horizontal: 16),
|
||||||
buildNavigationWidget(false),
|
buildNavigationWidget(false),
|
||||||
],
|
],
|
||||||
@@ -585,7 +601,8 @@ class CreatorHubScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
class _PublisherStatsWidget extends StatelessWidget {
|
class _PublisherStatsWidget extends StatelessWidget {
|
||||||
final SnPublisherStats stats;
|
final SnPublisherStats stats;
|
||||||
const _PublisherStatsWidget({required this.stats});
|
final SnHeatmap? heatmap;
|
||||||
|
const _PublisherStatsWidget({required this.stats, this.heatmap});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -638,6 +655,7 @@ class _PublisherStatsWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if (heatmap != null) ActivityHeatmapWidget(heatmap: heatmap!),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -149,6 +149,127 @@ class _PublisherStatsProviderElement
|
|||||||
String? get uname => (origin as PublisherStatsProvider).uname;
|
String? get uname => (origin as PublisherStatsProvider).uname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _$publisherHeatmapHash() => r'5f70c55e14629ec8628445a317888e02fccd9af2';
|
||||||
|
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
@ProviderFor(publisherHeatmap)
|
||||||
|
const publisherHeatmapProvider = PublisherHeatmapFamily();
|
||||||
|
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
class PublisherHeatmapFamily extends Family<AsyncValue<SnHeatmap?>> {
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
const PublisherHeatmapFamily();
|
||||||
|
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
PublisherHeatmapProvider call(String? uname) {
|
||||||
|
return PublisherHeatmapProvider(uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
PublisherHeatmapProvider getProviderOverride(
|
||||||
|
covariant PublisherHeatmapProvider provider,
|
||||||
|
) {
|
||||||
|
return call(provider.uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
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'publisherHeatmapProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
class PublisherHeatmapProvider extends AutoDisposeFutureProvider<SnHeatmap?> {
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
PublisherHeatmapProvider(String? uname)
|
||||||
|
: this._internal(
|
||||||
|
(ref) => publisherHeatmap(ref as PublisherHeatmapRef, uname),
|
||||||
|
from: publisherHeatmapProvider,
|
||||||
|
name: r'publisherHeatmapProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$publisherHeatmapHash,
|
||||||
|
dependencies: PublisherHeatmapFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
PublisherHeatmapFamily._allTransitiveDependencies,
|
||||||
|
uname: uname,
|
||||||
|
);
|
||||||
|
|
||||||
|
PublisherHeatmapProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.uname,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String? uname;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<SnHeatmap?> Function(PublisherHeatmapRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: PublisherHeatmapProvider._internal(
|
||||||
|
(ref) => create(ref as PublisherHeatmapRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
uname: uname,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<SnHeatmap?> createElement() {
|
||||||
|
return _PublisherHeatmapProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is PublisherHeatmapProvider && other.uname == uname;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, uname.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin PublisherHeatmapRef on AutoDisposeFutureProviderRef<SnHeatmap?> {
|
||||||
|
/// The parameter `uname` of this provider.
|
||||||
|
String? get uname;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PublisherHeatmapProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<SnHeatmap?>
|
||||||
|
with PublisherHeatmapRef {
|
||||||
|
_PublisherHeatmapProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get uname => (origin as PublisherHeatmapProvider).uname;
|
||||||
|
}
|
||||||
|
|
||||||
String _$publisherIdentityHash() => r'299372f25fa4b2bf8e11a8ba2d645100fc38e76f';
|
String _$publisherIdentityHash() => r'299372f25fa4b2bf8e11a8ba2d645100fc38e76f';
|
||||||
|
|
||||||
/// See also [publisherIdentity].
|
/// See also [publisherIdentity].
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:easy_localization/easy_localization.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: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';
|
||||||
import 'package:island/models/custom_app.dart';
|
import 'package:island/models/custom_app.dart';
|
||||||
@@ -32,6 +33,7 @@ class AppDetailScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
isNoBackground: false,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(appData.value?.name ?? 'appDetails'.tr()),
|
title: Text(appData.value?.name ?? 'appDetails'.tr()),
|
||||||
actions: [
|
actions: [
|
||||||
@@ -51,6 +53,7 @@ class AppDetailScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
const Gap(8),
|
||||||
],
|
],
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class BotDetailScreen extends HookConsumerWidget {
|
|||||||
final botData = ref.watch(botProvider(publisherName, projectId, botId));
|
final botData = ref.watch(botProvider(publisherName, projectId, botId));
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
isNoBackground: false,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(botData.value?.account.nick ?? 'botDetails'.tr()),
|
title: Text(botData.value?.account.nick ?? 'botDetails'.tr()),
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@@ -317,6 +317,7 @@ class EditAppScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
isNoBackground: false,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isNew ? 'createCustomApp'.tr() : 'editCustomApp'.tr()),
|
title: Text(isNew ? 'createCustomApp'.tr() : 'editCustomApp'.tr()),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ class EditBotScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
isNoBackground: false,
|
||||||
appBar: AppBar(title: Text(isNew ? 'createBot'.tr() : 'editBot'.tr())),
|
appBar: AppBar(title: Text(isNew ? 'createBot'.tr() : 'editBot'.tr())),
|
||||||
body:
|
body:
|
||||||
botData == null && !isNew
|
botData == null && !isNew
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ final developersProvider =
|
|||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnDeveloper>>;
|
typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnDeveloper>>;
|
||||||
String _$devProjectsHash() => r'87fdcab47cd7d79ab019a5625617abeb1ffa1f39';
|
String _$devProjectsHash() => r'715b395bebda785d38691ffee3b88e50b498c91a';
|
||||||
|
|
||||||
/// See also [devProjects].
|
/// See also [devProjects].
|
||||||
@ProviderFor(devProjects)
|
@ProviderFor(devProjects)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class ArticleDetailScreen extends ConsumerWidget {
|
|||||||
final articleAsync = ref.watch(articleDetailProvider(articleId));
|
final articleAsync = ref.watch(articleDetailProvider(articleId));
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
isNoBackground: false,
|
||||||
body: articleAsync.when(
|
body: articleAsync.when(
|
||||||
data:
|
data:
|
||||||
(article) => AppScaffold(
|
(article) => AppScaffold(
|
||||||
|
|||||||
@@ -54,7 +54,11 @@ Widget notificationIndicatorWidget(
|
|||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
contentPadding: EdgeInsets.only(left: 16, right: 15),
|
contentPadding: EdgeInsets.only(left: 16, right: 15),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed('notifications');
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => const NotificationScreen(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -330,7 +334,7 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
PostFeaturedList(),
|
PostFeaturedList(),
|
||||||
PostComposeCard(
|
PostComposeCard(
|
||||||
onSubmit: (post) {
|
onSubmit: () {
|
||||||
activitiesNotifier.forceRefresh();
|
activitiesNotifier.forceRefresh();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'dart:math' as math;
|
|||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gap/gap.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';
|
||||||
import 'package:island/models/account.dart';
|
import 'package:island/models/account.dart';
|
||||||
@@ -11,8 +10,8 @@ import 'package:island/pods/network.dart';
|
|||||||
import 'package:island/pods/websocket.dart';
|
import 'package:island/pods/websocket.dart';
|
||||||
import 'package:island/route.dart';
|
import 'package:island/route.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
|
||||||
import 'package:island/widgets/content/markdown.dart';
|
import 'package:island/widgets/content/markdown.dart';
|
||||||
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||||
import 'package:relative_time/relative_time.dart';
|
import 'package:relative_time/relative_time.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@@ -128,19 +127,15 @@ class NotificationScreen extends HookConsumerWidget {
|
|||||||
ref.watch(notificationUnreadCountNotifierProvider.notifier).clear();
|
ref.watch(notificationUnreadCountNotifierProvider.notifier).clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffold(
|
return SheetScaffold(
|
||||||
appBar: AppBar(
|
titleText: 'notifications'.tr(),
|
||||||
leading: const PageBackButton(),
|
actions: [
|
||||||
title: const Text('notifications').tr(),
|
IconButton(
|
||||||
actions: [
|
onPressed: markAllRead,
|
||||||
IconButton(
|
icon: const Icon(Symbols.mark_as_unread),
|
||||||
onPressed: markAllRead,
|
),
|
||||||
icon: const Icon(Symbols.mark_as_unread),
|
],
|
||||||
),
|
child: PagingHelperView(
|
||||||
const Gap(8),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: PagingHelperView(
|
|
||||||
provider: notificationListNotifierProvider,
|
provider: notificationListNotifierProvider,
|
||||||
futureRefreshable: notificationListNotifierProvider.future,
|
futureRefreshable: notificationListNotifierProvider.future,
|
||||||
notifierRefreshable: notificationListNotifierProvider.notifier,
|
notifierRefreshable: notificationListNotifierProvider.notifier,
|
||||||
|
|||||||
@@ -490,14 +490,13 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
if (user.value != null)
|
if (user.value != null)
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 0,
|
bottom: 16 + MediaQuery.of(context).padding.bottom,
|
||||||
left: 0,
|
left: 16,
|
||||||
right: 0,
|
right: 16,
|
||||||
child: Material(
|
child:
|
||||||
elevation: 2,
|
ConstrainedBox(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
constraints: BoxConstraints(maxWidth: 660),
|
||||||
child: postState
|
child: postState.when(
|
||||||
.when(
|
|
||||||
data:
|
data:
|
||||||
(post) => PostQuickReply(
|
(post) => PostQuickReply(
|
||||||
parent: post!,
|
parent: post!,
|
||||||
@@ -509,13 +508,8 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
loading: () => const SizedBox.shrink(),
|
loading: () => const SizedBox.shrink(),
|
||||||
error: (_, _) => const SizedBox.shrink(),
|
error: (_, _) => const SizedBox.shrink(),
|
||||||
)
|
|
||||||
.padding(
|
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 8,
|
|
||||||
top: 8,
|
|
||||||
horizontal: 16,
|
|
||||||
),
|
),
|
||||||
),
|
).center(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:island/models/post.dart';
|
import 'package:island/models/post.dart';
|
||||||
import 'package:island/models/publisher.dart';
|
import 'package:island/models/publisher.dart';
|
||||||
import 'package:island/models/account.dart';
|
import 'package:island/models/account.dart';
|
||||||
|
import 'package:island/models/heatmap.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/services/color.dart';
|
import 'package:island/services/color.dart';
|
||||||
@@ -20,6 +21,7 @@ import 'package:island/widgets/app_scaffold.dart';
|
|||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
import 'package:island/widgets/content/markdown.dart';
|
import 'package:island/widgets/content/markdown.dart';
|
||||||
import 'package:island/widgets/post/post_list.dart';
|
import 'package:island/widgets/post/post_list.dart';
|
||||||
|
import 'package:island/widgets/activity_heatmap.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:island/services/color_extraction.dart';
|
import 'package:island/services/color_extraction.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@@ -222,6 +224,32 @@ class _PublisherBioWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PublisherHeatmapWidget extends StatelessWidget {
|
||||||
|
final AsyncValue<SnHeatmap?> heatmap;
|
||||||
|
final bool forceDense;
|
||||||
|
|
||||||
|
const _PublisherHeatmapWidget({
|
||||||
|
required this.heatmap,
|
||||||
|
this.forceDense = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return heatmap.when(
|
||||||
|
data:
|
||||||
|
(data) =>
|
||||||
|
data != null
|
||||||
|
? ActivityHeatmapWidget(
|
||||||
|
heatmap: data,
|
||||||
|
forceDense: forceDense,
|
||||||
|
).padding(horizontal: 8)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
loading: () => const SizedBox.shrink(),
|
||||||
|
error: (_, _) => const SizedBox.shrink(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _PublisherCategoryTabWidget extends StatelessWidget {
|
class _PublisherCategoryTabWidget extends StatelessWidget {
|
||||||
final TabController categoryTabController;
|
final TabController categoryTabController;
|
||||||
|
|
||||||
@@ -292,6 +320,13 @@ Future<Color?> publisherAppbarForcegroundColor(Ref ref, String pubName) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnHeatmap?> publisherHeatmap(Ref ref, String uname) async {
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
final resp = await apiClient.get('/sphere/publishers/$uname/heatmap');
|
||||||
|
return SnHeatmap.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
class PublisherProfileScreen extends HookConsumerWidget {
|
class PublisherProfileScreen extends HookConsumerWidget {
|
||||||
final String name;
|
final String name;
|
||||||
const PublisherProfileScreen({super.key, required this.name});
|
const PublisherProfileScreen({super.key, required this.name});
|
||||||
@@ -301,6 +336,7 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
|||||||
final publisher = ref.watch(publisherProvider(name));
|
final publisher = ref.watch(publisherProvider(name));
|
||||||
final badges = ref.watch(publisherBadgesProvider(name));
|
final badges = ref.watch(publisherBadgesProvider(name));
|
||||||
final subStatus = ref.watch(publisherSubscriptionStatusProvider(name));
|
final subStatus = ref.watch(publisherSubscriptionStatusProvider(name));
|
||||||
|
final heatmap = ref.watch(publisherHeatmapProvider(name));
|
||||||
final appbarColor = ref.watch(
|
final appbarColor = ref.watch(
|
||||||
publisherAppbarForcegroundColorProvider(name),
|
publisherAppbarForcegroundColorProvider(name),
|
||||||
);
|
);
|
||||||
@@ -446,6 +482,10 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
_PublisherVerificationWidget(data: data),
|
_PublisherVerificationWidget(data: data),
|
||||||
_PublisherBioWidget(data: data),
|
_PublisherBioWidget(data: data),
|
||||||
|
_PublisherHeatmapWidget(
|
||||||
|
heatmap: heatmap,
|
||||||
|
forceDense: true,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -517,6 +557,9 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: _PublisherBioWidget(data: data),
|
child: _PublisherBioWidget(data: data),
|
||||||
),
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: _PublisherHeatmapWidget(heatmap: heatmap),
|
||||||
|
),
|
||||||
SliverPostList(pubName: name, pinned: true),
|
SliverPostList(pubName: name, pinned: true),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: _PublisherCategoryTabWidget(
|
child: _PublisherCategoryTabWidget(
|
||||||
|
|||||||
@@ -530,5 +530,126 @@ class _PublisherAppbarForcegroundColorProviderElement
|
|||||||
(origin as PublisherAppbarForcegroundColorProvider).pubName;
|
(origin as PublisherAppbarForcegroundColorProvider).pubName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _$publisherHeatmapHash() => r'86db275ce3861a2855b5ec35fbfef85fc47b23a6';
|
||||||
|
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
@ProviderFor(publisherHeatmap)
|
||||||
|
const publisherHeatmapProvider = PublisherHeatmapFamily();
|
||||||
|
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
class PublisherHeatmapFamily extends Family<AsyncValue<SnHeatmap?>> {
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
const PublisherHeatmapFamily();
|
||||||
|
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
PublisherHeatmapProvider call(String uname) {
|
||||||
|
return PublisherHeatmapProvider(uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
PublisherHeatmapProvider getProviderOverride(
|
||||||
|
covariant PublisherHeatmapProvider provider,
|
||||||
|
) {
|
||||||
|
return call(provider.uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
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'publisherHeatmapProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
class PublisherHeatmapProvider extends AutoDisposeFutureProvider<SnHeatmap?> {
|
||||||
|
/// See also [publisherHeatmap].
|
||||||
|
PublisherHeatmapProvider(String uname)
|
||||||
|
: this._internal(
|
||||||
|
(ref) => publisherHeatmap(ref as PublisherHeatmapRef, uname),
|
||||||
|
from: publisherHeatmapProvider,
|
||||||
|
name: r'publisherHeatmapProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$publisherHeatmapHash,
|
||||||
|
dependencies: PublisherHeatmapFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
PublisherHeatmapFamily._allTransitiveDependencies,
|
||||||
|
uname: uname,
|
||||||
|
);
|
||||||
|
|
||||||
|
PublisherHeatmapProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.uname,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String uname;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<SnHeatmap?> Function(PublisherHeatmapRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: PublisherHeatmapProvider._internal(
|
||||||
|
(ref) => create(ref as PublisherHeatmapRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
uname: uname,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<SnHeatmap?> createElement() {
|
||||||
|
return _PublisherHeatmapProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is PublisherHeatmapProvider && other.uname == uname;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, uname.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin PublisherHeatmapRef on AutoDisposeFutureProviderRef<SnHeatmap?> {
|
||||||
|
/// The parameter `uname` of this provider.
|
||||||
|
String get uname;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PublisherHeatmapProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<SnHeatmap?>
|
||||||
|
with PublisherHeatmapRef {
|
||||||
|
_PublisherHeatmapProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get uname => (origin as PublisherHeatmapProvider).uname;
|
||||||
|
}
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||||
|
|||||||
@@ -784,6 +784,22 @@ class SettingsScreen extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Disable animation settings
|
||||||
|
ListTile(
|
||||||
|
minLeadingWidth: 48,
|
||||||
|
title: Text('settingsDisableAnimation').tr(),
|
||||||
|
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
||||||
|
leading: const Icon(Symbols.animation),
|
||||||
|
trailing: Switch(
|
||||||
|
value: settings.disableAnimation,
|
||||||
|
onChanged: (value) {
|
||||||
|
ref
|
||||||
|
.read(appSettingsNotifierProvider.notifier)
|
||||||
|
.setDisableAnimation(value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Desktop-specific settings
|
// Desktop-specific settings
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -11,6 +12,16 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
|
|
||||||
final currentRouteProvider = StateProvider<String?>((ref) => null);
|
final currentRouteProvider = StateProvider<String?>((ref) => null);
|
||||||
|
|
||||||
|
const kWideScreenRouteStart = 4;
|
||||||
|
const kTabRoutes = [
|
||||||
|
'/',
|
||||||
|
'/chat',
|
||||||
|
'/realms',
|
||||||
|
'/account',
|
||||||
|
'/creators',
|
||||||
|
'/developers',
|
||||||
|
];
|
||||||
|
|
||||||
class TabsScreen extends HookConsumerWidget {
|
class TabsScreen extends HookConsumerWidget {
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
const TabsScreen({super.key, this.child});
|
const TabsScreen({super.key, this.child});
|
||||||
@@ -32,6 +43,8 @@ class TabsScreen extends HookConsumerWidget {
|
|||||||
notificationUnreadCountNotifierProvider,
|
notificationUnreadCountNotifierProvider,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final wideScreen = isWideScreen(context);
|
||||||
|
|
||||||
final destinations = [
|
final destinations = [
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
label: 'explore'.tr(),
|
label: 'explore'.tr(),
|
||||||
@@ -50,19 +63,30 @@ class TabsScreen extends HookConsumerWidget {
|
|||||||
child: const Icon(Symbols.account_circle),
|
child: const Icon(Symbols.account_circle),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (wideScreen)
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'creatorHub'.tr(),
|
||||||
|
icon: const Icon(Symbols.ink_pen),
|
||||||
|
),
|
||||||
|
if (wideScreen)
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'developerHub'.tr(),
|
||||||
|
icon: const Icon(Symbols.data_object),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
final routes = ['/', '/chat', '/realms', '/account'];
|
|
||||||
|
|
||||||
int getCurrentIndex() {
|
int getCurrentIndex() {
|
||||||
if (currentLocation.startsWith('/chat')) return 1;
|
if (currentLocation == '/') return 0;
|
||||||
if (currentLocation.startsWith('/realms')) return 2;
|
final idx = kTabRoutes.indexWhere(
|
||||||
if (currentLocation.startsWith('/account')) return 3;
|
(p) => currentLocation.startsWith(p),
|
||||||
return 0; // Default to explore
|
1,
|
||||||
|
);
|
||||||
|
final value = math.max(idx, 0);
|
||||||
|
return math.min(value, destinations.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDestinationSelected(int index) {
|
void onDestinationSelected(int index) {
|
||||||
context.go(routes[index]);
|
context.go(kTabRoutes[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
final currentIndex = getCurrentIndex();
|
final currentIndex = getCurrentIndex();
|
||||||
@@ -102,6 +126,7 @@ class TabsScreen extends HookConsumerWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
body: ClipRRect(
|
body: ClipRRect(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.circular(16),
|
topLeft: Radius.circular(16),
|
||||||
|
|||||||
40
lib/services/autocomplete_service.dart
Normal file
40
lib/services/autocomplete_service.dart
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:island/models/autocomplete_response.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
|
||||||
|
final autocompleteServiceProvider = Provider<AutocompleteService>((ref) {
|
||||||
|
final dio = ref.watch(apiClientProvider);
|
||||||
|
return AutocompleteService(dio);
|
||||||
|
});
|
||||||
|
|
||||||
|
class AutocompleteService {
|
||||||
|
final Dio _client;
|
||||||
|
|
||||||
|
AutocompleteService(this._client);
|
||||||
|
|
||||||
|
Future<List<AutocompleteSuggestion>> getSuggestions(
|
||||||
|
String roomId,
|
||||||
|
String content,
|
||||||
|
) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
'/sphere/chat/$roomId/autocomplete',
|
||||||
|
data: {'content': content},
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = response.data as List<dynamic>;
|
||||||
|
return data.map((json) => AutocompleteSuggestion.fromJson(json)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<AutocompleteSuggestion>> getGeneralSuggestions(
|
||||||
|
String content,
|
||||||
|
) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
'/sphere/autocomplete',
|
||||||
|
data: {'content': content},
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = response.data as List<dynamic>;
|
||||||
|
return data.map((json) => AutocompleteSuggestion.fromJson(json)).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,25 @@ import 'package:island/models/wallet.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';
|
||||||
|
|
||||||
|
const Map<String, Color> kUsernamePlainColors = {
|
||||||
|
'red': Colors.red,
|
||||||
|
'blue': Colors.blue,
|
||||||
|
'green': Colors.green,
|
||||||
|
'yellow': Colors.yellow,
|
||||||
|
'purple': Colors.purple,
|
||||||
|
'orange': Colors.orange,
|
||||||
|
'pink': Colors.pink,
|
||||||
|
'cyan': Colors.cyan,
|
||||||
|
'lime': Colors.lime,
|
||||||
|
'indigo': Colors.indigo,
|
||||||
|
'teal': Colors.teal,
|
||||||
|
'amber': Colors.amber,
|
||||||
|
'brown': Colors.brown,
|
||||||
|
'grey': Colors.grey,
|
||||||
|
'black': Colors.black,
|
||||||
|
'white': Colors.white,
|
||||||
|
};
|
||||||
|
|
||||||
const kVerificationMarkColors = [
|
const kVerificationMarkColors = [
|
||||||
Colors.teal,
|
Colors.teal,
|
||||||
Colors.blue,
|
Colors.blue,
|
||||||
@@ -17,12 +36,175 @@ const kVerificationMarkColors = [
|
|||||||
class AccountName extends StatelessWidget {
|
class AccountName extends StatelessWidget {
|
||||||
final SnAccount account;
|
final SnAccount account;
|
||||||
final TextStyle? style;
|
final TextStyle? style;
|
||||||
const AccountName({super.key, required this.account, this.style});
|
final bool ignorePermissions;
|
||||||
|
const AccountName({
|
||||||
|
super.key,
|
||||||
|
required this.account,
|
||||||
|
this.style,
|
||||||
|
this.ignorePermissions = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Alignment _parseGradientDirection(String direction) {
|
||||||
|
switch (direction) {
|
||||||
|
case 'to right':
|
||||||
|
return Alignment.centerLeft;
|
||||||
|
case 'to left':
|
||||||
|
return Alignment.centerRight;
|
||||||
|
case 'to bottom':
|
||||||
|
return Alignment.topCenter;
|
||||||
|
case 'to top':
|
||||||
|
return Alignment.bottomCenter;
|
||||||
|
case 'to bottom right':
|
||||||
|
return Alignment.topLeft;
|
||||||
|
case 'to bottom left':
|
||||||
|
return Alignment.topRight;
|
||||||
|
case 'to top right':
|
||||||
|
return Alignment.bottomLeft;
|
||||||
|
case 'to top left':
|
||||||
|
return Alignment.bottomRight;
|
||||||
|
default:
|
||||||
|
return Alignment.centerLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Alignment _parseGradientEnd(String direction) {
|
||||||
|
switch (direction) {
|
||||||
|
case 'to right':
|
||||||
|
return Alignment.centerRight;
|
||||||
|
case 'to left':
|
||||||
|
return Alignment.centerLeft;
|
||||||
|
case 'to bottom':
|
||||||
|
return Alignment.bottomCenter;
|
||||||
|
case 'to top':
|
||||||
|
return Alignment.topCenter;
|
||||||
|
case 'to bottom right':
|
||||||
|
return Alignment.bottomRight;
|
||||||
|
case 'to bottom left':
|
||||||
|
return Alignment.bottomLeft;
|
||||||
|
case 'to top right':
|
||||||
|
return Alignment.topRight;
|
||||||
|
case 'to top left':
|
||||||
|
return Alignment.topLeft;
|
||||||
|
default:
|
||||||
|
return Alignment.centerRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var nameStyle = (style ?? TextStyle());
|
var nameStyle = (style ?? TextStyle());
|
||||||
if (account.perkSubscription != null) {
|
|
||||||
|
// Apply username color based on membership tier and custom settings
|
||||||
|
if (account.profile.usernameColor != null) {
|
||||||
|
final usernameColor = account.profile.usernameColor!;
|
||||||
|
final tier = account.perkSubscription?.identifier;
|
||||||
|
|
||||||
|
// Check tier restrictions
|
||||||
|
final canUseCustomColor =
|
||||||
|
ignorePermissions ||
|
||||||
|
switch (tier) {
|
||||||
|
'solian.stellar.primary' =>
|
||||||
|
usernameColor.type == 'plain' &&
|
||||||
|
kUsernamePlainColors.containsKey(usernameColor.value),
|
||||||
|
'solian.stellar.nova' => usernameColor.type == 'plain',
|
||||||
|
'solian.stellar.supernova' => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (canUseCustomColor) {
|
||||||
|
if (usernameColor.type == 'plain') {
|
||||||
|
// Plain color
|
||||||
|
Color? color;
|
||||||
|
if (kUsernamePlainColors.containsKey(usernameColor.value)) {
|
||||||
|
color = kUsernamePlainColors[usernameColor.value];
|
||||||
|
} else if (usernameColor.value != null) {
|
||||||
|
// Try to parse hex color
|
||||||
|
try {
|
||||||
|
color = Color(
|
||||||
|
int.parse(
|
||||||
|
usernameColor.value!.replaceFirst('#', ''),
|
||||||
|
radix: 16,
|
||||||
|
) +
|
||||||
|
0xFF000000,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// Invalid hex, ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (color != null) {
|
||||||
|
nameStyle = nameStyle.copyWith(color: color);
|
||||||
|
}
|
||||||
|
} else if (usernameColor.type == 'gradient' &&
|
||||||
|
usernameColor.colors != null &&
|
||||||
|
usernameColor.colors!.isNotEmpty) {
|
||||||
|
// Gradient - use ShaderMask for text gradient
|
||||||
|
final colors = <Color>[];
|
||||||
|
for (final colorStr in usernameColor.colors!) {
|
||||||
|
Color? color;
|
||||||
|
if (kUsernamePlainColors.containsKey(colorStr)) {
|
||||||
|
color = kUsernamePlainColors[colorStr];
|
||||||
|
} else {
|
||||||
|
// Try to parse hex color
|
||||||
|
try {
|
||||||
|
color = Color(
|
||||||
|
int.parse(colorStr.replaceFirst('#', ''), radix: 16) +
|
||||||
|
0xFF000000,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// Invalid hex, skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (color != null) {
|
||||||
|
colors.add(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colors.isNotEmpty) {
|
||||||
|
final gradient = LinearGradient(
|
||||||
|
colors: colors,
|
||||||
|
begin: _parseGradientDirection(
|
||||||
|
usernameColor.direction ?? 'to right',
|
||||||
|
),
|
||||||
|
end: _parseGradientEnd(usernameColor.direction ?? 'to right'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: ShaderMask(
|
||||||
|
shaderCallback: (bounds) => gradient.createShader(bounds),
|
||||||
|
child: Text(
|
||||||
|
account.nick,
|
||||||
|
style: nameStyle.copyWith(color: Colors.white),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (account.perkSubscription != null)
|
||||||
|
StellarMembershipMark(membership: account.perkSubscription!),
|
||||||
|
if (account.profile.verification != null)
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (account.perkSubscription != null) {
|
||||||
|
// Default membership colors if no custom color is set
|
||||||
nameStyle = nameStyle.copyWith(
|
nameStyle = nameStyle.copyWith(
|
||||||
color: (switch (account.perkSubscription!.identifier) {
|
color: (switch (account.perkSubscription!.identifier) {
|
||||||
'solian.stellar.primary' => Colors.blueAccent,
|
'solian.stellar.primary' => Colors.blueAccent,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
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:gap/gap.dart';
|
||||||
@@ -6,14 +5,24 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:island/pods/event_calendar.dart';
|
import 'package:island/pods/event_calendar.dart';
|
||||||
import 'package:island/screens/account/profile.dart';
|
import 'package:island/screens/account/profile.dart';
|
||||||
import 'package:island/widgets/account/account_nameplate.dart';
|
import 'package:island/widgets/account/account_nameplate.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
|
||||||
import 'package:island/widgets/account/event_calendar.dart';
|
import 'package:island/widgets/account/event_calendar.dart';
|
||||||
import 'package:island/widgets/account/fortune_graph.dart';
|
import 'package:island/widgets/account/fortune_graph.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
class EventCalanderScreen extends HookConsumerWidget {
|
/// A reusable content widget for event calendar that can be used in screens or sheets
|
||||||
|
/// This widget manages the calendar state and displays the calendar and fortune graph
|
||||||
|
class EventCalendarContent extends HookConsumerWidget {
|
||||||
|
/// Username to fetch calendar for, null means current user ('me')
|
||||||
final String name;
|
final String name;
|
||||||
const EventCalanderScreen({super.key, required this.name});
|
|
||||||
|
/// Whether this is being displayed in a sheet (affects layout)
|
||||||
|
final bool isSheet;
|
||||||
|
|
||||||
|
const EventCalendarContent({
|
||||||
|
super.key,
|
||||||
|
required this.name,
|
||||||
|
this.isSheet = false,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@@ -45,13 +54,37 @@ class EventCalanderScreen extends HookConsumerWidget {
|
|||||||
selectedDay.value = day;
|
selectedDay.value = day;
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffold(
|
if (isSheet) {
|
||||||
isNoBackground: false,
|
// Sheet layout - simplified, no app bar, scrollable content
|
||||||
appBar: AppBar(
|
return SingleChildScrollView(
|
||||||
leading: const PageBackButton(),
|
child: Column(
|
||||||
title: Text('eventCalander').tr(),
|
children: [
|
||||||
),
|
// Use the reusable EventCalendarWidget
|
||||||
body: SingleChildScrollView(
|
EventCalendarWidget(
|
||||||
|
events: events,
|
||||||
|
initialDate: now,
|
||||||
|
showEventDetails: true,
|
||||||
|
onMonthChanged: onMonthChanged,
|
||||||
|
onDaySelected: onDaySelected,
|
||||||
|
),
|
||||||
|
|
||||||
|
// Add the fortune graph widget
|
||||||
|
const Divider(height: 1),
|
||||||
|
FortuneGraphWidget(
|
||||||
|
events: events,
|
||||||
|
onPointSelected: onDaySelected,
|
||||||
|
).padding(horizontal: 8, vertical: 4),
|
||||||
|
|
||||||
|
// Show user profile if viewing someone else's calendar
|
||||||
|
if (name != 'me' && user.value != null)
|
||||||
|
AccountNameplate(name: name),
|
||||||
|
Gap(MediaQuery.of(context).padding.bottom + 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Screen layout - with responsive design
|
||||||
|
return SingleChildScrollView(
|
||||||
child:
|
child:
|
||||||
MediaQuery.of(context).size.width > 480
|
MediaQuery.of(context).size.width > 480
|
||||||
? ConstrainedBox(
|
? ConstrainedBox(
|
||||||
@@ -111,7 +144,7 @@ class EventCalanderScreen extends HookConsumerWidget {
|
|||||||
Gap(MediaQuery.of(context).padding.bottom + 16),
|
Gap(MediaQuery.of(context).padding.bottom + 16),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,10 +76,17 @@ class EventDetailsWidget extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if ((getActivityTitle(status.label, status.meta) ?? status.label).isNotEmpty)
|
if ((getActivityTitle(status.label, status.meta) ??
|
||||||
Text(getActivityTitle(status.label, status.meta) ?? status.label),
|
status.label)
|
||||||
|
.isNotEmpty)
|
||||||
|
Text(
|
||||||
|
getActivityTitle(status.label, status.meta) ??
|
||||||
|
status.label,
|
||||||
|
),
|
||||||
if (getActivitySubtitle(status.meta) != null)
|
if (getActivitySubtitle(status.meta) != null)
|
||||||
Text(getActivitySubtitle(status.meta)!).fontSize(11).opacity(0.8),
|
Text(
|
||||||
|
getActivitySubtitle(status.meta)!,
|
||||||
|
).fontSize(11).opacity(0.8),
|
||||||
Text(
|
Text(
|
||||||
'${status.createdAt.formatSystem()} - ${status.clearedAt?.formatSystem() ?? 'present'.tr()}',
|
'${status.createdAt.formatSystem()} - ${status.clearedAt?.formatSystem() ?? 'present'.tr()}',
|
||||||
).fontSize(11).opacity(0.8),
|
).fontSize(11).opacity(0.8),
|
||||||
@@ -92,7 +99,7 @@ class EventDetailsWidget extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (event?.checkInResult == null && (event?.statuses.isEmpty ?? true))
|
if (event?.checkInResult == null && (event?.statuses.isEmpty ?? true))
|
||||||
Text('eventCalanderEmpty').tr(),
|
Text('eventCalendarEmpty').tr(),
|
||||||
],
|
],
|
||||||
).padding(vertical: 24, horizontal: 24);
|
).padding(vertical: 24, horizontal: 24);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/activity.dart';
|
import 'package:island/models/activity.dart';
|
||||||
|
import 'package:island/widgets/account/event_calendar_content.dart';
|
||||||
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
/// A widget that displays a graph of fortune levels over time
|
/// A widget that displays a graph of fortune levels over time
|
||||||
@@ -24,7 +25,7 @@ class FortuneGraphWidget extends HookConsumerWidget {
|
|||||||
/// Callback when a point is selected
|
/// Callback when a point is selected
|
||||||
final void Function(DateTime)? onPointSelected;
|
final void Function(DateTime)? onPointSelected;
|
||||||
|
|
||||||
final String? eventCalanderUser;
|
final String? eventCalandarUser;
|
||||||
|
|
||||||
final EdgeInsets? margin;
|
final EdgeInsets? margin;
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ class FortuneGraphWidget extends HookConsumerWidget {
|
|||||||
this.maxWidth = double.infinity,
|
this.maxWidth = double.infinity,
|
||||||
this.height = 180,
|
this.height = 180,
|
||||||
this.onPointSelected,
|
this.onPointSelected,
|
||||||
this.eventCalanderUser,
|
this.eventCalandarUser,
|
||||||
this.margin,
|
this.margin,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ class FortuneGraphWidget extends HookConsumerWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text('fortuneGraph').tr().fontSize(18).bold(),
|
Text('fortuneGraph').tr().fontSize(18).bold(),
|
||||||
if (eventCalanderUser != null)
|
if (eventCalandarUser != null)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.calendar_month, size: 20),
|
icon: const Icon(Icons.calendar_month, size: 20),
|
||||||
visualDensity: const VisualDensity(
|
visualDensity: const VisualDensity(
|
||||||
@@ -69,9 +70,17 @@ class FortuneGraphWidget extends HookConsumerWidget {
|
|||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: const BoxConstraints(),
|
constraints: const BoxConstraints(),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(
|
showModalBottomSheet(
|
||||||
'accountCalendar',
|
context: context,
|
||||||
pathParameters: {'name': eventCalanderUser!},
|
isScrollControlled: true,
|
||||||
|
builder:
|
||||||
|
(context) => SheetScaffold(
|
||||||
|
titleText: 'eventCalendar'.tr(),
|
||||||
|
child: EventCalendarContent(
|
||||||
|
name: eventCalandarUser!,
|
||||||
|
isSheet: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
282
lib/widgets/activity_heatmap.dart
Normal file
282
lib/widgets/activity_heatmap.dart
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:fl_heatmap/fl_heatmap.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/heatmap.dart';
|
||||||
|
import '../services/responsive.dart';
|
||||||
|
|
||||||
|
/// A reusable heatmap widget for displaying activity data in GitHub-style layout.
|
||||||
|
/// Shows exactly 365 days (wide screen) or 90 days (non-wide screen) of data ending at the current date.
|
||||||
|
class ActivityHeatmapWidget extends HookConsumerWidget {
|
||||||
|
final SnHeatmap heatmap;
|
||||||
|
final bool forceDense;
|
||||||
|
|
||||||
|
const ActivityHeatmapWidget({
|
||||||
|
super.key,
|
||||||
|
required this.heatmap,
|
||||||
|
this.forceDense = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final selectedItem = useState<HeatmapItem?>(null);
|
||||||
|
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
|
final isWide = isWideScreen(context);
|
||||||
|
final days = (isWide && !forceDense) ? 365 : 90;
|
||||||
|
|
||||||
|
// Start from exactly the selected days ago
|
||||||
|
final startDate = now.subtract(Duration(days: days));
|
||||||
|
// End at current date
|
||||||
|
final endDate = now;
|
||||||
|
|
||||||
|
// Find monday of the week containing start date
|
||||||
|
final startMonday = startDate.subtract(
|
||||||
|
Duration(days: startDate.weekday - 1),
|
||||||
|
);
|
||||||
|
// Find sunday of the week containing end date
|
||||||
|
final endSunday = endDate.add(Duration(days: 7 - endDate.weekday));
|
||||||
|
|
||||||
|
// Generate weeks to cover the selected date range
|
||||||
|
final weeks = <DateTime>[];
|
||||||
|
var current = startMonday;
|
||||||
|
while (current.isBefore(endSunday) || current.isAtSameMomentAs(endSunday)) {
|
||||||
|
weeks.add(current);
|
||||||
|
current = current.add(const Duration(days: 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create data map for all dates in the range
|
||||||
|
final dataMap = <DateTime, double>{};
|
||||||
|
for (final week in weeks) {
|
||||||
|
for (var i = 0; i < 7; i++) {
|
||||||
|
final date = week.add(Duration(days: i));
|
||||||
|
// Only include dates within our selected range
|
||||||
|
if (date.isAfter(startDate.subtract(const Duration(days: 1))) &&
|
||||||
|
date.isBefore(endDate.add(const Duration(days: 1)))) {
|
||||||
|
final item = heatmap.items.firstWhere(
|
||||||
|
(e) =>
|
||||||
|
e.date.year == date.year &&
|
||||||
|
e.date.month == date.month &&
|
||||||
|
e.date.day == date.day,
|
||||||
|
orElse: () => SnHeatmapItem(date: date, count: 0),
|
||||||
|
);
|
||||||
|
dataMap[date] = item.count.toDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate month labels for the top
|
||||||
|
final monthLabels = <String>[];
|
||||||
|
final monthPositions = <int>[];
|
||||||
|
final processedMonths =
|
||||||
|
<String>{}; // Track processed months to avoid duplicates
|
||||||
|
|
||||||
|
for (final week in weeks) {
|
||||||
|
final monthKey = '${week.year}-${week.month.toString().padLeft(2, '0')}';
|
||||||
|
|
||||||
|
// Only process each month once
|
||||||
|
if (!processedMonths.contains(monthKey)) {
|
||||||
|
processedMonths.add(monthKey);
|
||||||
|
|
||||||
|
// Find which week this month starts in
|
||||||
|
final firstDayOfMonth = DateTime(week.year, week.month, 1);
|
||||||
|
final monthStartMonday = firstDayOfMonth.subtract(
|
||||||
|
Duration(days: firstDayOfMonth.weekday - 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
final monthStartWeekIndex = weeks.indexWhere(
|
||||||
|
(w) =>
|
||||||
|
w.year == monthStartMonday.year &&
|
||||||
|
w.month == monthStartMonday.month &&
|
||||||
|
w.day == monthStartMonday.day,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (monthStartWeekIndex != -1) {
|
||||||
|
monthLabels.add(_getMonthAbbreviation(week.month));
|
||||||
|
monthPositions.add(monthStartWeekIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final heatmapData = HeatmapData(
|
||||||
|
rows: [
|
||||||
|
'Mon',
|
||||||
|
'Tue',
|
||||||
|
'Wed',
|
||||||
|
'Thu',
|
||||||
|
'Fri',
|
||||||
|
'Sat',
|
||||||
|
'Sun',
|
||||||
|
], // Days of week vertically
|
||||||
|
columns:
|
||||||
|
weeks
|
||||||
|
.map(
|
||||||
|
(w) =>
|
||||||
|
'${w.year}-${w.month.toString().padLeft(2, '0')}-${w.day.toString().padLeft(2, '0')}',
|
||||||
|
)
|
||||||
|
.toList(), // Weeks horizontally
|
||||||
|
items: [
|
||||||
|
for (int day = 0; day < 7; day++) // For each day of week (Mon-Sun)
|
||||||
|
for (final week in weeks) // For each week
|
||||||
|
HeatmapItem(
|
||||||
|
value: dataMap[week.add(Duration(days: day))] ?? 0.0,
|
||||||
|
unit: heatmap.unit,
|
||||||
|
xAxisLabel:
|
||||||
|
'${week.year}-${week.month.toString().padLeft(2, '0')}-${week.day.toString().padLeft(2, '0')}',
|
||||||
|
yAxisLabel:
|
||||||
|
day == 0
|
||||||
|
? 'Mon'
|
||||||
|
: day == 1
|
||||||
|
? 'Tue'
|
||||||
|
: day == 2
|
||||||
|
? 'Wed'
|
||||||
|
: day == 3
|
||||||
|
? 'Thu'
|
||||||
|
: day == 4
|
||||||
|
? 'Fri'
|
||||||
|
: day == 5
|
||||||
|
? 'Sat'
|
||||||
|
: 'Sun',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'activityHeatmap',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
).tr(),
|
||||||
|
const Gap(8),
|
||||||
|
// Month labels row
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 30), // Space for day labels
|
||||||
|
...monthLabels.asMap().entries.map((entry) {
|
||||||
|
final month = entry.value;
|
||||||
|
|
||||||
|
return Expanded(
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
month,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Heatmap(
|
||||||
|
heatmapData: heatmapData,
|
||||||
|
rowsVisible: 7,
|
||||||
|
showXAxisLabels: false,
|
||||||
|
onItemSelectedListener: (item) {
|
||||||
|
selectedItem.value = item;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
// Legend
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if (selectedItem.value != null)
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: selectedItem.value!.value.toInt().toString(),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall
|
||||||
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: ' activities on ',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: _formatDate(
|
||||||
|
selectedItem.value!.xAxisLabel ?? '',
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
'Less',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
// Color indicators (light to dark green)
|
||||||
|
...[
|
||||||
|
Colors.green.withOpacity(0.2),
|
||||||
|
Colors.green.withOpacity(0.4),
|
||||||
|
Colors.green.withOpacity(0.6),
|
||||||
|
Colors.green.withOpacity(0.8),
|
||||||
|
Colors.green,
|
||||||
|
].map(
|
||||||
|
(color) => Container(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 1),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
'More',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getMonthAbbreviation(int month) {
|
||||||
|
const monthNames = [
|
||||||
|
'Jan',
|
||||||
|
'Feb',
|
||||||
|
'Mar',
|
||||||
|
'Apr',
|
||||||
|
'May',
|
||||||
|
'Jun',
|
||||||
|
'Jul',
|
||||||
|
'Aug',
|
||||||
|
'Sep',
|
||||||
|
'Oct',
|
||||||
|
'Nov',
|
||||||
|
'Dec',
|
||||||
|
];
|
||||||
|
return monthNames[month - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDate(String dateString) {
|
||||||
|
try {
|
||||||
|
final date = DateTime.parse(dateString);
|
||||||
|
final monthAbbrev = _getMonthAbbreviation(date.month);
|
||||||
|
return '$monthAbbrev ${date.day}, ${date.year}';
|
||||||
|
} catch (e) {
|
||||||
|
return dateString; // Fallback to original string if parsing fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ 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/services.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.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';
|
||||||
@@ -68,132 +69,154 @@ class WindowScaffold extends HookConsumerWidget {
|
|||||||
return null;
|
return null;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
final pageButtonActions = [
|
final router = ref.watch(routerProvider);
|
||||||
IconButton(
|
|
||||||
icon: Icon(Symbols.keyboard_arrow_left),
|
final pageActionsButton = [
|
||||||
onPressed:
|
if (router.canPop())
|
||||||
ref.watch(routerProvider).canPop()
|
IconButton(
|
||||||
? () => ref.read(routerProvider).pop()
|
icon: Icon(Symbols.close),
|
||||||
: null,
|
onPressed: router.canPop() ? () => router.pop() : null,
|
||||||
iconSize: 16,
|
iconSize: 16,
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
constraints: BoxConstraints(),
|
constraints: BoxConstraints(),
|
||||||
color: Theme.of(context).iconTheme.color,
|
color: Theme.of(context).iconTheme.color,
|
||||||
),
|
)
|
||||||
IconButton(
|
else
|
||||||
icon: Icon(Symbols.home),
|
IconButton(
|
||||||
onPressed: () => ref.read(routerProvider).go('/'),
|
icon: Icon(Symbols.home),
|
||||||
iconSize: 16,
|
onPressed: () => router.go('/'),
|
||||||
padding: EdgeInsets.all(8),
|
iconSize: 16,
|
||||||
constraints: BoxConstraints(),
|
padding: EdgeInsets.all(8),
|
||||||
color: Theme.of(context).iconTheme.color,
|
constraints: BoxConstraints(),
|
||||||
),
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!kIsWeb &&
|
if (!kIsWeb &&
|
||||||
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
||||||
return Material(
|
return Shortcuts(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
shortcuts: <LogicalKeySet, Intent>{
|
||||||
child: Stack(
|
LogicalKeySet(LogicalKeyboardKey.escape): const PopIntent(),
|
||||||
fit: StackFit.expand,
|
},
|
||||||
children: [
|
child: Actions(
|
||||||
Column(
|
actions: <Type, Action<Intent>>{PopIntent: PopAction(ref)},
|
||||||
|
child: Material(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
DragToMoveArea(
|
Column(
|
||||||
child:
|
children: [
|
||||||
Platform.isMacOS
|
DragToMoveArea(
|
||||||
? Stack(
|
child:
|
||||||
alignment: Alignment.center,
|
Platform.isMacOS
|
||||||
children: [
|
? Stack(
|
||||||
Row(
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (Platform.isMacOS)
|
if (isWideScreen(context))
|
||||||
const SizedBox(width: 80),
|
Row(
|
||||||
...pageButtonActions,
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
...pageActionsButton,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else
|
||||||
|
SizedBox(height: 32),
|
||||||
|
Text(
|
||||||
|
'Solar Network',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
Theme.of(context).brightness ==
|
||||||
|
Brightness.dark
|
||||||
|
? 'assets/icons/icon-dark.png'
|
||||||
|
: 'assets/icons/icon.png',
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'Solar Network',
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 12, vertical: 5),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Symbols.minimize),
|
||||||
|
onPressed: () => windowManager.minimize(),
|
||||||
|
iconSize: 16,
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
isMaximized.value
|
||||||
|
? Symbols.fullscreen_exit
|
||||||
|
: Symbols.fullscreen,
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
if (await windowManager.isMaximized()) {
|
||||||
|
windowManager.restore();
|
||||||
|
} else {
|
||||||
|
windowManager.maximize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
iconSize: 16,
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Symbols.close),
|
||||||
|
onPressed: () => windowManager.hide(),
|
||||||
|
iconSize: 16,
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
'Solar Network',
|
Expanded(child: child),
|
||||||
textAlign: TextAlign.center,
|
],
|
||||||
style: TextStyle(
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
Theme.of(context).brightness ==
|
|
||||||
Brightness.dark
|
|
||||||
? 'assets/icons/icon-dark.png'
|
|
||||||
: 'assets/icons/icon.png',
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
'Solar Network',
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padding(horizontal: 12, vertical: 5),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Symbols.minimize),
|
|
||||||
onPressed: () => windowManager.minimize(),
|
|
||||||
iconSize: 16,
|
|
||||||
padding: EdgeInsets.all(8),
|
|
||||||
constraints: BoxConstraints(),
|
|
||||||
color: Theme.of(context).iconTheme.color,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
isMaximized.value
|
|
||||||
? Symbols.fullscreen_exit
|
|
||||||
: Symbols.fullscreen,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
if (await windowManager.isMaximized()) {
|
|
||||||
windowManager.restore();
|
|
||||||
} else {
|
|
||||||
windowManager.maximize();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
iconSize: 16,
|
|
||||||
padding: EdgeInsets.all(8),
|
|
||||||
constraints: BoxConstraints(),
|
|
||||||
color: Theme.of(context).iconTheme.color,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Symbols.close),
|
|
||||||
onPressed: () => windowManager.hide(),
|
|
||||||
iconSize: 16,
|
|
||||||
padding: EdgeInsets.all(8),
|
|
||||||
constraints: BoxConstraints(),
|
|
||||||
color: Theme.of(context).iconTheme.color,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(child: child),
|
_WebSocketIndicator(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
_WebSocketIndicator(),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Stack(
|
return Shortcuts(
|
||||||
fit: StackFit.expand,
|
shortcuts: <LogicalKeySet, Intent>{
|
||||||
children: [Positioned.fill(child: child), _WebSocketIndicator()],
|
LogicalKeySet(LogicalKeyboardKey.escape): const PopIntent(),
|
||||||
|
},
|
||||||
|
child: Actions(
|
||||||
|
actions: <Type, Action<Intent>>{PopIntent: PopAction(ref)},
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [Positioned.fill(child: child), _WebSocketIndicator()],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,48 +314,38 @@ class AppScaffold extends HookConsumerWidget {
|
|||||||
|
|
||||||
final noBackground = isNoBackground ?? isWideScreen(context);
|
final noBackground = isNoBackground ?? isWideScreen(context);
|
||||||
|
|
||||||
final content = Column(
|
final builtWidget = Focus(
|
||||||
children: [
|
focusNode: focusNode,
|
||||||
IgnorePointer(
|
child: Scaffold(
|
||||||
child: SizedBox(height: appBar != null ? appBarHeight + safeTop : 0),
|
extendBody: extendBody ?? true,
|
||||||
),
|
extendBodyBehindAppBar: true,
|
||||||
if (body != null) Expanded(child: body!),
|
backgroundColor: Colors.transparent,
|
||||||
],
|
body: Column(
|
||||||
);
|
children: [
|
||||||
|
IgnorePointer(
|
||||||
return Shortcuts(
|
child: SizedBox(
|
||||||
shortcuts: <LogicalKeySet, Intent>{
|
height: appBar != null ? appBarHeight + safeTop : 0,
|
||||||
LogicalKeySet(LogicalKeyboardKey.escape): const PopIntent(),
|
),
|
||||||
},
|
),
|
||||||
child: Actions(
|
if (body != null) Expanded(child: body!),
|
||||||
actions: <Type, Action<Intent>>{PopIntent: PopAction(context)},
|
],
|
||||||
child: Focus(
|
|
||||||
focusNode: focusNode,
|
|
||||||
child: Scaffold(
|
|
||||||
extendBody: extendBody ?? true,
|
|
||||||
extendBodyBehindAppBar: true,
|
|
||||||
backgroundColor:
|
|
||||||
noBackground
|
|
||||||
? Colors.transparent
|
|
||||||
: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
body:
|
|
||||||
noBackground
|
|
||||||
? content
|
|
||||||
: AppBackground(isRoot: true, child: content),
|
|
||||||
appBar: appBar,
|
|
||||||
bottomNavigationBar: bottomNavigationBar,
|
|
||||||
bottomSheet: bottomSheet,
|
|
||||||
drawer: drawer,
|
|
||||||
endDrawer: endDrawer,
|
|
||||||
floatingActionButton: floatingActionButton,
|
|
||||||
floatingActionButtonAnimator: floatingActionButtonAnimator,
|
|
||||||
floatingActionButtonLocation: TabbedFabLocation(context),
|
|
||||||
onDrawerChanged: onDrawerChanged,
|
|
||||||
onEndDrawerChanged: onEndDrawerChanged,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
appBar: appBar,
|
||||||
|
bottomNavigationBar: bottomNavigationBar,
|
||||||
|
bottomSheet: bottomSheet,
|
||||||
|
drawer: drawer,
|
||||||
|
endDrawer: endDrawer,
|
||||||
|
floatingActionButton: floatingActionButton,
|
||||||
|
floatingActionButtonAnimator: floatingActionButtonAnimator,
|
||||||
|
floatingActionButtonLocation: TabbedFabLocation(context),
|
||||||
|
onDrawerChanged: onDrawerChanged,
|
||||||
|
onEndDrawerChanged: onEndDrawerChanged,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return noBackground
|
||||||
|
? builtWidget
|
||||||
|
: AppBackground(isRoot: true, child: builtWidget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,14 +354,14 @@ class PopIntent extends Intent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PopAction extends Action<PopIntent> {
|
class PopAction extends Action<PopIntent> {
|
||||||
final BuildContext context;
|
final WidgetRef ref;
|
||||||
|
|
||||||
PopAction(this.context);
|
PopAction(this.ref);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void invoke(PopIntent intent) {
|
void invoke(PopIntent intent) {
|
||||||
if (context.canPop()) {
|
if (ref.watch(routerProvider).canPop()) {
|
||||||
context.pop();
|
ref.read(routerProvider).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +377,7 @@ class PageBackButton extends StatelessWidget {
|
|||||||
final isDesktop =
|
final isDesktop =
|
||||||
!kIsWeb && (Platform.isMacOS || Platform.isLinux || Platform.isWindows);
|
!kIsWeb && (Platform.isMacOS || Platform.isLinux || Platform.isWindows);
|
||||||
|
|
||||||
if (isDesktop) return const SizedBox.shrink();
|
if (isDesktop && isWideScreen(context)) return const SizedBox.shrink();
|
||||||
|
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -377,9 +390,11 @@ class PageBackButton extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
color: color,
|
color: color,
|
||||||
(!kIsWeb && (Platform.isMacOS || Platform.isIOS))
|
context.canPop()
|
||||||
? Symbols.arrow_back_ios_new
|
? (!kIsWeb && (Platform.isMacOS || Platform.isIOS))
|
||||||
: Symbols.arrow_back,
|
? Symbols.arrow_back_ios_new
|
||||||
|
: Symbols.arrow_back
|
||||||
|
: Symbols.home,
|
||||||
shadows: shadows,
|
shadows: shadows,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,20 +3,28 @@ import "package:easy_localization/easy_localization.dart";
|
|||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter/services.dart";
|
import "package:flutter/services.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
|
import "package:flutter_typeahead/flutter_typeahead.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:image_picker/image_picker.dart";
|
import "package:image_picker/image_picker.dart";
|
||||||
|
import "package:island/models/account.dart";
|
||||||
|
import "package:island/models/autocomplete_response.dart";
|
||||||
import "package:island/models/chat.dart";
|
import "package:island/models/chat.dart";
|
||||||
import "package:island/models/file.dart";
|
import "package:island/models/file.dart";
|
||||||
|
import "package:island/models/publisher.dart";
|
||||||
|
import "package:island/models/realm.dart";
|
||||||
|
import "package:island/models/sticker.dart";
|
||||||
import "package:island/pods/config.dart";
|
import "package:island/pods/config.dart";
|
||||||
|
import "package:island/services/autocomplete_service.dart";
|
||||||
import "package:island/services/responsive.dart";
|
import "package:island/services/responsive.dart";
|
||||||
import "package:island/widgets/content/attachment_preview.dart";
|
import "package:island/widgets/content/attachment_preview.dart";
|
||||||
|
import "package:island/widgets/content/cloud_files.dart";
|
||||||
import "package:island/widgets/shared/upload_menu.dart";
|
import "package:island/widgets/shared/upload_menu.dart";
|
||||||
import "package:material_symbols_icons/material_symbols_icons.dart";
|
import "package:material_symbols_icons/material_symbols_icons.dart";
|
||||||
import "package:pasteboard/pasteboard.dart";
|
import "package:pasteboard/pasteboard.dart";
|
||||||
import "package:styled_widget/styled_widget.dart";
|
import "package:styled_widget/styled_widget.dart";
|
||||||
import "package:material_symbols_icons/symbols.dart";
|
import "package:material_symbols_icons/symbols.dart";
|
||||||
import "package:island/widgets/stickers/picker.dart";
|
import "package:island/widgets/stickers/sticker_picker.dart";
|
||||||
import "package:island/pods/chat/chat_subscribe.dart";
|
import "package:island/pods/chat/chat_subscribe.dart";
|
||||||
|
|
||||||
class ChatInput extends HookConsumerWidget {
|
class ChatInput extends HookConsumerWidget {
|
||||||
@@ -65,10 +73,8 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
final chatSubscribe = ref.watch(chatSubscribeNotifierProvider(chatRoom.id));
|
final chatSubscribe = ref.watch(chatSubscribeNotifierProvider(chatRoom.id));
|
||||||
|
|
||||||
void send() {
|
void send() {
|
||||||
|
inputFocusNode.requestFocus();
|
||||||
onSend.call();
|
onSend.call();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
inputFocusNode.requestFocus();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void insertNewLine() {
|
void insertNewLine() {
|
||||||
@@ -112,6 +118,8 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final settings = ref.watch(appSettingsNotifierProvider);
|
||||||
|
|
||||||
inputFocusNode.onKeyEvent = (node, event) {
|
inputFocusNode.onKeyEvent = (node, event) {
|
||||||
if (event is! KeyDownEvent) return KeyEventResult.ignored;
|
if (event is! KeyDownEvent) return KeyEventResult.ignored;
|
||||||
|
|
||||||
@@ -125,7 +133,7 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
final enterToSend = ref.read(appSettingsNotifierProvider).enterToSend;
|
final enterToSend = settings.enterToSend;
|
||||||
final isEnter = event.logicalKey == LogicalKeyboardKey.enter;
|
final isEnter = event.logicalKey == LogicalKeyboardKey.enter;
|
||||||
|
|
||||||
if (isEnter) {
|
if (isEnter) {
|
||||||
@@ -217,86 +225,200 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
key: ValueKey('typing-indicator-none'),
|
key: ValueKey('typing-indicator-none'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (attachments.isNotEmpty)
|
AnimatedSwitcher(
|
||||||
SizedBox(
|
duration: const Duration(milliseconds: 250),
|
||||||
height: 180,
|
switchInCurve: Curves.easeOutCubic,
|
||||||
child: ListView.separated(
|
switchOutCurve: Curves.easeInCubic,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||||
scrollDirection: Axis.horizontal,
|
return SlideTransition(
|
||||||
itemCount: attachments.length,
|
position: Tween<Offset>(
|
||||||
itemBuilder: (context, idx) {
|
begin: const Offset(0, 0.1),
|
||||||
return SizedBox(
|
end: Offset.zero,
|
||||||
width: 180,
|
).animate(animation),
|
||||||
child: AttachmentPreview(
|
child: FadeTransition(
|
||||||
isCompact: true,
|
opacity: animation,
|
||||||
item: attachments[idx],
|
child: SizeTransition(
|
||||||
progress: attachmentProgress['chat-upload']?[idx],
|
sizeFactor: animation,
|
||||||
onRequestUpload: () => onUploadAttachment(idx),
|
axisAlignment: -1.0,
|
||||||
onDelete: () => onDeleteAttachment(idx),
|
child: child,
|
||||||
onUpdate: (value) {
|
|
||||||
attachments[idx] = value;
|
|
||||||
onAttachmentsChanged(attachments);
|
|
||||||
},
|
|
||||||
onMove: (delta) => onMoveAttachment(idx, delta),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
separatorBuilder: (_, _) => const Gap(8),
|
|
||||||
),
|
|
||||||
).padding(vertical: 12),
|
|
||||||
if (messageReplyingTo != null ||
|
|
||||||
messageForwardingTo != null ||
|
|
||||||
messageEditingTo != null)
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 4,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
|
||||||
borderRadius: BorderRadius.circular(32),
|
|
||||||
),
|
|
||||||
margin: const EdgeInsets.only(
|
|
||||||
left: 8,
|
|
||||||
right: 8,
|
|
||||||
top: 8,
|
|
||||||
bottom: 4,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
messageReplyingTo != null
|
|
||||||
? Symbols.reply
|
|
||||||
: messageForwardingTo != null
|
|
||||||
? Symbols.forward
|
|
||||||
: Symbols.edit,
|
|
||||||
size: 20,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
),
|
||||||
const Gap(8),
|
),
|
||||||
Expanded(
|
);
|
||||||
child: Text(
|
},
|
||||||
messageReplyingTo != null
|
child:
|
||||||
? 'Replying to ${messageReplyingTo?.sender.account.nick}'
|
attachments.isNotEmpty
|
||||||
: messageForwardingTo != null
|
? SizedBox(
|
||||||
? 'Forwarding message'
|
key: ValueKey('attachments-${attachments.length}'),
|
||||||
: 'Editing message',
|
height: 180,
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
child: ListView.separated(
|
||||||
maxLines: 1,
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
overflow: TextOverflow.ellipsis,
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: attachments.length,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
return SizedBox(
|
||||||
|
width: 180,
|
||||||
|
child: AttachmentPreview(
|
||||||
|
isCompact: true,
|
||||||
|
item: attachments[idx],
|
||||||
|
progress:
|
||||||
|
attachmentProgress['chat-upload']?[idx],
|
||||||
|
onRequestUpload:
|
||||||
|
() => onUploadAttachment(idx),
|
||||||
|
onDelete: () => onDeleteAttachment(idx),
|
||||||
|
onUpdate: (value) {
|
||||||
|
attachments[idx] = value;
|
||||||
|
onAttachmentsChanged(attachments);
|
||||||
|
},
|
||||||
|
onMove:
|
||||||
|
(delta) => onMoveAttachment(idx, delta),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, _) => const Gap(8),
|
||||||
|
),
|
||||||
|
).padding(vertical: 12)
|
||||||
|
: const SizedBox.shrink(
|
||||||
|
key: ValueKey('no-attachments'),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
switchInCurve: Curves.easeOutCubic,
|
||||||
|
switchOutCurve: Curves.easeInCubic,
|
||||||
|
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||||
|
return SlideTransition(
|
||||||
|
position: Tween<Offset>(
|
||||||
|
begin: const Offset(0, -0.2),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(animation),
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: SizeTransition(
|
||||||
|
sizeFactor: animation,
|
||||||
|
axisAlignment: -1.0,
|
||||||
|
child: child,
|
||||||
),
|
),
|
||||||
SizedBox(
|
),
|
||||||
width: 28,
|
);
|
||||||
height: 28,
|
},
|
||||||
child: InkWell(
|
child:
|
||||||
onTap: onClear,
|
(messageReplyingTo != null ||
|
||||||
child: const Icon(Icons.close, size: 20).center(),
|
messageForwardingTo != null ||
|
||||||
),
|
messageEditingTo != null)
|
||||||
),
|
? Container(
|
||||||
],
|
key: ValueKey(
|
||||||
),
|
messageReplyingTo?.id ??
|
||||||
),
|
messageForwardingTo?.id ??
|
||||||
|
messageEditingTo?.id ??
|
||||||
|
'action',
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.outline.withOpacity(0.2),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
|
bottom: 8,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
messageReplyingTo != null
|
||||||
|
? Symbols.reply
|
||||||
|
: messageForwardingTo != null
|
||||||
|
? Symbols.forward
|
||||||
|
: Symbols.edit,
|
||||||
|
size: 18,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
messageReplyingTo != null
|
||||||
|
? 'chatReplyingTo'.tr(
|
||||||
|
args: [
|
||||||
|
messageReplyingTo
|
||||||
|
?.sender
|
||||||
|
.account
|
||||||
|
.nick ??
|
||||||
|
'unknown'.tr(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: messageForwardingTo != null
|
||||||
|
? 'chatForwarding'.tr()
|
||||||
|
: 'chatEditing'.tr(),
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall!.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
icon: const Icon(Icons.close, size: 18),
|
||||||
|
onPressed: onClear,
|
||||||
|
tooltip: 'clear'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (messageReplyingTo != null ||
|
||||||
|
messageForwardingTo != null ||
|
||||||
|
messageEditingTo != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 6,
|
||||||
|
left: 26,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
(messageReplyingTo ??
|
||||||
|
messageForwardingTo ??
|
||||||
|
messageEditingTo)
|
||||||
|
?.content ??
|
||||||
|
'chatNoContent'.tr(),
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall!.copyWith(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(key: ValueKey('no-action')),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -345,24 +467,28 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
UploadMenu(
|
UploadMenu(
|
||||||
items: [
|
items: [
|
||||||
MenuItemData(
|
UploadMenuItemData(
|
||||||
Symbols.add_a_photo,
|
Symbols.add_a_photo,
|
||||||
'addPhoto',
|
'addPhoto',
|
||||||
() => onPickFile(true),
|
() => onPickFile(true),
|
||||||
),
|
),
|
||||||
MenuItemData(
|
UploadMenuItemData(
|
||||||
Symbols.videocam,
|
Symbols.videocam,
|
||||||
'addVideo',
|
'addVideo',
|
||||||
() => onPickFile(false),
|
() => onPickFile(false),
|
||||||
),
|
),
|
||||||
MenuItemData(Symbols.mic, 'addAudio', onPickAudio),
|
UploadMenuItemData(
|
||||||
MenuItemData(
|
Symbols.mic,
|
||||||
|
'addAudio',
|
||||||
|
onPickAudio,
|
||||||
|
),
|
||||||
|
UploadMenuItemData(
|
||||||
Symbols.file_upload,
|
Symbols.file_upload,
|
||||||
'uploadFile',
|
'uploadFile',
|
||||||
onPickGeneralFile,
|
onPickGeneralFile,
|
||||||
),
|
),
|
||||||
if (onLinkAttachment != null)
|
if (onLinkAttachment != null)
|
||||||
MenuItemData(
|
UploadMenuItemData(
|
||||||
Symbols.attach_file,
|
Symbols.attach_file,
|
||||||
'linkAttachment',
|
'linkAttachment',
|
||||||
onLinkAttachment!,
|
onLinkAttachment!,
|
||||||
@@ -373,37 +499,157 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TypeAheadField<AutocompleteSuggestion>(
|
||||||
focusNode: inputFocusNode,
|
|
||||||
controller: messageController,
|
controller: messageController,
|
||||||
keyboardType: TextInputType.multiline,
|
focusNode: inputFocusNode,
|
||||||
decoration: InputDecoration(
|
builder: (context, controller, focusNode) {
|
||||||
hintMaxLines: 1,
|
return TextField(
|
||||||
hintText:
|
focusNode: focusNode,
|
||||||
(chatRoom.type == 1 && chatRoom.name == null)
|
controller: controller,
|
||||||
? 'chatDirectMessageHint'.tr(
|
keyboardType: TextInputType.multiline,
|
||||||
args: [
|
decoration: InputDecoration(
|
||||||
chatRoom.members!
|
hintMaxLines: 1,
|
||||||
.map((e) => e.account.nick)
|
hintText:
|
||||||
.join(', '),
|
(chatRoom.type == 1 && chatRoom.name == null)
|
||||||
],
|
? 'chatDirectMessageHint'.tr(
|
||||||
)
|
args: [
|
||||||
: 'chatMessageHint'.tr(args: [chatRoom.name!]),
|
chatRoom.members!
|
||||||
border: InputBorder.none,
|
.map((e) => e.account.nick)
|
||||||
isDense: true,
|
.join(', '),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
],
|
||||||
horizontal: 12,
|
)
|
||||||
vertical: 12,
|
: 'chatMessageHint'.tr(
|
||||||
),
|
args: [chatRoom.name!],
|
||||||
counterText:
|
),
|
||||||
messageController.text.length > 1024
|
border: InputBorder.none,
|
||||||
? '${messageController.text.length}/4096'
|
isDense: true,
|
||||||
: null,
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
),
|
horizontal: 12,
|
||||||
maxLines: 3,
|
vertical: 12,
|
||||||
minLines: 1,
|
),
|
||||||
onTapOutside:
|
counterText:
|
||||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
messageController.text.length > 1024
|
||||||
|
? '${messageController.text.length}/4096'
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
maxLines: 5,
|
||||||
|
minLines: 1,
|
||||||
|
onTapOutside:
|
||||||
|
(_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
textInputAction:
|
||||||
|
settings.enterToSend
|
||||||
|
? TextInputAction.send
|
||||||
|
: null,
|
||||||
|
onSubmitted:
|
||||||
|
settings.enterToSend ? (_) => send() : null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
suggestionsCallback: (pattern) async {
|
||||||
|
// Only trigger on @ or :
|
||||||
|
final atIndex = pattern.lastIndexOf('@');
|
||||||
|
final colonIndex = pattern.lastIndexOf(':');
|
||||||
|
final triggerIndex =
|
||||||
|
atIndex > colonIndex ? atIndex : colonIndex;
|
||||||
|
if (triggerIndex == -1) return [];
|
||||||
|
final chopped = pattern.substring(triggerIndex);
|
||||||
|
if (chopped.contains(' ')) return [];
|
||||||
|
final service = ref.read(autocompleteServiceProvider);
|
||||||
|
try {
|
||||||
|
return await service.getSuggestions(
|
||||||
|
chatRoom.id,
|
||||||
|
chopped,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: (context, suggestion) {
|
||||||
|
String title = 'unknown'.tr();
|
||||||
|
Widget leading = Icon(Symbols.help);
|
||||||
|
switch (suggestion.type) {
|
||||||
|
case 'user':
|
||||||
|
final user = SnAccount.fromJson(suggestion.data);
|
||||||
|
title = user.nick;
|
||||||
|
leading = ProfilePictureWidget(
|
||||||
|
file: user.profile.picture,
|
||||||
|
radius: 18,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'chatroom':
|
||||||
|
final chatRoom = SnChatRoom.fromJson(
|
||||||
|
suggestion.data,
|
||||||
|
);
|
||||||
|
title = chatRoom.name ?? 'Chat Room';
|
||||||
|
leading = ProfilePictureWidget(
|
||||||
|
file: chatRoom.picture,
|
||||||
|
radius: 18,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'realm':
|
||||||
|
final realm = SnRealm.fromJson(suggestion.data);
|
||||||
|
title = realm.name;
|
||||||
|
leading = ProfilePictureWidget(
|
||||||
|
file: realm.picture,
|
||||||
|
radius: 18,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'publisher':
|
||||||
|
final publisher = SnPublisher.fromJson(
|
||||||
|
suggestion.data,
|
||||||
|
);
|
||||||
|
title = publisher.name;
|
||||||
|
leading = ProfilePictureWidget(
|
||||||
|
file: publisher.picture,
|
||||||
|
radius: 18,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'sticker':
|
||||||
|
final sticker = SnSticker.fromJson(suggestion.data);
|
||||||
|
title = sticker.slug;
|
||||||
|
leading = ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
child: CloudImageWidget(
|
||||||
|
fileId: sticker.imageId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return ListTile(
|
||||||
|
leading: leading,
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: Text(suggestion.keyword),
|
||||||
|
dense: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSelected: (suggestion) {
|
||||||
|
final text = messageController.text;
|
||||||
|
final atIndex = text.lastIndexOf('@');
|
||||||
|
final colonIndex = text.lastIndexOf(':');
|
||||||
|
final triggerIndex =
|
||||||
|
atIndex > colonIndex ? atIndex : colonIndex;
|
||||||
|
if (triggerIndex == -1) return;
|
||||||
|
final newText = text.replaceRange(
|
||||||
|
triggerIndex,
|
||||||
|
text.length,
|
||||||
|
suggestion.keyword,
|
||||||
|
);
|
||||||
|
messageController.value = TextEditingValue(
|
||||||
|
text: newText,
|
||||||
|
selection: TextSelection.collapsed(
|
||||||
|
offset: triggerIndex + suggestion.keyword.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
direction: VerticalDirection.up,
|
||||||
|
hideOnEmpty: true,
|
||||||
|
hideOnLoading: true,
|
||||||
|
debounceDuration: const Duration(milliseconds: 1000),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import 'package:island/widgets/content/embed/embed_list.dart';
|
|||||||
import 'package:island/widgets/post/post_shared.dart';
|
import 'package:island/widgets/post/post_shared.dart';
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:swipe_to/swipe_to.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
|
||||||
class MessageItemAction {
|
class MessageItemAction {
|
||||||
@@ -159,57 +160,80 @@ class MessageItem extends HookConsumerWidget {
|
|||||||
return Stack(
|
return Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
InkWell(
|
SwipeTo(
|
||||||
mouseCursor: MouseCursor.defer,
|
swipeSensitivity: 15,
|
||||||
focusColor: Colors.transparent,
|
rightSwipeWidget: Transform.flip(
|
||||||
onLongPress: showActionMenu,
|
flipX: true,
|
||||||
onSecondaryTap: showActionMenu,
|
child: Icon(Symbols.menu_open),
|
||||||
onTap: () {
|
).padding(left: 16),
|
||||||
// Jump to related message
|
leftSwipeWidget: Icon(
|
||||||
if (['messages.update', 'messages.delete'].contains(message.type) &&
|
isCurrentUser ? Symbols.forward : Symbols.reply,
|
||||||
message.meta['message_id'] is String &&
|
).padding(right: 16),
|
||||||
message.meta['message_id'] != null) {
|
onLeftSwipe: (details) {
|
||||||
onJump(message.meta['message_id']);
|
if (onAction != null) {
|
||||||
|
if (isCurrentUser) {
|
||||||
|
onAction!(MessageItemAction.forward);
|
||||||
|
} else {
|
||||||
|
onAction!(MessageItemAction.reply);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: SizedBox(
|
onRightSwipe: (details) => showActionMenu(),
|
||||||
width: double.infinity,
|
child: InkWell(
|
||||||
child: MouseRegion(
|
mouseCursor: MouseCursor.defer,
|
||||||
onEnter: (_) => isHovered.value = true,
|
focusColor: Colors.transparent,
|
||||||
onExit: (_) => isHovered.value = false,
|
onLongPress: showActionMenu,
|
||||||
child: AnimatedContainer(
|
onSecondaryTap: showActionMenu,
|
||||||
curve: Curves.easeInOut,
|
onTap: () {
|
||||||
duration: const Duration(milliseconds: kFlashDuration),
|
// Jump to related message
|
||||||
decoration: BoxDecoration(color: flashColor),
|
if ([
|
||||||
child: switch (settings.messageDisplayStyle) {
|
'messages.update',
|
||||||
'compact' => MessageItemDisplayIRC(
|
'messages.delete',
|
||||||
message: message,
|
].contains(message.type) &&
|
||||||
isCurrentUser: isCurrentUser,
|
message.meta['message_id'] is String &&
|
||||||
progress: progress,
|
message.meta['message_id'] != null) {
|
||||||
showAvatar: showAvatar,
|
onJump(message.meta['message_id']);
|
||||||
onJump: onJump,
|
}
|
||||||
translatedText: translatedText.value,
|
},
|
||||||
translating: translating.value,
|
child: SizedBox(
|
||||||
),
|
width: double.infinity,
|
||||||
'column' => MessageItemDisplayDiscord(
|
child: MouseRegion(
|
||||||
message: message,
|
onEnter: (_) => isHovered.value = true,
|
||||||
isCurrentUser: isCurrentUser,
|
onExit: (_) => isHovered.value = false,
|
||||||
progress: progress,
|
child: AnimatedContainer(
|
||||||
showAvatar: showAvatar,
|
curve: Curves.easeInOut,
|
||||||
onJump: onJump,
|
duration: const Duration(milliseconds: kFlashDuration),
|
||||||
translatedText: translatedText.value,
|
decoration: BoxDecoration(color: flashColor),
|
||||||
translating: translating.value,
|
child: switch (settings.messageDisplayStyle) {
|
||||||
),
|
'compact' => MessageItemDisplayIRC(
|
||||||
_ => MessageItemDisplayBubble(
|
message: message,
|
||||||
message: message,
|
isCurrentUser: isCurrentUser,
|
||||||
isCurrentUser: isCurrentUser,
|
progress: progress,
|
||||||
progress: progress,
|
showAvatar: showAvatar,
|
||||||
showAvatar: showAvatar,
|
onJump: onJump,
|
||||||
onJump: onJump,
|
translatedText: translatedText.value,
|
||||||
translatedText: translatedText.value,
|
translating: translating.value,
|
||||||
translating: translating.value,
|
),
|
||||||
),
|
'column' => MessageItemDisplayDiscord(
|
||||||
},
|
message: message,
|
||||||
|
isCurrentUser: isCurrentUser,
|
||||||
|
progress: progress,
|
||||||
|
showAvatar: showAvatar,
|
||||||
|
onJump: onJump,
|
||||||
|
translatedText: translatedText.value,
|
||||||
|
translating: translating.value,
|
||||||
|
),
|
||||||
|
_ => MessageItemDisplayBubble(
|
||||||
|
message: message,
|
||||||
|
isCurrentUser: isCurrentUser,
|
||||||
|
progress: progress,
|
||||||
|
showAvatar: showAvatar,
|
||||||
|
onJump: onJump,
|
||||||
|
translatedText: translatedText.value,
|
||||||
|
translating: translating.value,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -314,44 +338,47 @@ class _MessageActionSheetState extends State<MessageActionSheet> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// Header
|
// Header
|
||||||
Row(
|
SizedBox(
|
||||||
children: [
|
height: 24,
|
||||||
Icon(
|
child: Row(
|
||||||
Symbols.article,
|
children: [
|
||||||
size: 16,
|
Icon(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
Symbols.article,
|
||||||
),
|
size: 16,
|
||||||
const Gap(6),
|
|
||||||
Text(
|
|
||||||
'messageContent'.tr(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
const Gap(6),
|
||||||
const Spacer(),
|
Text(
|
||||||
if (_shouldShowExpandButton)
|
'messageContent'.tr(),
|
||||||
IconButton(
|
style: TextStyle(
|
||||||
visualDensity: VisualDensity.compact,
|
fontSize: 12,
|
||||||
icon: Icon(
|
fontWeight: FontWeight.w500,
|
||||||
_isExpanded
|
color: Theme.of(context).colorScheme.primary,
|
||||||
? Symbols.expand_less
|
|
||||||
: Symbols.expand_more,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_isExpanded = !_isExpanded;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
minWidth: 24,
|
|
||||||
minHeight: 24,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
const Spacer(),
|
||||||
|
if (_shouldShowExpandButton)
|
||||||
|
IconButton(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
icon: Icon(
|
||||||
|
_isExpanded
|
||||||
|
? Symbols.expand_less
|
||||||
|
: Symbols.expand_more,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_isExpanded = !_isExpanded;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 32,
|
||||||
|
minHeight: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
// Selectable content
|
// Selectable content
|
||||||
@@ -403,7 +430,7 @@ class _MessageActionSheetState extends State<MessageActionSheet> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (widget.isCurrentUser) const Divider(height: 8),
|
if (widget.isCurrentUser) const Divider(),
|
||||||
|
|
||||||
_ActionListTile(
|
_ActionListTile(
|
||||||
leading: Icon(Symbols.reply),
|
leading: Icon(Symbols.reply),
|
||||||
@@ -422,7 +449,7 @@ class _MessageActionSheetState extends State<MessageActionSheet> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
if (widget.translatableLanguage) const Divider(height: 8),
|
if (widget.translatableLanguage) const Divider(),
|
||||||
if (widget.translatableLanguage)
|
if (widget.translatableLanguage)
|
||||||
_ActionListTile(
|
_ActionListTile(
|
||||||
leading: Icon(Symbols.translate),
|
leading: Icon(Symbols.translate),
|
||||||
@@ -439,7 +466,7 @@ class _MessageActionSheetState extends State<MessageActionSheet> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
if (widget.isMobile) const Divider(height: 8),
|
if (widget.isMobile) const Divider(),
|
||||||
if (widget.isMobile)
|
if (widget.isMobile)
|
||||||
_ActionListTile(
|
_ActionListTile(
|
||||||
leading: Icon(Symbols.copy_all),
|
leading: Icon(Symbols.copy_all),
|
||||||
@@ -451,6 +478,8 @@ class _MessageActionSheetState extends State<MessageActionSheet> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Gap(MediaQuery.of(context).padding.bottom),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:easy_localization/easy_localization.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:go_router/go_router.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/activity.dart';
|
import 'package:island/models/activity.dart';
|
||||||
@@ -14,6 +13,8 @@ import 'package:island/pods/userinfo.dart';
|
|||||||
import 'package:island/screens/auth/captcha.dart';
|
import 'package:island/screens/auth/captcha.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/account/event_calendar_content.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:slide_countdown/slide_countdown.dart';
|
import 'package:slide_countdown/slide_countdown.dart';
|
||||||
@@ -256,9 +257,17 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
if (todayResult.valueOrNull == null) {
|
if (todayResult.valueOrNull == null) {
|
||||||
checkIn();
|
checkIn();
|
||||||
} else {
|
} else {
|
||||||
context.pushNamed(
|
showModalBottomSheet(
|
||||||
'accountCalendar',
|
context: context,
|
||||||
pathParameters: {'name': 'me'},
|
isScrollControlled: true,
|
||||||
|
builder:
|
||||||
|
(context) => SheetScaffold(
|
||||||
|
titleText: 'eventCalendar'.tr(),
|
||||||
|
child: EventCalendarContent(
|
||||||
|
name: 'me',
|
||||||
|
isSheet: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,12 +15,15 @@ import 'package:island/widgets/content/cloud_files.dart';
|
|||||||
import 'package:island/widgets/content/markdown_latex.dart';
|
import 'package:island/widgets/content/markdown_latex.dart';
|
||||||
import 'package:markdown/markdown.dart' as markdown;
|
import 'package:markdown/markdown.dart' as markdown;
|
||||||
import 'package:markdown_widget/markdown_widget.dart';
|
import 'package:markdown_widget/markdown_widget.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.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import 'image.dart';
|
import 'image.dart';
|
||||||
|
|
||||||
class MarkdownTextContent extends HookConsumerWidget {
|
class MarkdownTextContent extends HookConsumerWidget {
|
||||||
|
static const String stickerRegex = r':([-\w]*\+[-\w]*):';
|
||||||
|
|
||||||
final String content;
|
final String content;
|
||||||
final bool isAutoWarp;
|
final bool isAutoWarp;
|
||||||
final TextScaler? textScaler;
|
final TextScaler? textScaler;
|
||||||
@@ -47,7 +50,7 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final doesEnlargeSticker = useMemoized(() {
|
final doesEnlargeSticker = useMemoized(() {
|
||||||
// Check if content only contains one sticker by matching the sticker pattern
|
// Check if content only contains one sticker by matching the sticker pattern
|
||||||
final stickerPattern = RegExp(r':([-\w]+):');
|
final stickerPattern = RegExp(stickerRegex);
|
||||||
final matches = stickerPattern.allMatches(content);
|
final matches = stickerPattern.allMatches(content);
|
||||||
|
|
||||||
// Content should only contain one sticker and nothing else (except whitespace)
|
// Content should only contain one sticker and nothing else (except whitespace)
|
||||||
@@ -60,6 +63,31 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
final config =
|
final config =
|
||||||
isDark ? MarkdownConfig.darkConfig : MarkdownConfig.defaultConfig;
|
isDark ? MarkdownConfig.darkConfig : MarkdownConfig.defaultConfig;
|
||||||
|
|
||||||
|
final onMentionTap = useCallback((String type, String id) {
|
||||||
|
final fullPath = '/$type/$id';
|
||||||
|
context.push(fullPath);
|
||||||
|
}, [context]);
|
||||||
|
|
||||||
|
final mentionGenerator = MentionChipGenerator(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onSecondary,
|
||||||
|
onTap: onMentionTap,
|
||||||
|
);
|
||||||
|
|
||||||
|
final highlightGenerator = HighlightGenerator(
|
||||||
|
highlightColor: Theme.of(context).colorScheme.primaryContainer,
|
||||||
|
);
|
||||||
|
|
||||||
|
final spoilerRevealed = useState(false);
|
||||||
|
|
||||||
|
final spoilerGenerator = SpoilerGenerator(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.tertiary,
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onTertiary,
|
||||||
|
outlineColor: Theme.of(context).colorScheme.outline,
|
||||||
|
revealed: spoilerRevealed.value,
|
||||||
|
onToggle: () => spoilerRevealed.value = !spoilerRevealed.value,
|
||||||
|
);
|
||||||
|
|
||||||
return MarkdownBlock(
|
return MarkdownBlock(
|
||||||
data: content,
|
data: content,
|
||||||
selectable: isSelectable,
|
selectable: isSelectable,
|
||||||
@@ -96,16 +124,15 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
final url = Uri.tryParse(href);
|
final url = Uri.tryParse(href);
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
if (url.scheme == 'solian') {
|
if (url.scheme == 'solian') {
|
||||||
if (url.host == 'account') {
|
final fullPath = ['/', url.host, url.path].join('');
|
||||||
context.pushNamed(
|
context.push(fullPath);
|
||||||
'accountProfile',
|
|
||||||
pathParameters: {'name': url.pathSegments[0]},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final whitelistDomains = ['solian.app', 'solsynth.dev'];
|
final whitelistDomains = ['solian.app', 'solsynth.dev'];
|
||||||
if (whitelistDomains.contains(url.host)) {
|
if (whitelistDomains.any(
|
||||||
|
(domain) =>
|
||||||
|
url.host == domain || url.host.endsWith('.$domain'),
|
||||||
|
)) {
|
||||||
launchUrl(url, mode: LaunchMode.externalApplication);
|
launchUrl(url, mode: LaunchMode.externalApplication);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -201,6 +228,7 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
generator: MarkdownTextContent.buildGenerator(
|
generator: MarkdownTextContent.buildGenerator(
|
||||||
isDark: isDark,
|
isDark: isDark,
|
||||||
linesMargin: linesMargin,
|
linesMargin: linesMargin,
|
||||||
|
generators: [mentionGenerator, highlightGenerator, spoilerGenerator],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -208,11 +236,14 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
static MarkdownGenerator buildGenerator({
|
static MarkdownGenerator buildGenerator({
|
||||||
bool isDark = false,
|
bool isDark = false,
|
||||||
EdgeInsets? linesMargin,
|
EdgeInsets? linesMargin,
|
||||||
|
List<dynamic> generators = const [],
|
||||||
}) {
|
}) {
|
||||||
return MarkdownGenerator(
|
return MarkdownGenerator(
|
||||||
generators: [latexGenerator],
|
generators: [latexGenerator, ...generators],
|
||||||
inlineSyntaxList: [
|
inlineSyntaxList: [
|
||||||
_UserNameCardInlineSyntax(),
|
_MetionInlineSyntax(),
|
||||||
|
_HighlightInlineSyntax(),
|
||||||
|
_SpoilerInlineSyntax(),
|
||||||
_StickerInlineSyntax(),
|
_StickerInlineSyntax(),
|
||||||
LatexSyntax(isDark),
|
LatexSyntax(isDark),
|
||||||
],
|
],
|
||||||
@@ -221,24 +252,34 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UserNameCardInlineSyntax extends markdown.InlineSyntax {
|
class _MetionInlineSyntax extends markdown.InlineSyntax {
|
||||||
_UserNameCardInlineSyntax() : super(r'@[a-zA-Z0-9_]+');
|
_MetionInlineSyntax() : super(r'@[-a-zA-Z0-9_./]+');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool onMatch(markdown.InlineParser parser, Match match) {
|
bool onMatch(markdown.InlineParser parser, Match match) {
|
||||||
final alias = match[0]!;
|
final alias = match[0]!;
|
||||||
final anchor = markdown.Element.text('a', alias)
|
final parts = alias.substring(1).split('/');
|
||||||
..attributes['href'] = Uri.encodeFull(
|
final typeShortcut = parts.length == 1 ? 'u' : parts.first;
|
||||||
'solian://account/${alias.substring(1)}',
|
final type = switch (typeShortcut) {
|
||||||
);
|
'u' => 'accounts',
|
||||||
parser.addNode(anchor);
|
'r' => 'realms',
|
||||||
|
'p' => 'publishers',
|
||||||
|
"c" => 'chat',
|
||||||
|
_ => '',
|
||||||
|
};
|
||||||
|
final element =
|
||||||
|
markdown.Element('mention-chip', [markdown.Text(alias)])
|
||||||
|
..attributes['alias'] = alias
|
||||||
|
..attributes['type'] = type
|
||||||
|
..attributes['id'] = parts.last;
|
||||||
|
parser.addNode(element);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _StickerInlineSyntax extends markdown.InlineSyntax {
|
class _StickerInlineSyntax extends markdown.InlineSyntax {
|
||||||
_StickerInlineSyntax() : super(r':([-\w]+):');
|
_StickerInlineSyntax() : super(MarkdownTextContent.stickerRegex);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool onMatch(markdown.InlineParser parser, Match match) {
|
bool onMatch(markdown.InlineParser parser, Match match) {
|
||||||
@@ -250,3 +291,271 @@ class _StickerInlineSyntax extends markdown.InlineSyntax {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _HighlightInlineSyntax extends markdown.InlineSyntax {
|
||||||
|
_HighlightInlineSyntax() : super(r'==([^=]+)==');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool onMatch(markdown.InlineParser parser, Match match) {
|
||||||
|
final text = match[1]!;
|
||||||
|
final element = markdown.Element('highlight', [markdown.Text(text)]);
|
||||||
|
parser.addNode(element);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpoilerInlineSyntax extends markdown.InlineSyntax {
|
||||||
|
_SpoilerInlineSyntax() : super(r'=!([^!]+)!=');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool onMatch(markdown.InlineParser parser, Match match) {
|
||||||
|
final text = match[1]!;
|
||||||
|
final element = markdown.Element('spoiler', [markdown.Text(text)]);
|
||||||
|
parser.addNode(element);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MentionSpanNodeGenerator {
|
||||||
|
final Color backgroundColor;
|
||||||
|
final Color foregroundColor;
|
||||||
|
final void Function(String type, String id) onTap;
|
||||||
|
|
||||||
|
MentionSpanNodeGenerator({
|
||||||
|
required this.backgroundColor,
|
||||||
|
required this.foregroundColor,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
SpanNode? call(
|
||||||
|
String tag,
|
||||||
|
Map<String, String> attributes,
|
||||||
|
List<SpanNode> children,
|
||||||
|
) {
|
||||||
|
if (tag == 'mention-chip') {
|
||||||
|
return MentionChipSpanNode(
|
||||||
|
attributes: attributes,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
foregroundColor: foregroundColor,
|
||||||
|
onTap: onTap,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MentionChipGenerator extends SpanNodeGeneratorWithTag {
|
||||||
|
MentionChipGenerator({
|
||||||
|
required Color backgroundColor,
|
||||||
|
required Color foregroundColor,
|
||||||
|
required void Function(String type, String id) onTap,
|
||||||
|
}) : super(
|
||||||
|
tag: 'mention-chip',
|
||||||
|
generator: (
|
||||||
|
markdown.Element element,
|
||||||
|
MarkdownConfig config,
|
||||||
|
WidgetVisitor visitor,
|
||||||
|
) {
|
||||||
|
return MentionChipSpanNode(
|
||||||
|
attributes: element.attributes,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
foregroundColor: foregroundColor,
|
||||||
|
onTap: onTap,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MentionChipSpanNode extends SpanNode {
|
||||||
|
final Map<String, String> attributes;
|
||||||
|
final Color backgroundColor;
|
||||||
|
final Color foregroundColor;
|
||||||
|
final void Function(String type, String id) onTap;
|
||||||
|
|
||||||
|
MentionChipSpanNode({
|
||||||
|
required this.attributes,
|
||||||
|
required this.backgroundColor,
|
||||||
|
required this.foregroundColor,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
InlineSpan build() {
|
||||||
|
final alias = attributes['alias'] ?? '';
|
||||||
|
final type = attributes['type'] ?? '';
|
||||||
|
final id = attributes['id'] ?? '';
|
||||||
|
|
||||||
|
final parts = alias.substring(1).split('/');
|
||||||
|
|
||||||
|
return WidgetSpan(
|
||||||
|
alignment: PlaceholderAlignment.middle,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => onTap(type, id),
|
||||||
|
borderRadius: BorderRadius.circular(32),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(32),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor.withOpacity(0.5),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(32)),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
switch (parts.first.isEmpty ? 'u' : parts.first) {
|
||||||
|
'c' => Symbols.forum_rounded,
|
||||||
|
'r' => Symbols.group_rounded,
|
||||||
|
'u' => Symbols.person_rounded,
|
||||||
|
'p' => Symbols.edit_rounded,
|
||||||
|
_ => Symbols.person_rounded,
|
||||||
|
},
|
||||||
|
size: 14,
|
||||||
|
color: foregroundColor,
|
||||||
|
fill: 1,
|
||||||
|
).padding(all: 2),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
parts.last,
|
||||||
|
style: TextStyle(
|
||||||
|
color: backgroundColor,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HighlightGenerator extends SpanNodeGeneratorWithTag {
|
||||||
|
HighlightGenerator({required Color highlightColor})
|
||||||
|
: super(
|
||||||
|
tag: 'highlight',
|
||||||
|
generator: (
|
||||||
|
markdown.Element element,
|
||||||
|
MarkdownConfig config,
|
||||||
|
WidgetVisitor visitor,
|
||||||
|
) {
|
||||||
|
return HighlightSpanNode(
|
||||||
|
text: element.textContent,
|
||||||
|
highlightColor: highlightColor,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class HighlightSpanNode extends SpanNode {
|
||||||
|
final String text;
|
||||||
|
final Color highlightColor;
|
||||||
|
|
||||||
|
HighlightSpanNode({required this.text, required this.highlightColor});
|
||||||
|
|
||||||
|
@override
|
||||||
|
InlineSpan build() {
|
||||||
|
return TextSpan(
|
||||||
|
text: text,
|
||||||
|
style: TextStyle(backgroundColor: highlightColor),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpoilerGenerator extends SpanNodeGeneratorWithTag {
|
||||||
|
SpoilerGenerator({
|
||||||
|
required Color backgroundColor,
|
||||||
|
required Color foregroundColor,
|
||||||
|
required Color outlineColor,
|
||||||
|
required bool revealed,
|
||||||
|
required VoidCallback onToggle,
|
||||||
|
}) : super(
|
||||||
|
tag: 'spoiler',
|
||||||
|
generator: (
|
||||||
|
markdown.Element element,
|
||||||
|
MarkdownConfig config,
|
||||||
|
WidgetVisitor visitor,
|
||||||
|
) {
|
||||||
|
return SpoilerSpanNode(
|
||||||
|
text: element.textContent,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
foregroundColor: foregroundColor,
|
||||||
|
outlineColor: outlineColor,
|
||||||
|
revealed: revealed,
|
||||||
|
onToggle: onToggle,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpoilerSpanNode extends SpanNode {
|
||||||
|
final String text;
|
||||||
|
final Color backgroundColor;
|
||||||
|
final Color foregroundColor;
|
||||||
|
final Color outlineColor;
|
||||||
|
final bool revealed;
|
||||||
|
final VoidCallback onToggle;
|
||||||
|
|
||||||
|
SpoilerSpanNode({
|
||||||
|
required this.text,
|
||||||
|
required this.backgroundColor,
|
||||||
|
required this.foregroundColor,
|
||||||
|
required this.outlineColor,
|
||||||
|
required this.revealed,
|
||||||
|
required this.onToggle,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
InlineSpan build() {
|
||||||
|
return WidgetSpan(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onToggle,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: revealed ? Colors.transparent : backgroundColor,
|
||||||
|
border: revealed ? Border.all(color: outlineColor, width: 1) : null,
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
revealed
|
||||||
|
? Row(
|
||||||
|
spacing: 6,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Icon(Symbols.visibility, size: 18).padding(top: 1),
|
||||||
|
Flexible(child: Text(text)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
spacing: 6,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.visibility_off,
|
||||||
|
color: foregroundColor,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child:
|
||||||
|
Text(
|
||||||
|
'spoiler',
|
||||||
|
style: TextStyle(color: foregroundColor),
|
||||||
|
).tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import 'package:flutter/material.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';
|
||||||
|
import 'package:island/screens/tabs.dart';
|
||||||
|
import 'package:island/services/responsive.dart';
|
||||||
|
|
||||||
class ConditionalBottomNav extends HookConsumerWidget {
|
class ConditionalBottomNav extends HookConsumerWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
@@ -17,10 +19,11 @@ class ConditionalBottomNav extends HookConsumerWidget {
|
|||||||
return null;
|
return null;
|
||||||
}, [currentLocation]);
|
}, [currentLocation]);
|
||||||
|
|
||||||
// Use the same route logic as TabsScreen for consistency
|
final routes = kTabRoutes.sublist(
|
||||||
const mainTabRoutes = ['/', '/chat', '/realms', '/account'];
|
0,
|
||||||
|
isWideScreen(context) ? null : kWideScreenRouteStart,
|
||||||
final shouldShowBottomNav = mainTabRoutes.contains(currentLocation);
|
);
|
||||||
|
final shouldShowBottomNav = routes.contains(currentLocation);
|
||||||
|
|
||||||
return shouldShowBottomNav ? child : const SizedBox.shrink();
|
return shouldShowBottomNav ? child : const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ class PostComposeCard extends HookConsumerWidget {
|
|||||||
final SnPost? originalPost;
|
final SnPost? originalPost;
|
||||||
final PostComposeInitialState? initialState;
|
final PostComposeInitialState? initialState;
|
||||||
final VoidCallback? onCancel;
|
final VoidCallback? onCancel;
|
||||||
final Function(SnPost)? onSubmit;
|
final Function()? onSubmit;
|
||||||
final Function(ComposeState)? onStateChanged;
|
final Function(ComposeState)? onStateChanged;
|
||||||
final bool isInDialog;
|
final bool isDialog;
|
||||||
|
|
||||||
const PostComposeCard({
|
const PostComposeCard({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -45,7 +45,7 @@ class PostComposeCard extends HookConsumerWidget {
|
|||||||
this.onCancel,
|
this.onCancel,
|
||||||
this.onSubmit,
|
this.onSubmit,
|
||||||
this.onStateChanged,
|
this.onStateChanged,
|
||||||
this.isInDialog = false,
|
this.isDialog = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -152,7 +152,7 @@ class PostComposeCard extends HookConsumerWidget {
|
|||||||
originalPost: originalPost,
|
originalPost: originalPost,
|
||||||
repliedPost: repliedPost,
|
repliedPost: repliedPost,
|
||||||
forwardedPost: forwardedPost,
|
forwardedPost: forwardedPost,
|
||||||
onSuccess: (SnPost createdPost) {
|
onSuccess: () {
|
||||||
// Mark as submitted
|
// Mark as submitted
|
||||||
submitted.value = true;
|
submitted.value = true;
|
||||||
|
|
||||||
@@ -164,20 +164,19 @@ class PostComposeCard extends HookConsumerWidget {
|
|||||||
// Reset the form for new composition
|
// Reset the form for new composition
|
||||||
ComposeStateUtils.resetForm(state);
|
ComposeStateUtils.resetForm(state);
|
||||||
|
|
||||||
// Call the widget's onSubmit callback to trigger activity list refresh
|
onSubmit?.call();
|
||||||
onSubmit?.call(createdPost);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final maxHeight = math.min(
|
final maxHeight = math.min(
|
||||||
640.0,
|
640.0,
|
||||||
MediaQuery.of(context).size.height * (isInDialog ? 0.8 : 0.72),
|
MediaQuery.of(context).size.height * (isDialog ? 0.8 : 0.72),
|
||||||
);
|
);
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
color: isDialog ? Theme.of(context).colorScheme.surfaceContainer : null,
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(maxHeight: maxHeight),
|
constraints: BoxConstraints(maxHeight: maxHeight),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -311,7 +310,7 @@ class PostComposeCard extends HookConsumerWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
if (state.currentPublisher.value == null) {
|
if (state.currentPublisher.value == null) {
|
||||||
// No publisher loaded, guide user to create one
|
// No publisher loaded, guide user to create one
|
||||||
if (isInDialog) {
|
if (isDialog) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
context.pushNamed('creatorNew').then((value) {
|
context.pushNamed('creatorNew').then((value) {
|
||||||
@@ -348,7 +347,7 @@ class PostComposeCard extends HookConsumerWidget {
|
|||||||
onPublisherTap: () {
|
onPublisherTap: () {
|
||||||
if (state.currentPublisher.value == null) {
|
if (state.currentPublisher.value == null) {
|
||||||
// No publisher loaded, guide user to create one
|
// No publisher loaded, guide user to create one
|
||||||
if (isInDialog) {
|
if (isDialog) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
context.pushNamed('creatorNew').then((
|
context.pushNamed('creatorNew').then((
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ class PostComposeDialog extends HookConsumerWidget {
|
|||||||
this.isBottomSheet = false,
|
this.isBottomSheet = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
static Future<SnPost?> show(
|
static Future<bool?> show(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
SnPost? originalPost,
|
SnPost? originalPost,
|
||||||
PostComposeInitialState? initialState,
|
PostComposeInitialState? initialState,
|
||||||
}) {
|
}) {
|
||||||
return showDialog<SnPost>(
|
return showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
builder:
|
builder:
|
||||||
@@ -50,7 +50,11 @@ class PostComposeDialog extends HookConsumerWidget {
|
|||||||
final isWide = isWideScreen(context);
|
final isWide = isWideScreen(context);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (!prompted.value && originalPost == null && drafts.isNotEmpty) {
|
if (!prompted.value &&
|
||||||
|
originalPost == null &&
|
||||||
|
initialState?.replyingTo == null &&
|
||||||
|
initialState?.forwardingTo == null &&
|
||||||
|
drafts.isNotEmpty) {
|
||||||
prompted.value = true;
|
prompted.value = true;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_showRestoreDialog(ref, restoredInitialState);
|
_showRestoreDialog(ref, restoredInitialState);
|
||||||
@@ -70,8 +74,8 @@ class PostComposeDialog extends HookConsumerWidget {
|
|||||||
originalPost: originalPost,
|
originalPost: originalPost,
|
||||||
initialState: restoredInitialState.value ?? initialState,
|
initialState: restoredInitialState.value ?? initialState,
|
||||||
onCancel: () => Navigator.of(context).pop(),
|
onCancel: () => Navigator.of(context).pop(),
|
||||||
onSubmit: (post) => Navigator.of(context).pop(post),
|
onSubmit: () => Navigator.of(context).pop(true),
|
||||||
isInDialog: true,
|
isDialog: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/account.dart';
|
||||||
|
import 'package:island/models/autocomplete_response.dart';
|
||||||
|
import 'package:island/models/chat.dart';
|
||||||
|
import 'package:island/models/publisher.dart';
|
||||||
|
import 'package:island/models/realm.dart';
|
||||||
|
import 'package:island/models/sticker.dart';
|
||||||
|
import 'package:island/services/autocomplete_service.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
import 'package:island/widgets/post/compose_shared.dart';
|
import 'package:island/widgets/post/compose_shared.dart';
|
||||||
|
|
||||||
/// A reusable widget for the form fields in compose screens.
|
/// A reusable widget for the form fields in compose screens.
|
||||||
/// Includes title, description, and content text fields.
|
/// Includes title, description, and content text fields.
|
||||||
class ComposeFormFields extends StatelessWidget {
|
class ComposeFormFields extends HookConsumerWidget {
|
||||||
final ComposeState state;
|
final ComposeState state;
|
||||||
final bool enabled;
|
final bool enabled;
|
||||||
final bool showPublisherAvatar;
|
final bool showPublisherAvatar;
|
||||||
@@ -20,7 +29,7 @@ class ComposeFormFields extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
@@ -111,22 +120,124 @@ class ComposeFormFields extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Content field
|
// Content field
|
||||||
TextField(
|
TypeAheadField<AutocompleteSuggestion>(
|
||||||
controller: state.contentController,
|
controller: state.contentController,
|
||||||
enabled: enabled && state.currentPublisher.value != null,
|
builder: (context, controller, focusNode) {
|
||||||
style: theme.textTheme.bodyMedium,
|
return TextField(
|
||||||
decoration: InputDecoration(
|
focusNode: focusNode,
|
||||||
border: InputBorder.none,
|
controller: controller,
|
||||||
hintText: 'postContent'.tr(),
|
enabled: enabled && state.currentPublisher.value != null,
|
||||||
isCollapsed: true,
|
style: theme.textTheme.bodyMedium,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
decoration: InputDecoration(
|
||||||
vertical: 8,
|
border: InputBorder.none,
|
||||||
horizontal: 8,
|
hintText: 'postContent'.tr(),
|
||||||
),
|
isCollapsed: true,
|
||||||
),
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
maxLines: null,
|
vertical: 8,
|
||||||
onTapOutside:
|
horizontal: 8,
|
||||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
),
|
||||||
|
),
|
||||||
|
maxLines: null,
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
suggestionsCallback: (pattern) async {
|
||||||
|
// Only trigger on @ or :
|
||||||
|
final atIndex = pattern.lastIndexOf('@');
|
||||||
|
final colonIndex = pattern.lastIndexOf(':');
|
||||||
|
final triggerIndex =
|
||||||
|
atIndex > colonIndex ? atIndex : colonIndex;
|
||||||
|
if (triggerIndex == -1) return [];
|
||||||
|
final chopped = pattern.substring(triggerIndex);
|
||||||
|
if (chopped.contains(' ')) return [];
|
||||||
|
final service = ref.read(autocompleteServiceProvider);
|
||||||
|
try {
|
||||||
|
return await service.getGeneralSuggestions(chopped);
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: (context, suggestion) {
|
||||||
|
String title = 'unknown'.tr();
|
||||||
|
Widget leading = Icon(Icons.help);
|
||||||
|
switch (suggestion.type) {
|
||||||
|
case 'user':
|
||||||
|
final user = SnAccount.fromJson(suggestion.data);
|
||||||
|
title = user.nick;
|
||||||
|
leading = ProfilePictureWidget(
|
||||||
|
file: user.profile.picture,
|
||||||
|
radius: 18,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'chatroom':
|
||||||
|
final chatRoom = SnChatRoom.fromJson(suggestion.data);
|
||||||
|
title = chatRoom.name ?? 'Chat Room';
|
||||||
|
leading = ProfilePictureWidget(
|
||||||
|
file: chatRoom.picture,
|
||||||
|
radius: 18,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'realm':
|
||||||
|
final realm = SnRealm.fromJson(suggestion.data);
|
||||||
|
title = realm.name;
|
||||||
|
leading = ProfilePictureWidget(
|
||||||
|
file: realm.picture,
|
||||||
|
radius: 18,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'publisher':
|
||||||
|
final publisher = SnPublisher.fromJson(suggestion.data);
|
||||||
|
title = publisher.name;
|
||||||
|
leading = ProfilePictureWidget(
|
||||||
|
file: publisher.picture,
|
||||||
|
radius: 18,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'sticker':
|
||||||
|
final sticker = SnSticker.fromJson(suggestion.data);
|
||||||
|
title = sticker.slug;
|
||||||
|
leading = ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
child: CloudImageWidget(fileId: sticker.imageId),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return ListTile(
|
||||||
|
leading: leading,
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: Text(suggestion.keyword),
|
||||||
|
dense: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSelected: (suggestion) {
|
||||||
|
final text = state.contentController.text;
|
||||||
|
final atIndex = text.lastIndexOf('@');
|
||||||
|
final colonIndex = text.lastIndexOf(':');
|
||||||
|
final triggerIndex =
|
||||||
|
atIndex > colonIndex ? atIndex : colonIndex;
|
||||||
|
if (triggerIndex == -1) return;
|
||||||
|
final newText = text.replaceRange(
|
||||||
|
triggerIndex,
|
||||||
|
text.length,
|
||||||
|
suggestion.keyword,
|
||||||
|
);
|
||||||
|
state.contentController.value = TextEditingValue(
|
||||||
|
text: newText,
|
||||||
|
selection: TextSelection.collapsed(
|
||||||
|
offset: triggerIndex + suggestion.keyword.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
direction: VerticalDirection.down,
|
||||||
|
hideOnEmpty: true,
|
||||||
|
hideOnLoading: true,
|
||||||
|
debounceDuration: const Duration(milliseconds: 1000),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ import 'package:dropdown_button2/dropdown_button2.dart';
|
|||||||
import 'package:easy_localization/easy_localization.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:flutter_typeahead/flutter_typeahead.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/post_category.dart';
|
import 'package:island/models/post_category.dart';
|
||||||
|
import 'package:island/models/post_tag.dart';
|
||||||
import 'package:island/models/realm.dart';
|
import 'package:island/models/realm.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/screens/realm/realms.dart';
|
import 'package:island/screens/realm/realms.dart';
|
||||||
@@ -14,7 +16,6 @@ import 'package:island/widgets/post/compose_shared.dart';
|
|||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:textfield_tags/textfield_tags.dart';
|
|
||||||
|
|
||||||
part 'compose_settings_sheet.g.dart';
|
part 'compose_settings_sheet.g.dart';
|
||||||
|
|
||||||
@@ -35,103 +36,31 @@ Future<List<SnPostCategory>> postCategories(Ref ref) async {
|
|||||||
return uniqueCategories.values.toList();
|
return uniqueCategories.values.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A reusable widget for tag input fields with chip display
|
|
||||||
class ChipTagInputField extends StatelessWidget {
|
|
||||||
final InputFieldValues inputFieldValues;
|
|
||||||
final String labelText;
|
|
||||||
final String hintText;
|
|
||||||
|
|
||||||
const ChipTagInputField({
|
|
||||||
super.key,
|
|
||||||
required this.inputFieldValues,
|
|
||||||
required this.labelText,
|
|
||||||
required this.hintText,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return TextField(
|
|
||||||
controller: inputFieldValues.textEditingController,
|
|
||||||
focusNode: inputFieldValues.focusNode,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
label: Text(labelText).tr(),
|
|
||||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
|
||||||
contentPadding: const EdgeInsets.all(16),
|
|
||||||
hintText: inputFieldValues.tags.isNotEmpty ? '' : hintText.tr(),
|
|
||||||
errorText: inputFieldValues.error,
|
|
||||||
prefixIconConstraints: BoxConstraints(
|
|
||||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
|
||||||
),
|
|
||||||
prefixIcon:
|
|
||||||
inputFieldValues.tags.isNotEmpty
|
|
||||||
? SingleChildScrollView(
|
|
||||||
controller: inputFieldValues.tagScrollController,
|
|
||||||
scrollDirection: Axis.vertical,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 8),
|
|
||||||
child: Wrap(
|
|
||||||
runSpacing: 4.0,
|
|
||||||
spacing: 4.0,
|
|
||||||
children:
|
|
||||||
inputFieldValues.tags.map<Widget>((dynamic tag) {
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(20.0),
|
|
||||||
),
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
margin: const EdgeInsets.only(left: 5),
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 10.0,
|
|
||||||
vertical: 5.0,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
InkWell(
|
|
||||||
child: Text(
|
|
||||||
'#$tag',
|
|
||||||
style: TextStyle(
|
|
||||||
color:
|
|
||||||
Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onPrimary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
InkWell(
|
|
||||||
child: const Icon(
|
|
||||||
Icons.cancel,
|
|
||||||
size: 14.0,
|
|
||||||
color: Color.fromARGB(255, 233, 233, 233),
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
inputFieldValues.onTagRemoved(tag);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
onChanged: inputFieldValues.onTagChanged,
|
|
||||||
onSubmitted: inputFieldValues.onTagSubmitted,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ComposeSettingsSheet extends HookConsumerWidget {
|
class ComposeSettingsSheet extends HookConsumerWidget {
|
||||||
final ComposeState state;
|
final ComposeState state;
|
||||||
|
|
||||||
const ComposeSettingsSheet({super.key, required this.state});
|
const ComposeSettingsSheet({super.key, required this.state});
|
||||||
|
|
||||||
|
Future<List<SnPostTag>> _fetchTagSuggestions(
|
||||||
|
String query,
|
||||||
|
WidgetRef ref,
|
||||||
|
) async {
|
||||||
|
if (query.isEmpty) return [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
final response = await client.get(
|
||||||
|
'/sphere/posts/tags',
|
||||||
|
queryParameters: {'query': query},
|
||||||
|
);
|
||||||
|
return response.data
|
||||||
|
.map<SnPostTag>((json) => SnPostTag.fromJson(json))
|
||||||
|
.toList();
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
@@ -140,6 +69,7 @@ class ComposeSettingsSheet extends HookConsumerWidget {
|
|||||||
// Listen to visibility changes to trigger rebuilds
|
// Listen to visibility changes to trigger rebuilds
|
||||||
final currentVisibility = useValueListenable(state.visibility);
|
final currentVisibility = useValueListenable(state.visibility);
|
||||||
final currentCategories = useValueListenable(state.categories);
|
final currentCategories = useValueListenable(state.categories);
|
||||||
|
final currentTags = useValueListenable(state.tags);
|
||||||
final currentRealm = useValueListenable(state.realm);
|
final currentRealm = useValueListenable(state.realm);
|
||||||
final postCategories = ref.watch(postCategoriesProvider);
|
final postCategories = ref.watch(postCategoriesProvider);
|
||||||
final userRealms = ref.watch(realmsJoinedProvider);
|
final userRealms = ref.watch(realmsJoinedProvider);
|
||||||
@@ -255,23 +185,118 @@ class ComposeSettingsSheet extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Tags field
|
// Tags field
|
||||||
TextFieldTags(
|
Container(
|
||||||
textfieldTagsController: state.tagsController,
|
decoration: BoxDecoration(
|
||||||
textSeparators: const [' ', ','],
|
border: Border.all(
|
||||||
letterCase: LetterCase.normal,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
validator: (String tag) {
|
width: 1,
|
||||||
if (tag.isEmpty) {
|
),
|
||||||
return 'No, cannot be empty';
|
borderRadius: BorderRadius.circular(12),
|
||||||
}
|
),
|
||||||
return null;
|
padding: const EdgeInsets.all(16),
|
||||||
},
|
child: Column(
|
||||||
inputFieldBuilder: (context, inputFieldValues) {
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
return ChipTagInputField(
|
spacing: 12,
|
||||||
inputFieldValues: inputFieldValues,
|
children: [
|
||||||
labelText: 'tags',
|
Text(
|
||||||
hintText: 'tagsHint',
|
'tags'.tr(),
|
||||||
);
|
style: Theme.of(context).textTheme.labelLarge,
|
||||||
},
|
),
|
||||||
|
// Existing tags display
|
||||||
|
if (currentTags.isNotEmpty)
|
||||||
|
Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
|
children:
|
||||||
|
currentTags.map((tag) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 6,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'#$tag',
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onPrimary,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
final newTags = List<String>.from(
|
||||||
|
state.tags.value,
|
||||||
|
)..remove(tag);
|
||||||
|
state.tags.value = newTags;
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 16,
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
// Tag input with autocomplete
|
||||||
|
TypeAheadField<SnPostTag>(
|
||||||
|
builder: (context, controller, focusNode) {
|
||||||
|
return TextField(
|
||||||
|
controller: controller,
|
||||||
|
focusNode: focusNode,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'addTag'.tr(),
|
||||||
|
border: InputBorder.none,
|
||||||
|
isCollapsed: true,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
onSubmitted: (value) {
|
||||||
|
state.tags.value = [...state.tags.value, value];
|
||||||
|
controller.clear();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
suggestionsCallback:
|
||||||
|
(pattern) => _fetchTagSuggestions(pattern, ref),
|
||||||
|
itemBuilder: (context, suggestion) {
|
||||||
|
return ListTile(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
title: Text('#${suggestion.slug}'),
|
||||||
|
dense: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSelected: (suggestion) {
|
||||||
|
if (!state.tags.value.contains(suggestion.slug)) {
|
||||||
|
state.tags.value = [
|
||||||
|
...state.tags.value,
|
||||||
|
suggestion.slug,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
direction: VerticalDirection.down,
|
||||||
|
hideOnEmpty: true,
|
||||||
|
hideOnLoading: true,
|
||||||
|
debounceDuration: const Duration(milliseconds: 300),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Categories field
|
// Categories field
|
||||||
@@ -399,6 +424,25 @@ class ComposeSettingsSheet extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
).padding(left: 16, right: 8),
|
).padding(left: 16, right: 8),
|
||||||
),
|
),
|
||||||
|
// Include current realm if it's not null and not in joined realms
|
||||||
|
if (currentRealm != null &&
|
||||||
|
!(userRealms.value ?? []).any(
|
||||||
|
(r) => r.id == currentRealm.id,
|
||||||
|
))
|
||||||
|
DropdownMenuItem<SnRealm?>(
|
||||||
|
value: currentRealm,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
ProfilePictureWidget(
|
||||||
|
fileId: currentRealm.picture?.id,
|
||||||
|
fallbackIcon: Symbols.workspaces,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(currentRealm.name),
|
||||||
|
],
|
||||||
|
).padding(left: 16, right: 8),
|
||||||
|
),
|
||||||
if (userRealms.hasValue)
|
if (userRealms.hasValue)
|
||||||
...(userRealms.value ?? []).map(
|
...(userRealms.value ?? []).map(
|
||||||
(realm) => DropdownMenuItem<SnRealm?>(
|
(realm) => DropdownMenuItem<SnRealm?>(
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import 'package:island/widgets/post/compose_poll.dart';
|
|||||||
import 'package:island/widgets/post/compose_recorder.dart';
|
import 'package:island/widgets/post/compose_recorder.dart';
|
||||||
import 'package:island/pods/file_pool.dart';
|
import 'package:island/pods/file_pool.dart';
|
||||||
import 'package:pasteboard/pasteboard.dart';
|
import 'package:pasteboard/pasteboard.dart';
|
||||||
import 'package:textfield_tags/textfield_tags.dart';
|
|
||||||
import 'package:island/talker.dart';
|
import 'package:island/talker.dart';
|
||||||
|
|
||||||
class ComposeState {
|
class ComposeState {
|
||||||
@@ -37,7 +36,7 @@ class ComposeState {
|
|||||||
final ValueNotifier<SnPublisher?> currentPublisher;
|
final ValueNotifier<SnPublisher?> currentPublisher;
|
||||||
final ValueNotifier<bool> submitting;
|
final ValueNotifier<bool> submitting;
|
||||||
final ValueNotifier<List<SnPostCategory>> categories;
|
final ValueNotifier<List<SnPostCategory>> categories;
|
||||||
StringTagController tagsController;
|
final ValueNotifier<List<String>> tags;
|
||||||
final ValueNotifier<SnRealm?> realm;
|
final ValueNotifier<SnRealm?> realm;
|
||||||
final ValueNotifier<SnPostEmbedView?> embedView;
|
final ValueNotifier<SnPostEmbedView?> embedView;
|
||||||
final String draftId;
|
final String draftId;
|
||||||
@@ -56,7 +55,7 @@ class ComposeState {
|
|||||||
required this.attachmentProgress,
|
required this.attachmentProgress,
|
||||||
required this.currentPublisher,
|
required this.currentPublisher,
|
||||||
required this.submitting,
|
required this.submitting,
|
||||||
required this.tagsController,
|
required this.tags,
|
||||||
required this.categories,
|
required this.categories,
|
||||||
required this.realm,
|
required this.realm,
|
||||||
required this.embedView,
|
required this.embedView,
|
||||||
@@ -90,14 +89,10 @@ class ComposeLogic {
|
|||||||
int postType = 0,
|
int postType = 0,
|
||||||
}) {
|
}) {
|
||||||
final id = draftId ?? DateTime.now().millisecondsSinceEpoch.toString();
|
final id = draftId ?? DateTime.now().millisecondsSinceEpoch.toString();
|
||||||
final tagsController = StringTagController();
|
|
||||||
|
|
||||||
// Initialize tags from original post
|
// Initialize tags from original post
|
||||||
if (originalPost != null) {
|
final tags =
|
||||||
for (var tag in originalPost.tags) {
|
originalPost?.tags.map((tag) => tag.slug).toList() ?? <String>[];
|
||||||
tagsController.addTag(tag.slug);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize categories from original post
|
// Initialize categories from original post
|
||||||
final categories = originalPost?.categories ?? <SnPostCategory>[];
|
final categories = originalPost?.categories ?? <SnPostCategory>[];
|
||||||
@@ -129,7 +124,7 @@ class ComposeLogic {
|
|||||||
submitting: ValueNotifier<bool>(false),
|
submitting: ValueNotifier<bool>(false),
|
||||||
attachmentProgress: ValueNotifier<Map<int, double>>({}),
|
attachmentProgress: ValueNotifier<Map<int, double>>({}),
|
||||||
currentPublisher: ValueNotifier<SnPublisher?>(originalPost?.publisher),
|
currentPublisher: ValueNotifier<SnPublisher?>(originalPost?.publisher),
|
||||||
tagsController: tagsController,
|
tags: ValueNotifier<List<String>>(tags),
|
||||||
categories: ValueNotifier<List<SnPostCategory>>(categories),
|
categories: ValueNotifier<List<SnPostCategory>>(categories),
|
||||||
realm: ValueNotifier(originalPost?.realm),
|
realm: ValueNotifier(originalPost?.realm),
|
||||||
embedView: ValueNotifier<SnPostEmbedView?>(originalPost?.embedView),
|
embedView: ValueNotifier<SnPostEmbedView?>(originalPost?.embedView),
|
||||||
@@ -141,10 +136,7 @@ class ComposeLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ComposeState createStateFromDraft(SnPost draft, {int postType = 0}) {
|
static ComposeState createStateFromDraft(SnPost draft, {int postType = 0}) {
|
||||||
final tagsController = StringTagController();
|
final tags = draft.tags.map((tag) => tag.slug).toList();
|
||||||
for (var x in draft.tags) {
|
|
||||||
tagsController.addTag(x.slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComposeState(
|
return ComposeState(
|
||||||
attachments: ValueNotifier<List<UniversalFile>>(
|
attachments: ValueNotifier<List<UniversalFile>>(
|
||||||
@@ -158,7 +150,7 @@ class ComposeLogic {
|
|||||||
submitting: ValueNotifier<bool>(false),
|
submitting: ValueNotifier<bool>(false),
|
||||||
attachmentProgress: ValueNotifier<Map<int, double>>({}),
|
attachmentProgress: ValueNotifier<Map<int, double>>({}),
|
||||||
currentPublisher: ValueNotifier<SnPublisher?>(null),
|
currentPublisher: ValueNotifier<SnPublisher?>(null),
|
||||||
tagsController: tagsController,
|
tags: ValueNotifier<List<String>>(tags),
|
||||||
categories: ValueNotifier<List<SnPostCategory>>(draft.categories),
|
categories: ValueNotifier<List<SnPostCategory>>(draft.categories),
|
||||||
realm: ValueNotifier(draft.realm),
|
realm: ValueNotifier(draft.realm),
|
||||||
embedView: ValueNotifier<SnPostEmbedView?>(draft.embedView),
|
embedView: ValueNotifier<SnPostEmbedView?>(draft.embedView),
|
||||||
@@ -685,7 +677,7 @@ class ComposeLogic {
|
|||||||
'type': state.postType,
|
'type': state.postType,
|
||||||
if (repliedPost != null) 'replied_post_id': repliedPost.id,
|
if (repliedPost != null) 'replied_post_id': repliedPost.id,
|
||||||
if (forwardedPost != null) 'forwarded_post_id': forwardedPost.id,
|
if (forwardedPost != null) 'forwarded_post_id': forwardedPost.id,
|
||||||
'tags': state.tagsController.getTags,
|
'tags': state.tags.value,
|
||||||
'categories': state.categories.value.map((e) => e.slug).toList(),
|
'categories': state.categories.value.map((e) => e.slug).toList(),
|
||||||
if (state.realm.value != null) 'realm_id': state.realm.value?.id,
|
if (state.realm.value != null) 'realm_id': state.realm.value?.id,
|
||||||
if (state.pollId.value != null) 'poll_id': state.pollId.value,
|
if (state.pollId.value != null) 'poll_id': state.pollId.value,
|
||||||
@@ -781,7 +773,7 @@ class ComposeLogic {
|
|||||||
state.submitting.dispose();
|
state.submitting.dispose();
|
||||||
state.attachmentProgress.dispose();
|
state.attachmentProgress.dispose();
|
||||||
state.currentPublisher.dispose();
|
state.currentPublisher.dispose();
|
||||||
state.tagsController.dispose();
|
state.tags.dispose();
|
||||||
state.categories.dispose();
|
state.categories.dispose();
|
||||||
state.realm.dispose();
|
state.realm.dispose();
|
||||||
state.embedView.dispose();
|
state.embedView.dispose();
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ class ComposeStateUtils {
|
|||||||
state.attachmentProgress.value = {};
|
state.attachmentProgress.value = {};
|
||||||
|
|
||||||
// Clear tags
|
// Clear tags
|
||||||
state.tagsController.clearTags();
|
state.tags.value = [];
|
||||||
|
|
||||||
// Clear categories
|
// Clear categories
|
||||||
state.categories.value = [];
|
state.categories.value = [];
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class ComposeSubmitUtils {
|
|||||||
SnPost? originalPost,
|
SnPost? originalPost,
|
||||||
SnPost? repliedPost,
|
SnPost? repliedPost,
|
||||||
SnPost? forwardedPost,
|
SnPost? forwardedPost,
|
||||||
required Function(SnPost) onSuccess,
|
required Function() onSuccess,
|
||||||
}) async {
|
}) async {
|
||||||
if (state.submitting.value) {
|
if (state.submitting.value) {
|
||||||
throw Exception('Already submitting');
|
throw Exception('Already submitting');
|
||||||
@@ -75,7 +75,7 @@ class ComposeSubmitUtils {
|
|||||||
'type': state.postType,
|
'type': state.postType,
|
||||||
if (repliedPost != null) 'replied_post_id': repliedPost.id,
|
if (repliedPost != null) 'replied_post_id': repliedPost.id,
|
||||||
if (forwardedPost != null) 'forwarded_post_id': forwardedPost.id,
|
if (forwardedPost != null) 'forwarded_post_id': forwardedPost.id,
|
||||||
'tags': state.tagsController.getTags,
|
'tags': state.tags.value,
|
||||||
'categories': state.categories.value.map((e) => e.slug).toList(),
|
'categories': state.categories.value.map((e) => e.slug).toList(),
|
||||||
if (state.realm.value != null) 'realm_id': state.realm.value?.id,
|
if (state.realm.value != null) 'realm_id': state.realm.value?.id,
|
||||||
if (state.pollId.value != null) 'poll_id': state.pollId.value,
|
if (state.pollId.value != null) 'poll_id': state.pollId.value,
|
||||||
@@ -94,8 +94,8 @@ class ComposeSubmitUtils {
|
|||||||
// Parse the response into a SnPost
|
// Parse the response into a SnPost
|
||||||
final post = SnPost.fromJson(response.data);
|
final post = SnPost.fromJson(response.data);
|
||||||
|
|
||||||
// Call the success callback with the created/updated post
|
// Call the success callback
|
||||||
onSuccess(post);
|
onSuccess();
|
||||||
|
|
||||||
return post;
|
return post;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -82,10 +82,10 @@ class ComposeToolbar extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final uploadMenuItems = [
|
final uploadMenuItems = [
|
||||||
MenuItemData(Symbols.add_a_photo, 'addPhoto', pickPhotoMedia),
|
UploadMenuItemData(Symbols.add_a_photo, 'addPhoto', pickPhotoMedia),
|
||||||
MenuItemData(Symbols.videocam, 'addVideo', pickVideoMedia),
|
UploadMenuItemData(Symbols.videocam, 'addVideo', pickVideoMedia),
|
||||||
MenuItemData(Symbols.mic, 'addAudio', addAudio),
|
UploadMenuItemData(Symbols.mic, 'addAudio', addAudio),
|
||||||
MenuItemData(Symbols.file_upload, 'uploadFile', pickGeneralFile),
|
UploadMenuItemData(Symbols.file_upload, 'uploadFile', pickGeneralFile),
|
||||||
];
|
];
|
||||||
|
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ import 'package:island/pods/userinfo.dart';
|
|||||||
import 'package:island/screens/posts/compose.dart';
|
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/content/image.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_award_sheet.dart';
|
import 'package:island/widgets/post/post_award_sheet.dart';
|
||||||
import 'package:island/widgets/post/post_pin_sheet.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/post/embed_view_renderer.dart';
|
import 'package:island/widgets/post/embed_view_renderer.dart';
|
||||||
|
import 'package:island/widgets/post/post_reaction_sheet.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';
|
||||||
import 'package:island/widgets/post/compose_dialog.dart';
|
import 'package:island/widgets/post/compose_dialog.dart';
|
||||||
@@ -332,7 +334,7 @@ class PostActionableItem extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerLow,
|
color: Theme.of(context).cardTheme.color,
|
||||||
borderRadius:
|
borderRadius:
|
||||||
borderRadius != null
|
borderRadius != null
|
||||||
? BorderRadius.all(Radius.circular(borderRadius!))
|
? BorderRadius.all(Radius.circular(borderRadius!))
|
||||||
@@ -548,15 +550,39 @@ class PostItem extends HookConsumerWidget {
|
|||||||
).colorScheme.primary.withOpacity(0.75),
|
).colorScheme.primary.withOpacity(0.75),
|
||||||
textColor:
|
textColor:
|
||||||
Theme.of(context).colorScheme.onPrimary,
|
Theme.of(context).colorScheme.onPrimary,
|
||||||
child: _buildReactionIcon(
|
child:
|
||||||
mostReaction,
|
mostReaction.contains('+')
|
||||||
32,
|
? HookConsumer(
|
||||||
).padding(
|
builder: (context, ref, child) {
|
||||||
bottom:
|
final baseUrl = ref.watch(
|
||||||
_getReactionImageAvailable(mostReaction)
|
serverUrlProvider,
|
||||||
? 2
|
);
|
||||||
: 0,
|
final stickerUri =
|
||||||
),
|
'$baseUrl/sphere/stickers/lookup/$mostReaction/open';
|
||||||
|
return SizedBox(
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
child:
|
||||||
|
UniversalImage(
|
||||||
|
uri: stickerUri,
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
).center(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: _buildReactionIcon(
|
||||||
|
mostReaction,
|
||||||
|
32,
|
||||||
|
).padding(
|
||||||
|
bottom:
|
||||||
|
_getReactionImageAvailable(
|
||||||
|
mostReaction,
|
||||||
|
)
|
||||||
|
? 2
|
||||||
|
: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: WidgetStatePropertyAll(
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
@@ -572,12 +598,13 @@ class PostItem extends HookConsumerWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return _PostReactionSheet(
|
return PostReactionSheet(
|
||||||
reactionsCount: item.reactionsCount,
|
reactionsCount: item.reactionsCount,
|
||||||
reactionsMade: item.reactionsMade,
|
reactionsMade: item.reactionsMade,
|
||||||
onReact: (symbol, attitude) {
|
onReact: (symbol, attitude) {
|
||||||
reactPost(symbol, attitude);
|
reactPost(symbol, attitude);
|
||||||
},
|
},
|
||||||
|
postId: item.id,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -679,12 +706,13 @@ class PostReactionList extends HookConsumerWidget {
|
|||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return _PostReactionSheet(
|
return PostReactionSheet(
|
||||||
reactionsCount: reactions,
|
reactionsCount: reactions,
|
||||||
reactionsMade: reactionsMade,
|
reactionsMade: reactionsMade,
|
||||||
onReact: (symbol, attitude) {
|
onReact: (symbol, attitude) {
|
||||||
reactPost(symbol, attitude);
|
reactPost(symbol, attitude);
|
||||||
},
|
},
|
||||||
|
postId: parentId,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -723,212 +751,3 @@ class PostReactionList extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PostReactionSheet extends StatelessWidget {
|
|
||||||
final Map<String, int> reactionsCount;
|
|
||||||
final Map<String, bool> reactionsMade;
|
|
||||||
final Function(String symbol, int attitude) onReact;
|
|
||||||
const _PostReactionSheet({
|
|
||||||
required this.reactionsCount,
|
|
||||||
required this.reactionsMade,
|
|
||||||
required this.onReact,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 16,
|
|
||||||
left: 20,
|
|
||||||
right: 16,
|
|
||||||
bottom: 12,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'reactions'.plural(
|
|
||||||
reactionsCount.isNotEmpty
|
|
||||||
? reactionsCount.values.reduce((a, b) => a + b)
|
|
||||||
: 0,
|
|
||||||
),
|
|
||||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
letterSpacing: -0.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Symbols.close),
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Divider(height: 1),
|
|
||||||
Expanded(
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
_buildReactionSection(
|
|
||||||
context,
|
|
||||||
Symbols.mood,
|
|
||||||
'reactionPositive'.tr(),
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
_buildReactionSection(
|
|
||||||
context,
|
|
||||||
Symbols.sentiment_neutral,
|
|
||||||
'reactionNeutral'.tr(),
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
_buildReactionSection(
|
|
||||||
context,
|
|
||||||
Symbols.mood_bad,
|
|
||||||
'reactionNegative'.tr(),
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildReactionSection(
|
|
||||||
BuildContext context,
|
|
||||||
IconData icon,
|
|
||||||
String title,
|
|
||||||
int attitude,
|
|
||||||
) {
|
|
||||||
final allReactions =
|
|
||||||
kReactionTemplates.entries
|
|
||||||
.where((entry) => entry.value.attitude == attitude)
|
|
||||||
.map((entry) => entry.key)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
spacing: 8,
|
|
||||||
children: [Icon(icon), Text(title).fontSize(17).bold()],
|
|
||||||
).padding(horizontal: 24, top: 16, bottom: 6),
|
|
||||||
SizedBox(
|
|
||||||
height: 120,
|
|
||||||
child: GridView.builder(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: 1,
|
|
||||||
mainAxisExtent: 120,
|
|
||||||
mainAxisSpacing: 8.0,
|
|
||||||
crossAxisSpacing: 8.0,
|
|
||||||
childAspectRatio: 1.0,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
itemCount: allReactions.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final symbol = allReactions[index];
|
|
||||||
final count = reactionsCount[symbol] ?? 0;
|
|
||||||
final hasImage = _getReactionImageAvailable(symbol);
|
|
||||||
return Badge(
|
|
||||||
label: Text('x$count'),
|
|
||||||
isLabelVisible: count > 0,
|
|
||||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
offset: Offset(0, 0),
|
|
||||||
child: Card(
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerLowest,
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
onTap: () {
|
|
||||||
onReact(symbol, attitude);
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
decoration:
|
|
||||||
hasImage
|
|
||||||
? BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(
|
|
||||||
'assets/images/stickers/$symbol.png',
|
|
||||||
),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
colorFilter:
|
|
||||||
(reactionsMade[symbol] ?? false)
|
|
||||||
? ColorFilter.mode(
|
|
||||||
Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primaryContainer
|
|
||||||
.withOpacity(0.7),
|
|
||||||
BlendMode.srcATop,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
child: Stack(
|
|
||||||
fit: StackFit.expand,
|
|
||||||
children: [
|
|
||||||
if (hasImage)
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.bottomCenter,
|
|
||||||
end: Alignment.topCenter,
|
|
||||||
colors: [
|
|
||||||
Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.surfaceContainerHighest
|
|
||||||
.withOpacity(0.7),
|
|
||||||
Colors.transparent,
|
|
||||||
],
|
|
||||||
stops: [0.0, 0.3],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
mainAxisAlignment:
|
|
||||||
hasImage
|
|
||||||
? MainAxisAlignment.end
|
|
||||||
: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (!hasImage) _buildReactionIcon(symbol, 36),
|
|
||||||
Text(
|
|
||||||
ReactInfo.getTranslationKey(symbol),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
color: hasImage ? Colors.white : null,
|
|
||||||
shadows:
|
|
||||||
hasImage
|
|
||||||
? [
|
|
||||||
const Shadow(
|
|
||||||
blurRadius: 4,
|
|
||||||
offset: Offset(0.5, 0.5),
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
).tr(),
|
|
||||||
if (hasImage) const Gap(4),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class PostListNotifier extends _$PostListNotifier
|
|||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
bool? pinned,
|
bool? pinned,
|
||||||
bool shuffle = false,
|
bool shuffle = false,
|
||||||
|
bool? includeReplies,
|
||||||
}) {
|
}) {
|
||||||
return fetch(cursor: null);
|
return fetch(cursor: null);
|
||||||
}
|
}
|
||||||
@@ -42,6 +43,7 @@ class PostListNotifier extends _$PostListNotifier
|
|||||||
if (categories != null) 'categories': categories,
|
if (categories != null) 'categories': categories,
|
||||||
if (shuffle) 'shuffle': true,
|
if (shuffle) 'shuffle': true,
|
||||||
if (pinned != null) 'pinned': pinned,
|
if (pinned != null) 'pinned': pinned,
|
||||||
|
if (includeReplies != null) 'includeReplies': includeReplies,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await client.get(
|
final response = await client.get(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ part of 'post_list.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$postListNotifierHash() => r'3c0a8154ded4bcd8f5456f7a4ea2e542f57efa85';
|
String _$postListNotifierHash() => r'fc139ad4df0deb67bcbb949560319f2f7fbfb503';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
@@ -38,6 +38,7 @@ abstract class _$PostListNotifier
|
|||||||
late final List<String>? tags;
|
late final List<String>? tags;
|
||||||
late final bool? pinned;
|
late final bool? pinned;
|
||||||
late final bool shuffle;
|
late final bool shuffle;
|
||||||
|
late final bool? includeReplies;
|
||||||
|
|
||||||
FutureOr<CursorPagingData<SnPost>> build({
|
FutureOr<CursorPagingData<SnPost>> build({
|
||||||
String? pubName,
|
String? pubName,
|
||||||
@@ -47,6 +48,7 @@ abstract class _$PostListNotifier
|
|||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
bool? pinned,
|
bool? pinned,
|
||||||
bool shuffle = false,
|
bool shuffle = false,
|
||||||
|
bool? includeReplies,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +71,7 @@ class PostListNotifierFamily
|
|||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
bool? pinned,
|
bool? pinned,
|
||||||
bool shuffle = false,
|
bool shuffle = false,
|
||||||
|
bool? includeReplies,
|
||||||
}) {
|
}) {
|
||||||
return PostListNotifierProvider(
|
return PostListNotifierProvider(
|
||||||
pubName: pubName,
|
pubName: pubName,
|
||||||
@@ -78,6 +81,7 @@ class PostListNotifierFamily
|
|||||||
tags: tags,
|
tags: tags,
|
||||||
pinned: pinned,
|
pinned: pinned,
|
||||||
shuffle: shuffle,
|
shuffle: shuffle,
|
||||||
|
includeReplies: includeReplies,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +97,7 @@ class PostListNotifierFamily
|
|||||||
tags: provider.tags,
|
tags: provider.tags,
|
||||||
pinned: provider.pinned,
|
pinned: provider.pinned,
|
||||||
shuffle: provider.shuffle,
|
shuffle: provider.shuffle,
|
||||||
|
includeReplies: provider.includeReplies,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +132,7 @@ class PostListNotifierProvider
|
|||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
bool? pinned,
|
bool? pinned,
|
||||||
bool shuffle = false,
|
bool shuffle = false,
|
||||||
|
bool? includeReplies,
|
||||||
}) : this._internal(
|
}) : this._internal(
|
||||||
() =>
|
() =>
|
||||||
PostListNotifier()
|
PostListNotifier()
|
||||||
@@ -136,7 +142,8 @@ class PostListNotifierProvider
|
|||||||
..categories = categories
|
..categories = categories
|
||||||
..tags = tags
|
..tags = tags
|
||||||
..pinned = pinned
|
..pinned = pinned
|
||||||
..shuffle = shuffle,
|
..shuffle = shuffle
|
||||||
|
..includeReplies = includeReplies,
|
||||||
from: postListNotifierProvider,
|
from: postListNotifierProvider,
|
||||||
name: r'postListNotifierProvider',
|
name: r'postListNotifierProvider',
|
||||||
debugGetCreateSourceHash:
|
debugGetCreateSourceHash:
|
||||||
@@ -153,6 +160,7 @@ class PostListNotifierProvider
|
|||||||
tags: tags,
|
tags: tags,
|
||||||
pinned: pinned,
|
pinned: pinned,
|
||||||
shuffle: shuffle,
|
shuffle: shuffle,
|
||||||
|
includeReplies: includeReplies,
|
||||||
);
|
);
|
||||||
|
|
||||||
PostListNotifierProvider._internal(
|
PostListNotifierProvider._internal(
|
||||||
@@ -169,6 +177,7 @@ class PostListNotifierProvider
|
|||||||
required this.tags,
|
required this.tags,
|
||||||
required this.pinned,
|
required this.pinned,
|
||||||
required this.shuffle,
|
required this.shuffle,
|
||||||
|
required this.includeReplies,
|
||||||
}) : super.internal();
|
}) : super.internal();
|
||||||
|
|
||||||
final String? pubName;
|
final String? pubName;
|
||||||
@@ -178,6 +187,7 @@ class PostListNotifierProvider
|
|||||||
final List<String>? tags;
|
final List<String>? tags;
|
||||||
final bool? pinned;
|
final bool? pinned;
|
||||||
final bool shuffle;
|
final bool shuffle;
|
||||||
|
final bool? includeReplies;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<CursorPagingData<SnPost>> runNotifierBuild(
|
FutureOr<CursorPagingData<SnPost>> runNotifierBuild(
|
||||||
@@ -191,6 +201,7 @@ class PostListNotifierProvider
|
|||||||
tags: tags,
|
tags: tags,
|
||||||
pinned: pinned,
|
pinned: pinned,
|
||||||
shuffle: shuffle,
|
shuffle: shuffle,
|
||||||
|
includeReplies: includeReplies,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +218,8 @@ class PostListNotifierProvider
|
|||||||
..categories = categories
|
..categories = categories
|
||||||
..tags = tags
|
..tags = tags
|
||||||
..pinned = pinned
|
..pinned = pinned
|
||||||
..shuffle = shuffle,
|
..shuffle = shuffle
|
||||||
|
..includeReplies = includeReplies,
|
||||||
from: from,
|
from: from,
|
||||||
name: null,
|
name: null,
|
||||||
dependencies: null,
|
dependencies: null,
|
||||||
@@ -220,6 +232,7 @@ class PostListNotifierProvider
|
|||||||
tags: tags,
|
tags: tags,
|
||||||
pinned: pinned,
|
pinned: pinned,
|
||||||
shuffle: shuffle,
|
shuffle: shuffle,
|
||||||
|
includeReplies: includeReplies,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -242,7 +255,8 @@ class PostListNotifierProvider
|
|||||||
other.categories == categories &&
|
other.categories == categories &&
|
||||||
other.tags == tags &&
|
other.tags == tags &&
|
||||||
other.pinned == pinned &&
|
other.pinned == pinned &&
|
||||||
other.shuffle == shuffle;
|
other.shuffle == shuffle &&
|
||||||
|
other.includeReplies == includeReplies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -255,6 +269,7 @@ class PostListNotifierProvider
|
|||||||
hash = _SystemHash.combine(hash, tags.hashCode);
|
hash = _SystemHash.combine(hash, tags.hashCode);
|
||||||
hash = _SystemHash.combine(hash, pinned.hashCode);
|
hash = _SystemHash.combine(hash, pinned.hashCode);
|
||||||
hash = _SystemHash.combine(hash, shuffle.hashCode);
|
hash = _SystemHash.combine(hash, shuffle.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, includeReplies.hashCode);
|
||||||
|
|
||||||
return _SystemHash.finish(hash);
|
return _SystemHash.finish(hash);
|
||||||
}
|
}
|
||||||
@@ -284,6 +299,9 @@ mixin PostListNotifierRef
|
|||||||
|
|
||||||
/// The parameter `shuffle` of this provider.
|
/// The parameter `shuffle` of this provider.
|
||||||
bool get shuffle;
|
bool get shuffle;
|
||||||
|
|
||||||
|
/// The parameter `includeReplies` of this provider.
|
||||||
|
bool? get includeReplies;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PostListNotifierProviderElement
|
class _PostListNotifierProviderElement
|
||||||
@@ -310,6 +328,9 @@ class _PostListNotifierProviderElement
|
|||||||
bool? get pinned => (origin as PostListNotifierProvider).pinned;
|
bool? get pinned => (origin as PostListNotifierProvider).pinned;
|
||||||
@override
|
@override
|
||||||
bool get shuffle => (origin as PostListNotifierProvider).shuffle;
|
bool get shuffle => (origin as PostListNotifierProvider).shuffle;
|
||||||
|
@override
|
||||||
|
bool? get includeReplies =>
|
||||||
|
(origin as PostListNotifierProvider).includeReplies;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:easy_localization/easy_localization.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:go_router/go_router.dart';
|
import 'package:gap/gap.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/models/publisher.dart';
|
import 'package:island/models/publisher.dart';
|
||||||
@@ -10,6 +10,7 @@ import 'package:island/screens/creators/publishers_form.dart';
|
|||||||
import 'package:island/screens/posts/compose.dart';
|
import 'package:island/screens/posts/compose.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/post/compose_dialog.dart';
|
||||||
import 'package:island/widgets/post/publishers_modal.dart';
|
import 'package:island/widgets/post/publishers_modal.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';
|
||||||
@@ -67,81 +68,98 @@ class PostQuickReply extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const kInputChipHeight = 54.0;
|
||||||
|
|
||||||
return publishers.when(
|
return publishers.when(
|
||||||
data:
|
data:
|
||||||
(data) => Row(
|
(data) => Material(
|
||||||
spacing: 8,
|
elevation: 2,
|
||||||
children: [
|
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
GestureDetector(
|
borderRadius: BorderRadius.circular(28),
|
||||||
child: ProfilePictureWidget(
|
child: Container(
|
||||||
fileId: currentPublisher.value?.picture?.id,
|
constraints: BoxConstraints(minHeight: kInputChipHeight),
|
||||||
radius: 16,
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||||
),
|
child: Row(
|
||||||
onTap: () {
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
showModalBottomSheet(
|
children: [
|
||||||
isScrollControlled: true,
|
GestureDetector(
|
||||||
context: context,
|
child: ProfilePictureWidget(
|
||||||
builder: (context) => PublisherModal(),
|
fileId: currentPublisher.value?.picture?.id,
|
||||||
).then((value) {
|
radius: (kInputChipHeight * 0.5) - 6,
|
||||||
if (value is SnPublisher) currentPublisher.value = value;
|
),
|
||||||
});
|
onTap: () {
|
||||||
},
|
showModalBottomSheet(
|
||||||
).padding(right: 4),
|
isScrollControlled: true,
|
||||||
Expanded(
|
context: context,
|
||||||
child: TextField(
|
builder: (context) => PublisherModal(),
|
||||||
controller: contentController,
|
).then((value) {
|
||||||
decoration: InputDecoration(
|
if (value is SnPublisher) {
|
||||||
hintText: 'postReplyPlaceholder'.tr(),
|
currentPublisher.value = value;
|
||||||
border: InputBorder.none,
|
}
|
||||||
isDense: true,
|
});
|
||||||
isCollapsed: true,
|
},
|
||||||
contentPadding: EdgeInsets.symmetric(
|
).padding(right: 12),
|
||||||
horizontal: 12,
|
Expanded(
|
||||||
vertical: 8,
|
child: TextField(
|
||||||
|
controller: contentController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'postReplyPlaceholder'.tr(),
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
isCollapsed: true,
|
||||||
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 14,
|
||||||
|
),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
),
|
||||||
|
style: TextStyle(fontSize: 14),
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 5,
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: TextStyle(fontSize: 14),
|
const Gap(8),
|
||||||
maxLines: null,
|
IconButton(
|
||||||
onTapOutside:
|
onPressed: () async {
|
||||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onLaunch?.call();
|
||||||
),
|
final value = await PostComposeDialog.show(
|
||||||
),
|
context,
|
||||||
IconButton(
|
initialState: PostComposeInitialState(
|
||||||
onPressed: () {
|
|
||||||
onLaunch?.call();
|
|
||||||
GoRouter.of(context)
|
|
||||||
.pushNamed(
|
|
||||||
'postCompose',
|
|
||||||
extra: PostComposeInitialState(
|
|
||||||
content: contentController.text,
|
content: contentController.text,
|
||||||
replyingTo: parent,
|
replyingTo: parent,
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
.then((value) {
|
if (value != null) onPosted?.call();
|
||||||
if (value != null) onPosted?.call();
|
},
|
||||||
});
|
icon: const Icon(Symbols.launch, size: 20),
|
||||||
},
|
visualDensity: VisualDensity.compact,
|
||||||
icon: const Icon(Symbols.launch, size: 20),
|
constraints: BoxConstraints(
|
||||||
padding: EdgeInsets.zero,
|
maxHeight: kInputChipHeight - 6,
|
||||||
visualDensity: VisualDensity.compact,
|
minHeight: kInputChipHeight - 6,
|
||||||
constraints: const BoxConstraints(),
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon:
|
||||||
|
submitting.value
|
||||||
|
? SizedBox(
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 3),
|
||||||
|
)
|
||||||
|
: Icon(Symbols.send, size: 20),
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
onPressed: submitting.value ? null : performAction,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxHeight: kInputChipHeight - 6,
|
||||||
|
minHeight: kInputChipHeight - 6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
IconButton(
|
),
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
icon:
|
|
||||||
submitting.value
|
|
||||||
? SizedBox(
|
|
||||||
width: 28,
|
|
||||||
height: 28,
|
|
||||||
child: CircularProgressIndicator(strokeWidth: 3),
|
|
||||||
)
|
|
||||||
: Icon(Symbols.send, size: 20),
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
onPressed: submitting.value ? null : performAction,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
loading: () => const SizedBox.shrink(),
|
loading: () => const SizedBox.shrink(),
|
||||||
error: (e, _) => const SizedBox.shrink(),
|
error: (e, _) => const SizedBox.shrink(),
|
||||||
|
|||||||
698
lib/widgets/post/post_reaction_sheet.dart
Normal file
698
lib/widgets/post/post_reaction_sheet.dart
Normal file
@@ -0,0 +1,698 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_popup_card/flutter_popup_card.dart';
|
||||||
|
import 'package:gap/gap.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/services/time.dart';
|
||||||
|
import 'package:island/widgets/account/account_pfc.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:island/widgets/stickers/sticker_picker.dart';
|
||||||
|
import 'package:island/pods/config.dart';
|
||||||
|
|
||||||
|
part 'post_reaction_sheet.g.dart';
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
class ReactionListNotifier extends _$ReactionListNotifier
|
||||||
|
with CursorPagingNotifierMixin<SnPostReaction> {
|
||||||
|
static const int _pageSize = 20;
|
||||||
|
|
||||||
|
int? totalCount;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CursorPagingData<SnPostReaction>> build({
|
||||||
|
required String symbol,
|
||||||
|
required String postId,
|
||||||
|
}) {
|
||||||
|
return fetch(cursor: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CursorPagingData<SnPostReaction>> fetch({
|
||||||
|
required String? cursor,
|
||||||
|
}) async {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||||
|
|
||||||
|
final response = await client.get(
|
||||||
|
'/sphere/posts/$postId/reactions',
|
||||||
|
queryParameters: {'symbol': symbol, 'offset': offset, 'take': _pageSize},
|
||||||
|
);
|
||||||
|
|
||||||
|
totalCount = int.tryParse(response.headers.value('x-total') ?? '0') ?? 0;
|
||||||
|
|
||||||
|
final List<dynamic> data = response.data;
|
||||||
|
final reactions =
|
||||||
|
data.map((json) => SnPostReaction.fromJson(json)).toList();
|
||||||
|
|
||||||
|
final hasMore = reactions.length == _pageSize;
|
||||||
|
final nextCursor = hasMore ? (offset + reactions.length).toString() : null;
|
||||||
|
|
||||||
|
return CursorPagingData(
|
||||||
|
items: reactions,
|
||||||
|
hasMore: hasMore,
|
||||||
|
nextCursor: nextCursor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const kAvailableStickers = {
|
||||||
|
'angry',
|
||||||
|
'clap',
|
||||||
|
'confuse',
|
||||||
|
'pray',
|
||||||
|
'thumb_up',
|
||||||
|
'party',
|
||||||
|
};
|
||||||
|
|
||||||
|
bool _getReactionImageAvailable(String symbol) {
|
||||||
|
return kAvailableStickers.contains(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildReactionIcon(String symbol, double size, {double iconSize = 24}) {
|
||||||
|
if (_getReactionImageAvailable(symbol)) {
|
||||||
|
return Image.asset(
|
||||||
|
'assets/images/stickers/$symbol.png',
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Text(
|
||||||
|
kReactionTemplates[symbol]?.icon ?? '',
|
||||||
|
style: TextStyle(fontSize: iconSize),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostReactionSheet extends StatelessWidget {
|
||||||
|
final Map<String, int> reactionsCount;
|
||||||
|
final Map<String, bool> reactionsMade;
|
||||||
|
final Function(String symbol, int attitude) onReact;
|
||||||
|
final String postId;
|
||||||
|
const PostReactionSheet({
|
||||||
|
super.key,
|
||||||
|
required this.reactionsCount,
|
||||||
|
required this.reactionsMade,
|
||||||
|
required this.onReact,
|
||||||
|
required this.postId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DefaultTabController(
|
||||||
|
length: 2,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 16,
|
||||||
|
left: 20,
|
||||||
|
right: 16,
|
||||||
|
bottom: 12,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'reactions'.plural(
|
||||||
|
reactionsCount.isNotEmpty
|
||||||
|
? reactionsCount.values.reduce((a, b) => a + b)
|
||||||
|
: 0,
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
letterSpacing: -0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.close),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
TabBar(tabs: [Tab(text: 'overview'.tr()), Tab(text: 'custom'.tr())]),
|
||||||
|
const Divider(height: 1),
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
children: [
|
||||||
|
ListView(
|
||||||
|
children: [
|
||||||
|
_buildCustomReactionSection(context),
|
||||||
|
_buildReactionSection(
|
||||||
|
context,
|
||||||
|
Symbols.mood,
|
||||||
|
'reactionPositive'.tr(),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
_buildReactionSection(
|
||||||
|
context,
|
||||||
|
Symbols.sentiment_neutral,
|
||||||
|
'reactionNeutral'.tr(),
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
_buildReactionSection(
|
||||||
|
context,
|
||||||
|
Symbols.mood_bad,
|
||||||
|
'reactionNegative'.tr(),
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
CustomReactionForm(
|
||||||
|
postId: postId,
|
||||||
|
onReact: (s, a) => onReact(s.replaceAll(':', ''), a),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCustomReactionSection(BuildContext context) {
|
||||||
|
final customReactions =
|
||||||
|
reactionsCount.entries
|
||||||
|
.where((entry) => entry.key.contains('+'))
|
||||||
|
.map((entry) => entry.key)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (customReactions.isEmpty) return const SizedBox.shrink();
|
||||||
|
|
||||||
|
return HookConsumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.emoji_symbols),
|
||||||
|
Text('customReactions'.tr()).fontSize(17).bold(),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, top: 16, bottom: 6),
|
||||||
|
SizedBox(
|
||||||
|
height: 120,
|
||||||
|
child: GridView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 1,
|
||||||
|
mainAxisExtent: 120,
|
||||||
|
mainAxisSpacing: 8.0,
|
||||||
|
crossAxisSpacing: 8.0,
|
||||||
|
childAspectRatio: 1.0,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
itemCount: customReactions.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final symbol = customReactions[index];
|
||||||
|
final count = reactionsCount[symbol] ?? 0;
|
||||||
|
final stickerUri =
|
||||||
|
'$baseUrl/sphere/stickers/lookup/$symbol/open';
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onLongPressStart: (details) {
|
||||||
|
if (count > 0) {
|
||||||
|
showReactionDetailsPopup(
|
||||||
|
context,
|
||||||
|
symbol,
|
||||||
|
details.localPosition,
|
||||||
|
postId,
|
||||||
|
reactionsCount[symbol] ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSecondaryTapUp: (details) {
|
||||||
|
if (count > 0) {
|
||||||
|
showReactionDetailsPopup(
|
||||||
|
context,
|
||||||
|
symbol,
|
||||||
|
details.localPosition,
|
||||||
|
postId,
|
||||||
|
reactionsCount[symbol] ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Badge(
|
||||||
|
label: Text('x$count'),
|
||||||
|
isLabelVisible: count > 0,
|
||||||
|
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
offset: Offset(0, 0),
|
||||||
|
child: Card(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceContainerLowest,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(8),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
onReact(
|
||||||
|
symbol,
|
||||||
|
1,
|
||||||
|
); // Custom reactions use neutral attitude
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: double.infinity),
|
||||||
|
Container(
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
image: DecorationImage(
|
||||||
|
image: NetworkImage(stickerUri),
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
colorFilter:
|
||||||
|
(reactionsMade[symbol] ?? false)
|
||||||
|
? ColorFilter.mode(
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primaryContainer
|
||||||
|
.withOpacity(0.7),
|
||||||
|
BlendMode.srcATop,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
symbol,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: Colors.white,
|
||||||
|
shadows: [
|
||||||
|
Shadow(
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: Offset(0.5, 0.5),
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildReactionSection(
|
||||||
|
BuildContext context,
|
||||||
|
IconData icon,
|
||||||
|
String title,
|
||||||
|
int attitude,
|
||||||
|
) {
|
||||||
|
final allReactions =
|
||||||
|
kReactionTemplates.entries
|
||||||
|
.where((entry) => entry.value.attitude == attitude)
|
||||||
|
.map((entry) => entry.key)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
spacing: 8,
|
||||||
|
children: [Icon(icon), Text(title).fontSize(17).bold()],
|
||||||
|
).padding(horizontal: 24, top: 16, bottom: 6),
|
||||||
|
SizedBox(
|
||||||
|
height: 120,
|
||||||
|
child: GridView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 1,
|
||||||
|
mainAxisExtent: 120,
|
||||||
|
mainAxisSpacing: 8.0,
|
||||||
|
crossAxisSpacing: 8.0,
|
||||||
|
childAspectRatio: 1.0,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
itemCount: allReactions.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final symbol = allReactions[index];
|
||||||
|
final count = reactionsCount[symbol] ?? 0;
|
||||||
|
final hasImage = _getReactionImageAvailable(symbol);
|
||||||
|
return GestureDetector(
|
||||||
|
onLongPressStart: (details) {
|
||||||
|
if (count > 0) {
|
||||||
|
showReactionDetailsPopup(
|
||||||
|
context,
|
||||||
|
symbol,
|
||||||
|
details.localPosition,
|
||||||
|
postId,
|
||||||
|
reactionsCount[symbol] ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSecondaryTapUp: (details) {
|
||||||
|
if (count > 0) {
|
||||||
|
showReactionDetailsPopup(
|
||||||
|
context,
|
||||||
|
symbol,
|
||||||
|
details.localPosition,
|
||||||
|
postId,
|
||||||
|
reactionsCount[symbol] ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Badge(
|
||||||
|
label: Text('x$count'),
|
||||||
|
isLabelVisible: count > 0,
|
||||||
|
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
offset: Offset(0, 0),
|
||||||
|
child: Card(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerLowest,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
onTap: () {
|
||||||
|
onReact(symbol, attitude);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
decoration:
|
||||||
|
hasImage
|
||||||
|
? BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(
|
||||||
|
'assets/images/stickers/$symbol.png',
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
colorFilter:
|
||||||
|
(reactionsMade[symbol] ?? false)
|
||||||
|
? ColorFilter.mode(
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primaryContainer
|
||||||
|
.withOpacity(0.7),
|
||||||
|
BlendMode.srcATop,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
if (hasImage)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.bottomCenter,
|
||||||
|
end: Alignment.topCenter,
|
||||||
|
colors: [
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerHighest
|
||||||
|
.withOpacity(0.7),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
stops: [0.0, 0.3],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment:
|
||||||
|
hasImage
|
||||||
|
? MainAxisAlignment.end
|
||||||
|
: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (!hasImage) _buildReactionIcon(symbol, 36),
|
||||||
|
Text(
|
||||||
|
ReactInfo.getTranslationKey(symbol),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: hasImage ? Colors.white : null,
|
||||||
|
shadows:
|
||||||
|
hasImage
|
||||||
|
? [
|
||||||
|
const Shadow(
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: Offset(0.5, 0.5),
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
).tr(),
|
||||||
|
if (hasImage) const Gap(4),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReactionDetailsPopup extends HookConsumerWidget {
|
||||||
|
final String symbol;
|
||||||
|
final String postId;
|
||||||
|
final int totalCount;
|
||||||
|
const ReactionDetailsPopup({
|
||||||
|
super.key,
|
||||||
|
required this.symbol,
|
||||||
|
required this.postId,
|
||||||
|
required this.totalCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final provider = reactionListNotifierProvider(
|
||||||
|
symbol: symbol,
|
||||||
|
postId: postId,
|
||||||
|
);
|
||||||
|
|
||||||
|
final width = math.min(MediaQuery.of(context).size.width * 0.8, 480.0);
|
||||||
|
return PopupCard(
|
||||||
|
elevation: 8,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||||
|
child: SizedBox(
|
||||||
|
width: width,
|
||||||
|
height: 400,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_buildReactionIcon(symbol, 24),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
ReactInfo.getTranslationKey(symbol),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
).tr(),
|
||||||
|
const Spacer(),
|
||||||
|
Text('reactions'.plural(totalCount)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
Expanded(
|
||||||
|
child: PagingHelperView(
|
||||||
|
provider: provider,
|
||||||
|
futureRefreshable: provider.future,
|
||||||
|
notifierRefreshable: provider.notifier,
|
||||||
|
contentBuilder:
|
||||||
|
(data, widgetCount, endItemView) => ListView.builder(
|
||||||
|
itemCount: widgetCount,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == widgetCount - 1) {
|
||||||
|
return endItemView;
|
||||||
|
}
|
||||||
|
|
||||||
|
final reaction = data.items[index];
|
||||||
|
return ListTile(
|
||||||
|
leading: AccountPfcGestureDetector(
|
||||||
|
uname: reaction.account?.name ?? 'unknown',
|
||||||
|
child: ProfilePictureWidget(
|
||||||
|
file: reaction.account?.profile.picture,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(reaction.account?.nick ?? 'unknown'.tr()),
|
||||||
|
subtitle: Text(
|
||||||
|
'${reaction.createdAt.formatRelative(context)} · ${reaction.createdAt.formatSystem()}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomReactionForm extends HookConsumerWidget {
|
||||||
|
final String postId;
|
||||||
|
final Function(String symbol, int attitude) onReact;
|
||||||
|
|
||||||
|
const CustomReactionForm({
|
||||||
|
super.key,
|
||||||
|
required this.postId,
|
||||||
|
required this.onReact,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final attitude = useState<int>(1);
|
||||||
|
final symbol = useState<String>('');
|
||||||
|
|
||||||
|
Future<void> submitCustomReaction() async {
|
||||||
|
if (symbol.value.isEmpty) return;
|
||||||
|
onReact(symbol.value, attitude.value);
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'customReaction'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'stickerPlaceholder'.tr(),
|
||||||
|
hintText: 'prefix+slug',
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
suffixIcon: InkWell(
|
||||||
|
onTapDown: (details) async {
|
||||||
|
await showStickerPickerPopover(
|
||||||
|
context,
|
||||||
|
details.globalPosition.translate(-300, -280),
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
onPick: (placeholder) {
|
||||||
|
// Remove the surrounding : from the placeholder
|
||||||
|
symbol.value = placeholder.substring(
|
||||||
|
1,
|
||||||
|
placeholder.length - 1,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Icon(Symbols.sticky_note_2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
controller: TextEditingController(text: symbol.value),
|
||||||
|
onChanged: (value) => symbol.value = value,
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
Text(
|
||||||
|
'reactionAttitude'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
SegmentedButton(
|
||||||
|
segments: [
|
||||||
|
ButtonSegment(
|
||||||
|
value: 0,
|
||||||
|
icon: const Icon(Symbols.sentiment_satisfied),
|
||||||
|
label: Text('attitudePositive'.tr()),
|
||||||
|
),
|
||||||
|
ButtonSegment(
|
||||||
|
value: 1,
|
||||||
|
icon: const Icon(Symbols.sentiment_stressed),
|
||||||
|
label: Text('attitudeNeutral'.tr()),
|
||||||
|
),
|
||||||
|
ButtonSegment(
|
||||||
|
value: 2,
|
||||||
|
icon: const Icon(Symbols.sentiment_sad),
|
||||||
|
label: Text('attitudeNegative'.tr()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
selected: {attitude.value},
|
||||||
|
onSelectionChanged: (Set<int> newSelection) {
|
||||||
|
attitude.value = newSelection.first;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Gap(32),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed: symbol.value.isEmpty ? null : submitCustomReaction,
|
||||||
|
icon: const Icon(Symbols.send),
|
||||||
|
label: Text('addReaction'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Gap(MediaQuery.of(context).padding.bottom + 24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> showReactionDetailsPopup(
|
||||||
|
BuildContext context,
|
||||||
|
String symbol,
|
||||||
|
Offset offset,
|
||||||
|
String postId,
|
||||||
|
int totalCount,
|
||||||
|
) async {
|
||||||
|
await showPopupCard<void>(
|
||||||
|
offset: offset,
|
||||||
|
context: context,
|
||||||
|
builder:
|
||||||
|
(context) => ReactionDetailsPopup(
|
||||||
|
symbol: symbol,
|
||||||
|
postId: postId,
|
||||||
|
totalCount: totalCount,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
dimBackground: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
206
lib/widgets/post/post_reaction_sheet.g.dart
Normal file
206
lib/widgets/post/post_reaction_sheet.g.dart
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'post_reaction_sheet.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$reactionListNotifierHash() =>
|
||||||
|
r'92cf80d2461e46ca62cf6e6a37f8b16c239e7449';
|
||||||
|
|
||||||
|
/// Copied from Dart SDK
|
||||||
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
|
static int combine(int hash, int value) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + value);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
|
return hash ^ (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int finish(int hash) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = hash ^ (hash >> 11);
|
||||||
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _$ReactionListNotifier
|
||||||
|
extends
|
||||||
|
BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnPostReaction>> {
|
||||||
|
late final String symbol;
|
||||||
|
late final String postId;
|
||||||
|
|
||||||
|
FutureOr<CursorPagingData<SnPostReaction>> build({
|
||||||
|
required String symbol,
|
||||||
|
required String postId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [ReactionListNotifier].
|
||||||
|
@ProviderFor(ReactionListNotifier)
|
||||||
|
const reactionListNotifierProvider = ReactionListNotifierFamily();
|
||||||
|
|
||||||
|
/// See also [ReactionListNotifier].
|
||||||
|
class ReactionListNotifierFamily
|
||||||
|
extends Family<AsyncValue<CursorPagingData<SnPostReaction>>> {
|
||||||
|
/// See also [ReactionListNotifier].
|
||||||
|
const ReactionListNotifierFamily();
|
||||||
|
|
||||||
|
/// See also [ReactionListNotifier].
|
||||||
|
ReactionListNotifierProvider call({
|
||||||
|
required String symbol,
|
||||||
|
required String postId,
|
||||||
|
}) {
|
||||||
|
return ReactionListNotifierProvider(symbol: symbol, postId: postId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReactionListNotifierProvider getProviderOverride(
|
||||||
|
covariant ReactionListNotifierProvider provider,
|
||||||
|
) {
|
||||||
|
return call(symbol: provider.symbol, postId: provider.postId);
|
||||||
|
}
|
||||||
|
|
||||||
|
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'reactionListNotifierProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [ReactionListNotifier].
|
||||||
|
class ReactionListNotifierProvider
|
||||||
|
extends
|
||||||
|
AutoDisposeAsyncNotifierProviderImpl<
|
||||||
|
ReactionListNotifier,
|
||||||
|
CursorPagingData<SnPostReaction>
|
||||||
|
> {
|
||||||
|
/// See also [ReactionListNotifier].
|
||||||
|
ReactionListNotifierProvider({required String symbol, required String postId})
|
||||||
|
: this._internal(
|
||||||
|
() =>
|
||||||
|
ReactionListNotifier()
|
||||||
|
..symbol = symbol
|
||||||
|
..postId = postId,
|
||||||
|
from: reactionListNotifierProvider,
|
||||||
|
name: r'reactionListNotifierProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$reactionListNotifierHash,
|
||||||
|
dependencies: ReactionListNotifierFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
ReactionListNotifierFamily._allTransitiveDependencies,
|
||||||
|
symbol: symbol,
|
||||||
|
postId: postId,
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactionListNotifierProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.symbol,
|
||||||
|
required this.postId,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String symbol;
|
||||||
|
final String postId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<CursorPagingData<SnPostReaction>> runNotifierBuild(
|
||||||
|
covariant ReactionListNotifier notifier,
|
||||||
|
) {
|
||||||
|
return notifier.build(symbol: symbol, postId: postId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(ReactionListNotifier Function() create) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: ReactionListNotifierProvider._internal(
|
||||||
|
() =>
|
||||||
|
create()
|
||||||
|
..symbol = symbol
|
||||||
|
..postId = postId,
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
symbol: symbol,
|
||||||
|
postId: postId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeAsyncNotifierProviderElement<
|
||||||
|
ReactionListNotifier,
|
||||||
|
CursorPagingData<SnPostReaction>
|
||||||
|
>
|
||||||
|
createElement() {
|
||||||
|
return _ReactionListNotifierProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is ReactionListNotifierProvider &&
|
||||||
|
other.symbol == symbol &&
|
||||||
|
other.postId == postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, symbol.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, postId.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin ReactionListNotifierRef
|
||||||
|
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnPostReaction>> {
|
||||||
|
/// The parameter `symbol` of this provider.
|
||||||
|
String get symbol;
|
||||||
|
|
||||||
|
/// The parameter `postId` of this provider.
|
||||||
|
String get postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReactionListNotifierProviderElement
|
||||||
|
extends
|
||||||
|
AutoDisposeAsyncNotifierProviderElement<
|
||||||
|
ReactionListNotifier,
|
||||||
|
CursorPagingData<SnPostReaction>
|
||||||
|
>
|
||||||
|
with ReactionListNotifierRef {
|
||||||
|
_ReactionListNotifierProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get symbol => (origin as ReactionListNotifierProvider).symbol;
|
||||||
|
@override
|
||||||
|
String get postId => (origin as ReactionListNotifierProvider).postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.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/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
@@ -19,26 +20,24 @@ class PostRepliesSheet extends HookConsumerWidget {
|
|||||||
|
|
||||||
return SheetScaffold(
|
return SheetScaffold(
|
||||||
titleText: 'repliesCount'.plural(post.repliesCount),
|
titleText: 'repliesCount'.plural(post.repliesCount),
|
||||||
child: Column(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
// Replies list
|
CustomScrollView(
|
||||||
Expanded(
|
slivers: [
|
||||||
child: CustomScrollView(
|
PostRepliesList(
|
||||||
slivers: [
|
postId: post.id.toString(),
|
||||||
PostRepliesList(
|
onOpen: () {
|
||||||
postId: post.id.toString(),
|
Navigator.pop(context);
|
||||||
onOpen: () {
|
},
|
||||||
Navigator.pop(context);
|
),
|
||||||
},
|
SliverGap(80),
|
||||||
),
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
// Quick reply section
|
|
||||||
if (user.value != null)
|
if (user.value != null)
|
||||||
Material(
|
Positioned(
|
||||||
elevation: 2,
|
bottom: 0,
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
left: 0,
|
||||||
|
right: 0,
|
||||||
child: PostQuickReply(
|
child: PostQuickReply(
|
||||||
parent: post,
|
parent: post,
|
||||||
onPosted: () {
|
onPosted: () {
|
||||||
@@ -48,7 +47,7 @@ class PostRepliesSheet extends HookConsumerWidget {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
).padding(
|
).padding(
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 8,
|
bottom: MediaQuery.of(context).padding.bottom + 16,
|
||||||
top: 8,
|
top: 8,
|
||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
class MenuItemData {
|
class UploadMenuItemData {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final String textKey;
|
final String textKey;
|
||||||
final VoidCallback onPressed;
|
final VoidCallback onPressed;
|
||||||
|
|
||||||
const MenuItemData(this.icon, this.textKey, this.onPressed);
|
const UploadMenuItemData(this.icon, this.textKey, this.onPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
class UploadMenu extends StatelessWidget {
|
class UploadMenu extends StatelessWidget {
|
||||||
final List<MenuItemData> items;
|
final List<UploadMenuItemData> items;
|
||||||
final bool isCompact;
|
final bool isCompact;
|
||||||
final Color? iconColor;
|
final Color? iconColor;
|
||||||
|
|
||||||
@@ -51,6 +51,7 @@ class UploadMenu extends StatelessWidget {
|
|||||||
onPressed: item.onPressed,
|
onPressed: item.onPressed,
|
||||||
leadingIcon: Icon(item.icon),
|
leadingIcon: Icon(item.icon),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
padding: WidgetStatePropertyAll(
|
padding: WidgetStatePropertyAll(
|
||||||
EdgeInsets.only(left: 12, right: 16, top: 20, bottom: 20),
|
EdgeInsets.only(left: 12, right: 16, top: 20, bottom: 20),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
import 'package:flutter_popup_card/flutter_popup_card.dart';
|
import 'package:flutter_popup_card/flutter_popup_card.dart';
|
||||||
import 'package:island/widgets/extended_refresh_indicator.dart';
|
import 'package:island/widgets/extended_refresh_indicator.dart';
|
||||||
|
|
||||||
part 'picker.g.dart';
|
part 'sticker_picker.g.dart';
|
||||||
|
|
||||||
/// Fetch user-added sticker packs (with stickers) from API:
|
/// Fetch user-added sticker packs (with stickers) from API:
|
||||||
/// GET /sphere/stickers/me
|
/// GET /sphere/stickers/me
|
||||||
@@ -34,7 +34,7 @@ Future<List<SnStickerPack>> myStickerPacks(Ref ref) async {
|
|||||||
/// Sticker Picker popover dialog
|
/// Sticker Picker popover dialog
|
||||||
/// - Displays user-owned sticker packs as tabs (chips)
|
/// - Displays user-owned sticker packs as tabs (chips)
|
||||||
/// - Shows grid of stickers in selected pack
|
/// - Shows grid of stickers in selected pack
|
||||||
/// - On tap, returns placeholder string :{prefix}{slug}: via onPick callback
|
/// - On tap, returns placeholder string :{prefix}+{slug}: via onPick callback
|
||||||
class StickerPicker extends HookConsumerWidget {
|
class StickerPicker extends HookConsumerWidget {
|
||||||
final void Function(String placeholder) onPick;
|
final void Function(String placeholder) onPick;
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ class StickerPicker extends HookConsumerWidget {
|
|||||||
return _PackSwitcher(
|
return _PackSwitcher(
|
||||||
packs: packs,
|
packs: packs,
|
||||||
onPick: (pack, sticker) {
|
onPick: (pack, sticker) {
|
||||||
final placeholder = ':${pack.prefix}${sticker.slug}:';
|
final placeholder = ':${pack.prefix}+${sticker.slug}:';
|
||||||
HapticFeedback.selectionClick();
|
HapticFeedback.selectionClick();
|
||||||
onPick(placeholder);
|
onPick(placeholder);
|
||||||
if (Navigator.of(context).canPop()) {
|
if (Navigator.of(context).canPop()) {
|
||||||
@@ -247,7 +247,7 @@ class _StickersGrid extends StatelessWidget {
|
|||||||
itemCount: stickers.length,
|
itemCount: stickers.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final sticker = stickers[index];
|
final sticker = stickers[index];
|
||||||
final placeholder = ':${pack.prefix}${sticker.slug}:';
|
final placeholder = ':${pack.prefix}+${sticker.slug}:';
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
message: placeholder,
|
message: placeholder,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@@ -282,13 +282,14 @@ class _StickersGrid extends StatelessWidget {
|
|||||||
Future<void> showStickerPickerPopover(
|
Future<void> showStickerPickerPopover(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Offset offset, {
|
Offset offset, {
|
||||||
|
Alignment? alignment,
|
||||||
required void Function(String placeholder) onPick,
|
required void Function(String placeholder) onPick,
|
||||||
}) async {
|
}) async {
|
||||||
// Use flutter_popup_card to present the anchored popup near trigger.
|
// Use flutter_popup_card to present the anchored popup near trigger.
|
||||||
await showPopupCard<void>(
|
await showPopupCard<void>(
|
||||||
context: context,
|
context: context,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
alignment: Alignment.topLeft,
|
alignment: alignment ?? Alignment.topLeft,
|
||||||
dimBackground: true,
|
dimBackground: true,
|
||||||
builder:
|
builder:
|
||||||
(ctx) => SizedBox(
|
(ctx) => SizedBox(
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'picker.dart';
|
part of 'sticker_picker.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
@@ -13,85 +13,85 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- file_selector_macos (0.0.1):
|
- file_selector_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- Firebase/CoreOnly (12.2.0):
|
- Firebase/CoreOnly (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- Firebase/Crashlytics (12.2.0):
|
- Firebase/Crashlytics (12.4.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseCrashlytics (~> 12.2.0)
|
- FirebaseCrashlytics (~> 12.4.0)
|
||||||
- Firebase/Messaging (12.2.0):
|
- Firebase/Messaging (12.4.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 12.2.0)
|
- FirebaseMessaging (~> 12.4.0)
|
||||||
- firebase_analytics (12.0.2):
|
- firebase_analytics (12.0.3):
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- FirebaseAnalytics (= 12.2.0)
|
- FirebaseAnalytics (= 12.4.0)
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- firebase_core (4.1.1):
|
- firebase_core (4.2.0):
|
||||||
- Firebase/CoreOnly (~> 12.2.0)
|
- Firebase/CoreOnly (~> 12.4.0)
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- firebase_crashlytics (5.0.2):
|
- firebase_crashlytics (5.0.3):
|
||||||
- Firebase/CoreOnly (~> 12.2.0)
|
- Firebase/CoreOnly (~> 12.4.0)
|
||||||
- Firebase/Crashlytics (~> 12.2.0)
|
- Firebase/Crashlytics (~> 12.4.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- firebase_messaging (16.0.2):
|
- firebase_messaging (16.0.3):
|
||||||
- Firebase/CoreOnly (~> 12.2.0)
|
- Firebase/CoreOnly (~> 12.4.0)
|
||||||
- Firebase/Messaging (~> 12.2.0)
|
- Firebase/Messaging (~> 12.4.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- FirebaseAnalytics (12.2.0):
|
- FirebaseAnalytics (12.4.0):
|
||||||
- FirebaseAnalytics/Default (= 12.2.0)
|
- FirebaseAnalytics/Default (= 12.4.0)
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.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.2.0):
|
- FirebaseAnalytics/Default (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.0)
|
||||||
- GoogleAppMeasurement/Default (= 12.2.0)
|
- GoogleAppMeasurement/Default (= 12.4.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.2.0):
|
- FirebaseCore (12.4.0):
|
||||||
- FirebaseCoreInternal (~> 12.2.0)
|
- FirebaseCoreInternal (~> 12.4.0)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/Logger (~> 8.1)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
- FirebaseCoreExtension (12.2.0):
|
- FirebaseCoreExtension (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseCoreInternal (12.2.0):
|
- FirebaseCoreInternal (12.4.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- FirebaseCrashlytics (12.2.0):
|
- FirebaseCrashlytics (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.0)
|
||||||
- FirebaseRemoteConfigInterop (~> 12.2.0)
|
- FirebaseRemoteConfigInterop (~> 12.4.0)
|
||||||
- FirebaseSessions (~> 12.2.0)
|
- FirebaseSessions (~> 12.4.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.2.0):
|
- FirebaseInstallations (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.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.2.0):
|
- FirebaseMessaging (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.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.2.0)
|
- FirebaseRemoteConfigInterop (12.4.0)
|
||||||
- FirebaseSessions (12.2.0):
|
- FirebaseSessions (12.4.0):
|
||||||
- FirebaseCore (~> 12.2.0)
|
- FirebaseCore (~> 12.4.0)
|
||||||
- FirebaseCoreExtension (~> 12.2.0)
|
- FirebaseCoreExtension (~> 12.4.0)
|
||||||
- FirebaseInstallations (~> 12.2.0)
|
- FirebaseInstallations (~> 12.4.0)
|
||||||
- GoogleDataTransport (~> 10.1)
|
- GoogleDataTransport (~> 10.1)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
@@ -118,23 +118,23 @@ PODS:
|
|||||||
- gal (1.0.0):
|
- gal (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- GoogleAppMeasurement/Core (12.2.0):
|
- GoogleAppMeasurement/Core (12.4.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.2.0):
|
- GoogleAppMeasurement/Default (12.4.0):
|
||||||
- GoogleAdsOnDeviceConversion (= 2.3.0)
|
- GoogleAdsOnDeviceConversion (~> 3.1.0)
|
||||||
- GoogleAppMeasurement/Core (= 12.2.0)
|
- GoogleAppMeasurement/Core (= 12.4.0)
|
||||||
- GoogleAppMeasurement/IdentitySupport (= 12.2.0)
|
- GoogleAppMeasurement/IdentitySupport (= 12.4.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.2.0):
|
- GoogleAppMeasurement/IdentitySupport (12.4.0):
|
||||||
- GoogleAppMeasurement/Core (= 12.2.0)
|
- GoogleAppMeasurement/Core (= 12.4.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)
|
||||||
@@ -416,20 +416,20 @@ SPEC CHECKSUMS:
|
|||||||
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||||
file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f
|
file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f
|
||||||
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
|
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
|
||||||
Firebase: 26f6f8d460603af3df970ad505b16b15f5e2e9a1
|
Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e
|
||||||
firebase_analytics: 26346c2ccb9ba410c2f33d5d34c62e6369cbbf29
|
firebase_analytics: d876586269c1d8d2b3dcac085bc2d97c62abc9df
|
||||||
firebase_core: 54fd706197e1779d510b297548eee74d3b39577c
|
firebase_core: d81d1a44df95699ce074ae63d8cb43e9df21e142
|
||||||
firebase_crashlytics: 3694b4aca0849f6919244d7bbbb40615f989f46b
|
firebase_crashlytics: 723622cc39a9fa7320585424f5864c5699893ce1
|
||||||
firebase_messaging: 658f1a6906d80faec2fb20e3aadb81af6b09e441
|
firebase_messaging: 31f412ae5a54e02d1c46d467969f7ad92c4b81ec
|
||||||
FirebaseAnalytics: e04e23bc070e3014aa5cf4980f9df7ce5cd79ec8
|
FirebaseAnalytics: 0fc2b20091f0ddd21bf73397cf8f0eb5346dc24f
|
||||||
FirebaseCore: 311c48a147ad4a0ab7febbaed89e8025c67510cd
|
FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3
|
||||||
FirebaseCoreExtension: 73af080c22a2f7b44cefa391dc08f7e4ee162cb5
|
FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018
|
||||||
FirebaseCoreInternal: 56ea29f3dad2894f81b060f706f9d53509b6ed3b
|
FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6
|
||||||
FirebaseCrashlytics: f83cbf176d5c637ade108c0aacf1ccbd5ec499bf
|
FirebaseCrashlytics: a6ece278a837c7e88de2d9b5da0a3542f2342395
|
||||||
FirebaseInstallations: 3e884b01feabdf67582a80f3250425a00979b4ed
|
FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2
|
||||||
FirebaseMessaging: 43ec73bbfedd0c385a849bb91593ab4ad4b9e48e
|
FirebaseMessaging: d33971b7bb252745ea6cd31ab190d1a1df4b8ed5
|
||||||
FirebaseRemoteConfigInterop: 0896fd52ab72586a355c8f389ff85aaa9e5375e1
|
FirebaseRemoteConfigInterop: 1e31ec72b89c9924367c59bfb5ec9ab60d1d6766
|
||||||
FirebaseSessions: f4692789e770bec66ce17d772c0e9561c4f11737
|
FirebaseSessions: ba7c7a7ca8696a8d540eb3fe3800fbe98c79786d
|
||||||
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
||||||
flutter_local_notifications: 4bf37a31afde695b56091b4ae3e4d9c7a7e6cda0
|
flutter_local_notifications: 4bf37a31afde695b56091b4ae3e4d9c7a7e6cda0
|
||||||
flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
|
flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
|
||||||
@@ -439,7 +439,7 @@ SPEC CHECKSUMS:
|
|||||||
flutter_webrtc: 718eae22a371cd94e5d56aa4f301443ebc5bb737
|
flutter_webrtc: 718eae22a371cd94e5d56aa4f301443ebc5bb737
|
||||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||||
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
||||||
GoogleAppMeasurement: 09f341dfa8527d1612a09cbfe809a242c0b737af
|
GoogleAppMeasurement: 1e718274b7e015cefd846ac1fcf7820c70dc017d
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
|
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
|
||||||
|
|||||||
82
pubspec.lock
82
pubspec.lock
@@ -13,10 +13,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _flutterfire_internals
|
name: _flutterfire_internals
|
||||||
sha256: "23d16f00a2da8ffa997c782453c73867b0609bd90435195671a54de38a3566df"
|
sha256: f871a7d1b686bea1f13722aa51ab31554d05c81f47054d6de48cc8c45153508b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.62"
|
version: "1.3.63"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -457,6 +457,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.9.0"
|
version: "5.9.0"
|
||||||
|
dio_smart_retry:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dio_smart_retry
|
||||||
|
sha256: c8e20da5f49289fa7dce5c9c6b5b120928e3661aefa0fa2d206ea6d93f580928
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
dio_web_adapter:
|
dio_web_adapter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -621,90 +629,90 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_analytics
|
name: firebase_analytics
|
||||||
sha256: fce78440ab7b95563054039aac5e342088efed9dc009ac6f81d5cac07155d509
|
sha256: "3cfc4089e61e810ffb531af63cfde2c8cfd36f12dc14fdba359e623992311015"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.0.2"
|
version: "12.0.3"
|
||||||
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: "75bdcd2d2635c4cdcd7ec13727527751ddf2f9933e5bf1264a2387920246f3c5"
|
sha256: "775fc18d9b00a014362510a33f76f1f34deb30f69a64edcb41a7dfd0ebd9cf98"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.2"
|
version: "5.0.3"
|
||||||
firebase_analytics_web:
|
firebase_analytics_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_analytics_web
|
name: firebase_analytics_web
|
||||||
sha256: ed5767695b131cdd425ee6d49934dca80689d9df40609c0d0aa8907ee6f0f785
|
sha256: "6eafa8fef5fdca6c922ac3e353c9a093c12344a3ba996e65fd40f8db0a00d26f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0+2"
|
version: "0.6.0+3"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
sha256: "4dd96f05015c0dcceaa47711394c32971aee70169625d5e2477e7676c01ce0ee"
|
sha256: "132e1c311bc41e7d387b575df0aacdf24efbf4930365eb61042be5bde3978f03"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.1"
|
version: "4.2.0"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_platform_interface
|
name: firebase_core_platform_interface
|
||||||
sha256: "5873a370f0d232918e23a5a6137dbe4c2c47cf017301f4ea02d9d636e52f60f0"
|
sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.1"
|
version: "6.0.2"
|
||||||
firebase_core_web:
|
firebase_core_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_web
|
name: firebase_core_web
|
||||||
sha256: "61a51037312dac781f713308903bb7a1762a7f92f7bc286a3a0947fb2a713b82"
|
sha256: ecde2def458292404a4fcd3731ee4992fd631a0ec359d2d67c33baa8da5ec8ae
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.2.0"
|
||||||
firebase_crashlytics:
|
firebase_crashlytics:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_crashlytics
|
name: firebase_crashlytics
|
||||||
sha256: a636096df0d2a4bc72397bfc669a4fffc8896016a58de1a6f45a49d9ba064f94
|
sha256: "2f53d0d3c0875105b166f09bdf026026bb74f26930c6ffcd5d65b311ca5a9f58"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.2"
|
version: "5.0.3"
|
||||||
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: "1ccad077a6fc7bace97d8eace263f42e66dc23a23a839de864a4f10ac4a7c264"
|
sha256: de5c857525fc9576cd3fc30fc72422bc2371179ecae110246c0135ae896c6de3
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.8.13"
|
version: "3.8.14"
|
||||||
firebase_messaging:
|
firebase_messaging:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging
|
name: firebase_messaging
|
||||||
sha256: ba12ad0b600e0c939fbb9391e1cd3320a5b5dad5284276b9182fc21eb1e72c2b
|
sha256: "5021279acd1cb5ccaceaa388e616e82cc4a2e4d862f02637df0e8ab766e6900a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "16.0.2"
|
version: "16.0.3"
|
||||||
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: b4bade67bfc09fcc56eb012b3fc72b59ca9e2259a34cdfb81b368169770ff536
|
sha256: f3a16c51f02055ace2a7c16ccb341c1f1b36b67c13270a48bcef68c1d970bbe8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.2"
|
version: "4.7.3"
|
||||||
firebase_messaging_web:
|
firebase_messaging_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging_web
|
name: firebase_messaging_web
|
||||||
sha256: "8ae4a00d178993feb79603cad324b53696375cbb78805e8eb603fe331866629d"
|
sha256: "3eb9a1382caeb95b370f21e36d4a460496af777c9c2ef5df9b90d4803982c069"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.3"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -762,10 +770,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_card_swiper
|
name: flutter_card_swiper
|
||||||
sha256: "1eacbfab31b572223042e03409726553aec431abe48af48c8d591d376d070d3d"
|
sha256: "9fbe75c913c0a01f34f9f98068ad198e396695fcf8abfa433cc53652fceb5617"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.2"
|
version: "7.1.0"
|
||||||
flutter_colorpicker:
|
flutter_colorpicker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1984,10 +1992,11 @@ packages:
|
|||||||
receive_sharing_intent:
|
receive_sharing_intent:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: receive_sharing_intent
|
path: "."
|
||||||
sha256: ec76056e4d258ad708e76d85591d933678625318e411564dcb9059048ca3a593
|
ref: master
|
||||||
url: "https://pub.dev"
|
resolved-ref: "2cea396843cd3ab1b5ec4334be4233864637874e"
|
||||||
source: hosted
|
url: "https://github.com/KasemJaffer/receive_sharing_intent"
|
||||||
|
source: git
|
||||||
version: "1.8.1"
|
version: "1.8.1"
|
||||||
record:
|
record:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
@@ -2422,10 +2431,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqlite3
|
name: sqlite3
|
||||||
sha256: "809bc27297bd0633ef360a7e27e056e93a2ef598079616db97b0c93e275f6d31"
|
sha256: f18fd9a72d7a1ad2920db61368f2a69368f1cc9b56b8233e9d83b47b0a8435aa
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.9.2"
|
version: "2.9.3"
|
||||||
sqlite3_flutter_libs:
|
sqlite3_flutter_libs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2666,15 +2675,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.6"
|
version: "0.7.6"
|
||||||
textfield_tags:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
path: "."
|
|
||||||
ref: "fixes/allow-controller-re-registration"
|
|
||||||
resolved-ref: "7574e79649e34df1c3cc0c49b2f0cc2b92de6a7b"
|
|
||||||
url: "https://github.com/lionelmennig/textfield_tags.git"
|
|
||||||
source: git
|
|
||||||
version: "3.0.1"
|
|
||||||
timezone:
|
timezone:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
23
pubspec.yaml
23
pubspec.yaml
@@ -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+134
|
version: 3.3.0+136
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.2
|
sdk: ^3.7.2
|
||||||
@@ -78,9 +78,9 @@ dependencies:
|
|||||||
image_picker_android: ^0.8.13+4
|
image_picker_android: ^0.8.13+4
|
||||||
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.2
|
firebase_messaging: ^16.0.3
|
||||||
flutter_udid: ^4.0.0
|
flutter_udid: ^4.0.0
|
||||||
firebase_core: ^4.1.1
|
firebase_core: ^4.2.0
|
||||||
web_socket_channel: ^3.0.3
|
web_socket_channel: ^3.0.3
|
||||||
material_symbols_icons: ^4.2874.0
|
material_symbols_icons: ^4.2874.0
|
||||||
drift: ^2.28.2
|
drift: ^2.28.2
|
||||||
@@ -121,12 +121,12 @@ dependencies:
|
|||||||
flutter_secure_storage: ^9.2.4
|
flutter_secure_storage: ^9.2.4
|
||||||
flutter_math_fork: ^0.7.4
|
flutter_math_fork: ^0.7.4
|
||||||
share_plus: ^12.0.0
|
share_plus: ^12.0.0
|
||||||
receive_sharing_intent: ^1.8.1
|
# https://github.com/KasemJaffer/receive_sharing_intent/pull/333
|
||||||
top_snackbar_flutter: ^3.3.0
|
receive_sharing_intent:
|
||||||
textfield_tags:
|
|
||||||
git:
|
git:
|
||||||
url: https://github.com/lionelmennig/textfield_tags.git
|
url: https://github.com/KasemJaffer/receive_sharing_intent
|
||||||
ref: fixes/allow-controller-re-registration
|
ref: master
|
||||||
|
top_snackbar_flutter: ^3.3.0
|
||||||
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
|
||||||
@@ -134,11 +134,11 @@ dependencies:
|
|||||||
flutter_app_update: ^3.2.2
|
flutter_app_update: ^3.2.2
|
||||||
archive: ^4.0.7
|
archive: ^4.0.7
|
||||||
process_run: ^1.2.4
|
process_run: ^1.2.4
|
||||||
firebase_crashlytics: ^5.0.2
|
firebase_crashlytics: ^5.0.3
|
||||||
firebase_analytics: ^12.0.2
|
firebase_analytics: ^12.0.3
|
||||||
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.1.0
|
||||||
file_saver: ^0.3.1
|
file_saver: ^0.3.1
|
||||||
tray_manager: ^0.5.1
|
tray_manager: ^0.5.1
|
||||||
flutter_webrtc: ^1.2.0
|
flutter_webrtc: ^1.2.0
|
||||||
@@ -162,6 +162,7 @@ dependencies:
|
|||||||
syncfusion_flutter_pdfviewer: ^31.1.21
|
syncfusion_flutter_pdfviewer: ^31.1.21
|
||||||
swipe_to: ^1.0.6
|
swipe_to: ^1.0.6
|
||||||
fl_heatmap: ^0.4.5
|
fl_heatmap: ^0.4.5
|
||||||
|
dio_smart_retry: ^7.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user