Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
1210cda998 | |||
3b56b94242 | |||
34043e722b | |||
e4a5ac9d0a | |||
c991590b27 | |||
d8b2c7f81e | |||
1fd042bcae | |||
0a04c72468 | |||
4e8f2ddef3 | |||
85f97521e5 | |||
9c451f485a | |||
f836b22c73 | |||
18fba0c9e7 | |||
c68138e516 | |||
cedd0b083a | |||
1a0721ba3a | |||
a75f42e440 | |||
e4a6ff2da4 | |||
baa6b401d3 | |||
bd1369e72d | |||
10520b4448 | |||
cab2217793 | |||
4e4e551e2f | |||
597a8a802a |
@ -22,7 +22,8 @@
|
|||||||
android:label="Solian"
|
android:label="Solian"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:supportsRtl="true">
|
android:supportsRtl="true"
|
||||||
|
android:usesCleartextTraffic="true">
|
||||||
<receiver android:exported="false"
|
<receiver android:exported="false"
|
||||||
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
|
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
|
||||||
<receiver android:exported="false"
|
<receiver android:exported="false"
|
||||||
|
7
build.yaml
Normal file
7
build.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
targets:
|
||||||
|
$default:
|
||||||
|
builders:
|
||||||
|
json_serializable:
|
||||||
|
options:
|
||||||
|
explicit_to_json: true
|
||||||
|
field_rename: snake
|
380
ios/Podfile.lock
380
ios/Podfile.lock
@ -1,4 +1,6 @@
|
|||||||
PODS:
|
PODS:
|
||||||
|
- audio_session (0.0.1):
|
||||||
|
- Flutter
|
||||||
- connectivity_plus (0.0.1):
|
- connectivity_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@ -38,122 +40,121 @@ PODS:
|
|||||||
- file_picker (0.0.1):
|
- file_picker (0.0.1):
|
||||||
- DKImagePickerController/PhotoGallery
|
- DKImagePickerController/PhotoGallery
|
||||||
- Flutter
|
- Flutter
|
||||||
- Firebase/Analytics (10.29.0):
|
- Firebase/Analytics (11.0.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- Firebase/Core (10.29.0):
|
- Firebase/Core (11.0.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseAnalytics (~> 10.29.0)
|
- FirebaseAnalytics (~> 11.0.0)
|
||||||
- Firebase/CoreOnly (10.29.0):
|
- Firebase/CoreOnly (11.0.0):
|
||||||
- FirebaseCore (= 10.29.0)
|
- FirebaseCore (= 11.0.0)
|
||||||
- Firebase/Crashlytics (10.29.0):
|
- Firebase/Crashlytics (11.0.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseCrashlytics (~> 10.29.0)
|
- FirebaseCrashlytics (~> 11.0.0)
|
||||||
- Firebase/Messaging (10.29.0):
|
- Firebase/Messaging (11.0.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 10.29.0)
|
- FirebaseMessaging (~> 11.0.0)
|
||||||
- Firebase/Performance (10.29.0):
|
- Firebase/Performance (11.0.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebasePerformance (~> 10.29.0)
|
- FirebasePerformance (~> 11.0.0)
|
||||||
- firebase_analytics (11.2.1):
|
- firebase_analytics (11.3.0):
|
||||||
- Firebase/Analytics (= 10.29.0)
|
- Firebase/Analytics (= 11.0.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_core (3.3.0):
|
- firebase_core (3.4.0):
|
||||||
- Firebase/CoreOnly (= 10.29.0)
|
- Firebase/CoreOnly (= 11.0.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_crashlytics (4.0.4):
|
- firebase_crashlytics (4.1.0):
|
||||||
- Firebase/Crashlytics (= 10.29.0)
|
- Firebase/Crashlytics (= 11.0.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_messaging (15.0.4):
|
- firebase_messaging (15.1.0):
|
||||||
- Firebase/Messaging (= 10.29.0)
|
- Firebase/Messaging (= 11.0.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_performance (0.10.0-4):
|
- firebase_performance (0.10.0-5):
|
||||||
- Firebase/Performance (= 10.29.0)
|
- Firebase/Performance (= 11.0.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- FirebaseABTesting (10.29.0):
|
- FirebaseABTesting (11.1.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseAnalytics (10.29.0):
|
- FirebaseAnalytics (11.0.0):
|
||||||
- FirebaseAnalytics/AdIdSupport (= 10.29.0)
|
- FirebaseAnalytics/AdIdSupport (= 11.0.0)
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 11.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
- GoogleUtilities/MethodSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/Network (~> 7.11)
|
- GoogleUtilities/Network (~> 8.0)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseAnalytics/AdIdSupport (10.29.0):
|
- FirebaseAnalytics/AdIdSupport (11.0.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 11.0)
|
||||||
- GoogleAppMeasurement (= 10.29.0)
|
- GoogleAppMeasurement (= 11.0.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
- GoogleUtilities/MethodSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/Network (~> 7.11)
|
- GoogleUtilities/Network (~> 8.0)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseCore (10.29.0):
|
- FirebaseCore (11.0.0):
|
||||||
- FirebaseCoreInternal (~> 10.0)
|
- FirebaseCoreInternal (~> 11.0)
|
||||||
- GoogleUtilities/Environment (~> 7.12)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/Logger (~> 7.12)
|
- GoogleUtilities/Logger (~> 8.0)
|
||||||
- FirebaseCoreExtension (10.29.0):
|
- FirebaseCoreExtension (11.1.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseCoreInternal (10.29.0):
|
- FirebaseCoreInternal (11.1.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||||
- FirebaseCrashlytics (10.29.0):
|
- FirebaseCrashlytics (11.0.0):
|
||||||
- FirebaseCore (~> 10.5)
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 11.0)
|
||||||
- FirebaseRemoteConfigInterop (~> 10.23)
|
- FirebaseRemoteConfigInterop (~> 11.0)
|
||||||
- FirebaseSessions (~> 10.5)
|
- FirebaseSessions (~> 11.0)
|
||||||
- GoogleDataTransport (~> 9.2)
|
- GoogleDataTransport (~> 10.0)
|
||||||
- GoogleUtilities/Environment (~> 7.8)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- PromisesObjC (~> 2.1)
|
- PromisesObjC (~> 2.4)
|
||||||
- FirebaseInstallations (10.29.0):
|
- FirebaseInstallations (11.1.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 11.0)
|
||||||
- GoogleUtilities/Environment (~> 7.8)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/UserDefaults (~> 7.8)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- PromisesObjC (~> 2.1)
|
- PromisesObjC (~> 2.4)
|
||||||
- FirebaseMessaging (10.29.0):
|
- FirebaseMessaging (11.0.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 11.0)
|
||||||
- GoogleDataTransport (~> 9.3)
|
- GoogleDataTransport (~> 10.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/Environment (~> 7.8)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/Reachability (~> 7.8)
|
- GoogleUtilities/Reachability (~> 8.0)
|
||||||
- GoogleUtilities/UserDefaults (~> 7.8)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebasePerformance (10.29.0):
|
- FirebasePerformance (11.0.0):
|
||||||
- FirebaseCore (~> 10.5)
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 11.0)
|
||||||
- FirebaseRemoteConfig (~> 10.0)
|
- FirebaseRemoteConfig (~> 11.0)
|
||||||
- FirebaseSessions (~> 10.5)
|
- FirebaseSessions (~> 11.0)
|
||||||
- GoogleDataTransport (~> 9.2)
|
- GoogleDataTransport (~> 10.0)
|
||||||
- GoogleUtilities/Environment (~> 7.13)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/ISASwizzler (~> 7.13)
|
- GoogleUtilities/MethodSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.13)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- GoogleUtilities/UserDefaults (~> 7.13)
|
- nanopb (~> 3.30910.0)
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- FirebaseRemoteConfig (11.1.0):
|
||||||
- FirebaseRemoteConfig (10.29.0):
|
- FirebaseABTesting (~> 11.0)
|
||||||
- FirebaseABTesting (~> 10.0)
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseInstallations (~> 11.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseRemoteConfigInterop (~> 11.0)
|
||||||
- FirebaseRemoteConfigInterop (~> 10.23)
|
- FirebaseSharedSwift (~> 11.0)
|
||||||
- FirebaseSharedSwift (~> 10.0)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/Environment (~> 7.8)
|
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
- FirebaseRemoteConfigInterop (11.1.0)
|
||||||
- FirebaseRemoteConfigInterop (10.29.0)
|
- FirebaseSessions (11.1.0):
|
||||||
- FirebaseSessions (10.29.0):
|
- FirebaseCore (~> 11.0)
|
||||||
- FirebaseCore (~> 10.5)
|
- FirebaseCoreExtension (~> 11.0)
|
||||||
- FirebaseCoreExtension (~> 10.0)
|
- FirebaseInstallations (~> 11.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- GoogleDataTransport (~> 10.0)
|
||||||
- GoogleDataTransport (~> 9.2)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/Environment (~> 7.13)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- GoogleUtilities/UserDefaults (~> 7.13)
|
- nanopb (~> 3.30910.0)
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
|
||||||
- PromisesSwift (~> 2.1)
|
- PromisesSwift (~> 2.1)
|
||||||
- FirebaseSharedSwift (10.29.0)
|
- FirebaseSharedSwift (11.1.0)
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- flutter_keyboard_visibility (0.0.1):
|
- flutter_keyboard_visibility (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
@ -167,58 +168,54 @@ PODS:
|
|||||||
- gal (1.0.0):
|
- gal (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- GoogleAppMeasurement (10.29.0):
|
- GoogleAppMeasurement (11.0.0):
|
||||||
- GoogleAppMeasurement/AdIdSupport (= 10.29.0)
|
- GoogleAppMeasurement/AdIdSupport (= 11.0.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
- GoogleUtilities/MethodSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/Network (~> 7.11)
|
- GoogleUtilities/Network (~> 8.0)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- GoogleAppMeasurement/AdIdSupport (10.29.0):
|
- GoogleAppMeasurement/AdIdSupport (11.0.0):
|
||||||
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.29.0)
|
- GoogleAppMeasurement/WithoutAdIdSupport (= 11.0.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
- GoogleUtilities/MethodSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/Network (~> 7.11)
|
- GoogleUtilities/Network (~> 8.0)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- GoogleAppMeasurement/WithoutAdIdSupport (10.29.0):
|
- GoogleAppMeasurement/WithoutAdIdSupport (11.0.0):
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
- GoogleUtilities/MethodSwizzler (~> 8.0)
|
||||||
- GoogleUtilities/Network (~> 7.11)
|
- GoogleUtilities/Network (~> 8.0)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- GoogleDataTransport (9.4.1):
|
- GoogleDataTransport (10.1.0):
|
||||||
- GoogleUtilities/Environment (~> 7.7)
|
- nanopb (~> 3.30910.0)
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- PromisesObjC (~> 2.4)
|
||||||
- PromisesObjC (< 3.0, >= 1.2)
|
- GoogleUtilities/AppDelegateSwizzler (8.0.2):
|
||||||
- GoogleUtilities/AppDelegateSwizzler (7.13.3):
|
|
||||||
- GoogleUtilities/Environment
|
- GoogleUtilities/Environment
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- GoogleUtilities/Network
|
- GoogleUtilities/Network
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Environment (7.13.3):
|
- GoogleUtilities/Environment (8.0.2):
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- PromisesObjC (< 3.0, >= 1.2)
|
- GoogleUtilities/Logger (8.0.2):
|
||||||
- GoogleUtilities/ISASwizzler (7.13.3):
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- GoogleUtilities/Logger (7.13.3):
|
|
||||||
- GoogleUtilities/Environment
|
- GoogleUtilities/Environment
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/MethodSwizzler (7.13.3):
|
- GoogleUtilities/MethodSwizzler (8.0.2):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Network (7.13.3):
|
- GoogleUtilities/Network (8.0.2):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- "GoogleUtilities/NSData+zlib"
|
- "GoogleUtilities/NSData+zlib"
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Reachability
|
- GoogleUtilities/Reachability
|
||||||
- "GoogleUtilities/NSData+zlib (7.13.3)":
|
- "GoogleUtilities/NSData+zlib (8.0.2)":
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Privacy (7.13.3)
|
- GoogleUtilities/Privacy (8.0.2)
|
||||||
- GoogleUtilities/Reachability (7.13.3):
|
- GoogleUtilities/Reachability (8.0.2):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/UserDefaults (7.13.3):
|
- GoogleUtilities/UserDefaults (8.0.2):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- image_cropper (0.0.4):
|
- image_cropper (0.0.4):
|
||||||
@ -226,20 +223,16 @@ PODS:
|
|||||||
- TOCropViewController (~> 2.7.4)
|
- TOCropViewController (~> 2.7.4)
|
||||||
- image_picker_ios (0.0.1):
|
- image_picker_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- just_audio (0.0.1):
|
||||||
|
- Flutter
|
||||||
- livekit_client (2.2.4):
|
- livekit_client (2.2.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- WebRTC-SDK (= 125.6422.04)
|
- WebRTC-SDK (= 125.6422.04)
|
||||||
- media_kit_libs_ios_video (1.0.4):
|
- nanopb (3.30910.0):
|
||||||
- Flutter
|
- nanopb/decode (= 3.30910.0)
|
||||||
- media_kit_native_event_loop (1.0.0):
|
- nanopb/encode (= 3.30910.0)
|
||||||
- Flutter
|
- nanopb/decode (3.30910.0)
|
||||||
- media_kit_video (0.0.1):
|
- nanopb/encode (3.30910.0)
|
||||||
- Flutter
|
|
||||||
- nanopb (2.30910.0):
|
|
||||||
- nanopb/decode (= 2.30910.0)
|
|
||||||
- nanopb/encode (= 2.30910.0)
|
|
||||||
- nanopb/decode (2.30910.0)
|
|
||||||
- nanopb/encode (2.30910.0)
|
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- pasteboard (0.0.1):
|
- pasteboard (0.0.1):
|
||||||
@ -256,11 +249,9 @@ PODS:
|
|||||||
- PromisesObjC (= 2.4.0)
|
- PromisesObjC (= 2.4.0)
|
||||||
- protocol_handler_ios (0.0.1):
|
- protocol_handler_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- screen_brightness_ios (0.1.0):
|
- SDWebImage (5.19.7):
|
||||||
- Flutter
|
- SDWebImage/Core (= 5.19.7)
|
||||||
- SDWebImage (5.19.6):
|
- SDWebImage/Core (5.19.7)
|
||||||
- SDWebImage/Core (= 5.19.6)
|
|
||||||
- SDWebImage/Core (5.19.6)
|
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
@ -273,13 +264,15 @@ PODS:
|
|||||||
- TOCropViewController (2.7.4)
|
- TOCropViewController (2.7.4)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- volume_controller (0.0.1):
|
- video_player_avfoundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- wakelock_plus (0.0.1):
|
- wakelock_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- WebRTC-SDK (125.6422.04)
|
- WebRTC-SDK (125.6422.04)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
@ -296,22 +289,19 @@ DEPENDENCIES:
|
|||||||
- gal (from `.symlinks/plugins/gal/darwin`)
|
- gal (from `.symlinks/plugins/gal/darwin`)
|
||||||
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
|
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
|
||||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
|
- just_audio (from `.symlinks/plugins/just_audio/ios`)
|
||||||
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
|
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
|
||||||
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
|
||||||
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
|
|
||||||
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
|
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`)
|
- pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`)
|
||||||
- protocol_handler_ios (from `.symlinks/plugins/protocol_handler_ios/ios`)
|
- protocol_handler_ios (from `.symlinks/plugins/protocol_handler_ios/ios`)
|
||||||
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
|
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
|
||||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
@ -344,6 +334,8 @@ SPEC REPOS:
|
|||||||
- WebRTC-SDK
|
- WebRTC-SDK
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
audio_session:
|
||||||
|
:path: ".symlinks/plugins/audio_session/ios"
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
:path: ".symlinks/plugins/connectivity_plus/darwin"
|
:path: ".symlinks/plugins/connectivity_plus/darwin"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
@ -376,14 +368,10 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/image_cropper/ios"
|
:path: ".symlinks/plugins/image_cropper/ios"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||||
|
just_audio:
|
||||||
|
:path: ".symlinks/plugins/just_audio/ios"
|
||||||
livekit_client:
|
livekit_client:
|
||||||
:path: ".symlinks/plugins/livekit_client/ios"
|
:path: ".symlinks/plugins/livekit_client/ios"
|
||||||
media_kit_libs_ios_video:
|
|
||||||
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
|
|
||||||
media_kit_native_event_loop:
|
|
||||||
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
|
|
||||||
media_kit_video:
|
|
||||||
:path: ".symlinks/plugins/media_kit_video/ios"
|
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
pasteboard:
|
pasteboard:
|
||||||
@ -396,8 +384,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/pointer_interceptor_ios/ios"
|
:path: ".symlinks/plugins/pointer_interceptor_ios/ios"
|
||||||
protocol_handler_ios:
|
protocol_handler_ios:
|
||||||
:path: ".symlinks/plugins/protocol_handler_ios/ios"
|
:path: ".symlinks/plugins/protocol_handler_ios/ios"
|
||||||
screen_brightness_ios:
|
|
||||||
:path: ".symlinks/plugins/screen_brightness_ios/ios"
|
|
||||||
share_plus:
|
share_plus:
|
||||||
:path: ".symlinks/plugins/share_plus/ios"
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
@ -406,52 +392,51 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/sqflite/darwin"
|
:path: ".symlinks/plugins/sqflite/darwin"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
volume_controller:
|
video_player_avfoundation:
|
||||||
:path: ".symlinks/plugins/volume_controller/ios"
|
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
|
audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207
|
||||||
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
|
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
|
||||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
||||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||||
Firebase: cec914dab6fd7b1bd8ab56ea07ce4e03dd251c2d
|
Firebase: 9f574c08c2396885b5e7e100ed4293d956218af9
|
||||||
firebase_analytics: 04491d1ee74c8e7c2330c96afc54188a969b06ee
|
firebase_analytics: 1a66fe8d4375eccff44671ea37897683a78b2675
|
||||||
firebase_core: 57aeb91680e5d5e6df6b888064be7c785f146efb
|
firebase_core: ceec591a66629daaee82d3321551692c4a871493
|
||||||
firebase_crashlytics: e3d3e0c99bad5aaab5908385133dea8ec344693f
|
firebase_crashlytics: e4f04180f443d5a8b56fbc0685bdbd7d90dd26f0
|
||||||
firebase_messaging: c862b3d2b973ecc769194dc8de09bd22c77ae757
|
firebase_messaging: 15d8b557010f3bb7b98d0302e1c7c8fbcd244425
|
||||||
firebase_performance: 8643e815a354ee94da1192cd69335a48a7b625a4
|
firebase_performance: d373c742649e2d85d92cc223b4511c3d132887ef
|
||||||
FirebaseABTesting: d87f56707159bae64e269757a6e963d490f2eebe
|
FirebaseABTesting: c2e22c3aab99afa81d0561708b2c1c356c556976
|
||||||
FirebaseAnalytics: 23717de130b779aa506e757edb9713d24b6ffeda
|
FirebaseAnalytics: 27eb78b97880ea4a004839b9bac0b58880f5a92a
|
||||||
FirebaseCore: 30e9c1cbe3d38f5f5e75f48bfcea87d7c358ec16
|
FirebaseCore: 3cf438f431f18c12cdf2aaf64434648b63f7e383
|
||||||
FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f
|
FirebaseCoreExtension: aa5c9779c2d0d39d83f1ceb3fdbafe80c4feecfa
|
||||||
FirebaseCoreInternal: df84dd300b561c27d5571684f389bf60b0a5c934
|
FirebaseCoreInternal: adefedc9a88dbe393c4884640a73ec9e8e790f8c
|
||||||
FirebaseCrashlytics: 34647b41e18de773717fdd348a22206f2f9bc774
|
FirebaseCrashlytics: 745d8f0221fe49c62865391d1bf56f5a12eeec0b
|
||||||
FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd
|
FirebaseInstallations: d0a8fea5a6fa91abc661591cf57c0f0d70863e57
|
||||||
FirebaseMessaging: 7b5d8033e183ab59eb5b852a53201559e976d366
|
FirebaseMessaging: d2d1d9c62c46dd2db49a952f7deb5b16ad2c9742
|
||||||
FirebasePerformance: d0ac4aa90f8c1aedeb8d0329a56e2d77d8d9e004
|
FirebasePerformance: efdc02bacb1b4710588c9f867011605c081cdf79
|
||||||
FirebaseRemoteConfig: 48ef3f243742a8d72422ccfc9f986e19d7de53fd
|
FirebaseRemoteConfig: 05521e937b72e01847a7128da5a492327364c705
|
||||||
FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d
|
FirebaseRemoteConfigInterop: abf8b1bbc0bf1b84abd22b66746926410bf91a87
|
||||||
FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc
|
FirebaseSessions: 78f137e68dc01ca71606169ba4ac73b98c13752a
|
||||||
FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e
|
FirebaseSharedSwift: 260a35e08943ec810d820a70bc0359136351d0c5
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
|
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
|
||||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||||
flutter_webrtc: 75b868e4f9e817c7a9a42ca4b6169063de4eec9f
|
flutter_webrtc: 75b868e4f9e817c7a9a42ca4b6169063de4eec9f
|
||||||
gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1
|
gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1
|
||||||
GoogleAppMeasurement: f9de05ee17401e3355f68e8fc8b5064d429f5918
|
GoogleAppMeasurement: 6e49ffac7d3f2c3ded9cc663f912a13b67bbd0de
|
||||||
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
|
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||||
image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf
|
image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf
|
||||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||||
|
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
|
||||||
livekit_client: d079c5f040d4bf2b80440ff0ae997725a183e4bc
|
livekit_client: d079c5f040d4bf2b80440ff0ae997725a183e4bc
|
||||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
|
|
||||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
|
||||||
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
|
||||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||||
pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
|
pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
@ -460,15 +445,14 @@ SPEC CHECKSUMS:
|
|||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
||||||
protocol_handler_ios: a5db8abc38526ee326988b808be621e5fd568990
|
protocol_handler_ios: a5db8abc38526ee326988b808be621e5fd568990
|
||||||
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
|
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
|
||||||
SDWebImage: a79252b60f4678812d94316c91da69ec83089c9f
|
|
||||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
|
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
|
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
|
||||||
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
||||||
WebRTC-SDK: c3d69a87e7185fad3568f6f3cff7c9ac5890acf3
|
WebRTC-SDK: c3d69a87e7185fad3568f6f3cff7c9ac5890acf3
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -197,7 +198,7 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
height: 24,
|
height: 24,
|
||||||
child: CircularProgressIndicator(strokeWidth: 3),
|
child: CircularProgressIndicator(strokeWidth: 3),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const Gap(12),
|
||||||
CenteredContainer(
|
CenteredContainer(
|
||||||
maxWidth: 280,
|
maxWidth: 280,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
import 'package:solian/providers/content/posts.dart';
|
import 'package:solian/providers/content/posts.dart';
|
||||||
|
import 'package:solian/providers/last_read.dart';
|
||||||
|
|
||||||
class PostListController extends GetxController {
|
class PostListController extends GetxController {
|
||||||
String? author;
|
String? author;
|
||||||
@ -14,7 +17,7 @@ class PostListController extends GetxController {
|
|||||||
RxInt mode = 0.obs;
|
RxInt mode = 0.obs;
|
||||||
|
|
||||||
/// The paging controller for infinite loading.
|
/// The paging controller for infinite loading.
|
||||||
/// Only available when mode is `0` or `1`.
|
/// Only available when mode is `0`, `1` or `2`.
|
||||||
PagingController<int, Post> pagingController =
|
PagingController<int, Post> pagingController =
|
||||||
PagingController(firstPageKey: 0);
|
PagingController(firstPageKey: 0);
|
||||||
|
|
||||||
@ -96,6 +99,9 @@ class PostListController extends GetxController {
|
|||||||
final idx = <dynamic>{};
|
final idx = <dynamic>{};
|
||||||
postList.retainWhere((x) => idx.add(x.id));
|
postList.retainWhere((x) => idx.add(x.id));
|
||||||
|
|
||||||
|
var lastId = postList.map((x) => x.id).reduce(max);
|
||||||
|
Get.find<LastReadProvider>().feedLastReadAt = lastId;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,13 +6,14 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
|
||||||
import 'package:protocol_handler/protocol_handler.dart';
|
import 'package:protocol_handler/protocol_handler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:solian/bootstrapper.dart';
|
import 'package:solian/bootstrapper.dart';
|
||||||
import 'package:solian/firebase_options.dart';
|
import 'package:solian/firebase_options.dart';
|
||||||
import 'package:solian/platform.dart';
|
import 'package:solian/platform.dart';
|
||||||
import 'package:solian/providers/attachment_uploader.dart';
|
import 'package:solian/providers/attachment_uploader.dart';
|
||||||
|
import 'package:solian/providers/daily_sign.dart';
|
||||||
|
import 'package:solian/providers/last_read.dart';
|
||||||
import 'package:solian/providers/link_expander.dart';
|
import 'package:solian/providers/link_expander.dart';
|
||||||
import 'package:solian/providers/stickers.dart';
|
import 'package:solian/providers/stickers.dart';
|
||||||
import 'package:solian/providers/theme_switcher.dart';
|
import 'package:solian/providers/theme_switcher.dart';
|
||||||
@ -33,7 +34,6 @@ import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
|||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
MediaKit.ensureInitialized();
|
|
||||||
|
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
_initializeFirebase(),
|
_initializeFirebase(),
|
||||||
@ -107,7 +107,7 @@ class SolianApp extends StatelessWidget {
|
|||||||
return SystemShell(
|
return SystemShell(
|
||||||
child: ScaffoldMessenger(
|
child: ScaffoldMessenger(
|
||||||
child: BootstrapperShell(
|
child: BootstrapperShell(
|
||||||
child: child ?? const SizedBox(),
|
child: child ?? const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -129,6 +129,8 @@ class SolianApp extends StatelessWidget {
|
|||||||
Get.lazyPut(() => RealmProvider());
|
Get.lazyPut(() => RealmProvider());
|
||||||
Get.lazyPut(() => ChatCallProvider());
|
Get.lazyPut(() => ChatCallProvider());
|
||||||
Get.lazyPut(() => AttachmentUploaderController());
|
Get.lazyPut(() => AttachmentUploaderController());
|
||||||
Get.lazyPut(() => LinkExpandController());
|
Get.lazyPut(() => LinkExpandProvider());
|
||||||
|
Get.lazyPut(() => DailySignProvider());
|
||||||
|
Get.lazyPut(() => LastReadProvider());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'account.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Account {
|
class Account {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -10,6 +15,7 @@ class Account {
|
|||||||
dynamic avatar;
|
dynamic avatar;
|
||||||
dynamic banner;
|
dynamic banner;
|
||||||
String description;
|
String description;
|
||||||
|
AccountProfile? profile;
|
||||||
List<AccountBadge>? badges;
|
List<AccountBadge>? badges;
|
||||||
String? emailAddress;
|
String? emailAddress;
|
||||||
int? externalId;
|
int? externalId;
|
||||||
@ -26,55 +32,19 @@ class Account {
|
|||||||
required this.avatar,
|
required this.avatar,
|
||||||
required this.banner,
|
required this.banner,
|
||||||
required this.description,
|
required this.description,
|
||||||
|
required this.profile,
|
||||||
required this.badges,
|
required this.badges,
|
||||||
required this.emailAddress,
|
required this.emailAddress,
|
||||||
this.externalId,
|
this.externalId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Account.fromJson(Map<String, dynamic> json) => Account(
|
factory Account.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$AccountFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
confirmedAt: json['confirmed_at'] != null
|
|
||||||
? DateTime.parse(json['confirmed_at'])
|
|
||||||
: null,
|
|
||||||
suspendedAt: json['suspended_at'] != null
|
|
||||||
? DateTime.parse(json['suspended_at'])
|
|
||||||
: null,
|
|
||||||
name: json['name'],
|
|
||||||
nick: json['nick'],
|
|
||||||
avatar: json['avatar'],
|
|
||||||
banner: json['banner'],
|
|
||||||
description: json['description'],
|
|
||||||
emailAddress: json['email_address'],
|
|
||||||
badges: json['badges']
|
|
||||||
?.map((e) => AccountBadge.fromJson(e))
|
|
||||||
.toList()
|
|
||||||
.cast<AccountBadge>(),
|
|
||||||
externalId: json['external_id'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$AccountToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'confirmed_at': confirmedAt?.toIso8601String(),
|
|
||||||
'suspended_at': suspendedAt?.toIso8601String(),
|
|
||||||
'name': name,
|
|
||||||
'nick': nick,
|
|
||||||
'avatar': avatar,
|
|
||||||
'banner': banner,
|
|
||||||
'description': description,
|
|
||||||
'email_address': emailAddress,
|
|
||||||
'badges': badges?.map((e) => e.toJson()).toList(),
|
|
||||||
'external_id': externalId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class AccountBadge {
|
class AccountBadge {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -94,25 +64,40 @@ class AccountBadge {
|
|||||||
required this.type,
|
required this.type,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory AccountBadge.fromJson(Map<String, dynamic> json) => AccountBadge(
|
factory AccountBadge.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$AccountBadgeFromJson(json);
|
||||||
accountId: json['account_id'],
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
metadata: json['metadata'],
|
|
||||||
type: json['type'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$AccountBadgeToJson(this);
|
||||||
'id': id,
|
}
|
||||||
'account_id': accountId,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
@JsonSerializable()
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
class AccountProfile {
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
int id;
|
||||||
'metadata': metadata,
|
DateTime createdAt;
|
||||||
'type': type,
|
DateTime updatedAt;
|
||||||
};
|
DateTime? deletedAt;
|
||||||
|
String? firstName;
|
||||||
|
String? lastName;
|
||||||
|
int? experience;
|
||||||
|
DateTime? lastSeenAt;
|
||||||
|
DateTime? birthday;
|
||||||
|
int accountId;
|
||||||
|
|
||||||
|
AccountProfile({
|
||||||
|
required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.firstName,
|
||||||
|
required this.lastName,
|
||||||
|
required this.experience,
|
||||||
|
required this.lastSeenAt,
|
||||||
|
required this.birthday,
|
||||||
|
required this.accountId,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory AccountProfile.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AccountProfileFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$AccountProfileToJson(this);
|
||||||
}
|
}
|
||||||
|
110
lib/models/account.g.dart
Normal file
110
lib/models/account.g.dart
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'account.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Account _$AccountFromJson(Map<String, dynamic> json) => Account(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
confirmedAt: json['confirmed_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['confirmed_at'] as String),
|
||||||
|
suspendedAt: json['suspended_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['suspended_at'] as String),
|
||||||
|
name: json['name'] as String,
|
||||||
|
nick: json['nick'] as String,
|
||||||
|
avatar: json['avatar'],
|
||||||
|
banner: json['banner'],
|
||||||
|
description: json['description'] as String,
|
||||||
|
profile: json['profile'] == null
|
||||||
|
? null
|
||||||
|
: AccountProfile.fromJson(json['profile'] as Map<String, dynamic>),
|
||||||
|
badges: (json['badges'] as List<dynamic>?)
|
||||||
|
?.map((e) => AccountBadge.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
emailAddress: json['email_address'] as String?,
|
||||||
|
externalId: (json['external_id'] as num?)?.toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$AccountToJson(Account instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'confirmed_at': instance.confirmedAt?.toIso8601String(),
|
||||||
|
'suspended_at': instance.suspendedAt?.toIso8601String(),
|
||||||
|
'name': instance.name,
|
||||||
|
'nick': instance.nick,
|
||||||
|
'avatar': instance.avatar,
|
||||||
|
'banner': instance.banner,
|
||||||
|
'description': instance.description,
|
||||||
|
'profile': instance.profile?.toJson(),
|
||||||
|
'badges': instance.badges?.map((e) => e.toJson()).toList(),
|
||||||
|
'email_address': instance.emailAddress,
|
||||||
|
'external_id': instance.externalId,
|
||||||
|
};
|
||||||
|
|
||||||
|
AccountBadge _$AccountBadgeFromJson(Map<String, dynamic> json) => AccountBadge(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
metadata: json['metadata'] as Map<String, dynamic>?,
|
||||||
|
type: json['type'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$AccountBadgeToJson(AccountBadge instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'metadata': instance.metadata,
|
||||||
|
'type': instance.type,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
||||||
|
|
||||||
|
AccountProfile _$AccountProfileFromJson(Map<String, dynamic> json) =>
|
||||||
|
AccountProfile(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
firstName: json['first_name'] as String?,
|
||||||
|
lastName: json['last_name'] as String?,
|
||||||
|
experience: (json['experience'] as num?)?.toInt(),
|
||||||
|
lastSeenAt: json['last_seen_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['last_seen_at'] as String),
|
||||||
|
birthday: json['birthday'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['birthday'] as String),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$AccountProfileToJson(AccountProfile instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'first_name': instance.firstName,
|
||||||
|
'last_name': instance.lastName,
|
||||||
|
'experience': instance.experience,
|
||||||
|
'last_seen_at': instance.lastSeenAt?.toIso8601String(),
|
||||||
|
'birthday': instance.birthday?.toIso8601String(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
@ -1,3 +1,8 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'account_status.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class AccountStatus {
|
class AccountStatus {
|
||||||
bool isDisturbable;
|
bool isDisturbable;
|
||||||
bool isOnline;
|
bool isOnline;
|
||||||
@ -11,21 +16,13 @@ class AccountStatus {
|
|||||||
required this.status,
|
required this.status,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory AccountStatus.fromJson(Map<String, dynamic> json) => AccountStatus(
|
factory AccountStatus.fromJson(Map<String, dynamic> json) =>
|
||||||
isDisturbable: json['is_disturbable'],
|
_$AccountStatusFromJson(json);
|
||||||
isOnline: json['is_online'],
|
|
||||||
lastSeenAt: json['last_seen_at'] != null ? DateTime.parse(json['last_seen_at']) : null,
|
|
||||||
status: json['status'] != null ? Status.fromJson(json['status']) : null,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$AccountStatusToJson(this);
|
||||||
'is_disturbable': isDisturbable,
|
|
||||||
'is_online': isOnline,
|
|
||||||
'last_seen_at': lastSeenAt?.toIso8601String(),
|
|
||||||
'status': status?.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Status {
|
class Status {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -53,31 +50,7 @@ class Status {
|
|||||||
required this.accountId,
|
required this.accountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Status.fromJson(Map<String, dynamic> json) => Status(
|
factory Status.fromJson(Map<String, dynamic> json) => _$StatusFromJson(json);
|
||||||
id: json['id'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null ? DateTime.parse(json['deleted_at']) : null,
|
|
||||||
type: json['type'],
|
|
||||||
label: json['label'],
|
|
||||||
attitude: json['attitude'],
|
|
||||||
isNoDisturb: json['is_no_disturb'],
|
|
||||||
isInvisible: json['is_invisible'],
|
|
||||||
clearAt: json['clear_at'] != null ? DateTime.parse(json['clear_at']) : null,
|
|
||||||
accountId: json['account_id'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$StatusToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'type': type,
|
|
||||||
'label': label,
|
|
||||||
'attitude': attitude,
|
|
||||||
'is_no_disturb': isNoDisturb,
|
|
||||||
'is_invisible': isInvisible,
|
|
||||||
'clear_at': clearAt?.toIso8601String(),
|
|
||||||
'account_id': accountId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
59
lib/models/account_status.g.dart
Normal file
59
lib/models/account_status.g.dart
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'account_status.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
AccountStatus _$AccountStatusFromJson(Map<String, dynamic> json) =>
|
||||||
|
AccountStatus(
|
||||||
|
isDisturbable: json['is_disturbable'] as bool,
|
||||||
|
isOnline: json['is_online'] as bool,
|
||||||
|
lastSeenAt: json['last_seen_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['last_seen_at'] as String),
|
||||||
|
status: json['status'] == null
|
||||||
|
? null
|
||||||
|
: Status.fromJson(json['status'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$AccountStatusToJson(AccountStatus instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'is_disturbable': instance.isDisturbable,
|
||||||
|
'is_online': instance.isOnline,
|
||||||
|
'last_seen_at': instance.lastSeenAt?.toIso8601String(),
|
||||||
|
'status': instance.status?.toJson(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Status _$StatusFromJson(Map<String, dynamic> json) => Status(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
type: json['type'] as String,
|
||||||
|
label: json['label'] as String,
|
||||||
|
attitude: (json['attitude'] as num).toInt(),
|
||||||
|
isNoDisturb: json['is_no_disturb'] as bool,
|
||||||
|
isInvisible: json['is_invisible'] as bool,
|
||||||
|
clearAt: json['clear_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['clear_at'] as String),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$StatusToJson(Status instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'type': instance.type,
|
||||||
|
'label': instance.label,
|
||||||
|
'attitude': instance.attitude,
|
||||||
|
'is_no_disturb': instance.isNoDisturb,
|
||||||
|
'is_invisible': instance.isInvisible,
|
||||||
|
'clear_at': instance.clearAt?.toIso8601String(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
@ -1,5 +1,9 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
|
|
||||||
|
part 'attachment.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class AttachmentPlaceholder {
|
class AttachmentPlaceholder {
|
||||||
int chunkCount;
|
int chunkCount;
|
||||||
int chunkSize;
|
int chunkSize;
|
||||||
@ -12,19 +16,12 @@ class AttachmentPlaceholder {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory AttachmentPlaceholder.fromJson(Map<String, dynamic> json) =>
|
factory AttachmentPlaceholder.fromJson(Map<String, dynamic> json) =>
|
||||||
AttachmentPlaceholder(
|
_$AttachmentPlaceholderFromJson(json);
|
||||||
chunkCount: json['chunk_count'],
|
|
||||||
chunkSize: json['chunk_size'],
|
|
||||||
meta: Attachment.fromJson(json['meta']),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$AttachmentPlaceholderToJson(this);
|
||||||
'chunk_count': chunkCount,
|
|
||||||
'chunk_size': chunkSize,
|
|
||||||
'meta': meta.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Attachment {
|
class Attachment {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -68,50 +65,8 @@ class Attachment {
|
|||||||
required this.accountId,
|
required this.accountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Attachment.fromJson(Map<String, dynamic> json) => Attachment(
|
factory Attachment.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$AttachmentFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
rid: json['rid'],
|
|
||||||
uuid: json['uuid'],
|
|
||||||
size: json['size'],
|
|
||||||
name: json['name'],
|
|
||||||
alt: json['alt'],
|
|
||||||
mimetype: json['mimetype'],
|
|
||||||
hash: json['hash'],
|
|
||||||
destination: json['destination'],
|
|
||||||
isAnalyzed: json['is_analyzed'],
|
|
||||||
isUploaded: json['is_uploaded'],
|
|
||||||
metadata: json['metadata'],
|
|
||||||
fileChunks: json['file_chunks'],
|
|
||||||
isMature: json['is_mature'],
|
|
||||||
account:
|
|
||||||
json['account'] != null ? Account.fromJson(json['account']) : null,
|
|
||||||
accountId: json['account_id'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$AttachmentToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'rid': rid,
|
|
||||||
'uuid': uuid,
|
|
||||||
'size': size,
|
|
||||||
'name': name,
|
|
||||||
'alt': alt,
|
|
||||||
'mimetype': mimetype,
|
|
||||||
'hash': hash,
|
|
||||||
'destination': destination,
|
|
||||||
'is_analyzed': isAnalyzed,
|
|
||||||
'is_uploaded': isUploaded,
|
|
||||||
'metadata': metadata,
|
|
||||||
'file_chunks': fileChunks,
|
|
||||||
'is_mature': isMature,
|
|
||||||
'account': account?.toJson(),
|
|
||||||
'account_id': accountId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
72
lib/models/attachment.g.dart
Normal file
72
lib/models/attachment.g.dart
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'attachment.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
AttachmentPlaceholder _$AttachmentPlaceholderFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
AttachmentPlaceholder(
|
||||||
|
chunkCount: (json['chunk_count'] as num).toInt(),
|
||||||
|
chunkSize: (json['chunk_size'] as num).toInt(),
|
||||||
|
meta: Attachment.fromJson(json['meta'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$AttachmentPlaceholderToJson(
|
||||||
|
AttachmentPlaceholder instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'chunk_count': instance.chunkCount,
|
||||||
|
'chunk_size': instance.chunkSize,
|
||||||
|
'meta': instance.meta.toJson(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Attachment _$AttachmentFromJson(Map<String, dynamic> json) => Attachment(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
rid: json['rid'] as String,
|
||||||
|
uuid: json['uuid'] as String,
|
||||||
|
size: (json['size'] as num).toInt(),
|
||||||
|
name: json['name'] as String,
|
||||||
|
alt: json['alt'] as String,
|
||||||
|
mimetype: json['mimetype'] as String,
|
||||||
|
hash: json['hash'] as String,
|
||||||
|
destination: (json['destination'] as num).toInt(),
|
||||||
|
isAnalyzed: json['is_analyzed'] as bool,
|
||||||
|
isUploaded: json['is_uploaded'] as bool,
|
||||||
|
metadata: json['metadata'] as Map<String, dynamic>?,
|
||||||
|
fileChunks: json['file_chunks'] as Map<String, dynamic>?,
|
||||||
|
isMature: json['is_mature'] as bool,
|
||||||
|
account: json['account'] == null
|
||||||
|
? null
|
||||||
|
: Account.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
accountId: (json['account_id'] as num?)?.toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$AttachmentToJson(Attachment instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'rid': instance.rid,
|
||||||
|
'uuid': instance.uuid,
|
||||||
|
'size': instance.size,
|
||||||
|
'name': instance.name,
|
||||||
|
'alt': instance.alt,
|
||||||
|
'mimetype': instance.mimetype,
|
||||||
|
'hash': instance.hash,
|
||||||
|
'destination': instance.destination,
|
||||||
|
'is_analyzed': instance.isAnalyzed,
|
||||||
|
'is_uploaded': instance.isUploaded,
|
||||||
|
'metadata': instance.metadata,
|
||||||
|
'file_chunks': instance.fileChunks,
|
||||||
|
'is_mature': instance.isMature,
|
||||||
|
'account': instance.account?.toJson(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
@ -1,6 +1,10 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:livekit_client/livekit_client.dart';
|
import 'package:livekit_client/livekit_client.dart';
|
||||||
import 'package:solian/models/channel.dart';
|
import 'package:solian/models/channel.dart';
|
||||||
|
|
||||||
|
part 'call.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Call {
|
class Call {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -10,7 +14,8 @@ class Call {
|
|||||||
String externalId;
|
String externalId;
|
||||||
int founderId;
|
int founderId;
|
||||||
int channelId;
|
int channelId;
|
||||||
List<dynamic> participants;
|
@JsonKey(defaultValue: [])
|
||||||
|
List<dynamic>? participants;
|
||||||
Channel channel;
|
Channel channel;
|
||||||
|
|
||||||
Call({
|
Call({
|
||||||
@ -26,32 +31,9 @@ class Call {
|
|||||||
required this.channel,
|
required this.channel,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Call.fromJson(Map<String, dynamic> json) => Call(
|
factory Call.fromJson(Map<String, dynamic> json) => _$CallFromJson(json);
|
||||||
id: json['id'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'],
|
|
||||||
endedAt:
|
|
||||||
json['ended_at'] != null ? DateTime.parse(json['ended_at']) : null,
|
|
||||||
externalId: json['external_id'],
|
|
||||||
founderId: json['founder_id'],
|
|
||||||
channelId: json['channel_id'],
|
|
||||||
participants: json['participants'] ?? List.empty(),
|
|
||||||
channel: Channel.fromJson(json['channel']),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$CallToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt,
|
|
||||||
'ended_at': endedAt?.toIso8601String(),
|
|
||||||
'external_id': externalId,
|
|
||||||
'founder_id': founderId,
|
|
||||||
'channel_id': channelId,
|
|
||||||
'participants': participants,
|
|
||||||
'channel': channel.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ParticipantStatsType {
|
enum ParticipantStatsType {
|
||||||
|
37
lib/models/call.g.dart
Normal file
37
lib/models/call.g.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'call.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Call _$CallFromJson(Map<String, dynamic> json) => Call(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
endedAt: json['ended_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['ended_at'] as String),
|
||||||
|
externalId: json['external_id'] as String,
|
||||||
|
founderId: (json['founder_id'] as num).toInt(),
|
||||||
|
channelId: (json['channel_id'] as num).toInt(),
|
||||||
|
participants: json['participants'] as List<dynamic>? ?? [],
|
||||||
|
channel: Channel.fromJson(json['channel'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$CallToJson(Call instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'ended_at': instance.endedAt?.toIso8601String(),
|
||||||
|
'external_id': instance.externalId,
|
||||||
|
'founder_id': instance.founderId,
|
||||||
|
'channel_id': instance.channelId,
|
||||||
|
'participants': instance.participants,
|
||||||
|
'channel': instance.channel.toJson(),
|
||||||
|
};
|
@ -1,6 +1,10 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
import 'package:solian/models/realm.dart';
|
import 'package:solian/models/realm.dart';
|
||||||
|
|
||||||
|
part 'channel.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Channel {
|
class Channel {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -17,6 +21,7 @@ class Channel {
|
|||||||
int? realmId;
|
int? realmId;
|
||||||
bool isEncrypted;
|
bool isEncrypted;
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: true)
|
||||||
bool isAvailable = false;
|
bool isAvailable = false;
|
||||||
|
|
||||||
Channel({
|
Channel({
|
||||||
@ -36,44 +41,22 @@ class Channel {
|
|||||||
required this.realmId,
|
required this.realmId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Channel.fromJson(Map<String, dynamic> json) => Channel(
|
factory Channel.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$ChannelFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'],
|
|
||||||
alias: json['alias'],
|
|
||||||
name: json['name'],
|
|
||||||
description: json['description'],
|
|
||||||
type: json['type'],
|
|
||||||
members: json['members']
|
|
||||||
?.map((e) => ChannelMember.fromJson(e))
|
|
||||||
.toList()
|
|
||||||
.cast<ChannelMember>(),
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
accountId: json['account_id'],
|
|
||||||
realm: json['realm'] != null ? Realm.fromJson(json['realm']) : null,
|
|
||||||
realmId: json['realm_id'],
|
|
||||||
isEncrypted: json['is_encrypted'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$ChannelToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
@override
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
bool operator ==(Object other) {
|
||||||
'deleted_at': deletedAt,
|
if (other is! Channel) return false;
|
||||||
'alias': alias,
|
return id == other.id;
|
||||||
'name': name,
|
|
||||||
'description': description,
|
|
||||||
'type': type,
|
|
||||||
'members': members?.map((e) => e.toJson()).toList(),
|
|
||||||
'account': account.toJson(),
|
|
||||||
'account_id': accountId,
|
|
||||||
'realm': realm?.toJson(),
|
|
||||||
'realm_id': realmId,
|
|
||||||
'is_encrypted': isEncrypted,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class ChannelMember {
|
class ChannelMember {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -95,25 +78,8 @@ class ChannelMember {
|
|||||||
required this.notify,
|
required this.notify,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ChannelMember.fromJson(Map<String, dynamic> json) => ChannelMember(
|
factory ChannelMember.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$ChannelMemberFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'],
|
|
||||||
channelId: json['channel_id'],
|
|
||||||
accountId: json['account_id'],
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
notify: json['notify'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$ChannelMemberToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt,
|
|
||||||
'channel_id': channelId,
|
|
||||||
'account_id': accountId,
|
|
||||||
'account': account.toJson(),
|
|
||||||
'notify': notify,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
74
lib/models/channel.g.dart
Normal file
74
lib/models/channel.g.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'channel.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Channel _$ChannelFromJson(Map<String, dynamic> json) => Channel(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
alias: json['alias'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
type: (json['type'] as num).toInt(),
|
||||||
|
members: (json['members'] as List<dynamic>?)
|
||||||
|
?.map((e) => ChannelMember.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
account: Account.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
isEncrypted: json['is_encrypted'] as bool,
|
||||||
|
realm: json['realm'] == null
|
||||||
|
? null
|
||||||
|
: Realm.fromJson(json['realm'] as Map<String, dynamic>),
|
||||||
|
realmId: (json['realm_id'] as num?)?.toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$ChannelToJson(Channel instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'alias': instance.alias,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'type': instance.type,
|
||||||
|
'members': instance.members?.map((e) => e.toJson()).toList(),
|
||||||
|
'account': instance.account.toJson(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'realm': instance.realm?.toJson(),
|
||||||
|
'realm_id': instance.realmId,
|
||||||
|
'is_encrypted': instance.isEncrypted,
|
||||||
|
'is_available': instance.isAvailable,
|
||||||
|
};
|
||||||
|
|
||||||
|
ChannelMember _$ChannelMemberFromJson(Map<String, dynamic> json) =>
|
||||||
|
ChannelMember(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
channelId: (json['channel_id'] as num).toInt(),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
account: Account.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
notify: (json['notify'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$ChannelMemberToJson(ChannelMember instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'channel_id': instance.channelId,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'account': instance.account.toJson(),
|
||||||
|
'notify': instance.notify,
|
||||||
|
};
|
49
lib/models/daily_sign.dart
Normal file
49
lib/models/daily_sign.dart
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:solian/models/account.dart';
|
||||||
|
|
||||||
|
part 'daily_sign.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class DailySignRecord {
|
||||||
|
int id;
|
||||||
|
DateTime createdAt;
|
||||||
|
DateTime updatedAt;
|
||||||
|
DateTime? deletedAt;
|
||||||
|
Account account;
|
||||||
|
int resultTier;
|
||||||
|
int resultExperience;
|
||||||
|
int accountId;
|
||||||
|
|
||||||
|
DailySignRecord({
|
||||||
|
required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.resultTier,
|
||||||
|
required this.resultExperience,
|
||||||
|
required this.account,
|
||||||
|
required this.accountId,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory DailySignRecord.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$DailySignRecordFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$DailySignRecordToJson(this);
|
||||||
|
|
||||||
|
String get symbol => switch (resultTier) {
|
||||||
|
0 => '大\n凶',
|
||||||
|
1 => '凶',
|
||||||
|
2 => '中\n平',
|
||||||
|
3 => '吉',
|
||||||
|
_ => '大\n吉',
|
||||||
|
};
|
||||||
|
|
||||||
|
String get overviewSuggestion => switch (resultTier) {
|
||||||
|
0 => 'dailySignTier0'.tr,
|
||||||
|
1 => 'dailySignTier1'.tr,
|
||||||
|
2 => 'dailySignTier2'.tr,
|
||||||
|
3 => 'dailySignTier3'.tr,
|
||||||
|
_ => 'dailySignTier4'.tr,
|
||||||
|
};
|
||||||
|
}
|
33
lib/models/daily_sign.g.dart
Normal file
33
lib/models/daily_sign.g.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'daily_sign.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
DailySignRecord _$DailySignRecordFromJson(Map<String, dynamic> json) =>
|
||||||
|
DailySignRecord(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
resultTier: (json['result_tier'] as num).toInt(),
|
||||||
|
resultExperience: (json['result_experience'] as num).toInt(),
|
||||||
|
account: Account.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$DailySignRecordToJson(DailySignRecord instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'account': instance.account.toJson(),
|
||||||
|
'result_tier': instance.resultTier,
|
||||||
|
'result_experience': instance.resultExperience,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
@ -1,6 +1,9 @@
|
|||||||
import 'package:solian/models/account.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:solian/models/channel.dart';
|
import 'package:solian/models/channel.dart';
|
||||||
|
|
||||||
|
part 'event.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Event {
|
class Event {
|
||||||
int id;
|
int id;
|
||||||
String uuid;
|
String uuid;
|
||||||
@ -10,10 +13,11 @@ class Event {
|
|||||||
Map<String, dynamic> body;
|
Map<String, dynamic> body;
|
||||||
String type;
|
String type;
|
||||||
Channel? channel;
|
Channel? channel;
|
||||||
Sender sender;
|
ChannelMember sender;
|
||||||
int channelId;
|
int channelId;
|
||||||
int senderId;
|
int senderId;
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: true)
|
||||||
bool isPending = false;
|
bool isPending = false;
|
||||||
|
|
||||||
Event({
|
Event({
|
||||||
@ -30,38 +34,16 @@ class Event {
|
|||||||
required this.senderId,
|
required this.senderId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Event.fromJson(Map<String, dynamic> json) => Event(
|
factory Event.fromJson(Map<String, dynamic> json) => _$EventFromJson(json);
|
||||||
id: json['id'],
|
|
||||||
uuid: json['uuid'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'],
|
|
||||||
body: json['body'],
|
|
||||||
type: json['type'],
|
|
||||||
channel:
|
|
||||||
json['channel'] != null ? Channel.fromJson(json['channel']) : null,
|
|
||||||
sender: Sender.fromJson(json['sender']),
|
|
||||||
channelId: json['channel_id'],
|
|
||||||
senderId: json['sender_id'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$EventToJson(this);
|
||||||
'id': id,
|
|
||||||
'uuid': uuid,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt,
|
|
||||||
'body': body,
|
|
||||||
'type': type,
|
|
||||||
'channel': channel?.toJson(),
|
|
||||||
'sender': sender.toJson(),
|
|
||||||
'channel_id': channelId,
|
|
||||||
'sender_id': senderId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class EventMessageBody {
|
class EventMessageBody {
|
||||||
|
@JsonKey(defaultValue: '')
|
||||||
String text;
|
String text;
|
||||||
|
@JsonKey(defaultValue: 'plain')
|
||||||
String algorithm;
|
String algorithm;
|
||||||
List<String>? attachments;
|
List<String>? attachments;
|
||||||
int? quoteEvent;
|
int? quoteEvent;
|
||||||
@ -78,69 +60,7 @@ class EventMessageBody {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory EventMessageBody.fromJson(Map<String, dynamic> json) =>
|
factory EventMessageBody.fromJson(Map<String, dynamic> json) =>
|
||||||
EventMessageBody(
|
_$EventMessageBodyFromJson(json);
|
||||||
text: json['text'] ?? '',
|
|
||||||
algorithm: json['algorithm'] ?? 'plain',
|
|
||||||
attachments: json['attachments'] != null
|
|
||||||
? List<String>.from(json['attachments']?.whereType<String>())
|
|
||||||
: null,
|
|
||||||
quoteEvent: json['quote_event'],
|
|
||||||
relatedEvent: json['related_event'],
|
|
||||||
relatedUsers: json['related_users'] != null
|
|
||||||
? List<int>.from(json['related_users'].map((x) => x))
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$EventMessageBodyToJson(this);
|
||||||
'text': text,
|
|
||||||
'algorithm': algorithm,
|
|
||||||
'attachments': attachments?.cast<dynamic>(),
|
|
||||||
'quote_event': quoteEvent,
|
|
||||||
'related_event': relatedEvent,
|
|
||||||
'related_users': relatedUsers?.cast<dynamic>(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Sender {
|
|
||||||
int id;
|
|
||||||
DateTime createdAt;
|
|
||||||
DateTime updatedAt;
|
|
||||||
DateTime? deletedAt;
|
|
||||||
Account account;
|
|
||||||
int channelId;
|
|
||||||
int accountId;
|
|
||||||
int notify;
|
|
||||||
|
|
||||||
Sender({
|
|
||||||
required this.id,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
this.deletedAt,
|
|
||||||
required this.account,
|
|
||||||
required this.channelId,
|
|
||||||
required this.accountId,
|
|
||||||
required this.notify,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Sender.fromJson(Map<String, dynamic> json) => Sender(
|
|
||||||
id: json['id'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'],
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
channelId: json['channel_id'],
|
|
||||||
accountId: json['account_id'],
|
|
||||||
notify: json['notify'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt,
|
|
||||||
'account': account.toJson(),
|
|
||||||
'channel_id': channelId,
|
|
||||||
'account_id': accountId,
|
|
||||||
'notify': notify,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
64
lib/models/event.g.dart
Normal file
64
lib/models/event.g.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'event.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Event _$EventFromJson(Map<String, dynamic> json) => Event(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
uuid: json['uuid'] as String,
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
body: json['body'] as Map<String, dynamic>,
|
||||||
|
type: json['type'] as String,
|
||||||
|
channel: json['channel'] == null
|
||||||
|
? null
|
||||||
|
: Channel.fromJson(json['channel'] as Map<String, dynamic>),
|
||||||
|
sender: ChannelMember.fromJson(json['sender'] as Map<String, dynamic>),
|
||||||
|
channelId: (json['channel_id'] as num).toInt(),
|
||||||
|
senderId: (json['sender_id'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$EventToJson(Event instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'uuid': instance.uuid,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'body': instance.body,
|
||||||
|
'type': instance.type,
|
||||||
|
'channel': instance.channel?.toJson(),
|
||||||
|
'sender': instance.sender.toJson(),
|
||||||
|
'channel_id': instance.channelId,
|
||||||
|
'sender_id': instance.senderId,
|
||||||
|
'is_pending': instance.isPending,
|
||||||
|
};
|
||||||
|
|
||||||
|
EventMessageBody _$EventMessageBodyFromJson(Map<String, dynamic> json) =>
|
||||||
|
EventMessageBody(
|
||||||
|
text: json['text'] as String? ?? '',
|
||||||
|
algorithm: json['algorithm'] as String? ?? 'plain',
|
||||||
|
attachments: (json['attachments'] as List<dynamic>?)
|
||||||
|
?.map((e) => e as String)
|
||||||
|
.toList(),
|
||||||
|
quoteEvent: (json['quote_event'] as num?)?.toInt(),
|
||||||
|
relatedEvent: (json['related_event'] as num?)?.toInt(),
|
||||||
|
relatedUsers: (json['related_users'] as List<dynamic>?)
|
||||||
|
?.map((e) => (e as num).toInt())
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$EventMessageBodyToJson(EventMessageBody instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'text': instance.text,
|
||||||
|
'algorithm': instance.algorithm,
|
||||||
|
'attachments': instance.attachments,
|
||||||
|
'quote_event': instance.quoteEvent,
|
||||||
|
'related_event': instance.relatedEvent,
|
||||||
|
'related_users': instance.relatedUsers,
|
||||||
|
};
|
@ -1,83 +0,0 @@
|
|||||||
class Tag {
|
|
||||||
int id;
|
|
||||||
String alias;
|
|
||||||
String name;
|
|
||||||
String description;
|
|
||||||
DateTime createdAt;
|
|
||||||
DateTime updatedAt;
|
|
||||||
DateTime? deletedAt;
|
|
||||||
|
|
||||||
Tag({
|
|
||||||
required this.id,
|
|
||||||
required this.alias,
|
|
||||||
required this.name,
|
|
||||||
required this.description,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
required this.deletedAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Tag.fromJson(Map<String, dynamic> json) => Tag(
|
|
||||||
id: json['id'],
|
|
||||||
alias: json['alias'],
|
|
||||||
name: json['name'],
|
|
||||||
description: json['description'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'id': id,
|
|
||||||
'alias': alias,
|
|
||||||
'description': description,
|
|
||||||
'name': name,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Category {
|
|
||||||
int id;
|
|
||||||
String alias;
|
|
||||||
String name;
|
|
||||||
String description;
|
|
||||||
DateTime createdAt;
|
|
||||||
DateTime updatedAt;
|
|
||||||
DateTime? deletedAt;
|
|
||||||
|
|
||||||
Category({
|
|
||||||
required this.id,
|
|
||||||
required this.alias,
|
|
||||||
required this.name,
|
|
||||||
required this.description,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
required this.deletedAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Category.fromJson(Map<String, dynamic> json) => Category(
|
|
||||||
id: json['id'],
|
|
||||||
alias: json['alias'],
|
|
||||||
name: json['name'],
|
|
||||||
description: json['description'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'id': id,
|
|
||||||
'alias': alias,
|
|
||||||
'description': description,
|
|
||||||
'name': name,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
class Keypair {
|
|
||||||
final String id;
|
|
||||||
final String algorithm;
|
|
||||||
final String publicKey;
|
|
||||||
final String? privateKey;
|
|
||||||
|
|
||||||
final bool isOwned;
|
|
||||||
|
|
||||||
Keypair({
|
|
||||||
required this.id,
|
|
||||||
required this.algorithm,
|
|
||||||
required this.publicKey,
|
|
||||||
required this.privateKey,
|
|
||||||
this.isOwned = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Keypair.fromJson(Map<String, dynamic> json) => Keypair(
|
|
||||||
id: json['id'],
|
|
||||||
algorithm: json['algorithm'],
|
|
||||||
publicKey: json['public_key'],
|
|
||||||
privateKey: json['private_key'],
|
|
||||||
isOwned: json['is_owned'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'id': id,
|
|
||||||
'algorithm': algorithm,
|
|
||||||
'public_key': publicKey,
|
|
||||||
'private_key': privateKey,
|
|
||||||
'is_owned': isOwned,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,3 +1,8 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'link.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class LinkMeta {
|
class LinkMeta {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -29,37 +34,8 @@ class LinkMeta {
|
|||||||
required this.siteName,
|
required this.siteName,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory LinkMeta.fromJson(Map<String, dynamic> json) => LinkMeta(
|
factory LinkMeta.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$LinkMetaFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
entryId: json['entry_id'],
|
|
||||||
icon: json['icon'],
|
|
||||||
url: json['url'],
|
|
||||||
title: json['title'],
|
|
||||||
image: json['image'],
|
|
||||||
video: json['video'],
|
|
||||||
audio: json['audio'],
|
|
||||||
description: json['description'],
|
|
||||||
siteName: json['site_name'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$LinkMetaToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'entry_id': entryId,
|
|
||||||
'icon': icon,
|
|
||||||
'url': url,
|
|
||||||
'title': title,
|
|
||||||
'image': image,
|
|
||||||
'video': video,
|
|
||||||
'audio': audio,
|
|
||||||
'description': description,
|
|
||||||
'site_name': siteName,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
41
lib/models/link.g.dart
Normal file
41
lib/models/link.g.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'link.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
LinkMeta _$LinkMetaFromJson(Map<String, dynamic> json) => LinkMeta(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
entryId: json['entry_id'] as String,
|
||||||
|
icon: json['icon'] as String?,
|
||||||
|
url: json['url'] as String,
|
||||||
|
title: json['title'] as String?,
|
||||||
|
image: json['image'] as String?,
|
||||||
|
video: json['video'] as String?,
|
||||||
|
audio: json['audio'] as String?,
|
||||||
|
description: json['description'] as String?,
|
||||||
|
siteName: json['site_name'] as String?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$LinkMetaToJson(LinkMeta instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'entry_id': instance.entryId,
|
||||||
|
'icon': instance.icon,
|
||||||
|
'url': instance.url,
|
||||||
|
'title': instance.title,
|
||||||
|
'image': instance.image,
|
||||||
|
'video': instance.video,
|
||||||
|
'audio': instance.audio,
|
||||||
|
'description': instance.description,
|
||||||
|
'site_name': instance.siteName,
|
||||||
|
};
|
@ -1,3 +1,8 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'notification.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Notification {
|
class Notification {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -25,35 +30,8 @@ class Notification {
|
|||||||
required this.accountId,
|
required this.accountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Notification.fromJson(Map<String, dynamic> json) => Notification(
|
factory Notification.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'] ?? 0,
|
_$NotificationFromJson(json);
|
||||||
createdAt: json['created_at'] == null
|
|
||||||
? DateTime.now()
|
|
||||||
: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: json['updated_at'] == null
|
|
||||||
? DateTime.now()
|
|
||||||
: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'],
|
|
||||||
title: json['title'],
|
|
||||||
subtitle: json['subtitle'],
|
|
||||||
body: json['body'],
|
|
||||||
avatar: json['avatar'],
|
|
||||||
picture: json['picture'],
|
|
||||||
senderId: json['sender_id'],
|
|
||||||
accountId: json['account_id'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$NotificationToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt,
|
|
||||||
'title': title,
|
|
||||||
'subtitle': subtitle,
|
|
||||||
'body': body,
|
|
||||||
'avatar': avatar,
|
|
||||||
'picture': picture,
|
|
||||||
'sender_id': senderId,
|
|
||||||
'account_id': accountId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
38
lib/models/notification.g.dart
Normal file
38
lib/models/notification.g.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'notification.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Notification _$NotificationFromJson(Map<String, dynamic> json) => Notification(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
title: json['title'] as String,
|
||||||
|
subtitle: json['subtitle'] as String?,
|
||||||
|
body: json['body'] as String,
|
||||||
|
avatar: json['avatar'] as String?,
|
||||||
|
picture: json['picture'] as String?,
|
||||||
|
senderId: (json['sender_id'] as num?)?.toInt(),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$NotificationToJson(Notification instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'title': instance.title,
|
||||||
|
'subtitle': instance.subtitle,
|
||||||
|
'body': instance.body,
|
||||||
|
'avatar': instance.avatar,
|
||||||
|
'picture': instance.picture,
|
||||||
|
'sender_id': instance.senderId,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
@ -1,7 +1,16 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'packet.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class NetworkPackage {
|
class NetworkPackage {
|
||||||
|
@JsonKey(name: 'w')
|
||||||
String method;
|
String method;
|
||||||
|
@JsonKey(name: 'e')
|
||||||
String? endpoint;
|
String? endpoint;
|
||||||
|
@JsonKey(name: 'm')
|
||||||
String? message;
|
String? message;
|
||||||
|
@JsonKey(name: 'p')
|
||||||
Map<String, dynamic>? payload;
|
Map<String, dynamic>? payload;
|
||||||
|
|
||||||
NetworkPackage({
|
NetworkPackage({
|
||||||
@ -11,17 +20,8 @@ class NetworkPackage {
|
|||||||
this.payload,
|
this.payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory NetworkPackage.fromJson(Map<String, dynamic> json) => NetworkPackage(
|
factory NetworkPackage.fromJson(Map<String, dynamic> json) =>
|
||||||
method: json['w'],
|
_$NetworkPackageFromJson(json);
|
||||||
endpoint: json['e'],
|
|
||||||
message: json['m'],
|
|
||||||
payload: json['p'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$NetworkPackageToJson(this);
|
||||||
'w': method,
|
|
||||||
'e': endpoint,
|
|
||||||
'm': message,
|
|
||||||
'p': payload,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
23
lib/models/packet.g.dart
Normal file
23
lib/models/packet.g.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'packet.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
NetworkPackage _$NetworkPackageFromJson(Map<String, dynamic> json) =>
|
||||||
|
NetworkPackage(
|
||||||
|
method: json['w'] as String,
|
||||||
|
endpoint: json['e'] as String?,
|
||||||
|
message: json['m'] as String?,
|
||||||
|
payload: json['p'] as Map<String, dynamic>?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$NetworkPackageToJson(NetworkPackage instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'w': instance.method,
|
||||||
|
'e': instance.endpoint,
|
||||||
|
'm': instance.message,
|
||||||
|
'p': instance.payload,
|
||||||
|
};
|
@ -1,3 +1,8 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'pagination.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class PaginationResult {
|
class PaginationResult {
|
||||||
int count;
|
int count;
|
||||||
List<dynamic>? data;
|
List<dynamic>? data;
|
||||||
@ -8,10 +13,7 @@ class PaginationResult {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory PaginationResult.fromJson(Map<String, dynamic> json) =>
|
factory PaginationResult.fromJson(Map<String, dynamic> json) =>
|
||||||
PaginationResult(count: json['count'], data: json['data']);
|
_$PaginationResultFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$PaginationResultToJson(this);
|
||||||
'count': count,
|
|
||||||
'data': data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
19
lib/models/pagination.g.dart
Normal file
19
lib/models/pagination.g.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'pagination.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
PaginationResult _$PaginationResultFromJson(Map<String, dynamic> json) =>
|
||||||
|
PaginationResult(
|
||||||
|
count: (json['count'] as num).toInt(),
|
||||||
|
data: json['data'] as List<dynamic>?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$PaginationResultToJson(PaginationResult instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'count': instance.count,
|
||||||
|
'data': instance.data,
|
||||||
|
};
|
@ -1,7 +1,11 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
import 'package:solian/models/feed.dart';
|
import 'package:solian/models/post_categories.dart';
|
||||||
import 'package:solian/models/realm.dart';
|
import 'package:solian/models/realm.dart';
|
||||||
|
|
||||||
|
part 'post.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Post {
|
class Post {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -57,81 +61,15 @@ class Post {
|
|||||||
required this.metric,
|
required this.metric,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Post.fromJson(Map<String, dynamic> json) => Post(
|
factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
|
||||||
id: json['id'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
alias: json['alias'],
|
|
||||||
areaAlias: json['area_alias'],
|
|
||||||
type: json['type'],
|
|
||||||
body: json['body'],
|
|
||||||
tags: json['tags']?.map((x) => Tag.fromJson(x)).toList().cast<Tag>(),
|
|
||||||
categories: json['categories']
|
|
||||||
?.map((x) => Category.fromJson(x))
|
|
||||||
.toList()
|
|
||||||
.cast<Category>(),
|
|
||||||
replies: json['replies'],
|
|
||||||
replyId: json['reply_id'],
|
|
||||||
repostId: json['repost_id'],
|
|
||||||
realmId: json['realm_id'],
|
|
||||||
replyTo:
|
|
||||||
json['reply_to'] != null ? Post.fromJson(json['reply_to']) : null,
|
|
||||||
repostTo:
|
|
||||||
json['repost_to'] != null ? Post.fromJson(json['repost_to']) : null,
|
|
||||||
realm: json['realm'] != null ? Realm.fromJson(json['realm']) : null,
|
|
||||||
editedAt: json['edited_at'] != null
|
|
||||||
? DateTime.parse(json['edited_at'])
|
|
||||||
: null,
|
|
||||||
publishedAt: json['published_at'] != null
|
|
||||||
? DateTime.parse(json['published_at'])
|
|
||||||
: null,
|
|
||||||
publishedUntil: json['published_until'] != null
|
|
||||||
? DateTime.parse(json['published_until'])
|
|
||||||
: null,
|
|
||||||
pinnedAt: json['pinned_at'] != null
|
|
||||||
? DateTime.parse(json['pinned_at'])
|
|
||||||
: null,
|
|
||||||
isDraft: json['is_draft'],
|
|
||||||
authorId: json['author_id'],
|
|
||||||
author: Account.fromJson(json['author']),
|
|
||||||
metric:
|
|
||||||
json['metric'] != null ? PostMetric.fromJson(json['metric']) : null,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$PostToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'edited_at': editedAt?.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'alias': alias,
|
|
||||||
'area_alias': areaAlias,
|
|
||||||
'type': type,
|
|
||||||
'body': body,
|
|
||||||
'tags': tags,
|
|
||||||
'categories': categories,
|
|
||||||
'replies': replies,
|
|
||||||
'reply_id': replyId,
|
|
||||||
'repost_id': repostId,
|
|
||||||
'realm_id': realmId,
|
|
||||||
'reply_to': replyTo?.toJson(),
|
|
||||||
'repost_to': repostTo?.toJson(),
|
|
||||||
'realm': realm?.toJson(),
|
|
||||||
'published_at': publishedAt?.toIso8601String(),
|
|
||||||
'published_until': publishedUntil?.toIso8601String(),
|
|
||||||
'pinned_at': pinnedAt?.toIso8601String(),
|
|
||||||
'is_draft': isDraft,
|
|
||||||
'author_id': authorId,
|
|
||||||
'author': author.toJson(),
|
|
||||||
'metric': metric?.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class PostMetric {
|
class PostMetric {
|
||||||
int reactionCount;
|
int reactionCount;
|
||||||
|
@JsonKey(defaultValue: {})
|
||||||
Map<String, int> reactionList;
|
Map<String, int> reactionList;
|
||||||
int replyCount;
|
int replyCount;
|
||||||
|
|
||||||
@ -141,22 +79,8 @@ class PostMetric {
|
|||||||
required this.replyCount,
|
required this.replyCount,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PostMetric.fromJson(Map<String, dynamic> json) => PostMetric(
|
factory PostMetric.fromJson(Map<String, dynamic> json) =>
|
||||||
reactionCount: json['reaction_count'],
|
_$PostMetricFromJson(json);
|
||||||
replyCount: json['reply_count'],
|
|
||||||
reactionList: json['reaction_list'] != null
|
|
||||||
? json['reaction_list']
|
|
||||||
.map((key, value) => MapEntry(
|
|
||||||
key,
|
|
||||||
int.tryParse(value.toString()) ??
|
|
||||||
(value is double ? value.toInt() : null)))
|
|
||||||
.cast<String, int>()
|
|
||||||
: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$PostMetricToJson(this);
|
||||||
'reaction_count': reactionCount,
|
|
||||||
'reply_count': replyCount,
|
|
||||||
'reaction_list': reactionList,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
103
lib/models/post.g.dart
Normal file
103
lib/models/post.g.dart
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'post.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Post _$PostFromJson(Map<String, dynamic> json) => Post(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
editedAt: json['edited_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['edited_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
alias: json['alias'] as String?,
|
||||||
|
areaAlias: json['area_alias'] as String?,
|
||||||
|
type: json['type'] as String,
|
||||||
|
body: json['body'],
|
||||||
|
tags: (json['tags'] as List<dynamic>?)
|
||||||
|
?.map((e) => Tag.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
categories: (json['categories'] as List<dynamic>?)
|
||||||
|
?.map((e) => Category.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
replies: (json['replies'] as List<dynamic>?)
|
||||||
|
?.map((e) => Post.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
replyId: (json['reply_id'] as num?)?.toInt(),
|
||||||
|
repostId: (json['repost_id'] as num?)?.toInt(),
|
||||||
|
realmId: (json['realm_id'] as num?)?.toInt(),
|
||||||
|
replyTo: json['reply_to'] == null
|
||||||
|
? null
|
||||||
|
: Post.fromJson(json['reply_to'] as Map<String, dynamic>),
|
||||||
|
repostTo: json['repost_to'] == null
|
||||||
|
? null
|
||||||
|
: Post.fromJson(json['repost_to'] as Map<String, dynamic>),
|
||||||
|
realm: json['realm'] == null
|
||||||
|
? null
|
||||||
|
: Realm.fromJson(json['realm'] as Map<String, dynamic>),
|
||||||
|
publishedAt: json['published_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['published_at'] as String),
|
||||||
|
publishedUntil: json['published_until'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['published_until'] as String),
|
||||||
|
pinnedAt: json['pinned_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['pinned_at'] as String),
|
||||||
|
isDraft: json['is_draft'] as bool?,
|
||||||
|
authorId: (json['author_id'] as num).toInt(),
|
||||||
|
author: Account.fromJson(json['author'] as Map<String, dynamic>),
|
||||||
|
metric: json['metric'] == null
|
||||||
|
? null
|
||||||
|
: PostMetric.fromJson(json['metric'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$PostToJson(Post instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'edited_at': instance.editedAt?.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'alias': instance.alias,
|
||||||
|
'area_alias': instance.areaAlias,
|
||||||
|
'body': instance.body,
|
||||||
|
'tags': instance.tags?.map((e) => e.toJson()).toList(),
|
||||||
|
'categories': instance.categories?.map((e) => e.toJson()).toList(),
|
||||||
|
'replies': instance.replies?.map((e) => e.toJson()).toList(),
|
||||||
|
'type': instance.type,
|
||||||
|
'reply_id': instance.replyId,
|
||||||
|
'repost_id': instance.repostId,
|
||||||
|
'realm_id': instance.realmId,
|
||||||
|
'reply_to': instance.replyTo?.toJson(),
|
||||||
|
'repost_to': instance.repostTo?.toJson(),
|
||||||
|
'realm': instance.realm?.toJson(),
|
||||||
|
'published_at': instance.publishedAt?.toIso8601String(),
|
||||||
|
'published_until': instance.publishedUntil?.toIso8601String(),
|
||||||
|
'pinned_at': instance.pinnedAt?.toIso8601String(),
|
||||||
|
'is_draft': instance.isDraft,
|
||||||
|
'author_id': instance.authorId,
|
||||||
|
'author': instance.author.toJson(),
|
||||||
|
'metric': instance.metric?.toJson(),
|
||||||
|
};
|
||||||
|
|
||||||
|
PostMetric _$PostMetricFromJson(Map<String, dynamic> json) => PostMetric(
|
||||||
|
reactionCount: (json['reaction_count'] as num).toInt(),
|
||||||
|
reactionList: (json['reaction_list'] as Map<String, dynamic>?)?.map(
|
||||||
|
(k, e) => MapEntry(k, (e as num).toInt()),
|
||||||
|
) ??
|
||||||
|
{},
|
||||||
|
replyCount: (json['reply_count'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$PostMetricToJson(PostMetric instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'reaction_count': instance.reactionCount,
|
||||||
|
'reaction_list': instance.reactionList,
|
||||||
|
'reply_count': instance.replyCount,
|
||||||
|
};
|
54
lib/models/post_categories.dart
Normal file
54
lib/models/post_categories.dart
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'post_categories.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class Tag {
|
||||||
|
int id;
|
||||||
|
String alias;
|
||||||
|
String name;
|
||||||
|
String description;
|
||||||
|
DateTime createdAt;
|
||||||
|
DateTime updatedAt;
|
||||||
|
DateTime? deletedAt;
|
||||||
|
|
||||||
|
Tag({
|
||||||
|
required this.id,
|
||||||
|
required this.alias,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Tag.fromJson(Map<String, dynamic> json) => _$TagFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$TagToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class Category {
|
||||||
|
int id;
|
||||||
|
String alias;
|
||||||
|
String name;
|
||||||
|
String description;
|
||||||
|
DateTime createdAt;
|
||||||
|
DateTime updatedAt;
|
||||||
|
DateTime? deletedAt;
|
||||||
|
|
||||||
|
Category({
|
||||||
|
required this.id,
|
||||||
|
required this.alias,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Category.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$CategoryFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$CategoryToJson(this);
|
||||||
|
}
|
51
lib/models/post_categories.g.dart
Normal file
51
lib/models/post_categories.g.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'post_categories.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Tag _$TagFromJson(Map<String, dynamic> json) => Tag(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
alias: json['alias'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$TagToJson(Tag instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'alias': instance.alias,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Category _$CategoryFromJson(Map<String, dynamic> json) => Category(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
alias: json['alias'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$CategoryToJson(Category instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'alias': instance.alias,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
};
|
@ -1,5 +1,9 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
|
|
||||||
|
part 'realm.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Realm {
|
class Realm {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -25,33 +29,9 @@ class Realm {
|
|||||||
this.accountId,
|
this.accountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Realm.fromJson(Map<String, dynamic> json) => Realm(
|
factory Realm.fromJson(Map<String, dynamic> json) => _$RealmFromJson(json);
|
||||||
id: json['id'],
|
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
alias: json['alias'],
|
|
||||||
name: json['name'],
|
|
||||||
description: json['description'],
|
|
||||||
isPublic: json['is_public'],
|
|
||||||
isCommunity: json['is_community'],
|
|
||||||
accountId: json['account_id'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$RealmToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt,
|
|
||||||
'alias': alias,
|
|
||||||
'name': name,
|
|
||||||
'description': description,
|
|
||||||
'is_public': isPublic,
|
|
||||||
'is_community': isCommunity,
|
|
||||||
'account_id': accountId,
|
|
||||||
};
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
@ -65,6 +45,7 @@ class Realm {
|
|||||||
int get hashCode => id;
|
int get hashCode => id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class RealmMember {
|
class RealmMember {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -86,27 +67,8 @@ class RealmMember {
|
|||||||
required this.powerLevel,
|
required this.powerLevel,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory RealmMember.fromJson(Map<String, dynamic> json) => RealmMember(
|
factory RealmMember.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$RealmMemberFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: null,
|
|
||||||
realmId: json['realm_id'],
|
|
||||||
accountId: json['account_id'],
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
powerLevel: json['power_level'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$RealmMemberToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt,
|
|
||||||
'realm_id': realmId,
|
|
||||||
'account_id': accountId,
|
|
||||||
'account': account.toJson(),
|
|
||||||
'power_level': powerLevel,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
60
lib/models/realm.g.dart
Normal file
60
lib/models/realm.g.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'realm.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Realm _$RealmFromJson(Map<String, dynamic> json) => Realm(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
alias: json['alias'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
isPublic: json['is_public'] as bool,
|
||||||
|
isCommunity: json['is_community'] as bool,
|
||||||
|
accountId: (json['account_id'] as num?)?.toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$RealmToJson(Realm instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'alias': instance.alias,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'is_public': instance.isPublic,
|
||||||
|
'is_community': instance.isCommunity,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
||||||
|
|
||||||
|
RealmMember _$RealmMemberFromJson(Map<String, dynamic> json) => RealmMember(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
realmId: (json['realm_id'] as num).toInt(),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
account: Account.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
powerLevel: (json['power_level'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$RealmMemberToJson(RealmMember instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'realm_id': instance.realmId,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'account': instance.account.toJson(),
|
||||||
|
'power_level': instance.powerLevel,
|
||||||
|
};
|
@ -1,5 +1,9 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
|
|
||||||
|
part 'relations.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Relationship {
|
class Relationship {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -23,27 +27,8 @@ class Relationship {
|
|||||||
required this.status,
|
required this.status,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Relationship.fromJson(Map<String, dynamic> json) => Relationship(
|
factory Relationship.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$RelationshipFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'],
|
|
||||||
accountId: json['account_id'],
|
|
||||||
relatedId: json['related_id'],
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
related: Account.fromJson(json['related']),
|
|
||||||
status: json['status'],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$RelationshipToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt,
|
|
||||||
'account_id': accountId,
|
|
||||||
'related_id': relatedId,
|
|
||||||
'account': account.toJson(),
|
|
||||||
'related': related.toJson(),
|
|
||||||
'status': status,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
34
lib/models/relations.g.dart
Normal file
34
lib/models/relations.g.dart
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'relations.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Relationship _$RelationshipFromJson(Map<String, dynamic> json) => Relationship(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
relatedId: (json['related_id'] as num).toInt(),
|
||||||
|
account: Account.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
related: Account.fromJson(json['related'] as Map<String, dynamic>),
|
||||||
|
status: (json['status'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$RelationshipToJson(Relationship instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'related_id': instance.relatedId,
|
||||||
|
'account': instance.account.toJson(),
|
||||||
|
'related': instance.related.toJson(),
|
||||||
|
'status': instance.status,
|
||||||
|
};
|
@ -1,7 +1,11 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
|
|
||||||
|
part 'stickers.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Sticker {
|
class Sticker {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -39,38 +43,13 @@ class Sticker {
|
|||||||
'/attachments/${attachment.rid}',
|
'/attachments/${attachment.rid}',
|
||||||
);
|
);
|
||||||
|
|
||||||
factory Sticker.fromJson(Map<String, dynamic> json) => Sticker(
|
factory Sticker.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$StickerFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: json['deleted_at'],
|
|
||||||
alias: json['alias'],
|
|
||||||
name: json['name'],
|
|
||||||
attachmentId: json['attachment_id'],
|
|
||||||
attachment: Attachment.fromJson(json['attachment']),
|
|
||||||
packId: json['pack_id'],
|
|
||||||
pack: json['pack'] != null ? StickerPack.fromJson(json['pack']) : null,
|
|
||||||
accountId: json['account_id'],
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$StickerToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'alias': alias,
|
|
||||||
'name': name,
|
|
||||||
'attachment_id': attachmentId,
|
|
||||||
'attachment': attachment.toJson(),
|
|
||||||
'pack_id': packId,
|
|
||||||
'account_id': accountId,
|
|
||||||
'account': account.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class StickerPack {
|
class StickerPack {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -96,36 +75,8 @@ class StickerPack {
|
|||||||
required this.account,
|
required this.account,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory StickerPack.fromJson(Map<String, dynamic> json) => StickerPack(
|
factory StickerPack.fromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'],
|
_$StickerPackFromJson(json);
|
||||||
createdAt: DateTime.parse(json['created_at']),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at']),
|
|
||||||
deletedAt: json['deleted_at'] != null
|
|
||||||
? DateTime.parse(json['deleted_at'])
|
|
||||||
: json['deleted_at'],
|
|
||||||
prefix: json['prefix'],
|
|
||||||
name: json['name'],
|
|
||||||
description: json['description'],
|
|
||||||
stickers: json['stickers'] == null
|
|
||||||
? []
|
|
||||||
: List<Sticker>.from(
|
|
||||||
json['stickers']!.map((x) => Sticker.fromJson(x))),
|
|
||||||
accountId: json['account_id'],
|
|
||||||
account: Account.fromJson(json['account']),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$StickerPackToJson(this);
|
||||||
'id': id,
|
|
||||||
'created_at': createdAt.toIso8601String(),
|
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': deletedAt?.toIso8601String(),
|
|
||||||
'prefix': prefix,
|
|
||||||
'name': name,
|
|
||||||
'description': description,
|
|
||||||
'stickers': stickers == null
|
|
||||||
? []
|
|
||||||
: List<dynamic>.from(stickers!.map((x) => x.toJson())),
|
|
||||||
'account_id': accountId,
|
|
||||||
'account': account.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
73
lib/models/stickers.g.dart
Normal file
73
lib/models/stickers.g.dart
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'stickers.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Sticker _$StickerFromJson(Map<String, dynamic> json) => Sticker(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
alias: json['alias'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
attachmentId: (json['attachment_id'] as num).toInt(),
|
||||||
|
attachment:
|
||||||
|
Attachment.fromJson(json['attachment'] as Map<String, dynamic>),
|
||||||
|
packId: (json['pack_id'] as num).toInt(),
|
||||||
|
pack: json['pack'] == null
|
||||||
|
? null
|
||||||
|
: StickerPack.fromJson(json['pack'] as Map<String, dynamic>),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
account: Account.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$StickerToJson(Sticker instance) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'alias': instance.alias,
|
||||||
|
'name': instance.name,
|
||||||
|
'attachment_id': instance.attachmentId,
|
||||||
|
'attachment': instance.attachment.toJson(),
|
||||||
|
'pack_id': instance.packId,
|
||||||
|
'pack': instance.pack?.toJson(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'account': instance.account.toJson(),
|
||||||
|
};
|
||||||
|
|
||||||
|
StickerPack _$StickerPackFromJson(Map<String, dynamic> json) => StickerPack(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
prefix: json['prefix'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
stickers: (json['stickers'] as List<dynamic>?)
|
||||||
|
?.map((e) => Sticker.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
account: Account.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$StickerPackToJson(StickerPack instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'prefix': instance.prefix,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'stickers': instance.stickers?.map((e) => e.toJson()).toList(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'account': instance.account.toJson(),
|
||||||
|
};
|
@ -10,6 +10,22 @@ class PostProvider extends GetConnect {
|
|||||||
httpClient.baseUrl = ServiceFinder.buildUrl('interactive', null);
|
httpClient.baseUrl = ServiceFinder.buildUrl('interactive', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Response> seeWhatsNew(int pivot) async {
|
||||||
|
GetConnect client;
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.value) {
|
||||||
|
client = auth.configureClient('co');
|
||||||
|
} else {
|
||||||
|
client = ServiceFinder.configureClient('co');
|
||||||
|
}
|
||||||
|
final resp = await client.get('/whats-new?pivot=$pivot');
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw RequestException(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Response> listRecommendations(int page,
|
Future<Response> listRecommendations(int page,
|
||||||
{String? realm, String? channel}) async {
|
{String? realm, String? channel}) async {
|
||||||
GetConnect client;
|
GetConnect client;
|
||||||
|
58
lib/providers/daily_sign.dart
Normal file
58
lib/providers/daily_sign.dart
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:solian/exceptions/request.dart';
|
||||||
|
import 'package:solian/exceptions/unauthorized.dart';
|
||||||
|
import 'package:solian/models/daily_sign.dart';
|
||||||
|
import 'package:solian/models/pagination.dart';
|
||||||
|
import 'package:solian/providers/auth.dart';
|
||||||
|
|
||||||
|
class DailySignProvider extends GetxController {
|
||||||
|
Future<List<DailySignRecord>> listLastRecord(int take) async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
|
||||||
|
|
||||||
|
final client = auth.configureClient('id');
|
||||||
|
|
||||||
|
final resp = await client.get('/daily?take=$take');
|
||||||
|
if (resp.statusCode != 200 && resp.statusCode != 404) {
|
||||||
|
throw RequestException(resp);
|
||||||
|
} else if (resp.statusCode == 404) {
|
||||||
|
return List.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = PaginationResult.fromJson(resp.body);
|
||||||
|
|
||||||
|
return List.from(
|
||||||
|
result.data?.map((x) => DailySignRecord.fromJson(x)) ?? [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DailySignRecord?> getToday() async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
|
||||||
|
|
||||||
|
final client = auth.configureClient('id');
|
||||||
|
|
||||||
|
final resp = await client.get('/daily/today');
|
||||||
|
if (resp.statusCode != 200 && resp.statusCode != 404) {
|
||||||
|
throw RequestException(resp);
|
||||||
|
} else if (resp.statusCode == 404) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DailySignRecord.fromJson(resp.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DailySignRecord> signToday() async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
|
||||||
|
|
||||||
|
final client = auth.configureClient('id');
|
||||||
|
|
||||||
|
final resp = await client.post('/daily', {});
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw RequestException(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DailySignRecord.fromJson(resp.body);
|
||||||
|
}
|
||||||
|
}
|
26
lib/providers/durations.dart
Normal file
26
lib/providers/durations.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
extension DurationToHumanReadableString on Duration {
|
||||||
|
String toHumanReadableString({padZero = true}) {
|
||||||
|
final mm = inMinutes
|
||||||
|
.remainder(60)
|
||||||
|
.toString()
|
||||||
|
.padLeft(2, !padZero && inHours == 0 ? '' : '0');
|
||||||
|
final ss = inSeconds.remainder(60).toString().padLeft(2, '0');
|
||||||
|
|
||||||
|
if (inHours > 0) {
|
||||||
|
final hh = inHours.toString().padLeft(2, !padZero ? '' : '0');
|
||||||
|
return '$hh:$mm:$ss';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '$mm:$ss';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ParseDuration on Duration {
|
||||||
|
static Duration fromString(String duration) {
|
||||||
|
final parts = duration.split(':').reversed.toList();
|
||||||
|
final seconds = int.parse(parts[0]);
|
||||||
|
final minutes = parts.length > 1 ? int.parse(parts[1]) : 0;
|
||||||
|
final hours = parts.length > 2 ? int.parse(parts[2]) : 0;
|
||||||
|
return Duration(hours: hours, minutes: minutes, seconds: seconds);
|
||||||
|
}
|
||||||
|
}
|
51
lib/providers/experience.dart
Normal file
51
lib/providers/experience.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class ExperienceProvider extends GetxController {
|
||||||
|
static List<int> experienceToLevelRequirements = [
|
||||||
|
0, // Level 0
|
||||||
|
100, // Level 1
|
||||||
|
400, // Level 2
|
||||||
|
900, // Level 3
|
||||||
|
1600, // Level 4
|
||||||
|
2500, // Level 5
|
||||||
|
3600, // Level 6
|
||||||
|
4900, // Level 7
|
||||||
|
6400, // Level 8
|
||||||
|
8100, // Level 9
|
||||||
|
10000, // Level 10
|
||||||
|
12100, // Level 11
|
||||||
|
14400, // Level 12
|
||||||
|
36800 // Level 13
|
||||||
|
];
|
||||||
|
|
||||||
|
static List<String> levelLabelMapping =
|
||||||
|
List.generate(experienceToLevelRequirements.length, (x) => 'userLevel$x');
|
||||||
|
|
||||||
|
static (int level, String label) getLevelFromExp(int experience) {
|
||||||
|
final exp = experienceToLevelRequirements.reversed
|
||||||
|
.firstWhere((x) => x <= experience);
|
||||||
|
final idx = experienceToLevelRequirements.indexOf(exp);
|
||||||
|
return (idx, levelLabelMapping[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double calcLevelUpProgress(int experience) {
|
||||||
|
final exp = experienceToLevelRequirements.reversed
|
||||||
|
.firstWhere((x) => x <= experience);
|
||||||
|
final idx = experienceToLevelRequirements.indexOf(exp);
|
||||||
|
if (idx + 1 >= experienceToLevelRequirements.length) return 1;
|
||||||
|
final nextExp = experienceToLevelRequirements[idx + 1];
|
||||||
|
return exp / nextExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String calcLevelUpProgressLevel(int experience) {
|
||||||
|
final exp = experienceToLevelRequirements.reversed
|
||||||
|
.firstWhere((x) => x <= experience);
|
||||||
|
final idx = experienceToLevelRequirements.indexOf(exp);
|
||||||
|
if (idx + 1 >= experienceToLevelRequirements.length) return 'Infinity';
|
||||||
|
final nextExp = experienceToLevelRequirements[idx + 1];
|
||||||
|
final formatter =
|
||||||
|
NumberFormat.compactCurrency(symbol: '', decimalDigits: 1);
|
||||||
|
return '${formatter.format(exp)}/${formatter.format(nextExp)}';
|
||||||
|
}
|
||||||
|
}
|
54
lib/providers/last_read.dart
Normal file
54
lib/providers/last_read.dart
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class LastReadProvider extends GetxController {
|
||||||
|
int? _feedLastReadAt;
|
||||||
|
int? _messagesLastReadAt;
|
||||||
|
|
||||||
|
int? get feedLastReadAt => _feedLastReadAt;
|
||||||
|
int? get messagesLastReadAt => _messagesLastReadAt;
|
||||||
|
|
||||||
|
set feedLastReadAt(int? value) {
|
||||||
|
if (value == _feedLastReadAt) return;
|
||||||
|
final newValue = max(_feedLastReadAt ?? 0, value ?? 0);
|
||||||
|
if (newValue != _feedLastReadAt) {
|
||||||
|
_feedLastReadAt = newValue;
|
||||||
|
_saveToStorage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set messagesLastReadAt(int? value) {
|
||||||
|
if (value == _messagesLastReadAt) return;
|
||||||
|
final newValue = max(_messagesLastReadAt ?? 0, value ?? 0);
|
||||||
|
if (newValue != _messagesLastReadAt) {
|
||||||
|
_messagesLastReadAt = newValue;
|
||||||
|
_saveToStorage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LastReadProvider() {
|
||||||
|
_revertFromStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _revertFromStorage() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
if (prefs.containsKey('feed_last_read_at')) {
|
||||||
|
_feedLastReadAt = prefs.getInt('feed_last_read_at')!;
|
||||||
|
}
|
||||||
|
if (prefs.containsKey('messages_last_read_at')) {
|
||||||
|
_messagesLastReadAt = prefs.getInt('messages_last_read_at');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _saveToStorage() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
if (_feedLastReadAt != null) {
|
||||||
|
prefs.setInt('feed_last_read_at', _feedLastReadAt!);
|
||||||
|
}
|
||||||
|
if (_messagesLastReadAt != null) {
|
||||||
|
prefs.setInt('messages_last_read_at', _messagesLastReadAt!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:solian/models/link.dart';
|
import 'package:solian/models/link.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
|
|
||||||
class LinkExpandController extends GetxController {
|
class LinkExpandProvider extends GetxController {
|
||||||
final Map<String, LinkMeta?> _cachedResponse = {};
|
final Map<String, LinkMeta?> _cachedResponse = {};
|
||||||
|
|
||||||
Future<LinkMeta?> expandLink(String url) async {
|
Future<LinkMeta?> expandLink(String url) async {
|
||||||
|
@ -17,6 +17,27 @@ Future<MessageHistoryDb> createHistoryDb() async {
|
|||||||
.addMigrations([migration1to2]).build();
|
.addMigrations([migration1to2]).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<(List<Event>, int)?> getWhatsNewEvents(int pivot, {take = 10}) async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.isFalse) return null;
|
||||||
|
|
||||||
|
final client = auth.configureClient('messaging');
|
||||||
|
|
||||||
|
final resp = await client.get(
|
||||||
|
'/whats-new?pivot=$pivot&take=$take',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw RequestException(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
final PaginationResult response = PaginationResult.fromJson(resp.body);
|
||||||
|
final result =
|
||||||
|
response.data?.map((e) => Event.fromJson(e)).toList() ?? List.empty();
|
||||||
|
|
||||||
|
return (result, response.count);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Event?> getRemoteEvent(int id, Channel channel, String scope) async {
|
Future<Event?> getRemoteEvent(int id, Channel channel, String scope) async {
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse) return null;
|
if (auth.isAuthorized.isFalse) return null;
|
||||||
|
@ -12,6 +12,7 @@ import 'package:solian/screens/channel/channel_chat.dart';
|
|||||||
import 'package:solian/screens/channel/channel_detail.dart';
|
import 'package:solian/screens/channel/channel_detail.dart';
|
||||||
import 'package:solian/screens/channel/channel_organize.dart';
|
import 'package:solian/screens/channel/channel_organize.dart';
|
||||||
import 'package:solian/screens/chat.dart';
|
import 'package:solian/screens/chat.dart';
|
||||||
|
import 'package:solian/screens/dashboard.dart';
|
||||||
import 'package:solian/screens/feed/search.dart';
|
import 'package:solian/screens/feed/search.dart';
|
||||||
import 'package:solian/screens/posts/post_detail.dart';
|
import 'package:solian/screens/posts/post_detail.dart';
|
||||||
import 'package:solian/screens/feed/draft_box.dart';
|
import 'package:solian/screens/feed/draft_box.dart';
|
||||||
@ -19,7 +20,7 @@ import 'package:solian/screens/realms.dart';
|
|||||||
import 'package:solian/screens/realms/realm_detail.dart';
|
import 'package:solian/screens/realms/realm_detail.dart';
|
||||||
import 'package:solian/screens/realms/realm_organize.dart';
|
import 'package:solian/screens/realms/realm_organize.dart';
|
||||||
import 'package:solian/screens/realms/realm_view.dart';
|
import 'package:solian/screens/realms/realm_view.dart';
|
||||||
import 'package:solian/screens/home.dart';
|
import 'package:solian/screens/feed.dart';
|
||||||
import 'package:solian/screens/posts/post_editor.dart';
|
import 'package:solian/screens/posts/post_editor.dart';
|
||||||
import 'package:solian/screens/settings.dart';
|
import 'package:solian/screens/settings.dart';
|
||||||
import 'package:solian/shells/root_shell.dart';
|
import 'package:solian/shells/root_shell.dart';
|
||||||
@ -34,6 +35,14 @@ abstract class AppRouter {
|
|||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
routes: [
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: '/',
|
||||||
|
name: 'dashboard',
|
||||||
|
builder: (context, state) => TitleShell(
|
||||||
|
state: state,
|
||||||
|
child: const DashboardScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
_feedRoute,
|
_feedRoute,
|
||||||
_chatRoute,
|
_chatRoute,
|
||||||
_realmRoute,
|
_realmRoute,
|
||||||
@ -63,9 +72,9 @@ abstract class AppRouter {
|
|||||||
builder: (context, state, child) => child,
|
builder: (context, state, child) => child,
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/',
|
path: '/feed',
|
||||||
name: 'home',
|
name: 'feed',
|
||||||
builder: (context, state) => const HomeScreen(),
|
builder: (context, state) => const FeedScreen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/feed/search',
|
path: '/feed/search',
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ class AboutScreen extends StatelessWidget {
|
|||||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
child: Image.asset('assets/logo.png', width: 120, height: 120),
|
child: Image.asset('assets/logo.png', width: 120, height: 120),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
Text(
|
Text(
|
||||||
'Solian',
|
'Solian',
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
@ -30,12 +31,12 @@ class AboutScreen extends StatelessWidget {
|
|||||||
'The Solar Network',
|
'The Solar Network',
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: PackageInfo.fromPlatform(),
|
future: PackageInfo.fromPlatform(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Text(
|
return Text(
|
||||||
@ -45,7 +46,7 @@ class AboutScreen extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Text('Copyright © ${DateTime.now().year} Solsynth LLC'),
|
Text('Copyright © ${DateTime.now().year} Solsynth LLC'),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: denseButtonStyle,
|
style: denseButtonStyle,
|
||||||
child: const Text('App Details'),
|
child: const Text('App Details'),
|
||||||
@ -59,7 +60,8 @@ class AboutScreen extends StatelessWidget {
|
|||||||
'The Solar Network App is an intuitive and self-hostable social network and computing platform. Experience the freedom of a user-friendly design that empowers you to create and connect with communities on your own terms. Embrace the future of social networking with a platform that prioritizes your independence and privacy.',
|
'The Solar Network App is an intuitive and self-hostable social network and computing platform. Experience the freedom of a user-friendly design that empowers you to create and connect with communities on your own terms. Embrace the future of social networking with a platform that prioritizes your independence and privacy.',
|
||||||
applicationIcon: ClipRRect(
|
applicationIcon: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
child: Image.asset('assets/logo.png', width: 60, height: 60),
|
child:
|
||||||
|
Image.asset('assets/logo.png', width: 60, height: 60),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -71,7 +73,7 @@ class AboutScreen extends StatelessWidget {
|
|||||||
launchUrlString('https://solsynth.dev/products/solar-network');
|
launchUrlString('https://solsynth.dev/products/solar-network');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
const Text(
|
const Text(
|
||||||
'Open-sourced under AGPLv3',
|
'Open-sourced under AGPLv3',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/models/relations.dart';
|
import 'package:solian/models/relations.dart';
|
||||||
@ -56,7 +57,7 @@ class _FriendScreenState extends State<FriendScreen>
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text('accountFriendNewHint'.tr, textAlign: TextAlign.left),
|
Text('accountFriendNewHint'.tr, textAlign: TextAlign.left),
|
||||||
const SizedBox(height: 18),
|
const Gap(18),
|
||||||
TextField(
|
TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -16,7 +16,7 @@ class NotificationScreen extends StatefulWidget {
|
|||||||
class _NotificationScreenState extends State<NotificationScreen> {
|
class _NotificationScreenState extends State<NotificationScreen> {
|
||||||
bool _isBusy = false;
|
bool _isBusy = false;
|
||||||
|
|
||||||
Future<void> markAllRead() async {
|
Future<void> _markAllRead() async {
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse) return;
|
if (auth.isAuthorized.isFalse) return;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
setState(() => _isBusy = false);
|
setState(() => _isBusy = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> markOneRead(notify.Notification element, int index) async {
|
Future<void> _markOneRead(notify.Notification element, int index) async {
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse) return;
|
if (auth.isAuthorized.isFalse) return;
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final WebSocketProvider provider = Get.find();
|
final WebSocketProvider ws = Get.find();
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: MediaQuery.of(context).size.height * 0.85,
|
height: MediaQuery.of(context).size.height * 0.85,
|
||||||
@ -83,7 +83,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: const LinearProgressIndicator().animate().scaleX(),
|
child: const LinearProgressIndicator().animate().scaleX(),
|
||||||
),
|
),
|
||||||
if (provider.notifications.isEmpty)
|
if (ws.notifications.isEmpty)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
@ -96,7 +96,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (provider.notifications.isNotEmpty)
|
if (ws.notifications.isNotEmpty)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
@ -104,14 +104,14 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.checklist),
|
leading: const Icon(Icons.checklist),
|
||||||
title: Text('notifyAllRead'.tr),
|
title: Text('notifyAllRead'.tr),
|
||||||
onTap: _isBusy ? null : () => markAllRead(),
|
onTap: _isBusy ? null : () => _markAllRead(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverList.separated(
|
SliverList.separated(
|
||||||
itemCount: provider.notifications.length,
|
itemCount: ws.notifications.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
var element = provider.notifications[index];
|
var element = ws.notifications[index];
|
||||||
return Dismissible(
|
return Dismissible(
|
||||||
key: Key(const Uuid().v4()),
|
key: Key(const Uuid().v4()),
|
||||||
background: Container(
|
background: Container(
|
||||||
@ -135,7 +135,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onDismissed: (_) => markOneRead(element, index),
|
onDismissed: (_) => _markOneRead(element, index),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (_, __) =>
|
separatorBuilder: (_, __) =>
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:image_cropper/image_cropper.dart';
|
import 'package:image_cropper/image_cropper.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
@ -185,7 +186,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
if (_isBusy) const LinearProgressIndicator().animate().scaleX(),
|
if (_isBusy) const LinearProgressIndicator().animate().scaleX(),
|
||||||
const SizedBox(height: 24),
|
const Gap(24),
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
AccountAvatar(content: _avatar, radius: 40),
|
AccountAvatar(content: _avatar, radius: 40),
|
||||||
@ -202,7 +203,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: padding),
|
).paddingSymmetric(horizontal: padding),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
@ -247,7 +248,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: padding),
|
).paddingSymmetric(horizontal: padding),
|
||||||
const SizedBox(height: 24),
|
const Gap(24),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
@ -262,7 +263,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const Gap(16),
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@ -275,7 +276,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: padding),
|
).paddingSymmetric(horizontal: padding),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
@ -288,7 +289,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const Gap(16),
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@ -301,7 +302,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: padding),
|
).paddingSymmetric(horizontal: padding),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _descriptionController,
|
controller: _descriptionController,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
@ -312,7 +313,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
labelText: 'description'.tr,
|
labelText: 'description'.tr,
|
||||||
),
|
),
|
||||||
).paddingSymmetric(horizontal: padding),
|
).paddingSymmetric(horizontal: padding),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _birthdayController,
|
controller: _birthdayController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@ -322,7 +323,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
),
|
),
|
||||||
onTap: () => _selectBirthday(),
|
onTap: () => _selectBirthday(),
|
||||||
).paddingSymmetric(horizontal: padding),
|
).paddingSymmetric(horizontal: padding),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:solian/controllers/post_list_controller.dart';
|
import 'package:solian/controllers/post_list_controller.dart';
|
||||||
@ -7,10 +8,12 @@ import 'package:solian/models/account.dart';
|
|||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
|
import 'package:solian/providers/account_status.dart';
|
||||||
import 'package:solian/providers/relation.dart';
|
import 'package:solian/providers/relation.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
import 'package:solian/theme.dart';
|
import 'package:solian/theme.dart';
|
||||||
import 'package:solian/widgets/account/account_avatar.dart';
|
import 'package:solian/widgets/account/account_avatar.dart';
|
||||||
|
import 'package:solian/widgets/account/account_heading.dart';
|
||||||
import 'package:solian/widgets/app_bar_leading.dart';
|
import 'package:solian/widgets/app_bar_leading.dart';
|
||||||
import 'package:solian/widgets/attachments/attachment_list.dart';
|
import 'package:solian/widgets/attachments/attachment_list.dart';
|
||||||
import 'package:solian/widgets/posts/post_list.dart';
|
import 'package:solian/widgets/posts/post_list.dart';
|
||||||
@ -83,10 +86,7 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int get _userSocialCreditPoints {
|
int get _userSocialCreditPoints {
|
||||||
int birthPart =
|
return _totalUpvote * 2 - _totalDownvote + _postController.postTotal.value;
|
||||||
DateTime.now().difference(_userinfo!.createdAt.toLocal()).inSeconds;
|
|
||||||
birthPart = birthPart >> 16;
|
|
||||||
return _totalUpvote * 2 - _totalDownvote + birthPart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -96,8 +96,9 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
_postController = PostListController(author: widget.name);
|
_postController = PostListController(author: widget.name);
|
||||||
_albumPagingController.addPageRequestListener((pageKey) async {
|
_albumPagingController.addPageRequestListener((pageKey) async {
|
||||||
final client = ServiceFinder.configureClient('files');
|
final client = ServiceFinder.configureClient('files');
|
||||||
final resp = await client
|
final resp = await client.get(
|
||||||
.get('/attachments?take=10&offset=$pageKey&author=${widget.name}');
|
'/attachments?take=10&offset=$pageKey&author=${widget.name}&original=true',
|
||||||
|
);
|
||||||
if (resp.statusCode == 200) {
|
if (resp.statusCode == 200) {
|
||||||
final result = PaginationResult.fromJson(resp.body);
|
final result = PaginationResult.fromJson(resp.body);
|
||||||
final out = result.data
|
final out = result.data
|
||||||
@ -144,7 +145,7 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
return Material(
|
return Material(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: DefaultTabController(
|
child: DefaultTabController(
|
||||||
length: 2,
|
length: 3,
|
||||||
child: NestedScrollView(
|
child: NestedScrollView(
|
||||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||||
return [
|
return [
|
||||||
@ -156,12 +157,11 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
flexibleSpace: Row(
|
flexibleSpace: Row(
|
||||||
children: [
|
children: [
|
||||||
AppBarLeadingButton.adaptive(context) ??
|
AppBarLeadingButton.adaptive(context) ?? const Gap(8),
|
||||||
const SizedBox(width: 8),
|
const Gap(8),
|
||||||
const SizedBox(width: 8),
|
|
||||||
if (_userinfo != null)
|
if (_userinfo != null)
|
||||||
AccountAvatar(content: _userinfo!.avatar, radius: 16),
|
AccountAvatar(content: _userinfo!.avatar, radius: 16),
|
||||||
const SizedBox(width: 12),
|
const Gap(12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -213,6 +213,7 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
),
|
),
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
tabs: [
|
tabs: [
|
||||||
|
Tab(text: 'profilePage'.tr),
|
||||||
Tab(text: 'profilePosts'.tr),
|
Tab(text: 'profilePosts'.tr),
|
||||||
Tab(text: 'profileAlbum'.tr),
|
Tab(text: 'profileAlbum'.tr),
|
||||||
],
|
],
|
||||||
@ -223,6 +224,24 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
body: TabBarView(
|
body: TabBarView(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
children: [
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const Gap(16),
|
||||||
|
AccountHeadingWidget(
|
||||||
|
name: _userinfo!.name,
|
||||||
|
nick: _userinfo!.nick,
|
||||||
|
desc: _userinfo!.description,
|
||||||
|
badges: _userinfo!.badges,
|
||||||
|
banner: _userinfo!.banner,
|
||||||
|
avatar: _userinfo!.avatar,
|
||||||
|
status: Get.find<StatusProvider>()
|
||||||
|
.getSomeoneStatus(_userinfo!.name),
|
||||||
|
detail: _userinfo,
|
||||||
|
profile: _userinfo!.profile,
|
||||||
|
extraWidgets: const [],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
RefreshIndicator(
|
RefreshIndicator(
|
||||||
onRefresh: () => Future.wait([
|
onRefresh: () => Future.wait([
|
||||||
_postController.reloadAllOver(),
|
_postController.reloadAllOver(),
|
||||||
@ -243,7 +262,7 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
@ -163,7 +164,7 @@ class _StickerScreenState extends State<StickerScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(item.name),
|
Text(item.name),
|
||||||
const SizedBox(width: 6),
|
const Gap(6),
|
||||||
Badge(
|
Badge(
|
||||||
label: Text('#${item.id}'),
|
label: Text('#${item.id}'),
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:protocol_handler/protocol_handler.dart';
|
import 'package:protocol_handler/protocol_handler.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
@ -160,7 +161,7 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
|
|||||||
onTapOutside: (_) =>
|
onTapOutside: (_) =>
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const Gap(12),
|
||||||
TextField(
|
TextField(
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
@ -176,7 +177,7 @@ class _SignInPopupState extends State<SignInPopup> with ProtocolListener {
|
|||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
onSubmitted: (_) => performAction(),
|
onSubmitted: (_) => performAction(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const Gap(12),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
@ -94,7 +95,7 @@ class _SignUpPopupState extends State<SignUpPopup> {
|
|||||||
onTapOutside: (_) =>
|
onTapOutside: (_) =>
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const Gap(12),
|
||||||
TextField(
|
TextField(
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
enableSuggestions: false,
|
enableSuggestions: false,
|
||||||
@ -108,7 +109,7 @@ class _SignUpPopupState extends State<SignUpPopup> {
|
|||||||
onTapOutside: (_) =>
|
onTapOutside: (_) =>
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const Gap(12),
|
||||||
TextField(
|
TextField(
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
enableSuggestions: false,
|
enableSuggestions: false,
|
||||||
@ -122,7 +123,7 @@ class _SignUpPopupState extends State<SignUpPopup> {
|
|||||||
onTapOutside: (_) =>
|
onTapOutside: (_) =>
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const Gap(12),
|
||||||
TextField(
|
TextField(
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
@ -138,7 +139,7 @@ class _SignUpPopupState extends State<SignUpPopup> {
|
|||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
onSubmitted: (_) => performAction(context),
|
onSubmitted: (_) => performAction(context),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:math' as math;
|
|||||||
|
|
||||||
import 'package:async/async.dart';
|
import 'package:async/async.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/providers/call.dart';
|
import 'package:solian/providers/call.dart';
|
||||||
import 'package:solian/theme.dart';
|
import 'package:solian/theme.dart';
|
||||||
@ -83,7 +84,7 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
|
|||||||
participant: call.focusTrack.value!,
|
participant: call.focusTrack.value!,
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
left: 0,
|
||||||
@ -257,7 +258,7 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const Gap(6),
|
||||||
Text(call.lastDuration.value)
|
Text(call.lastDuration.value)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -276,7 +277,7 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
|
|||||||
'callStatusReconnecting'.tr,
|
'callStatusReconnecting'.tr,
|
||||||
}[call.room.connectionState]!,
|
}[call.room.connectionState]!,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const Gap(6),
|
||||||
if (connectionQuality !=
|
if (connectionQuality !=
|
||||||
livekit.ConnectionQuality.unknown)
|
livekit.ConnectionQuality.unknown)
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -98,6 +98,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen>
|
|||||||
setState(() => _ongoingCall = Call.fromJson(resp.body));
|
setState(() => _ongoingCall = Call.fromJson(resp.body));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
print((e as dynamic).stackTrace);
|
||||||
context.showErrorDialog(e);
|
context.showErrorDialog(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +223,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen>
|
|||||||
actions: [
|
actions: [
|
||||||
const BackgroundStateWidget(),
|
const BackgroundStateWidget(),
|
||||||
Builder(builder: (context) {
|
Builder(builder: (context) {
|
||||||
if (_isBusy || _channel == null) return const SizedBox();
|
if (_isBusy || _channel == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
return ChatCallButton(
|
return ChatCallButton(
|
||||||
realm: _channel!.realm,
|
realm: _channel!.realm,
|
||||||
@ -299,7 +300,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen>
|
|||||||
if (_chatController.isLoading.isTrue) {
|
if (_chatController.isLoading.isTrue) {
|
||||||
return const LinearProgressIndicator().animate().slideY();
|
return const LinearProgressIndicator().animate().slideY();
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
ClipRect(
|
ClipRect(
|
||||||
@ -350,7 +351,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen>
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/models/channel.dart';
|
import 'package:solian/models/channel.dart';
|
||||||
@ -148,7 +149,7 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
|
|||||||
size: 18,
|
size: 18,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const Gap(16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -112,7 +112,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (_channels.isLoading.isFalse) {
|
if (_channels.isLoading.isFalse) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
} else {
|
} else {
|
||||||
return const LinearProgressIndicator();
|
return const LinearProgressIndicator();
|
||||||
}
|
}
|
||||||
|
515
lib/screens/dashboard.dart
Normal file
515
lib/screens/dashboard.dart
Normal file
@ -0,0 +1,515 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
import 'dart:math' hide log;
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/material.dart' hide Notification;
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:solian/exts.dart';
|
||||||
|
import 'package:solian/models/channel.dart';
|
||||||
|
import 'package:solian/models/daily_sign.dart';
|
||||||
|
import 'package:solian/models/event.dart';
|
||||||
|
import 'package:solian/models/notification.dart';
|
||||||
|
import 'package:solian/models/pagination.dart';
|
||||||
|
import 'package:solian/models/post.dart';
|
||||||
|
import 'package:solian/providers/auth.dart';
|
||||||
|
import 'package:solian/providers/content/posts.dart';
|
||||||
|
import 'package:solian/providers/daily_sign.dart';
|
||||||
|
import 'package:solian/providers/last_read.dart';
|
||||||
|
import 'package:solian/providers/message/adaptor.dart';
|
||||||
|
import 'package:solian/providers/websocket.dart';
|
||||||
|
import 'package:solian/router.dart';
|
||||||
|
import 'package:solian/screens/account/notification.dart';
|
||||||
|
import 'package:solian/theme.dart';
|
||||||
|
import 'package:solian/widgets/chat/chat_event.dart';
|
||||||
|
import 'package:solian/widgets/daily_sign/history_chart.dart';
|
||||||
|
import 'package:solian/widgets/posts/post_list.dart';
|
||||||
|
|
||||||
|
class DashboardScreen extends StatefulWidget {
|
||||||
|
const DashboardScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DashboardScreen> createState() => _DashboardScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DashboardScreenState extends State<DashboardScreen> {
|
||||||
|
late final AuthProvider _auth = Get.find();
|
||||||
|
late final LastReadProvider _lastRead = Get.find();
|
||||||
|
late final WebSocketProvider _ws = Get.find();
|
||||||
|
late final PostProvider _posts = Get.find();
|
||||||
|
late final DailySignProvider _dailySign = Get.find();
|
||||||
|
|
||||||
|
Color get _unFocusColor =>
|
||||||
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
|
||||||
|
|
||||||
|
List<Notification> get _pendingNotifications =>
|
||||||
|
List<Notification>.from(_ws.notifications)
|
||||||
|
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
|
||||||
|
|
||||||
|
List<Post>? _currentPosts;
|
||||||
|
int? _currentPostsCount;
|
||||||
|
|
||||||
|
Future<void> _pullPosts() async {
|
||||||
|
if (_lastRead.feedLastReadAt == null) return;
|
||||||
|
log('[Dashboard] Pulling posts with pivot: ${_lastRead.feedLastReadAt}');
|
||||||
|
final resp = await _posts.seeWhatsNew(_lastRead.feedLastReadAt!);
|
||||||
|
final result = PaginationResult.fromJson(resp.body);
|
||||||
|
setState(() {
|
||||||
|
_currentPostsCount = result.count;
|
||||||
|
_currentPosts = result.data?.map((e) => Post.fromJson(e)).toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Event>? _currentMessages;
|
||||||
|
int? _currentMessagesCount;
|
||||||
|
|
||||||
|
Map<Channel, List<Event>>? get _currentGroupedMessages =>
|
||||||
|
_currentMessages?.groupListsBy((x) => x.channel!);
|
||||||
|
|
||||||
|
Future<void> _pullMessages() async {
|
||||||
|
if (_lastRead.messagesLastReadAt == null) return;
|
||||||
|
log('[Dashboard] Pulling messages with pivot: ${_lastRead.messagesLastReadAt}');
|
||||||
|
final out = await getWhatsNewEvents(_lastRead.messagesLastReadAt!);
|
||||||
|
if (out == null) return;
|
||||||
|
setState(() {
|
||||||
|
_currentMessages = out.$1;
|
||||||
|
_currentMessagesCount = out.$2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _signingDaily = true;
|
||||||
|
DailySignRecord? _signRecord;
|
||||||
|
List<DailySignRecord>? _signRecordHistory;
|
||||||
|
|
||||||
|
Future<void> _pullDaily() async {
|
||||||
|
try {
|
||||||
|
_signRecord = await _dailySign.getToday();
|
||||||
|
_dailySign.listLastRecord(30).then((value) {
|
||||||
|
setState(() => _signRecordHistory = value);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
context.showErrorDialog(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() => _signingDaily = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _signDaily() async {
|
||||||
|
setState(() => _signingDaily = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
_signRecord = await _dailySign.signToday();
|
||||||
|
} catch (e) {
|
||||||
|
context.showErrorDialog(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() => _signingDaily = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _pullData() async {
|
||||||
|
if (!_auth.isAuthorized.value) return;
|
||||||
|
await Future.wait([
|
||||||
|
_pullPosts(),
|
||||||
|
_pullMessages(),
|
||||||
|
_pullDaily(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_pullData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final width = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: _pullData,
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
DateTime.now().day == DateTime.now().toUtc().day
|
||||||
|
? 'today'.tr
|
||||||
|
: 'yesterday'.tr,
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
Text(DateFormat('yyyy/MM/dd').format(DateTime.now().toUtc())),
|
||||||
|
],
|
||||||
|
).paddingOnly(top: 8, left: 18, right: 18, bottom: 12),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: AnimatedSwitcher(
|
||||||
|
switchInCurve: Curves.fastOutSlowIn,
|
||||||
|
switchOutCurve: Curves.fastOutSlowIn,
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder: (child, animation) {
|
||||||
|
return ScaleTransition(
|
||||||
|
scale: animation,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: _signRecord == null
|
||||||
|
? Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
DateFormat('dd').format(DateTime.now()),
|
||||||
|
style: GoogleFonts.robotoMono(
|
||||||
|
fontSize: 22, height: 1.2),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
DateFormat('yy/MM').format(DateTime.now()),
|
||||||
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
_signRecord!.symbol,
|
||||||
|
style: GoogleFonts.notoSerifHk(
|
||||||
|
fontSize: 20, height: 1),
|
||||||
|
).paddingSymmetric(horizontal: 9),
|
||||||
|
).paddingOnly(left: 4),
|
||||||
|
title: _signRecord == null
|
||||||
|
? Text('dailySign'.tr)
|
||||||
|
: Text(_signRecord!.overviewSuggestion),
|
||||||
|
subtitle: _signRecord == null
|
||||||
|
? Text('dailySignNone'.tr)
|
||||||
|
: Text('+${_signRecord!.resultExperience} EXP'),
|
||||||
|
trailing: AnimatedSwitcher(
|
||||||
|
switchInCurve: Curves.fastOutSlowIn,
|
||||||
|
switchOutCurve: Curves.fastOutSlowIn,
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder: (child, animation) {
|
||||||
|
return ScaleTransition(
|
||||||
|
scale: animation,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: _signRecord == null
|
||||||
|
? IconButton(
|
||||||
|
tooltip: '上香求签',
|
||||||
|
icon: const Icon(Icons.local_fire_department),
|
||||||
|
onPressed: _signingDaily ? null : _signDaily,
|
||||||
|
)
|
||||||
|
: IconButton(
|
||||||
|
tooltip: '查看运势历史',
|
||||||
|
icon: const Icon(Icons.history),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
builder: (context) =>
|
||||||
|
DailySignHistoryChartDialog(
|
||||||
|
data: _signRecordHistory,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).paddingSymmetric(horizontal: 8),
|
||||||
|
const Divider(thickness: 0.3).paddingSymmetric(vertical: 8),
|
||||||
|
|
||||||
|
/// Unread notifications
|
||||||
|
Obx(
|
||||||
|
() => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'notification'.tr,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!
|
||||||
|
.copyWith(fontSize: 18),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'notificationUnreadCount'.trParams({
|
||||||
|
'count': _ws.notifications.length.toString(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.more_horiz),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const NotificationScreen(),
|
||||||
|
).then((_) => _ws.notificationUnread.value = 0);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingOnly(left: 18, right: 18, bottom: 8),
|
||||||
|
if (_ws.notifications.isNotEmpty)
|
||||||
|
SizedBox(
|
||||||
|
height: 76,
|
||||||
|
child: ListView.separated(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
itemCount: min(_pendingNotifications.length, 10),
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final x = _pendingNotifications[idx];
|
||||||
|
return SizedBox(
|
||||||
|
width: min(360, width),
|
||||||
|
child: Card(
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
x.title,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (x.subtitle != null)
|
||||||
|
Text(
|
||||||
|
x.subtitle!,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Text(
|
||||||
|
x.body,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, __) => const Gap(4),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
trailing: const Icon(Icons.inbox_outlined),
|
||||||
|
title: Text('notifyEmpty'.tr),
|
||||||
|
subtitle: Text('notifyEmptyCaption'.tr),
|
||||||
|
),
|
||||||
|
).paddingSymmetric(horizontal: 8),
|
||||||
|
],
|
||||||
|
).paddingOnly(bottom: 12),
|
||||||
|
),
|
||||||
|
|
||||||
|
/// Unread friends / followed people posts
|
||||||
|
if (_currentPosts?.isNotEmpty ?? false)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'feed'.tr,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!
|
||||||
|
.copyWith(fontSize: 18),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'feedUnreadCount'.trParams({
|
||||||
|
'count': (_currentPostsCount ?? 0).toString(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_forward),
|
||||||
|
onPressed: () {
|
||||||
|
AppRouter.instance.goNamed('feed');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingOnly(left: 18, right: 18, bottom: 8),
|
||||||
|
SizedBox(
|
||||||
|
height: 360,
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: _currentPosts!.length,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final item = _currentPosts![idx];
|
||||||
|
return SizedBox(
|
||||||
|
width: min(480, width),
|
||||||
|
child: Card(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: PostListEntryWidget(
|
||||||
|
item: item,
|
||||||
|
isClickable: true,
|
||||||
|
isShowEmbed: true,
|
||||||
|
isNestedClickable: true,
|
||||||
|
onUpdate: (_) {
|
||||||
|
_pullPosts();
|
||||||
|
},
|
||||||
|
backgroundColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerLow,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).paddingSymmetric(horizontal: 8),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
).paddingOnly(bottom: 12),
|
||||||
|
|
||||||
|
/// Unread messages part
|
||||||
|
if (_currentMessages?.isNotEmpty ?? false)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'messages'.tr,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!
|
||||||
|
.copyWith(fontSize: 18),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'messagesUnreadCount'.trParams({
|
||||||
|
'count': (_currentMessagesCount ?? 0).toString(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_forward),
|
||||||
|
onPressed: () {
|
||||||
|
AppRouter.instance.goNamed('chat');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingOnly(left: 18, right: 18, bottom: 8),
|
||||||
|
SizedBox(
|
||||||
|
height: 360,
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: _currentGroupedMessages!.length,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final channel =
|
||||||
|
_currentGroupedMessages!.keys.elementAt(idx);
|
||||||
|
final itemList =
|
||||||
|
_currentGroupedMessages!.values.elementAt(idx);
|
||||||
|
return SizedBox(
|
||||||
|
width: min(520, width),
|
||||||
|
child: Card(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
tileColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerHigh,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(8),
|
||||||
|
topRight: Radius.circular(8),
|
||||||
|
)),
|
||||||
|
leading: CircleAvatar(
|
||||||
|
backgroundColor: channel.realmId == null
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.transparent,
|
||||||
|
radius: 20,
|
||||||
|
child: FaIcon(
|
||||||
|
FontAwesomeIcons.hashtag,
|
||||||
|
color: channel.realmId == null
|
||||||
|
? Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onPrimary
|
||||||
|
: Theme.of(context).colorScheme.primary,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
title: Text(channel.name),
|
||||||
|
subtitle: Text(channel.description),
|
||||||
|
onTap: () {
|
||||||
|
AppRouter.instance.pushNamed(
|
||||||
|
'channelChat',
|
||||||
|
pathParameters: {'alias': channel.alias},
|
||||||
|
queryParameters: {
|
||||||
|
if (channel.realmId != null)
|
||||||
|
'realm': channel.realm!.alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: itemList.length,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final item = itemList[idx];
|
||||||
|
return ChatEvent(item: item).paddingOnly(
|
||||||
|
bottom: 8, top: 16, left: 8, right: 8);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).paddingSymmetric(horizontal: 8),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
).paddingOnly(bottom: 12),
|
||||||
|
|
||||||
|
/// Footer
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: SolianTheme.isLargeScreen(context)
|
||||||
|
? MainAxisAlignment.start
|
||||||
|
: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Powered by Solar Network',
|
||||||
|
style: TextStyle(color: _unFocusColor, fontSize: 12),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'dashboardFooter'.tr,
|
||||||
|
style: TextStyle(color: _unFocusColor, fontSize: 12),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
).paddingAll(8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/controllers/post_list_controller.dart';
|
import 'package:solian/controllers/post_list_controller.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
@ -12,14 +13,14 @@ import 'package:solian/widgets/app_bar_leading.dart';
|
|||||||
import 'package:solian/widgets/posts/post_shuffle_swiper.dart';
|
import 'package:solian/widgets/posts/post_shuffle_swiper.dart';
|
||||||
import 'package:solian/widgets/posts/post_warped_list.dart';
|
import 'package:solian/widgets/posts/post_warped_list.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatefulWidget {
|
class FeedScreen extends StatefulWidget {
|
||||||
const HomeScreen({super.key});
|
const FeedScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<HomeScreen> createState() => _HomeScreenState();
|
State<FeedScreen> createState() => _FeedScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HomeScreenState extends State<HomeScreen>
|
class _FeedScreenState extends State<FeedScreen>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late final PostListController _postController;
|
late final PostListController _postController;
|
||||||
late final TabController _tabController;
|
late final TabController _tabController;
|
||||||
@ -65,7 +66,7 @@ class _HomeScreenState extends State<HomeScreen>
|
|||||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||||
return [
|
return [
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
title: AppBarTitle('home'.tr),
|
title: AppBarTitle('feed'.tr),
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
floating: true,
|
floating: true,
|
||||||
toolbarHeight: SolianTheme.toolbarHeight(context),
|
toolbarHeight: SolianTheme.toolbarHeight(context),
|
||||||
@ -154,7 +155,7 @@ class PostCreatePopup extends StatelessWidget {
|
|||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
|
|
||||||
if (auth.isAuthorized.isFalse) {
|
if (auth.isAuthorized.isFalse) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<dynamic> actionList = [
|
final List<dynamic> actionList = [
|
||||||
@ -224,7 +225,7 @@ class PostCreatePopup extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
x.icon,
|
x.icon,
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
x.label,
|
x.label,
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:markdown_toolbar/markdown_toolbar.dart';
|
import 'package:markdown_toolbar/markdown_toolbar.dart';
|
||||||
@ -183,7 +184,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const Gap(6),
|
||||||
if (_editorController.aliasController.text.isNotEmpty)
|
if (_editorController.aliasController.text.isNotEmpty)
|
||||||
Badge(
|
Badge(
|
||||||
label: Text('#${_editorController.aliasController.text}'),
|
label: Text('#${_editorController.aliasController.text}'),
|
||||||
@ -296,7 +297,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
|
|||||||
?.unfocus(),
|
?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 120)
|
const Gap(120)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/models/realm.dart';
|
import 'package:solian/models/realm.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
@ -95,7 +96,7 @@ class _RealmDetailScreenState extends State<RealmDetailScreen> {
|
|||||||
backgroundColor: Colors.teal,
|
backgroundColor: Colors.teal,
|
||||||
child: Icon(Icons.group, color: Colors.white),
|
child: Icon(Icons.group, color: Colors.white),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const Gap(16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:solian/theme.dart';
|
import 'package:solian/theme.dart';
|
||||||
import 'package:solian/widgets/app_bar_title.dart';
|
import 'package:solian/widgets/app_bar_title.dart';
|
||||||
import 'package:solian/widgets/app_bar_leading.dart';
|
import 'package:solian/widgets/app_bar_leading.dart';
|
||||||
|
import 'package:solian/widgets/current_state_action.dart';
|
||||||
|
|
||||||
class TitleShell extends StatelessWidget {
|
class TitleShell extends StatelessWidget {
|
||||||
final bool showAppBar;
|
final bool showAppBar;
|
||||||
@ -32,6 +33,12 @@ class TitleShell extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
centerTitle: isCenteredTitle,
|
centerTitle: isCenteredTitle,
|
||||||
toolbarHeight: SolianTheme.toolbarHeight(context),
|
toolbarHeight: SolianTheme.toolbarHeight(context),
|
||||||
|
actions: [
|
||||||
|
const BackgroundStateWidget(),
|
||||||
|
SizedBox(
|
||||||
|
width: SolianTheme.isLargeScreen(context) ? 8 : 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
body: child,
|
body: child,
|
||||||
|
@ -8,6 +8,9 @@ const i18nEnglish = {
|
|||||||
'home': 'Home',
|
'home': 'Home',
|
||||||
'guest': 'Guest',
|
'guest': 'Guest',
|
||||||
'draft': 'Draft',
|
'draft': 'Draft',
|
||||||
|
'dashboard': 'Dashboard',
|
||||||
|
'today': 'Today',
|
||||||
|
'yesterday': 'Yesterday',
|
||||||
'draftSave': 'Save',
|
'draftSave': 'Save',
|
||||||
'draftBox': 'Draft Box',
|
'draftBox': 'Draft Box',
|
||||||
'more': 'More',
|
'more': 'More',
|
||||||
@ -19,7 +22,19 @@ const i18nEnglish = {
|
|||||||
'feedSearch': 'Search Feed',
|
'feedSearch': 'Search Feed',
|
||||||
'feedSearchWithTag': 'Searching with tag #@key',
|
'feedSearchWithTag': 'Searching with tag #@key',
|
||||||
'feedSearchWithCategory': 'Searching in category @category',
|
'feedSearchWithCategory': 'Searching in category @category',
|
||||||
|
'feedUnreadCount': '@count posts you may missed',
|
||||||
|
'messages': 'Messages',
|
||||||
|
'messagesUnreadCount': '@count messages unread',
|
||||||
|
'dailySign': 'Daily Sign',
|
||||||
|
'dailySignNone': 'You haven\'t sign today',
|
||||||
|
'dailySignTier0': 'Everything may not be good',
|
||||||
|
'dailySignTier1': 'Something may be wrong',
|
||||||
|
'dailySignTier2': 'Just so so',
|
||||||
|
'dailySignTier3': 'Something may goes well',
|
||||||
|
'dailySignTier4': 'Everything will be awesome',
|
||||||
|
'dashboardFooter': 'Don\'t be serious, just for fun.',
|
||||||
'visitProfilePage': 'Visit Profile Page',
|
'visitProfilePage': 'Visit Profile Page',
|
||||||
|
'profilePage': 'Page',
|
||||||
'profilePosts': 'Posts',
|
'profilePosts': 'Posts',
|
||||||
'profileAlbum': 'Album',
|
'profileAlbum': 'Album',
|
||||||
'chat': 'Chat',
|
'chat': 'Chat',
|
||||||
@ -40,6 +55,7 @@ const i18nEnglish = {
|
|||||||
'openInAlbum': 'Open in album',
|
'openInAlbum': 'Open in album',
|
||||||
'openInBrowser': 'Open in browser',
|
'openInBrowser': 'Open in browser',
|
||||||
'notification': 'Notification',
|
'notification': 'Notification',
|
||||||
|
'notificationUnreadCount': '@count unread notifications',
|
||||||
'errorHappened': 'An error occurred',
|
'errorHappened': 'An error occurred',
|
||||||
'errorHappenedUnauthorized':
|
'errorHappenedUnauthorized':
|
||||||
'Unauthorized request, please sign in or try resign in.',
|
'Unauthorized request, please sign in or try resign in.',
|
||||||
@ -386,4 +402,18 @@ const i18nEnglish = {
|
|||||||
'collapse': 'Collapse',
|
'collapse': 'Collapse',
|
||||||
'expand': 'Expand',
|
'expand': 'Expand',
|
||||||
'typingMessage': '@user are typing...',
|
'typingMessage': '@user are typing...',
|
||||||
|
'userLevel0': 'Newbie',
|
||||||
|
'userLevel1': 'Novice',
|
||||||
|
'userLevel2': 'Apprentice',
|
||||||
|
'userLevel3': 'Explorer',
|
||||||
|
'userLevel4': 'Adventurer',
|
||||||
|
'userLevel5': 'Warrior',
|
||||||
|
'userLevel6': 'Knight',
|
||||||
|
'userLevel7': 'Champion',
|
||||||
|
'userLevel8': 'Hero',
|
||||||
|
'userLevel9': 'Master',
|
||||||
|
'userLevel10': 'Grandmaster',
|
||||||
|
'userLevel11': 'Legend',
|
||||||
|
'userLevel12': 'Mythic',
|
||||||
|
'userLevel13': 'Immortal',
|
||||||
};
|
};
|
||||||
|
@ -24,10 +24,25 @@ const i18nSimplifiedChinese = {
|
|||||||
'alias': '别名',
|
'alias': '别名',
|
||||||
'feed': '资讯',
|
'feed': '资讯',
|
||||||
'unlink': '移除链接',
|
'unlink': '移除链接',
|
||||||
|
'dashboard': '仪表盘',
|
||||||
|
'today': '今日',
|
||||||
|
'yesterday': '昨日',
|
||||||
'feedSearch': '搜索资讯',
|
'feedSearch': '搜索资讯',
|
||||||
'feedSearchWithTag': '检索带有 #@key 标签的资讯',
|
'feedSearchWithTag': '检索带有 #@key 标签的资讯',
|
||||||
'feedSearchWithCategory': '检索位于分类 @category 的资讯',
|
'feedSearchWithCategory': '检索位于分类 @category 的资讯',
|
||||||
|
'feedUnreadCount': '@count 条你可能错过的帖子',
|
||||||
|
'messages': '消息',
|
||||||
|
'messagesUnreadCount': '@count 条未读的消息',
|
||||||
|
'dailySign': '签到',
|
||||||
|
'dailySignNone': '今日未拜访佛祖',
|
||||||
|
'dailySignTier0': '诸事不宜',
|
||||||
|
'dailySignTier1': '有些不宜',
|
||||||
|
'dailySignTier2': '平平淡淡',
|
||||||
|
'dailySignTier3': '有些事宜',
|
||||||
|
'dailySignTier4': '诸事皆宜',
|
||||||
|
'dashboardFooter': '占卜多少沾点玩,人生还得靠实力',
|
||||||
'visitProfilePage': '造访个人主页',
|
'visitProfilePage': '造访个人主页',
|
||||||
|
'profilePage': '主页',
|
||||||
'profilePosts': '帖子',
|
'profilePosts': '帖子',
|
||||||
'profileAlbum': '相簿',
|
'profileAlbum': '相簿',
|
||||||
'chat': '聊天',
|
'chat': '聊天',
|
||||||
@ -40,6 +55,7 @@ const i18nSimplifiedChinese = {
|
|||||||
'openInAlbum': '在相簿中打开',
|
'openInAlbum': '在相簿中打开',
|
||||||
'openInBrowser': '在浏览器中打开',
|
'openInBrowser': '在浏览器中打开',
|
||||||
'notification': '通知',
|
'notification': '通知',
|
||||||
|
'notificationUnreadCount': '@count 条未读通知',
|
||||||
'errorHappened': '发生错误了',
|
'errorHappened': '发生错误了',
|
||||||
'errorHappenedUnauthorized': '未经授权的请求,请登录或尝试重新登录。',
|
'errorHappenedUnauthorized': '未经授权的请求,请登录或尝试重新登录。',
|
||||||
'errorHappenedRequestBad': '请求错误,服务器拒绝处理该请求,请检查您的请求数据。',
|
'errorHappenedRequestBad': '请求错误,服务器拒绝处理该请求,请检查您的请求数据。',
|
||||||
@ -356,4 +372,18 @@ const i18nSimplifiedChinese = {
|
|||||||
'collapse': '折叠',
|
'collapse': '折叠',
|
||||||
'expand': '展开',
|
'expand': '展开',
|
||||||
'typingMessage': '@user 正在输入中…',
|
'typingMessage': '@user 正在输入中…',
|
||||||
|
'userLevel0': '不慕名利',
|
||||||
|
'userLevel1': '初出茅庐',
|
||||||
|
'userLevel2': '小试牛刀',
|
||||||
|
'userLevel3': '磨杵成针',
|
||||||
|
'userLevel4': '披荆斩棘',
|
||||||
|
'userLevel5': '力挽狂澜',
|
||||||
|
'userLevel6': '一骑当千',
|
||||||
|
'userLevel7': '所向披靡',
|
||||||
|
'userLevel8': '气吞山河',
|
||||||
|
'userLevel9': '登峰造极',
|
||||||
|
'userLevel10': '出神入化',
|
||||||
|
'userLevel11': '名垂千古',
|
||||||
|
'userLevel12': '独占鳌头',
|
||||||
|
'userLevel13': '万古流芳',
|
||||||
};
|
};
|
||||||
|
@ -45,7 +45,7 @@ class _AccountBadgeWidgetState extends State<AccountBadgeWidget> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final spec = badges[widget.item.type];
|
final spec = badges[widget.item.type];
|
||||||
|
|
||||||
if (spec == null) return const SizedBox();
|
if (spec == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
richMessage: TextSpan(
|
richMessage: TextSpan(
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
import 'package:solian/models/account_status.dart';
|
import 'package:solian/models/account_status.dart';
|
||||||
import 'package:solian/platform.dart';
|
import 'package:solian/platform.dart';
|
||||||
import 'package:solian/providers/account_status.dart';
|
import 'package:solian/providers/account_status.dart';
|
||||||
|
import 'package:solian/providers/experience.dart';
|
||||||
import 'package:solian/widgets/account/account_avatar.dart';
|
import 'package:solian/widgets/account/account_avatar.dart';
|
||||||
import 'package:solian/widgets/account/account_badge.dart';
|
import 'package:solian/widgets/account/account_badge.dart';
|
||||||
import 'package:solian/widgets/account/account_status_action.dart';
|
import 'package:solian/widgets/account/account_status_action.dart';
|
||||||
@ -17,6 +20,7 @@ class AccountHeadingWidget extends StatelessWidget {
|
|||||||
final String nick;
|
final String nick;
|
||||||
final String? desc;
|
final String? desc;
|
||||||
final Account? detail;
|
final Account? detail;
|
||||||
|
final AccountProfile? profile;
|
||||||
final List<AccountBadge>? badges;
|
final List<AccountBadge>? badges;
|
||||||
final List<Widget>? extraWidgets;
|
final List<Widget>? extraWidgets;
|
||||||
|
|
||||||
@ -32,6 +36,7 @@ class AccountHeadingWidget extends StatelessWidget {
|
|||||||
required this.desc,
|
required this.desc,
|
||||||
required this.badges,
|
required this.badges,
|
||||||
this.detail,
|
this.detail,
|
||||||
|
this.profile,
|
||||||
this.status,
|
this.status,
|
||||||
this.extraWidgets,
|
this.extraWidgets,
|
||||||
this.onEditStatus,
|
this.onEditStatus,
|
||||||
@ -70,7 +75,7 @@ class AccountHeadingWidget extends StatelessWidget {
|
|||||||
content: banner,
|
content: banner,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).paddingSymmetric(horizontal: 16),
|
).paddingSymmetric(horizontal: 16),
|
||||||
@ -129,7 +134,10 @@ class AccountHeadingWidget extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(info.$3),
|
Text(
|
||||||
|
info.$3,
|
||||||
|
style: const TextStyle(height: 1),
|
||||||
|
).paddingOnly(bottom: 3),
|
||||||
if (!status.isOnline && status.lastSeenAt != null)
|
if (!status.isOnline && status.lastSeenAt != null)
|
||||||
Opacity(
|
Opacity(
|
||||||
opacity: 0.75,
|
opacity: 0.75,
|
||||||
@ -150,7 +158,7 @@ class AccountHeadingWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingOnly(left: 116, top: 6),
|
).paddingOnly(left: 116, top: 6),
|
||||||
const SizedBox(height: 4),
|
const Gap(4),
|
||||||
if (badges?.isNotEmpty ?? false)
|
if (badges?.isNotEmpty ?? false)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@ -181,6 +189,63 @@ class AccountHeadingWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
).paddingSymmetric(horizontal: 16),
|
).paddingSymmetric(horizontal: 16),
|
||||||
|
if (profile != null)
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
visualDensity:
|
||||||
|
const VisualDensity(horizontal: -4, vertical: -2),
|
||||||
|
title: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
ExperienceProvider.getLevelFromExp(
|
||||||
|
profile!.experience ?? 0,
|
||||||
|
).$2.tr,
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Badge(
|
||||||
|
label: Text(
|
||||||
|
'Lv${ExperienceProvider.getLevelFromExp(
|
||||||
|
profile!.experience ?? 0,
|
||||||
|
).$1}',
|
||||||
|
style: GoogleFonts.dosis(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).paddingOnly(top: 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: SizedBox(
|
||||||
|
height: 20,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(8),
|
||||||
|
),
|
||||||
|
value: ExperienceProvider.calcLevelUpProgress(
|
||||||
|
profile!.experience ?? 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'${ExperienceProvider.calcLevelUpProgressLevel(profile!.experience ?? 0)} EXP',
|
||||||
|
style: GoogleFonts.robotoMono(
|
||||||
|
fontSize: 10,
|
||||||
|
height: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).paddingSymmetric(horizontal: 16),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Card(
|
child: Card(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
import 'package:solian/providers/account_status.dart';
|
import 'package:solian/providers/account_status.dart';
|
||||||
@ -72,7 +73,7 @@ class _AccountProfilePopupState extends State<AccountProfilePopup> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.cancel, size: 24),
|
const Icon(Icons.cancel, size: 24),
|
||||||
const SizedBox(height: 12),
|
const Gap(12),
|
||||||
Text(
|
Text(
|
||||||
_hasError.toString(),
|
_hasError.toString(),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -98,6 +99,7 @@ class _AccountProfilePopupState extends State<AccountProfilePopup> {
|
|||||||
nick: _userinfo!.nick,
|
nick: _userinfo!.nick,
|
||||||
desc: _userinfo!.description,
|
desc: _userinfo!.description,
|
||||||
detail: _userinfo!,
|
detail: _userinfo!,
|
||||||
|
profile: _userinfo!.profile,
|
||||||
badges: _userinfo!.badges,
|
badges: _userinfo!.badges,
|
||||||
status:
|
status:
|
||||||
Get.find<StatusProvider>().getSomeoneStatus(_userinfo!.name),
|
Get.find<StatusProvider>().getSomeoneStatus(_userinfo!.name),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
@ -214,7 +215,7 @@ class _AccountStatusEditorDialogState extends State<AccountStatusEditorDialog> {
|
|||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: const LinearProgressIndicator().animate().scaleX(),
|
child: const LinearProgressIndicator().animate().scaleX(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 18),
|
const Gap(18),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _labelController,
|
controller: _labelController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -226,7 +227,7 @@ class _AccountStatusEditorDialogState extends State<AccountStatusEditorDialog> {
|
|||||||
onTapOutside: (_) =>
|
onTapOutside: (_) =>
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _clearAtController,
|
controller: _clearAtController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@ -238,7 +239,7 @@ class _AccountStatusEditorDialogState extends State<AccountStatusEditorDialog> {
|
|||||||
),
|
),
|
||||||
onTap: () => selectClearAt(),
|
onTap: () => selectClearAt(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
@ -281,7 +282,7 @@ class _AccountStatusEditorDialogState extends State<AccountStatusEditorDialog> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/screens/auth/signin.dart';
|
import 'package:solian/screens/auth/signin.dart';
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ class SigninRequiredOverlay extends StatelessWidget {
|
|||||||
Icons.login,
|
Icons.login,
|
||||||
size: 48,
|
size: 48,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
Text(
|
Text(
|
||||||
'signinRequired'.tr,
|
'signinRequired'.tr,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
@ -22,7 +22,7 @@ class AppBarLeadingButton extends StatelessWidget {
|
|||||||
onPressed: () => rootScaffoldKey.currentState!.openDrawer(),
|
onPressed: () => rootScaffoldKey.currentState!.openDrawer(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
@ -74,7 +75,7 @@ class _AttachmentAttrEditorDialogState
|
|||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: const LinearProgressIndicator().animate().scaleX(),
|
child: const LinearProgressIndicator().animate().scaleX(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 18),
|
const Gap(18),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _altController,
|
controller: _altController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -86,7 +87,7 @@ class _AttachmentAttrEditorDialogState
|
|||||||
onTapOutside: (_) =>
|
onTapOutside: (_) =>
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
contentPadding: const EdgeInsets.only(left: 4, right: 18),
|
contentPadding: const EdgeInsets.only(left: 4, right: 18),
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
|
@ -7,6 +7,7 @@ import 'package:dismissible_page/dismissible_page.dart';
|
|||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:image_cropper/image_cropper.dart';
|
import 'package:image_cropper/image_cropper.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
@ -144,7 +145,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text('attachmentAddLinkHint'.tr, textAlign: TextAlign.left),
|
Text('attachmentAddLinkHint'.tr, textAlign: TextAlign.left),
|
||||||
const SizedBox(height: 18),
|
const Gap(18),
|
||||||
TextField(
|
TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -346,14 +347,16 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: element.file.length(),
|
future: element.file.length(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) return const SizedBox();
|
if (!snapshot.hasData) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
return Text(
|
return Text(
|
||||||
_formatBytes(snapshot.data!),
|
_formatBytes(snapshot.data!),
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const Gap(6),
|
||||||
if (element.progress != null)
|
if (element.progress != null)
|
||||||
Text(
|
Text(
|
||||||
'${(element.progress! * 100).toStringAsFixed(2)}%',
|
'${(element.progress! * 100).toStringAsFixed(2)}%',
|
||||||
@ -614,7 +617,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const Gap(10),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (_uploadController.isUploading.value) {
|
if (_uploadController.isUploading.value) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
@ -627,7 +630,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -635,9 +638,9 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
const Gap(20),
|
||||||
Text('attachmentAutoUpload'.tr),
|
Text('attachmentAutoUpload'.tr),
|
||||||
const SizedBox(width: 8),
|
const Gap(8),
|
||||||
Switch(
|
Switch(
|
||||||
value: _isAutoUpload,
|
value: _isAutoUpload,
|
||||||
onChanged: (bool? value) {
|
onChanged: (bool? value) {
|
||||||
@ -666,7 +669,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
),
|
),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (_uploadController.isUploading.value) {
|
if (_uploadController.isUploading.value) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return TextButton(
|
return TextButton(
|
||||||
child: Text('attachmentUploadQueueStart'.tr),
|
child: Text('attachmentUploadQueueStart'.tr),
|
||||||
@ -680,7 +683,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||||
}),
|
}),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (_uploadController.queueOfUpload.isNotEmpty) {
|
if (_uploadController.queueOfUpload.isNotEmpty) {
|
||||||
@ -696,7 +699,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||||
}),
|
}),
|
||||||
if (_attachments.isNotEmpty)
|
if (_attachments.isNotEmpty)
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
|
@ -6,6 +6,7 @@ import 'package:dismissible_page/dismissible_page.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:gal/gal.dart';
|
import 'package:gal/gal.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
@ -198,7 +199,7 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
|
|||||||
radius: 19,
|
radius: 19,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const IgnorePointer(child: SizedBox(width: 8)),
|
const Gap(8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -240,7 +241,7 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const IgnorePointer(child: SizedBox(height: 4)),
|
const Gap(4),
|
||||||
IgnorePointer(
|
IgnorePointer(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.item.alt,
|
widget.item.alt,
|
||||||
@ -252,7 +253,7 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const IgnorePointer(child: SizedBox(height: 2)),
|
const Gap(2),
|
||||||
IgnorePointer(
|
IgnorePointer(
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
import 'package:solian/platform.dart';
|
import 'package:solian/platform.dart';
|
||||||
|
import 'package:solian/providers/durations.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
import 'package:solian/widgets/sized_container.dart';
|
import 'package:solian/widgets/sized_container.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
@ -55,6 +60,11 @@ class _AttachmentItemState extends State<AttachmentItem> {
|
|||||||
item: widget.item,
|
item: widget.item,
|
||||||
autoload: widget.autoload,
|
autoload: widget.autoload,
|
||||||
);
|
);
|
||||||
|
case 'audio':
|
||||||
|
return _AttachmentItemAudio(
|
||||||
|
item: widget.item,
|
||||||
|
autoload: widget.autoload,
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return Center(
|
return Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -65,7 +75,7 @@ class _AttachmentItemState extends State<AttachmentItem> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.file_present, size: 32),
|
const Icon(Icons.file_present, size: 32),
|
||||||
const SizedBox(height: 6),
|
const Gap(6),
|
||||||
Text(
|
Text(
|
||||||
widget.item.mimetype,
|
widget.item.mimetype,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -74,13 +84,13 @@ class _AttachmentItemState extends State<AttachmentItem> {
|
|||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const Gap(2),
|
||||||
Text(
|
Text(
|
||||||
widget.item.alt,
|
widget.item.alt,
|
||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: 13),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const Gap(12),
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
icon: const Icon(Icons.launch),
|
icon: const Icon(Icons.launch),
|
||||||
label: Text('openInBrowser'.tr),
|
label: Text('openInBrowser'.tr),
|
||||||
@ -239,22 +249,21 @@ class _AttachmentItemVideo extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
|
class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
|
||||||
late final _player = Player(
|
|
||||||
configuration: const PlayerConfiguration(
|
|
||||||
logLevel: MPVLogLevel.error,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
late final _controller = VideoController(_player);
|
|
||||||
|
|
||||||
bool _showContent = false;
|
bool _showContent = false;
|
||||||
|
|
||||||
|
Player? _videoPlayer;
|
||||||
|
VideoController? _videoController;
|
||||||
|
|
||||||
Future<void> _startLoad() async {
|
Future<void> _startLoad() async {
|
||||||
await _player.open(
|
|
||||||
Media(ServiceFinder.buildUrl('files', '/attachments/${widget.item.rid}')),
|
|
||||||
play: false,
|
|
||||||
);
|
|
||||||
setState(() => _showContent = true);
|
setState(() => _showContent = true);
|
||||||
|
MediaKit.ensureInitialized();
|
||||||
|
final url = ServiceFinder.buildUrl(
|
||||||
|
'files',
|
||||||
|
'/attachments/${widget.item.rid}',
|
||||||
|
);
|
||||||
|
_videoPlayer = Player();
|
||||||
|
_videoController = VideoController(_videoPlayer!);
|
||||||
|
_videoPlayer!.open(Media(url), play: !widget.autoload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -282,7 +291,7 @@ class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
size: 32,
|
size: 32,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
Text(
|
Text(
|
||||||
'attachmentUnload'.tr,
|
'attachmentUnload'.tr,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
@ -304,17 +313,228 @@ class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
|
|||||||
_startLoad();
|
_startLoad();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} else if (_videoController == null) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Video(
|
return Video(
|
||||||
|
controller: _videoController!,
|
||||||
aspectRatio: ratio,
|
aspectRatio: ratio,
|
||||||
controller: _controller,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_player.dispose();
|
_videoPlayer?.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _AttachmentItemAudio extends StatefulWidget {
|
||||||
|
final Attachment item;
|
||||||
|
final bool autoload;
|
||||||
|
|
||||||
|
const _AttachmentItemAudio({
|
||||||
|
required this.item,
|
||||||
|
this.autoload = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_AttachmentItemAudio> createState() => _AttachmentItemAudioState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AttachmentItemAudioState extends State<_AttachmentItemAudio> {
|
||||||
|
bool _showContent = false;
|
||||||
|
|
||||||
|
double? _draggingValue;
|
||||||
|
bool _isPlaying = false;
|
||||||
|
Duration _duration = Duration.zero;
|
||||||
|
Duration _position = Duration.zero;
|
||||||
|
Duration _bufferedPosition = Duration.zero;
|
||||||
|
|
||||||
|
Player? _audioPlayer;
|
||||||
|
|
||||||
|
Future<void> _startLoad() async {
|
||||||
|
setState(() => _showContent = true);
|
||||||
|
MediaKit.ensureInitialized();
|
||||||
|
final url = ServiceFinder.buildUrl(
|
||||||
|
'files',
|
||||||
|
'/attachments/${widget.item.rid}',
|
||||||
|
);
|
||||||
|
_audioPlayer = Player();
|
||||||
|
await _audioPlayer!.open(Media(url), play: !widget.autoload);
|
||||||
|
_audioPlayer!.stream.playing.listen((v) => setState(() => _isPlaying = v));
|
||||||
|
_audioPlayer!.stream.position.listen((v) => setState(() => _position = v));
|
||||||
|
_audioPlayer!.stream.duration.listen((v) => setState(() => _duration = v));
|
||||||
|
_audioPlayer!.stream.buffer.listen(
|
||||||
|
(v) => setState(() => _bufferedPosition = v),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (widget.autoload) {
|
||||||
|
_startLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
const ratio = 16 / 9;
|
||||||
|
if (!_showContent) {
|
||||||
|
return GestureDetector(
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: ratio,
|
||||||
|
child: CenteredContainer(
|
||||||
|
maxWidth: 280,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.not_started,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'attachmentUnload'.tr,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'attachmentUnloadCaption'.tr,
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_startLoad();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (_audioPlayer == null) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: ratio,
|
||||||
|
child: CenteredContainer(
|
||||||
|
maxWidth: 320,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.audio_file, size: 32),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
widget.item.alt,
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SliderTheme(
|
||||||
|
data: SliderThemeData(
|
||||||
|
trackHeight: 2,
|
||||||
|
trackShape: _PlayerProgressTrackShape(),
|
||||||
|
thumbShape: const RoundSliderThumbShape(
|
||||||
|
enabledThumbRadius: 8,
|
||||||
|
),
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
),
|
||||||
|
child: Slider(
|
||||||
|
secondaryTrackValue:
|
||||||
|
_bufferedPosition.inMilliseconds.abs().toDouble(),
|
||||||
|
value: _draggingValue?.abs() ??
|
||||||
|
_position.inMilliseconds.toDouble().abs(),
|
||||||
|
min: 0,
|
||||||
|
max: max(
|
||||||
|
_bufferedPosition.inMilliseconds.abs(),
|
||||||
|
max(
|
||||||
|
_position.inMilliseconds.abs(),
|
||||||
|
_duration.inMilliseconds.abs(),
|
||||||
|
),
|
||||||
|
).toDouble(),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _draggingValue = value);
|
||||||
|
},
|
||||||
|
onChangeEnd: (value) {
|
||||||
|
_audioPlayer!
|
||||||
|
.seek(Duration(milliseconds: value.toInt()));
|
||||||
|
setState(() => _draggingValue = null);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_position.toHumanReadableString(),
|
||||||
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
_duration.toHumanReadableString(),
|
||||||
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(horizontal: 8, vertical: 4),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
IconButton.filled(
|
||||||
|
icon: _isPlaying
|
||||||
|
? const Icon(Icons.pause)
|
||||||
|
: const Icon(Icons.play_arrow),
|
||||||
|
onPressed: () {
|
||||||
|
_audioPlayer!.playOrPause();
|
||||||
|
},
|
||||||
|
visualDensity: const VisualDensity(
|
||||||
|
horizontal: -4,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_audioPlayer?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlayerProgressTrackShape extends RoundedRectSliderTrackShape {
|
||||||
|
@override
|
||||||
|
Rect getPreferredRect({
|
||||||
|
required RenderBox parentBox,
|
||||||
|
Offset offset = Offset.zero,
|
||||||
|
required SliderThemeData sliderTheme,
|
||||||
|
bool isEnabled = false,
|
||||||
|
bool isDiscrete = false,
|
||||||
|
}) {
|
||||||
|
final trackHeight = sliderTheme.trackHeight;
|
||||||
|
final trackLeft = offset.dx;
|
||||||
|
final trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2;
|
||||||
|
final trackWidth = parentBox.size.width;
|
||||||
|
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:carousel_slider/carousel_slider.dart';
|
|||||||
import 'package:dismissible_page/dismissible_page.dart';
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
import 'package:flutter/material.dart' hide CarouselController;
|
import 'package:flutter/material.dart' hide CarouselController;
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
import 'package:solian/widgets/attachments/attachment_item.dart';
|
import 'package:solian/widgets/attachments/attachment_item.dart';
|
||||||
@ -93,6 +94,8 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
} else {
|
} else {
|
||||||
portrait++;
|
portrait++;
|
||||||
}
|
}
|
||||||
|
} else if (entry.mimetype.split('/').firstOrNull == 'audio') {
|
||||||
|
landscape++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isConsistent && consistentValue != null) {
|
if (isConsistent && consistentValue != null) {
|
||||||
@ -138,7 +141,7 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (widget.attachmentsId.isEmpty) {
|
if (widget.attachmentsId.isEmpty) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_isLoading) {
|
if (_isLoading) {
|
||||||
@ -171,7 +174,7 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
children: widget.attachmentsId.map((x) {
|
children: widget.attachmentsId.map((x) {
|
||||||
final element = _attachmentsMeta[idx];
|
final element = _attachmentsMeta[idx];
|
||||||
idx++;
|
idx++;
|
||||||
if (element == null) return const SizedBox();
|
if (element == null) return const SizedBox.shrink();
|
||||||
double ratio = element.metadata?['ratio']?.toDouble() ?? 16 / 9;
|
double ratio = element.metadata?['ratio']?.toDouble() ?? 16 / 9;
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -359,7 +362,7 @@ class AttachmentListEntry extends StatelessWidget {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
size: 32,
|
size: 32,
|
||||||
),
|
),
|
||||||
if (!isDense) const SizedBox(height: 8),
|
if (!isDense) const Gap(8),
|
||||||
if (!isDense)
|
if (!isDense)
|
||||||
Text(
|
Text(
|
||||||
'matureContent'.tr,
|
'matureContent'.tr,
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'package:avatar_stack/avatar_stack.dart';
|
import 'package:avatar_stack/avatar_stack.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
import 'package:solian/models/call.dart';
|
import 'package:solian/models/call.dart';
|
||||||
@ -52,27 +53,27 @@ class ChannelCallIndicator extends StatelessWidget {
|
|||||||
return Text('callOngoingJoined'.trParams({
|
return Text('callOngoingJoined'.trParams({
|
||||||
'duration': call.lastDuration.value,
|
'duration': call.lastDuration.value,
|
||||||
}));
|
}));
|
||||||
} else if (ongoingCall.participants.isEmpty) {
|
} else if (ongoingCall.participants!.isEmpty) {
|
||||||
return Text('callOngoingEmpty'.tr);
|
return Text('callOngoingEmpty'.tr);
|
||||||
} else {
|
} else {
|
||||||
return Text('callOngoingParticipants'.trParams({
|
return Text('callOngoingParticipants'.trParams({
|
||||||
'count': ongoingCall.participants.length.toString(),
|
'count': ongoingCall.participants!.length.toString(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
const SizedBox(width: 6),
|
const Gap(6),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (call.isInitialized.value) {
|
if (call.isInitialized.value) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
if (ongoingCall.participants.isNotEmpty) {
|
if (ongoingCall.participants!.isNotEmpty) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 28,
|
height: 28,
|
||||||
constraints: const BoxConstraints(maxWidth: 120),
|
constraints: const BoxConstraints(maxWidth: 120),
|
||||||
child: AvatarStack(
|
child: AvatarStack(
|
||||||
height: 28,
|
height: 28,
|
||||||
borderWidth: 0,
|
borderWidth: 0,
|
||||||
avatars: ongoingCall.participants.map((x) {
|
avatars: ongoingCall.participants!.map((x) {
|
||||||
final userinfo =
|
final userinfo =
|
||||||
Account.fromJson(jsonDecode(x['metadata']));
|
Account.fromJson(jsonDecode(x['metadata']));
|
||||||
return PlatformInfo.canCacheImage
|
return PlatformInfo.canCacheImage
|
||||||
@ -83,7 +84,7 @@ class ChannelCallIndicator extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -112,7 +113,7 @@ class ChannelCallIndicator extends StatelessWidget {
|
|||||||
child: Text('callJoin'.tr),
|
child: Text('callJoin'.tr),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:livekit_client/livekit_client.dart';
|
import 'package:livekit_client/livekit_client.dart';
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ class ParticipantInfoWidget extends StatelessWidget {
|
|||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 5),
|
const Gap(5),
|
||||||
isScreenShare
|
isScreenShare
|
||||||
? const Icon(
|
? const Icon(
|
||||||
Icons.monitor,
|
Icons.monitor,
|
||||||
@ -47,7 +48,7 @@ class ParticipantInfoWidget extends StatelessWidget {
|
|||||||
color: audioAvailable ? Colors.white : Colors.red,
|
color: audioAvailable ? Colors.white : Colors.red,
|
||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 3),
|
const Gap(3),
|
||||||
if (connectionQuality != ConnectionQuality.unknown)
|
if (connectionQuality != ConnectionQuality.unknown)
|
||||||
Icon(
|
Icon(
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@ class ChatCallCurrentIndicator extends StatelessWidget {
|
|||||||
|
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
if (call.current.value == null || call.channel.value == null) {
|
if (call.current.value == null || call.channel.value == null) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/controllers/chat_events_controller.dart';
|
import 'package:solian/controllers/chat_events_controller.dart';
|
||||||
import 'package:solian/models/event.dart';
|
import 'package:solian/models/event.dart';
|
||||||
@ -39,7 +40,7 @@ class ChatEvent extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLinkExpansion() {
|
Widget _buildLinkExpansion() {
|
||||||
if (item.body['text'] == null) return const SizedBox();
|
if (item.body['text'] == null) return const SizedBox.shrink();
|
||||||
return LinkExpansion(content: item.body['text']);
|
return LinkExpansion(content: item.body['text']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ class ChatEvent extends StatelessWidget {
|
|||||||
? List<String>.from(item.body['attachments']?.whereType<String>())
|
? List<String>.from(item.body['attachments']?.whereType<String>())
|
||||||
: List<String>.empty();
|
: List<String>.empty();
|
||||||
|
|
||||||
if (attachments.isEmpty) return const SizedBox();
|
if (attachments.isEmpty) return const SizedBox.shrink();
|
||||||
|
|
||||||
if (isMinimal) {
|
if (isMinimal) {
|
||||||
final unFocusColor =
|
final unFocusColor =
|
||||||
@ -93,7 +94,7 @@ class ChatEvent extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData || snapshot.data == null) {
|
if (!snapshot.hasData || snapshot.data == null) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
@ -214,7 +215,7 @@ class ChatEvent extends StatelessWidget {
|
|||||||
opacity: 0.75,
|
opacity: 0.75,
|
||||||
child: FaIcon(FontAwesomeIcons.quoteLeft, size: 14),
|
child: FaIcon(FontAwesomeIcons.quoteLeft, size: 14),
|
||||||
).paddingOnly(bottom: 2.75),
|
).paddingOnly(bottom: 2.75),
|
||||||
const SizedBox(width: 4),
|
const Gap(4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
@ -226,12 +227,12 @@ class ChatEvent extends StatelessWidget {
|
|||||||
content: item.sender.account.avatar,
|
content: item.sender.account.avatar,
|
||||||
radius: 9,
|
radius: 9,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 5),
|
const Gap(5),
|
||||||
Text(
|
Text(
|
||||||
item.sender.account.nick,
|
item.sender.account.nick,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const Gap(4),
|
||||||
Text(format(item.createdAt, locale: 'en_short')),
|
Text(format(item.createdAt, locale: 'en_short')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -275,7 +276,7 @@ class ChatEvent extends StatelessWidget {
|
|||||||
item.sender.account.nick,
|
item.sender.account.nick,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const Gap(4),
|
||||||
Text(format(item.createdAt, locale: 'en_short'))
|
Text(format(item.createdAt, locale: 'en_short'))
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 12),
|
).paddingSymmetric(horizontal: 12),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class ChatEventMessageActionLog extends StatelessWidget {
|
class ChatEventMessageActionLog extends StatelessWidget {
|
||||||
@ -25,7 +26,7 @@ class ChatEventMessageActionLog extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
icon,
|
icon,
|
||||||
const SizedBox(width: 4),
|
const Gap(4),
|
||||||
Text(text),
|
Text(text),
|
||||||
],
|
],
|
||||||
).paddingOnly(
|
).paddingOnly(
|
||||||
|
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:solian/controllers/chat_events_controller.dart';
|
import 'package:solian/controllers/chat_events_controller.dart';
|
||||||
import 'package:solian/models/channel.dart';
|
import 'package:solian/models/channel.dart';
|
||||||
import 'package:solian/models/event.dart';
|
import 'package:solian/models/event.dart';
|
||||||
|
import 'package:solian/providers/last_read.dart';
|
||||||
import 'package:solian/widgets/chat/chat_event.dart';
|
import 'package:solian/widgets/chat/chat_event.dart';
|
||||||
import 'package:solian/widgets/chat/chat_event_action.dart';
|
import 'package:solian/widgets/chat/chat_event_action.dart';
|
||||||
|
|
||||||
@ -39,6 +40,9 @@ class ChatEventList extends StatelessWidget {
|
|||||||
key: Key('chat-history#${channel.id}'),
|
key: Key('chat-history#${channel.id}'),
|
||||||
itemCount: chatController.currentEvents.length,
|
itemCount: chatController.currentEvents.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
Get.find<LastReadProvider>().messagesLastReadAt =
|
||||||
|
chatController.currentEvents[index].id;
|
||||||
|
|
||||||
bool isMerged = false, hasMerged = false;
|
bool isMerged = false, hasMerged = false;
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
hasMerged = _checkMessageMergeable(
|
hasMerged = _checkMessageMergeable(
|
||||||
@ -55,7 +59,8 @@ class ChatEventList extends StatelessWidget {
|
|||||||
|
|
||||||
final item = chatController.currentEvents[index].data;
|
final item = chatController.currentEvents[index].data;
|
||||||
|
|
||||||
return InkWell(
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
child: ChatEvent(
|
child: ChatEvent(
|
||||||
key: Key('m${item.uuid}'),
|
key: Key('m${item.uuid}'),
|
||||||
item: item,
|
item: item,
|
||||||
@ -91,7 +96,7 @@ class ChatEventList extends StatelessWidget {
|
|||||||
chatController.totalEvents - chatController.currentEvents.length;
|
chatController.totalEvents - chatController.currentEvents.length;
|
||||||
|
|
||||||
if (amount.value <= 0 || chatController.isLoading.isTrue) {
|
if (amount.value <= 0 || chatController.isLoading.isTrue) {
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||||
}
|
}
|
||||||
|
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
|
@ -156,7 +156,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// The local mock data
|
// The local mock data
|
||||||
final sender = Sender(
|
final sender = ChannelMember(
|
||||||
id: 0,
|
id: 0,
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
updatedAt: DateTime.now(),
|
updatedAt: DateTime.now(),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/models/channel.dart';
|
import 'package:solian/models/channel.dart';
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ class _ChatTypingIndicatorState extends State<ChatTypingIndicator>
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.more_horiz),
|
const Icon(Icons.more_horiz),
|
||||||
const SizedBox(width: 6),
|
const Gap(6),
|
||||||
Text('typingMessage'.trParams({
|
Text('typingMessage'.trParams({
|
||||||
'user': widget.users.map((x) => x.account.nick).join(', '),
|
'user': widget.users.map((x) => x.account.nick).join(', '),
|
||||||
})),
|
})),
|
||||||
|
253
lib/widgets/daily_sign/history_chart.dart
Normal file
253
lib/widgets/daily_sign/history_chart.dart
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:solian/models/daily_sign.dart';
|
||||||
|
|
||||||
|
class DailySignHistoryChartDialog extends StatelessWidget {
|
||||||
|
final List<DailySignRecord>? data;
|
||||||
|
|
||||||
|
const DailySignHistoryChartDialog({super.key, required this.data});
|
||||||
|
|
||||||
|
static List<String> signSymbols = ['大凶', '凶', '中平', '吉', '大吉'];
|
||||||
|
|
||||||
|
DateTime? get _firstRecordDate => data?.map((x) => x.createdAt).reduce(
|
||||||
|
(a, b) => DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
min(a.millisecondsSinceEpoch, b.millisecondsSinceEpoch)));
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('运势历史'),
|
||||||
|
Text(
|
||||||
|
'${DateFormat('yyyy/MM/dd').format(_firstRecordDate!)} - ${DateFormat('yyyy/MM/dd').format(DateTime.now())}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: data == null
|
||||||
|
? SizedBox(
|
||||||
|
height: 180,
|
||||||
|
width: max(640, MediaQuery.of(context).size.width),
|
||||||
|
child: const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('近期运势', style: Theme.of(context).textTheme.titleMedium)
|
||||||
|
.paddingOnly(bottom: 18),
|
||||||
|
SizedBox(
|
||||||
|
height: 180,
|
||||||
|
width: max(640, MediaQuery.of(context).size.width),
|
||||||
|
child: LineChart(
|
||||||
|
LineChartData(
|
||||||
|
lineBarsData: [
|
||||||
|
LineChartBarData(
|
||||||
|
isCurved: true,
|
||||||
|
isStrokeCapRound: true,
|
||||||
|
isStrokeJoinRound: true,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
belowBarData: BarAreaData(
|
||||||
|
show: true,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: List.filled(
|
||||||
|
data!.length,
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary
|
||||||
|
.withOpacity(0.3),
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
spots: data!
|
||||||
|
.map(
|
||||||
|
(x) => FlSpot(
|
||||||
|
x.createdAt
|
||||||
|
.copyWith(
|
||||||
|
hour: 0,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
millisecond: 0,
|
||||||
|
microsecond: 0,
|
||||||
|
)
|
||||||
|
.millisecondsSinceEpoch
|
||||||
|
.toDouble(),
|
||||||
|
x.resultTier.toDouble(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
lineTouchData: LineTouchData(
|
||||||
|
touchTooltipData: LineTouchTooltipData(
|
||||||
|
getTooltipItems: (spots) => spots
|
||||||
|
.map((spot) => LineTooltipItem(
|
||||||
|
'${signSymbols[spot.y.toInt()]}\n${DateFormat('MM/dd').format(DateTime.fromMillisecondsSinceEpoch(spot.x.toInt()))}',
|
||||||
|
TextStyle(
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
getTooltipColor: (_) =>
|
||||||
|
Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
|
)),
|
||||||
|
titlesData: FlTitlesData(
|
||||||
|
topTitles: const AxisTitles(
|
||||||
|
sideTitles: SideTitles(showTitles: false),
|
||||||
|
),
|
||||||
|
rightTitles: const AxisTitles(
|
||||||
|
sideTitles: SideTitles(showTitles: false),
|
||||||
|
),
|
||||||
|
leftTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
showTitles: true,
|
||||||
|
reservedSize: 40,
|
||||||
|
interval: 1,
|
||||||
|
getTitlesWidget: (value, _) => Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Text(
|
||||||
|
signSymbols[value.toInt()],
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
).paddingOnly(right: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
showTitles: true,
|
||||||
|
reservedSize: 28,
|
||||||
|
interval: 86400000,
|
||||||
|
getTitlesWidget: (value, _) => Text(
|
||||||
|
DateFormat('dd').format(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
value.toInt(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
).paddingOnly(top: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
gridData: const FlGridData(show: false),
|
||||||
|
borderData: FlBorderData(show: false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).marginOnly(right: 24, bottom: 8, top: 8),
|
||||||
|
const Gap(16),
|
||||||
|
Text('功德趋势', style: Theme.of(context).textTheme.titleMedium)
|
||||||
|
.paddingOnly(bottom: 18),
|
||||||
|
SizedBox(
|
||||||
|
height: 180,
|
||||||
|
width: max(640, MediaQuery.of(context).size.width),
|
||||||
|
child: LineChart(
|
||||||
|
LineChartData(
|
||||||
|
lineBarsData: [
|
||||||
|
LineChartBarData(
|
||||||
|
isCurved: true,
|
||||||
|
isStrokeCapRound: true,
|
||||||
|
isStrokeJoinRound: true,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
belowBarData: BarAreaData(
|
||||||
|
show: true,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: List.filled(
|
||||||
|
data!.length,
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary
|
||||||
|
.withOpacity(0.3),
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
spots: data!
|
||||||
|
.map(
|
||||||
|
(x) => FlSpot(
|
||||||
|
x.createdAt
|
||||||
|
.copyWith(
|
||||||
|
hour: 0,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
millisecond: 0,
|
||||||
|
microsecond: 0,
|
||||||
|
)
|
||||||
|
.millisecondsSinceEpoch
|
||||||
|
.toDouble(),
|
||||||
|
x.resultExperience.toDouble(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
lineTouchData: LineTouchData(
|
||||||
|
touchTooltipData: LineTouchTooltipData(
|
||||||
|
getTooltipItems: (spots) => spots
|
||||||
|
.map((spot) => LineTooltipItem(
|
||||||
|
'+${spot.y.toStringAsFixed(0)} EXP\n${DateFormat('MM/dd').format(DateTime.fromMillisecondsSinceEpoch(spot.x.toInt()))}',
|
||||||
|
TextStyle(
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
getTooltipColor: (_) =>
|
||||||
|
Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
|
)),
|
||||||
|
titlesData: FlTitlesData(
|
||||||
|
topTitles: const AxisTitles(
|
||||||
|
sideTitles: SideTitles(showTitles: false),
|
||||||
|
),
|
||||||
|
rightTitles: const AxisTitles(
|
||||||
|
sideTitles: SideTitles(showTitles: false),
|
||||||
|
),
|
||||||
|
leftTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
showTitles: true,
|
||||||
|
reservedSize: 40,
|
||||||
|
getTitlesWidget: (value, _) => Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Text(
|
||||||
|
value.toStringAsFixed(0),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
).paddingOnly(right: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
showTitles: true,
|
||||||
|
reservedSize: 28,
|
||||||
|
interval: 86400000,
|
||||||
|
getTitlesWidget: (value, _) => Text(
|
||||||
|
DateFormat('dd').format(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
value.toInt(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
).paddingOnly(top: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
gridData: const FlGridData(show: false),
|
||||||
|
borderData: FlBorderData(show: false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).marginOnly(right: 24, bottom: 8, top: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -57,10 +57,10 @@ class LinkExpansion extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
final matches = linkRegex.allMatches(content);
|
final matches = linkRegex.allMatches(content);
|
||||||
if (matches.isEmpty) {
|
if (matches.isEmpty) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
final LinkExpandController expandController = Get.find();
|
final LinkExpandProvider expandController = Get.find();
|
||||||
|
|
||||||
return Wrap(
|
return Wrap(
|
||||||
children: matches.map((x) {
|
children: matches.map((x) {
|
||||||
@ -72,7 +72,7 @@ class LinkExpansion extends StatelessWidget {
|
|||||||
future: expandController.expandLink(x.group(0)!),
|
future: expandController.expandLink(x.group(0)!),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
final isRichDescription = [
|
final isRichDescription = [
|
||||||
|
@ -4,9 +4,14 @@ import 'package:get/utils.dart';
|
|||||||
abstract class AppNavigation {
|
abstract class AppNavigation {
|
||||||
static List<AppNavigationDestination> destinations = [
|
static List<AppNavigationDestination> destinations = [
|
||||||
AppNavigationDestination(
|
AppNavigationDestination(
|
||||||
icon: Icons.home,
|
icon: Icons.dashboard,
|
||||||
label: 'home'.tr,
|
label: 'dashboard'.tr,
|
||||||
page: 'home',
|
page: 'dashboard',
|
||||||
|
),
|
||||||
|
AppNavigationDestination(
|
||||||
|
icon: Icons.newspaper,
|
||||||
|
label: 'feed'.tr,
|
||||||
|
page: 'feed',
|
||||||
),
|
),
|
||||||
AppNavigationDestination(
|
AppNavigationDestination(
|
||||||
icon: Icons.workspaces,
|
icon: Icons.workspaces,
|
||||||
|
@ -26,7 +26,7 @@ class AppNavigationDrawer extends StatefulWidget {
|
|||||||
|
|
||||||
class _AppNavigationDrawerState extends State<AppNavigationDrawer>
|
class _AppNavigationDrawerState extends State<AppNavigationDrawer>
|
||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
bool _isCollapsed = false;
|
bool _isCollapsed = true;
|
||||||
|
|
||||||
late final AnimationController _drawerAnimationController =
|
late final AnimationController _drawerAnimationController =
|
||||||
AnimationController(
|
AnimationController(
|
||||||
|
@ -16,7 +16,7 @@ class AppNavigationRegion extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
void _gotoChannel(Channel item) {
|
void _gotoChannel(Channel item) {
|
||||||
AppRouter.instance.pushReplacementNamed(
|
AppRouter.instance.goNamed(
|
||||||
'channelChat',
|
'channelChat',
|
||||||
pathParameters: {'alias': item.alias},
|
pathParameters: {'alias': item.alias},
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:solian/controllers/post_editor_controller.dart';
|
import 'package:solian/controllers/post_editor_controller.dart';
|
||||||
@ -86,7 +87,7 @@ class _PostEditorDateDialogState extends State<PostEditorDateDialog> {
|
|||||||
),
|
),
|
||||||
onTap: () => _selectDate(0),
|
onTap: () => _selectDate(0),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _publishedUntilController,
|
controller: _publishedUntilController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/controllers/post_editor_controller.dart';
|
import 'package:solian/controllers/post_editor_controller.dart';
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ class PostEditorOverviewDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
TextField(
|
TextField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
autocorrect: true,
|
autocorrect: true,
|
||||||
@ -37,7 +38,7 @@ class PostEditorOverviewDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
TextField(
|
TextField(
|
||||||
enabled: controller.mode.value == 1,
|
enabled: controller.mode.value == 1,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/controllers/post_editor_controller.dart';
|
import 'package:solian/controllers/post_editor_controller.dart';
|
||||||
import 'package:solian/widgets/attachments/attachment_editor.dart';
|
import 'package:solian/widgets/attachments/attachment_editor.dart';
|
||||||
@ -68,7 +69,7 @@ class _PostEditorThumbnailDialogState extends State<PostEditorThumbnailDialog> {
|
|||||||
_promptUploadNewAttachment();
|
_promptUploadNewAttachment();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const Gap(8),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _attachmentController,
|
controller: _attachmentController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/controllers/post_editor_controller.dart';
|
import 'package:solian/controllers/post_editor_controller.dart';
|
||||||
import 'package:solian/widgets/account/account_select.dart';
|
import 'package:solian/widgets/account/account_select.dart';
|
||||||
@ -56,9 +57,9 @@ class PostEditorVisibilityDialog extends StatelessWidget {
|
|||||||
Obx(() {
|
Obx(() {
|
||||||
if (controller.visibility.value == 2 ||
|
if (controller.visibility.value == 2 ||
|
||||||
controller.visibility.value == 3) {
|
controller.visibility.value == 3) {
|
||||||
return const SizedBox(height: 8);
|
return const Gap(8);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}),
|
}),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (controller.visibility.value == 2) {
|
if (controller.visibility.value == 2) {
|
||||||
@ -85,7 +86,7 @@ class PostEditorVisibilityDialog extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}),
|
}),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (controller.visibility.value == 3) {
|
if (controller.visibility.value == 3) {
|
||||||
@ -112,7 +113,7 @@ class PostEditorVisibilityDialog extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ class _TagsFieldState extends State<TagsField> {
|
|||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4.0),
|
const Gap(4),
|
||||||
InkWell(
|
InkWell(
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
Icons.cancel,
|
Icons.cancel,
|
||||||
|
@ -2,6 +2,7 @@ import 'package:animations/animations.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get_utils/get_utils.dart';
|
import 'package:get/get_utils/get_utils.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
@ -78,7 +79,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildThumbnail() {
|
Widget _buildThumbnail() {
|
||||||
if (widget.item.body['thumbnail'] == null) return const SizedBox();
|
if (widget.item.body['thumbnail'] == null) return const SizedBox.shrink();
|
||||||
final border = BorderSide(
|
final border = BorderSide(
|
||||||
color: Theme.of(context).dividerColor,
|
color: Theme.of(context).dividerColor,
|
||||||
width: 0.3,
|
width: 0.3,
|
||||||
@ -147,7 +148,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
vertical: 8,
|
vertical: 8,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFooter() {
|
Widget _buildFooter() {
|
||||||
@ -186,7 +187,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (widgets.isEmpty) {
|
if (widgets.isEmpty) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
} else {
|
} else {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -519,7 +520,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
bottom: 10,
|
bottom: 10,
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
const SizedBox(height: 10),
|
const Gap(10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
openBuilder: (_, __) => TitleShell(
|
openBuilder: (_, __) => TitleShell(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/models/reaction.dart';
|
import 'package:solian/models/reaction.dart';
|
||||||
|
|
||||||
@ -33,9 +34,11 @@ class PostReactionPopup extends StatelessWidget {
|
|||||||
label: Row(
|
label: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(e.key,
|
Text(
|
||||||
style: const TextStyle(fontFamily: 'monospace')),
|
e.key,
|
||||||
const SizedBox(width: 6),
|
style: const TextStyle(fontFamily: 'monospace'),
|
||||||
|
),
|
||||||
|
const Gap(6),
|
||||||
Text('x${reactionList[e.key]?.toString() ?? '0'}',
|
Text('x${reactionList[e.key]?.toString() ?? '0'}',
|
||||||
style:
|
style:
|
||||||
const TextStyle(fontWeight: FontWeight.bold)),
|
const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:solian/models/feed.dart';
|
import 'package:solian/models/post_categories.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
|
|
||||||
class PostTagsList extends StatelessWidget {
|
class PostTagsList extends StatelessWidget {
|
||||||
|
@ -29,7 +29,7 @@ class PostWarpedListWidget extends StatelessWidget {
|
|||||||
builderDelegate: PagedChildBuilderDelegate<Post>(
|
builderDelegate: PagedChildBuilderDelegate<Post>(
|
||||||
itemBuilder: (context, item, index) {
|
itemBuilder: (context, item, index) {
|
||||||
if (item.pinnedAt != null && !isPinned) {
|
if (item.pinnedAt != null && !isPinned) {
|
||||||
return const SizedBox();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return PostListEntryWidget(
|
return PostListEntryWidget(
|
||||||
renderOrder: index,
|
renderOrder: index,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user