Compare commits
60 Commits
1def3e1895
...
3.2.0+125
| Author | SHA1 | Date | |
|---|---|---|---|
| fbfe8cbdee | |||
| fbbab0a981 | |||
| ae2fb3b303 | |||
| 3d7a4666ed | |||
| 5d3e0fb800 | |||
| 85ff52a661 | |||
| da7fd64a43 | |||
| 3902633217 | |||
| f478ea8b84 | |||
| 0f481aff5b | |||
| 7a31663310 | |||
| 0239c53c04 | |||
| 16987c758e | |||
| 3a36915140 | |||
| 4bde708878 | |||
| 2f0cf560f8 | |||
| cf355a95fd | |||
| 2f43073172 | |||
| 8236d31ecc | |||
| 459a7dade0 | |||
| e6000a660a | |||
| 75abaac205 | |||
| 603d5c3f73 | |||
| 4e4bd99598 | |||
| d1fbe5f15e | |||
| c061ef2132 | |||
| c378309bdd | |||
| b2c5d64fc5 | |||
|
|
5371637b16 | ||
| c5cbf0af37 | |||
| 1a31e22450 | |||
|
|
49db54529d | ||
| 8e0c0c6054 | |||
| f3d1183076 | |||
| a9f7f0cce0 | |||
| f2943f8411 | |||
| 808e7dcffa | |||
| 9bed4fa6fb | |||
| e6255a340b | |||
| 78bf319fb7 | |||
| 36a966d582 | |||
| f72b268d36 | |||
| 44ef31034e | |||
| 229dc2186f | |||
| a2f9a1efb4 | |||
|
|
823e3c5de6 | ||
|
|
faac7bac35 | ||
| 1fac1bfe02 | |||
| 9394b1d9c8 | |||
| 43dd13bac4 | |||
| 65bc372103 | |||
| 6558854a7a | |||
| 892035ab27 | |||
| 87ae8d2ff4 | |||
| 15c2dbaa0d | |||
| 6b3338b885 | |||
| bb00b1bc6a | |||
| 5e1a15ada2 | |||
| 9bdf8ba346 | |||
| 204c087f29 |
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -41,6 +41,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: build-output-windows
|
name: build-output-windows
|
||||||
path: build/windows/x64/runner/Release
|
path: build/windows/x64/runner/Release
|
||||||
|
- name: Compile Installer
|
||||||
|
uses: Minionguyjpro/Inno-Setup-Action@v1.2.2
|
||||||
|
with:
|
||||||
|
path: setup.iss
|
||||||
|
- name: Archive installer artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: build-output-windows-installer
|
||||||
|
path: Installer/windows-x86_64-setup.exe
|
||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,6 +12,9 @@
|
|||||||
.swiftpm/
|
.swiftpm/
|
||||||
migrate_working_dir/
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# Inno Setup
|
||||||
|
Installer/
|
||||||
|
|
||||||
# IntelliJ related
|
# IntelliJ related
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ plugins {
|
|||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
// START: FlutterFire Configuration
|
// START: FlutterFire Configuration
|
||||||
id("com.google.gms.google-services")
|
id("com.google.gms.google-services")
|
||||||
|
id("com.google.firebase.crashlytics")
|
||||||
// END: FlutterFire Configuration
|
// END: FlutterFire Configuration
|
||||||
id("kotlin-android")
|
id("kotlin-android")
|
||||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
@@ -51,6 +52,12 @@ android {
|
|||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("release")
|
||||||
|
|
||||||
|
isMinifyEnabled = true
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +65,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.google.android.material:material:1.12.0")
|
implementation("com.google.android.material:material:1.12.0")
|
||||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
implementation("com.squareup.okhttp3:okhttp:5.1.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
flutter {
|
flutter {
|
||||||
|
|||||||
5
android/app/proguard-rules.pro
vendored
Normal file
5
android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# JNI Zero initialization (required for WebRTC native method registration)
|
||||||
|
-keep class livekit.org.jni_zero.JniInit {
|
||||||
|
# Keep the init method un-obfuscated for native code callback
|
||||||
|
private static java.lang.Object[] init();
|
||||||
|
}
|
||||||
BIN
android/app/src/main/res/drawable/ic_notification.png
Executable file
BIN
android/app/src/main/res/drawable/ic_notification.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip
|
||||||
|
|||||||
@@ -18,11 +18,12 @@ pluginManagement {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
id("com.android.application") version "8.10.1" apply false
|
id("com.android.application") version "8.12.0" apply false
|
||||||
// START: FlutterFire Configuration
|
// START: FlutterFire Configuration
|
||||||
id("com.google.gms.google-services") version("4.3.15") apply false
|
id("com.google.gms.google-services") version("4.3.15") apply false
|
||||||
|
id("com.google.firebase.crashlytics") version("2.8.1") apply false
|
||||||
// END: FlutterFire Configuration
|
// END: FlutterFire Configuration
|
||||||
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
|
id("org.jetbrains.kotlin.android") version("2.2.0") apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include(":app")
|
include(":app")
|
||||||
|
|||||||
@@ -573,6 +573,7 @@
|
|||||||
"keyboardShortcuts": "Keyboard Shortcuts",
|
"keyboardShortcuts": "Keyboard Shortcuts",
|
||||||
"share": "Share",
|
"share": "Share",
|
||||||
"sharePost": "Share Post",
|
"sharePost": "Share Post",
|
||||||
|
"sharePostPhoto": "Share Post as Photo",
|
||||||
"quickActions": "Quick Actions",
|
"quickActions": "Quick Actions",
|
||||||
"post": "Post",
|
"post": "Post",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
@@ -706,6 +707,7 @@
|
|||||||
"copyToClipboardTooltip": "Copy to clipboard",
|
"copyToClipboardTooltip": "Copy to clipboard",
|
||||||
"postForwardingTo": "Forwarding to",
|
"postForwardingTo": "Forwarding to",
|
||||||
"postReplyingTo": "Replying to",
|
"postReplyingTo": "Replying to",
|
||||||
|
"postReplyPlaceholder": "Post your reply",
|
||||||
"postEditing": "You are editing an existing post",
|
"postEditing": "You are editing an existing post",
|
||||||
"postArticle": "Article",
|
"postArticle": "Article",
|
||||||
"aboutDeviceName": "Device Name",
|
"aboutDeviceName": "Device Name",
|
||||||
@@ -759,6 +761,7 @@
|
|||||||
"pollsRecent": "Recent Polls",
|
"pollsRecent": "Recent Polls",
|
||||||
"pollCreateNew": "Create New",
|
"pollCreateNew": "Create New",
|
||||||
"pollCreateNewHint": "Create a new poll for your post. Pick a publisher and continue.",
|
"pollCreateNewHint": "Create a new poll for your post. Pick a publisher and continue.",
|
||||||
|
"pollQuestions": "Questions",
|
||||||
"publisher": "Publisher",
|
"publisher": "Publisher",
|
||||||
"publisherHint": "Enter the publisher name",
|
"publisherHint": "Enter the publisher name",
|
||||||
"publisherCannotBeEmpty": "Publisher cannot be empty",
|
"publisherCannotBeEmpty": "Publisher cannot be empty",
|
||||||
@@ -782,5 +785,56 @@
|
|||||||
"postCategoryStudy": "Study",
|
"postCategoryStudy": "Study",
|
||||||
"postCategoryGaming": "Gaming",
|
"postCategoryGaming": "Gaming",
|
||||||
"postCategoryProgramming": "Programming",
|
"postCategoryProgramming": "Programming",
|
||||||
"postCategoryMusic": "Music"
|
"postCategoryMusic": "Music",
|
||||||
}
|
"links": "Links",
|
||||||
|
"addLink": "Add link",
|
||||||
|
"linkKey": "Link Name",
|
||||||
|
"linkValue": "URL",
|
||||||
|
"debugOptions": "Debug Options",
|
||||||
|
"joinedAt": "Joined at {}",
|
||||||
|
"searchAccounts": "Search accounts...",
|
||||||
|
"webFeeds": "Web Feeds",
|
||||||
|
"polls": "Polls",
|
||||||
|
"sharePostSlogan": "Explore more on the Solar Network",
|
||||||
|
"filesListAdditional": {
|
||||||
|
"one": "+{} file remaining",
|
||||||
|
"other": "+{} files remaining"
|
||||||
|
},
|
||||||
|
"pollAnswerSubmitted": "Poll answer has been submitted.",
|
||||||
|
"modifyAnswers": "Modify Answers",
|
||||||
|
"back": "Back",
|
||||||
|
"submit": "Submit",
|
||||||
|
"pollOptionDefaultLabel": "Option 1",
|
||||||
|
"pollUpdated": "Poll updated.",
|
||||||
|
"pollCreated": "Poll created.",
|
||||||
|
"pollCreate": "Create Poll",
|
||||||
|
"pollEdit": "Edit Poll",
|
||||||
|
"pollPreviewJsonDebug": "Debug Preview",
|
||||||
|
"pollTitleRequired": "Title is required",
|
||||||
|
"pollEndDateOptional": "End date & time (optional)",
|
||||||
|
"notSet": "Not set",
|
||||||
|
"pick": "Pick",
|
||||||
|
"clear": "Clear",
|
||||||
|
"questions": "Questions",
|
||||||
|
"pollAddQuestion": "Add question",
|
||||||
|
"pollQuestionTypeSingleChoice": "Single choice",
|
||||||
|
"pollQuestionTypeMultipleChoice": "Multiple choice",
|
||||||
|
"pollQuestionTypeFreeText": "Free text",
|
||||||
|
"pollQuestionTypeYesNo": "Yes / No",
|
||||||
|
"pollQuestionTypeRating": "Rating",
|
||||||
|
"pollNoQuestionsYet": "No questions yet",
|
||||||
|
"pollNoQuestionsHint": "Use \"Add question\" to start building your poll.",
|
||||||
|
"pollDebugPreview": "Debug Preview",
|
||||||
|
"pollUntitledQuestion": "Untitled question",
|
||||||
|
"moveUp": "Move up",
|
||||||
|
"moveDown": "Move down",
|
||||||
|
"required": "Required",
|
||||||
|
"pollQuestionTitle": "Question title",
|
||||||
|
"pollQuestionTitleRequired": "Question title is required",
|
||||||
|
"pollQuestionDescriptionOptional": "Question description (optional)",
|
||||||
|
"options": "Options",
|
||||||
|
"pollAddOption": "Add option",
|
||||||
|
"pollOptionLabel": "Option label",
|
||||||
|
"pollLongTextAnswerPreview": "Long text answer (preview)",
|
||||||
|
"pollShortTextAnswerPreview": "Short text answer (preview)"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
assets/icons/icon.ico
Normal file
BIN
assets/icons/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
135
ios/Podfile.lock
135
ios/Podfile.lock
@@ -42,22 +42,62 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- Firebase/CoreOnly (12.0.0):
|
- Firebase/CoreOnly (12.0.0):
|
||||||
- FirebaseCore (~> 12.0.0)
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- Firebase/Crashlytics (12.0.0):
|
||||||
|
- Firebase/CoreOnly
|
||||||
|
- FirebaseCrashlytics (~> 12.0.0)
|
||||||
- Firebase/Messaging (12.0.0):
|
- Firebase/Messaging (12.0.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 12.0.0)
|
- FirebaseMessaging (~> 12.0.0)
|
||||||
|
- firebase_analytics (12.0.0):
|
||||||
|
- firebase_core
|
||||||
|
- FirebaseAnalytics (= 12.0.0)
|
||||||
|
- Flutter
|
||||||
- firebase_core (4.0.0):
|
- firebase_core (4.0.0):
|
||||||
- Firebase/CoreOnly (= 12.0.0)
|
- Firebase/CoreOnly (= 12.0.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- firebase_crashlytics (5.0.0):
|
||||||
|
- Firebase/Crashlytics (= 12.0.0)
|
||||||
|
- firebase_core
|
||||||
|
- Flutter
|
||||||
- firebase_messaging (16.0.0):
|
- firebase_messaging (16.0.0):
|
||||||
- Firebase/Messaging (= 12.0.0)
|
- Firebase/Messaging (= 12.0.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- FirebaseAnalytics (12.0.0):
|
||||||
|
- FirebaseAnalytics/Default (= 12.0.0)
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- FirebaseInstallations (~> 12.0.0)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- FirebaseAnalytics/Default (12.0.0):
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- FirebaseInstallations (~> 12.0.0)
|
||||||
|
- GoogleAppMeasurement/Default (= 12.0.0)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseCore (12.0.0):
|
- FirebaseCore (12.0.0):
|
||||||
- FirebaseCoreInternal (~> 12.0.0)
|
- FirebaseCoreInternal (~> 12.0.0)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/Logger (~> 8.1)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
|
- FirebaseCoreExtension (12.0.0):
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
- FirebaseCoreInternal (12.0.0):
|
- FirebaseCoreInternal (12.0.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- FirebaseCrashlytics (12.0.0):
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- FirebaseInstallations (~> 12.0.0)
|
||||||
|
- FirebaseRemoteConfigInterop (~> 12.0.0)
|
||||||
|
- FirebaseSessions (~> 12.0.0)
|
||||||
|
- GoogleDataTransport (~> 10.1)
|
||||||
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- PromisesObjC (~> 2.4)
|
||||||
- FirebaseInstallations (12.0.0):
|
- FirebaseInstallations (12.0.0):
|
||||||
- FirebaseCore (~> 12.0.0)
|
- FirebaseCore (~> 12.0.0)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
@@ -72,7 +112,19 @@ PODS:
|
|||||||
- GoogleUtilities/Reachability (~> 8.1)
|
- GoogleUtilities/Reachability (~> 8.1)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
|
- FirebaseRemoteConfigInterop (12.0.0)
|
||||||
|
- FirebaseSessions (12.0.0):
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- FirebaseCoreExtension (~> 12.0.0)
|
||||||
|
- FirebaseInstallations (~> 12.0.0)
|
||||||
|
- GoogleDataTransport (~> 10.1)
|
||||||
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- PromisesSwift (~> 2.1)
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_app_update (0.0.1):
|
||||||
|
- Flutter
|
||||||
- flutter_inappwebview_ios (0.0.1):
|
- flutter_inappwebview_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_inappwebview_ios/Core (= 0.0.1)
|
- flutter_inappwebview_ios/Core (= 0.0.1)
|
||||||
@@ -99,6 +151,32 @@ PODS:
|
|||||||
- gal (1.0.0):
|
- gal (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- GoogleAdsOnDeviceConversion (2.1.0):
|
||||||
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- GoogleAppMeasurement/Core (12.0.0):
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- GoogleAppMeasurement/Default (12.0.0):
|
||||||
|
- GoogleAdsOnDeviceConversion (= 2.1.0)
|
||||||
|
- GoogleAppMeasurement/Core (= 12.0.0)
|
||||||
|
- GoogleAppMeasurement/IdentitySupport (= 12.0.0)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- GoogleAppMeasurement/IdentitySupport (12.0.0):
|
||||||
|
- GoogleAppMeasurement/Core (= 12.0.0)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
- GoogleDataTransport (10.1.0):
|
- GoogleDataTransport (10.1.0):
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- PromisesObjC (~> 2.4)
|
- PromisesObjC (~> 2.4)
|
||||||
@@ -112,6 +190,9 @@ PODS:
|
|||||||
- GoogleUtilities/Logger (8.1.0):
|
- GoogleUtilities/Logger (8.1.0):
|
||||||
- GoogleUtilities/Environment
|
- GoogleUtilities/Environment
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilities/MethodSwizzler (8.1.0):
|
||||||
|
- GoogleUtilities/Logger
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Network (8.1.0):
|
- GoogleUtilities/Network (8.1.0):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- "GoogleUtilities/NSData+zlib"
|
- "GoogleUtilities/NSData+zlib"
|
||||||
@@ -160,6 +241,8 @@ PODS:
|
|||||||
- pointer_interceptor_ios (0.0.1):
|
- pointer_interceptor_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- PromisesObjC (2.4.0)
|
- PromisesObjC (2.4.0)
|
||||||
|
- PromisesSwift (2.4.0):
|
||||||
|
- PromisesObjC (= 2.4.0)
|
||||||
- receive_sharing_intent (1.8.1):
|
- receive_sharing_intent (1.8.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- record_ios (1.0.0):
|
- record_ios (1.0.0):
|
||||||
@@ -178,25 +261,25 @@ PODS:
|
|||||||
- sqflite_darwin (0.0.4):
|
- sqflite_darwin (0.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqlite3 (3.50.3):
|
- sqlite3 (3.50.4):
|
||||||
- sqlite3/common (= 3.50.3)
|
- sqlite3/common (= 3.50.4)
|
||||||
- sqlite3/common (3.50.3)
|
- sqlite3/common (3.50.4)
|
||||||
- sqlite3/dbstatvtab (3.50.3):
|
- sqlite3/dbstatvtab (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/fts5 (3.50.3):
|
- sqlite3/fts5 (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/math (3.50.3):
|
- sqlite3/math (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/perf-threadsafe (3.50.3):
|
- sqlite3/perf-threadsafe (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/rtree (3.50.3):
|
- sqlite3/rtree (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/session (3.50.3):
|
- sqlite3/session (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3_flutter_libs (0.0.1):
|
- sqlite3_flutter_libs (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqlite3 (~> 3.50.3)
|
- sqlite3 (~> 3.50.4)
|
||||||
- sqlite3/dbstatvtab
|
- sqlite3/dbstatvtab
|
||||||
- sqlite3/fts5
|
- sqlite3/fts5
|
||||||
- sqlite3/math
|
- sqlite3/math
|
||||||
@@ -220,9 +303,12 @@ DEPENDENCIES:
|
|||||||
- croppy (from `.symlinks/plugins/croppy/ios`)
|
- croppy (from `.symlinks/plugins/croppy/ios`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
|
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
|
||||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||||
|
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
|
||||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
|
||||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
||||||
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
|
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
@@ -262,16 +348,24 @@ SPEC REPOS:
|
|||||||
- DKImagePickerController
|
- DKImagePickerController
|
||||||
- DKPhotoGallery
|
- DKPhotoGallery
|
||||||
- Firebase
|
- Firebase
|
||||||
|
- FirebaseAnalytics
|
||||||
- FirebaseCore
|
- FirebaseCore
|
||||||
|
- FirebaseCoreExtension
|
||||||
- FirebaseCoreInternal
|
- FirebaseCoreInternal
|
||||||
|
- FirebaseCrashlytics
|
||||||
- FirebaseInstallations
|
- FirebaseInstallations
|
||||||
- FirebaseMessaging
|
- FirebaseMessaging
|
||||||
|
- FirebaseRemoteConfigInterop
|
||||||
|
- FirebaseSessions
|
||||||
|
- GoogleAdsOnDeviceConversion
|
||||||
|
- GoogleAppMeasurement
|
||||||
- GoogleDataTransport
|
- GoogleDataTransport
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
- Kingfisher
|
- Kingfisher
|
||||||
- nanopb
|
- nanopb
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
- PromisesObjC
|
- PromisesObjC
|
||||||
|
- PromisesSwift
|
||||||
- SAMKeychain
|
- SAMKeychain
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- sqlite3
|
- sqlite3
|
||||||
@@ -287,12 +381,18 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
file_picker:
|
file_picker:
|
||||||
:path: ".symlinks/plugins/file_picker/ios"
|
:path: ".symlinks/plugins/file_picker/ios"
|
||||||
|
firebase_analytics:
|
||||||
|
:path: ".symlinks/plugins/firebase_analytics/ios"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
:path: ".symlinks/plugins/firebase_core/ios"
|
:path: ".symlinks/plugins/firebase_core/ios"
|
||||||
|
firebase_crashlytics:
|
||||||
|
:path: ".symlinks/plugins/firebase_crashlytics/ios"
|
||||||
firebase_messaging:
|
firebase_messaging:
|
||||||
:path: ".symlinks/plugins/firebase_messaging/ios"
|
:path: ".symlinks/plugins/firebase_messaging/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_app_update:
|
||||||
|
:path: ".symlinks/plugins/flutter_app_update/ios"
|
||||||
flutter_inappwebview_ios:
|
flutter_inappwebview_ios:
|
||||||
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
|
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
|
||||||
flutter_keyboard_visibility:
|
flutter_keyboard_visibility:
|
||||||
@@ -365,13 +465,21 @@ SPEC CHECKSUMS:
|
|||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||||
Firebase: 800d487043c0557d9faed71477a38d9aafb08a41
|
Firebase: 800d487043c0557d9faed71477a38d9aafb08a41
|
||||||
|
firebase_analytics: cd56fc56f75c1df30a6ff5290cd56e230996a76d
|
||||||
firebase_core: 633e1851ffe1b9ab875f6467a4f574c79cef02e4
|
firebase_core: 633e1851ffe1b9ab875f6467a4f574c79cef02e4
|
||||||
|
firebase_crashlytics: 2c6c1a17900a38081d938330e9f48e60ec5b255d
|
||||||
firebase_messaging: d17feef781edc84ebefe62624fb384358ad96361
|
firebase_messaging: d17feef781edc84ebefe62624fb384358ad96361
|
||||||
|
FirebaseAnalytics: 6d790cd1b159b4eb61a99948df0934ce505a34f7
|
||||||
FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a
|
FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a
|
||||||
|
FirebaseCoreExtension: 639afb3de6abd611952be78a794c54a47fa0f361
|
||||||
FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55
|
FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55
|
||||||
|
FirebaseCrashlytics: db75aa0cab8d00f68406fa247c32fe17ade884d7
|
||||||
FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988
|
FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988
|
||||||
FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde
|
FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde
|
||||||
|
FirebaseRemoteConfigInterop: bfa0ea72ba3dc5af739777296424e46bd6f42613
|
||||||
|
FirebaseSessions: 4e784acda213108aafef536535cdfc03504acc42
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
|
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
|
||||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||||
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
|
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
|
||||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||||
@@ -381,6 +489,8 @@ SPEC CHECKSUMS:
|
|||||||
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
|
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
|
||||||
flutter_webrtc: 6f7da106613d52ade777d5b4875a43f48c28b457
|
flutter_webrtc: 6f7da106613d52ade777d5b4875a43f48c28b457
|
||||||
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
||||||
|
GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64
|
||||||
|
GoogleAppMeasurement: 8f6ab04ad6ae493b53fcf56bd26323fb2f1384f3
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
||||||
@@ -398,6 +508,7 @@ SPEC CHECKSUMS:
|
|||||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
pointer_interceptor_ios: ec847ef8b0915778bed2b2cef636f4d177fa8eed
|
pointer_interceptor_ios: ec847ef8b0915778bed2b2cef636f4d177fa8eed
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
|
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
||||||
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
|
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
|
||||||
record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b
|
record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b
|
||||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||||
@@ -406,8 +517,8 @@ SPEC CHECKSUMS:
|
|||||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||||
sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
|
sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
|
||||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||||
sqlite3: 83105acd294c9137c026e2da1931c30b4588ab81
|
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
|
||||||
sqlite3_flutter_libs: 616267f2fca40e9c6af8c5d82324e05667040b6e
|
sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
|
||||||
super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
|
super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||||
|
|||||||
@@ -439,6 +439,7 @@
|
|||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
8C0351B03869BBF493808288 /* [CP] Embed Pods Frameworks */,
|
8C0351B03869BBF493808288 /* [CP] Embed Pods Frameworks */,
|
||||||
5E7D6EF29B671AC7EDBA5649 /* [CP] Copy Pods Resources */,
|
5E7D6EF29B671AC7EDBA5649 /* [CP] Copy Pods Resources */,
|
||||||
|
E86CDE9D6464F4F52B910856 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -682,6 +683,24 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
};
|
};
|
||||||
|
E86CDE9D6464F4F52B910856 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\"";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:${PUB_CACHE}/bin:$HOME/.pub-cache/bin\"\n\nif [ -z \"$PODS_ROOT\" ] || [ ! -d \"$PODS_ROOT/FirebaseCrashlytics\" ]; then\n # Cannot use \"BUILD_DIR%/Build/*\" as per Firebase documentation, it points to \"flutter-project/build/ios/*\" path which doesn't have run script\n DERIVED_DATA_PATH=$(echo \"$BUILD_ROOT\" | sed -E 's|(.*DerivedData/[^/]+).*|\\1|')\n PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT=\"${DERIVED_DATA_PATH}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\"\nelse\n PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT=\"$PODS_ROOT/FirebaseCrashlytics/run\"\nfi\n\n# Command to upload symbols script used to upload symbols to Firebase server\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=\"$PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT\" --platform=ios --apple-project-path=\"${SRCROOT}\" --env-platform-name=\"${PLATFORM_NAME}\" --env-configuration=\"${CONFIGURATION}\" --env-project-dir=\"${PROJECT_DIR}\" --env-built-products-dir=\"${BUILT_PRODUCTS_DIR}\" --env-dwarf-dsym-folder-path=\"${DWARF_DSYM_FOLDER_PATH}\" --env-dwarf-dsym-file-name=\"${DWARF_DSYM_FILE_NAME}\" --env-infoplist-path=\"${INFOPLIST_PATH}\" --default-config=default\n";
|
||||||
|
};
|
||||||
E947029FCA058878F9B63890 /* [CP] Check Pods Manifest.lock */ = {
|
E947029FCA058878F9B63890 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class NotifyDelegate: UIResponder, UNUserNotificationCenterDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let serverUrl = UserDefaults.standard.getServerUrl()
|
let serverUrl = UserDefaults.standard.getServerUrl()
|
||||||
let url = "\(serverUrl)/chat/\(metadata["room_id"] ?? "")/messages"
|
let url = "\(serverUrl)/sphere/chat/\(metadata["room_id"] ?? "")/messages"
|
||||||
|
|
||||||
let parameters: [String: Any?] = [
|
let parameters: [String: Any?] = [
|
||||||
"content": textResponse.userText,
|
"content": textResponse.userText,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
func getAttachmentUrl(for identifier: String) -> String {
|
func getAttachmentUrl(for identifier: String) -> String {
|
||||||
let serverBaseUrl = "https://nt.solian.app"
|
let serverBaseUrl = "https://api.solian.app"
|
||||||
|
|
||||||
return identifier.starts(with: "http") ? identifier : "\(serverBaseUrl)/files/\(identifier)"
|
return identifier.starts(with: "http") ? identifier : "\(serverBaseUrl)/drive/files/\(identifier)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,10 +61,8 @@ class DefaultFirebaseOptions {
|
|||||||
messagingSenderId: '961776991058',
|
messagingSenderId: '961776991058',
|
||||||
projectId: 'solian-0x001',
|
projectId: 'solian-0x001',
|
||||||
storageBucket: 'solian-0x001.firebasestorage.app',
|
storageBucket: 'solian-0x001.firebasestorage.app',
|
||||||
androidClientId:
|
androidClientId: '961776991058-r4iv9qoio57ul7utbfpgfrda2etvtch8.apps.googleusercontent.com',
|
||||||
'961776991058-r4iv9qoio57ul7utbfpgfrda2etvtch8.apps.googleusercontent.com',
|
iosClientId: '961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com',
|
||||||
iosClientId:
|
|
||||||
'961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com',
|
|
||||||
iosBundleId: 'dev.solsynth.solian',
|
iosBundleId: 'dev.solsynth.solian',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -74,10 +72,8 @@ class DefaultFirebaseOptions {
|
|||||||
messagingSenderId: '961776991058',
|
messagingSenderId: '961776991058',
|
||||||
projectId: 'solian-0x001',
|
projectId: 'solian-0x001',
|
||||||
storageBucket: 'solian-0x001.firebasestorage.app',
|
storageBucket: 'solian-0x001.firebasestorage.app',
|
||||||
androidClientId:
|
androidClientId: '961776991058-r4iv9qoio57ul7utbfpgfrda2etvtch8.apps.googleusercontent.com',
|
||||||
'961776991058-r4iv9qoio57ul7utbfpgfrda2etvtch8.apps.googleusercontent.com',
|
iosClientId: '961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com',
|
||||||
iosClientId:
|
|
||||||
'961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com',
|
|
||||||
iosBundleId: 'dev.solsynth.solian',
|
iosBundleId: 'dev.solsynth.solian',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -90,4 +86,5 @@ class DefaultFirebaseOptions {
|
|||||||
storageBucket: 'solian-0x001.firebasestorage.app',
|
storageBucket: 'solian-0x001.firebasestorage.app',
|
||||||
measurementId: 'G-JD1YEG9D6F',
|
measurementId: 'G-JD1YEG9D6F',
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import 'dart:io';
|
|||||||
import 'package:croppy/croppy.dart';
|
import 'package:croppy/croppy.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart' hide TextDirection;
|
import 'package:easy_localization/easy_localization.dart' hide TextDirection;
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -30,7 +31,6 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface.
|
|||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
|
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
|
||||||
import 'package:island/services/update_service.dart';
|
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||||
@@ -62,6 +62,17 @@ void main() async {
|
|||||||
FirebaseMessaging.onBackgroundMessage(
|
FirebaseMessaging.onBackgroundMessage(
|
||||||
_firebaseMessagingBackgroundHandler,
|
_firebaseMessagingBackgroundHandler,
|
||||||
);
|
);
|
||||||
|
// Although previous if case checked this. Still check is web or not
|
||||||
|
// Otherwise the web platform will broke due to there is no Platform api on the web
|
||||||
|
// Skip crashlytics setup on debug mode to prevent unexpected report to firebase
|
||||||
|
if ((kIsWeb || !Platform.isWindows) && !kDebugMode) {
|
||||||
|
FlutterError.onError =
|
||||||
|
FirebaseCrashlytics.instance.recordFlutterFatalError;
|
||||||
|
PlatformDispatcher.instance.onError = (error, stack) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log("[SplashScreen] Firebase is ready!");
|
log("[SplashScreen] Firebase is ready!");
|
||||||
@@ -144,15 +155,6 @@ void main() async {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Schedule update check shortly after startup, when a context is available.
|
|
||||||
// Uses the global overlay key to obtain a BuildContext safely.
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
final ctx = globalOverlay.currentContext;
|
|
||||||
if (ctx != null) {
|
|
||||||
UpdateService().checkForUpdates(ctx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Router will be provided through Riverpod
|
// Router will be provided through Riverpod
|
||||||
@@ -181,6 +183,9 @@ class IslandApp extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
|
if (!kIsWeb && Platform.isLinux) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const channel = MethodChannel('dev.solsynth.solian/notifications');
|
const channel = MethodChannel('dev.solsynth.solian/notifications');
|
||||||
|
|
||||||
Future<void> handleInitialLink() async {
|
Future<void> handleInitialLink() async {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:island/models/auth.dart';
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
import 'package:island/models/wallet.dart';
|
import 'package:island/models/wallet.dart';
|
||||||
|
|
||||||
part 'user.freezed.dart';
|
part 'account.freezed.dart';
|
||||||
part 'user.g.dart';
|
part 'account.g.dart';
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class SnAccount with _$SnAccount {
|
sealed class SnAccount with _$SnAccount {
|
||||||
@@ -25,6 +26,32 @@ sealed class SnAccount with _$SnAccount {
|
|||||||
_$SnAccountFromJson(json);
|
_$SnAccountFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class ProfileLink with _$ProfileLink {
|
||||||
|
const factory ProfileLink({required String name, required String url}) =
|
||||||
|
_ProfileLink;
|
||||||
|
|
||||||
|
factory ProfileLink.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ProfileLinkFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProfileLinkConverter
|
||||||
|
implements JsonConverter<List<ProfileLink>, dynamic> {
|
||||||
|
const ProfileLinkConverter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ProfileLink> fromJson(dynamic json) {
|
||||||
|
return json is List<dynamic>
|
||||||
|
? json.map((e) => ProfileLink.fromJson(e)).cast<ProfileLink>().toList()
|
||||||
|
: <ProfileLink>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<dynamic> toJson(List<ProfileLink> object) {
|
||||||
|
return object.map((e) => e.toJson()).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class SnAccountProfile with _$SnAccountProfile {
|
sealed class SnAccountProfile with _$SnAccountProfile {
|
||||||
const factory SnAccountProfile({
|
const factory SnAccountProfile({
|
||||||
@@ -38,6 +65,7 @@ sealed class SnAccountProfile with _$SnAccountProfile {
|
|||||||
@Default('') String location,
|
@Default('') String location,
|
||||||
@Default('') String timeZone,
|
@Default('') String timeZone,
|
||||||
DateTime? birthday,
|
DateTime? birthday,
|
||||||
|
@ProfileLinkConverter() @Default([]) List<ProfileLink> links,
|
||||||
DateTime? lastSeenAt,
|
DateTime? lastSeenAt,
|
||||||
SnAccountBadge? activeBadge,
|
SnAccountBadge? activeBadge,
|
||||||
required int experience,
|
required int experience,
|
||||||
@@ -147,3 +175,36 @@ sealed class SnVerificationMark with _$SnVerificationMark {
|
|||||||
factory SnVerificationMark.fromJson(Map<String, dynamic> json) =>
|
factory SnVerificationMark.fromJson(Map<String, dynamic> json) =>
|
||||||
_$SnVerificationMarkFromJson(json);
|
_$SnVerificationMarkFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnAuthDevice with _$SnAuthDevice {
|
||||||
|
const factory SnAuthDevice({
|
||||||
|
required String id,
|
||||||
|
required String deviceId,
|
||||||
|
required String deviceName,
|
||||||
|
required String? deviceLabel,
|
||||||
|
required String accountId,
|
||||||
|
required int platform,
|
||||||
|
@Default(false) bool isCurrent,
|
||||||
|
}) = _SnAuthDevice;
|
||||||
|
|
||||||
|
factory SnAuthDevice.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnAuthDeviceFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnAuthDeviceWithChallenge with _$SnAuthDeviceWithChallenge {
|
||||||
|
const factory SnAuthDeviceWithChallenge({
|
||||||
|
required String id,
|
||||||
|
required String deviceId,
|
||||||
|
required String deviceName,
|
||||||
|
required String? deviceLabel,
|
||||||
|
required String accountId,
|
||||||
|
required int platform,
|
||||||
|
required List<SnAuthChallenge> challenges,
|
||||||
|
@Default(false) bool isCurrent,
|
||||||
|
}) = _SnAuthDeviceWithChallengee;
|
||||||
|
|
||||||
|
factory SnAuthDeviceWithChallenge.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnAuthDeviceWithChallengeFromJson(json);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
part of 'user.dart';
|
part of 'account.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// FreezedGenerator
|
// FreezedGenerator
|
||||||
@@ -347,10 +347,270 @@ $SnWalletSubscriptionRefCopyWith<$Res>? get perkSubscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$ProfileLink {
|
||||||
|
|
||||||
|
String get name; String get url;
|
||||||
|
/// Create a copy of ProfileLink
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$ProfileLinkCopyWith<ProfileLink> get copyWith => _$ProfileLinkCopyWithImpl<ProfileLink>(this as ProfileLink, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this ProfileLink to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProfileLink&&(identical(other.name, name) || other.name == name)&&(identical(other.url, url) || other.url == url));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,name,url);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ProfileLink(name: $name, url: $url)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $ProfileLinkCopyWith<$Res> {
|
||||||
|
factory $ProfileLinkCopyWith(ProfileLink value, $Res Function(ProfileLink) _then) = _$ProfileLinkCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String name, String url
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$ProfileLinkCopyWithImpl<$Res>
|
||||||
|
implements $ProfileLinkCopyWith<$Res> {
|
||||||
|
_$ProfileLinkCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final ProfileLink _self;
|
||||||
|
final $Res Function(ProfileLink) _then;
|
||||||
|
|
||||||
|
/// Create a copy of ProfileLink
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? url = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [ProfileLink].
|
||||||
|
extension ProfileLinkPatterns on ProfileLink {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProfileLink value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _ProfileLink() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProfileLink value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _ProfileLink():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProfileLink value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _ProfileLink() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name, String url)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _ProfileLink() when $default != null:
|
||||||
|
return $default(_that.name,_that.url);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name, String url) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _ProfileLink():
|
||||||
|
return $default(_that.name,_that.url);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name, String url)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _ProfileLink() when $default != null:
|
||||||
|
return $default(_that.name,_that.url);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _ProfileLink implements ProfileLink {
|
||||||
|
const _ProfileLink({required this.name, required this.url});
|
||||||
|
factory _ProfileLink.fromJson(Map<String, dynamic> json) => _$ProfileLinkFromJson(json);
|
||||||
|
|
||||||
|
@override final String name;
|
||||||
|
@override final String url;
|
||||||
|
|
||||||
|
/// Create a copy of ProfileLink
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$ProfileLinkCopyWith<_ProfileLink> get copyWith => __$ProfileLinkCopyWithImpl<_ProfileLink>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$ProfileLinkToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProfileLink&&(identical(other.name, name) || other.name == name)&&(identical(other.url, url) || other.url == url));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,name,url);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ProfileLink(name: $name, url: $url)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$ProfileLinkCopyWith<$Res> implements $ProfileLinkCopyWith<$Res> {
|
||||||
|
factory _$ProfileLinkCopyWith(_ProfileLink value, $Res Function(_ProfileLink) _then) = __$ProfileLinkCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String name, String url
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$ProfileLinkCopyWithImpl<$Res>
|
||||||
|
implements _$ProfileLinkCopyWith<$Res> {
|
||||||
|
__$ProfileLinkCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _ProfileLink _self;
|
||||||
|
final $Res Function(_ProfileLink) _then;
|
||||||
|
|
||||||
|
/// Create a copy of ProfileLink
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? url = null,}) {
|
||||||
|
return _then(_ProfileLink(
|
||||||
|
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnAccountProfile {
|
mixin _$SnAccountProfile {
|
||||||
|
|
||||||
String get id; String get firstName; String get middleName; String get lastName; String get bio; String get gender; String get pronouns; String get location; String get timeZone; DateTime? get birthday; DateTime? get lastSeenAt; SnAccountBadge? get activeBadge; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
String get id; String get firstName; String get middleName; String get lastName; String get bio; String get gender; String get pronouns; String get location; String get timeZone; DateTime? get birthday;@ProfileLinkConverter() List<ProfileLink> get links; DateTime? get lastSeenAt; SnAccountBadge? get activeBadge; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||||
/// Create a copy of SnAccountProfile
|
/// Create a copy of SnAccountProfile
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -363,16 +623,16 @@ $SnAccountProfileCopyWith<SnAccountProfile> get copyWith => _$SnAccountProfileCo
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&const DeepCollectionEquality().equals(other.links, links)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
|
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,const DeepCollectionEquality().hash(links),lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, links: $links, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -383,7 +643,7 @@ abstract mixin class $SnAccountProfileCopyWith<$Res> {
|
|||||||
factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl;
|
factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday,@ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -400,7 +660,7 @@ class _$SnAccountProfileCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnAccountProfile
|
/// Create a copy of SnAccountProfile
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? links = null,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
|
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -412,7 +672,8 @@ as String,pronouns: null == pronouns ? _self.pronouns : pronouns // ignore: cast
|
|||||||
as String,location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
as String,location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
||||||
as String,timeZone: null == timeZone ? _self.timeZone : timeZone // ignore: cast_nullable_to_non_nullable
|
as String,timeZone: null == timeZone ? _self.timeZone : timeZone // ignore: cast_nullable_to_non_nullable
|
||||||
as String,birthday: freezed == birthday ? _self.birthday : birthday // ignore: cast_nullable_to_non_nullable
|
as String,birthday: freezed == birthday ? _self.birthday : birthday // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : lastSeenAt // ignore: cast_nullable_to_non_nullable
|
as DateTime?,links: null == links ? _self.links : links // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<ProfileLink>,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : lastSeenAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable
|
as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable
|
||||||
as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable
|
as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable
|
||||||
as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable
|
as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -553,10 +814,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAccountProfile() when $default != null:
|
case _SnAccountProfile() when $default != null:
|
||||||
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -574,10 +835,10 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAccountProfile():
|
case _SnAccountProfile():
|
||||||
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -591,10 +852,10 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, @ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAccountProfile() when $default != null:
|
case _SnAccountProfile() when $default != null:
|
||||||
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -606,7 +867,7 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnAccountProfile implements SnAccountProfile {
|
class _SnAccountProfile implements SnAccountProfile {
|
||||||
const _SnAccountProfile({required this.id, this.firstName = '', this.middleName = '', this.lastName = '', this.bio = '', this.gender = '', this.pronouns = '', this.location = '', this.timeZone = '', this.birthday, this.lastSeenAt, this.activeBadge, required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
const _SnAccountProfile({required this.id, this.firstName = '', this.middleName = '', this.lastName = '', this.bio = '', this.gender = '', this.pronouns = '', this.location = '', this.timeZone = '', this.birthday, @ProfileLinkConverter() final List<ProfileLink> links = const [], this.lastSeenAt, this.activeBadge, required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.createdAt, required this.updatedAt, required this.deletedAt}): _links = links;
|
||||||
factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => _$SnAccountProfileFromJson(json);
|
factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => _$SnAccountProfileFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@@ -619,6 +880,13 @@ class _SnAccountProfile implements SnAccountProfile {
|
|||||||
@override@JsonKey() final String location;
|
@override@JsonKey() final String location;
|
||||||
@override@JsonKey() final String timeZone;
|
@override@JsonKey() final String timeZone;
|
||||||
@override final DateTime? birthday;
|
@override final DateTime? birthday;
|
||||||
|
final List<ProfileLink> _links;
|
||||||
|
@override@JsonKey()@ProfileLinkConverter() List<ProfileLink> get links {
|
||||||
|
if (_links is EqualUnmodifiableListView) return _links;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_links);
|
||||||
|
}
|
||||||
|
|
||||||
@override final DateTime? lastSeenAt;
|
@override final DateTime? lastSeenAt;
|
||||||
@override final SnAccountBadge? activeBadge;
|
@override final SnAccountBadge? activeBadge;
|
||||||
@override final int experience;
|
@override final int experience;
|
||||||
@@ -644,16 +912,16 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&const DeepCollectionEquality().equals(other._links, _links)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
|
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,const DeepCollectionEquality().hash(_links),lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, links: $links, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -664,7 +932,7 @@ abstract mixin class _$SnAccountProfileCopyWith<$Res> implements $SnAccountProfi
|
|||||||
factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl;
|
factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday,@ProfileLinkConverter() List<ProfileLink> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -681,7 +949,7 @@ class __$SnAccountProfileCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnAccountProfile
|
/// Create a copy of SnAccountProfile
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? links = null,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
return _then(_SnAccountProfile(
|
return _then(_SnAccountProfile(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
|
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -693,7 +961,8 @@ as String,pronouns: null == pronouns ? _self.pronouns : pronouns // ignore: cast
|
|||||||
as String,location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
as String,location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
||||||
as String,timeZone: null == timeZone ? _self.timeZone : timeZone // ignore: cast_nullable_to_non_nullable
|
as String,timeZone: null == timeZone ? _self.timeZone : timeZone // ignore: cast_nullable_to_non_nullable
|
||||||
as String,birthday: freezed == birthday ? _self.birthday : birthday // ignore: cast_nullable_to_non_nullable
|
as String,birthday: freezed == birthday ? _self.birthday : birthday // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : lastSeenAt // ignore: cast_nullable_to_non_nullable
|
as DateTime?,links: null == links ? _self._links : links // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<ProfileLink>,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : lastSeenAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable
|
as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable
|
||||||
as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable
|
as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable
|
||||||
as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable
|
as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -2183,6 +2452,572 @@ as String?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnAuthDevice {
|
||||||
|
|
||||||
|
String get id; String get deviceId; String get deviceName; String? get deviceLabel; String get accountId; int get platform; bool get isCurrent;
|
||||||
|
/// Create a copy of SnAuthDevice
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAuthDeviceCopyWith<SnAuthDevice> get copyWith => _$SnAuthDeviceCopyWithImpl<SnAuthDevice>(this as SnAuthDevice, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnAuthDevice to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAuthDevice&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&(identical(other.deviceLabel, deviceLabel) || other.deviceLabel == deviceLabel)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.platform, platform) || other.platform == platform)&&(identical(other.isCurrent, isCurrent) || other.isCurrent == isCurrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,deviceId,deviceName,deviceLabel,accountId,platform,isCurrent);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnAuthDevice(id: $id, deviceId: $deviceId, deviceName: $deviceName, deviceLabel: $deviceLabel, accountId: $accountId, platform: $platform, isCurrent: $isCurrent)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnAuthDeviceCopyWith<$Res> {
|
||||||
|
factory $SnAuthDeviceCopyWith(SnAuthDevice value, $Res Function(SnAuthDevice) _then) = _$SnAuthDeviceCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, bool isCurrent
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnAuthDeviceCopyWithImpl<$Res>
|
||||||
|
implements $SnAuthDeviceCopyWith<$Res> {
|
||||||
|
_$SnAuthDeviceCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnAuthDevice _self;
|
||||||
|
final $Res Function(SnAuthDevice) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnAuthDevice
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceId = null,Object? deviceName = null,Object? deviceLabel = freezed,Object? accountId = null,Object? platform = null,Object? isCurrent = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceLabel: freezed == deviceLabel ? _self.deviceLabel : deviceLabel // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,isCurrent: null == isCurrent ? _self.isCurrent : isCurrent // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnAuthDevice].
|
||||||
|
extension SnAuthDevicePatterns on SnAuthDevice {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnAuthDevice value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDevice() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnAuthDevice value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDevice():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnAuthDevice value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDevice() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, bool isCurrent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDevice() when $default != null:
|
||||||
|
return $default(_that.id,_that.deviceId,_that.deviceName,_that.deviceLabel,_that.accountId,_that.platform,_that.isCurrent);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, bool isCurrent) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDevice():
|
||||||
|
return $default(_that.id,_that.deviceId,_that.deviceName,_that.deviceLabel,_that.accountId,_that.platform,_that.isCurrent);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, bool isCurrent)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDevice() when $default != null:
|
||||||
|
return $default(_that.id,_that.deviceId,_that.deviceName,_that.deviceLabel,_that.accountId,_that.platform,_that.isCurrent);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnAuthDevice implements SnAuthDevice {
|
||||||
|
const _SnAuthDevice({required this.id, required this.deviceId, required this.deviceName, required this.deviceLabel, required this.accountId, required this.platform, this.isCurrent = false});
|
||||||
|
factory _SnAuthDevice.fromJson(Map<String, dynamic> json) => _$SnAuthDeviceFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final String deviceId;
|
||||||
|
@override final String deviceName;
|
||||||
|
@override final String? deviceLabel;
|
||||||
|
@override final String accountId;
|
||||||
|
@override final int platform;
|
||||||
|
@override@JsonKey() final bool isCurrent;
|
||||||
|
|
||||||
|
/// Create a copy of SnAuthDevice
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnAuthDeviceCopyWith<_SnAuthDevice> get copyWith => __$SnAuthDeviceCopyWithImpl<_SnAuthDevice>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnAuthDeviceToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAuthDevice&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&(identical(other.deviceLabel, deviceLabel) || other.deviceLabel == deviceLabel)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.platform, platform) || other.platform == platform)&&(identical(other.isCurrent, isCurrent) || other.isCurrent == isCurrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,deviceId,deviceName,deviceLabel,accountId,platform,isCurrent);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnAuthDevice(id: $id, deviceId: $deviceId, deviceName: $deviceName, deviceLabel: $deviceLabel, accountId: $accountId, platform: $platform, isCurrent: $isCurrent)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnAuthDeviceCopyWith<$Res> implements $SnAuthDeviceCopyWith<$Res> {
|
||||||
|
factory _$SnAuthDeviceCopyWith(_SnAuthDevice value, $Res Function(_SnAuthDevice) _then) = __$SnAuthDeviceCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, bool isCurrent
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnAuthDeviceCopyWithImpl<$Res>
|
||||||
|
implements _$SnAuthDeviceCopyWith<$Res> {
|
||||||
|
__$SnAuthDeviceCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnAuthDevice _self;
|
||||||
|
final $Res Function(_SnAuthDevice) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnAuthDevice
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceId = null,Object? deviceName = null,Object? deviceLabel = freezed,Object? accountId = null,Object? platform = null,Object? isCurrent = null,}) {
|
||||||
|
return _then(_SnAuthDevice(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceLabel: freezed == deviceLabel ? _self.deviceLabel : deviceLabel // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,isCurrent: null == isCurrent ? _self.isCurrent : isCurrent // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SnAuthDeviceWithChallenge _$SnAuthDeviceWithChallengeFromJson(
|
||||||
|
Map<String, dynamic> json
|
||||||
|
) {
|
||||||
|
return _SnAuthDeviceWithChallengee.fromJson(
|
||||||
|
json
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnAuthDeviceWithChallenge {
|
||||||
|
|
||||||
|
String get id; String get deviceId; String get deviceName; String? get deviceLabel; String get accountId; int get platform; List<SnAuthChallenge> get challenges; bool get isCurrent;
|
||||||
|
/// Create a copy of SnAuthDeviceWithChallenge
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAuthDeviceWithChallengeCopyWith<SnAuthDeviceWithChallenge> get copyWith => _$SnAuthDeviceWithChallengeCopyWithImpl<SnAuthDeviceWithChallenge>(this as SnAuthDeviceWithChallenge, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnAuthDeviceWithChallenge to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAuthDeviceWithChallenge&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&(identical(other.deviceLabel, deviceLabel) || other.deviceLabel == deviceLabel)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.platform, platform) || other.platform == platform)&&const DeepCollectionEquality().equals(other.challenges, challenges)&&(identical(other.isCurrent, isCurrent) || other.isCurrent == isCurrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,deviceId,deviceName,deviceLabel,accountId,platform,const DeepCollectionEquality().hash(challenges),isCurrent);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnAuthDeviceWithChallenge(id: $id, deviceId: $deviceId, deviceName: $deviceName, deviceLabel: $deviceLabel, accountId: $accountId, platform: $platform, challenges: $challenges, isCurrent: $isCurrent)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnAuthDeviceWithChallengeCopyWith<$Res> {
|
||||||
|
factory $SnAuthDeviceWithChallengeCopyWith(SnAuthDeviceWithChallenge value, $Res Function(SnAuthDeviceWithChallenge) _then) = _$SnAuthDeviceWithChallengeCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, List<SnAuthChallenge> challenges, bool isCurrent
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnAuthDeviceWithChallengeCopyWithImpl<$Res>
|
||||||
|
implements $SnAuthDeviceWithChallengeCopyWith<$Res> {
|
||||||
|
_$SnAuthDeviceWithChallengeCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnAuthDeviceWithChallenge _self;
|
||||||
|
final $Res Function(SnAuthDeviceWithChallenge) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnAuthDeviceWithChallenge
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceId = null,Object? deviceName = null,Object? deviceLabel = freezed,Object? accountId = null,Object? platform = null,Object? challenges = null,Object? isCurrent = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceLabel: freezed == deviceLabel ? _self.deviceLabel : deviceLabel // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,challenges: null == challenges ? _self.challenges : challenges // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnAuthChallenge>,isCurrent: null == isCurrent ? _self.isCurrent : isCurrent // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnAuthDeviceWithChallenge].
|
||||||
|
extension SnAuthDeviceWithChallengePatterns on SnAuthDeviceWithChallenge {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnAuthDeviceWithChallengee value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDeviceWithChallengee() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnAuthDeviceWithChallengee value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDeviceWithChallengee():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnAuthDeviceWithChallengee value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDeviceWithChallengee() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, List<SnAuthChallenge> challenges, bool isCurrent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDeviceWithChallengee() when $default != null:
|
||||||
|
return $default(_that.id,_that.deviceId,_that.deviceName,_that.deviceLabel,_that.accountId,_that.platform,_that.challenges,_that.isCurrent);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, List<SnAuthChallenge> challenges, bool isCurrent) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDeviceWithChallengee():
|
||||||
|
return $default(_that.id,_that.deviceId,_that.deviceName,_that.deviceLabel,_that.accountId,_that.platform,_that.challenges,_that.isCurrent);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, List<SnAuthChallenge> challenges, bool isCurrent)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnAuthDeviceWithChallengee() when $default != null:
|
||||||
|
return $default(_that.id,_that.deviceId,_that.deviceName,_that.deviceLabel,_that.accountId,_that.platform,_that.challenges,_that.isCurrent);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnAuthDeviceWithChallengee implements SnAuthDeviceWithChallenge {
|
||||||
|
const _SnAuthDeviceWithChallengee({required this.id, required this.deviceId, required this.deviceName, required this.deviceLabel, required this.accountId, required this.platform, required final List<SnAuthChallenge> challenges, this.isCurrent = false}): _challenges = challenges;
|
||||||
|
factory _SnAuthDeviceWithChallengee.fromJson(Map<String, dynamic> json) => _$SnAuthDeviceWithChallengeeFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final String deviceId;
|
||||||
|
@override final String deviceName;
|
||||||
|
@override final String? deviceLabel;
|
||||||
|
@override final String accountId;
|
||||||
|
@override final int platform;
|
||||||
|
final List<SnAuthChallenge> _challenges;
|
||||||
|
@override List<SnAuthChallenge> get challenges {
|
||||||
|
if (_challenges is EqualUnmodifiableListView) return _challenges;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_challenges);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override@JsonKey() final bool isCurrent;
|
||||||
|
|
||||||
|
/// Create a copy of SnAuthDeviceWithChallenge
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnAuthDeviceWithChallengeeCopyWith<_SnAuthDeviceWithChallengee> get copyWith => __$SnAuthDeviceWithChallengeeCopyWithImpl<_SnAuthDeviceWithChallengee>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnAuthDeviceWithChallengeeToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAuthDeviceWithChallengee&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&(identical(other.deviceLabel, deviceLabel) || other.deviceLabel == deviceLabel)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.platform, platform) || other.platform == platform)&&const DeepCollectionEquality().equals(other._challenges, _challenges)&&(identical(other.isCurrent, isCurrent) || other.isCurrent == isCurrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,deviceId,deviceName,deviceLabel,accountId,platform,const DeepCollectionEquality().hash(_challenges),isCurrent);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnAuthDeviceWithChallenge(id: $id, deviceId: $deviceId, deviceName: $deviceName, deviceLabel: $deviceLabel, accountId: $accountId, platform: $platform, challenges: $challenges, isCurrent: $isCurrent)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnAuthDeviceWithChallengeeCopyWith<$Res> implements $SnAuthDeviceWithChallengeCopyWith<$Res> {
|
||||||
|
factory _$SnAuthDeviceWithChallengeeCopyWith(_SnAuthDeviceWithChallengee value, $Res Function(_SnAuthDeviceWithChallengee) _then) = __$SnAuthDeviceWithChallengeeCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String deviceId, String deviceName, String? deviceLabel, String accountId, int platform, List<SnAuthChallenge> challenges, bool isCurrent
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnAuthDeviceWithChallengeeCopyWithImpl<$Res>
|
||||||
|
implements _$SnAuthDeviceWithChallengeeCopyWith<$Res> {
|
||||||
|
__$SnAuthDeviceWithChallengeeCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnAuthDeviceWithChallengee _self;
|
||||||
|
final $Res Function(_SnAuthDeviceWithChallengee) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnAuthDeviceWithChallenge
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceId = null,Object? deviceName = null,Object? deviceLabel = freezed,Object? accountId = null,Object? platform = null,Object? challenges = null,Object? isCurrent = null,}) {
|
||||||
|
return _then(_SnAuthDeviceWithChallengee(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,deviceLabel: freezed == deviceLabel ? _self.deviceLabel : deviceLabel // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,challenges: null == challenges ? _self._challenges : challenges // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnAuthChallenge>,isCurrent: null == isCurrent ? _self.isCurrent : isCurrent // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dart format on
|
// dart format on
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'user.dart';
|
part of 'account.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
@@ -47,6 +47,12 @@ Map<String, dynamic> _$SnAccountToJson(_SnAccount instance) =>
|
|||||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_ProfileLink _$ProfileLinkFromJson(Map<String, dynamic> json) =>
|
||||||
|
_ProfileLink(name: json['name'] as String, url: json['url'] as String);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$ProfileLinkToJson(_ProfileLink instance) =>
|
||||||
|
<String, dynamic>{'name': instance.name, 'url': instance.url};
|
||||||
|
|
||||||
_SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
|
_SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
|
||||||
_SnAccountProfile(
|
_SnAccountProfile(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
@@ -62,6 +68,10 @@ _SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
|
|||||||
json['birthday'] == null
|
json['birthday'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['birthday'] as String),
|
: DateTime.parse(json['birthday'] as String),
|
||||||
|
links:
|
||||||
|
json['links'] == null
|
||||||
|
? const []
|
||||||
|
: const ProfileLinkConverter().fromJson(json['links']),
|
||||||
lastSeenAt:
|
lastSeenAt:
|
||||||
json['last_seen_at'] == null
|
json['last_seen_at'] == null
|
||||||
? null
|
? null
|
||||||
@@ -111,6 +121,7 @@ Map<String, dynamic> _$SnAccountProfileToJson(_SnAccountProfile instance) =>
|
|||||||
'location': instance.location,
|
'location': instance.location,
|
||||||
'time_zone': instance.timeZone,
|
'time_zone': instance.timeZone,
|
||||||
'birthday': instance.birthday?.toIso8601String(),
|
'birthday': instance.birthday?.toIso8601String(),
|
||||||
|
'links': const ProfileLinkConverter().toJson(instance.links),
|
||||||
'last_seen_at': instance.lastSeenAt?.toIso8601String(),
|
'last_seen_at': instance.lastSeenAt?.toIso8601String(),
|
||||||
'active_badge': instance.activeBadge?.toJson(),
|
'active_badge': instance.activeBadge?.toJson(),
|
||||||
'experience': instance.experience,
|
'experience': instance.experience,
|
||||||
@@ -286,3 +297,54 @@ Map<String, dynamic> _$SnVerificationMarkToJson(_SnVerificationMark instance) =>
|
|||||||
'description': instance.description,
|
'description': instance.description,
|
||||||
'verified_by': instance.verifiedBy,
|
'verified_by': instance.verifiedBy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_SnAuthDevice _$SnAuthDeviceFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnAuthDevice(
|
||||||
|
id: json['id'] as String,
|
||||||
|
deviceId: json['device_id'] as String,
|
||||||
|
deviceName: json['device_name'] as String,
|
||||||
|
deviceLabel: json['device_label'] as String?,
|
||||||
|
accountId: json['account_id'] as String,
|
||||||
|
platform: (json['platform'] as num).toInt(),
|
||||||
|
isCurrent: json['is_current'] as bool? ?? false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnAuthDeviceToJson(_SnAuthDevice instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'device_id': instance.deviceId,
|
||||||
|
'device_name': instance.deviceName,
|
||||||
|
'device_label': instance.deviceLabel,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'platform': instance.platform,
|
||||||
|
'is_current': instance.isCurrent,
|
||||||
|
};
|
||||||
|
|
||||||
|
_SnAuthDeviceWithChallengee _$SnAuthDeviceWithChallengeeFromJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
|
) => _SnAuthDeviceWithChallengee(
|
||||||
|
id: json['id'] as String,
|
||||||
|
deviceId: json['device_id'] as String,
|
||||||
|
deviceName: json['device_name'] as String,
|
||||||
|
deviceLabel: json['device_label'] as String?,
|
||||||
|
accountId: json['account_id'] as String,
|
||||||
|
platform: (json['platform'] as num).toInt(),
|
||||||
|
challenges:
|
||||||
|
(json['challenges'] as List<dynamic>)
|
||||||
|
.map((e) => SnAuthChallenge.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
isCurrent: json['is_current'] as bool? ?? false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnAuthDeviceWithChallengeeToJson(
|
||||||
|
_SnAuthDeviceWithChallengee instance,
|
||||||
|
) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'device_id': instance.deviceId,
|
||||||
|
'device_name': instance.deviceName,
|
||||||
|
'device_label': instance.deviceLabel,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'platform': instance.platform,
|
||||||
|
'challenges': instance.challenges.map((e) => e.toJson()).toList(),
|
||||||
|
'is_current': instance.isCurrent,
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
|
|
||||||
part 'activity.freezed.dart';
|
part 'activity.freezed.dart';
|
||||||
part 'activity.g.dart';
|
part 'activity.g.dart';
|
||||||
|
|||||||
@@ -19,14 +19,12 @@ sealed class SnAuthChallenge with _$SnAuthChallenge {
|
|||||||
required int stepRemain,
|
required int stepRemain,
|
||||||
required int stepTotal,
|
required int stepTotal,
|
||||||
required int failedAttempts,
|
required int failedAttempts,
|
||||||
required int platform,
|
|
||||||
required int type,
|
required int type,
|
||||||
required List<String> blacklistFactors,
|
required List<String> blacklistFactors,
|
||||||
required List<dynamic> audiences,
|
required List<dynamic> audiences,
|
||||||
required List<dynamic> scopes,
|
required List<dynamic> scopes,
|
||||||
required String ipAddress,
|
required String ipAddress,
|
||||||
required String userAgent,
|
required String userAgent,
|
||||||
required String deviceId,
|
|
||||||
required String? nonce,
|
required String? nonce,
|
||||||
required String? location,
|
required String? location,
|
||||||
required String accountId,
|
required String accountId,
|
||||||
@@ -76,22 +74,6 @@ sealed class SnAuthFactor with _$SnAuthFactor {
|
|||||||
_$SnAuthFactorFromJson(json);
|
_$SnAuthFactorFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
|
||||||
sealed class SnAuthDevice with _$SnAuthDevice {
|
|
||||||
const factory SnAuthDevice({
|
|
||||||
required dynamic label,
|
|
||||||
required String userAgent,
|
|
||||||
required String deviceId,
|
|
||||||
required int platform,
|
|
||||||
required List<SnAuthSession> sessions,
|
|
||||||
// Not from backend, used for UI
|
|
||||||
@Default(false) bool isCurrent,
|
|
||||||
}) = _SnAuthDevice;
|
|
||||||
|
|
||||||
factory SnAuthDevice.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$SnAuthDeviceFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class SnAccountConnection with _$SnAccountConnection {
|
sealed class SnAccountConnection with _$SnAccountConnection {
|
||||||
const factory SnAccountConnection({
|
const factory SnAccountConnection({
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ as String,
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnAuthChallenge {
|
mixin _$SnAuthChallenge {
|
||||||
|
|
||||||
String get id; DateTime get expiredAt; int get stepRemain; int get stepTotal; int get failedAttempts; int get platform; int get type; List<String> get blacklistFactors; List<dynamic> get audiences; List<dynamic> get scopes; String get ipAddress; String get userAgent; String get deviceId; String? get nonce; String? get location; String get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
String get id; DateTime get expiredAt; int get stepRemain; int get stepTotal; int get failedAttempts; int get type; List<String> get blacklistFactors; List<dynamic> get audiences; List<dynamic> get scopes; String get ipAddress; String get userAgent; String? get nonce; String? get location; String get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||||
/// Create a copy of SnAuthChallenge
|
/// Create a copy of SnAuthChallenge
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -285,16 +285,16 @@ $SnAuthChallengeCopyWith<SnAuthChallenge> get copyWith => _$SnAuthChallengeCopyW
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAuthChallenge&&(identical(other.id, id) || other.id == id)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.stepRemain, stepRemain) || other.stepRemain == stepRemain)&&(identical(other.stepTotal, stepTotal) || other.stepTotal == stepTotal)&&(identical(other.failedAttempts, failedAttempts) || other.failedAttempts == failedAttempts)&&(identical(other.platform, platform) || other.platform == platform)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.blacklistFactors, blacklistFactors)&&const DeepCollectionEquality().equals(other.audiences, audiences)&&const DeepCollectionEquality().equals(other.scopes, scopes)&&(identical(other.ipAddress, ipAddress) || other.ipAddress == ipAddress)&&(identical(other.userAgent, userAgent) || other.userAgent == userAgent)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&(identical(other.location, location) || other.location == location)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAuthChallenge&&(identical(other.id, id) || other.id == id)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.stepRemain, stepRemain) || other.stepRemain == stepRemain)&&(identical(other.stepTotal, stepTotal) || other.stepTotal == stepTotal)&&(identical(other.failedAttempts, failedAttempts) || other.failedAttempts == failedAttempts)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.blacklistFactors, blacklistFactors)&&const DeepCollectionEquality().equals(other.audiences, audiences)&&const DeepCollectionEquality().equals(other.scopes, scopes)&&(identical(other.ipAddress, ipAddress) || other.ipAddress == ipAddress)&&(identical(other.userAgent, userAgent) || other.userAgent == userAgent)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&(identical(other.location, location) || other.location == location)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hashAll([runtimeType,id,expiredAt,stepRemain,stepTotal,failedAttempts,platform,type,const DeepCollectionEquality().hash(blacklistFactors),const DeepCollectionEquality().hash(audiences),const DeepCollectionEquality().hash(scopes),ipAddress,userAgent,deviceId,nonce,location,accountId,createdAt,updatedAt,deletedAt]);
|
int get hashCode => Object.hash(runtimeType,id,expiredAt,stepRemain,stepTotal,failedAttempts,type,const DeepCollectionEquality().hash(blacklistFactors),const DeepCollectionEquality().hash(audiences),const DeepCollectionEquality().hash(scopes),ipAddress,userAgent,nonce,location,accountId,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnAuthChallenge(id: $id, expiredAt: $expiredAt, stepRemain: $stepRemain, stepTotal: $stepTotal, failedAttempts: $failedAttempts, platform: $platform, type: $type, blacklistFactors: $blacklistFactors, audiences: $audiences, scopes: $scopes, ipAddress: $ipAddress, userAgent: $userAgent, deviceId: $deviceId, nonce: $nonce, location: $location, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
return 'SnAuthChallenge(id: $id, expiredAt: $expiredAt, stepRemain: $stepRemain, stepTotal: $stepTotal, failedAttempts: $failedAttempts, type: $type, blacklistFactors: $blacklistFactors, audiences: $audiences, scopes: $scopes, ipAddress: $ipAddress, userAgent: $userAgent, nonce: $nonce, location: $location, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -305,7 +305,7 @@ abstract mixin class $SnAuthChallengeCopyWith<$Res> {
|
|||||||
factory $SnAuthChallengeCopyWith(SnAuthChallenge value, $Res Function(SnAuthChallenge) _then) = _$SnAuthChallengeCopyWithImpl;
|
factory $SnAuthChallengeCopyWith(SnAuthChallenge value, $Res Function(SnAuthChallenge) _then) = _$SnAuthChallengeCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int platform, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String deviceId, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -322,21 +322,19 @@ class _$SnAuthChallengeCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnAuthChallenge
|
/// Create a copy of SnAuthChallenge
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? expiredAt = null,Object? stepRemain = null,Object? stepTotal = null,Object? failedAttempts = null,Object? platform = null,Object? type = null,Object? blacklistFactors = null,Object? audiences = null,Object? scopes = null,Object? ipAddress = null,Object? userAgent = null,Object? deviceId = null,Object? nonce = freezed,Object? location = freezed,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? expiredAt = null,Object? stepRemain = null,Object? stepTotal = null,Object? failedAttempts = null,Object? type = null,Object? blacklistFactors = null,Object? audiences = null,Object? scopes = null,Object? ipAddress = null,Object? userAgent = null,Object? nonce = freezed,Object? location = freezed,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,expiredAt: null == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
|
as String,expiredAt: null == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,stepRemain: null == stepRemain ? _self.stepRemain : stepRemain // ignore: cast_nullable_to_non_nullable
|
as DateTime,stepRemain: null == stepRemain ? _self.stepRemain : stepRemain // ignore: cast_nullable_to_non_nullable
|
||||||
as int,stepTotal: null == stepTotal ? _self.stepTotal : stepTotal // ignore: cast_nullable_to_non_nullable
|
as int,stepTotal: null == stepTotal ? _self.stepTotal : stepTotal // ignore: cast_nullable_to_non_nullable
|
||||||
as int,failedAttempts: null == failedAttempts ? _self.failedAttempts : failedAttempts // ignore: cast_nullable_to_non_nullable
|
as int,failedAttempts: null == failedAttempts ? _self.failedAttempts : failedAttempts // ignore: cast_nullable_to_non_nullable
|
||||||
as int,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
as int,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as int,blacklistFactors: null == blacklistFactors ? _self.blacklistFactors : blacklistFactors // ignore: cast_nullable_to_non_nullable
|
as int,blacklistFactors: null == blacklistFactors ? _self.blacklistFactors : blacklistFactors // ignore: cast_nullable_to_non_nullable
|
||||||
as List<String>,audiences: null == audiences ? _self.audiences : audiences // ignore: cast_nullable_to_non_nullable
|
as List<String>,audiences: null == audiences ? _self.audiences : audiences // ignore: cast_nullable_to_non_nullable
|
||||||
as List<dynamic>,scopes: null == scopes ? _self.scopes : scopes // ignore: cast_nullable_to_non_nullable
|
as List<dynamic>,scopes: null == scopes ? _self.scopes : scopes // ignore: cast_nullable_to_non_nullable
|
||||||
as List<dynamic>,ipAddress: null == ipAddress ? _self.ipAddress : ipAddress // ignore: cast_nullable_to_non_nullable
|
as List<dynamic>,ipAddress: null == ipAddress ? _self.ipAddress : ipAddress // ignore: cast_nullable_to_non_nullable
|
||||||
as String,userAgent: null == userAgent ? _self.userAgent : userAgent // ignore: cast_nullable_to_non_nullable
|
as String,userAgent: null == userAgent ? _self.userAgent : userAgent // ignore: cast_nullable_to_non_nullable
|
||||||
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,nonce: freezed == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
|
as String,nonce: freezed == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,location: freezed == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
as String?,location: freezed == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -425,10 +423,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int platform, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String deviceId, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAuthChallenge() when $default != null:
|
case _SnAuthChallenge() when $default != null:
|
||||||
return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.failedAttempts,_that.platform,_that.type,_that.blacklistFactors,_that.audiences,_that.scopes,_that.ipAddress,_that.userAgent,_that.deviceId,_that.nonce,_that.location,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.failedAttempts,_that.type,_that.blacklistFactors,_that.audiences,_that.scopes,_that.ipAddress,_that.userAgent,_that.nonce,_that.location,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -446,10 +444,10 @@ return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int platform, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String deviceId, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAuthChallenge():
|
case _SnAuthChallenge():
|
||||||
return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.failedAttempts,_that.platform,_that.type,_that.blacklistFactors,_that.audiences,_that.scopes,_that.ipAddress,_that.userAgent,_that.deviceId,_that.nonce,_that.location,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.failedAttempts,_that.type,_that.blacklistFactors,_that.audiences,_that.scopes,_that.ipAddress,_that.userAgent,_that.nonce,_that.location,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -463,10 +461,10 @@ return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int platform, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String deviceId, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAuthChallenge() when $default != null:
|
case _SnAuthChallenge() when $default != null:
|
||||||
return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.failedAttempts,_that.platform,_that.type,_that.blacklistFactors,_that.audiences,_that.scopes,_that.ipAddress,_that.userAgent,_that.deviceId,_that.nonce,_that.location,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.failedAttempts,_that.type,_that.blacklistFactors,_that.audiences,_that.scopes,_that.ipAddress,_that.userAgent,_that.nonce,_that.location,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -478,7 +476,7 @@ return $default(_that.id,_that.expiredAt,_that.stepRemain,_that.stepTotal,_that.
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnAuthChallenge implements SnAuthChallenge {
|
class _SnAuthChallenge implements SnAuthChallenge {
|
||||||
const _SnAuthChallenge({required this.id, required this.expiredAt, required this.stepRemain, required this.stepTotal, required this.failedAttempts, required this.platform, required this.type, required final List<String> blacklistFactors, required final List<dynamic> audiences, required final List<dynamic> scopes, required this.ipAddress, required this.userAgent, required this.deviceId, required this.nonce, required this.location, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt}): _blacklistFactors = blacklistFactors,_audiences = audiences,_scopes = scopes;
|
const _SnAuthChallenge({required this.id, required this.expiredAt, required this.stepRemain, required this.stepTotal, required this.failedAttempts, required this.type, required final List<String> blacklistFactors, required final List<dynamic> audiences, required final List<dynamic> scopes, required this.ipAddress, required this.userAgent, required this.nonce, required this.location, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt}): _blacklistFactors = blacklistFactors,_audiences = audiences,_scopes = scopes;
|
||||||
factory _SnAuthChallenge.fromJson(Map<String, dynamic> json) => _$SnAuthChallengeFromJson(json);
|
factory _SnAuthChallenge.fromJson(Map<String, dynamic> json) => _$SnAuthChallengeFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@@ -486,7 +484,6 @@ class _SnAuthChallenge implements SnAuthChallenge {
|
|||||||
@override final int stepRemain;
|
@override final int stepRemain;
|
||||||
@override final int stepTotal;
|
@override final int stepTotal;
|
||||||
@override final int failedAttempts;
|
@override final int failedAttempts;
|
||||||
@override final int platform;
|
|
||||||
@override final int type;
|
@override final int type;
|
||||||
final List<String> _blacklistFactors;
|
final List<String> _blacklistFactors;
|
||||||
@override List<String> get blacklistFactors {
|
@override List<String> get blacklistFactors {
|
||||||
@@ -511,7 +508,6 @@ class _SnAuthChallenge implements SnAuthChallenge {
|
|||||||
|
|
||||||
@override final String ipAddress;
|
@override final String ipAddress;
|
||||||
@override final String userAgent;
|
@override final String userAgent;
|
||||||
@override final String deviceId;
|
|
||||||
@override final String? nonce;
|
@override final String? nonce;
|
||||||
@override final String? location;
|
@override final String? location;
|
||||||
@override final String accountId;
|
@override final String accountId;
|
||||||
@@ -532,16 +528,16 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAuthChallenge&&(identical(other.id, id) || other.id == id)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.stepRemain, stepRemain) || other.stepRemain == stepRemain)&&(identical(other.stepTotal, stepTotal) || other.stepTotal == stepTotal)&&(identical(other.failedAttempts, failedAttempts) || other.failedAttempts == failedAttempts)&&(identical(other.platform, platform) || other.platform == platform)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._blacklistFactors, _blacklistFactors)&&const DeepCollectionEquality().equals(other._audiences, _audiences)&&const DeepCollectionEquality().equals(other._scopes, _scopes)&&(identical(other.ipAddress, ipAddress) || other.ipAddress == ipAddress)&&(identical(other.userAgent, userAgent) || other.userAgent == userAgent)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&(identical(other.location, location) || other.location == location)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAuthChallenge&&(identical(other.id, id) || other.id == id)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.stepRemain, stepRemain) || other.stepRemain == stepRemain)&&(identical(other.stepTotal, stepTotal) || other.stepTotal == stepTotal)&&(identical(other.failedAttempts, failedAttempts) || other.failedAttempts == failedAttempts)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._blacklistFactors, _blacklistFactors)&&const DeepCollectionEquality().equals(other._audiences, _audiences)&&const DeepCollectionEquality().equals(other._scopes, _scopes)&&(identical(other.ipAddress, ipAddress) || other.ipAddress == ipAddress)&&(identical(other.userAgent, userAgent) || other.userAgent == userAgent)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&(identical(other.location, location) || other.location == location)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hashAll([runtimeType,id,expiredAt,stepRemain,stepTotal,failedAttempts,platform,type,const DeepCollectionEquality().hash(_blacklistFactors),const DeepCollectionEquality().hash(_audiences),const DeepCollectionEquality().hash(_scopes),ipAddress,userAgent,deviceId,nonce,location,accountId,createdAt,updatedAt,deletedAt]);
|
int get hashCode => Object.hash(runtimeType,id,expiredAt,stepRemain,stepTotal,failedAttempts,type,const DeepCollectionEquality().hash(_blacklistFactors),const DeepCollectionEquality().hash(_audiences),const DeepCollectionEquality().hash(_scopes),ipAddress,userAgent,nonce,location,accountId,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnAuthChallenge(id: $id, expiredAt: $expiredAt, stepRemain: $stepRemain, stepTotal: $stepTotal, failedAttempts: $failedAttempts, platform: $platform, type: $type, blacklistFactors: $blacklistFactors, audiences: $audiences, scopes: $scopes, ipAddress: $ipAddress, userAgent: $userAgent, deviceId: $deviceId, nonce: $nonce, location: $location, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
return 'SnAuthChallenge(id: $id, expiredAt: $expiredAt, stepRemain: $stepRemain, stepTotal: $stepTotal, failedAttempts: $failedAttempts, type: $type, blacklistFactors: $blacklistFactors, audiences: $audiences, scopes: $scopes, ipAddress: $ipAddress, userAgent: $userAgent, nonce: $nonce, location: $location, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -552,7 +548,7 @@ abstract mixin class _$SnAuthChallengeCopyWith<$Res> implements $SnAuthChallenge
|
|||||||
factory _$SnAuthChallengeCopyWith(_SnAuthChallenge value, $Res Function(_SnAuthChallenge) _then) = __$SnAuthChallengeCopyWithImpl;
|
factory _$SnAuthChallengeCopyWith(_SnAuthChallenge value, $Res Function(_SnAuthChallenge) _then) = __$SnAuthChallengeCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int platform, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String deviceId, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
String id, DateTime expiredAt, int stepRemain, int stepTotal, int failedAttempts, int type, List<String> blacklistFactors, List<dynamic> audiences, List<dynamic> scopes, String ipAddress, String userAgent, String? nonce, String? location, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -569,21 +565,19 @@ class __$SnAuthChallengeCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnAuthChallenge
|
/// Create a copy of SnAuthChallenge
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? expiredAt = null,Object? stepRemain = null,Object? stepTotal = null,Object? failedAttempts = null,Object? platform = null,Object? type = null,Object? blacklistFactors = null,Object? audiences = null,Object? scopes = null,Object? ipAddress = null,Object? userAgent = null,Object? deviceId = null,Object? nonce = freezed,Object? location = freezed,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? expiredAt = null,Object? stepRemain = null,Object? stepTotal = null,Object? failedAttempts = null,Object? type = null,Object? blacklistFactors = null,Object? audiences = null,Object? scopes = null,Object? ipAddress = null,Object? userAgent = null,Object? nonce = freezed,Object? location = freezed,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
return _then(_SnAuthChallenge(
|
return _then(_SnAuthChallenge(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,expiredAt: null == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
|
as String,expiredAt: null == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,stepRemain: null == stepRemain ? _self.stepRemain : stepRemain // ignore: cast_nullable_to_non_nullable
|
as DateTime,stepRemain: null == stepRemain ? _self.stepRemain : stepRemain // ignore: cast_nullable_to_non_nullable
|
||||||
as int,stepTotal: null == stepTotal ? _self.stepTotal : stepTotal // ignore: cast_nullable_to_non_nullable
|
as int,stepTotal: null == stepTotal ? _self.stepTotal : stepTotal // ignore: cast_nullable_to_non_nullable
|
||||||
as int,failedAttempts: null == failedAttempts ? _self.failedAttempts : failedAttempts // ignore: cast_nullable_to_non_nullable
|
as int,failedAttempts: null == failedAttempts ? _self.failedAttempts : failedAttempts // ignore: cast_nullable_to_non_nullable
|
||||||
as int,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
as int,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as int,blacklistFactors: null == blacklistFactors ? _self._blacklistFactors : blacklistFactors // ignore: cast_nullable_to_non_nullable
|
as int,blacklistFactors: null == blacklistFactors ? _self._blacklistFactors : blacklistFactors // ignore: cast_nullable_to_non_nullable
|
||||||
as List<String>,audiences: null == audiences ? _self._audiences : audiences // ignore: cast_nullable_to_non_nullable
|
as List<String>,audiences: null == audiences ? _self._audiences : audiences // ignore: cast_nullable_to_non_nullable
|
||||||
as List<dynamic>,scopes: null == scopes ? _self._scopes : scopes // ignore: cast_nullable_to_non_nullable
|
as List<dynamic>,scopes: null == scopes ? _self._scopes : scopes // ignore: cast_nullable_to_non_nullable
|
||||||
as List<dynamic>,ipAddress: null == ipAddress ? _self.ipAddress : ipAddress // ignore: cast_nullable_to_non_nullable
|
as List<dynamic>,ipAddress: null == ipAddress ? _self.ipAddress : ipAddress // ignore: cast_nullable_to_non_nullable
|
||||||
as String,userAgent: null == userAgent ? _self.userAgent : userAgent // ignore: cast_nullable_to_non_nullable
|
as String,userAgent: null == userAgent ? _self.userAgent : userAgent // ignore: cast_nullable_to_non_nullable
|
||||||
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,nonce: freezed == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
|
as String,nonce: freezed == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,location: freezed == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
as String?,location: freezed == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -1189,286 +1183,6 @@ as Map<String, dynamic>?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$SnAuthDevice {
|
|
||||||
|
|
||||||
dynamic get label; String get userAgent; String get deviceId; int get platform; List<SnAuthSession> get sessions;// Not from backend, used for UI
|
|
||||||
bool get isCurrent;
|
|
||||||
/// Create a copy of SnAuthDevice
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$SnAuthDeviceCopyWith<SnAuthDevice> get copyWith => _$SnAuthDeviceCopyWithImpl<SnAuthDevice>(this as SnAuthDevice, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this SnAuthDevice to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAuthDevice&&const DeepCollectionEquality().equals(other.label, label)&&(identical(other.userAgent, userAgent) || other.userAgent == userAgent)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.platform, platform) || other.platform == platform)&&const DeepCollectionEquality().equals(other.sessions, sessions)&&(identical(other.isCurrent, isCurrent) || other.isCurrent == isCurrent));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(label),userAgent,deviceId,platform,const DeepCollectionEquality().hash(sessions),isCurrent);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'SnAuthDevice(label: $label, userAgent: $userAgent, deviceId: $deviceId, platform: $platform, sessions: $sessions, isCurrent: $isCurrent)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $SnAuthDeviceCopyWith<$Res> {
|
|
||||||
factory $SnAuthDeviceCopyWith(SnAuthDevice value, $Res Function(SnAuthDevice) _then) = _$SnAuthDeviceCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
dynamic label, String userAgent, String deviceId, int platform, List<SnAuthSession> sessions, bool isCurrent
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$SnAuthDeviceCopyWithImpl<$Res>
|
|
||||||
implements $SnAuthDeviceCopyWith<$Res> {
|
|
||||||
_$SnAuthDeviceCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final SnAuthDevice _self;
|
|
||||||
final $Res Function(SnAuthDevice) _then;
|
|
||||||
|
|
||||||
/// Create a copy of SnAuthDevice
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? label = freezed,Object? userAgent = null,Object? deviceId = null,Object? platform = null,Object? sessions = null,Object? isCurrent = null,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable
|
|
||||||
as dynamic,userAgent: null == userAgent ? _self.userAgent : userAgent // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,sessions: null == sessions ? _self.sessions : sessions // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<SnAuthSession>,isCurrent: null == isCurrent ? _self.isCurrent : isCurrent // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [SnAuthDevice].
|
|
||||||
extension SnAuthDevicePatterns on SnAuthDevice {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnAuthDevice value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _SnAuthDevice() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnAuthDevice value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _SnAuthDevice():
|
|
||||||
return $default(_that);}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnAuthDevice value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _SnAuthDevice() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( dynamic label, String userAgent, String deviceId, int platform, List<SnAuthSession> sessions, bool isCurrent)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _SnAuthDevice() when $default != null:
|
|
||||||
return $default(_that.label,_that.userAgent,_that.deviceId,_that.platform,_that.sessions,_that.isCurrent);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( dynamic label, String userAgent, String deviceId, int platform, List<SnAuthSession> sessions, bool isCurrent) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _SnAuthDevice():
|
|
||||||
return $default(_that.label,_that.userAgent,_that.deviceId,_that.platform,_that.sessions,_that.isCurrent);}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( dynamic label, String userAgent, String deviceId, int platform, List<SnAuthSession> sessions, bool isCurrent)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _SnAuthDevice() when $default != null:
|
|
||||||
return $default(_that.label,_that.userAgent,_that.deviceId,_that.platform,_that.sessions,_that.isCurrent);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _SnAuthDevice implements SnAuthDevice {
|
|
||||||
const _SnAuthDevice({required this.label, required this.userAgent, required this.deviceId, required this.platform, required final List<SnAuthSession> sessions, this.isCurrent = false}): _sessions = sessions;
|
|
||||||
factory _SnAuthDevice.fromJson(Map<String, dynamic> json) => _$SnAuthDeviceFromJson(json);
|
|
||||||
|
|
||||||
@override final dynamic label;
|
|
||||||
@override final String userAgent;
|
|
||||||
@override final String deviceId;
|
|
||||||
@override final int platform;
|
|
||||||
final List<SnAuthSession> _sessions;
|
|
||||||
@override List<SnAuthSession> get sessions {
|
|
||||||
if (_sessions is EqualUnmodifiableListView) return _sessions;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_sessions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not from backend, used for UI
|
|
||||||
@override@JsonKey() final bool isCurrent;
|
|
||||||
|
|
||||||
/// Create a copy of SnAuthDevice
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$SnAuthDeviceCopyWith<_SnAuthDevice> get copyWith => __$SnAuthDeviceCopyWithImpl<_SnAuthDevice>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$SnAuthDeviceToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAuthDevice&&const DeepCollectionEquality().equals(other.label, label)&&(identical(other.userAgent, userAgent) || other.userAgent == userAgent)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.platform, platform) || other.platform == platform)&&const DeepCollectionEquality().equals(other._sessions, _sessions)&&(identical(other.isCurrent, isCurrent) || other.isCurrent == isCurrent));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(label),userAgent,deviceId,platform,const DeepCollectionEquality().hash(_sessions),isCurrent);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'SnAuthDevice(label: $label, userAgent: $userAgent, deviceId: $deviceId, platform: $platform, sessions: $sessions, isCurrent: $isCurrent)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$SnAuthDeviceCopyWith<$Res> implements $SnAuthDeviceCopyWith<$Res> {
|
|
||||||
factory _$SnAuthDeviceCopyWith(_SnAuthDevice value, $Res Function(_SnAuthDevice) _then) = __$SnAuthDeviceCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
dynamic label, String userAgent, String deviceId, int platform, List<SnAuthSession> sessions, bool isCurrent
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$SnAuthDeviceCopyWithImpl<$Res>
|
|
||||||
implements _$SnAuthDeviceCopyWith<$Res> {
|
|
||||||
__$SnAuthDeviceCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _SnAuthDevice _self;
|
|
||||||
final $Res Function(_SnAuthDevice) _then;
|
|
||||||
|
|
||||||
/// Create a copy of SnAuthDevice
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? label = freezed,Object? userAgent = null,Object? deviceId = null,Object? platform = null,Object? sessions = null,Object? isCurrent = null,}) {
|
|
||||||
return _then(_SnAuthDevice(
|
|
||||||
label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable
|
|
||||||
as dynamic,userAgent: null == userAgent ? _self.userAgent : userAgent // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,sessions: null == sessions ? _self._sessions : sessions // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<SnAuthSession>,isCurrent: null == isCurrent ? _self.isCurrent : isCurrent // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnAccountConnection {
|
mixin _$SnAccountConnection {
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ _SnAuthChallenge _$SnAuthChallengeFromJson(Map<String, dynamic> json) =>
|
|||||||
stepRemain: (json['step_remain'] as num).toInt(),
|
stepRemain: (json['step_remain'] as num).toInt(),
|
||||||
stepTotal: (json['step_total'] as num).toInt(),
|
stepTotal: (json['step_total'] as num).toInt(),
|
||||||
failedAttempts: (json['failed_attempts'] as num).toInt(),
|
failedAttempts: (json['failed_attempts'] as num).toInt(),
|
||||||
platform: (json['platform'] as num).toInt(),
|
|
||||||
type: (json['type'] as num).toInt(),
|
type: (json['type'] as num).toInt(),
|
||||||
blacklistFactors:
|
blacklistFactors:
|
||||||
(json['blacklist_factors'] as List<dynamic>)
|
(json['blacklist_factors'] as List<dynamic>)
|
||||||
@@ -30,7 +29,6 @@ _SnAuthChallenge _$SnAuthChallengeFromJson(Map<String, dynamic> json) =>
|
|||||||
scopes: json['scopes'] as List<dynamic>,
|
scopes: json['scopes'] as List<dynamic>,
|
||||||
ipAddress: json['ip_address'] as String,
|
ipAddress: json['ip_address'] as String,
|
||||||
userAgent: json['user_agent'] as String,
|
userAgent: json['user_agent'] as String,
|
||||||
deviceId: json['device_id'] as String,
|
|
||||||
nonce: json['nonce'] as String?,
|
nonce: json['nonce'] as String?,
|
||||||
location: json['location'] as String?,
|
location: json['location'] as String?,
|
||||||
accountId: json['account_id'] as String,
|
accountId: json['account_id'] as String,
|
||||||
@@ -49,14 +47,12 @@ Map<String, dynamic> _$SnAuthChallengeToJson(_SnAuthChallenge instance) =>
|
|||||||
'step_remain': instance.stepRemain,
|
'step_remain': instance.stepRemain,
|
||||||
'step_total': instance.stepTotal,
|
'step_total': instance.stepTotal,
|
||||||
'failed_attempts': instance.failedAttempts,
|
'failed_attempts': instance.failedAttempts,
|
||||||
'platform': instance.platform,
|
|
||||||
'type': instance.type,
|
'type': instance.type,
|
||||||
'blacklist_factors': instance.blacklistFactors,
|
'blacklist_factors': instance.blacklistFactors,
|
||||||
'audiences': instance.audiences,
|
'audiences': instance.audiences,
|
||||||
'scopes': instance.scopes,
|
'scopes': instance.scopes,
|
||||||
'ip_address': instance.ipAddress,
|
'ip_address': instance.ipAddress,
|
||||||
'user_agent': instance.userAgent,
|
'user_agent': instance.userAgent,
|
||||||
'device_id': instance.deviceId,
|
|
||||||
'nonce': instance.nonce,
|
'nonce': instance.nonce,
|
||||||
'location': instance.location,
|
'location': instance.location,
|
||||||
'account_id': instance.accountId,
|
'account_id': instance.accountId,
|
||||||
@@ -133,29 +129,6 @@ Map<String, dynamic> _$SnAuthFactorToJson(_SnAuthFactor instance) =>
|
|||||||
'created_response': instance.createdResponse,
|
'created_response': instance.createdResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
_SnAuthDevice _$SnAuthDeviceFromJson(Map<String, dynamic> json) =>
|
|
||||||
_SnAuthDevice(
|
|
||||||
label: json['label'],
|
|
||||||
userAgent: json['user_agent'] as String,
|
|
||||||
deviceId: json['device_id'] as String,
|
|
||||||
platform: (json['platform'] as num).toInt(),
|
|
||||||
sessions:
|
|
||||||
(json['sessions'] as List<dynamic>)
|
|
||||||
.map((e) => SnAuthSession.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
isCurrent: json['is_current'] as bool? ?? false,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$SnAuthDeviceToJson(_SnAuthDevice instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'label': instance.label,
|
|
||||||
'user_agent': instance.userAgent,
|
|
||||||
'device_id': instance.deviceId,
|
|
||||||
'platform': instance.platform,
|
|
||||||
'sessions': instance.sessions.map((e) => e.toJson()).toList(),
|
|
||||||
'is_current': instance.isCurrent,
|
|
||||||
};
|
|
||||||
|
|
||||||
_SnAccountConnection _$SnAccountConnectionFromJson(Map<String, dynamic> json) =>
|
_SnAccountConnection _$SnAccountConnectionFromJson(Map<String, dynamic> json) =>
|
||||||
_SnAccountConnection(
|
_SnAccountConnection(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
import 'package:island/models/realm.dart';
|
import 'package:island/models/realm.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
|
|
||||||
part 'chat.freezed.dart';
|
part 'chat.freezed.dart';
|
||||||
part 'chat.g.dart';
|
part 'chat.g.dart';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
|
|
||||||
part 'custom_app.freezed.dart';
|
part 'custom_app.freezed.dart';
|
||||||
part 'custom_app.g.dart';
|
part 'custom_app.g.dart';
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:island/models/publisher.dart';
|
||||||
|
|
||||||
part 'developer.freezed.dart';
|
part 'developer.freezed.dart';
|
||||||
part 'developer.g.dart';
|
part 'developer.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnDeveloper with _$SnDeveloper {
|
||||||
|
const factory SnDeveloper({
|
||||||
|
required String id,
|
||||||
|
required String publisherId,
|
||||||
|
SnPublisher? publisher,
|
||||||
|
}) = _SnDeveloper;
|
||||||
|
|
||||||
|
factory SnDeveloper.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnDeveloperFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class DeveloperStats with _$DeveloperStats {
|
sealed class DeveloperStats with _$DeveloperStats {
|
||||||
const factory DeveloperStats({
|
const factory DeveloperStats({@Default(0) int totalCustomApps}) =
|
||||||
@Default(0) int totalCustomApps,
|
_DeveloperStats;
|
||||||
}) = _DeveloperStats;
|
|
||||||
|
|
||||||
factory DeveloperStats.fromJson(Map<String, dynamic> json) =>
|
factory DeveloperStats.fromJson(Map<String, dynamic> json) =>
|
||||||
_$DeveloperStatsFromJson(json);
|
_$DeveloperStatsFromJson(json);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,293 @@ part of 'developer.dart';
|
|||||||
// dart format off
|
// dart format off
|
||||||
T _$identity<T>(T value) => value;
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnDeveloper {
|
||||||
|
|
||||||
|
String get id; String get publisherId; SnPublisher? get publisher;
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnDeveloperCopyWith<SnDeveloper> get copyWith => _$SnDeveloperCopyWithImpl<SnDeveloper>(this as SnDeveloper, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnDeveloper to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnDeveloper&&(identical(other.id, id) || other.id == id)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,publisherId,publisher);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnDeveloper(id: $id, publisherId: $publisherId, publisher: $publisher)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnDeveloperCopyWith<$Res> {
|
||||||
|
factory $SnDeveloperCopyWith(SnDeveloper value, $Res Function(SnDeveloper) _then) = _$SnDeveloperCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String publisherId, SnPublisher? publisher
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnDeveloperCopyWithImpl<$Res>
|
||||||
|
implements $SnDeveloperCopyWith<$Res> {
|
||||||
|
_$SnDeveloperCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnDeveloper _self;
|
||||||
|
final $Res Function(SnDeveloper) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? publisherId = null,Object? publisher = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPublisher?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher {
|
||||||
|
if (_self.publisher == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
|
||||||
|
return _then(_self.copyWith(publisher: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnDeveloper].
|
||||||
|
extension SnDeveloperPatterns on SnDeveloper {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnDeveloper value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnDeveloper value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnDeveloper value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String publisherId, SnPublisher? publisher)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper() when $default != null:
|
||||||
|
return $default(_that.id,_that.publisherId,_that.publisher);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String publisherId, SnPublisher? publisher) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper():
|
||||||
|
return $default(_that.id,_that.publisherId,_that.publisher);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String publisherId, SnPublisher? publisher)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper() when $default != null:
|
||||||
|
return $default(_that.id,_that.publisherId,_that.publisher);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnDeveloper implements SnDeveloper {
|
||||||
|
const _SnDeveloper({required this.id, required this.publisherId, this.publisher});
|
||||||
|
factory _SnDeveloper.fromJson(Map<String, dynamic> json) => _$SnDeveloperFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final String publisherId;
|
||||||
|
@override final SnPublisher? publisher;
|
||||||
|
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnDeveloperCopyWith<_SnDeveloper> get copyWith => __$SnDeveloperCopyWithImpl<_SnDeveloper>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnDeveloperToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnDeveloper&&(identical(other.id, id) || other.id == id)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,publisherId,publisher);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnDeveloper(id: $id, publisherId: $publisherId, publisher: $publisher)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnDeveloperCopyWith<$Res> implements $SnDeveloperCopyWith<$Res> {
|
||||||
|
factory _$SnDeveloperCopyWith(_SnDeveloper value, $Res Function(_SnDeveloper) _then) = __$SnDeveloperCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String publisherId, SnPublisher? publisher
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@override $SnPublisherCopyWith<$Res>? get publisher;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnDeveloperCopyWithImpl<$Res>
|
||||||
|
implements _$SnDeveloperCopyWith<$Res> {
|
||||||
|
__$SnDeveloperCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnDeveloper _self;
|
||||||
|
final $Res Function(_SnDeveloper) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? publisherId = null,Object? publisher = freezed,}) {
|
||||||
|
return _then(_SnDeveloper(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPublisher?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher {
|
||||||
|
if (_self.publisher == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
|
||||||
|
return _then(_self.copyWith(publisher: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$DeveloperStats {
|
mixin _$DeveloperStats {
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,22 @@ part of 'developer.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
|
_SnDeveloper _$SnDeveloperFromJson(Map<String, dynamic> json) => _SnDeveloper(
|
||||||
|
id: json['id'] as String,
|
||||||
|
publisherId: json['publisher_id'] as String,
|
||||||
|
publisher:
|
||||||
|
json['publisher'] == null
|
||||||
|
? null
|
||||||
|
: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnDeveloperToJson(_SnDeveloper instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'publisher_id': instance.publisherId,
|
||||||
|
'publisher': instance.publisher?.toJson(),
|
||||||
|
};
|
||||||
|
|
||||||
_DeveloperStats _$DeveloperStatsFromJson(Map<String, dynamic> json) =>
|
_DeveloperStats _$DeveloperStatsFromJson(Map<String, dynamic> json) =>
|
||||||
_DeveloperStats(
|
_DeveloperStats(
|
||||||
totalCustomApps: (json['total_custom_apps'] as num?)?.toInt() ?? 0,
|
totalCustomApps: (json['total_custom_apps'] as num?)?.toInt() ?? 0,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ part 'poll.g.dart';
|
|||||||
sealed class SnPollWithStats with _$SnPollWithStats {
|
sealed class SnPollWithStats with _$SnPollWithStats {
|
||||||
const factory SnPollWithStats({
|
const factory SnPollWithStats({
|
||||||
required Map<String, dynamic>? userAnswer,
|
required Map<String, dynamic>? userAnswer,
|
||||||
required Map<String, dynamic> stats,
|
@Default({}) Map<String, dynamic> stats,
|
||||||
required String id,
|
required String id,
|
||||||
required List<SnPollQuestion> questions,
|
required List<SnPollQuestion> questions,
|
||||||
String? title,
|
String? title,
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ return $default(_that.userAnswer,_that.stats,_that.id,_that.questions,_that.titl
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnPollWithStats implements SnPollWithStats {
|
class _SnPollWithStats implements SnPollWithStats {
|
||||||
const _SnPollWithStats({required final Map<String, dynamic>? userAnswer, required final Map<String, dynamic> stats, required this.id, required final List<SnPollQuestion> questions, this.title, this.description, this.endedAt, required this.publisherId, required this.createdAt, required this.updatedAt, this.deletedAt}): _userAnswer = userAnswer,_stats = stats,_questions = questions;
|
const _SnPollWithStats({required final Map<String, dynamic>? userAnswer, final Map<String, dynamic> stats = const {}, required this.id, required final List<SnPollQuestion> questions, this.title, this.description, this.endedAt, required this.publisherId, required this.createdAt, required this.updatedAt, this.deletedAt}): _userAnswer = userAnswer,_stats = stats,_questions = questions;
|
||||||
factory _SnPollWithStats.fromJson(Map<String, dynamic> json) => _$SnPollWithStatsFromJson(json);
|
factory _SnPollWithStats.fromJson(Map<String, dynamic> json) => _$SnPollWithStatsFromJson(json);
|
||||||
|
|
||||||
final Map<String, dynamic>? _userAnswer;
|
final Map<String, dynamic>? _userAnswer;
|
||||||
@@ -226,7 +226,7 @@ class _SnPollWithStats implements SnPollWithStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, dynamic> _stats;
|
final Map<String, dynamic> _stats;
|
||||||
@override Map<String, dynamic> get stats {
|
@override@JsonKey() Map<String, dynamic> get stats {
|
||||||
if (_stats is EqualUnmodifiableMapView) return _stats;
|
if (_stats is EqualUnmodifiableMapView) return _stats;
|
||||||
// ignore: implicit_dynamic_type
|
// ignore: implicit_dynamic_type
|
||||||
return EqualUnmodifiableMapView(_stats);
|
return EqualUnmodifiableMapView(_stats);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ part of 'poll.dart';
|
|||||||
_SnPollWithStats _$SnPollWithStatsFromJson(Map<String, dynamic> json) =>
|
_SnPollWithStats _$SnPollWithStatsFromJson(Map<String, dynamic> json) =>
|
||||||
_SnPollWithStats(
|
_SnPollWithStats(
|
||||||
userAnswer: json['user_answer'] as Map<String, dynamic>?,
|
userAnswer: json['user_answer'] as Map<String, dynamic>?,
|
||||||
stats: json['stats'] as Map<String, dynamic>,
|
stats: json['stats'] as Map<String, dynamic>? ?? const {},
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
questions:
|
questions:
|
||||||
(json['questions'] as List<dynamic>)
|
(json['questions'] as List<dynamic>)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
|
|
||||||
part 'publisher.freezed.dart';
|
part 'publisher.freezed.dart';
|
||||||
part 'publisher.g.dart';
|
part 'publisher.g.dart';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
|
|
||||||
part 'realm.freezed.dart';
|
part 'realm.freezed.dart';
|
||||||
part 'realm.g.dart';
|
part 'realm.g.dart';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
|
|
||||||
part 'relationship.freezed.dart';
|
part 'relationship.freezed.dart';
|
||||||
part 'relationship.g.dart';
|
part 'relationship.g.dart';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
|
|
||||||
part 'wallet.freezed.dart';
|
part 'wallet.freezed.dart';
|
||||||
part 'wallet.g.dart';
|
part 'wallet.g.dart';
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
|
|||||||
final response = await client.get('/id/accounts/me');
|
final response = await client.get('/id/accounts/me');
|
||||||
final user = SnAccount.fromJson(response.data);
|
final user = SnAccount.fromJson(response.data);
|
||||||
state = AsyncValue.data(user);
|
state = AsyncValue.data(user);
|
||||||
|
FirebaseAnalytics.instance.setUserId(id: user.id);
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
log(
|
log(
|
||||||
"[UserInfo] Failed to fetch user info...",
|
"[UserInfo] Failed to fetch user info...",
|
||||||
@@ -33,6 +35,7 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
|
|||||||
final prefs = _ref.read(sharedPreferencesProvider);
|
final prefs = _ref.read(sharedPreferencesProvider);
|
||||||
await prefs.remove(kTokenPairStoreKey);
|
await prefs.remove(kTokenPairStoreKey);
|
||||||
_ref.invalidate(tokenProvider);
|
_ref.invalidate(tokenProvider);
|
||||||
|
FirebaseAnalytics.instance.setUserId(id: null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@@ -59,6 +61,9 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
return GoRouter(
|
return GoRouter(
|
||||||
navigatorKey: rootNavigatorKey,
|
navigatorKey: rootNavigatorKey,
|
||||||
initialLocation: '/',
|
initialLocation: '/',
|
||||||
|
observers: [
|
||||||
|
FirebaseAnalyticsObserver(analytics: FirebaseAnalytics.instance),
|
||||||
|
],
|
||||||
routes: [
|
routes: [
|
||||||
ShellRoute(
|
ShellRoute(
|
||||||
navigatorKey: _shellNavigatorKey,
|
navigatorKey: _shellNavigatorKey,
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/services/udid.native.dart';
|
import 'package:island/services/udid.native.dart';
|
||||||
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:island/services/update_service.dart';
|
import 'package:island/services/update_service.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
@@ -102,235 +102,226 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
|
|||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: _errorMessage != null
|
: _errorMessage != null
|
||||||
? Center(child: Text(_errorMessage!))
|
? Center(child: Text(_errorMessage!))
|
||||||
: SingleChildScrollView(
|
: Center(
|
||||||
child: Column(
|
child: ConstrainedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
constraints: const BoxConstraints(maxWidth: 540),
|
||||||
children: [
|
child: SingleChildScrollView(
|
||||||
const SizedBox(height: 24),
|
child: Column(
|
||||||
// App Icon and Name
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
CircleAvatar(
|
|
||||||
radius: 50,
|
|
||||||
backgroundColor: theme.colorScheme.primary.withOpacity(
|
|
||||||
0.1,
|
|
||||||
),
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/icons/icon.png',
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
_packageInfo.appName,
|
|
||||||
style: theme.textTheme.headlineSmall?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'aboutScreenVersionInfo'.tr(
|
|
||||||
args: [_packageInfo.version, _packageInfo.buildNumber],
|
|
||||||
),
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
|
||||||
color: theme.textTheme.bodySmall?.color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// App Info Card
|
|
||||||
_buildSection(
|
|
||||||
context,
|
|
||||||
title: 'aboutScreenAppInfoSectionTitle'.tr(),
|
|
||||||
children: [
|
children: [
|
||||||
_buildInfoItem(
|
const SizedBox(height: 24),
|
||||||
context,
|
// App Icon and Name
|
||||||
icon: Symbols.info,
|
CircleAvatar(
|
||||||
label: 'aboutScreenPackageNameLabel'.tr(),
|
radius: 50,
|
||||||
value: _packageInfo.packageName,
|
backgroundColor: theme.colorScheme.primary
|
||||||
),
|
.withOpacity(0.1),
|
||||||
_buildInfoItem(
|
child: Image.asset(
|
||||||
context,
|
'assets/icons/icon.png',
|
||||||
icon: Symbols.update,
|
width: 56,
|
||||||
label: 'aboutScreenVersionLabel'.tr(),
|
height: 56,
|
||||||
value: _packageInfo.version,
|
|
||||||
),
|
|
||||||
_buildInfoItem(
|
|
||||||
context,
|
|
||||||
icon: Symbols.build,
|
|
||||||
label: 'aboutScreenBuildNumberLabel'.tr(),
|
|
||||||
value: _packageInfo.buildNumber,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
if (_deviceInfo != null) const SizedBox(height: 16),
|
|
||||||
|
|
||||||
if (_deviceInfo != null)
|
|
||||||
_buildSection(
|
|
||||||
context,
|
|
||||||
title: 'Device Information',
|
|
||||||
children: [
|
|
||||||
_buildInfoItem(
|
|
||||||
context,
|
|
||||||
icon: Symbols.label,
|
|
||||||
label: 'aboutDeviceName'.tr(),
|
|
||||||
value: _deviceInfo?.data['name'],
|
|
||||||
),
|
),
|
||||||
_buildInfoItem(
|
),
|
||||||
context,
|
const SizedBox(height: 16),
|
||||||
icon: Symbols.fingerprint,
|
Text(
|
||||||
label: 'aboutDeviceIdentifier'.tr(),
|
_packageInfo.appName,
|
||||||
value: _deviceUdid ?? 'N/A',
|
style: theme.textTheme.headlineSmall?.copyWith(
|
||||||
copyable: true,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Links Card
|
|
||||||
_buildSection(
|
|
||||||
context,
|
|
||||||
title: 'aboutScreenLinksSectionTitle'.tr(),
|
|
||||||
children: [
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.system_update,
|
|
||||||
title: 'Check for updates',
|
|
||||||
onTap: () async {
|
|
||||||
// Fetch latest release and show the unified sheet
|
|
||||||
final svc = UpdateService();
|
|
||||||
// Reuse service fetch + compare to decide content
|
|
||||||
final release = await svc.fetchLatestRelease();
|
|
||||||
if (release != null) {
|
|
||||||
await svc.showUpdateSheet(context, release);
|
|
||||||
} else {
|
|
||||||
// Fallback: show a simple sheet indicating no info
|
|
||||||
// Use your SheetScaffold for consistent styling
|
|
||||||
// Show a minimal message
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
useSafeArea: true,
|
|
||||||
showDragHandle: true,
|
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).colorScheme.surface,
|
|
||||||
builder:
|
|
||||||
(_) => const SheetScaffold(
|
|
||||||
titleText: 'Update',
|
|
||||||
child: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(24),
|
|
||||||
child: Text(
|
|
||||||
'Unable to fetch release info at this time.',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
_buildListTile(
|
Text(
|
||||||
context,
|
'aboutScreenVersionInfo'.tr(
|
||||||
icon: Symbols.privacy_tip,
|
args: [
|
||||||
title: 'aboutScreenPrivacyPolicyTitle'.tr(),
|
_packageInfo.version,
|
||||||
onTap:
|
_packageInfo.buildNumber,
|
||||||
() => _launchURL(
|
],
|
||||||
'https://solsynth.dev/terms/privacy-policy',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.description,
|
|
||||||
title: 'aboutScreenTermsOfServiceTitle'.tr(),
|
|
||||||
onTap:
|
|
||||||
() => _launchURL(
|
|
||||||
'https://solsynth.dev/terms/user-agreement',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.code,
|
|
||||||
title: 'aboutScreenOpenSourceLicensesTitle'.tr(),
|
|
||||||
onTap: () {
|
|
||||||
showLicensePage(
|
|
||||||
context: context,
|
|
||||||
applicationName: _packageInfo.appName,
|
|
||||||
applicationVersion:
|
|
||||||
'Version ${_packageInfo.version}',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Developer Info
|
|
||||||
_buildSection(
|
|
||||||
context,
|
|
||||||
title: 'aboutScreenDeveloperSectionTitle'.tr(),
|
|
||||||
children: [
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.email,
|
|
||||||
title: 'aboutScreenContactUsTitle'.tr(),
|
|
||||||
subtitle: 'lily@solsynth.dev',
|
|
||||||
onTap: () => _launchURL('mailto:lily@solsynth.dev'),
|
|
||||||
),
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.copyright,
|
|
||||||
title: 'aboutScreenLicenseTitle'.tr(),
|
|
||||||
subtitle: 'aboutScreenLicenseContent'.tr(
|
|
||||||
args: [DateTime.now().year.toString()],
|
|
||||||
),
|
),
|
||||||
onTap:
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
() => _launchURL(
|
color: theme.textTheme.bodySmall?.color,
|
||||||
'https://github.com/Solsynth/Solian/blob/v3/LICENSE.txt',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (kIsWeb || !(Platform.isMacOS || Platform.isIOS))
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.favorite,
|
|
||||||
title: 'donate'.tr(),
|
|
||||||
subtitle: 'donateDescription'.tr(),
|
|
||||||
onTap: () {
|
|
||||||
launchUrlString(
|
|
||||||
'https://afdian.com/@littlesheep',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
const SizedBox(height: 32),
|
// App Info Card
|
||||||
|
_buildSection(
|
||||||
// Copyright
|
context,
|
||||||
Padding(
|
title: 'aboutScreenAppInfoSectionTitle'.tr(),
|
||||||
padding: const EdgeInsets.all(16.0),
|
children: [
|
||||||
child: Column(
|
_buildInfoItem(
|
||||||
children: [
|
context,
|
||||||
Text(
|
icon: Symbols.info,
|
||||||
'aboutScreenCopyright'.tr(
|
label: 'aboutScreenPackageNameLabel'.tr(),
|
||||||
args: [DateTime.now().year.toString()],
|
value: _packageInfo.packageName,
|
||||||
),
|
),
|
||||||
style: theme.textTheme.bodySmall,
|
_buildInfoItem(
|
||||||
textAlign: TextAlign.center,
|
context,
|
||||||
),
|
icon: Symbols.update,
|
||||||
const Gap(1),
|
label: 'aboutScreenVersionLabel'.tr(),
|
||||||
Text(
|
value: _packageInfo.version,
|
||||||
'aboutScreenMadeWith'.tr(),
|
),
|
||||||
textAlign: TextAlign.center,
|
_buildInfoItem(
|
||||||
).fontSize(10).opacity(0.8),
|
context,
|
||||||
],
|
icon: Symbols.build,
|
||||||
),
|
label: 'aboutScreenBuildNumberLabel'.tr(),
|
||||||
),
|
value: _packageInfo.buildNumber,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
Gap(MediaQuery.of(context).padding.bottom + 16),
|
if (_deviceInfo != null) const SizedBox(height: 16),
|
||||||
],
|
|
||||||
|
if (_deviceInfo != null)
|
||||||
|
_buildSection(
|
||||||
|
context,
|
||||||
|
title: 'Device Information',
|
||||||
|
children: [
|
||||||
|
_buildInfoItem(
|
||||||
|
context,
|
||||||
|
icon: Symbols.label,
|
||||||
|
label: 'aboutDeviceName'.tr(),
|
||||||
|
value: _deviceInfo?.data['name'],
|
||||||
|
),
|
||||||
|
_buildInfoItem(
|
||||||
|
context,
|
||||||
|
icon: Symbols.fingerprint,
|
||||||
|
label: 'aboutDeviceIdentifier'.tr(),
|
||||||
|
value: _deviceUdid ?? 'N/A',
|
||||||
|
copyable: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Links Card
|
||||||
|
_buildSection(
|
||||||
|
context,
|
||||||
|
title: 'aboutScreenLinksSectionTitle'.tr(),
|
||||||
|
children: [
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.system_update,
|
||||||
|
title: 'Check for updates',
|
||||||
|
onTap: () async {
|
||||||
|
// Fetch latest release and show the unified sheet
|
||||||
|
final svc = UpdateService();
|
||||||
|
// Reuse service fetch + compare to decide content
|
||||||
|
showLoadingModal(context);
|
||||||
|
final release = await svc.fetchLatestRelease();
|
||||||
|
if (!context.mounted) return;
|
||||||
|
hideLoadingModal(context);
|
||||||
|
if (release != null) {
|
||||||
|
await svc.showUpdateSheet(context, release);
|
||||||
|
} else {
|
||||||
|
showInfoAlert(
|
||||||
|
'Currently cannot get update from the GitHub.',
|
||||||
|
'Unable to check for updates',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.privacy_tip,
|
||||||
|
title: 'aboutScreenPrivacyPolicyTitle'.tr(),
|
||||||
|
onTap:
|
||||||
|
() => _launchURL(
|
||||||
|
'https://solsynth.dev/terms/privacy-policy',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.description,
|
||||||
|
title: 'aboutScreenTermsOfServiceTitle'.tr(),
|
||||||
|
onTap:
|
||||||
|
() => _launchURL(
|
||||||
|
'https://solsynth.dev/terms/user-agreement',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.code,
|
||||||
|
title: 'aboutScreenOpenSourceLicensesTitle'.tr(),
|
||||||
|
onTap: () {
|
||||||
|
showLicensePage(
|
||||||
|
context: context,
|
||||||
|
applicationName: _packageInfo.appName,
|
||||||
|
applicationVersion:
|
||||||
|
'Version ${_packageInfo.version}',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Developer Info
|
||||||
|
_buildSection(
|
||||||
|
context,
|
||||||
|
title: 'aboutScreenDeveloperSectionTitle'.tr(),
|
||||||
|
children: [
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.email,
|
||||||
|
title: 'aboutScreenContactUsTitle'.tr(),
|
||||||
|
subtitle: 'lily@solsynth.dev',
|
||||||
|
onTap:
|
||||||
|
() => _launchURL('mailto:lily@solsynth.dev'),
|
||||||
|
),
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.copyright,
|
||||||
|
title: 'aboutScreenLicenseTitle'.tr(),
|
||||||
|
subtitle: 'aboutScreenLicenseContent'.tr(
|
||||||
|
args: [DateTime.now().year.toString()],
|
||||||
|
),
|
||||||
|
onTap:
|
||||||
|
() => _launchURL(
|
||||||
|
'https://github.com/Solsynth/Solian/blob/v3/LICENSE.txt',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (kIsWeb || !(Platform.isMacOS || Platform.isIOS))
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.favorite,
|
||||||
|
title: 'donate'.tr(),
|
||||||
|
subtitle: 'donateDescription'.tr(),
|
||||||
|
onTap: () {
|
||||||
|
launchUrlString(
|
||||||
|
'https://afdian.com/@littlesheep',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
|
// Copyright
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'aboutScreenCopyright'.tr(
|
||||||
|
args: [DateTime.now().year.toString()],
|
||||||
|
),
|
||||||
|
style: theme.textTheme.bodySmall,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const Gap(1),
|
||||||
|
Text(
|
||||||
|
'aboutScreenMadeWith'.tr(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
).fontSize(10).opacity(0.8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Gap(MediaQuery.of(context).padding.bottom + 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/pods/message.dart';
|
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/screens/notification.dart';
|
import 'package:island/screens/notification.dart';
|
||||||
@@ -13,8 +10,10 @@ import 'package:island/services/responsive.dart';
|
|||||||
import 'package:island/widgets/account/account_name.dart';
|
import 'package:island/widgets/account/account_name.dart';
|
||||||
import 'package:island/widgets/account/status.dart';
|
import 'package:island/widgets/account/status.dart';
|
||||||
import 'package:island/widgets/account/leveling_progress.dart';
|
import 'package:island/widgets/account/leveling_progress.dart';
|
||||||
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:island/widgets/debug_sheet.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
@@ -239,7 +238,7 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
title: Text('abuseReports').tr(),
|
title: Text('abuseReport').tr(),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
leading: const Icon(Symbols.gavel),
|
leading: const Icon(Symbols.gavel),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
@@ -276,30 +275,6 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed('accountSettings');
|
context.pushNamed('accountSettings');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (kDebugMode) const Divider(height: 1).padding(vertical: 8),
|
|
||||||
if (kDebugMode)
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
leading: const Icon(Symbols.copy_all),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
title: Text('Copy access token'),
|
|
||||||
onTap: () async {
|
|
||||||
final tk = ref.watch(tokenProvider);
|
|
||||||
Clipboard.setData(ClipboardData(text: tk!.token));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (kDebugMode)
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
leading: const Icon(Symbols.delete),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
title: Text('Reset database'),
|
|
||||||
onTap: () async {
|
|
||||||
resetDatabase(ref);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Divider(height: 1).padding(vertical: 8),
|
const Divider(height: 1).padding(vertical: 8),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
@@ -311,13 +286,31 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed('about');
|
context.pushNamed('about');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.bug_report),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('debugOptions').tr(),
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DebugSheet(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
leading: const Icon(Symbols.logout),
|
leading: const Icon(Symbols.logout),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
title: Text('logout').tr(),
|
title: Text('logout').tr(),
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
showLoadingModal(context);
|
||||||
|
await apiClient.delete('/id/accounts/me/sessions/current');
|
||||||
|
if (!context.mounted) return;
|
||||||
|
hideLoadingModal(context);
|
||||||
final userNotifier = ref.read(userInfoProvider.notifier);
|
final userNotifier = ref.read(userInfoProvider.notifier);
|
||||||
userNotifier.logOut();
|
userNotifier.logOut();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/auth.dart';
|
import 'package:island/models/auth.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/screens/account/me/settings_auth_factors.dart';
|
import 'package:island/screens/account/me/settings_auth_factors.dart';
|
||||||
@@ -15,7 +15,7 @@ import 'package:island/screens/account/me/settings_contacts.dart';
|
|||||||
import 'package:island/screens/auth/captcha.dart';
|
import 'package:island/screens/auth/captcha.dart';
|
||||||
import 'package:island/screens/auth/login.dart';
|
import 'package:island/screens/auth/login.dart';
|
||||||
import 'package:island/services/responsive.dart';
|
import 'package:island/services/responsive.dart';
|
||||||
import 'package:island/widgets/account/account_session_sheet.dart';
|
import 'package:island/widgets/account/account_devices.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:island/widgets/response.dart';
|
import 'package:island/widgets/response.dart';
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ class AccountConnectionNewSheet extends HookConsumerWidget {
|
|||||||
webAuthenticationOptions: WebAuthenticationOptions(
|
webAuthenticationOptions: WebAuthenticationOptions(
|
||||||
clientId: 'dev.solsynth.solarpass',
|
clientId: 'dev.solsynth.solarpass',
|
||||||
redirectUri: Uri.parse(
|
redirectUri: Uri.parse(
|
||||||
'https://nt.solian.app/auth/callback/apple',
|
'https://id.solian.app/auth/callback/apple',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import 'package:dropdown_button2/dropdown_button2.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
@@ -94,6 +96,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
final usernameController = useTextEditingController(text: user.value!.name);
|
final usernameController = useTextEditingController(text: user.value!.name);
|
||||||
final nicknameController = useTextEditingController(text: user.value!.nick);
|
final nicknameController = useTextEditingController(text: user.value!.nick);
|
||||||
final language = useState(user.value!.language);
|
final language = useState(user.value!.language);
|
||||||
|
final links = useState<List<ProfileLink>>(user.value!.profile.links);
|
||||||
|
|
||||||
void updateBasicInfo() async {
|
void updateBasicInfo() async {
|
||||||
if (!formKeyBasicInfo.currentState!.validate()) return;
|
if (!formKeyBasicInfo.currentState!.validate()) return;
|
||||||
@@ -165,6 +168,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
'location': locationController.text,
|
'location': locationController.text,
|
||||||
'time_zone': timeZoneController.text,
|
'time_zone': timeZoneController.text,
|
||||||
'birthday': birthday.value?.toUtc().toIso8601String(),
|
'birthday': birthday.value?.toUtc().toIso8601String(),
|
||||||
|
'links': links.value,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final userNotifier = ref.read(userInfoProvider.notifier);
|
final userNotifier = ref.read(userInfoProvider.notifier);
|
||||||
@@ -558,6 +562,73 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Text('links').tr().bold().fontSize(18).padding(top: 16),
|
||||||
|
Column(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
for (var i = 0; i < links.value.length; i++)
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
initialValue: links.value[i].name,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'linkKey'.tr(),
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
links.value[i] = links.value[i].copyWith(
|
||||||
|
name: value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTapOutside:
|
||||||
|
(_) =>
|
||||||
|
FocusManager.instance.primaryFocus
|
||||||
|
?.unfocus(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
initialValue: links.value[i].url,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'linkValue'.tr(),
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
links.value[i] = links.value[i].copyWith(
|
||||||
|
url: value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTapOutside:
|
||||||
|
(_) =>
|
||||||
|
FocusManager.instance.primaryFocus
|
||||||
|
?.unfocus(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.delete),
|
||||||
|
onPressed: () {
|
||||||
|
links.value = List.from(links.value)
|
||||||
|
..removeAt(i);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
links.value = List.from(links.value)
|
||||||
|
..add(ProfileLink(name: '', url: ''));
|
||||||
|
},
|
||||||
|
label: Text('addLink').tr(),
|
||||||
|
icon: const Icon(Symbols.add),
|
||||||
|
).padding(top: 8),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: TextButton.icon(
|
child: TextButton.icon(
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/chat.dart';
|
import 'package:island/models/chat.dart';
|
||||||
import 'package:island/models/relationship.dart';
|
import 'package:island/models/relationship.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/pods/event_calendar.dart';
|
import 'package:island/pods/event_calendar.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/services/color.dart';
|
import 'package:island/services/color.dart';
|
||||||
import 'package:island/services/responsive.dart';
|
import 'package:island/services/responsive.dart';
|
||||||
|
import 'package:island/services/text.dart';
|
||||||
import 'package:island/services/time.dart';
|
import 'package:island/services/time.dart';
|
||||||
import 'package:island/services/timezone/native.dart';
|
import 'package:island/services/timezone/native.dart';
|
||||||
import 'package:island/widgets/account/account_name.dart';
|
import 'package:island/widgets/account/account_name.dart';
|
||||||
@@ -30,6 +33,7 @@ import 'package:palette_generator/palette_generator.dart';
|
|||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
part 'profile.g.dart';
|
part 'profile.g.dart';
|
||||||
|
|
||||||
@@ -194,6 +198,15 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
List<Widget> buildSubcolumn(SnAccount data) {
|
List<Widget> buildSubcolumn(SnAccount data) {
|
||||||
return [
|
return [
|
||||||
|
Row(
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.join, size: 17, fill: 1),
|
||||||
|
Text(
|
||||||
|
'joinedAt'.tr(args: [data.createdAt.formatCustom('yyyy-MM-dd')]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
if (data.profile.birthday != null)
|
if (data.profile.birthday != null)
|
||||||
Row(
|
Row(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
@@ -250,6 +263,10 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final user = ref.watch(userInfoProvider);
|
final user = ref.watch(userInfoProvider);
|
||||||
|
final isCurrentUser = useMemoized(
|
||||||
|
() => user.value?.id == account.value?.id,
|
||||||
|
[user, account],
|
||||||
|
);
|
||||||
|
|
||||||
Widget accountBasicInfo(SnAccount data) => Padding(
|
Widget accountBasicInfo(SnAccount data) => Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||||
@@ -320,7 +337,7 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
spacing: 2,
|
spacing: 2,
|
||||||
children: buildSubcolumn(data),
|
children: buildSubcolumn(data),
|
||||||
),
|
),
|
||||||
if (data.profile.timeZone.isNotEmpty)
|
if (data.profile.timeZone.isNotEmpty && !kIsWeb)
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -350,6 +367,32 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
).padding(horizontal: 24, vertical: 16),
|
).padding(horizontal: 24, vertical: 16),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget accountProfileLinks(SnAccount data) => Card(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('links').tr().bold().padding(horizontal: 24, top: 12, bottom: 4),
|
||||||
|
for (final link in data.profile.links)
|
||||||
|
ListTile(
|
||||||
|
title: Text(link.name.capitalizeEachWord()),
|
||||||
|
subtitle: Text(link.url),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
if (!link.url.startsWith('http') && !link.url.contains('://')) {
|
||||||
|
launchUrlString('https://${link.url}');
|
||||||
|
} else {
|
||||||
|
launchUrlString(link.url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
Widget accountAction(SnAccount data) => Card(
|
Widget accountAction(SnAccount data) => Card(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -452,7 +495,7 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 8),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
);
|
);
|
||||||
|
|
||||||
return account.when(
|
return account.when(
|
||||||
@@ -509,9 +552,11 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
SliverToBoxAdapter(child: accountBasicInfo(data)),
|
SliverToBoxAdapter(child: accountBasicInfo(data)),
|
||||||
if (data.badges.isNotEmpty)
|
if (data.badges.isNotEmpty)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: BadgeList(
|
child: Card(
|
||||||
badges: data.badges,
|
child: BadgeList(
|
||||||
).padding(horizontal: 24, bottom: 24),
|
badges: data.badges,
|
||||||
|
).padding(horizontal: 26, vertical: 20),
|
||||||
|
).padding(left: 2, right: 4),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -521,9 +566,10 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
level: data.profile.level,
|
level: data.profile.level,
|
||||||
experience: data.profile.experience,
|
experience: data.profile.experience,
|
||||||
progress: data.profile.levelingProgress,
|
progress: data.profile.levelingProgress,
|
||||||
),
|
).padding(left: 2, right: 4),
|
||||||
if (data.profile.verification != null)
|
if (data.profile.verification != null)
|
||||||
Card(
|
Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
child: VerificationStatusCard(
|
child: VerificationStatusCard(
|
||||||
mark: data.profile.verification!,
|
mark: data.profile.verification!,
|
||||||
),
|
),
|
||||||
@@ -534,6 +580,10 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountProfileBio(data).padding(top: 4),
|
child: accountProfileBio(data).padding(top: 4),
|
||||||
),
|
),
|
||||||
|
if (data.profile.links.isNotEmpty)
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: accountProfileLinks(data),
|
||||||
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountProfileDetail(data),
|
child: accountProfileDetail(data),
|
||||||
),
|
),
|
||||||
@@ -544,7 +594,7 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverGap(24),
|
SliverGap(24),
|
||||||
if (user.value != null)
|
if (user.value != null && !isCurrentUser)
|
||||||
SliverToBoxAdapter(child: accountAction(data)),
|
SliverToBoxAdapter(child: accountAction(data)),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Card(
|
child: Card(
|
||||||
@@ -604,9 +654,11 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
SliverToBoxAdapter(child: accountBasicInfo(data)),
|
SliverToBoxAdapter(child: accountBasicInfo(data)),
|
||||||
if (data.badges.isNotEmpty)
|
if (data.badges.isNotEmpty)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: BadgeList(
|
child: Card(
|
||||||
badges: data.badges,
|
child: BadgeList(
|
||||||
).padding(horizontal: 24, bottom: 24),
|
badges: data.badges,
|
||||||
|
).padding(horizontal: 26, vertical: 20),
|
||||||
|
).padding(horizontal: 4),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -628,12 +680,18 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountProfileBio(data).padding(horizontal: 4),
|
child: accountProfileBio(data).padding(horizontal: 4),
|
||||||
),
|
),
|
||||||
|
if (data.profile.links.isNotEmpty)
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: accountProfileLinks(
|
||||||
|
data,
|
||||||
|
).padding(horizontal: 4),
|
||||||
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountProfileDetail(
|
child: accountProfileDetail(
|
||||||
data,
|
data,
|
||||||
).padding(horizontal: 4),
|
).padding(horizontal: 4),
|
||||||
),
|
),
|
||||||
if (user.value != null)
|
if (user.value != null && !isCurrentUser)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountAction(data).padding(horizontal: 4),
|
child: accountAction(data).padding(horizontal: 4),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ class RelationshipScreen extends HookConsumerWidget {
|
|||||||
final result = await showModalBottomSheet(
|
final result = await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
builder: (context) => AccountPickerSheet(),
|
builder: (context) => AccountPickerSheet(),
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
|
|||||||
@@ -42,6 +42,22 @@ final Map<int, (String, String, IconData)> kFactorTypes = {
|
|||||||
4: ('authFactorPin', 'authFactorPinDescription', Symbols.nest_secure_alarm),
|
4: ('authFactorPin', 'authFactorPinDescription', Symbols.nest_secure_alarm),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Future<String?> getDeviceName() async {
|
||||||
|
if (kIsWeb) return null;
|
||||||
|
String? name;
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
final deviceInfo = await DeviceInfoPlugin().iosInfo;
|
||||||
|
name = deviceInfo.name;
|
||||||
|
} else if (Platform.isAndroid) {
|
||||||
|
final deviceInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
|
name = deviceInfo.name;
|
||||||
|
} else if (Platform.isWindows) {
|
||||||
|
final deviceInfo = await DeviceInfoPlugin().windowsInfo;
|
||||||
|
name = deviceInfo.computerName;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
class LoginScreen extends HookConsumerWidget {
|
class LoginScreen extends HookConsumerWidget {
|
||||||
const LoginScreen({super.key});
|
const LoginScreen({super.key});
|
||||||
|
|
||||||
@@ -198,28 +214,6 @@ class _LoginCheckScreen extends HookConsumerWidget {
|
|||||||
wsNotifier.connect();
|
wsNotifier.connect();
|
||||||
if (context.mounted) Navigator.pop(context, true);
|
if (context.mounted) Navigator.pop(context, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the sessions' device name is available
|
|
||||||
if (!kIsWeb) {
|
|
||||||
String? name;
|
|
||||||
if (Platform.isIOS) {
|
|
||||||
final deviceInfo = await DeviceInfoPlugin().iosInfo;
|
|
||||||
name = deviceInfo.name;
|
|
||||||
} else if (Platform.isAndroid) {
|
|
||||||
final deviceInfo = await DeviceInfoPlugin().androidInfo;
|
|
||||||
name = deviceInfo.name;
|
|
||||||
} else if (Platform.isWindows) {
|
|
||||||
final deviceInfo = await DeviceInfoPlugin().windowsInfo;
|
|
||||||
name = deviceInfo.computerName;
|
|
||||||
}
|
|
||||||
if (name != null) {
|
|
||||||
final client = ref.watch(apiClientProvider);
|
|
||||||
await client.patch(
|
|
||||||
'/id/accounts/me/sessions/current/label',
|
|
||||||
data: jsonEncode(name),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
@@ -578,6 +572,7 @@ class _LoginLookupScreen extends HookConsumerWidget {
|
|||||||
data: {
|
data: {
|
||||||
'account': uname,
|
'account': uname,
|
||||||
'device_id': await getUdid(),
|
'device_id': await getUdid(),
|
||||||
|
'device_name': await getDeviceName(),
|
||||||
'platform':
|
'platform':
|
||||||
kIsWeb
|
kIsWeb
|
||||||
? 1
|
? 1
|
||||||
@@ -628,6 +623,7 @@ class _LoginLookupScreen extends HookConsumerWidget {
|
|||||||
'identity_token': credential.identityToken!,
|
'identity_token': credential.identityToken!,
|
||||||
'authorization_code': credential.authorizationCode,
|
'authorization_code': credential.authorizationCode,
|
||||||
'device_id': await getUdid(),
|
'device_id': await getUdid(),
|
||||||
|
'device_name': await getDeviceName(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -227,6 +227,7 @@ class ChatListScreen extends HookConsumerWidget {
|
|||||||
final result = await showModalBottomSheet(
|
final result = await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
builder: (context) => const AccountPickerSheet(),
|
builder: (context) => const AccountPickerSheet(),
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await apiClient.post(
|
await apiClient.post(
|
||||||
'/chat/${chatRoom.value!.id}/members/me',
|
'/sphere/chat/${chatRoom.value!.id}/members/me',
|
||||||
);
|
);
|
||||||
ref.invalidate(chatroomIdentityProvider(id));
|
ref.invalidate(chatroomIdentityProvider(id));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -929,7 +929,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
if (attachment.isOnCloud) {
|
if (attachment.isOnCloud) {
|
||||||
final client = ref.watch(apiClientProvider);
|
final client = ref.watch(apiClientProvider);
|
||||||
await client.delete(
|
await client.delete(
|
||||||
'/files/${attachment.data.id}',
|
'/drive/files/${attachment.data.id}',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final clone = List.of(attachments.value);
|
final clone = List.of(attachments.value);
|
||||||
|
|||||||
@@ -589,6 +589,7 @@ class _ChatMemberListSheet extends HookConsumerWidget {
|
|||||||
final result = await showModalBottomSheet(
|
final result = await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
builder: (context) => const AccountPickerSheet(),
|
builder: (context) => const AccountPickerSheet(),
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
@@ -727,7 +728,7 @@ class _ChatMemberListSheet extends HookConsumerWidget {
|
|||||||
apiClientProvider,
|
apiClientProvider,
|
||||||
);
|
);
|
||||||
await apiClient.delete(
|
await apiClient.delete(
|
||||||
'/chat/$roomId/members/${member.accountId}',
|
'/sphere/chat/$roomId/members/${member.accountId}',
|
||||||
);
|
);
|
||||||
// Refresh both providers
|
// Refresh both providers
|
||||||
memberNotifier.reset();
|
memberNotifier.reset();
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ class CreatorHubScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
title: const Text('Polls'),
|
title: Text('polls').tr(),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
leading: const Icon(Symbols.poll),
|
leading: const Icon(Symbols.poll),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
@@ -419,7 +419,7 @@ class CreatorHubScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
title: const Text('Web Feeds').tr(),
|
title: const Text('webFeeds').tr(),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
leading: const Icon(Symbols.rss_feed),
|
leading: const Icon(Symbols.rss_feed),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
@@ -659,7 +659,7 @@ class PublisherMemberNotifier extends StateNotifier<PublisherMemberState> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await _apiClient.get(
|
final response = await _apiClient.get(
|
||||||
'/publishers/$publisherUname/members',
|
'/sphere/publishers/$publisherUname/members',
|
||||||
queryParameters: {'offset': offset, 'take': take},
|
queryParameters: {'offset': offset, 'take': take},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -708,6 +708,7 @@ class _PublisherMemberListSheet extends HookConsumerWidget {
|
|||||||
|
|
||||||
Future<void> invitePerson() async {
|
Future<void> invitePerson() async {
|
||||||
final result = await showModalBottomSheet(
|
final result = await showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => const AccountPickerSheet(),
|
builder: (context) => const AccountPickerSheet(),
|
||||||
@@ -719,6 +720,9 @@ class _PublisherMemberListSheet extends HookConsumerWidget {
|
|||||||
'/publishers/$publisherUname/invites',
|
'/publishers/$publisherUname/invites',
|
||||||
data: {'related_user_id': result.id, 'role': 0},
|
data: {'related_user_id': result.id, 'role': 0},
|
||||||
);
|
);
|
||||||
|
// Refresh both providers
|
||||||
|
memberNotifier.reset();
|
||||||
|
await memberNotifier.loadMore();
|
||||||
ref.invalidate(memberListProvider);
|
ref.invalidate(memberListProvider);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
@@ -822,6 +826,9 @@ class _PublisherMemberListSheet extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
).then((value) {
|
).then((value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
// Refresh both providers
|
||||||
|
memberNotifier.reset();
|
||||||
|
memberNotifier.loadMore();
|
||||||
ref.invalidate(memberListProvider);
|
ref.invalidate(memberListProvider);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -843,6 +850,9 @@ class _PublisherMemberListSheet extends HookConsumerWidget {
|
|||||||
await apiClient.delete(
|
await apiClient.delete(
|
||||||
'/publishers/$publisherUname/members/${member.accountId}',
|
'/publishers/$publisherUname/members/${member.accountId}',
|
||||||
);
|
);
|
||||||
|
// Refresh both providers
|
||||||
|
memberNotifier.reset();
|
||||||
|
memberNotifier.loadMore();
|
||||||
ref.invalidate(memberListProvider);
|
ref.invalidate(memberListProvider);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/poll.dart';
|
import 'package:island/models/poll.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:island/widgets/poll/poll_feedback.dart';
|
import 'package:island/widgets/poll/poll_feedback.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@@ -13,17 +14,19 @@ part 'poll_list.g.dart';
|
|||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
class PollListNotifier extends _$PollListNotifier
|
class PollListNotifier extends _$PollListNotifier
|
||||||
with CursorPagingNotifierMixin<SnPoll> {
|
with CursorPagingNotifierMixin<SnPollWithStats> {
|
||||||
static const int _pageSize = 20;
|
static const int _pageSize = 20;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CursorPagingData<SnPoll>> build(String? pubName) {
|
Future<CursorPagingData<SnPollWithStats>> build(String? pubName) {
|
||||||
// immediately load first page
|
// immediately load first page
|
||||||
return fetch(cursor: null);
|
return fetch(cursor: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CursorPagingData<SnPoll>> fetch({required String? cursor}) async {
|
Future<CursorPagingData<SnPollWithStats>> fetch({
|
||||||
|
required String? cursor,
|
||||||
|
}) async {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
final offset = cursor == null ? 0 : int.parse(cursor);
|
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ class PollListNotifier extends _$PollListNotifier
|
|||||||
);
|
);
|
||||||
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||||
final List<dynamic> data = response.data;
|
final List<dynamic> data = response.data;
|
||||||
final items = data.map((json) => SnPoll.fromJson(json)).toList();
|
final items = data.map((json) => SnPollWithStats.fromJson(json)).toList();
|
||||||
|
|
||||||
final hasMore = offset + items.length < total;
|
final hasMore = offset + items.length < total;
|
||||||
final nextCursor = hasMore ? (offset + items.length).toString() : null;
|
final nextCursor = hasMore ? (offset + items.length).toString() : null;
|
||||||
@@ -54,6 +57,13 @@ class PollListNotifier extends _$PollListNotifier
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnPollWithStats> pollWithStats(Ref ref, String id) async {
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
final resp = await apiClient.get('/sphere/polls/$id');
|
||||||
|
return SnPollWithStats.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
class CreatorPollListScreen extends HookConsumerWidget {
|
class CreatorPollListScreen extends HookConsumerWidget {
|
||||||
const CreatorPollListScreen({super.key, required this.pubName});
|
const CreatorPollListScreen({super.key, required this.pubName});
|
||||||
|
|
||||||
@@ -63,14 +73,14 @@ class CreatorPollListScreen extends HookConsumerWidget {
|
|||||||
final result = await GoRouter.of(
|
final result = await GoRouter.of(
|
||||||
context,
|
context,
|
||||||
).pushNamed('creatorPollNew', pathParameters: {'name': pubName});
|
).pushNamed('creatorPollNew', pathParameters: {'name': pubName});
|
||||||
if (result is SnPoll && context.mounted) {
|
if (result is SnPollWithStats && context.mounted) {
|
||||||
Navigator.of(context).maybePop(result);
|
Navigator.of(context).maybePop(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return Scaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(title: const Text('Polls')),
|
appBar: AppBar(title: const Text('Polls')),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () => _createPoll(context),
|
onPressed: () => _createPoll(context),
|
||||||
@@ -91,8 +101,11 @@ class CreatorPollListScreen extends HookConsumerWidget {
|
|||||||
if (index == widgetCount - 1) {
|
if (index == widgetCount - 1) {
|
||||||
return endItemView;
|
return endItemView;
|
||||||
}
|
}
|
||||||
final poll = data.items[index];
|
final pollWithStats = data.items[index];
|
||||||
return _CreatorPollItem(poll: poll, pubName: pubName);
|
return _CreatorPollItem(
|
||||||
|
pollWithStats: pollWithStats,
|
||||||
|
pubName: pubName,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -105,14 +118,14 @@ class CreatorPollListScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
class _CreatorPollItem extends StatelessWidget {
|
class _CreatorPollItem extends StatelessWidget {
|
||||||
final String pubName;
|
final String pubName;
|
||||||
const _CreatorPollItem({required this.poll, required this.pubName});
|
const _CreatorPollItem({required this.pollWithStats, required this.pubName});
|
||||||
|
|
||||||
final SnPoll poll;
|
final SnPollWithStats pollWithStats;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final ended = poll.endedAt;
|
final ended = pollWithStats.endedAt;
|
||||||
final endedText =
|
final endedText =
|
||||||
ended == null
|
ended == null
|
||||||
? 'No end'
|
? 'No end'
|
||||||
@@ -122,15 +135,16 @@ class _CreatorPollItem extends StatelessWidget {
|
|||||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(poll.title ?? 'Untitled poll'),
|
title: Text(pollWithStats.title ?? 'Untitled poll'),
|
||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (poll.description != null && poll.description!.isNotEmpty)
|
if (pollWithStats.description != null &&
|
||||||
|
pollWithStats.description!.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 4),
|
||||||
child: Text(
|
child: Text(
|
||||||
poll.description!,
|
pollWithStats.description!,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -138,7 +152,7 @@ class _CreatorPollItem extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 4),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Questions: ${poll.questions.length} · Ends: $endedText',
|
'Questions: ${pollWithStats.questions.length} · Ends: $endedText',
|
||||||
style: theme.textTheme.bodySmall,
|
style: theme.textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -158,7 +172,7 @@ class _CreatorPollItem extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'creatorPollEdit',
|
'creatorPollEdit',
|
||||||
pathParameters: {'name': pubName, 'id': poll.id},
|
pathParameters: {'name': pubName, 'id': pollWithStats.id},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -169,8 +183,7 @@ class _CreatorPollItem extends StatelessWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder:
|
builder: (context) => PollFeedbackSheet(pollId: pollWithStats.id),
|
||||||
(context) => PollFeedbackSheet(pollId: poll.id, poll: poll),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ part of 'poll_list.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$pollListNotifierHash() => r'd3da24ff6bbb8f35b06d57fc41625dc0312508e4';
|
String _$pollWithStatsHash() => r'6bb910046ce1e09368f9922dbec52fdc2cc86740';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
@@ -29,11 +29,133 @@ class _SystemHash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See also [pollWithStats].
|
||||||
|
@ProviderFor(pollWithStats)
|
||||||
|
const pollWithStatsProvider = PollWithStatsFamily();
|
||||||
|
|
||||||
|
/// See also [pollWithStats].
|
||||||
|
class PollWithStatsFamily extends Family<AsyncValue<SnPollWithStats>> {
|
||||||
|
/// See also [pollWithStats].
|
||||||
|
const PollWithStatsFamily();
|
||||||
|
|
||||||
|
/// See also [pollWithStats].
|
||||||
|
PollWithStatsProvider call(String id) {
|
||||||
|
return PollWithStatsProvider(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
PollWithStatsProvider getProviderOverride(
|
||||||
|
covariant PollWithStatsProvider provider,
|
||||||
|
) {
|
||||||
|
return call(provider.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'pollWithStatsProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [pollWithStats].
|
||||||
|
class PollWithStatsProvider extends AutoDisposeFutureProvider<SnPollWithStats> {
|
||||||
|
/// See also [pollWithStats].
|
||||||
|
PollWithStatsProvider(String id)
|
||||||
|
: this._internal(
|
||||||
|
(ref) => pollWithStats(ref as PollWithStatsRef, id),
|
||||||
|
from: pollWithStatsProvider,
|
||||||
|
name: r'pollWithStatsProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$pollWithStatsHash,
|
||||||
|
dependencies: PollWithStatsFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
PollWithStatsFamily._allTransitiveDependencies,
|
||||||
|
id: id,
|
||||||
|
);
|
||||||
|
|
||||||
|
PollWithStatsProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.id,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<SnPollWithStats> Function(PollWithStatsRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: PollWithStatsProvider._internal(
|
||||||
|
(ref) => create(ref as PollWithStatsRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
id: id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<SnPollWithStats> createElement() {
|
||||||
|
return _PollWithStatsProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is PollWithStatsProvider && other.id == id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, id.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin PollWithStatsRef on AutoDisposeFutureProviderRef<SnPollWithStats> {
|
||||||
|
/// The parameter `id` of this provider.
|
||||||
|
String get id;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PollWithStatsProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<SnPollWithStats>
|
||||||
|
with PollWithStatsRef {
|
||||||
|
_PollWithStatsProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get id => (origin as PollWithStatsProvider).id;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$pollListNotifierHash() => r'd5b822e737788be8982f5cb3b501d460441930c1';
|
||||||
|
|
||||||
abstract class _$PollListNotifier
|
abstract class _$PollListNotifier
|
||||||
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnPoll>> {
|
extends
|
||||||
|
BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnPollWithStats>> {
|
||||||
late final String? pubName;
|
late final String? pubName;
|
||||||
|
|
||||||
FutureOr<CursorPagingData<SnPoll>> build(String? pubName);
|
FutureOr<CursorPagingData<SnPollWithStats>> build(String? pubName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See also [PollListNotifier].
|
/// See also [PollListNotifier].
|
||||||
@@ -42,7 +164,7 @@ const pollListNotifierProvider = PollListNotifierFamily();
|
|||||||
|
|
||||||
/// See also [PollListNotifier].
|
/// See also [PollListNotifier].
|
||||||
class PollListNotifierFamily
|
class PollListNotifierFamily
|
||||||
extends Family<AsyncValue<CursorPagingData<SnPoll>>> {
|
extends Family<AsyncValue<CursorPagingData<SnPollWithStats>>> {
|
||||||
/// See also [PollListNotifier].
|
/// See also [PollListNotifier].
|
||||||
const PollListNotifierFamily();
|
const PollListNotifierFamily();
|
||||||
|
|
||||||
@@ -78,7 +200,7 @@ class PollListNotifierProvider
|
|||||||
extends
|
extends
|
||||||
AutoDisposeAsyncNotifierProviderImpl<
|
AutoDisposeAsyncNotifierProviderImpl<
|
||||||
PollListNotifier,
|
PollListNotifier,
|
||||||
CursorPagingData<SnPoll>
|
CursorPagingData<SnPollWithStats>
|
||||||
> {
|
> {
|
||||||
/// See also [PollListNotifier].
|
/// See also [PollListNotifier].
|
||||||
PollListNotifierProvider(String? pubName)
|
PollListNotifierProvider(String? pubName)
|
||||||
@@ -109,7 +231,7 @@ class PollListNotifierProvider
|
|||||||
final String? pubName;
|
final String? pubName;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<CursorPagingData<SnPoll>> runNotifierBuild(
|
FutureOr<CursorPagingData<SnPollWithStats>> runNotifierBuild(
|
||||||
covariant PollListNotifier notifier,
|
covariant PollListNotifier notifier,
|
||||||
) {
|
) {
|
||||||
return notifier.build(pubName);
|
return notifier.build(pubName);
|
||||||
@@ -134,7 +256,7 @@ class PollListNotifierProvider
|
|||||||
@override
|
@override
|
||||||
AutoDisposeAsyncNotifierProviderElement<
|
AutoDisposeAsyncNotifierProviderElement<
|
||||||
PollListNotifier,
|
PollListNotifier,
|
||||||
CursorPagingData<SnPoll>
|
CursorPagingData<SnPollWithStats>
|
||||||
>
|
>
|
||||||
createElement() {
|
createElement() {
|
||||||
return _PollListNotifierProviderElement(this);
|
return _PollListNotifierProviderElement(this);
|
||||||
@@ -157,7 +279,7 @@ class PollListNotifierProvider
|
|||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
mixin PollListNotifierRef
|
mixin PollListNotifierRef
|
||||||
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnPoll>> {
|
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnPollWithStats>> {
|
||||||
/// The parameter `pubName` of this provider.
|
/// The parameter `pubName` of this provider.
|
||||||
String? get pubName;
|
String? get pubName;
|
||||||
}
|
}
|
||||||
@@ -166,7 +288,7 @@ class _PollListNotifierProviderElement
|
|||||||
extends
|
extends
|
||||||
AutoDisposeAsyncNotifierProviderElement<
|
AutoDisposeAsyncNotifierProviderElement<
|
||||||
PollListNotifier,
|
PollListNotifier,
|
||||||
CursorPagingData<SnPoll>
|
CursorPagingData<SnPollWithStats>
|
||||||
>
|
>
|
||||||
with PollListNotifierRef {
|
with PollListNotifierRef {
|
||||||
_PollListNotifierProviderElement(super.provider);
|
_PollListNotifierProviderElement(super.provider);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class StickerPackDetailScreen extends HookConsumerWidget {
|
|||||||
try {
|
try {
|
||||||
showLoadingModal(context);
|
showLoadingModal(context);
|
||||||
final apiClient = ref.watch(apiClientProvider);
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
await apiClient.delete('/stickers/$id/content/${sticker.id}');
|
await apiClient.delete('/sphere/stickers/$id/content/${sticker.id}');
|
||||||
ref.invalidate(stickerPackContentProvider(id));
|
ref.invalidate(stickerPackContentProvider(id));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
@@ -180,6 +180,7 @@ class StickerPackDetailScreen extends HookConsumerWidget {
|
|||||||
.pushNamed(
|
.pushNamed(
|
||||||
'creatorStickerEdit',
|
'creatorStickerEdit',
|
||||||
pathParameters: {
|
pathParameters: {
|
||||||
|
'name': pubName,
|
||||||
'packId': id,
|
'packId': id,
|
||||||
'id': sticker.id,
|
'id': sticker.id,
|
||||||
},
|
},
|
||||||
@@ -297,7 +298,7 @@ class _StickerPackActionMenu extends HookConsumerWidget {
|
|||||||
).then((confirm) {
|
).then((confirm) {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
final client = ref.watch(apiClientProvider);
|
final client = ref.watch(apiClientProvider);
|
||||||
client.delete('/stickers/$packId');
|
client.delete('/sphere/stickers/$packId');
|
||||||
ref.invalidate(stickerPacksNotifierProvider);
|
ref.invalidate(stickerPacksNotifierProvider);
|
||||||
if (context.mounted) context.pop(true);
|
if (context.mounted) context.pop(true);
|
||||||
}
|
}
|
||||||
@@ -325,7 +326,7 @@ Future<SnSticker?> stickerPackSticker(
|
|||||||
if (query == null) return null;
|
if (query == null) return null;
|
||||||
final apiClient = ref.watch(apiClientProvider);
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
final resp = await apiClient.get(
|
final resp = await apiClient.get(
|
||||||
'/stickers/${query.packId}/content/${query.id}',
|
'/sphere/stickers/${query.packId}/content/${query.id}',
|
||||||
);
|
);
|
||||||
if (resp.data == null) return null;
|
if (resp.data == null) return null;
|
||||||
return SnSticker.fromJson(resp.data);
|
return SnSticker.fromJson(resp.data);
|
||||||
@@ -379,8 +380,8 @@ class EditStickersScreen extends HookConsumerWidget {
|
|||||||
try {
|
try {
|
||||||
final resp = await apiClient.request(
|
final resp = await apiClient.request(
|
||||||
id == null
|
id == null
|
||||||
? '/stickers/$packId/content'
|
? '/sphere/stickers/$packId/content'
|
||||||
: '/stickers/$packId/content/$id',
|
: '/sphere/stickers/$packId/content/$id',
|
||||||
data: {'slug': slugController.text, 'image_id': imageController.text},
|
data: {'slug': slugController.text, 'image_id': imageController.text},
|
||||||
options: Options(method: id == null ? 'POST' : 'PATCH'),
|
options: Options(method: id == null ? 'POST' : 'PATCH'),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ class _StickerPackContentProviderElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$stickerPackStickerHash() =>
|
String _$stickerPackStickerHash() =>
|
||||||
r'36f524c047e632236d5597aaaa8678ed86599602';
|
r'5c553666b3a63530bdebae4b7cd52f303c5ab3a0';
|
||||||
|
|
||||||
/// See also [stickerPackSticker].
|
/// See also [stickerPackSticker].
|
||||||
@ProviderFor(stickerPackSticker)
|
@ProviderFor(stickerPackSticker)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class StickersScreen extends HookConsumerWidget {
|
|||||||
context
|
context
|
||||||
.pushNamed(
|
.pushNamed(
|
||||||
'creatorStickerPackNew',
|
'creatorStickerPackNew',
|
||||||
queryParameters: {'name': pubName},
|
pathParameters: {'name': pubName},
|
||||||
)
|
)
|
||||||
.then((value) {
|
.then((value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
@@ -187,10 +187,8 @@ class EditStickerPacksScreen extends HookConsumerWidget {
|
|||||||
'description': descriptionController.text,
|
'description': descriptionController.text,
|
||||||
'prefix': prefixController.text,
|
'prefix': prefixController.text,
|
||||||
},
|
},
|
||||||
options: Options(
|
queryParameters: {'pub': pubName},
|
||||||
method: packId == null ? 'POST' : 'PATCH',
|
options: Options(method: packId == null ? 'POST' : 'PATCH'),
|
||||||
headers: {'X-Pub': pubName},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
context.pop(SnStickerPack.fromJson(resp.data));
|
context.pop(SnStickerPack.fromJson(resp.data));
|
||||||
|
|||||||
@@ -114,10 +114,11 @@ class WebFeedEditScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
return feedAsync.when(
|
return feedAsync.when(
|
||||||
loading:
|
loading:
|
||||||
() =>
|
() => const AppScaffold(
|
||||||
const Scaffold(body: Center(child: CircularProgressIndicator())),
|
body: Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
error:
|
error:
|
||||||
(error, stack) => Scaffold(
|
(error, stack) => AppScaffold(
|
||||||
appBar: AppBar(title: const Text('Error')),
|
appBar: AppBar(title: const Text('Error')),
|
||||||
body: Center(child: Text('Error: $error')),
|
body: Center(child: Text('Error: $error')),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ Future<DeveloperStats?> developerStats(Ref ref, String? uname) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
Future<List<SnPublisher>> developers(Ref ref) async {
|
Future<List<SnDeveloper>> developers(Ref ref) async {
|
||||||
final client = ref.watch(apiClientProvider);
|
final client = ref.watch(apiClientProvider);
|
||||||
final resp = await client.get('/develop/developers');
|
final resp = await client.get('/develop/developers');
|
||||||
return resp.data
|
return resp.data
|
||||||
.map((e) => SnPublisher.fromJson(e))
|
.map((e) => SnDeveloper.fromJson(e))
|
||||||
.cast<SnPublisher>()
|
.cast<SnDeveloper>()
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,25 +74,25 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final developers = ref.watch(developersProvider);
|
final developers = ref.watch(developersProvider);
|
||||||
final currentDeveloper = useState<SnPublisher?>(
|
final currentDeveloper = useState<SnDeveloper?>(
|
||||||
developers.value?.firstOrNull,
|
developers.value?.firstOrNull,
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<DropdownMenuItem<SnPublisher>> developersMenu = developers.when(
|
final List<DropdownMenuItem<SnDeveloper>> developersMenu = developers.when(
|
||||||
data:
|
data:
|
||||||
(data) =>
|
(data) =>
|
||||||
data
|
data
|
||||||
.map(
|
.map(
|
||||||
(item) => DropdownMenuItem<SnPublisher>(
|
(item) => DropdownMenuItem<SnDeveloper>(
|
||||||
value: item,
|
value: item,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
leading: ProfilePictureWidget(
|
leading: ProfilePictureWidget(
|
||||||
radius: 16,
|
radius: 16,
|
||||||
fileId: item.picture?.id,
|
fileId: item.publisher?.picture?.id,
|
||||||
),
|
),
|
||||||
title: Text(item.nick),
|
title: Text(item.publisher!.nick),
|
||||||
subtitle: Text('@${item.name}'),
|
subtitle: Text('@${item.publisher!.name}'),
|
||||||
trailing:
|
trailing:
|
||||||
currentDeveloper.value?.id == item.id
|
currentDeveloper.value?.id == item.id
|
||||||
? const Icon(Icons.check)
|
? const Icon(Icons.check)
|
||||||
@@ -107,7 +107,7 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final developerStats = ref.watch(
|
final developerStats = ref.watch(
|
||||||
developerStatsProvider(currentDeveloper.value?.name),
|
developerStatsProvider(currentDeveloper.value?.publisher?.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
@@ -117,7 +117,7 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
title: Text('developerHub').tr(),
|
title: Text('developerHub').tr(),
|
||||||
actions: [
|
actions: [
|
||||||
DropdownButtonHideUnderline(
|
DropdownButtonHideUnderline(
|
||||||
child: DropdownButton2<SnPublisher>(
|
child: DropdownButton2<SnDeveloper>(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
value: currentDeveloper.value,
|
value: currentDeveloper.value,
|
||||||
hint: CircleAvatar(
|
hint: CircleAvatar(
|
||||||
@@ -139,7 +139,7 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
...developersMenu.map(
|
...developersMenu.map(
|
||||||
(e) => ProfilePictureWidget(
|
(e) => ProfilePictureWidget(
|
||||||
radius: 16,
|
radius: 16,
|
||||||
fileId: e.value?.picture?.id,
|
fileId: e.value?.publisher?.picture?.id,
|
||||||
).center().padding(right: 8),
|
).center().padding(right: 8),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@@ -193,10 +193,12 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
...(developers.value?.map(
|
...(developers.value?.map(
|
||||||
(developer) => ListTile(
|
(developer) => ListTile(
|
||||||
leading: ProfilePictureWidget(
|
leading: ProfilePictureWidget(
|
||||||
file: developer.picture,
|
file: developer.publisher?.picture,
|
||||||
|
),
|
||||||
|
title: Text(developer.publisher!.nick),
|
||||||
|
subtitle: Text(
|
||||||
|
'@${developer.publisher!.name}',
|
||||||
),
|
),
|
||||||
title: Text(developer.nick),
|
|
||||||
subtitle: Text('@${developer.name}'),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
currentDeveloper.value = developer;
|
currentDeveloper.value = developer;
|
||||||
},
|
},
|
||||||
@@ -243,7 +245,8 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed(
|
context.pushNamed(
|
||||||
'developerApps',
|
'developerApps',
|
||||||
pathParameters: {
|
pathParameters: {
|
||||||
'name': currentDeveloper.value!.name,
|
'name':
|
||||||
|
currentDeveloper.value!.publisher!.name,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -257,7 +260,9 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
error: err,
|
error: err,
|
||||||
onRetry: () {
|
onRetry: () {
|
||||||
ref.invalidate(
|
ref.invalidate(
|
||||||
developerStatsProvider(currentDeveloper.value?.name),
|
developerStatsProvider(
|
||||||
|
currentDeveloper.value?.publisher!.name,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -354,7 +359,7 @@ class _DeveloperEnrollmentSheet extends HookConsumerWidget {
|
|||||||
? Center(
|
? Center(
|
||||||
child:
|
child:
|
||||||
Text(
|
Text(
|
||||||
'noPublishersToEnroll',
|
'noDevelopersToEnroll',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
).tr(),
|
).tr(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -149,12 +149,12 @@ class _DeveloperStatsProviderElement
|
|||||||
String? get uname => (origin as DeveloperStatsProvider).uname;
|
String? get uname => (origin as DeveloperStatsProvider).uname;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$developersHash() => r'04f25db31f511f651a5add128d56631236ed0b39';
|
String _$developersHash() => r'252341098617ac398ce133994453f318dd3edbd2';
|
||||||
|
|
||||||
/// See also [developers].
|
/// See also [developers].
|
||||||
@ProviderFor(developers)
|
@ProviderFor(developers)
|
||||||
final developersProvider =
|
final developersProvider =
|
||||||
AutoDisposeFutureProvider<List<SnPublisher>>.internal(
|
AutoDisposeFutureProvider<List<SnDeveloper>>.internal(
|
||||||
developers,
|
developers,
|
||||||
name: r'developersProvider',
|
name: r'developersProvider',
|
||||||
debugGetCreateSourceHash:
|
debugGetCreateSourceHash:
|
||||||
@@ -167,6 +167,6 @@ final developersProvider =
|
|||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnPublisher>>;
|
typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnDeveloper>>;
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/websocket.dart';
|
import 'package:island/pods/websocket.dart';
|
||||||
import 'package:island/route.dart';
|
import 'package:island/route.dart';
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import 'package:gap/gap.dart';
|
|||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/models/poll.dart';
|
import 'package:island/models/poll.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class PollEditorState {
|
class PollEditorState {
|
||||||
String? id; // for editing
|
String? id; // for editing
|
||||||
@@ -109,7 +111,7 @@ class PollEditor extends Notifier<PollEditorState> {
|
|||||||
? [
|
? [
|
||||||
SnPollOption(
|
SnPollOption(
|
||||||
id: const Uuid().v4(),
|
id: const Uuid().v4(),
|
||||||
label: 'Option 1',
|
label: 'pollOptionDefaultLabel'.tr(),
|
||||||
order: 0,
|
order: 0,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -190,7 +192,7 @@ class PollEditor extends Notifier<PollEditorState> {
|
|||||||
: [
|
: [
|
||||||
SnPollOption(
|
SnPollOption(
|
||||||
id: const Uuid().v4(),
|
id: const Uuid().v4(),
|
||||||
label: 'Option 1',
|
label: 'pollOptionDefaultLabel'.tr(),
|
||||||
order: 0,
|
order: 0,
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
@@ -388,7 +390,7 @@ class PollEditorScreen extends ConsumerWidget {
|
|||||||
data: body,
|
data: body,
|
||||||
));
|
));
|
||||||
|
|
||||||
showSnackBar(isUpdate ? 'Poll updated.' : 'Poll created.');
|
showSnackBar(isUpdate ? 'pollUpdated'.tr() : 'pollCreated'.tr());
|
||||||
|
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
Navigator.of(context).maybePop(res.data);
|
Navigator.of(context).maybePop(res.data);
|
||||||
@@ -413,13 +415,13 @@ class PollEditorScreen extends ConsumerWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(model.id == null ? 'Create Poll' : 'Edit Poll'),
|
title: Text(model.id == null ? 'pollCreate'.tr() : 'pollEdit'.tr()),
|
||||||
actions: [
|
actions: [
|
||||||
if (kDebugMode)
|
if (kDebugMode)
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: 'Preview JSON (debug)',
|
tooltip: 'pollPreviewJsonDebug'.tr(),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_showDebugPreview(context, model);
|
_showDebugPreview(context, model);
|
||||||
},
|
},
|
||||||
@@ -428,175 +430,175 @@ class PollEditorScreen extends ConsumerWidget {
|
|||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: Column(
|
||||||
child: Form(
|
children: [
|
||||||
key: ValueKey(model.id),
|
Expanded(
|
||||||
child: ListView(
|
child: Form(
|
||||||
padding: const EdgeInsets.all(16),
|
key: ValueKey(model.id),
|
||||||
children: [
|
child: ListView(
|
||||||
TextFormField(
|
padding: const EdgeInsets.all(16),
|
||||||
initialValue: model.title ?? '',
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Title',
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textInputAction: TextInputAction.next,
|
|
||||||
maxLength: 256,
|
|
||||||
onChanged: notifier.setTitle,
|
|
||||||
onTapOutside:
|
|
||||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
validator: (v) {
|
|
||||||
if (v == null || v.trim().isEmpty) {
|
|
||||||
return 'Title is required';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Gap(12),
|
|
||||||
TextFormField(
|
|
||||||
initialValue: model.description ?? '',
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Description',
|
|
||||||
alignLabelWithHint: true,
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
maxLines: 3,
|
|
||||||
maxLength: 4096,
|
|
||||||
onChanged: notifier.setDescription,
|
|
||||||
onTapOutside:
|
|
||||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
|
||||||
const Gap(12),
|
|
||||||
_EndDatePicker(
|
|
||||||
value: model.endedAt,
|
|
||||||
onChanged: notifier.setEndedAt,
|
|
||||||
),
|
|
||||||
const Gap(24),
|
|
||||||
Row(
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
TextFormField(
|
||||||
'Questions',
|
initialValue: model.title ?? '',
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
decoration: InputDecoration(
|
||||||
),
|
labelText: 'title'.tr(),
|
||||||
const Spacer(),
|
border: OutlineInputBorder(
|
||||||
MenuAnchor(
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
builder: (context, controller, child) {
|
),
|
||||||
return FilledButton.icon(
|
),
|
||||||
onPressed: () {
|
textInputAction: TextInputAction.next,
|
||||||
controller.isOpen
|
maxLength: 256,
|
||||||
? controller.close()
|
onChanged: notifier.setTitle,
|
||||||
: controller.open();
|
onTapOutside:
|
||||||
},
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
icon: const Icon(Icons.add),
|
validator: (v) {
|
||||||
label: const Text('Add question'),
|
if (v == null || v.trim().isEmpty) {
|
||||||
);
|
return 'pollTitleRequired'.tr();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
menuChildren:
|
|
||||||
SnPollQuestionType.values
|
|
||||||
.map(
|
|
||||||
(t) => MenuItemButton(
|
|
||||||
leadingIcon: Icon(_iconForType(t)),
|
|
||||||
onPressed: () => notifier.addQuestion(t),
|
|
||||||
child: Text(_labelForType(t)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
|
const Gap(12),
|
||||||
|
TextFormField(
|
||||||
|
initialValue: model.description ?? '',
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'description'.tr(),
|
||||||
|
alignLabelWithHint: true,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
maxLines: 3,
|
||||||
|
maxLength: 4096,
|
||||||
|
onChanged: notifier.setDescription,
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
_EndDatePicker(
|
||||||
|
value: model.endedAt,
|
||||||
|
onChanged: notifier.setEndedAt,
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'questions'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
MenuAnchor(
|
||||||
|
builder: (context, controller, child) {
|
||||||
|
return FilledButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
controller.isOpen
|
||||||
|
? controller.close()
|
||||||
|
: controller.open();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: Text('pollAddQuestion'.tr()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
menuChildren:
|
||||||
|
SnPollQuestionType.values
|
||||||
|
.map(
|
||||||
|
(t) => MenuItemButton(
|
||||||
|
leadingIcon: Icon(_iconForType(t)),
|
||||||
|
onPressed: () => notifier.addQuestion(t),
|
||||||
|
child: Text(_labelForType(t)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
if (model.questions.isEmpty)
|
||||||
|
_EmptyState(
|
||||||
|
title: 'pollNoQuestionsYet'.tr(),
|
||||||
|
subtitle:
|
||||||
|
'pollNoQuestionsHint'.tr(),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
ReorderableListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: model.questions.length,
|
||||||
|
onReorder: (oldIndex, newIndex) {
|
||||||
|
// Convert to stepwise moves using provided functions
|
||||||
|
if (newIndex > oldIndex) newIndex -= 1;
|
||||||
|
final steps = newIndex - oldIndex;
|
||||||
|
if (steps == 0) return;
|
||||||
|
if (steps > 0) {
|
||||||
|
for (int i = 0; i < steps; i++) {
|
||||||
|
notifier.moveQuestionDown(oldIndex + i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i > steps; i--) {
|
||||||
|
notifier.moveQuestionUp(oldIndex + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buildDefaultDragHandles: false,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final q = model.questions[index];
|
||||||
|
return Card(
|
||||||
|
key: ValueKey('q_$index'),
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_QuestionHeader(
|
||||||
|
index: index,
|
||||||
|
question: q,
|
||||||
|
onMoveUp:
|
||||||
|
index > 0
|
||||||
|
? () => notifier.moveQuestionUp(index)
|
||||||
|
: null,
|
||||||
|
onMoveDown:
|
||||||
|
index < model.questions.length - 1
|
||||||
|
? () => notifier.moveQuestionDown(index)
|
||||||
|
: null,
|
||||||
|
onDelete: () => notifier.removeQuestion(index),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: _QuestionEditor(
|
||||||
|
index: index,
|
||||||
|
question: q,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Gap(96),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Gap(8),
|
),
|
||||||
if (model.questions.isEmpty)
|
),
|
||||||
_EmptyState(
|
Row(
|
||||||
title: 'No questions yet',
|
children: [
|
||||||
subtitle: 'Use "Add question" to start building your poll.',
|
OutlinedButton.icon(
|
||||||
)
|
onPressed: () {
|
||||||
else
|
Navigator.of(context).maybePop();
|
||||||
ReorderableListView.builder(
|
},
|
||||||
shrinkWrap: true,
|
icon: const Icon(Icons.close),
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
label: Text('cancel'.tr()),
|
||||||
itemCount: model.questions.length,
|
),
|
||||||
onReorder: (oldIndex, newIndex) {
|
const Spacer(),
|
||||||
// Convert to stepwise moves using provided functions
|
FilledButton.icon(
|
||||||
if (newIndex > oldIndex) newIndex -= 1;
|
onPressed: () {
|
||||||
final steps = newIndex - oldIndex;
|
_submitPoll(context, ref);
|
||||||
if (steps == 0) return;
|
},
|
||||||
if (steps > 0) {
|
icon: const Icon(Icons.cloud_upload_outlined),
|
||||||
for (int i = 0; i < steps; i++) {
|
label: Text(model.id == null ? 'create'.tr() : 'update'.tr()),
|
||||||
notifier.moveQuestionDown(oldIndex + i);
|
),
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i > steps; i--) {
|
|
||||||
notifier.moveQuestionUp(oldIndex + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buildDefaultDragHandles: false,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final q = model.questions[index];
|
|
||||||
return Card(
|
|
||||||
key: ValueKey('q_$index'),
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
_QuestionHeader(
|
|
||||||
index: index,
|
|
||||||
question: q,
|
|
||||||
onMoveUp:
|
|
||||||
index > 0
|
|
||||||
? () => notifier.moveQuestionUp(index)
|
|
||||||
: null,
|
|
||||||
onMoveDown:
|
|
||||||
index < model.questions.length - 1
|
|
||||||
? () => notifier.moveQuestionDown(index)
|
|
||||||
: null,
|
|
||||||
onDelete: () => notifier.removeQuestion(index),
|
|
||||||
),
|
|
||||||
const Divider(height: 1),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: _QuestionEditor(index: index, question: q),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Gap(96),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
|
||||||
bottomNavigationBar: Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(
|
|
||||||
16,
|
|
||||||
8,
|
|
||||||
16,
|
|
||||||
16 + MediaQuery.of(context).padding.bottom,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).maybePop();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.close),
|
|
||||||
label: const Text('Cancel'),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
FilledButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
_submitPoll(context, ref);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.cloud_upload_outlined),
|
|
||||||
label: Text(model.id == null ? 'Create' : 'Update'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -636,14 +638,14 @@ class PollEditorScreen extends ConsumerWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
builder:
|
builder:
|
||||||
(_) => AlertDialog(
|
(_) => AlertDialog(
|
||||||
title: const Text('Debug Preview'),
|
title: Text('pollDebugPreview'.tr()),
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: SelectableText(buf.toString()),
|
child: SelectableText(buf.toString()),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('Close'),
|
child: Text('close'.tr()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -672,15 +674,15 @@ IconData _iconForType(SnPollQuestionType t) {
|
|||||||
String _labelForType(SnPollQuestionType t) {
|
String _labelForType(SnPollQuestionType t) {
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case SnPollQuestionType.singleChoice:
|
case SnPollQuestionType.singleChoice:
|
||||||
return 'Single choice';
|
return 'pollQuestionTypeSingleChoice'.tr();
|
||||||
case SnPollQuestionType.multipleChoice:
|
case SnPollQuestionType.multipleChoice:
|
||||||
return 'Multiple choice';
|
return 'pollQuestionTypeMultipleChoice'.tr();
|
||||||
case SnPollQuestionType.freeText:
|
case SnPollQuestionType.freeText:
|
||||||
return 'Free text';
|
return 'pollQuestionTypeFreeText'.tr();
|
||||||
case SnPollQuestionType.yesNo:
|
case SnPollQuestionType.yesNo:
|
||||||
return 'Yes / No';
|
return 'pollQuestionTypeYesNo'.tr();
|
||||||
case SnPollQuestionType.rating:
|
case SnPollQuestionType.rating:
|
||||||
return 'Rating';
|
return 'pollQuestionTypeRating'.tr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,8 +699,8 @@ class _EndDatePicker extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InputDecorator(
|
child: InputDecorator(
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'End date & time (optional)',
|
labelText: 'pollEndDateOptional'.tr(),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
),
|
||||||
@@ -710,7 +712,7 @@ class _EndDatePicker extends StatelessWidget {
|
|||||||
Icon(Icons.event, color: Theme.of(context).colorScheme.primary),
|
Icon(Icons.event, color: Theme.of(context).colorScheme.primary),
|
||||||
Text(
|
Text(
|
||||||
value == null
|
value == null
|
||||||
? 'Not set'
|
? 'notSet'.tr()
|
||||||
: MaterialLocalizations.of(
|
: MaterialLocalizations.of(
|
||||||
context,
|
context,
|
||||||
).formatFullDate(value!),
|
).formatFullDate(value!),
|
||||||
@@ -758,12 +760,12 @@ class _EndDatePicker extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
onChanged(dt);
|
onChanged(dt);
|
||||||
},
|
},
|
||||||
child: const Text('Pick'),
|
child: Text('pick'.tr()),
|
||||||
),
|
),
|
||||||
if (value != null)
|
if (value != null)
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => onChanged(null),
|
onPressed: () => onChanged(null),
|
||||||
child: const Text('Clear'),
|
child: Text('clear'.tr()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -798,7 +800,7 @@ class _QuestionHeader extends StatelessWidget {
|
|||||||
child: const Icon(Icons.drag_handle),
|
child: const Icon(Icons.drag_handle),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
question.title.isEmpty ? 'Untitled question' : question.title,
|
question.title.isEmpty ? 'pollUntitledQuestion'.tr() : question.title,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -807,17 +809,17 @@ class _QuestionHeader extends StatelessWidget {
|
|||||||
spacing: 4,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: 'Move up',
|
tooltip: 'moveUp'.tr(),
|
||||||
onPressed: onMoveUp,
|
onPressed: onMoveUp,
|
||||||
icon: const Icon(Icons.arrow_upward),
|
icon: const Icon(Icons.arrow_upward),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: 'Move down',
|
tooltip: 'moveDown'.tr(),
|
||||||
onPressed: onMoveDown,
|
onPressed: onMoveDown,
|
||||||
icon: const Icon(Icons.arrow_downward),
|
icon: const Icon(Icons.arrow_downward),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: 'Delete',
|
tooltip: 'delete'.tr(),
|
||||||
onPressed: onDelete,
|
onPressed: onDelete,
|
||||||
icon: const Icon(Icons.delete_outline),
|
icon: const Icon(Icons.delete_outline),
|
||||||
color: Theme.of(context).colorScheme.error,
|
color: Theme.of(context).colorScheme.error,
|
||||||
@@ -852,7 +854,7 @@ class _QuestionEditor extends ConsumerWidget {
|
|||||||
onChanged: (t) => notifier.setQuestionType(index, t),
|
onChanged: (t) => notifier.setQuestionType(index, t),
|
||||||
),
|
),
|
||||||
FilterChip(
|
FilterChip(
|
||||||
label: const Text('Required'),
|
label: Text('required'.tr()),
|
||||||
selected: question.isRequired,
|
selected: question.isRequired,
|
||||||
onSelected: (v) => notifier.setQuestionRequired(index, v),
|
onSelected: (v) => notifier.setQuestionRequired(index, v),
|
||||||
avatar: Icon(
|
avatar: Icon(
|
||||||
@@ -866,8 +868,8 @@ class _QuestionEditor extends ConsumerWidget {
|
|||||||
const Gap(12),
|
const Gap(12),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: question.title,
|
initialValue: question.title,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Question title',
|
labelText: 'pollQuestionTitle'.tr(),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
),
|
||||||
@@ -878,7 +880,7 @@ class _QuestionEditor extends ConsumerWidget {
|
|||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
validator: (v) {
|
validator: (v) {
|
||||||
if (v == null || v.trim().isEmpty) {
|
if (v == null || v.trim().isEmpty) {
|
||||||
return 'Question title is required';
|
return 'pollQuestionTitleRequired'.tr();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
@@ -886,8 +888,8 @@ class _QuestionEditor extends ConsumerWidget {
|
|||||||
const Gap(12),
|
const Gap(12),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: question.description ?? '',
|
initialValue: question.description ?? '',
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Question description (optional)',
|
labelText: 'pollQuestionDescriptionOptional'.tr(),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
),
|
||||||
@@ -901,7 +903,7 @@ class _QuestionEditor extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
if (question.options != null) ...[
|
if (question.options != null) ...[
|
||||||
const Gap(16),
|
const Gap(16),
|
||||||
Text('Options', style: Theme.of(context).textTheme.titleMedium),
|
Text('options'.tr(), style: Theme.of(context).textTheme.titleMedium),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
_OptionsEditor(index: index, options: question.options!),
|
_OptionsEditor(index: index, options: question.options!),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
@@ -910,7 +912,7 @@ class _QuestionEditor extends ConsumerWidget {
|
|||||||
child: OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
onPressed: () => notifier.addOption(index),
|
onPressed: () => notifier.addOption(index),
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
label: const Text('Add option'),
|
label: Text('pollAddOption'.tr()),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -936,8 +938,8 @@ class _QuestionTypePicker extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DropdownButtonFormField<SnPollQuestionType>(
|
return DropdownButtonFormField<SnPollQuestionType>(
|
||||||
value: value,
|
value: value,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Type',
|
labelText: 'Type'.tr(),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
),
|
||||||
@@ -986,8 +988,8 @@ class _OptionsEditor extends ConsumerWidget {
|
|||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
key: ValueKey(options[i].id),
|
key: ValueKey(options[i].id),
|
||||||
initialValue: options[i].label,
|
initialValue: options[i].label,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Option label',
|
labelText: 'pollOptionLabel'.tr(),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
),
|
||||||
@@ -1002,7 +1004,7 @@ class _OptionsEditor extends ConsumerWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: 40,
|
width: 40,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: 'Move up',
|
tooltip: 'moveUp'.tr(),
|
||||||
onPressed:
|
onPressed:
|
||||||
i > 0 ? () => notifier.moveOptionUp(index, i) : null,
|
i > 0 ? () => notifier.moveOptionUp(index, i) : null,
|
||||||
icon: const Icon(Icons.arrow_upward),
|
icon: const Icon(Icons.arrow_upward),
|
||||||
@@ -1011,7 +1013,7 @@ class _OptionsEditor extends ConsumerWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: 40,
|
width: 40,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: 'Move down',
|
tooltip: 'moveDown'.tr(),
|
||||||
onPressed:
|
onPressed:
|
||||||
i < options.length - 1
|
i < options.length - 1
|
||||||
? () => notifier.moveOptionDown(index, i)
|
? () => notifier.moveOptionDown(index, i)
|
||||||
@@ -1022,7 +1024,7 @@ class _OptionsEditor extends ConsumerWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: 40,
|
width: 40,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: 'Delete',
|
tooltip: 'delete'.tr(),
|
||||||
onPressed: () => notifier.removeOption(index, i),
|
onPressed: () => notifier.removeOption(index, i),
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
),
|
),
|
||||||
@@ -1047,7 +1049,7 @@ class _TextAnswerPreview extends StatelessWidget {
|
|||||||
maxLines: long ? 4 : 1,
|
maxLines: long ? 4 : 1,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText:
|
labelText:
|
||||||
long ? 'Long text answer (preview)' : 'Short text answer (preview)',
|
long ? 'pollLongTextAnswerPreview'.tr() : 'pollShortTextAnswerPreview'.tr(),
|
||||||
border: const OutlineInputBorder(
|
border: const OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
),
|
||||||
@@ -1081,9 +1083,9 @@ class _EmptyState extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(title, style: Theme.of(context).textTheme.titleMedium),
|
Text('pollNoQuestionsYet'.tr(), style: Theme.of(context).textTheme.titleMedium),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
Text(subtitle, style: Theme.of(context).textTheme.bodyMedium),
|
Text('pollNoQuestionsHint'.tr(), style: Theme.of(context).textTheme.bodyMedium),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
right: 0,
|
right: 0,
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
child: postState
|
child: postState
|
||||||
.when(
|
.when(
|
||||||
data:
|
data:
|
||||||
@@ -107,8 +108,8 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
error: (_, _) => const SizedBox.shrink(),
|
error: (_, _) => const SizedBox.shrink(),
|
||||||
)
|
)
|
||||||
.padding(
|
.padding(
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 16,
|
bottom: MediaQuery.of(context).padding.bottom + 8,
|
||||||
top: 16,
|
top: 8,
|
||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:gap/gap.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/post.dart';
|
import 'package:island/models/post.dart';
|
||||||
import 'package:island/models/publisher.dart';
|
import 'package:island/models/publisher.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/services/color.dart';
|
import 'package:island/services/color.dart';
|
||||||
|
|||||||
@@ -488,6 +488,7 @@ class _RealmMemberListSheet extends HookConsumerWidget {
|
|||||||
Future<void> invitePerson() async {
|
Future<void> invitePerson() async {
|
||||||
final result = await showModalBottomSheet(
|
final result = await showModalBottomSheet(
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
useRootNavigator: true,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => const AccountPickerSheet(),
|
builder: (context) => const AccountPickerSheet(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:island/main.dart';
|
import 'package:island/main.dart';
|
||||||
import 'package:island/route.dart';
|
import 'package:island/route.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/websocket.dart';
|
import 'package:island/pods/websocket.dart';
|
||||||
import 'package:island/widgets/app_notification.dart';
|
import 'package:island/widgets/app_notification.dart';
|
||||||
import 'package:top_snackbar_flutter/top_snack_bar.dart';
|
import 'package:top_snackbar_flutter/top_snack_bar.dart';
|
||||||
@@ -67,6 +67,9 @@ Future<void> subscribePushNotification(
|
|||||||
Dio apiClient, {
|
Dio apiClient, {
|
||||||
bool detailedErrors = false,
|
bool detailedErrors = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await FirebaseMessaging.instance.requestPermission(
|
await FirebaseMessaging.instance.requestPermission(
|
||||||
alert: true,
|
alert: true,
|
||||||
badge: true,
|
badge: true,
|
||||||
|
|||||||
@@ -1,19 +1,28 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app_update/azhon_app_update.dart';
|
||||||
|
import 'package:flutter_app_update/update_model.dart';
|
||||||
|
import 'package:island/widgets/content/markdown.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
import 'package:collection/collection.dart'; // Added for firstWhereOrNull
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
|
||||||
/// Data model for a GitHub release we care about
|
/// Data model for a GitHub release we care about
|
||||||
class GithubReleaseInfo {
|
class GithubReleaseInfo {
|
||||||
final String tagName; // e.g. 3.1.0+118
|
final String tagName;
|
||||||
final String name; // release title
|
final String name;
|
||||||
final String body; // changelog markdown
|
final String body;
|
||||||
final String htmlUrl; // release page
|
final String htmlUrl;
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
|
final List<GithubReleaseAsset> assets;
|
||||||
|
|
||||||
const GithubReleaseInfo({
|
const GithubReleaseInfo({
|
||||||
required this.tagName,
|
required this.tagName,
|
||||||
@@ -21,9 +30,28 @@ class GithubReleaseInfo {
|
|||||||
required this.body,
|
required this.body,
|
||||||
required this.htmlUrl,
|
required this.htmlUrl,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
|
this.assets = const [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data model for a GitHub release asset
|
||||||
|
class GithubReleaseAsset {
|
||||||
|
final String name;
|
||||||
|
final String browserDownloadUrl;
|
||||||
|
|
||||||
|
const GithubReleaseAsset({
|
||||||
|
required this.name,
|
||||||
|
required this.browserDownloadUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory GithubReleaseAsset.fromJson(Map<String, dynamic> json) {
|
||||||
|
return GithubReleaseAsset(
|
||||||
|
name: json['name'] as String,
|
||||||
|
browserDownloadUrl: json['browser_download_url'] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses version and build number from "x.y.z+build"
|
/// Parses version and build number from "x.y.z+build"
|
||||||
class _ParsedVersion implements Comparable<_ParsedVersion> {
|
class _ParsedVersion implements Comparable<_ParsedVersion> {
|
||||||
final int major;
|
final int major;
|
||||||
@@ -62,7 +90,7 @@ class _ParsedVersion implements Comparable<_ParsedVersion> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class UpdateService {
|
class UpdateService {
|
||||||
UpdateService({Dio? dio})
|
UpdateService({Dio? dio, this.useProxy = false})
|
||||||
: _dio =
|
: _dio =
|
||||||
dio ??
|
dio ??
|
||||||
Dio(
|
Dio(
|
||||||
@@ -78,6 +106,9 @@ class UpdateService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final Dio _dio;
|
final Dio _dio;
|
||||||
|
final bool useProxy;
|
||||||
|
|
||||||
|
static const _proxyBaseUrl = 'https://ghfast.top/';
|
||||||
|
|
||||||
static const _releasesLatestApi =
|
static const _releasesLatestApi =
|
||||||
'https://api.github.com/repos/solsynth/solian/releases/latest';
|
'https://api.github.com/repos/solsynth/solian/releases/latest';
|
||||||
@@ -85,31 +116,52 @@ class UpdateService {
|
|||||||
/// Checks GitHub for the latest release and compares against the current app version.
|
/// Checks GitHub for the latest release and compares against the current app version.
|
||||||
/// If update is available, shows a bottom sheet with changelog and an action to open release page.
|
/// If update is available, shows a bottom sheet with changelog and an action to open release page.
|
||||||
Future<void> checkForUpdates(BuildContext context) async {
|
Future<void> checkForUpdates(BuildContext context) async {
|
||||||
|
log('[Update] Checking for updates...');
|
||||||
try {
|
try {
|
||||||
final release = await fetchLatestRelease();
|
final release = await fetchLatestRelease();
|
||||||
if (release == null) return;
|
if (release == null) {
|
||||||
|
log('[Update] No latest release found or could not fetch.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log('[Update] Fetched latest release: ${release.tagName}');
|
||||||
|
|
||||||
final info = await PackageInfo.fromPlatform();
|
final info = await PackageInfo.fromPlatform();
|
||||||
final localVersionStr = '${info.version}+${info.buildNumber}';
|
final localVersionStr = '${info.version}+${info.buildNumber}';
|
||||||
|
log('[Update] Local app version: $localVersionStr');
|
||||||
|
|
||||||
final latest = _ParsedVersion.tryParse(release.tagName);
|
final latest = _ParsedVersion.tryParse(release.tagName);
|
||||||
final local = _ParsedVersion.tryParse(localVersionStr);
|
final local = _ParsedVersion.tryParse(localVersionStr);
|
||||||
|
|
||||||
if (latest == null || local == null) {
|
if (latest == null || local == null) {
|
||||||
|
log(
|
||||||
|
'[Update] Failed to parse versions. Latest: ${release.tagName}, Local: $localVersionStr',
|
||||||
|
);
|
||||||
// If parsing fails, do nothing silently
|
// If parsing fails, do nothing silently
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
log('[Update] Parsed versions. Latest: $latest, Local: $local');
|
||||||
|
|
||||||
final needsUpdate = latest.compareTo(local) > 0;
|
final needsUpdate = latest.compareTo(local) > 0;
|
||||||
if (!needsUpdate) return;
|
if (!needsUpdate) {
|
||||||
|
log('[Update] App is up to date. No update needed.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log('[Update] Update available! Latest: $latest, Local: $local');
|
||||||
|
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) {
|
||||||
|
log('[Update] Context not mounted, cannot show update sheet.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Delay to ensure UI is ready (if called at startup)
|
// Delay to ensure UI is ready (if called at startup)
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
await showUpdateSheet(context, release);
|
if (context.mounted) {
|
||||||
} catch (_) {
|
await showUpdateSheet(context, release);
|
||||||
|
log('[Update] Update sheet shown.');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('[Update] Error checking for updates: $e');
|
||||||
// Ignore errors (network, api, etc.)
|
// Ignore errors (network, api, etc.)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -126,25 +178,68 @@ class UpdateService {
|
|||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
builder:
|
builder: (ctx) {
|
||||||
(ctx) => _UpdateSheet(
|
String? androidUpdateUrl;
|
||||||
release: release,
|
if (Platform.isAndroid) {
|
||||||
onOpen: () async {
|
androidUpdateUrl = _getAndroidUpdateUrl(release.assets);
|
||||||
final uri = Uri.parse(release.htmlUrl);
|
}
|
||||||
if (await canLaunchUrl(uri)) {
|
return _UpdateSheet(
|
||||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
release: release,
|
||||||
}
|
onOpen: () async {
|
||||||
},
|
final uri = Uri.parse(release.htmlUrl);
|
||||||
),
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
androidUpdateUrl: androidUpdateUrl,
|
||||||
|
useProxy: useProxy, // Pass the useProxy flag
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? _getAndroidUpdateUrl(List<GithubReleaseAsset> assets) {
|
||||||
|
final arm64 = assets.firstWhereOrNull(
|
||||||
|
(asset) => asset.name == 'app-arm64-v8a-release.apk',
|
||||||
|
);
|
||||||
|
final armeabi = assets.firstWhereOrNull(
|
||||||
|
(asset) => asset.name == 'app-armeabi-v7a-release.apk',
|
||||||
|
);
|
||||||
|
final x86_64 = assets.firstWhereOrNull(
|
||||||
|
(asset) => asset.name == 'app-x86_64-release.apk',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prioritize arm64, then armeabi, then x86_64
|
||||||
|
if (arm64 != null) {
|
||||||
|
return arm64.browserDownloadUrl;
|
||||||
|
} else if (armeabi != null) {
|
||||||
|
return armeabi.browserDownloadUrl;
|
||||||
|
} else if (x86_64 != null) {
|
||||||
|
return x86_64.browserDownloadUrl;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch the latest release info from GitHub.
|
/// Fetch the latest release info from GitHub.
|
||||||
/// Public so other screens (e.g., About) can manually trigger update checks.
|
/// Public so other screens (e.g., About) can manually trigger update checks.
|
||||||
Future<GithubReleaseInfo?> fetchLatestRelease() async {
|
Future<GithubReleaseInfo?> fetchLatestRelease() async {
|
||||||
final resp = await _dio.get(_releasesLatestApi);
|
final apiEndpoint =
|
||||||
if (resp.statusCode != 200) return null;
|
useProxy
|
||||||
|
? '$_proxyBaseUrl${Uri.encodeComponent(_releasesLatestApi)}'
|
||||||
|
: _releasesLatestApi;
|
||||||
|
|
||||||
|
log(
|
||||||
|
'[Update] Fetching latest release from GitHub API: $apiEndpoint (Proxy: $useProxy)',
|
||||||
|
);
|
||||||
|
final resp = await _dio.get(apiEndpoint);
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
log(
|
||||||
|
'[Update] Failed to fetch latest release. Status code: ${resp.statusCode}',
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final data = resp.data as Map<String, dynamic>;
|
final data = resp.data as Map<String, dynamic>;
|
||||||
|
log('[Update] Successfully fetched release data.');
|
||||||
|
|
||||||
final tagName = (data['tag_name'] ?? '').toString();
|
final tagName = (data['tag_name'] ?? '').toString();
|
||||||
final name = (data['name'] ?? tagName).toString();
|
final name = (data['name'] ?? tagName).toString();
|
||||||
@@ -152,25 +247,70 @@ class UpdateService {
|
|||||||
final htmlUrl = (data['html_url'] ?? '').toString();
|
final htmlUrl = (data['html_url'] ?? '').toString();
|
||||||
final createdAtStr = (data['created_at'] ?? '').toString();
|
final createdAtStr = (data['created_at'] ?? '').toString();
|
||||||
final createdAt = DateTime.tryParse(createdAtStr) ?? DateTime.now();
|
final createdAt = DateTime.tryParse(createdAtStr) ?? DateTime.now();
|
||||||
|
final assetsData =
|
||||||
|
(data['assets'] as List<dynamic>?)
|
||||||
|
?.map((e) => GithubReleaseAsset.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
|
||||||
if (tagName.isEmpty || htmlUrl.isEmpty) return null;
|
if (tagName.isEmpty || htmlUrl.isEmpty) {
|
||||||
|
log(
|
||||||
|
'[Update] Missing tag_name or html_url in release data. TagName: "$tagName", HtmlUrl: "$htmlUrl"',
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
log('[Update] Returning GithubReleaseInfo for tag: $tagName');
|
||||||
return GithubReleaseInfo(
|
return GithubReleaseInfo(
|
||||||
tagName: tagName,
|
tagName: tagName,
|
||||||
name: name,
|
name: name,
|
||||||
body: body,
|
body: body,
|
||||||
htmlUrl: htmlUrl,
|
htmlUrl: htmlUrl,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
|
assets: assetsData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UpdateSheet extends StatelessWidget {
|
class _UpdateSheet extends StatefulWidget {
|
||||||
const _UpdateSheet({required this.release, required this.onOpen});
|
const _UpdateSheet({
|
||||||
|
required this.release,
|
||||||
|
required this.onOpen,
|
||||||
|
this.androidUpdateUrl,
|
||||||
|
this.useProxy = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? androidUpdateUrl;
|
||||||
|
final bool useProxy;
|
||||||
final GithubReleaseInfo release;
|
final GithubReleaseInfo release;
|
||||||
final VoidCallback onOpen;
|
final VoidCallback onOpen;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_UpdateSheet> createState() => _UpdateSheetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UpdateSheetState extends State<_UpdateSheet> {
|
||||||
|
late bool _useProxy;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_useProxy = widget.useProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _installUpdate(String url) async {
|
||||||
|
final downloadUrl =
|
||||||
|
_useProxy ? 'https://ghfast.top/${Uri.encodeComponent(url)}' : url;
|
||||||
|
|
||||||
|
UpdateModel model = UpdateModel(
|
||||||
|
downloadUrl,
|
||||||
|
"solian-update-${widget.release.tagName}.apk",
|
||||||
|
"launcher_icon",
|
||||||
|
'https://apps.apple.com/us/app/solian/id6499032345',
|
||||||
|
);
|
||||||
|
AzhonAppUpdate.update(model);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
@@ -186,8 +326,11 @@ class _UpdateSheet extends StatelessWidget {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(release.name, style: theme.textTheme.titleMedium).bold(),
|
Text(
|
||||||
Text(release.tagName).fontSize(12),
|
widget.release.name,
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
).bold(),
|
||||||
|
Text(widget.release.tagName).fontSize(12),
|
||||||
],
|
],
|
||||||
).padding(vertical: 16, horizontal: 16),
|
).padding(vertical: 16, horizontal: 16),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
@@ -197,21 +340,45 @@ class _UpdateSheet extends StatelessWidget {
|
|||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
vertical: 16,
|
vertical: 16,
|
||||||
),
|
),
|
||||||
child: SelectableText(
|
child: MarkdownTextContent(
|
||||||
release.body.isEmpty
|
content:
|
||||||
? 'No changelog provided.'
|
widget.release.body.isEmpty
|
||||||
: release.body,
|
? 'No changelog provided.'
|
||||||
style: theme.textTheme.bodyMedium,
|
: widget.release.body,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (!kIsWeb && Platform.isAndroid)
|
||||||
|
SwitchListTile(
|
||||||
|
title: const Text('Use GitHub Proxy for Download'),
|
||||||
|
value: _useProxy,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_useProxy = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
).padding(horizontal: 8),
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
|
if (!kIsWeb &&
|
||||||
|
Platform.isAndroid &&
|
||||||
|
widget.androidUpdateUrl != null)
|
||||||
|
Expanded(
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
log(widget.androidUpdateUrl!);
|
||||||
|
_installUpdate(widget.androidUpdateUrl!);
|
||||||
|
},
|
||||||
|
icon: const Icon(Symbols.update),
|
||||||
|
label: const Text('Install update'),
|
||||||
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: FilledButton.icon(
|
child: FilledButton.icon(
|
||||||
onPressed: onOpen,
|
onPressed: widget.onOpen,
|
||||||
icon: const Icon(Icons.open_in_new),
|
icon: const Icon(Icons.open_in_new),
|
||||||
label: const Text('Open release page'),
|
label: const Text('Open release page'),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,33 +3,34 @@ import 'dart:convert';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/auth.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/services/responsive.dart';
|
import 'package:island/services/responsive.dart';
|
||||||
|
import 'package:island/services/udid.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
import 'package:island/widgets/response.dart';
|
import 'package:island/widgets/response.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
part 'account_session_sheet.g.dart';
|
part 'account_devices.g.dart';
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
Future<List<SnAuthDevice>> authDevices(Ref ref) async {
|
Future<List<SnAuthDeviceWithChallenge>> authDevices(Ref ref) async {
|
||||||
final resp = await ref
|
final resp = await ref
|
||||||
.watch(apiClientProvider)
|
.watch(apiClientProvider)
|
||||||
.get('/id/accounts/me/devices');
|
.get('/id/accounts/me/devices');
|
||||||
final sessionId = resp.headers.value('x-auth-session');
|
final currentId = await getUdid();
|
||||||
final data =
|
final data =
|
||||||
resp.data.map<SnAuthDevice>((e) {
|
resp.data.map<SnAuthDeviceWithChallenge>((e) {
|
||||||
final ele = SnAuthDevice.fromJson(e);
|
final ele = SnAuthDeviceWithChallenge.fromJson(e);
|
||||||
return ele.copyWith(isCurrent: ele.sessions.first.id == sessionId);
|
return ele.copyWith(isCurrent: ele.deviceId == currentId);
|
||||||
}).toList();
|
}).toList();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DeviceListTile extends StatelessWidget {
|
class _DeviceListTile extends StatelessWidget {
|
||||||
final SnAuthDevice device;
|
final SnAuthDeviceWithChallenge device;
|
||||||
final Function(String) updateDeviceLabel;
|
final Function(String) updateDeviceLabel;
|
||||||
final Function(String) logoutDevice;
|
final Function(String) logoutDevice;
|
||||||
|
|
||||||
@@ -57,17 +58,16 @@ class _DeviceListTile extends StatelessWidget {
|
|||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Text('authSessionsCount'.plural(device.sessions.length)),
|
|
||||||
Text(
|
Text(
|
||||||
'lastActiveAt'.tr(
|
'lastActiveAt'.tr(
|
||||||
args: [
|
args: [
|
||||||
DateFormat().format(
|
DateFormat().format(
|
||||||
device.sessions.first.lastGrantedAt.toLocal(),
|
device.challenges.first.createdAt.toLocal(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(device.sessions.first.challenge.ipAddress),
|
Text(device.challenges.first.ipAddress),
|
||||||
if (device.isCurrent)
|
if (device.isCurrent)
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -84,7 +84,7 @@ class _DeviceListTile extends StatelessWidget {
|
|||||||
).padding(top: 4),
|
).padding(top: 4),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
title: Text(device.label ?? device.sessions.first.challenge.userAgent),
|
title: Text(device.deviceLabel ?? device.deviceName),
|
||||||
trailing:
|
trailing:
|
||||||
isWideScreen(context)
|
isWideScreen(context)
|
||||||
? Row(
|
? Row(
|
||||||
@@ -93,14 +93,13 @@ class _DeviceListTile extends StatelessWidget {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.edit),
|
icon: Icon(Icons.edit),
|
||||||
tooltip: 'authDeviceEditLabel'.tr(),
|
tooltip: 'authDeviceEditLabel'.tr(),
|
||||||
onPressed:
|
onPressed: () => updateDeviceLabel(device.deviceId),
|
||||||
() => updateDeviceLabel(device.sessions.first.id),
|
|
||||||
),
|
),
|
||||||
if (!device.isCurrent)
|
if (!device.isCurrent)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.logout),
|
icon: Icon(Icons.logout),
|
||||||
tooltip: 'authDeviceLogout'.tr(),
|
tooltip: 'authDeviceLogout'.tr(),
|
||||||
onPressed: () => logoutDevice(device.sessions.first.id),
|
onPressed: () => logoutDevice(device.deviceId),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -124,7 +123,7 @@ class AccountSessionSheet extends HookConsumerWidget {
|
|||||||
if (!confirm || !context.mounted) return;
|
if (!confirm || !context.mounted) return;
|
||||||
try {
|
try {
|
||||||
final apiClient = ref.watch(apiClientProvider);
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
await apiClient.delete('/id/accounts/me/sessions/$sessionId');
|
await apiClient.delete('/id/accounts/me/devices/$sessionId');
|
||||||
ref.invalidate(authDevicesProvider);
|
ref.invalidate(authDevicesProvider);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
@@ -163,7 +162,7 @@ class AccountSessionSheet extends HookConsumerWidget {
|
|||||||
try {
|
try {
|
||||||
final apiClient = ref.watch(apiClientProvider);
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
await apiClient.patch(
|
await apiClient.patch(
|
||||||
'/accounts/me/sessions/$sessionId/label',
|
'/accounts/me/devices/$sessionId/label',
|
||||||
data: jsonEncode(label),
|
data: jsonEncode(label),
|
||||||
);
|
);
|
||||||
ref.invalidate(authDevicesProvider);
|
ref.invalidate(authDevicesProvider);
|
||||||
@@ -194,7 +193,7 @@ class AccountSessionSheet extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Dismissible(
|
return Dismissible(
|
||||||
key: Key('device-${device.sessions.first.id}'),
|
key: Key('device-${device.id}'),
|
||||||
direction:
|
direction:
|
||||||
device.isCurrent
|
device.isCurrent
|
||||||
? DismissDirection.startToEnd
|
? DismissDirection.startToEnd
|
||||||
@@ -213,7 +212,7 @@ class AccountSessionSheet extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
confirmDismiss: (direction) async {
|
confirmDismiss: (direction) async {
|
||||||
if (direction == DismissDirection.startToEnd) {
|
if (direction == DismissDirection.startToEnd) {
|
||||||
updateDeviceLabel(device.sessions.first.id);
|
updateDeviceLabel(device.deviceId);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
final confirm = await showConfirmAlert(
|
final confirm = await showConfirmAlert(
|
||||||
@@ -221,7 +220,7 @@ class AccountSessionSheet extends HookConsumerWidget {
|
|||||||
'authDeviceLogout'.tr(),
|
'authDeviceLogout'.tr(),
|
||||||
);
|
);
|
||||||
if (confirm && context.mounted) {
|
if (confirm && context.mounted) {
|
||||||
logoutDevice(device.sessions.first.id);
|
logoutDevice(device.deviceId);
|
||||||
}
|
}
|
||||||
return false; // Don't dismiss
|
return false; // Don't dismiss
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'account_session_sheet.dart';
|
part of 'account_devices.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$authDevicesHash() => r'8bc41a1ffc37df8e757c977b4ddae11db8faaeb5';
|
String _$authDevicesHash() => r'feb19238f759921e51c888f8b443a3d7761e68da';
|
||||||
|
|
||||||
/// See also [authDevices].
|
/// See also [authDevices].
|
||||||
@ProviderFor(authDevices)
|
@ProviderFor(authDevices)
|
||||||
final authDevicesProvider =
|
final authDevicesProvider =
|
||||||
AutoDisposeFutureProvider<List<SnAuthDevice>>.internal(
|
AutoDisposeFutureProvider<List<SnAuthDeviceWithChallenge>>.internal(
|
||||||
authDevices,
|
authDevices,
|
||||||
name: r'authDevicesProvider',
|
name: r'authDevicesProvider',
|
||||||
debugGetCreateSourceHash:
|
debugGetCreateSourceHash:
|
||||||
@@ -24,6 +24,7 @@ final authDevicesProvider =
|
|||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef AuthDevicesRef = AutoDisposeFutureProviderRef<List<SnAuthDevice>>;
|
typedef AuthDevicesRef =
|
||||||
|
AutoDisposeFutureProviderRef<List<SnAuthDeviceWithChallenge>>;
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/models/wallet.dart';
|
import 'package:island/models/wallet.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@@ -44,9 +45,8 @@ class AccountPickerSheet extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
constraints: BoxConstraints(
|
padding: MediaQuery.of(context).viewInsets,
|
||||||
maxHeight: MediaQuery.of(context).size.height * 0.4,
|
height: MediaQuery.of(context).size.height * 0.6,
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
@@ -54,8 +54,8 @@ class AccountPickerSheet extends HookConsumerWidget {
|
|||||||
child: TextField(
|
child: TextField(
|
||||||
controller: searchController,
|
controller: searchController,
|
||||||
onChanged: onSearchChanged,
|
onChanged: onSearchChanged,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Search accounts...',
|
hintText: 'searchAccounts'.tr(),
|
||||||
contentPadding: EdgeInsets.symmetric(
|
contentPadding: EdgeInsets.symmetric(
|
||||||
horizontal: 18,
|
horizontal: 18,
|
||||||
vertical: 16,
|
vertical: 16,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/models/badge.dart';
|
import 'package:island/models/badge.dart';
|
||||||
|
|
||||||
class BadgeList extends StatelessWidget {
|
class BadgeList extends StatelessWidget {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/screens/account/profile.dart';
|
import 'package:island/screens/account/profile.dart';
|
||||||
import 'package:island/services/time.dart';
|
import 'package:island/services/time.dart';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/widgets/account/status.dart';
|
import 'package:island/widgets/account/status.dart';
|
||||||
@@ -55,7 +55,7 @@ class AccountStatusCreationSheet extends HookConsumerWidget {
|
|||||||
'attitude': attitude.value,
|
'attitude': attitude.value,
|
||||||
'is_invisible': isInvisible.value,
|
'is_invisible': isInvisible.value,
|
||||||
'is_not_disturb': isNotDisturb.value,
|
'is_not_disturb': isNotDisturb.value,
|
||||||
'cleared_at': clearedAt.value?.toIso8601String(),
|
'cleared_at': clearedAt.value?.toUtc().toIso8601String(),
|
||||||
if (labelController.text.isNotEmpty) 'label': labelController.text,
|
if (labelController.text.isNotEmpty) 'label': labelController.text,
|
||||||
},
|
},
|
||||||
options: Options(method: initialStatus == null ? 'POST' : 'PATCH'),
|
options: Options(method: initialStatus == null ? 'POST' : 'PATCH'),
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ void showLoadingModal(BuildContext context) {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
CircularProgressIndicator(year2023: true),
|
CircularProgressIndicator(year2023: false),
|
||||||
const Gap(24),
|
const Gap(24),
|
||||||
Text('loading'.tr()),
|
Text('loading'.tr()),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/user.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ class _WebSocketIndicator extends HookConsumerWidget {
|
|||||||
final user = ref.watch(userInfoProvider);
|
final user = ref.watch(userInfoProvider);
|
||||||
final websocketState = ref.watch(websocketStateProvider);
|
final websocketState = ref.watch(websocketStateProvider);
|
||||||
final indicatorHeight =
|
final indicatorHeight =
|
||||||
MediaQuery.of(context).padding.top + (isDesktop ? 27.5 : 20);
|
MediaQuery.of(context).padding.top + (isDesktop ? 27.5 : 25);
|
||||||
|
|
||||||
Color indicatorColor;
|
Color indicatorColor;
|
||||||
String indicatorText;
|
String indicatorText;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:island/pods/websocket.dart';
|
import 'package:island/pods/websocket.dart';
|
||||||
import 'package:island/services/notify.dart';
|
import 'package:island/services/notify.dart';
|
||||||
import 'package:island/services/sharing_intent.dart';
|
import 'package:island/services/sharing_intent.dart';
|
||||||
|
import 'package:island/services/update_service.dart';
|
||||||
import 'package:island/widgets/content/network_status_sheet.dart';
|
import 'package:island/widgets/content/network_status_sheet.dart';
|
||||||
import 'package:island/widgets/tour/tour.dart';
|
import 'package:island/widgets/tour/tour.dart';
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ class AppWrapper extends HookConsumerWidget {
|
|||||||
});
|
});
|
||||||
final sharingService = SharingIntentService();
|
final sharingService = SharingIntentService();
|
||||||
sharingService.initialize(context);
|
sharingService.initialize(context);
|
||||||
|
UpdateService().checkForUpdates(context);
|
||||||
return () {
|
return () {
|
||||||
sharingService.dispose();
|
sharingService.dispose();
|
||||||
ntySubs?.cancel();
|
ntySubs?.cancel();
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class CloudFileList extends HookConsumerWidget {
|
|||||||
final bool disableZoomIn;
|
final bool disableZoomIn;
|
||||||
final bool disableConstraint;
|
final bool disableConstraint;
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
|
final bool isColumn;
|
||||||
const CloudFileList({
|
const CloudFileList({
|
||||||
super.key,
|
super.key,
|
||||||
required this.files,
|
required this.files,
|
||||||
@@ -40,6 +41,7 @@ class CloudFileList extends HookConsumerWidget {
|
|||||||
this.disableZoomIn = false,
|
this.disableZoomIn = false,
|
||||||
this.disableConstraint = false,
|
this.disableConstraint = false,
|
||||||
this.padding,
|
this.padding,
|
||||||
|
this.isColumn = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
double calculateAspectRatio() {
|
double calculateAspectRatio() {
|
||||||
@@ -63,6 +65,74 @@ class CloudFileList extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (files.isEmpty) return const SizedBox.shrink();
|
if (files.isEmpty) return const SizedBox.shrink();
|
||||||
|
|
||||||
|
if (isColumn) {
|
||||||
|
final children = <Widget>[];
|
||||||
|
const maxFiles = 2;
|
||||||
|
final filesToShow = files.take(maxFiles).toList();
|
||||||
|
|
||||||
|
for (var i = 0; i < filesToShow.length; i++) {
|
||||||
|
final file = filesToShow[i];
|
||||||
|
final isImage = file.mimeType?.startsWith('image') ?? false;
|
||||||
|
final isAudio = file.mimeType?.startsWith('audio') ?? false;
|
||||||
|
final widgetItem = ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: _CloudFileListEntry(
|
||||||
|
file: file,
|
||||||
|
heroTag: heroTags[i],
|
||||||
|
isImage: isImage,
|
||||||
|
disableZoomIn: disableZoomIn,
|
||||||
|
onTap: () {
|
||||||
|
if (!isImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!disableZoomIn) {
|
||||||
|
context.pushTransparentRoute(
|
||||||
|
CloudFileZoomIn(item: file, heroTag: heroTags[i]),
|
||||||
|
rootNavigator: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget item;
|
||||||
|
if (isAudio) {
|
||||||
|
item = SizedBox(height: 120, child: widgetItem);
|
||||||
|
} else {
|
||||||
|
item = AspectRatio(
|
||||||
|
aspectRatio: file.fileMeta?['ratio'] as double? ?? 1.0,
|
||||||
|
child: widgetItem,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
children.add(item);
|
||||||
|
if (i < filesToShow.length - 1) {
|
||||||
|
children.add(const Gap(8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.length > maxFiles) {
|
||||||
|
children.add(const Gap(8));
|
||||||
|
children.add(
|
||||||
|
Text(
|
||||||
|
'filesListAdditional'.plural(files.length - filesToShow.length),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: padding ?? EdgeInsets.zero,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: children,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (files.length == 1) {
|
if (files.length == 1) {
|
||||||
final isImage = files.first.mimeType?.startsWith('image') ?? false;
|
final isImage = files.first.mimeType?.startsWith('image') ?? false;
|
||||||
final isAudio = files.first.mimeType?.startsWith('audio') ?? false;
|
final isAudio = files.first.mimeType?.startsWith('audio') ?? false;
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ class CloudVideoWidget extends HookConsumerWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
if (item.fileMeta?['duration'] != null)
|
if (item.fileMeta?['duration'] != null)
|
||||||
@@ -199,8 +199,8 @@ class CloudVideoWidget extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
).padding(horizontal: 16, bottom: 12),
|
||||||
).padding(horizontal: 16, bottom: 12),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_highlight/themes/a11y-dark.dart';
|
import 'package:flutter_highlight/themes/a11y-dark.dart';
|
||||||
import 'package:flutter_highlight/themes/a11y-light.dart';
|
import 'package:flutter_highlight/themes/a11y-light.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
@@ -71,7 +72,22 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
textStyle: textStyle ?? Theme.of(context).textTheme.bodyMedium!,
|
textStyle: textStyle ?? Theme.of(context).textTheme.bodyMedium!,
|
||||||
),
|
),
|
||||||
HrConfig(height: 1, color: Theme.of(context).dividerColor),
|
HrConfig(height: 1, color: Theme.of(context).dividerColor),
|
||||||
PreConfig(theme: isDark ? a11yDarkTheme : a11yLightTheme),
|
PreConfig(
|
||||||
|
theme: isDark ? a11yDarkTheme : a11yLightTheme,
|
||||||
|
textStyle: GoogleFonts.robotoMono(fontSize: 14),
|
||||||
|
styleNotMatched: GoogleFonts.robotoMono(fontSize: 14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TableConfig(
|
||||||
|
wrapper:
|
||||||
|
(child) => SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
LinkConfig(
|
LinkConfig(
|
||||||
style:
|
style:
|
||||||
linkStyle ??
|
linkStyle ??
|
||||||
@@ -160,7 +176,7 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
uri: stickerUri,
|
uri: stickerUri,
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.contain,
|
||||||
noCacheOptimization: true,
|
noCacheOptimization: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
76
lib/widgets/debug_sheet.dart
Normal file
76
lib/widgets/debug_sheet.dart
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/pods/message.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/pods/websocket.dart';
|
||||||
|
import 'package:island/widgets/content/network_status_sheet.dart';
|
||||||
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
|
class DebugSheet extends HookConsumerWidget {
|
||||||
|
const DebugSheet({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final wsNotifier = ref.watch(websocketStateProvider.notifier);
|
||||||
|
|
||||||
|
return SheetScaffold(
|
||||||
|
titleText: 'Debug',
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.wifi),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
title: Text('Connection Status'),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder:
|
||||||
|
(context) => NetworkStatusSheet(
|
||||||
|
onReconnect: () => wsNotifier.connect(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.copy_all),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('Copy access token'),
|
||||||
|
onTap: () async {
|
||||||
|
final tk = ref.watch(tokenProvider);
|
||||||
|
Clipboard.setData(ClipboardData(text: tk!.token));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.delete),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('Reset database'),
|
||||||
|
onTap: () async {
|
||||||
|
resetDatabase(ref);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.clear),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('Clear cache'),
|
||||||
|
onTap: () async {
|
||||||
|
DefaultCacheManager().emptyCache();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -248,7 +248,7 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
|
|||||||
try {
|
try {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
final response = await client.post(
|
final response = await client.post(
|
||||||
'/orders/${widget.order.id}/pay',
|
'/id/orders/${widget.order.id}/pay',
|
||||||
data: {'pin_code': pin},
|
data: {'pin_code': pin},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/poll.dart';
|
import 'package:island/models/poll.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/screens/creators/poll/poll_list.dart';
|
||||||
import 'package:island/services/time.dart';
|
import 'package:island/services/time.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
import 'package:island/widgets/poll/poll_stats_widget.dart';
|
||||||
|
import 'package:island/widgets/response.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
@@ -52,59 +57,60 @@ class PollFeedbackNotifier extends _$PollFeedbackNotifier
|
|||||||
class PollFeedbackSheet extends HookConsumerWidget {
|
class PollFeedbackSheet extends HookConsumerWidget {
|
||||||
final String pollId;
|
final String pollId;
|
||||||
final String? title;
|
final String? title;
|
||||||
final SnPoll poll;
|
const PollFeedbackSheet({super.key, required this.pollId, this.title});
|
||||||
final Map<String, dynamic>? stats; // stats object similar to PollSubmit
|
|
||||||
const PollFeedbackSheet({
|
|
||||||
super.key,
|
|
||||||
required this.pollId,
|
|
||||||
required this.poll,
|
|
||||||
this.title,
|
|
||||||
this.stats,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final poll = ref.watch(pollWithStatsProvider(pollId));
|
||||||
|
|
||||||
return SheetScaffold(
|
return SheetScaffold(
|
||||||
titleText: title ?? 'Poll feedback',
|
titleText: title ?? 'Poll feedback',
|
||||||
child: Column(
|
child: poll.when(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
data:
|
||||||
children: [
|
(data) => CustomScrollView(
|
||||||
_PollHeader(poll: poll, stats: stats),
|
slivers: [
|
||||||
const Divider(height: 1),
|
SliverToBoxAdapter(child: _PollHeader(poll: data)),
|
||||||
Expanded(
|
SliverToBoxAdapter(child: const Divider(height: 1)),
|
||||||
child: PagingHelperView(
|
SliverGap(4),
|
||||||
provider: pollFeedbackNotifierProvider(pollId),
|
PagingHelperSliverView(
|
||||||
futureRefreshable: pollFeedbackNotifierProvider(pollId).future,
|
provider: pollFeedbackNotifierProvider(pollId),
|
||||||
notifierRefreshable:
|
futureRefreshable:
|
||||||
pollFeedbackNotifierProvider(pollId).notifier,
|
pollFeedbackNotifierProvider(pollId).future,
|
||||||
contentBuilder:
|
notifierRefreshable:
|
||||||
(data, widgetCount, endItemView) => ListView.separated(
|
pollFeedbackNotifierProvider(pollId).notifier,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
contentBuilder:
|
||||||
itemCount: widgetCount,
|
(val, widgetCount, endItemView) => SliverList.separated(
|
||||||
itemBuilder: (context, index) {
|
itemCount: widgetCount,
|
||||||
if (index == widgetCount - 1) {
|
itemBuilder: (context, index) {
|
||||||
// Provided by PagingHelperView to indicate end/loading
|
if (index == widgetCount - 1) {
|
||||||
return endItemView;
|
// Provided by PagingHelperView to indicate end/loading
|
||||||
}
|
return endItemView;
|
||||||
final answer = data.items[index];
|
}
|
||||||
return _PollAnswerTile(answer: answer, poll: poll);
|
final answer = val.items[index];
|
||||||
},
|
return _PollAnswerTile(answer: answer, poll: data);
|
||||||
separatorBuilder:
|
},
|
||||||
(context, index) =>
|
separatorBuilder:
|
||||||
const Divider(height: 1).padding(vertical: 4),
|
(context, index) =>
|
||||||
),
|
const Divider(height: 1).padding(vertical: 4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverGap(4 + MediaQuery.of(context).padding.bottom),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
error:
|
||||||
],
|
(err, _) => ResponseErrorWidget(
|
||||||
|
error: err,
|
||||||
|
onRetry: () => ref.invalidate(pollWithStatsProvider(pollId)),
|
||||||
|
),
|
||||||
|
loading: () => ResponseLoadingWidget(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PollHeader extends StatelessWidget {
|
class _PollHeader extends StatelessWidget {
|
||||||
const _PollHeader({required this.poll, this.stats});
|
const _PollHeader({required this.poll});
|
||||||
final SnPoll poll;
|
final SnPollWithStats poll;
|
||||||
final Map<String, dynamic>? stats;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -112,18 +118,32 @@ class _PollHeader extends StatelessWidget {
|
|||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 12,
|
||||||
children: [
|
children: [
|
||||||
if (poll.title != null)
|
if (poll.title != null || (poll.description?.isNotEmpty ?? false))
|
||||||
Text(poll.title!, style: theme.textTheme.titleLarge),
|
Column(
|
||||||
if (poll.description != null)
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.only(top: 2),
|
if (poll.title != null)
|
||||||
child: Text(
|
Text(poll.title!, style: theme.textTheme.titleLarge),
|
||||||
poll.description!,
|
if (poll.description?.isNotEmpty ?? false)
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
Text(
|
||||||
color: theme.textTheme.bodyMedium?.color?.withOpacity(0.7),
|
poll.description!,
|
||||||
),
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
),
|
color: theme.textTheme.bodyMedium?.color?.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text('pollQuestions').tr().fontSize(17).bold(),
|
||||||
|
for (final q in poll.questions)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (q.title.isNotEmpty) Text(q.title).bold(),
|
||||||
|
if (q.description?.isNotEmpty ?? false) Text(q.description!),
|
||||||
|
PollStatsWidget(question: q, stats: poll.stats),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 20, vertical: 16);
|
).padding(horizontal: 20, vertical: 16);
|
||||||
@@ -132,7 +152,7 @@ class _PollHeader extends StatelessWidget {
|
|||||||
|
|
||||||
class _PollAnswerTile extends StatelessWidget {
|
class _PollAnswerTile extends StatelessWidget {
|
||||||
final SnPollAnswer answer;
|
final SnPollAnswer answer;
|
||||||
final SnPoll poll;
|
final SnPollWithStats poll;
|
||||||
const _PollAnswerTile({required this.answer, required this.poll});
|
const _PollAnswerTile({required this.answer, required this.poll});
|
||||||
|
|
||||||
String _formatPerQuestionAnswer(
|
String _formatPerQuestionAnswer(
|
||||||
|
|||||||
233
lib/widgets/poll/poll_stats_widget.dart
Normal file
233
lib/widgets/poll/poll_stats_widget.dart
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:island/models/poll.dart';
|
||||||
|
|
||||||
|
class PollStatsWidget extends StatelessWidget {
|
||||||
|
const PollStatsWidget({
|
||||||
|
super.key,
|
||||||
|
required this.question,
|
||||||
|
required this.stats,
|
||||||
|
});
|
||||||
|
|
||||||
|
final SnPollQuestion question;
|
||||||
|
final Map<String, dynamic>? stats;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (stats == null) return const SizedBox.shrink();
|
||||||
|
final raw = stats![question.id];
|
||||||
|
if (raw == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
|
Widget? body;
|
||||||
|
|
||||||
|
switch (question.type) {
|
||||||
|
case SnPollQuestionType.rating:
|
||||||
|
// rating: avg score (double or int)
|
||||||
|
final avg = (raw['rating'] as num?)?.toDouble();
|
||||||
|
if (avg == null) break;
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
body = Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.star, color: Colors.amber.shade600, size: 18),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
avg.toStringAsFixed(1),
|
||||||
|
style: theme.textTheme.labelMedium?.copyWith(
|
||||||
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SnPollQuestionType.yesNo:
|
||||||
|
// yes/no: map {true: count, false: count}
|
||||||
|
if (raw is Map) {
|
||||||
|
final int yes =
|
||||||
|
(raw[true] is int)
|
||||||
|
? raw[true] as int
|
||||||
|
: int.tryParse('${raw[true]}') ?? 0;
|
||||||
|
final int no =
|
||||||
|
(raw[false] is int)
|
||||||
|
? raw[false] as int
|
||||||
|
: int.tryParse('${raw[false]}') ?? 0;
|
||||||
|
final total = (yes + no).clamp(0, 1 << 31);
|
||||||
|
final yesPct = total == 0 ? 0.0 : yes / total;
|
||||||
|
final noPct = total == 0 ? 0.0 : no / total;
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
body = Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_BarStatRow(
|
||||||
|
label: 'Yes',
|
||||||
|
count: yes,
|
||||||
|
fraction: yesPct,
|
||||||
|
color: Colors.green.shade600,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
_BarStatRow(
|
||||||
|
label: 'No',
|
||||||
|
count: no,
|
||||||
|
fraction: noPct,
|
||||||
|
color: Colors.red.shade600,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Total: $total',
|
||||||
|
style: theme.textTheme.labelSmall?.copyWith(
|
||||||
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SnPollQuestionType.singleChoice:
|
||||||
|
case SnPollQuestionType.multipleChoice:
|
||||||
|
// map optionId -> count
|
||||||
|
if (raw is Map) {
|
||||||
|
final options = [...?question.options]
|
||||||
|
..sort((a, b) => a.order.compareTo(b.order));
|
||||||
|
final List<_OptionCount> items = [];
|
||||||
|
int total = 0;
|
||||||
|
for (final opt in options) {
|
||||||
|
final dynamic v = raw[opt.id];
|
||||||
|
final int count = v is int ? v : int.tryParse('$v') ?? 0;
|
||||||
|
total += count;
|
||||||
|
items.add(_OptionCount(id: opt.id, label: opt.label, count: count));
|
||||||
|
}
|
||||||
|
if (items.isNotEmpty) {
|
||||||
|
items.sort(
|
||||||
|
(a, b) => b.count.compareTo(a.count),
|
||||||
|
); // show highest first
|
||||||
|
}
|
||||||
|
body = Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
for (final it in items)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 6),
|
||||||
|
child: _BarStatRow(
|
||||||
|
label: it.label,
|
||||||
|
count: it.count,
|
||||||
|
fraction: total == 0 ? 0 : it.count / total,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (items.isNotEmpty)
|
||||||
|
Text(
|
||||||
|
'Total: $total',
|
||||||
|
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SnPollQuestionType.freeText:
|
||||||
|
// No stats
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body == null) return Text('No stats available');
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.35),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Stats',
|
||||||
|
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
body,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OptionCount {
|
||||||
|
final String id;
|
||||||
|
final String label;
|
||||||
|
final int count;
|
||||||
|
const _OptionCount({
|
||||||
|
required this.id,
|
||||||
|
required this.label,
|
||||||
|
required this.count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BarStatRow extends StatelessWidget {
|
||||||
|
const _BarStatRow({
|
||||||
|
required this.label,
|
||||||
|
required this.count,
|
||||||
|
required this.fraction,
|
||||||
|
this.color,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
final int count;
|
||||||
|
final double fraction;
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final barColor = color ?? Theme.of(context).colorScheme.primary;
|
||||||
|
final bgColor = Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceVariant.withOpacity(0.6);
|
||||||
|
final fg =
|
||||||
|
(fraction.isNaN || fraction.isInfinite)
|
||||||
|
? 0.0
|
||||||
|
: fraction.clamp(0.0, 1.0);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('$label · $count', style: Theme.of(context).textTheme.labelMedium),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final width = constraints.maxWidth;
|
||||||
|
final filled = width * fg;
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 8,
|
||||||
|
width: width,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: bgColor,
|
||||||
|
borderRadius: BorderRadius.circular(999),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 8,
|
||||||
|
width: filled,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: barColor,
|
||||||
|
borderRadius: BorderRadius.circular(999),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:island/models/poll.dart';
|
import 'package:island/models/poll.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
|
import 'package:island/widgets/poll/poll_stats_widget.dart';
|
||||||
|
|
||||||
class PollSubmit extends ConsumerStatefulWidget {
|
class PollSubmit extends ConsumerStatefulWidget {
|
||||||
const PollSubmit({
|
const PollSubmit({
|
||||||
@@ -14,6 +16,7 @@ class PollSubmit extends ConsumerStatefulWidget {
|
|||||||
this.initialAnswers,
|
this.initialAnswers,
|
||||||
this.onCancel,
|
this.onCancel,
|
||||||
this.showProgress = true,
|
this.showProgress = true,
|
||||||
|
this.isReadonly = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final SnPollWithStats poll;
|
final SnPollWithStats poll;
|
||||||
@@ -31,6 +34,8 @@ class PollSubmit extends ConsumerStatefulWidget {
|
|||||||
/// Whether to show a progress indicator (e.g., "2 / N").
|
/// Whether to show a progress indicator (e.g., "2 / N").
|
||||||
final bool showProgress;
|
final bool showProgress;
|
||||||
|
|
||||||
|
final bool isReadonly;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<PollSubmit> createState() => _PollSubmitState();
|
ConsumerState<PollSubmit> createState() => _PollSubmitState();
|
||||||
}
|
}
|
||||||
@@ -39,6 +44,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
late final List<SnPollQuestion> _questions;
|
late final List<SnPollQuestion> _questions;
|
||||||
int _index = 0;
|
int _index = 0;
|
||||||
bool _submitting = false;
|
bool _submitting = false;
|
||||||
|
bool _isModifying = false; // New state to track if user is modifying answers
|
||||||
|
|
||||||
/// Collected answers, keyed by questionId
|
/// Collected answers, keyed by questionId
|
||||||
late Map<String, dynamic> _answers;
|
late Map<String, dynamic> _answers;
|
||||||
@@ -59,7 +65,14 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
_questions = [...widget.poll.questions]
|
_questions = [...widget.poll.questions]
|
||||||
..sort((a, b) => a.order.compareTo(b.order));
|
..sort((a, b) => a.order.compareTo(b.order));
|
||||||
_answers = Map<String, dynamic>.from(widget.initialAnswers ?? {});
|
_answers = Map<String, dynamic>.from(widget.initialAnswers ?? {});
|
||||||
_loadCurrentIntoLocalState();
|
if (!widget.isReadonly) {
|
||||||
|
_loadCurrentIntoLocalState();
|
||||||
|
// If initial answers are provided, set _isModifying to false initially
|
||||||
|
// so the "Modify" button is shown.
|
||||||
|
if (widget.initialAnswers != null && widget.initialAnswers!.isNotEmpty) {
|
||||||
|
_isModifying = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -74,7 +87,11 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
[...widget.poll.questions]
|
[...widget.poll.questions]
|
||||||
..sort((a, b) => a.order.compareTo(b.order)),
|
..sort((a, b) => a.order.compareTo(b.order)),
|
||||||
);
|
);
|
||||||
_loadCurrentIntoLocalState();
|
if (!widget.isReadonly) {
|
||||||
|
_loadCurrentIntoLocalState();
|
||||||
|
// If poll ID changes, reset modification state
|
||||||
|
_isModifying = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +213,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
// Only call onSubmit after server accepts
|
// Only call onSubmit after server accepts
|
||||||
widget.onSubmit(Map<String, dynamic>.unmodifiable(_answers));
|
widget.onSubmit(Map<String, dynamic>.unmodifiable(_answers));
|
||||||
|
|
||||||
showSnackBar('Poll answer has been submitted.');
|
showSnackBar('pollAnswerSubmitted'.tr());
|
||||||
HapticFeedback.heavyImpact();
|
HapticFeedback.heavyImpact();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showErrorAlert(e);
|
showErrorAlert(e);
|
||||||
@@ -268,7 +285,8 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.showProgress)
|
if (widget.showProgress &&
|
||||||
|
_isModifying) // Only show progress when modifying
|
||||||
Text(
|
Text(
|
||||||
'${_index + 1} / ${_questions.length}',
|
'${_index + 1} / ${_questions.length}',
|
||||||
style: Theme.of(context).textTheme.labelMedium,
|
style: Theme.of(context).textTheme.labelMedium,
|
||||||
@@ -310,154 +328,13 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStats(BuildContext context, SnPollQuestion q) {
|
Widget _buildStats(BuildContext context, SnPollQuestion q) {
|
||||||
if (widget.stats == null) return const SizedBox.shrink();
|
return PollStatsWidget(question: q, stats: widget.stats);
|
||||||
final raw = widget.stats![q.id];
|
|
||||||
if (raw == null) return const SizedBox.shrink();
|
|
||||||
|
|
||||||
Widget? body;
|
|
||||||
|
|
||||||
switch (q.type) {
|
|
||||||
case SnPollQuestionType.rating:
|
|
||||||
// rating: avg score (double or int)
|
|
||||||
final avg = (raw['rating'] as num?)?.toDouble();
|
|
||||||
if (avg == null) break;
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
body = Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.star, color: Colors.amber.shade600, size: 18),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
avg.toStringAsFixed(1),
|
|
||||||
style: theme.textTheme.labelMedium?.copyWith(
|
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SnPollQuestionType.yesNo:
|
|
||||||
// yes/no: map {true: count, false: count}
|
|
||||||
if (raw is Map) {
|
|
||||||
final int yes =
|
|
||||||
(raw[true] is int)
|
|
||||||
? raw[true] as int
|
|
||||||
: int.tryParse('${raw[true]}') ?? 0;
|
|
||||||
final int no =
|
|
||||||
(raw[false] is int)
|
|
||||||
? raw[false] as int
|
|
||||||
: int.tryParse('${raw[false]}') ?? 0;
|
|
||||||
final total = (yes + no).clamp(0, 1 << 31);
|
|
||||||
final yesPct = total == 0 ? 0.0 : yes / total;
|
|
||||||
final noPct = total == 0 ? 0.0 : no / total;
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
body = Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_BarStatRow(
|
|
||||||
label: 'Yes',
|
|
||||||
count: yes,
|
|
||||||
fraction: yesPct,
|
|
||||||
color: Colors.green.shade600,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
_BarStatRow(
|
|
||||||
label: 'No',
|
|
||||||
count: no,
|
|
||||||
fraction: noPct,
|
|
||||||
color: Colors.red.shade600,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Total: $total',
|
|
||||||
style: theme.textTheme.labelSmall?.copyWith(
|
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SnPollQuestionType.singleChoice:
|
|
||||||
case SnPollQuestionType.multipleChoice:
|
|
||||||
// map optionId -> count
|
|
||||||
if (raw is Map) {
|
|
||||||
final options = [...?q.options]
|
|
||||||
..sort((a, b) => a.order.compareTo(b.order));
|
|
||||||
final List<_OptionCount> items = [];
|
|
||||||
int total = 0;
|
|
||||||
for (final opt in options) {
|
|
||||||
final dynamic v = raw[opt.id];
|
|
||||||
final int count = v is int ? v : int.tryParse('$v') ?? 0;
|
|
||||||
total += count;
|
|
||||||
items.add(_OptionCount(id: opt.id, label: opt.label, count: count));
|
|
||||||
}
|
|
||||||
if (items.isNotEmpty) {
|
|
||||||
items.sort(
|
|
||||||
(a, b) => b.count.compareTo(a.count),
|
|
||||||
); // show highest first
|
|
||||||
}
|
|
||||||
body = Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
for (final it in items)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 6),
|
|
||||||
child: _BarStatRow(
|
|
||||||
label: it.label,
|
|
||||||
count: it.count,
|
|
||||||
fraction: total == 0 ? 0 : it.count / total,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (items.isNotEmpty)
|
|
||||||
Text(
|
|
||||||
'Total: $total',
|
|
||||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SnPollQuestionType.freeText:
|
|
||||||
// No stats
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (body == null) return const SizedBox.shrink();
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8),
|
|
||||||
child: DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.35),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Stats',
|
|
||||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
body,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(BuildContext context) {
|
Widget _buildBody(BuildContext context) {
|
||||||
|
if (widget.initialAnswers != null && !widget.isReadonly && !_isModifying) {
|
||||||
|
return const SizedBox.shrink(); // Collapse input fields if already submitted and not modifying
|
||||||
|
}
|
||||||
final q = _current;
|
final q = _current;
|
||||||
switch (q.type) {
|
switch (q.type) {
|
||||||
case SnPollQuestionType.singleChoice:
|
case SnPollQuestionType.singleChoice:
|
||||||
@@ -517,9 +394,9 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SegmentedButton<bool>(
|
child: SegmentedButton<bool>(
|
||||||
segments: const [
|
segments: [
|
||||||
ButtonSegment(value: true, label: Text('Yes')),
|
ButtonSegment(value: true, label: Text('yes'.tr())),
|
||||||
ButtonSegment(value: false, label: Text('No')),
|
ButtonSegment(value: false, label: Text('no'.tr())),
|
||||||
],
|
],
|
||||||
selected: _yesNoSelected == null ? {} : {_yesNoSelected!},
|
selected: _yesNoSelected == null ? {} : {_yesNoSelected!},
|
||||||
onSelectionChanged: (sel) {
|
onSelectionChanged: (sel) {
|
||||||
@@ -568,12 +445,39 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
final isLast = _index == _questions.length - 1;
|
final isLast = _index == _questions.length - 1;
|
||||||
final canProceed = _isCurrentAnswered() && !_submitting;
|
final canProceed = _isCurrentAnswered() && !_submitting;
|
||||||
|
|
||||||
|
if (widget.initialAnswers != null && !_isModifying && !widget.isReadonly) {
|
||||||
|
// If poll is submitted and not in modification mode, show "Modify" button
|
||||||
|
return FilledButton.icon(
|
||||||
|
icon: const Icon(Icons.edit),
|
||||||
|
label: Text('modifyAnswers'.tr()),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_isModifying = true;
|
||||||
|
_index = 0; // Reset to first question for modification
|
||||||
|
_loadCurrentIntoLocalState();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
label: Text(_index == 0 ? 'Cancel' : 'Back'),
|
label: Text(_index == 0 ? 'cancel'.tr() : 'back'.tr()),
|
||||||
onPressed: _submitting ? null : _back,
|
onPressed:
|
||||||
|
_submitting
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
if (_index == 0 && _isModifying) {
|
||||||
|
// If at first question and in modification mode, go back to submitted view
|
||||||
|
setState(() {
|
||||||
|
_isModifying = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_back();
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
FilledButton.icon(
|
FilledButton.icon(
|
||||||
@@ -585,19 +489,188 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
child: CircularProgressIndicator(strokeWidth: 2),
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
)
|
)
|
||||||
: Icon(isLast ? Icons.check : Icons.arrow_forward),
|
: Icon(isLast ? Icons.check : Icons.arrow_forward),
|
||||||
label: Text(isLast ? 'Submit' : 'Next'),
|
label: Text(isLast ? 'submit'.tr() : 'next'.tr()),
|
||||||
onPressed: canProceed ? _next : null,
|
onPressed: canProceed ? _next : null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildSubmittedView(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (widget.poll.title != null || widget.poll.description != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (widget.poll.title?.isNotEmpty ?? false)
|
||||||
|
Text(
|
||||||
|
widget.poll.title!,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
if (widget.poll.description?.isNotEmpty ?? false)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
widget.poll.description!,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodyMedium?.color?.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
for (final q in _questions)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
q.title,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (q.isRequired)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8),
|
||||||
|
child: Text(
|
||||||
|
'*',
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleMedium?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (q.description != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
q.description!,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall?.color?.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildStats(context, q),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildReadonlyView(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (widget.poll.title != null || widget.poll.description != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (widget.poll.title != null)
|
||||||
|
Text(
|
||||||
|
widget.poll.title!,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
if (widget.poll.description != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
widget.poll.description!,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodyMedium?.color?.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
for (final q in _questions)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
q.title,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (q.isRequired)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8),
|
||||||
|
child: Text(
|
||||||
|
'*',
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleMedium?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (q.description != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
q.description!,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall?.color?.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildStats(context, q),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_questions.isEmpty) {
|
if (_questions.isEmpty) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If poll is already submitted and not in readonly mode, and not in modification mode, show submitted view
|
||||||
|
if (widget.initialAnswers != null && !widget.isReadonly && !_isModifying) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [_buildSubmittedView(context), _buildNavBar(context)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If poll is in readonly mode, show readonly view
|
||||||
|
if (widget.isReadonly) {
|
||||||
|
return _buildReadonlyView(context);
|
||||||
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@@ -617,77 +690,6 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OptionCount {
|
|
||||||
final String id;
|
|
||||||
final String label;
|
|
||||||
final int count;
|
|
||||||
const _OptionCount({
|
|
||||||
required this.id,
|
|
||||||
required this.label,
|
|
||||||
required this.count,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BarStatRow extends StatelessWidget {
|
|
||||||
const _BarStatRow({
|
|
||||||
required this.label,
|
|
||||||
required this.count,
|
|
||||||
required this.fraction,
|
|
||||||
this.color,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String label;
|
|
||||||
final int count;
|
|
||||||
final double fraction;
|
|
||||||
final Color? color;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final barColor = color ?? Theme.of(context).colorScheme.primary;
|
|
||||||
final bgColor = Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.surfaceVariant.withOpacity(0.6);
|
|
||||||
final fg =
|
|
||||||
(fraction.isNaN || fraction.isInfinite)
|
|
||||||
? 0.0
|
|
||||||
: fraction.clamp(0.0, 1.0);
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('$label · $count', style: Theme.of(context).textTheme.labelMedium),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
final width = constraints.maxWidth;
|
|
||||||
final filled = width * fg;
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
height: 8,
|
|
||||||
width: width,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: bgColor,
|
|
||||||
borderRadius: BorderRadius.circular(999),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: 8,
|
|
||||||
width: filled,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: barColor,
|
|
||||||
borderRadius: BorderRadius.circular(999),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Simple fade/slide transition between questions.
|
/// Simple fade/slide transition between questions.
|
||||||
class _AnimatedStep extends StatelessWidget {
|
class _AnimatedStep extends StatelessWidget {
|
||||||
const _AnimatedStep({super.key, required this.child});
|
const _AnimatedStep({super.key, required this.child});
|
||||||
|
|||||||
@@ -186,10 +186,9 @@ class ComposePollSheet extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget? _buildPollSubtitle(SnPoll poll) {
|
Widget? _buildPollSubtitle(SnPollWithStats poll) {
|
||||||
try {
|
try {
|
||||||
final SnPoll dyn = poll;
|
final List<SnPollQuestion> options = poll.questions;
|
||||||
final List<SnPollQuestion> options = dyn.questions;
|
|
||||||
if (options.isEmpty) return null;
|
if (options.isEmpty) return null;
|
||||||
final preview = options.take(3).map((e) => e.title).join(' · ');
|
final preview = options.take(3).map((e) => e.title).join(' · ');
|
||||||
if (preview.trim().isEmpty) return null;
|
if (preview.trim().isEmpty) return null;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,11 +7,9 @@ import 'package:island/models/post.dart';
|
|||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/services/time.dart';
|
import 'package:island/services/time.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/content/cloud_file_collection.dart';
|
|
||||||
import 'package:island/widgets/content/markdown.dart';
|
|
||||||
import 'package:island/widgets/post/post_item.dart';
|
import 'package:island/widgets/post/post_item.dart';
|
||||||
|
import 'package:island/widgets/post/post_shared.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
|
||||||
import 'package:super_context_menu/super_context_menu.dart';
|
import 'package:super_context_menu/super_context_menu.dart';
|
||||||
|
|
||||||
class PostItemCreator extends HookConsumerWidget {
|
class PostItemCreator extends HookConsumerWidget {
|
||||||
@@ -81,7 +79,6 @@ class PostItemCreator extends HookConsumerWidget {
|
|||||||
title: 'copyLink'.tr(),
|
title: 'copyLink'.tr(),
|
||||||
image: MenuImage.icon(Symbols.link),
|
image: MenuImage.icon(Symbols.link),
|
||||||
callback: () {
|
callback: () {
|
||||||
// Copy post link to clipboard
|
|
||||||
context.pushNamed(
|
context.pushNamed(
|
||||||
'postDetail',
|
'postDetail',
|
||||||
pathParameters: {'id': item.id},
|
pathParameters: {'id': item.id},
|
||||||
@@ -105,8 +102,9 @@ class PostItemCreator extends HookConsumerWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_buildPostHeader(context),
|
PostHeader(item: item),
|
||||||
_buildPostContent(context),
|
PostBody(item: item),
|
||||||
|
ReferencedPostWidget(item: item),
|
||||||
const Gap(16),
|
const Gap(16),
|
||||||
_buildAnalyticsSection(context),
|
_buildAnalyticsSection(context),
|
||||||
],
|
],
|
||||||
@@ -117,128 +115,12 @@ class PostItemCreator extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPostHeader(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Post ID and timestamp row
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.primaryContainer,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'ID: ${item.id.substring(0, 6)}',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Icon(
|
|
||||||
_getVisibilityIcon(item.visibility),
|
|
||||||
size: 16,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
_getVisibilityText(item.visibility).tr(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
Text(
|
|
||||||
item.publishedAt?.formatSystem() ?? '',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
|
|
||||||
// Title and description
|
|
||||||
if (item.title?.isNotEmpty ?? false)
|
|
||||||
Text(
|
|
||||||
item.title!,
|
|
||||||
style: Theme.of(
|
|
||||||
context,
|
|
||||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
if (item.description?.isNotEmpty ?? false)
|
|
||||||
Text(
|
|
||||||
item.description!,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
).padding(top: 4),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildPostContent(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Content preview
|
|
||||||
if (item.content?.isNotEmpty ?? false)
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(top: 12),
|
|
||||||
child: MarkdownTextContent(content: item.content!),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Attachments
|
|
||||||
if (item.attachments.isNotEmpty)
|
|
||||||
CloudFileList(
|
|
||||||
files: item.attachments,
|
|
||||||
maxWidth: MediaQuery.of(context).size.width * 0.85,
|
|
||||||
padding: EdgeInsets.only(top: 8),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Reference post indicator
|
|
||||||
if (item.repliedPost != null || item.forwardedPost != null)
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(top: 8),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
item.repliedPost != null ? Symbols.reply : Symbols.forward,
|
|
||||||
size: 16,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
Text(
|
|
||||||
item.repliedPost != null
|
|
||||||
? 'repliedTo'.tr()
|
|
||||||
: 'forwarded'.tr(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAnalyticsSection(BuildContext context) {
|
Widget _buildAnalyticsSection(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('Analytics', style: Theme.of(context).textTheme.titleSmall),
|
Text('Analytics', style: Theme.of(context).textTheme.titleSmall),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
|
|
||||||
// Engagement metrics in a card
|
|
||||||
Card(
|
Card(
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
@@ -279,15 +161,9 @@ class PostItemCreator extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(16),
|
const Gap(16),
|
||||||
|
|
||||||
// Reactions summary
|
|
||||||
if (item.reactionsCount.isNotEmpty) _buildReactionsSection(context),
|
if (item.reactionsCount.isNotEmpty) _buildReactionsSection(context),
|
||||||
|
|
||||||
// Metadata section
|
|
||||||
if (item.meta != null && item.meta!.isNotEmpty)
|
if (item.meta != null && item.meta!.isNotEmpty)
|
||||||
_buildMetadataSection(context),
|
_buildMetadataSection(context),
|
||||||
|
|
||||||
// Creation and modification timestamps
|
|
||||||
const Gap(16),
|
const Gap(16),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@@ -425,31 +301,3 @@ class PostItemCreator extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to get the appropriate icon for each visibility status
|
|
||||||
IconData _getVisibilityIcon(int visibility) {
|
|
||||||
switch (visibility) {
|
|
||||||
case 1: // Friends
|
|
||||||
return Symbols.group;
|
|
||||||
case 2: // Unlisted
|
|
||||||
return Symbols.link_off;
|
|
||||||
case 3: // Private
|
|
||||||
return Symbols.lock;
|
|
||||||
default: // Public (0) or unknown
|
|
||||||
return Symbols.public;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to get the translation key for each visibility status
|
|
||||||
String _getVisibilityText(int visibility) {
|
|
||||||
switch (visibility) {
|
|
||||||
case 1: // Friends
|
|
||||||
return 'postVisibilityFriends';
|
|
||||||
case 2: // Unlisted
|
|
||||||
return 'postVisibilityUnlisted';
|
|
||||||
case 3: // Private
|
|
||||||
return 'postVisibilityPrivate';
|
|
||||||
default: // Public (0) or unknown
|
|
||||||
return 'postVisibilityPublic';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
135
lib/widgets/post/post_item_screenshot.dart
Normal file
135
lib/widgets/post/post_item_screenshot.dart
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/post.dart';
|
||||||
|
import 'package:island/widgets/post/post_shared.dart';
|
||||||
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
class PostItemScreenshot extends ConsumerWidget {
|
||||||
|
final SnPost item;
|
||||||
|
final EdgeInsets? padding;
|
||||||
|
final bool isFullPost;
|
||||||
|
final bool isShowReference;
|
||||||
|
const PostItemScreenshot({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
this.padding,
|
||||||
|
this.isFullPost = false,
|
||||||
|
this.isShowReference = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final renderingPadding =
|
||||||
|
padding ?? const EdgeInsets.symmetric(horizontal: 8, vertical: 8);
|
||||||
|
|
||||||
|
final mostReaction =
|
||||||
|
item.reactionsCount.isEmpty
|
||||||
|
? null
|
||||||
|
: item.reactionsCount.entries
|
||||||
|
.sortedBy((e) => e.value)
|
||||||
|
.map((e) => e.key)
|
||||||
|
.last;
|
||||||
|
|
||||||
|
final isDark = MediaQuery.of(context).platformBrightness == Brightness.dark;
|
||||||
|
|
||||||
|
return Material(
|
||||||
|
elevation: 0,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Gap(renderingPadding.vertical),
|
||||||
|
PostHeader(
|
||||||
|
item: item,
|
||||||
|
isFullPost: isFullPost,
|
||||||
|
isInteractive: false,
|
||||||
|
renderingPadding: renderingPadding,
|
||||||
|
isRelativeTime: false,
|
||||||
|
trailing:
|
||||||
|
mostReaction != null
|
||||||
|
? Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
kReactionTemplates[mostReaction]?.icon ?? '',
|
||||||
|
style: const TextStyle(fontSize: 20),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
'x${item.reactionsCount[mostReaction]}',
|
||||||
|
style: const TextStyle(fontSize: 11),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
PostBody(
|
||||||
|
item: item,
|
||||||
|
renderingPadding: renderingPadding,
|
||||||
|
isFullPost: isFullPost,
|
||||||
|
isTextSelectable: false,
|
||||||
|
isInteractive: false,
|
||||||
|
),
|
||||||
|
if (isShowReference)
|
||||||
|
ReferencedPostWidget(
|
||||||
|
item: item,
|
||||||
|
isInteractive: false,
|
||||||
|
renderingPadding: renderingPadding,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerLow,
|
||||||
|
margin: const EdgeInsets.only(top: 8),
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: renderingPadding.horizontal,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/icons/icon${isDark ? '-dark' : ''}.png',
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
).padding(vertical: 8, right: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Solar Network',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
'sharePostSlogan',
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
).tr().opacity(0.9),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
QrImageView(
|
||||||
|
data: 'https://solian.app/posts/${item.id}',
|
||||||
|
version: QrVersions.auto,
|
||||||
|
size: 60,
|
||||||
|
errorCorrectionLevel: QrErrorCorrectLevel.M,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onSurface,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ part of 'post_list.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$postListNotifierHash() => r'dc57fc6aaff6bfb4e9b4d1185984162b099b8773';
|
String _$postListNotifierHash() => r'2ca4f3cfbbcd04f3cc32e7f7bd511a5811042829';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/post.dart';
|
import 'package:island/models/post.dart';
|
||||||
import 'package:island/models/publisher.dart';
|
import 'package:island/models/publisher.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/screens/creators/publishers.dart';
|
import 'package:island/screens/creators/publishers.dart';
|
||||||
|
import 'package:island/screens/posts/compose.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
import 'package:island/widgets/post/publishers_modal.dart';
|
import 'package:island/widgets/post/publishers_modal.dart';
|
||||||
@@ -14,8 +16,14 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
|
|
||||||
class PostQuickReply extends HookConsumerWidget {
|
class PostQuickReply extends HookConsumerWidget {
|
||||||
final SnPost parent;
|
final SnPost parent;
|
||||||
final Function? onPosted;
|
final VoidCallback? onPosted;
|
||||||
const PostQuickReply({super.key, required this.parent, this.onPosted});
|
final VoidCallback? onLaunch;
|
||||||
|
const PostQuickReply({
|
||||||
|
super.key,
|
||||||
|
required this.parent,
|
||||||
|
this.onPosted,
|
||||||
|
this.onLaunch,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@@ -48,7 +56,7 @@ class PostQuickReply extends HookConsumerWidget {
|
|||||||
'content': contentController.text,
|
'content': contentController.text,
|
||||||
'replied_post_id': parent.id,
|
'replied_post_id': parent.id,
|
||||||
},
|
},
|
||||||
options: Options(headers: {'X-Pub': currentPublisher.value?.name}),
|
queryParameters: {'pub': currentPublisher.value?.name},
|
||||||
);
|
);
|
||||||
contentController.clear();
|
contentController.clear();
|
||||||
onPosted?.call();
|
onPosted?.call();
|
||||||
@@ -83,9 +91,10 @@ class PostQuickReply extends HookConsumerWidget {
|
|||||||
child: TextField(
|
child: TextField(
|
||||||
controller: contentController,
|
controller: contentController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Post your reply',
|
hintText: 'postReplyPlaceholder'.tr(),
|
||||||
border: const OutlineInputBorder(),
|
border: InputBorder.none,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
|
isCollapsed: true,
|
||||||
contentPadding: EdgeInsets.symmetric(
|
contentPadding: EdgeInsets.symmetric(
|
||||||
horizontal: 12,
|
horizontal: 12,
|
||||||
vertical: 8,
|
vertical: 8,
|
||||||
@@ -97,6 +106,26 @@ class PostQuickReply extends HookConsumerWidget {
|
|||||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
onLaunch?.call();
|
||||||
|
GoRouter.of(context)
|
||||||
|
.pushNamed(
|
||||||
|
'postCompose',
|
||||||
|
extra: PostComposeInitialState(
|
||||||
|
content: contentController.text,
|
||||||
|
replyingTo: parent,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.then((value) {
|
||||||
|
if (value != null) onPosted?.call();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: const Icon(Symbols.launch, size: 20),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
@@ -110,6 +139,7 @@ class PostQuickReply extends HookConsumerWidget {
|
|||||||
: Icon(Symbols.send, size: 20),
|
: Icon(Symbols.send, size: 20),
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
onPressed: submitting.value ? null : performAction,
|
onPressed: submitting.value ? null : performAction,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -38,14 +38,18 @@ class PostRepliesSheet extends HookConsumerWidget {
|
|||||||
if (user.value != null)
|
if (user.value != null)
|
||||||
Material(
|
Material(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
child: PostQuickReply(
|
child: PostQuickReply(
|
||||||
parent: post,
|
parent: post,
|
||||||
onPosted: () {
|
onPosted: () {
|
||||||
ref.invalidate(postRepliesNotifierProvider(post.id));
|
ref.invalidate(postRepliesNotifierProvider(post.id));
|
||||||
},
|
},
|
||||||
|
onLaunch: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
).padding(
|
).padding(
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 16,
|
bottom: MediaQuery.of(context).padding.bottom + 8,
|
||||||
top: 16,
|
top: 8,
|
||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
841
lib/widgets/post/post_shared.dart
Normal file
841
lib/widgets/post/post_shared.dart
Normal file
@@ -0,0 +1,841 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/embed.dart';
|
||||||
|
import 'package:island/models/poll.dart';
|
||||||
|
import 'package:island/models/post.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/services/responsive.dart';
|
||||||
|
import 'package:island/services/time.dart';
|
||||||
|
import 'package:island/utils/mapping.dart';
|
||||||
|
import 'package:island/widgets/account/account_name.dart';
|
||||||
|
import 'package:island/widgets/alert.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_file_collection.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:island/widgets/content/embed/link.dart';
|
||||||
|
import 'package:island/widgets/content/markdown.dart';
|
||||||
|
import 'package:island/widgets/poll/poll_submit.dart';
|
||||||
|
import 'package:island/widgets/post/post_replies_sheet.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
part 'post_shared.g.dart';
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnPost?> postFeaturedReply(Ref ref, String id) async {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
try {
|
||||||
|
final resp = await client.get('/sphere/posts/$id/replies/featured');
|
||||||
|
return SnPost.fromJson(resp.data);
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostVisibilityHelpers {
|
||||||
|
static IconData getVisibilityIcon(int visibility) {
|
||||||
|
switch (visibility) {
|
||||||
|
case 1:
|
||||||
|
return Symbols.group;
|
||||||
|
case 2:
|
||||||
|
return Symbols.link_off;
|
||||||
|
case 3:
|
||||||
|
return Symbols.lock;
|
||||||
|
default:
|
||||||
|
return Symbols.public;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getVisibilityText(int visibility) {
|
||||||
|
switch (visibility) {
|
||||||
|
case 1:
|
||||||
|
return 'postVisibilityFriends';
|
||||||
|
case 2:
|
||||||
|
return 'postVisibilityUnlisted';
|
||||||
|
case 3:
|
||||||
|
return 'postVisibilityPrivate';
|
||||||
|
default:
|
||||||
|
return 'postVisibilityPublic';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostReplyPreview extends HookConsumerWidget {
|
||||||
|
final SnPost parent;
|
||||||
|
final bool isOpenable;
|
||||||
|
final bool isCompact;
|
||||||
|
final bool isAutoload;
|
||||||
|
final VoidCallback? onOpen;
|
||||||
|
const PostReplyPreview({
|
||||||
|
super.key,
|
||||||
|
required this.parent,
|
||||||
|
this.isOpenable = false,
|
||||||
|
this.isCompact = false,
|
||||||
|
this.isAutoload = true,
|
||||||
|
this.onOpen,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final posts = useState<List<SnPost>>([]);
|
||||||
|
final loading = useState(false);
|
||||||
|
|
||||||
|
Future<void> fetchMoreReplies({int pageSize = 3}) async {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await client.get(
|
||||||
|
'/sphere/posts/${parent.id}/replies',
|
||||||
|
queryParameters: {'offset': posts.value.length, 'take': pageSize},
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
posts.value = [
|
||||||
|
...posts.value,
|
||||||
|
...response.data.map((e) => SnPost.fromJson(e)),
|
||||||
|
];
|
||||||
|
} catch (_) {
|
||||||
|
// ignore disposed
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
loading.value = false;
|
||||||
|
} catch (_) {
|
||||||
|
// ignore disposed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
if (isAutoload) fetchMoreReplies();
|
||||||
|
return null;
|
||||||
|
}, [parent]);
|
||||||
|
|
||||||
|
final featuredReply =
|
||||||
|
isOpenable ? null : ref.watch(PostFeaturedReplyProvider(parent.id));
|
||||||
|
|
||||||
|
final itemWidget =
|
||||||
|
isOpenable
|
||||||
|
? Column(
|
||||||
|
children: [
|
||||||
|
for (final post in posts.value)
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
ProfilePictureWidget(
|
||||||
|
file: post.publisher.picture,
|
||||||
|
radius: 12,
|
||||||
|
).padding(top: 4),
|
||||||
|
if (post.content?.isNotEmpty ?? false)
|
||||||
|
Expanded(
|
||||||
|
child: MarkdownTextContent(
|
||||||
|
content: post.content!,
|
||||||
|
).padding(top: 2),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'postHasAttachments',
|
||||||
|
).plural(post.attachments.length),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
onOpen?.call();
|
||||||
|
context.pushNamed(
|
||||||
|
'postDetail',
|
||||||
|
pathParameters: {'id': post.id},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (post.repliesCount > 0)
|
||||||
|
PostReplyPreview(
|
||||||
|
parent: post,
|
||||||
|
isOpenable: true,
|
||||||
|
isCompact: true,
|
||||||
|
isAutoload: false,
|
||||||
|
onOpen: onOpen,
|
||||||
|
).padding(left: 24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (loading.value)
|
||||||
|
Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
Text('loading').tr(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else if (posts.value.length < parent.repliesCount)
|
||||||
|
InkWell(
|
||||||
|
child: Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.keyboard_arrow_down, size: 20),
|
||||||
|
Text('repliesLoadMore').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
fetchMoreReplies();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: (featuredReply!).map(
|
||||||
|
data:
|
||||||
|
(data) => Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
ProfilePictureWidget(
|
||||||
|
file: data.value?.publisher.picture,
|
||||||
|
radius: 12,
|
||||||
|
).padding(top: 4),
|
||||||
|
if (data.value?.content?.isNotEmpty ?? false)
|
||||||
|
Expanded(
|
||||||
|
child: MarkdownTextContent(
|
||||||
|
content: data.value!.content!,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'postHasAttachments',
|
||||||
|
).plural(data.value?.attachments.length ?? 0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
error:
|
||||||
|
(e) => Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.close, size: 18),
|
||||||
|
Text(e.error.toString()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
loading:
|
||||||
|
(_) => Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
Text('loading').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final contentWidget =
|
||||||
|
isCompact
|
||||||
|
? itemWidget
|
||||||
|
: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerLow,
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
Text('repliesCount')
|
||||||
|
.plural(parent.repliesCount)
|
||||||
|
.fontSize(15)
|
||||||
|
.bold()
|
||||||
|
.padding(horizontal: 5),
|
||||||
|
itemWidget,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return InkWell(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
useRootNavigator: true,
|
||||||
|
builder: (context) => PostRepliesSheet(post: parent),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: contentWidget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostTruncateHint extends StatelessWidget {
|
||||||
|
final bool isCompact;
|
||||||
|
final EdgeInsets? margin;
|
||||||
|
final bool withArrow;
|
||||||
|
|
||||||
|
const PostTruncateHint({
|
||||||
|
super.key,
|
||||||
|
this.isCompact = false,
|
||||||
|
this.margin,
|
||||||
|
this.withArrow = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: margin ?? EdgeInsets.only(top: isCompact ? 4 : 8),
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: isCompact ? 8 : 12,
|
||||||
|
vertical: isCompact ? 4 : 8,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.more_horiz,
|
||||||
|
size: isCompact ? 14 : 16,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
SizedBox(width: isCompact ? 4 : 6),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
'postTruncated'.tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isCompact ? 10 : 12,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (withArrow) ...[
|
||||||
|
SizedBox(width: isCompact ? 3 : 4),
|
||||||
|
Icon(
|
||||||
|
Symbols.arrow_forward,
|
||||||
|
size: isCompact ? 12 : 14,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReferencedPostWidget extends StatelessWidget {
|
||||||
|
final SnPost item;
|
||||||
|
final bool isInteractive;
|
||||||
|
final EdgeInsets renderingPadding;
|
||||||
|
|
||||||
|
const ReferencedPostWidget({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
this.isInteractive = true,
|
||||||
|
this.renderingPadding = EdgeInsets.zero,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final referencePost = item.repliedPost ?? item.forwardedPost;
|
||||||
|
if (referencePost == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
|
final isReply = item.repliedPost != null;
|
||||||
|
|
||||||
|
final content = Container(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: renderingPadding.horizontal,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
top: 8,
|
||||||
|
left: renderingPadding.vertical,
|
||||||
|
right: renderingPadding.vertical,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
isReply ? Symbols.reply : Symbols.forward,
|
||||||
|
size: 16,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
isReply ? 'repliedTo'.tr() : 'forwarded'.tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ProfilePictureWidget(
|
||||||
|
fileId: referencePost.publisher.picture?.id,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
referencePost.publisher.nick,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (referencePost.visibility != 0)
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
PostVisibilityHelpers.getVisibilityIcon(
|
||||||
|
referencePost.visibility,
|
||||||
|
),
|
||||||
|
size: 12,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
PostVisibilityHelpers.getVisibilityText(
|
||||||
|
referencePost.visibility,
|
||||||
|
).tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(top: 2, bottom: 2),
|
||||||
|
if (referencePost.title?.isNotEmpty ?? false)
|
||||||
|
Text(
|
||||||
|
referencePost.title!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 13,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
).padding(top: 2, bottom: 2),
|
||||||
|
if (referencePost.description?.isNotEmpty ?? false)
|
||||||
|
Text(
|
||||||
|
referencePost.description!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).padding(bottom: 2),
|
||||||
|
if (referencePost.content?.isNotEmpty ?? false)
|
||||||
|
MarkdownTextContent(
|
||||||
|
content: referencePost.content!,
|
||||||
|
textStyle: const TextStyle(fontSize: 14),
|
||||||
|
isSelectable: false,
|
||||||
|
linesMargin:
|
||||||
|
referencePost.type == 0
|
||||||
|
? const EdgeInsets.only(bottom: 4)
|
||||||
|
: null,
|
||||||
|
attachments: item.attachments,
|
||||||
|
).padding(bottom: 4),
|
||||||
|
if (referencePost.isTruncated)
|
||||||
|
const PostTruncateHint(
|
||||||
|
isCompact: true,
|
||||||
|
margin: EdgeInsets.only(top: 4, bottom: 8),
|
||||||
|
),
|
||||||
|
if (referencePost.attachments.isNotEmpty &&
|
||||||
|
referencePost.type != 1)
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.attach_file,
|
||||||
|
size: 12,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'postHasAttachments'.plural(
|
||||||
|
referencePost.attachments.length,
|
||||||
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(vertical: 2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isInteractive) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return content.gestures(
|
||||||
|
onTap:
|
||||||
|
() => context.pushNamed(
|
||||||
|
'postDetail',
|
||||||
|
pathParameters: {'id': referencePost.id},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostHeader extends StatelessWidget {
|
||||||
|
final SnPost item;
|
||||||
|
final bool isFullPost;
|
||||||
|
final Widget? trailing;
|
||||||
|
final bool isInteractive;
|
||||||
|
final EdgeInsets renderingPadding;
|
||||||
|
final bool isRelativeTime;
|
||||||
|
|
||||||
|
const PostHeader({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
this.isFullPost = false,
|
||||||
|
this.trailing,
|
||||||
|
this.isInteractive = true,
|
||||||
|
this.renderingPadding = EdgeInsets.zero,
|
||||||
|
this.isRelativeTime = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
spacing: 12,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap:
|
||||||
|
isInteractive
|
||||||
|
? () {
|
||||||
|
context.pushNamed(
|
||||||
|
'publisherProfile',
|
||||||
|
pathParameters: {'name': item.publisher.name},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: ProfilePictureWidget(file: item.publisher.picture, radius: 16),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
Text(item.publisher.nick).bold(),
|
||||||
|
if (item.publisher.verification != null)
|
||||||
|
VerificationMark(mark: item.publisher.verification!),
|
||||||
|
Text('@${item.publisher.name}').fontSize(11),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
spacing: 6,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
!isFullPost && isRelativeTime
|
||||||
|
? (item.publishedAt ?? item.createdAt)!.formatRelative(
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
: (item.publishedAt ?? item.createdAt)!.formatSystem(),
|
||||||
|
).fontSize(10),
|
||||||
|
if (item.editedAt != null)
|
||||||
|
Text(
|
||||||
|
'editedAt'.tr(
|
||||||
|
args: [
|
||||||
|
!isFullPost && isRelativeTime
|
||||||
|
? item.editedAt!.formatRelative(context)
|
||||||
|
: item.editedAt!.formatSystem(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).fontSize(10),
|
||||||
|
if (item.visibility != 0)
|
||||||
|
Text(
|
||||||
|
PostVisibilityHelpers.getVisibilityText(
|
||||||
|
item.visibility,
|
||||||
|
).tr(),
|
||||||
|
).fontSize(10),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (trailing != null) trailing!,
|
||||||
|
],
|
||||||
|
).padding(horizontal: renderingPadding.horizontal, bottom: 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostBody extends ConsumerWidget {
|
||||||
|
final SnPost item;
|
||||||
|
final bool isFullPost;
|
||||||
|
final bool isTextSelectable;
|
||||||
|
final Widget? translationSection;
|
||||||
|
final bool isInteractive;
|
||||||
|
final EdgeInsets renderingPadding;
|
||||||
|
|
||||||
|
const PostBody({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
this.isFullPost = false,
|
||||||
|
this.isTextSelectable = true,
|
||||||
|
this.translationSection,
|
||||||
|
this.isInteractive = true,
|
||||||
|
this.renderingPadding = EdgeInsets.zero,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (!isFullPost && item.type == 1)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
top: 4,
|
||||||
|
left: renderingPadding.horizontal,
|
||||||
|
right: renderingPadding.vertical,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Badge(
|
||||||
|
label: const Text('postArticle').tr(),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
if (item.title != null)
|
||||||
|
Text(
|
||||||
|
item.title!,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (item.description != null)
|
||||||
|
Text(
|
||||||
|
item.description!,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
MarkdownTextContent(content: '${item.content!}...'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else if ((item.content?.isNotEmpty ?? false) ||
|
||||||
|
(item.title?.isNotEmpty ?? false) ||
|
||||||
|
(item.description?.isNotEmpty ?? false))
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: renderingPadding.horizontal,
|
||||||
|
right: renderingPadding.horizontal,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
if ((item.title?.isNotEmpty ?? false) ||
|
||||||
|
(item.description?.isNotEmpty ?? false))
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (item.title?.isNotEmpty ?? false)
|
||||||
|
Text(
|
||||||
|
item.title!,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium!
|
||||||
|
.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
if (item.description?.isNotEmpty ?? false)
|
||||||
|
Text(
|
||||||
|
item.description!,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(bottom: 4),
|
||||||
|
MarkdownTextContent(
|
||||||
|
content:
|
||||||
|
item.isTruncated ? '${item.content!}...' : item.content!,
|
||||||
|
isSelectable: isTextSelectable,
|
||||||
|
),
|
||||||
|
if (translationSection != null) translationSection!,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (item.isTruncated && item.type != 1)
|
||||||
|
PostTruncateHint(
|
||||||
|
isCompact: true,
|
||||||
|
withArrow: isInteractive,
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
top: 4,
|
||||||
|
bottom: 4,
|
||||||
|
left: renderingPadding.horizontal,
|
||||||
|
right: renderingPadding.horizontal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (item.attachments.isNotEmpty && item.type != 1)
|
||||||
|
CloudFileList(
|
||||||
|
files: item.attachments,
|
||||||
|
isColumn: !isInteractive,
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: renderingPadding.horizontal,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (item.tags.isNotEmpty || item.categories.isNotEmpty)
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 2,
|
||||||
|
children: [
|
||||||
|
if (item.tags.isNotEmpty)
|
||||||
|
Wrap(
|
||||||
|
runAlignment: WrapAlignment.center,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.label, size: 16).padding(top: 2),
|
||||||
|
for (final tag
|
||||||
|
in isFullPost ? item.tags : item.tags.take(3))
|
||||||
|
InkWell(
|
||||||
|
onTap:
|
||||||
|
isInteractive
|
||||||
|
? () {
|
||||||
|
GoRouter.of(context).pushNamed(
|
||||||
|
'postTagDetail',
|
||||||
|
pathParameters: {'slug': tag.slug},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text('#${tag.name ?? tag.slug}'),
|
||||||
|
),
|
||||||
|
if (!isFullPost && item.tags.length > 3)
|
||||||
|
Text('+${item.tags.length - 3}').opacity(0.6),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (item.categories.isNotEmpty)
|
||||||
|
Wrap(
|
||||||
|
runAlignment: WrapAlignment.center,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.category, size: 16).padding(top: 2),
|
||||||
|
for (final category
|
||||||
|
in isFullPost
|
||||||
|
? item.categories
|
||||||
|
: item.categories.take(2))
|
||||||
|
InkWell(
|
||||||
|
onTap:
|
||||||
|
isInteractive
|
||||||
|
? () {
|
||||||
|
GoRouter.of(context).pushNamed(
|
||||||
|
'postCategoryDetail',
|
||||||
|
pathParameters: {'slug': category.slug},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(category.categoryDisplayTitle),
|
||||||
|
),
|
||||||
|
if (!isFullPost && item.categories.length > 2)
|
||||||
|
Text('+${item.categories.length - 2}').opacity(0.6),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: renderingPadding.horizontal + 4, top: 4),
|
||||||
|
if (item.meta?['embeds'] != null)
|
||||||
|
...((item.meta!['embeds'] as List<dynamic>)
|
||||||
|
.map((embedData) => convertMapKeysToSnakeCase(embedData))
|
||||||
|
.map(
|
||||||
|
(embedData) => switch (embedData['type']) {
|
||||||
|
'link' => EmbedLinkWidget(
|
||||||
|
link: SnScrappedLink.fromJson(embedData),
|
||||||
|
maxWidth: math.min(
|
||||||
|
MediaQuery.of(context).size.width,
|
||||||
|
kWideScreenWidth,
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
top: 4,
|
||||||
|
bottom: 4,
|
||||||
|
left: renderingPadding.horizontal,
|
||||||
|
right: renderingPadding.horizontal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'poll' => Card(
|
||||||
|
margin: EdgeInsets.symmetric(
|
||||||
|
horizontal: renderingPadding.horizontal,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
embedData['poll'] == null
|
||||||
|
? const Text('Poll was not loaded...')
|
||||||
|
: PollSubmit(
|
||||||
|
initialAnswers:
|
||||||
|
embedData['poll']?['user_answer']?['answer'],
|
||||||
|
stats: embedData['poll']?['stats'],
|
||||||
|
poll: SnPollWithStats.fromJson(embedData['poll']),
|
||||||
|
onSubmit: (_) {},
|
||||||
|
isReadonly: !isInteractive,
|
||||||
|
).padding(horizontal: 16, vertical: 12),
|
||||||
|
),
|
||||||
|
_ => Text('Unable show embed: ${embedData['type']}'),
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'post_item.dart';
|
part of 'post_shared.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
@@ -10,7 +10,9 @@ import connectivity_plus
|
|||||||
import device_info_plus
|
import device_info_plus
|
||||||
import file_picker
|
import file_picker
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
|
import firebase_analytics
|
||||||
import firebase_core
|
import firebase_core
|
||||||
|
import firebase_crashlytics
|
||||||
import firebase_messaging
|
import firebase_messaging
|
||||||
import flutter_inappwebview_macos
|
import flutter_inappwebview_macos
|
||||||
import flutter_platform_alert
|
import flutter_platform_alert
|
||||||
@@ -44,7 +46,9 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
|
FirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FirebaseAnalyticsPlugin"))
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
|
FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin"))
|
||||||
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
||||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||||
FlutterPlatformAlertPlugin.register(with: registry.registrar(forPlugin: "FlutterPlatformAlertPlugin"))
|
FlutterPlatformAlertPlugin.register(with: registry.registrar(forPlugin: "FlutterPlatformAlertPlugin"))
|
||||||
|
|||||||
@@ -13,23 +13,64 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- Firebase/CoreOnly (12.0.0):
|
- Firebase/CoreOnly (12.0.0):
|
||||||
- FirebaseCore (~> 12.0.0)
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- Firebase/Crashlytics (12.0.0):
|
||||||
|
- Firebase/CoreOnly
|
||||||
|
- FirebaseCrashlytics (~> 12.0.0)
|
||||||
- Firebase/Messaging (12.0.0):
|
- Firebase/Messaging (12.0.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 12.0.0)
|
- FirebaseMessaging (~> 12.0.0)
|
||||||
|
- firebase_analytics (12.0.0):
|
||||||
|
- firebase_core
|
||||||
|
- FirebaseAnalytics (= 12.0.0)
|
||||||
|
- FlutterMacOS
|
||||||
- firebase_core (4.0.0):
|
- firebase_core (4.0.0):
|
||||||
- Firebase/CoreOnly (~> 12.0.0)
|
- Firebase/CoreOnly (~> 12.0.0)
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- firebase_crashlytics (5.0.0):
|
||||||
|
- Firebase/CoreOnly (~> 12.0.0)
|
||||||
|
- Firebase/Crashlytics (~> 12.0.0)
|
||||||
|
- firebase_core
|
||||||
|
- FlutterMacOS
|
||||||
- firebase_messaging (16.0.0):
|
- firebase_messaging (16.0.0):
|
||||||
- Firebase/CoreOnly (~> 12.0.0)
|
- Firebase/CoreOnly (~> 12.0.0)
|
||||||
- Firebase/Messaging (~> 12.0.0)
|
- Firebase/Messaging (~> 12.0.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- FirebaseAnalytics (12.0.0):
|
||||||
|
- FirebaseAnalytics/Default (= 12.0.0)
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- FirebaseInstallations (~> 12.0.0)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- FirebaseAnalytics/Default (12.0.0):
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- FirebaseInstallations (~> 12.0.0)
|
||||||
|
- GoogleAppMeasurement/Default (= 12.0.0)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseCore (12.0.0):
|
- FirebaseCore (12.0.0):
|
||||||
- FirebaseCoreInternal (~> 12.0.0)
|
- FirebaseCoreInternal (~> 12.0.0)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/Logger (~> 8.1)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
|
- FirebaseCoreExtension (12.0.0):
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
- FirebaseCoreInternal (12.0.0):
|
- FirebaseCoreInternal (12.0.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- FirebaseCrashlytics (12.0.0):
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- FirebaseInstallations (~> 12.0.0)
|
||||||
|
- FirebaseRemoteConfigInterop (~> 12.0.0)
|
||||||
|
- FirebaseSessions (~> 12.0.0)
|
||||||
|
- GoogleDataTransport (~> 10.1)
|
||||||
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- PromisesObjC (~> 2.4)
|
||||||
- FirebaseInstallations (12.0.0):
|
- FirebaseInstallations (12.0.0):
|
||||||
- FirebaseCore (~> 12.0.0)
|
- FirebaseCore (~> 12.0.0)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
@@ -44,6 +85,16 @@ PODS:
|
|||||||
- GoogleUtilities/Reachability (~> 8.1)
|
- GoogleUtilities/Reachability (~> 8.1)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
|
- FirebaseRemoteConfigInterop (12.0.0)
|
||||||
|
- FirebaseSessions (12.0.0):
|
||||||
|
- FirebaseCore (~> 12.0.0)
|
||||||
|
- FirebaseCoreExtension (~> 12.0.0)
|
||||||
|
- FirebaseInstallations (~> 12.0.0)
|
||||||
|
- GoogleDataTransport (~> 10.1)
|
||||||
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- PromisesSwift (~> 2.1)
|
||||||
- flutter_inappwebview_macos (0.0.1):
|
- flutter_inappwebview_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- OrderedSet (~> 6.0.3)
|
- OrderedSet (~> 6.0.3)
|
||||||
@@ -63,6 +114,28 @@ PODS:
|
|||||||
- gal (1.0.0):
|
- gal (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- GoogleAppMeasurement/Core (12.0.0):
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- GoogleAppMeasurement/Default (12.0.0):
|
||||||
|
- GoogleAdsOnDeviceConversion (= 2.1.0)
|
||||||
|
- GoogleAppMeasurement/Core (= 12.0.0)
|
||||||
|
- GoogleAppMeasurement/IdentitySupport (= 12.0.0)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- GoogleAppMeasurement/IdentitySupport (12.0.0):
|
||||||
|
- GoogleAppMeasurement/Core (= 12.0.0)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
- GoogleDataTransport (10.1.0):
|
- GoogleDataTransport (10.1.0):
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- PromisesObjC (~> 2.4)
|
- PromisesObjC (~> 2.4)
|
||||||
@@ -76,6 +149,9 @@ PODS:
|
|||||||
- GoogleUtilities/Logger (8.1.0):
|
- GoogleUtilities/Logger (8.1.0):
|
||||||
- GoogleUtilities/Environment
|
- GoogleUtilities/Environment
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilities/MethodSwizzler (8.1.0):
|
||||||
|
- GoogleUtilities/Logger
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Network (8.1.0):
|
- GoogleUtilities/Network (8.1.0):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- "GoogleUtilities/NSData+zlib"
|
- "GoogleUtilities/NSData+zlib"
|
||||||
@@ -117,6 +193,8 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- PromisesObjC (2.4.0)
|
- PromisesObjC (2.4.0)
|
||||||
|
- PromisesSwift (2.4.0):
|
||||||
|
- PromisesObjC (= 2.4.0)
|
||||||
- record_macos (1.0.0):
|
- record_macos (1.0.0):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- SAMKeychain (1.5.3)
|
- SAMKeychain (1.5.3)
|
||||||
@@ -130,25 +208,25 @@ PODS:
|
|||||||
- sqflite_darwin (0.0.4):
|
- sqflite_darwin (0.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqlite3 (3.50.3):
|
- sqlite3 (3.50.4):
|
||||||
- sqlite3/common (= 3.50.3)
|
- sqlite3/common (= 3.50.4)
|
||||||
- sqlite3/common (3.50.3)
|
- sqlite3/common (3.50.4)
|
||||||
- sqlite3/dbstatvtab (3.50.3):
|
- sqlite3/dbstatvtab (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/fts5 (3.50.3):
|
- sqlite3/fts5 (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/math (3.50.3):
|
- sqlite3/math (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/perf-threadsafe (3.50.3):
|
- sqlite3/perf-threadsafe (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/rtree (3.50.3):
|
- sqlite3/rtree (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/session (3.50.3):
|
- sqlite3/session (3.50.4):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3_flutter_libs (0.0.1):
|
- sqlite3_flutter_libs (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqlite3 (~> 3.50.3)
|
- sqlite3 (~> 3.50.4)
|
||||||
- sqlite3/dbstatvtab
|
- sqlite3/dbstatvtab
|
||||||
- sqlite3/fts5
|
- sqlite3/fts5
|
||||||
- sqlite3/math
|
- sqlite3/math
|
||||||
@@ -172,7 +250,9 @@ DEPENDENCIES:
|
|||||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||||
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
|
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
|
||||||
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
|
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
|
||||||
|
- firebase_analytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos`)
|
||||||
- firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`)
|
- firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`)
|
||||||
|
- firebase_crashlytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_crashlytics/macos`)
|
||||||
- firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`)
|
- firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`)
|
||||||
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
||||||
- flutter_platform_alert (from `Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos`)
|
- flutter_platform_alert (from `Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos`)
|
||||||
@@ -204,15 +284,22 @@ DEPENDENCIES:
|
|||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- Firebase
|
- Firebase
|
||||||
|
- FirebaseAnalytics
|
||||||
- FirebaseCore
|
- FirebaseCore
|
||||||
|
- FirebaseCoreExtension
|
||||||
- FirebaseCoreInternal
|
- FirebaseCoreInternal
|
||||||
|
- FirebaseCrashlytics
|
||||||
- FirebaseInstallations
|
- FirebaseInstallations
|
||||||
- FirebaseMessaging
|
- FirebaseMessaging
|
||||||
|
- FirebaseRemoteConfigInterop
|
||||||
|
- FirebaseSessions
|
||||||
|
- GoogleAppMeasurement
|
||||||
- GoogleDataTransport
|
- GoogleDataTransport
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
- nanopb
|
- nanopb
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
- PromisesObjC
|
- PromisesObjC
|
||||||
|
- PromisesSwift
|
||||||
- SAMKeychain
|
- SAMKeychain
|
||||||
- sqlite3
|
- sqlite3
|
||||||
- WebRTC-SDK
|
- WebRTC-SDK
|
||||||
@@ -230,8 +317,12 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
|
||||||
file_selector_macos:
|
file_selector_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
|
||||||
|
firebase_analytics:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos
|
||||||
firebase_core:
|
firebase_core:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos
|
||||||
|
firebase_crashlytics:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/firebase_crashlytics/macos
|
||||||
firebase_messaging:
|
firebase_messaging:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos
|
||||||
flutter_inappwebview_macos:
|
flutter_inappwebview_macos:
|
||||||
@@ -295,12 +386,19 @@ SPEC CHECKSUMS:
|
|||||||
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||||
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
|
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
|
||||||
Firebase: 800d487043c0557d9faed71477a38d9aafb08a41
|
Firebase: 800d487043c0557d9faed71477a38d9aafb08a41
|
||||||
|
firebase_analytics: 53f0dc87ad10f56a6df8746da60d8a5fe41f886f
|
||||||
firebase_core: eeea10f64026b68cd0bc3dee079ab4717e22909e
|
firebase_core: eeea10f64026b68cd0bc3dee079ab4717e22909e
|
||||||
|
firebase_crashlytics: 7be1dacc38809971354def57193b280636a3d51a
|
||||||
firebase_messaging: 5eefcd5bde556bfacdd9968e11c52f39032dfbe5
|
firebase_messaging: 5eefcd5bde556bfacdd9968e11c52f39032dfbe5
|
||||||
|
FirebaseAnalytics: 6d790cd1b159b4eb61a99948df0934ce505a34f7
|
||||||
FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a
|
FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a
|
||||||
|
FirebaseCoreExtension: 639afb3de6abd611952be78a794c54a47fa0f361
|
||||||
FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55
|
FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55
|
||||||
|
FirebaseCrashlytics: db75aa0cab8d00f68406fa247c32fe17ade884d7
|
||||||
FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988
|
FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988
|
||||||
FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde
|
FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde
|
||||||
|
FirebaseRemoteConfigInterop: bfa0ea72ba3dc5af739777296424e46bd6f42613
|
||||||
|
FirebaseSessions: 4e784acda213108aafef536535cdfc03504acc42
|
||||||
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
||||||
flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
|
flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
|
||||||
flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54
|
flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54
|
||||||
@@ -309,6 +407,7 @@ SPEC CHECKSUMS:
|
|||||||
flutter_webrtc: 0d70bd8782c19bde286dc52f766eebbea26de201
|
flutter_webrtc: 0d70bd8782c19bde286dc52f766eebbea26de201
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
||||||
|
GoogleAppMeasurement: 8f6ab04ad6ae493b53fcf56bd26323fb2f1384f3
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
|
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
|
||||||
@@ -322,14 +421,15 @@ SPEC CHECKSUMS:
|
|||||||
pasteboard: 278d8100149f940fb795d6b3a74f0720c890ecb7
|
pasteboard: 278d8100149f940fb795d6b3a74f0720c890ecb7
|
||||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
|
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
||||||
record_macos: 295d70bd5fb47145df78df7b80e6697cd18403c0
|
record_macos: 295d70bd5fb47145df78df7b80e6697cd18403c0
|
||||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||||
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
|
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
|
||||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||||
sign_in_with_apple: 6673c03c9e3643f6c8d33601943fbfa9ae99f94e
|
sign_in_with_apple: 6673c03c9e3643f6c8d33601943fbfa9ae99f94e
|
||||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||||
sqlite3: 83105acd294c9137c026e2da1931c30b4588ab81
|
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
|
||||||
sqlite3_flutter_libs: 616267f2fca40e9c6af8c5d82324e05667040b6e
|
sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
|
||||||
super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189
|
super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189
|
||||||
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
|
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
|
||||||
volume_controller: 5c068e6d085c80dadd33fc2c918d2114b775b3dd
|
volume_controller: 5c068e6d085c80dadd33fc2c918d2114b775b3dd
|
||||||
|
|||||||
@@ -234,6 +234,7 @@
|
|||||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||||
F1E275A871246799FC3019F6 /* [CP] Embed Pods Frameworks */,
|
F1E275A871246799FC3019F6 /* [CP] Embed Pods Frameworks */,
|
||||||
8D06F41203F1FD2FDE04DC7F /* [CP] Copy Pods Resources */,
|
8D06F41203F1FD2FDE04DC7F /* [CP] Copy Pods Resources */,
|
||||||
|
6B512DBE9D8E74A09686E70F /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -376,6 +377,24 @@
|
|||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
|
6B512DBE9D8E74A09686E70F /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\"";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:${PUB_CACHE}/bin:$HOME/.pub-cache/bin\"\n\nif [ -z \"$PODS_ROOT\" ] || [ ! -d \"$PODS_ROOT/FirebaseCrashlytics\" ]; then\n # Cannot use \"BUILD_DIR%/Build/*\" as per Firebase documentation, it points to \"flutter-project/build/ios/*\" path which doesn't have run script\n DERIVED_DATA_PATH=$(echo \"$BUILD_ROOT\" | sed -E 's|(.*DerivedData/[^/]+).*|\\1|')\n PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT=\"${DERIVED_DATA_PATH}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\"\nelse\n PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT=\"$PODS_ROOT/FirebaseCrashlytics/run\"\nfi\n\n# Command to upload symbols script used to upload symbols to Firebase server\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=\"$PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT\" --platform=macos --apple-project-path=\"${SRCROOT}\" --env-platform-name=\"${PLATFORM_NAME}\" --env-configuration=\"${CONFIGURATION}\" --env-project-dir=\"${PROJECT_DIR}\" --env-built-products-dir=\"${BUILT_PRODUCTS_DIR}\" --env-dwarf-dsym-folder-path=\"${DWARF_DSYM_FOLDER_PATH}\" --env-dwarf-dsym-file-name=\"${DWARF_DSYM_FILE_NAME}\" --env-infoplist-path=\"${INFOPLIST_PATH}\" --default-config=default\n";
|
||||||
|
};
|
||||||
8D06F41203F1FD2FDE04DC7F /* [CP] Copy Pods Resources */ = {
|
8D06F41203F1FD2FDE04DC7F /* [CP] Copy Pods Resources */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
|||||||
192
pubspec.lock
192
pubspec.lock
@@ -73,22 +73,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.13.0"
|
version: "2.13.0"
|
||||||
auto_route:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: auto_route
|
|
||||||
sha256: b8c036fa613a98a759cf0fdcba26e62f4985dcbff01a5e760ab411e8554bbaf0
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "10.1.0+1"
|
|
||||||
auto_route_generator:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description:
|
|
||||||
name: auto_route_generator
|
|
||||||
sha256: "9e3846fcbeacba5c362557328dd8c8fbc953b6a0cbc3395365e8d8f92eea29c4"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "10.1.0"
|
|
||||||
avatar_stack:
|
avatar_stack:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -205,10 +189,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: "0b1b12a0a549605e5f04476031cd0bc91ead1d7c8e830773a18ee54179b3cb62"
|
sha256: ba95c961bafcd8686d1cf63be864eb59447e795e124d98d6a27d91fcd13602fb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.11.0"
|
version: "8.11.1"
|
||||||
cached_network_image:
|
cached_network_image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -317,10 +301,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: connectivity_plus
|
name: connectivity_plus
|
||||||
sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99"
|
sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.4"
|
version: "6.1.5"
|
||||||
connectivity_plus_platform_interface:
|
connectivity_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -329,6 +313,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
console:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: console
|
||||||
|
sha256: e04e7824384c5b39389acdd6dc7d33f3efe6b232f6f16d7626f194f6a01ad69a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.0"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -421,10 +413,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_webrtc
|
name: dart_webrtc
|
||||||
sha256: a2ae542cdadc21359022adedc26138fa3487cc3b3547c24ff4f556681869e28c
|
sha256: "3bfa069a8b14a53ba506f6dd529e9b88c878ba0cc238f311051a39bf1e53d075"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.3+hotfix.4"
|
version: "1.5.3+hotfix.5"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -453,10 +445,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
|
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.8.0+1"
|
version: "5.9.0"
|
||||||
dio_web_adapter:
|
dio_web_adapter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -573,10 +565,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
sha256: "13ba4e627ef24503a465d1d61b32596ce10eb6b8903678d362a528f9939b4aa8"
|
sha256: ef7d2a085c1b1d69d17b6842d0734aad90156de08df6bd3c12496d0bd6ddf8e2
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.2.1"
|
version: "10.3.1"
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -609,6 +601,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.3+4"
|
version: "0.9.3+4"
|
||||||
|
firebase_analytics:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: firebase_analytics
|
||||||
|
sha256: "07146e89e11302c6b07e3465c2c556ebcdd0053a3c5b1aa9bfd3203b778e5b4c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "12.0.0"
|
||||||
|
firebase_analytics_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_analytics_platform_interface
|
||||||
|
sha256: "27e81a0efc821bec6cba64abc1083b91c8ddbad28eeb4c6f6b7c78a59d06f259"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
|
firebase_analytics_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_analytics_web
|
||||||
|
sha256: "7d87f47462042a7d9125e3123db2783bc72917d85e2719d4cb6aeaec209605e1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.0"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -633,6 +649,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
|
firebase_crashlytics:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: firebase_crashlytics
|
||||||
|
sha256: "95b6871850b1a7e3b09c284c59a0c71fafcad3eee8ac1b6f06aaf8979290cbb8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
|
firebase_crashlytics_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_crashlytics_platform_interface
|
||||||
|
sha256: ba5b7a916f1ebedc6db35b33abdc618f202fc25e0792088dfba698e19fec9c09
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.8.11"
|
||||||
firebase_messaging:
|
firebase_messaging:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -678,6 +710,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_app_update:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_app_update
|
||||||
|
sha256: "09290240949c4651581cd6fc535e52d019e189e694d6019c56b5a56c2e69ba65"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
flutter_blurhash:
|
flutter_blurhash:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -714,10 +754,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_hooks
|
name: flutter_hooks
|
||||||
sha256: b772e710d16d7a20c0740c4f855095026b31c7eb5ba3ab67d2bd52021cd9461d
|
sha256: c3df76c62bb3a9f9bee75c57cdab40abab6123b734c1cd7e9b26a5dbd436eceb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.21.2"
|
version: "0.21.3"
|
||||||
flutter_inappwebview:
|
flutter_inappwebview:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -911,10 +951,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e
|
sha256: "6382ce712ff69b0f719640ce957559dde459e55ecd433c767e06d139ddf16cab"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.28"
|
version: "2.0.29"
|
||||||
flutter_popup_card:
|
flutter_popup_card:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1033,10 +1073,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: font_awesome_flutter
|
name: font_awesome_flutter
|
||||||
sha256: d3a89184101baec7f4600d58840a764d2ef760fe1c5a20ef9e6b0e9b24a07a3a
|
sha256: f50ce90dbe26d977415b9540400d6778bef00894aced6358ae578abd92b14b10
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.8.0"
|
version: "10.9.0"
|
||||||
freezed:
|
freezed:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -1077,6 +1117,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
get_it:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: get_it
|
||||||
|
sha256: a4292e7cf67193f8e7c1258203104eb2a51ec8b3a04baa14695f4064c144297b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.2.0"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1089,10 +1137,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: c489908a54ce2131f1d1b7cc631af9c1a06fac5ca7c449e959192089f9489431
|
sha256: "8b1f37dfaf6e958c6b872322db06f946509433bec3de753c3491a42ae9ec2b48"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "16.0.0"
|
version: "16.1.0"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1153,10 +1201,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.5.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1193,10 +1241,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image_picker_android
|
name: image_picker_android
|
||||||
sha256: "6fae381e6af2bbe0365a5e4ce1db3959462fa0c4d234facf070746024bb80c8d"
|
sha256: b08e9a04d0f8d91f4a6e767a745b9871bfbc585410205c311d0492de20a7ccd6
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.12+24"
|
version: "0.8.12+25"
|
||||||
image_picker_for_web:
|
image_picker_for_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1361,18 +1409,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: local_auth_android
|
name: local_auth_android
|
||||||
sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79"
|
sha256: "316503f6772dea9c0c038bb7aac4f68ab00112d707d258c770f7fc3c250a2d88"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.50"
|
version: "1.0.51"
|
||||||
local_auth_darwin:
|
local_auth_darwin:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: local_auth_darwin
|
name: local_auth_darwin
|
||||||
sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f"
|
sha256: "0e9706a8543a4a2eee60346294d6a633dd7c3ee60fae6b752570457c4ff32055"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.6.0"
|
||||||
local_auth_platform_interface:
|
local_auth_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1438,7 +1486,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.17"
|
version: "0.12.17"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||||
@@ -1549,6 +1597,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
|
msix:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: msix
|
||||||
|
sha256: f88033fcb9e0dd8de5b18897cbebbd28ea30596810f4a7c86b12b0c03ace87e5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.16.12"
|
||||||
native_exif:
|
native_exif:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1593,18 +1649,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191"
|
sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.3.0"
|
version: "8.3.1"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_platform_interface
|
name: package_info_plus_platform_interface
|
||||||
sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c"
|
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.1"
|
||||||
palette_generator:
|
palette_generator:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1777,10 +1833,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: protobuf
|
name: protobuf
|
||||||
sha256: "6153efcc92a06910918f3db8231fd2cf828ac81e50ebd87adc8f8a8cb3caff0e"
|
sha256: de9c9eb2c33f8e933a42932fe1dc504800ca45ebc3d673e6ed7f39754ee4053e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.1"
|
version: "4.2.0"
|
||||||
provider:
|
provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1989,6 +2045,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
screenshot:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: screenshot
|
||||||
|
sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
scroll_to_index:
|
scroll_to_index:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2009,18 +2073,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: share_plus
|
name: share_plus
|
||||||
sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0
|
sha256: d7dc0630a923883c6328ca31b89aa682bacbf2f8304162d29f7c6aaff03a27a1
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.0"
|
version: "11.1.0"
|
||||||
share_plus_platform_interface:
|
share_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: share_plus_platform_interface
|
name: share_plus_platform_interface
|
||||||
sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef"
|
sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.1.0"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -2033,10 +2097,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
|
sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.10"
|
version: "2.4.11"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2142,10 +2206,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
sha256: "4f81479fe5194a622cdd1713fe1ecb683a6e6c85cd8cec8e2e35ee5ab3fdf2a1"
|
sha256: a447acb083d3a5ef17f983dd36201aeea33fedadb3228fa831f2f0c92f0f3aca
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.6"
|
version: "1.3.7"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2206,18 +2270,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqlite3
|
name: sqlite3
|
||||||
sha256: dd806fff004a0aeb01e208b858dbc649bc72104670d425a81a6dd17698535f6e
|
sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.0"
|
version: "2.9.0"
|
||||||
sqlite3_flutter_libs:
|
sqlite3_flutter_libs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqlite3_flutter_libs
|
name: sqlite3_flutter_libs
|
||||||
sha256: fd996da5515a73aacd0a04ae7063db5fe8df42670d974df4c3ee538c652eef2e
|
sha256: "2b03273e71867a8a4d030861fc21706200debe5c5858a4b9e58f4a1c129586a4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.38"
|
version: "0.5.39"
|
||||||
sqlparser:
|
sqlparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2424,10 +2488,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79"
|
sha256: "0aedad096a85b49df2e4725fa32118f9fa580f3b14af7a2d2221896a02cd5656"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.16"
|
version: "6.3.17"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2654,4 +2718,4 @@ packages:
|
|||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.0 <4.0.0"
|
dart: ">=3.8.0 <4.0.0"
|
||||||
flutter: ">=3.29.0"
|
flutter: ">=3.32.0"
|
||||||
|
|||||||
31
pubspec.yaml
31
pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 3.1.0+117
|
version: 3.2.0+125
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.2
|
sdk: ^3.7.2
|
||||||
@@ -36,15 +36,15 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
flutter_hooks: ^0.21.2
|
flutter_hooks: ^0.21.3
|
||||||
hooks_riverpod: ^2.6.1
|
hooks_riverpod: ^2.6.1
|
||||||
bitsdojo_window: ^0.1.6
|
bitsdojo_window: ^0.1.6
|
||||||
go_router: ^16.0.0
|
go_router: ^16.1.0
|
||||||
styled_widget: ^0.4.1
|
styled_widget: ^0.4.1
|
||||||
shared_preferences: ^2.5.3
|
shared_preferences: ^2.5.3
|
||||||
flutter_riverpod: ^2.6.1
|
flutter_riverpod: ^2.6.1
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
dio: ^5.8.0+1
|
dio: ^5.9.0
|
||||||
very_good_infinite_list: ^0.9.0
|
very_good_infinite_list: ^0.9.0
|
||||||
freezed_annotation: ^3.1.0
|
freezed_annotation: ^3.1.0
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
@@ -67,16 +67,16 @@ dependencies:
|
|||||||
easy_localization: ^3.0.8
|
easy_localization: ^3.0.8
|
||||||
flutter_inappwebview: ^6.1.5
|
flutter_inappwebview: ^6.1.5
|
||||||
animations: ^2.0.11
|
animations: ^2.0.11
|
||||||
package_info_plus: ^8.3.0
|
package_info_plus: ^8.3.1
|
||||||
device_info_plus: ^11.5.0
|
device_info_plus: ^11.5.0
|
||||||
tus_client_dart:
|
tus_client_dart:
|
||||||
git: https://github.com/LittleSheep2Code/tus_client.git
|
git: https://github.com/LittleSheep2Code/tus_client.git
|
||||||
cross_file: ^0.3.4+2
|
cross_file: ^0.3.4+2
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
file_picker: ^10.2.1
|
file_picker: ^10.3.1
|
||||||
riverpod_annotation: ^2.6.1
|
riverpod_annotation: ^2.6.1
|
||||||
image_picker_platform_interface: ^2.10.1
|
image_picker_platform_interface: ^2.10.1
|
||||||
image_picker_android: ^0.8.12+24
|
image_picker_android: ^0.8.12+25
|
||||||
super_context_menu: ^0.9.1
|
super_context_menu: ^0.9.1
|
||||||
modal_bottom_sheet: ^3.0.0
|
modal_bottom_sheet: ^3.0.0
|
||||||
firebase_messaging: ^16.0.0
|
firebase_messaging: ^16.0.0
|
||||||
@@ -121,7 +121,7 @@ dependencies:
|
|||||||
local_auth: ^2.3.0
|
local_auth: ^2.3.0
|
||||||
flutter_secure_storage: ^9.2.4
|
flutter_secure_storage: ^9.2.4
|
||||||
flutter_math_fork: ^0.7.4
|
flutter_math_fork: ^0.7.4
|
||||||
share_plus: ^11.0.0
|
share_plus: ^11.1.0
|
||||||
receive_sharing_intent: ^1.8.1
|
receive_sharing_intent: ^1.8.1
|
||||||
top_snackbar_flutter: ^3.3.0
|
top_snackbar_flutter: ^3.3.0
|
||||||
textfield_tags:
|
textfield_tags:
|
||||||
@@ -133,6 +133,11 @@ dependencies:
|
|||||||
flutter_typeahead: ^5.2.0
|
flutter_typeahead: ^5.2.0
|
||||||
flutter_langdetect: ^0.0.2
|
flutter_langdetect: ^0.0.2
|
||||||
waveform_flutter: ^1.2.0
|
waveform_flutter: ^1.2.0
|
||||||
|
flutter_app_update: ^3.2.2
|
||||||
|
firebase_crashlytics: ^5.0.0
|
||||||
|
firebase_analytics: ^12.0.0
|
||||||
|
material_color_utilities: ^0.11.1
|
||||||
|
screenshot: ^3.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -144,7 +149,6 @@ dev_dependencies:
|
|||||||
# package. See that file for information about deactivating specific lint
|
# package. See that file for information about deactivating specific lint
|
||||||
# rules and activating additional ones.
|
# rules and activating additional ones.
|
||||||
flutter_lints: ^6.0.0
|
flutter_lints: ^6.0.0
|
||||||
auto_route_generator: ^10.1.0
|
|
||||||
build_runner: ^2.5.4
|
build_runner: ^2.5.4
|
||||||
freezed: ^3.1.0
|
freezed: ^3.1.0
|
||||||
json_serializable: ^6.9.5
|
json_serializable: ^6.9.5
|
||||||
@@ -153,6 +157,7 @@ dev_dependencies:
|
|||||||
riverpod_lint: ^2.6.5
|
riverpod_lint: ^2.6.5
|
||||||
drift_dev: ^2.28.0
|
drift_dev: ^2.28.0
|
||||||
flutter_launcher_icons: ^0.14.4
|
flutter_launcher_icons: ^0.14.4
|
||||||
|
msix: ^3.16.12
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
@@ -222,3 +227,11 @@ flutter_native_splash:
|
|||||||
image_dark: "assets/icons/icon-dark.png"
|
image_dark: "assets/icons/icon-dark.png"
|
||||||
color: "#ffffff"
|
color: "#ffffff"
|
||||||
color_dark: "#121212"
|
color_dark: "#121212"
|
||||||
|
|
||||||
|
msix_config:
|
||||||
|
display_name: Solian
|
||||||
|
publisher_display_name: Solsynth LLC
|
||||||
|
identity_name: dev.solian.app
|
||||||
|
msix_version: 3.2.0.0
|
||||||
|
logo_path: .\assets\icons\icon.png
|
||||||
|
capabilities: internetClientServer, location, microphone, webcam
|
||||||
52
setup.iss
Normal file
52
setup.iss
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
; ==================================================
|
||||||
|
#define AppVersion "3.2.0"
|
||||||
|
#define BuildNumber "124"
|
||||||
|
; ==================================================
|
||||||
|
|
||||||
|
#define FullVersion AppVersion + "." + BuildNumber
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
AppName=Solian
|
||||||
|
AppVersion={#AppVersion}
|
||||||
|
AppPublisher=Solsynth
|
||||||
|
AppPublisherURL=https://solsynth.dev
|
||||||
|
AppSupportURL=https://kb.solsynth.dev/zh/solar-network
|
||||||
|
AppUpdatesURL=https://github.com/Solsynth/Solian/releases
|
||||||
|
AppCopyright=Copyright © 2025 Solsynth
|
||||||
|
VersionInfoVersion={#FullVersion}
|
||||||
|
UninstallDisplayName=Solian
|
||||||
|
UninstallDisplayIcon={app}\Solian.exe
|
||||||
|
|
||||||
|
DefaultDirName={commonpf}\Solian
|
||||||
|
UsePreviousAppDir=no
|
||||||
|
|
||||||
|
OutputDir=.\Installer
|
||||||
|
OutputBaseFilename=windows-x86_64-setup
|
||||||
|
SetupIconFile=.\assets\icons\icon.ico
|
||||||
|
|
||||||
|
Compression=lzma2/ultra64
|
||||||
|
SolidCompression=yes
|
||||||
|
LZMAUseSeparateProcess=yes
|
||||||
|
LZMANumBlockThreads=4
|
||||||
|
|
||||||
|
ArchitecturesAllowed=x64compatible
|
||||||
|
PrivilegesRequired=admin
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
Source: ".\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Name: "{group}\Solian"; Filename: "{app}\Solian.exe";IconFilename: "{app}\Solian.exe"
|
||||||
|
Name: "{group}\{cm:UninstallProgram,Solian}"; Filename: "{uninstallexe}"
|
||||||
|
Name: "{autodesktop}\Solian"; Filename: "{app}\Solian.exe"; Tasks: desktopicon
|
||||||
|
|
||||||
|
[Tasks]
|
||||||
|
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||||
|
|
||||||
|
[Run]
|
||||||
|
Filename: "{app}\Solian.exe"; Description: "Launch Solian"; Flags: nowait postinstall skipifsilent
|
||||||
|
|
||||||
|
[UninstallDelete]
|
||||||
|
Type: filesandordirs; Name: "{userappdata}\dev.solsynth\Solian"
|
||||||
|
Type: files; Name: "{group}\Solian.lnk" ;
|
||||||
|
Type: files; Name: "{autodesktop}\Solian.lnk" ;
|
||||||
Reference in New Issue
Block a user