mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-03-03 17:06:47 +00:00
commit
b9a2300021
120 changed files with 2574 additions and 1402 deletions
|
|
@ -1,5 +1,12 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.0.92
|
||||||
|
|
||||||
|
- Adds the option to share contacts
|
||||||
|
- Adds option to zoom in received images / videos
|
||||||
|
- Fixes issue with "reuploaded requested" not working
|
||||||
|
- Fixes race condition while writing to the log file
|
||||||
|
|
||||||
## 0.0.91
|
## 0.0.91
|
||||||
|
|
||||||
- Fixes link preview on iOS
|
- Fixes link preview on iOS
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ 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.9.0' apply false
|
id "com.android.application" version '8.9.1' 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
|
||||||
// END: FlutterFire Configuration
|
// END: FlutterFire Configuration
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3a3a7e5a6323da5413e3dd8c21abfa7cbe1c3a6f
|
Subproject commit 33111edeb285db34edeb9fd21762825babe71ab0
|
||||||
209
ios/Podfile.lock
209
ios/Podfile.lock
|
|
@ -54,55 +54,55 @@ PODS:
|
||||||
- file_picker (0.0.1):
|
- file_picker (0.0.1):
|
||||||
- DKImagePickerController/PhotoGallery
|
- DKImagePickerController/PhotoGallery
|
||||||
- Flutter
|
- Flutter
|
||||||
- Firebase (12.6.0):
|
- Firebase (12.8.0):
|
||||||
- Firebase/Core (= 12.6.0)
|
- Firebase/Core (= 12.8.0)
|
||||||
- Firebase/Core (12.6.0):
|
- Firebase/Core (12.8.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseAnalytics (~> 12.6.0)
|
- FirebaseAnalytics (~> 12.8.0)
|
||||||
- Firebase/CoreOnly (12.6.0):
|
- Firebase/CoreOnly (12.8.0):
|
||||||
- FirebaseCore (~> 12.6.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- Firebase/Messaging (12.6.0):
|
- Firebase/Messaging (12.8.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 12.6.0)
|
- FirebaseMessaging (~> 12.8.0)
|
||||||
- firebase_core (4.3.0):
|
- firebase_core (4.4.0):
|
||||||
- Firebase/CoreOnly (= 12.6.0)
|
- Firebase/CoreOnly (= 12.8.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_messaging (16.1.0):
|
- firebase_messaging (16.1.1):
|
||||||
- Firebase/Messaging (= 12.6.0)
|
- Firebase/Messaging (= 12.8.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- FirebaseAnalytics (12.6.0):
|
- FirebaseAnalytics (12.8.0):
|
||||||
- FirebaseAnalytics/Default (= 12.6.0)
|
- FirebaseAnalytics/Default (= 12.8.0)
|
||||||
- FirebaseCore (~> 12.6.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- FirebaseInstallations (~> 12.6.0)
|
- FirebaseInstallations (~> 12.8.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/Network (~> 8.1)
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseAnalytics/Default (12.6.0):
|
- FirebaseAnalytics/Default (12.8.0):
|
||||||
- FirebaseCore (~> 12.6.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- FirebaseInstallations (~> 12.6.0)
|
- FirebaseInstallations (~> 12.8.0)
|
||||||
- GoogleAppMeasurement/Default (= 12.6.0)
|
- GoogleAppMeasurement/Default (= 12.8.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/Network (~> 8.1)
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseCore (12.6.0):
|
- FirebaseCore (12.8.0):
|
||||||
- FirebaseCoreInternal (~> 12.6.0)
|
- FirebaseCoreInternal (~> 12.8.0)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/Logger (~> 8.1)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
- FirebaseCoreInternal (12.6.0):
|
- FirebaseCoreInternal (12.8.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- FirebaseInstallations (12.6.0):
|
- FirebaseInstallations (12.8.0):
|
||||||
- FirebaseCore (~> 12.6.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
- PromisesObjC (~> 2.4)
|
- PromisesObjC (~> 2.4)
|
||||||
- FirebaseMessaging (12.6.0):
|
- FirebaseMessaging (12.8.0):
|
||||||
- FirebaseCore (~> 12.6.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- FirebaseInstallations (~> 12.6.0)
|
- FirebaseInstallations (~> 12.8.0)
|
||||||
- GoogleDataTransport (~> 10.1)
|
- GoogleDataTransport (~> 10.1)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
|
|
@ -129,39 +129,39 @@ PODS:
|
||||||
- gal (1.0.0):
|
- gal (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- google_mlkit_barcode_scanning (0.14.1):
|
- google_mlkit_barcode_scanning (0.14.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- google_mlkit_commons
|
- google_mlkit_commons
|
||||||
- GoogleMLKit/BarcodeScanning (~> 7.0.0)
|
- GoogleMLKit/BarcodeScanning (~> 9.0.0)
|
||||||
- google_mlkit_commons (0.11.0):
|
- google_mlkit_commons (0.11.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- MLKitVision
|
- MLKitVision (~> 10.0.0)
|
||||||
- google_mlkit_face_detection (0.13.1):
|
- google_mlkit_face_detection (0.13.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- google_mlkit_commons
|
- google_mlkit_commons
|
||||||
- GoogleMLKit/FaceDetection (~> 7.0.0)
|
- GoogleMLKit/FaceDetection (~> 9.0.0)
|
||||||
- GoogleAdsOnDeviceConversion (3.2.0):
|
- GoogleAdsOnDeviceConversion (3.2.0):
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/Logger (~> 8.1)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
- GoogleUtilities/Network (~> 8.1)
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- GoogleAppMeasurement/Core (12.6.0):
|
- GoogleAppMeasurement/Core (12.8.0):
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/Network (~> 8.1)
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- GoogleAppMeasurement/Default (12.6.0):
|
- GoogleAppMeasurement/Default (12.8.0):
|
||||||
- GoogleAdsOnDeviceConversion (~> 3.2.0)
|
- GoogleAdsOnDeviceConversion (~> 3.2.0)
|
||||||
- GoogleAppMeasurement/Core (= 12.6.0)
|
- GoogleAppMeasurement/Core (= 12.8.0)
|
||||||
- GoogleAppMeasurement/IdentitySupport (= 12.6.0)
|
- GoogleAppMeasurement/IdentitySupport (= 12.8.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/Network (~> 8.1)
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- GoogleAppMeasurement/IdentitySupport (12.6.0):
|
- GoogleAppMeasurement/IdentitySupport (12.8.0):
|
||||||
- GoogleAppMeasurement/Core (= 12.6.0)
|
- GoogleAppMeasurement/Core (= 12.8.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
- GoogleUtilities/MethodSwizzler (~> 8.1)
|
||||||
- GoogleUtilities/Network (~> 8.1)
|
- GoogleUtilities/Network (~> 8.1)
|
||||||
|
|
@ -170,14 +170,14 @@ PODS:
|
||||||
- GoogleDataTransport (10.1.0):
|
- GoogleDataTransport (10.1.0):
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- PromisesObjC (~> 2.4)
|
- PromisesObjC (~> 2.4)
|
||||||
- GoogleMLKit/BarcodeScanning (7.0.0):
|
- GoogleMLKit/BarcodeScanning (9.0.0):
|
||||||
- GoogleMLKit/MLKitCore
|
- GoogleMLKit/MLKitCore
|
||||||
- MLKitBarcodeScanning (~> 6.0.0)
|
- MLKitBarcodeScanning (~> 8.0.0)
|
||||||
- GoogleMLKit/FaceDetection (7.0.0):
|
- GoogleMLKit/FaceDetection (9.0.0):
|
||||||
- GoogleMLKit/MLKitCore
|
- GoogleMLKit/MLKitCore
|
||||||
- MLKitFaceDetection (~> 6.0.0)
|
- MLKitFaceDetection (~> 8.0.0)
|
||||||
- GoogleMLKit/MLKitCore (7.0.0):
|
- GoogleMLKit/MLKitCore (9.0.0):
|
||||||
- MLKitCommon (~> 12.0.0)
|
- MLKitCommon (~> 14.0.0)
|
||||||
- GoogleToolboxForMac/Defines (4.2.1)
|
- GoogleToolboxForMac/Defines (4.2.1)
|
||||||
- GoogleToolboxForMac/Logger (4.2.1):
|
- GoogleToolboxForMac/Logger (4.2.1):
|
||||||
- GoogleToolboxForMac/Defines (= 4.2.1)
|
- GoogleToolboxForMac/Defines (= 4.2.1)
|
||||||
|
|
@ -247,26 +247,26 @@ PODS:
|
||||||
- Mantle (2.2.0):
|
- Mantle (2.2.0):
|
||||||
- Mantle/extobjc (= 2.2.0)
|
- Mantle/extobjc (= 2.2.0)
|
||||||
- Mantle/extobjc (2.2.0)
|
- Mantle/extobjc (2.2.0)
|
||||||
- MLImage (1.0.0-beta6)
|
- MLImage (1.0.0-beta8)
|
||||||
- MLKitBarcodeScanning (6.0.0):
|
- MLKitBarcodeScanning (8.0.0):
|
||||||
- MLKitCommon (~> 12.0)
|
- MLKitCommon (~> 14.0)
|
||||||
- MLKitVision (~> 8.0)
|
- MLKitVision (~> 10.0)
|
||||||
- MLKitCommon (12.0.0):
|
- MLKitCommon (14.0.0):
|
||||||
- GoogleDataTransport (~> 10.0)
|
- GoogleDataTransport (~> 10.0)
|
||||||
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
|
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
|
||||||
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
|
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
|
||||||
- GoogleUtilities/Logger (~> 8.0)
|
- GoogleUtilities/Logger (~> 8.0)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
|
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
|
||||||
- MLKitFaceDetection (6.0.0):
|
- MLKitFaceDetection (8.0.0):
|
||||||
- MLKitCommon (~> 12.0)
|
- MLKitCommon (~> 14.0)
|
||||||
- MLKitVision (~> 8.0)
|
- MLKitVision (~> 10.0)
|
||||||
- MLKitVision (8.0.0):
|
- MLKitVision (10.0.0):
|
||||||
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
|
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
|
||||||
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
|
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
|
||||||
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
|
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
|
||||||
- MLImage (= 1.0.0-beta6)
|
- MLImage (= 1.0.0-beta8)
|
||||||
- MLKitCommon (~> 12.0)
|
- MLKitCommon (~> 14.0)
|
||||||
- nanopb (3.30910.0):
|
- nanopb (3.30910.0):
|
||||||
- nanopb/decode (= 3.30910.0)
|
- nanopb/decode (= 3.30910.0)
|
||||||
- nanopb/encode (= 3.30910.0)
|
- nanopb/encode (= 3.30910.0)
|
||||||
|
|
@ -274,28 +274,21 @@ PODS:
|
||||||
- nanopb/encode (3.30910.0)
|
- nanopb/encode (3.30910.0)
|
||||||
- no_screenshot (0.3.2-beta.3):
|
- no_screenshot (0.3.2-beta.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- ScreenProtectorKit (~> 1.3.1)
|
|
||||||
- objective_c (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- path_provider_foundation (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- permission_handler_apple (9.3.0):
|
- permission_handler_apple (9.3.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- PromisesObjC (2.4.0)
|
- PromisesObjC (2.4.0)
|
||||||
- restart_app (0.0.1):
|
- restart_app (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- ScreenProtectorKit (1.3.1)
|
- SDWebImage (5.21.6):
|
||||||
- SDWebImage (5.21.5):
|
- SDWebImage/Core (= 5.21.6)
|
||||||
- SDWebImage/Core (= 5.21.5)
|
- SDWebImage/Core (5.21.6)
|
||||||
- SDWebImage/Core (5.21.5)
|
|
||||||
- SDWebImageWebPCoder (0.15.0):
|
- SDWebImageWebPCoder (0.15.0):
|
||||||
- libwebp (~> 1.0)
|
- libwebp (~> 1.0)
|
||||||
- SDWebImage/Core (~> 5.17)
|
- SDWebImage/Core (~> 5.17)
|
||||||
- Sentry/HybridSDK (8.56.2)
|
- Sentry/HybridSDK (8.56.2)
|
||||||
- sentry_flutter (9.8.0):
|
- sentry_flutter (9.13.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- Sentry/HybridSDK (= 8.56.2)
|
- Sentry/HybridSDK (= 8.56.2)
|
||||||
|
|
@ -307,32 +300,32 @@ PODS:
|
||||||
- sqflite_darwin (0.0.4):
|
- sqflite_darwin (0.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqlite3 (3.50.4):
|
- sqlite3 (3.51.1):
|
||||||
- sqlite3/common (= 3.50.4)
|
- sqlite3/common (= 3.51.1)
|
||||||
- sqlite3/common (3.50.4)
|
- sqlite3/common (3.51.1)
|
||||||
- sqlite3/dbstatvtab (3.50.4):
|
- sqlite3/dbstatvtab (3.51.1):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/fts5 (3.50.4):
|
- sqlite3/fts5 (3.51.1):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/math (3.50.4):
|
- sqlite3/math (3.51.1):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/perf-threadsafe (3.50.4):
|
- sqlite3/perf-threadsafe (3.51.1):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/rtree (3.50.4):
|
- sqlite3/rtree (3.51.1):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3/session (3.50.4):
|
- sqlite3/session (3.51.1):
|
||||||
- sqlite3/common
|
- sqlite3/common
|
||||||
- sqlite3_flutter_libs (0.0.1):
|
- sqlite3_flutter_libs (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqlite3 (~> 3.50.4)
|
- sqlite3 (~> 3.51.1)
|
||||||
- sqlite3/dbstatvtab
|
- sqlite3/dbstatvtab
|
||||||
- sqlite3/fts5
|
- sqlite3/fts5
|
||||||
- sqlite3/math
|
- sqlite3/math
|
||||||
- sqlite3/perf-threadsafe
|
- sqlite3/perf-threadsafe
|
||||||
- sqlite3/rtree
|
- sqlite3/rtree
|
||||||
- sqlite3/session
|
- sqlite3/session
|
||||||
- SwiftProtobuf (1.33.3)
|
- SwiftProtobuf (1.34.1)
|
||||||
- SwiftyGif (5.4.5)
|
- SwiftyGif (5.4.5)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
@ -373,9 +366,7 @@ DEPENDENCIES:
|
||||||
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
|
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
|
||||||
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
||||||
- no_screenshot (from `.symlinks/plugins/no_screenshot/ios`)
|
- no_screenshot (from `.symlinks/plugins/no_screenshot/ios`)
|
||||||
- objective_c (from `.symlinks/plugins/objective_c/ios`)
|
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- restart_app (from `.symlinks/plugins/restart_app/ios`)
|
- restart_app (from `.symlinks/plugins/restart_app/ios`)
|
||||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||||
|
|
@ -413,7 +404,6 @@ SPEC REPOS:
|
||||||
- MLKitVision
|
- MLKitVision
|
||||||
- nanopb
|
- nanopb
|
||||||
- PromisesObjC
|
- PromisesObjC
|
||||||
- ScreenProtectorKit
|
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SDWebImageWebPCoder
|
- SDWebImageWebPCoder
|
||||||
- Sentry
|
- Sentry
|
||||||
|
|
@ -476,12 +466,8 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
||||||
no_screenshot:
|
no_screenshot:
|
||||||
:path: ".symlinks/plugins/no_screenshot/ios"
|
:path: ".symlinks/plugins/no_screenshot/ios"
|
||||||
objective_c:
|
|
||||||
:path: ".symlinks/plugins/objective_c/ios"
|
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
restart_app:
|
restart_app:
|
||||||
|
|
@ -514,14 +500,14 @@ SPEC CHECKSUMS:
|
||||||
emoji_picker_flutter: ece213fc274bdddefb77d502d33080dc54e616cc
|
emoji_picker_flutter: ece213fc274bdddefb77d502d33080dc54e616cc
|
||||||
ffmpeg_kit_flutter_new: 12426a19f10ac81186c67c6ebc4717f8f4364b7f
|
ffmpeg_kit_flutter_new: 12426a19f10ac81186c67c6ebc4717f8f4364b7f
|
||||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||||
Firebase: a451a7b61536298fd5cbfe3a746fd40443a50679
|
Firebase: 9a58fdbc9d8655ed7b79a19cf9690bb007d3d46d
|
||||||
firebase_core: ba00a168e719694f38960502ceb560285603d073
|
firebase_core: ee30637e6744af8e0c12a6a1e8a9718506ec2398
|
||||||
firebase_messaging: bf0e29321927edc02a563c984dbfa5b063864b15
|
firebase_messaging: 343de01a8d3e18b60df0c6d37f7174c44ae38e02
|
||||||
FirebaseAnalytics: d0a97a0db6425e5a5d966340b87f92ca7b13a557
|
FirebaseAnalytics: f20bbad8cb7f65d8a5eaefeb424ae8800a31bdfc
|
||||||
FirebaseCore: 0e38ad5d62d980a47a64b8e9301ffa311457be04
|
FirebaseCore: 0dbad74bda10b8fb9ca34ad8f375fb9dd3ebef7c
|
||||||
FirebaseCoreInternal: 69bf1306a05b8ac43004f6cc1f804bb7b05b229e
|
FirebaseCoreInternal: fe5fa466aeb314787093a7dce9f0beeaad5a2a21
|
||||||
FirebaseInstallations: 631b38da2e11a83daa4bfb482f79d286a5dfa7ad
|
FirebaseInstallations: 6a14ab3d694ebd9f839c48d330da5547e9ca9dc0
|
||||||
FirebaseMessaging: a61bc42dcab3f7a346d94bbb54dab2c9435b18b2
|
FirebaseMessaging: 7f42cfd10ec64181db4e01b305a613791c8e782c
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
|
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
|
||||||
flutter_keyboard_visibility_temp_fork: 95b2d534bacf6ac62e7fcbe5c2a9e2c2a17ce06f
|
flutter_keyboard_visibility_temp_fork: 95b2d534bacf6ac62e7fcbe5c2a9e2c2a17ce06f
|
||||||
|
|
@ -530,13 +516,13 @@ SPEC CHECKSUMS:
|
||||||
flutter_sharing_intent: 0c1e53949f09fa8df8ac2268505687bde8ff264c
|
flutter_sharing_intent: 0c1e53949f09fa8df8ac2268505687bde8ff264c
|
||||||
flutter_volume_controller: c2be490cb0487e8b88d0d9fc2b7e1c139a4ebccb
|
flutter_volume_controller: c2be490cb0487e8b88d0d9fc2b7e1c139a4ebccb
|
||||||
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
||||||
google_mlkit_barcode_scanning: 8f5987f244a43fe1167689c548342a5174108159
|
google_mlkit_barcode_scanning: 12d8422d8f7b00726dedf9cac00188a2b98750c2
|
||||||
google_mlkit_commons: 2abe6a70e1824e431d16a51085cb475b672c8aab
|
google_mlkit_commons: a5e4ffae5bc59ea4c7b9025dc72cb6cb79dc1166
|
||||||
google_mlkit_face_detection: 754da2113a1952f063c7c5dc347ac6ae8934fb77
|
google_mlkit_face_detection: ee4b72cfae062b4c972204be955d83055a4bfd36
|
||||||
GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f
|
GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f
|
||||||
GoogleAppMeasurement: 3bf40aff49a601af5da1c3345702fcb4991d35ee
|
GoogleAppMeasurement: 72c9a682fec6290327ea5e3c4b829b247fcb2c17
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318
|
GoogleMLKit: b1eee21a41c57704fe72483b15c85cb2c0cd7444
|
||||||
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
|
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
||||||
|
|
@ -545,30 +531,27 @@ SPEC CHECKSUMS:
|
||||||
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
||||||
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
|
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
|
||||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||||
MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56
|
MLImage: 0de5c6c2bf9e93b80ef752e2797f0836f03b58c0
|
||||||
MLKitBarcodeScanning: 0a3064da0a7f49ac24ceb3cb46a5bc67496facd2
|
MLKitBarcodeScanning: 39de223e7b1b8a8fbf10816a536dd292d8a39343
|
||||||
MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d
|
MLKitCommon: 47d47b50a031d00db62f1b0efe5a1d8b09a3b2e6
|
||||||
MLKitFaceDetection: 2a593db4837db503ad3426b565e7aab045cefea5
|
MLKitFaceDetection: 32549f1e70e6e7731261bf9cea2b74095e2531cb
|
||||||
MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e
|
MLKitVision: 39a5a812db83c4a0794445088e567f3631c11961
|
||||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
no_screenshot: 89e778ede9f1e39cc3fb9404d782a42712f2a0b2
|
no_screenshot: 5e345998c43ffcad5d6834f249590483fcc037bd
|
||||||
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
|
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
|
||||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
restart_app: 9cda5378aacc5000e3f66ee76a9201534e7d3ecf
|
restart_app: 9cda5378aacc5000e3f66ee76a9201534e7d3ecf
|
||||||
ScreenProtectorKit: 83a6281b02c7a5902ee6eac4f5045f674e902ae4
|
SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477
|
||||||
SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838
|
|
||||||
SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377
|
SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377
|
||||||
Sentry: b53951377b78e21a734f5dc8318e333dbfc682d7
|
Sentry: b53951377b78e21a734f5dc8318e333dbfc682d7
|
||||||
sentry_flutter: 4c33648b7e83310aa1fdb1b10c5491027d9643f0
|
sentry_flutter: dbed9a62ae39716b685a80140705c330d200d941
|
||||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||||
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
|
sqlite3: 8d708bc63e9f4ce48f0ad9d6269e478c5ced1d9b
|
||||||
sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
|
sqlite3_flutter_libs: d13b8b3003f18f596e542bcb9482d105577eff41
|
||||||
SwiftProtobuf: e1b437c8e31a4c5577b643249a0bb62ed4f02153
|
SwiftProtobuf: c901f00a3e125dc33cac9b16824da85682ee47da
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||||
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
||||||
|
|
|
||||||
55
lib/app.dart
55
lib/app.dart
|
|
@ -7,8 +7,11 @@ import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
||||||
import 'package:twonly/src/providers/connection.provider.dart';
|
import 'package:twonly/src/providers/connection.provider.dart';
|
||||||
import 'package:twonly/src/providers/purchases.provider.dart';
|
import 'package:twonly/src/providers/purchases.provider.dart';
|
||||||
|
import 'package:twonly/src/providers/routing.provider.dart';
|
||||||
import 'package:twonly/src/providers/settings.provider.dart';
|
import 'package:twonly/src/providers/settings.provider.dart';
|
||||||
import 'package:twonly/src/services/subscription.service.dart';
|
import 'package:twonly/src/services/subscription.service.dart';
|
||||||
|
import 'package:twonly/src/themes/dark.dart';
|
||||||
|
import 'package:twonly/src/themes/light.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/pow.dart';
|
import 'package:twonly/src/utils/pow.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
|
|
@ -16,7 +19,7 @@ import 'package:twonly/src/views/components/app_outdated.dart';
|
||||||
import 'package:twonly/src/views/home.view.dart';
|
import 'package:twonly/src/views/home.view.dart';
|
||||||
import 'package:twonly/src/views/onboarding/onboarding.view.dart';
|
import 'package:twonly/src/views/onboarding/onboarding.view.dart';
|
||||||
import 'package:twonly/src/views/onboarding/register.view.dart';
|
import 'package:twonly/src/views/onboarding/register.view.dart';
|
||||||
import 'package:twonly/src/views/settings/backup/twonly_safe_backup.view.dart';
|
import 'package:twonly/src/views/settings/backup/setup_backup.view.dart';
|
||||||
import 'package:twonly/src/views/updates/62_database_migration.view.dart';
|
import 'package:twonly/src/views/updates/62_database_migration.view.dart';
|
||||||
|
|
||||||
class App extends StatefulWidget {
|
class App extends StatefulWidget {
|
||||||
|
|
@ -34,14 +37,14 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
globalIsAppInBackground = false;
|
globalIsAppInBackground = false;
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
globalCallbackConnectionState = ({required bool isConnected}) async {
|
globalCallbackConnectionState = ({required isConnected}) async {
|
||||||
await context
|
await context
|
||||||
.read<CustomChangeProvider>()
|
.read<CustomChangeProvider>()
|
||||||
.updateConnectionState(isConnected);
|
.updateConnectionState(isConnected);
|
||||||
await setUserPlan();
|
await setUserPlan();
|
||||||
};
|
};
|
||||||
|
|
||||||
globalCallbackUpdatePlan = (SubscriptionPlan plan) {
|
globalCallbackUpdatePlan = (plan) {
|
||||||
context.read<PurchasesProvider>().updatePlan(plan);
|
context.read<PurchasesProvider>().updatePlan(plan);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -83,8 +86,8 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
globalCallbackConnectionState = ({required bool isConnected}) {};
|
globalCallbackConnectionState = ({required isConnected}) {};
|
||||||
globalCallbackUpdatePlan = (SubscriptionPlan planId) {};
|
globalCallbackUpdatePlan = (planId) {};
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,51 +95,25 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: context.watch<SettingsChangeProvider>(),
|
listenable: context.watch<SettingsChangeProvider>(),
|
||||||
builder: (BuildContext context, Widget? child) {
|
builder: (context, child) {
|
||||||
return MaterialApp(
|
return MaterialApp.router(
|
||||||
|
routerConfig: routerProvider,
|
||||||
scaffoldMessengerKey: globalRootScaffoldMessengerKey,
|
scaffoldMessengerKey: globalRootScaffoldMessengerKey,
|
||||||
restorationScopeId: 'app',
|
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
AppLocalizations.delegate,
|
AppLocalizations.delegate,
|
||||||
GlobalMaterialLocalizations.delegate,
|
GlobalMaterialLocalizations.delegate,
|
||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
GlobalCupertinoLocalizations.delegate,
|
GlobalCupertinoLocalizations.delegate,
|
||||||
],
|
],
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
supportedLocales: const [
|
supportedLocales: const [
|
||||||
Locale('en', ''),
|
Locale('en', ''),
|
||||||
Locale('de', ''),
|
Locale('de', ''),
|
||||||
],
|
],
|
||||||
onGenerateTitle: (BuildContext context) => 'twonly',
|
title: 'twonly',
|
||||||
theme: ThemeData(
|
theme: lightTheme,
|
||||||
colorScheme: ColorScheme.fromSeed(
|
darkTheme: darkTheme,
|
||||||
seedColor: const Color(0xFF57CC99),
|
|
||||||
),
|
|
||||||
pageTransitionsTheme: const PageTransitionsTheme(
|
|
||||||
builders: {
|
|
||||||
TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
darkTheme: ThemeData.dark().copyWith(
|
|
||||||
colorScheme: ColorScheme.fromSeed(
|
|
||||||
brightness: Brightness.dark,
|
|
||||||
seedColor: const Color(0xFF57CC99),
|
|
||||||
surface: const Color.fromARGB(255, 20, 18, 23),
|
|
||||||
surfaceContainer: const Color.fromARGB(255, 33, 30, 39),
|
|
||||||
),
|
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
themeMode: context.watch<SettingsChangeProvider>().themeMode,
|
themeMode: context.watch<SettingsChangeProvider>().themeMode,
|
||||||
initialRoute: '/',
|
|
||||||
routes: {
|
|
||||||
'/': (context) => const AppMainWidget(initialPage: 1),
|
|
||||||
'/chats': (context) => const AppMainWidget(initialPage: 0),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -213,7 +190,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
child = const DatabaseMigrationView();
|
child = const DatabaseMigrationView();
|
||||||
} else if (_isUserCreated) {
|
} else if (_isUserCreated) {
|
||||||
if (gUser.twonlySafeBackup == null && !_skipBackup && kReleaseMode) {
|
if (gUser.twonlySafeBackup == null && !_skipBackup && kReleaseMode) {
|
||||||
child = TwonlyIdentityBackupView(
|
child = SetupBackupView(
|
||||||
callBack: () {
|
callBack: () {
|
||||||
_skipBackup = true;
|
_skipBackup = true;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,11 @@ late UserData gUser;
|
||||||
|
|
||||||
// This callback called by the apiProvider
|
// This callback called by the apiProvider
|
||||||
void Function({required bool isConnected}) globalCallbackConnectionState = ({
|
void Function({required bool isConnected}) globalCallbackConnectionState = ({
|
||||||
required bool isConnected,
|
required isConnected,
|
||||||
}) {};
|
}) {};
|
||||||
void Function() globalCallbackAppIsOutdated = () {};
|
void Function() globalCallbackAppIsOutdated = () {};
|
||||||
void Function() globalCallbackNewDeviceRegistered = () {};
|
void Function() globalCallbackNewDeviceRegistered = () {};
|
||||||
void Function(SubscriptionPlan plan) globalCallbackUpdatePlan =
|
void Function(SubscriptionPlan plan) globalCallbackUpdatePlan = (plan) {};
|
||||||
(SubscriptionPlan plan) {};
|
|
||||||
|
|
||||||
Map<String, VoidCallback> globalUserDataChangedCallBack = {};
|
Map<String, VoidCallback> globalUserDataChangedCallBack = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import 'package:twonly/src/providers/image_editor.provider.dart';
|
||||||
import 'package:twonly/src/providers/purchases.provider.dart';
|
import 'package:twonly/src/providers/purchases.provider.dart';
|
||||||
import 'package:twonly/src/providers/settings.provider.dart';
|
import 'package:twonly/src/providers/settings.provider.dart';
|
||||||
import 'package:twonly/src/services/api.service.dart';
|
import 'package:twonly/src/services/api.service.dart';
|
||||||
|
import 'package:twonly/src/services/api/mediafiles/download.service.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/media_background.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/media_background.service.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
import 'package:twonly/src/services/fcm.service.dart';
|
import 'package:twonly/src/services/fcm.service.dart';
|
||||||
|
|
@ -73,6 +74,14 @@ void main() async {
|
||||||
return u;
|
return u;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (gUser.appVersion < 91) {
|
||||||
|
// BUG: Requested media files for reupload where not reuploaded because the wrong state...
|
||||||
|
await makeMigrationToVersion91();
|
||||||
|
await updateUserdata((u) {
|
||||||
|
u.appVersion = 91;
|
||||||
|
return u;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await twonlyDB.messagesDao.purgeMessageTable();
|
await twonlyDB.messagesDao.purgeMessageTable();
|
||||||
|
|
|
||||||
55
lib/src/constants/routes.keys.dart
Normal file
55
lib/src/constants/routes.keys.dart
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
class Routes {
|
||||||
|
static const String home = '/';
|
||||||
|
static const String chats = '/chats';
|
||||||
|
static const String chatsAddNewUser = '/chats/add_new_user';
|
||||||
|
static const String chatsArchived = '/chats/archived';
|
||||||
|
static const String chatsStartNewChat = '/chats/start_new_chat';
|
||||||
|
static const String chatsCameraSendTo = '/chats/camera_send_to';
|
||||||
|
static const String chatsMediaViewer = '/chats/media_viewer';
|
||||||
|
static const String chatsMessages = '/chats/messages';
|
||||||
|
|
||||||
|
static String groupCreateSelectMember(String? groupId) =>
|
||||||
|
'/group/create/select_member${groupId == null ? '' : '/$groupId'}';
|
||||||
|
|
||||||
|
static String profileGroup(String groupId) => '/profile/group/$groupId';
|
||||||
|
static String profileContact(int contactId) => '/profile/contact/$contactId';
|
||||||
|
|
||||||
|
static const String cameraQRScanner = '/camera/qr_scanner';
|
||||||
|
|
||||||
|
static const String settings = '/settings';
|
||||||
|
static const String settingsProfile = '/settings/profile';
|
||||||
|
static const String settingsPublicProfile = '/settings/public_profile';
|
||||||
|
static const String settingsProfileModifyAvatar =
|
||||||
|
'/settings/profile/modify_avatar';
|
||||||
|
static const String settingsAccount = '/settings/account';
|
||||||
|
static const String settingsSubscription = '/settings/subscription';
|
||||||
|
static const String settingsBackup = '/settings/backup';
|
||||||
|
static const String settingsBackupServer = '/settings/backup/server';
|
||||||
|
static const String settingsBackupRecovery = '/settings/backup/recovery';
|
||||||
|
static const String settingsBackupSetup = '/settings/backup/setup';
|
||||||
|
static const String settingsAppearance = '/settings/appearance';
|
||||||
|
static const String settingsChats = '/settings/chats';
|
||||||
|
static const String settingsChatsReactions = '/settings/chats/reactions';
|
||||||
|
static const String settingsPrivacy = '/settings/privacy';
|
||||||
|
static const String settingsPrivacyBlockUsers =
|
||||||
|
'/settings/privacy/block_users';
|
||||||
|
static const String settingsNotification = '/settings/notification';
|
||||||
|
static const String settingsStorage = '/settings/storage_data';
|
||||||
|
static const String settingsStorageImport = '/settings/storage_data/import';
|
||||||
|
static const String settingsStorageExport = '/settings/storage_data/export';
|
||||||
|
static const String settingsHelp = '/settings/help';
|
||||||
|
static const String settingsHelpFaq = '/settings/help/faq';
|
||||||
|
static const String settingsHelpContactUs = '/settings/help/contact_us';
|
||||||
|
static const String settingsHelpDiagnostics = '/settings/help/diagnostics';
|
||||||
|
static const String settingsHelpUserStudy = '/settings/help/user_study';
|
||||||
|
static const String settingsHelpUserStudyQuestionnaire =
|
||||||
|
'/settings/help/user_study/questionnaire';
|
||||||
|
static const String settingsHelpCredits = '/settings/help/credits';
|
||||||
|
static const String settingsHelpChangelog = '/settings/help/changelog';
|
||||||
|
static const String settingsDeveloper = '/settings/developer';
|
||||||
|
static const String settingsDeveloperRetransmissionDatabase =
|
||||||
|
'/settings/developer/retransmission_database';
|
||||||
|
static const String settingsDeveloperAutomatedTesting =
|
||||||
|
'/settings/developer/automated_testing';
|
||||||
|
static const String settingsInvite = '/settings/invite';
|
||||||
|
}
|
||||||
|
|
@ -5,4 +5,12 @@ part of 'contacts.dao.dart';
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
mixin _$ContactsDaoMixin on DatabaseAccessor<TwonlyDB> {
|
mixin _$ContactsDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
$ContactsTable get contacts => attachedDatabase.contacts;
|
$ContactsTable get contacts => attachedDatabase.contacts;
|
||||||
|
ContactsDaoManager get managers => ContactsDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ContactsDaoManager {
|
||||||
|
final _$ContactsDaoMixin _db;
|
||||||
|
ContactsDaoManager(this._db);
|
||||||
|
$$ContactsTableTableManager get contacts =>
|
||||||
|
$$ContactsTableTableManager(_db.attachedDatabase, _db.contacts);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,19 @@ mixin _$GroupsDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
$ContactsTable get contacts => attachedDatabase.contacts;
|
$ContactsTable get contacts => attachedDatabase.contacts;
|
||||||
$GroupMembersTable get groupMembers => attachedDatabase.groupMembers;
|
$GroupMembersTable get groupMembers => attachedDatabase.groupMembers;
|
||||||
$GroupHistoriesTable get groupHistories => attachedDatabase.groupHistories;
|
$GroupHistoriesTable get groupHistories => attachedDatabase.groupHistories;
|
||||||
|
GroupsDaoManager get managers => GroupsDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class GroupsDaoManager {
|
||||||
|
final _$GroupsDaoMixin _db;
|
||||||
|
GroupsDaoManager(this._db);
|
||||||
|
$$GroupsTableTableManager get groups =>
|
||||||
|
$$GroupsTableTableManager(_db.attachedDatabase, _db.groups);
|
||||||
|
$$ContactsTableTableManager get contacts =>
|
||||||
|
$$ContactsTableTableManager(_db.attachedDatabase, _db.contacts);
|
||||||
|
$$GroupMembersTableTableManager get groupMembers =>
|
||||||
|
$$GroupMembersTableTableManager(_db.attachedDatabase, _db.groupMembers);
|
||||||
|
$$GroupHistoriesTableTableManager get groupHistories =>
|
||||||
|
$$GroupHistoriesTableTableManager(
|
||||||
|
_db.attachedDatabase, _db.groupHistories);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,14 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<MediaFile>> getAllMediaFilesReuploadRequested() async {
|
||||||
|
return (select(mediaFiles)
|
||||||
|
..where(
|
||||||
|
(t) => t.downloadState.equals(DownloadState.reuploadRequested.name),
|
||||||
|
))
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<MediaFile>> getAllNonHashedStoredMediaFiles() async {
|
Future<List<MediaFile>> getAllNonHashedStoredMediaFiles() async {
|
||||||
return (select(mediaFiles)
|
return (select(mediaFiles)
|
||||||
..where(
|
..where(
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,12 @@ part of 'mediafiles.dao.dart';
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
mixin _$MediaFilesDaoMixin on DatabaseAccessor<TwonlyDB> {
|
mixin _$MediaFilesDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
|
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
|
||||||
|
MediaFilesDaoManager get managers => MediaFilesDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MediaFilesDaoManager {
|
||||||
|
final _$MediaFilesDaoMixin _db;
|
||||||
|
MediaFilesDaoManager(this._db);
|
||||||
|
$$MediaFilesTableTableManager get mediaFiles =>
|
||||||
|
$$MediaFilesTableTableManager(_db.attachedDatabase, _db.mediaFiles);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,12 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
(t) =>
|
(t) =>
|
||||||
t.groupId.equals(groupId) &
|
t.groupId.equals(groupId) &
|
||||||
(t.isDeletedFromSender.equals(true) |
|
(t.isDeletedFromSender.equals(true) |
|
||||||
((t.type.equals(MessageType.text.name) &
|
(t.type.equals(MessageType.text.name).not() |
|
||||||
|
t.type.equals(MessageType.media.name).not()) |
|
||||||
|
(t.type.equals(MessageType.text.name) &
|
||||||
t.content.isNotNull()) |
|
t.content.isNotNull()) |
|
||||||
(t.type.equals(MessageType.media.name) &
|
(t.type.equals(MessageType.media.name) &
|
||||||
t.mediaId.isNotNull()))),
|
t.mediaId.isNotNull())),
|
||||||
))
|
))
|
||||||
..orderBy([(t) => OrderingTerm.asc(t.createdAt)]))
|
..orderBy([(t) => OrderingTerm.asc(t.createdAt)]))
|
||||||
.watch();
|
.watch();
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,28 @@ mixin _$MessagesDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
attachedDatabase.messageHistories;
|
attachedDatabase.messageHistories;
|
||||||
$GroupMembersTable get groupMembers => attachedDatabase.groupMembers;
|
$GroupMembersTable get groupMembers => attachedDatabase.groupMembers;
|
||||||
$MessageActionsTable get messageActions => attachedDatabase.messageActions;
|
$MessageActionsTable get messageActions => attachedDatabase.messageActions;
|
||||||
|
MessagesDaoManager get managers => MessagesDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessagesDaoManager {
|
||||||
|
final _$MessagesDaoMixin _db;
|
||||||
|
MessagesDaoManager(this._db);
|
||||||
|
$$GroupsTableTableManager get groups =>
|
||||||
|
$$GroupsTableTableManager(_db.attachedDatabase, _db.groups);
|
||||||
|
$$ContactsTableTableManager get contacts =>
|
||||||
|
$$ContactsTableTableManager(_db.attachedDatabase, _db.contacts);
|
||||||
|
$$MediaFilesTableTableManager get mediaFiles =>
|
||||||
|
$$MediaFilesTableTableManager(_db.attachedDatabase, _db.mediaFiles);
|
||||||
|
$$MessagesTableTableManager get messages =>
|
||||||
|
$$MessagesTableTableManager(_db.attachedDatabase, _db.messages);
|
||||||
|
$$ReactionsTableTableManager get reactions =>
|
||||||
|
$$ReactionsTableTableManager(_db.attachedDatabase, _db.reactions);
|
||||||
|
$$MessageHistoriesTableTableManager get messageHistories =>
|
||||||
|
$$MessageHistoriesTableTableManager(
|
||||||
|
_db.attachedDatabase, _db.messageHistories);
|
||||||
|
$$GroupMembersTableTableManager get groupMembers =>
|
||||||
|
$$GroupMembersTableTableManager(_db.attachedDatabase, _db.groupMembers);
|
||||||
|
$$MessageActionsTableTableManager get messageActions =>
|
||||||
|
$$MessageActionsTableTableManager(
|
||||||
|
_db.attachedDatabase, _db.messageActions);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,20 @@ mixin _$ReactionsDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
|
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
|
||||||
$MessagesTable get messages => attachedDatabase.messages;
|
$MessagesTable get messages => attachedDatabase.messages;
|
||||||
$ReactionsTable get reactions => attachedDatabase.reactions;
|
$ReactionsTable get reactions => attachedDatabase.reactions;
|
||||||
|
ReactionsDaoManager get managers => ReactionsDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReactionsDaoManager {
|
||||||
|
final _$ReactionsDaoMixin _db;
|
||||||
|
ReactionsDaoManager(this._db);
|
||||||
|
$$GroupsTableTableManager get groups =>
|
||||||
|
$$GroupsTableTableManager(_db.attachedDatabase, _db.groups);
|
||||||
|
$$ContactsTableTableManager get contacts =>
|
||||||
|
$$ContactsTableTableManager(_db.attachedDatabase, _db.contacts);
|
||||||
|
$$MediaFilesTableTableManager get mediaFiles =>
|
||||||
|
$$MediaFilesTableTableManager(_db.attachedDatabase, _db.mediaFiles);
|
||||||
|
$$MessagesTableTableManager get messages =>
|
||||||
|
$$MessagesTableTableManager(_db.attachedDatabase, _db.messages);
|
||||||
|
$$ReactionsTableTableManager get reactions =>
|
||||||
|
$$ReactionsTableTableManager(_db.attachedDatabase, _db.reactions);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,26 @@ mixin _$ReceiptsDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
$MessageActionsTable get messageActions => attachedDatabase.messageActions;
|
$MessageActionsTable get messageActions => attachedDatabase.messageActions;
|
||||||
$ReceivedReceiptsTable get receivedReceipts =>
|
$ReceivedReceiptsTable get receivedReceipts =>
|
||||||
attachedDatabase.receivedReceipts;
|
attachedDatabase.receivedReceipts;
|
||||||
|
ReceiptsDaoManager get managers => ReceiptsDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReceiptsDaoManager {
|
||||||
|
final _$ReceiptsDaoMixin _db;
|
||||||
|
ReceiptsDaoManager(this._db);
|
||||||
|
$$ContactsTableTableManager get contacts =>
|
||||||
|
$$ContactsTableTableManager(_db.attachedDatabase, _db.contacts);
|
||||||
|
$$GroupsTableTableManager get groups =>
|
||||||
|
$$GroupsTableTableManager(_db.attachedDatabase, _db.groups);
|
||||||
|
$$MediaFilesTableTableManager get mediaFiles =>
|
||||||
|
$$MediaFilesTableTableManager(_db.attachedDatabase, _db.mediaFiles);
|
||||||
|
$$MessagesTableTableManager get messages =>
|
||||||
|
$$MessagesTableTableManager(_db.attachedDatabase, _db.messages);
|
||||||
|
$$ReceiptsTableTableManager get receipts =>
|
||||||
|
$$ReceiptsTableTableManager(_db.attachedDatabase, _db.receipts);
|
||||||
|
$$MessageActionsTableTableManager get messageActions =>
|
||||||
|
$$MessageActionsTableTableManager(
|
||||||
|
_db.attachedDatabase, _db.messageActions);
|
||||||
|
$$ReceivedReceiptsTableTableManager get receivedReceipts =>
|
||||||
|
$$ReceivedReceiptsTableTableManager(
|
||||||
|
_db.attachedDatabase, _db.receivedReceipts);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,17 +93,15 @@ class SignalDao extends DatabaseAccessor<TwonlyDB> with _$SignalDaoMixin {
|
||||||
await into(signalContactSignedPreKeys).insert(signedPreKey);
|
await into(signalContactSignedPreKeys).insert(signedPreKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> purgeOutDatedPreKeys() async {
|
Future<void> purgePreKeysFromContact(int contactId) async {
|
||||||
// Deletion is a workaround for the issue, that own pre keys where deleted after 40 days, while they could be 30days
|
|
||||||
// on the server + 25 days on the others device old, resulting in the issue that the receiver could not decrypt the
|
|
||||||
// messages...
|
|
||||||
await (delete(signalContactPreKeys)
|
await (delete(signalContactPreKeys)
|
||||||
..where(
|
..where(
|
||||||
(t) => (t.createdAt.isSmallerThanValue(
|
(t) => (t.contactId.equals(contactId)),
|
||||||
DateTime(2025, 10, 10),
|
|
||||||
)),
|
|
||||||
))
|
))
|
||||||
.go();
|
.go();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> purgeOutDatedPreKeys() async {
|
||||||
// other pre keys are valid 100 days
|
// other pre keys are valid 100 days
|
||||||
await (delete(signalContactPreKeys)
|
await (delete(signalContactPreKeys)
|
||||||
..where(
|
..where(
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,19 @@ mixin _$SignalDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
attachedDatabase.signalContactPreKeys;
|
attachedDatabase.signalContactPreKeys;
|
||||||
$SignalContactSignedPreKeysTable get signalContactSignedPreKeys =>
|
$SignalContactSignedPreKeysTable get signalContactSignedPreKeys =>
|
||||||
attachedDatabase.signalContactSignedPreKeys;
|
attachedDatabase.signalContactSignedPreKeys;
|
||||||
|
SignalDaoManager get managers => SignalDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignalDaoManager {
|
||||||
|
final _$SignalDaoMixin _db;
|
||||||
|
SignalDaoManager(this._db);
|
||||||
|
$$ContactsTableTableManager get contacts =>
|
||||||
|
$$ContactsTableTableManager(_db.attachedDatabase, _db.contacts);
|
||||||
|
$$SignalContactPreKeysTableTableManager get signalContactPreKeys =>
|
||||||
|
$$SignalContactPreKeysTableTableManager(
|
||||||
|
_db.attachedDatabase, _db.signalContactPreKeys);
|
||||||
|
$$SignalContactSignedPreKeysTableTableManager
|
||||||
|
get signalContactSignedPreKeys =>
|
||||||
|
$$SignalContactSignedPreKeysTableTableManager(
|
||||||
|
_db.attachedDatabase, _db.signalContactSignedPreKeys);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/tables/groups.table.dart';
|
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
|
|
||||||
enum MessageType { media, text }
|
enum MessageType { media, text, contacts }
|
||||||
|
|
||||||
@DataClassName('Message')
|
@DataClassName('Message')
|
||||||
class Messages extends Table {
|
class Messages extends Table {
|
||||||
|
|
@ -15,7 +15,7 @@ class Messages extends Table {
|
||||||
IntColumn get senderId =>
|
IntColumn get senderId =>
|
||||||
integer().nullable().references(Contacts, #userId)();
|
integer().nullable().references(Contacts, #userId)();
|
||||||
|
|
||||||
TextColumn get type => textEnum<MessageType>()();
|
TextColumn get type => text()();
|
||||||
|
|
||||||
TextColumn get content => text().nullable()();
|
TextColumn get content => text().nullable()();
|
||||||
TextColumn get mediaId => text()
|
TextColumn get mediaId => text()
|
||||||
|
|
|
||||||
|
|
@ -2776,11 +2776,11 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints:
|
defaultConstraints:
|
||||||
GeneratedColumn.constraintIsAlways('REFERENCES contacts (user_id)'));
|
GeneratedColumn.constraintIsAlways('REFERENCES contacts (user_id)'));
|
||||||
|
static const VerificationMeta _typeMeta = const VerificationMeta('type');
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumnWithTypeConverter<MessageType, String> type =
|
late final GeneratedColumn<String> type = GeneratedColumn<String>(
|
||||||
GeneratedColumn<String>('type', aliasedName, false,
|
'type', aliasedName, false,
|
||||||
type: DriftSqlType.string, requiredDuringInsert: true)
|
type: DriftSqlType.string, requiredDuringInsert: true);
|
||||||
.withConverter<MessageType>($MessagesTable.$convertertype);
|
|
||||||
static const VerificationMeta _contentMeta =
|
static const VerificationMeta _contentMeta =
|
||||||
const VerificationMeta('content');
|
const VerificationMeta('content');
|
||||||
@override
|
@override
|
||||||
|
|
@ -2929,6 +2929,12 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
context.handle(_senderIdMeta,
|
context.handle(_senderIdMeta,
|
||||||
senderId.isAcceptableOrUnknown(data['sender_id']!, _senderIdMeta));
|
senderId.isAcceptableOrUnknown(data['sender_id']!, _senderIdMeta));
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('type')) {
|
||||||
|
context.handle(
|
||||||
|
_typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_typeMeta);
|
||||||
|
}
|
||||||
if (data.containsKey('content')) {
|
if (data.containsKey('content')) {
|
||||||
context.handle(_contentMeta,
|
context.handle(_contentMeta,
|
||||||
content.isAcceptableOrUnknown(data['content']!, _contentMeta));
|
content.isAcceptableOrUnknown(data['content']!, _contentMeta));
|
||||||
|
|
@ -3020,8 +3026,8 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
.read(DriftSqlType.string, data['${effectivePrefix}message_id'])!,
|
.read(DriftSqlType.string, data['${effectivePrefix}message_id'])!,
|
||||||
senderId: attachedDatabase.typeMapping
|
senderId: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.int, data['${effectivePrefix}sender_id']),
|
.read(DriftSqlType.int, data['${effectivePrefix}sender_id']),
|
||||||
type: $MessagesTable.$convertertype.fromSql(attachedDatabase.typeMapping
|
type: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.string, data['${effectivePrefix}type'])!),
|
.read(DriftSqlType.string, data['${effectivePrefix}type'])!,
|
||||||
content: attachedDatabase.typeMapping
|
content: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.string, data['${effectivePrefix}content']),
|
.read(DriftSqlType.string, data['${effectivePrefix}content']),
|
||||||
mediaId: attachedDatabase.typeMapping
|
mediaId: attachedDatabase.typeMapping
|
||||||
|
|
@ -3057,16 +3063,13 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
$MessagesTable createAlias(String alias) {
|
$MessagesTable createAlias(String alias) {
|
||||||
return $MessagesTable(attachedDatabase, alias);
|
return $MessagesTable(attachedDatabase, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonTypeConverter2<MessageType, String, String> $convertertype =
|
|
||||||
const EnumNameConverter<MessageType>(MessageType.values);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Message extends DataClass implements Insertable<Message> {
|
class Message extends DataClass implements Insertable<Message> {
|
||||||
final String groupId;
|
final String groupId;
|
||||||
final String messageId;
|
final String messageId;
|
||||||
final int? senderId;
|
final int? senderId;
|
||||||
final MessageType type;
|
final String type;
|
||||||
final String? content;
|
final String? content;
|
||||||
final String? mediaId;
|
final String? mediaId;
|
||||||
final Uint8List? additionalMessageData;
|
final Uint8List? additionalMessageData;
|
||||||
|
|
@ -3108,9 +3111,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
if (!nullToAbsent || senderId != null) {
|
if (!nullToAbsent || senderId != null) {
|
||||||
map['sender_id'] = Variable<int>(senderId);
|
map['sender_id'] = Variable<int>(senderId);
|
||||||
}
|
}
|
||||||
{
|
map['type'] = Variable<String>(type);
|
||||||
map['type'] = Variable<String>($MessagesTable.$convertertype.toSql(type));
|
|
||||||
}
|
|
||||||
if (!nullToAbsent || content != null) {
|
if (!nullToAbsent || content != null) {
|
||||||
map['content'] = Variable<String>(content);
|
map['content'] = Variable<String>(content);
|
||||||
}
|
}
|
||||||
|
|
@ -3201,8 +3202,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
groupId: serializer.fromJson<String>(json['groupId']),
|
groupId: serializer.fromJson<String>(json['groupId']),
|
||||||
messageId: serializer.fromJson<String>(json['messageId']),
|
messageId: serializer.fromJson<String>(json['messageId']),
|
||||||
senderId: serializer.fromJson<int?>(json['senderId']),
|
senderId: serializer.fromJson<int?>(json['senderId']),
|
||||||
type: $MessagesTable.$convertertype
|
type: serializer.fromJson<String>(json['type']),
|
||||||
.fromJson(serializer.fromJson<String>(json['type'])),
|
|
||||||
content: serializer.fromJson<String?>(json['content']),
|
content: serializer.fromJson<String?>(json['content']),
|
||||||
mediaId: serializer.fromJson<String?>(json['mediaId']),
|
mediaId: serializer.fromJson<String?>(json['mediaId']),
|
||||||
additionalMessageData:
|
additionalMessageData:
|
||||||
|
|
@ -3228,8 +3228,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
'groupId': serializer.toJson<String>(groupId),
|
'groupId': serializer.toJson<String>(groupId),
|
||||||
'messageId': serializer.toJson<String>(messageId),
|
'messageId': serializer.toJson<String>(messageId),
|
||||||
'senderId': serializer.toJson<int?>(senderId),
|
'senderId': serializer.toJson<int?>(senderId),
|
||||||
'type':
|
'type': serializer.toJson<String>(type),
|
||||||
serializer.toJson<String>($MessagesTable.$convertertype.toJson(type)),
|
|
||||||
'content': serializer.toJson<String?>(content),
|
'content': serializer.toJson<String?>(content),
|
||||||
'mediaId': serializer.toJson<String?>(mediaId),
|
'mediaId': serializer.toJson<String?>(mediaId),
|
||||||
'additionalMessageData':
|
'additionalMessageData':
|
||||||
|
|
@ -3252,7 +3251,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
{String? groupId,
|
{String? groupId,
|
||||||
String? messageId,
|
String? messageId,
|
||||||
Value<int?> senderId = const Value.absent(),
|
Value<int?> senderId = const Value.absent(),
|
||||||
MessageType? type,
|
String? type,
|
||||||
Value<String?> content = const Value.absent(),
|
Value<String?> content = const Value.absent(),
|
||||||
Value<String?> mediaId = const Value.absent(),
|
Value<String?> mediaId = const Value.absent(),
|
||||||
Value<Uint8List?> additionalMessageData = const Value.absent(),
|
Value<Uint8List?> additionalMessageData = const Value.absent(),
|
||||||
|
|
@ -3403,7 +3402,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
final Value<String> groupId;
|
final Value<String> groupId;
|
||||||
final Value<String> messageId;
|
final Value<String> messageId;
|
||||||
final Value<int?> senderId;
|
final Value<int?> senderId;
|
||||||
final Value<MessageType> type;
|
final Value<String> type;
|
||||||
final Value<String?> content;
|
final Value<String?> content;
|
||||||
final Value<String?> mediaId;
|
final Value<String?> mediaId;
|
||||||
final Value<Uint8List?> additionalMessageData;
|
final Value<Uint8List?> additionalMessageData;
|
||||||
|
|
@ -3444,7 +3443,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
required String groupId,
|
required String groupId,
|
||||||
required String messageId,
|
required String messageId,
|
||||||
this.senderId = const Value.absent(),
|
this.senderId = const Value.absent(),
|
||||||
required MessageType type,
|
required String type,
|
||||||
this.content = const Value.absent(),
|
this.content = const Value.absent(),
|
||||||
this.mediaId = const Value.absent(),
|
this.mediaId = const Value.absent(),
|
||||||
this.additionalMessageData = const Value.absent(),
|
this.additionalMessageData = const Value.absent(),
|
||||||
|
|
@ -3513,7 +3512,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
{Value<String>? groupId,
|
{Value<String>? groupId,
|
||||||
Value<String>? messageId,
|
Value<String>? messageId,
|
||||||
Value<int?>? senderId,
|
Value<int?>? senderId,
|
||||||
Value<MessageType>? type,
|
Value<String>? type,
|
||||||
Value<String?>? content,
|
Value<String?>? content,
|
||||||
Value<String?>? mediaId,
|
Value<String?>? mediaId,
|
||||||
Value<Uint8List?>? additionalMessageData,
|
Value<Uint8List?>? additionalMessageData,
|
||||||
|
|
@ -3566,8 +3565,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
map['sender_id'] = Variable<int>(senderId.value);
|
map['sender_id'] = Variable<int>(senderId.value);
|
||||||
}
|
}
|
||||||
if (type.present) {
|
if (type.present) {
|
||||||
map['type'] =
|
map['type'] = Variable<String>(type.value);
|
||||||
Variable<String>($MessagesTable.$convertertype.toSql(type.value));
|
|
||||||
}
|
}
|
||||||
if (content.present) {
|
if (content.present) {
|
||||||
map['content'] = Variable<String>(content.value);
|
map['content'] = Variable<String>(content.value);
|
||||||
|
|
@ -10148,7 +10146,7 @@ typedef $$MessagesTableCreateCompanionBuilder = MessagesCompanion Function({
|
||||||
required String groupId,
|
required String groupId,
|
||||||
required String messageId,
|
required String messageId,
|
||||||
Value<int?> senderId,
|
Value<int?> senderId,
|
||||||
required MessageType type,
|
required String type,
|
||||||
Value<String?> content,
|
Value<String?> content,
|
||||||
Value<String?> mediaId,
|
Value<String?> mediaId,
|
||||||
Value<Uint8List?> additionalMessageData,
|
Value<Uint8List?> additionalMessageData,
|
||||||
|
|
@ -10169,7 +10167,7 @@ typedef $$MessagesTableUpdateCompanionBuilder = MessagesCompanion Function({
|
||||||
Value<String> groupId,
|
Value<String> groupId,
|
||||||
Value<String> messageId,
|
Value<String> messageId,
|
||||||
Value<int?> senderId,
|
Value<int?> senderId,
|
||||||
Value<MessageType> type,
|
Value<String> type,
|
||||||
Value<String?> content,
|
Value<String?> content,
|
||||||
Value<String?> mediaId,
|
Value<String?> mediaId,
|
||||||
Value<Uint8List?> additionalMessageData,
|
Value<Uint8List?> additionalMessageData,
|
||||||
|
|
@ -10314,10 +10312,8 @@ class $$MessagesTableFilterComposer
|
||||||
ColumnFilters<String> get messageId => $composableBuilder(
|
ColumnFilters<String> get messageId => $composableBuilder(
|
||||||
column: $table.messageId, builder: (column) => ColumnFilters(column));
|
column: $table.messageId, builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
ColumnWithTypeConverterFilters<MessageType, MessageType, String> get type =>
|
ColumnFilters<String> get type => $composableBuilder(
|
||||||
$composableBuilder(
|
column: $table.type, builder: (column) => ColumnFilters(column));
|
||||||
column: $table.type,
|
|
||||||
builder: (column) => ColumnWithTypeConverterFilters(column));
|
|
||||||
|
|
||||||
ColumnFilters<String> get content => $composableBuilder(
|
ColumnFilters<String> get content => $composableBuilder(
|
||||||
column: $table.content, builder: (column) => ColumnFilters(column));
|
column: $table.content, builder: (column) => ColumnFilters(column));
|
||||||
|
|
@ -10638,7 +10634,7 @@ class $$MessagesTableAnnotationComposer
|
||||||
GeneratedColumn<String> get messageId =>
|
GeneratedColumn<String> get messageId =>
|
||||||
$composableBuilder(column: $table.messageId, builder: (column) => column);
|
$composableBuilder(column: $table.messageId, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumnWithTypeConverter<MessageType, String> get type =>
|
GeneratedColumn<String> get type =>
|
||||||
$composableBuilder(column: $table.type, builder: (column) => column);
|
$composableBuilder(column: $table.type, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumn<String> get content =>
|
GeneratedColumn<String> get content =>
|
||||||
|
|
@ -10858,7 +10854,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
||||||
Value<String> groupId = const Value.absent(),
|
Value<String> groupId = const Value.absent(),
|
||||||
Value<String> messageId = const Value.absent(),
|
Value<String> messageId = const Value.absent(),
|
||||||
Value<int?> senderId = const Value.absent(),
|
Value<int?> senderId = const Value.absent(),
|
||||||
Value<MessageType> type = const Value.absent(),
|
Value<String> type = const Value.absent(),
|
||||||
Value<String?> content = const Value.absent(),
|
Value<String?> content = const Value.absent(),
|
||||||
Value<String?> mediaId = const Value.absent(),
|
Value<String?> mediaId = const Value.absent(),
|
||||||
Value<Uint8List?> additionalMessageData = const Value.absent(),
|
Value<Uint8List?> additionalMessageData = const Value.absent(),
|
||||||
|
|
@ -10900,7 +10896,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
||||||
required String groupId,
|
required String groupId,
|
||||||
required String messageId,
|
required String messageId,
|
||||||
Value<int?> senderId = const Value.absent(),
|
Value<int?> senderId = const Value.absent(),
|
||||||
required MessageType type,
|
required String type,
|
||||||
Value<String?> content = const Value.absent(),
|
Value<String?> content = const Value.absent(),
|
||||||
Value<String?> mediaId = const Value.absent(),
|
Value<String?> mediaId = const Value.absent(),
|
||||||
Value<Uint8List?> additionalMessageData = const Value.absent(),
|
Value<Uint8List?> additionalMessageData = const Value.absent(),
|
||||||
|
|
|
||||||
|
|
@ -2967,6 +2967,30 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'You must authenticate to reopen the image.'**
|
/// **'You must authenticate to reopen the image.'**
|
||||||
String get authRequestReopenImage;
|
String get authRequestReopenImage;
|
||||||
|
|
||||||
|
/// No description provided for @shareContactsMenu.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Contact'**
|
||||||
|
String get shareContactsMenu;
|
||||||
|
|
||||||
|
/// No description provided for @shareContactsTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Select contacts'**
|
||||||
|
String get shareContactsTitle;
|
||||||
|
|
||||||
|
/// No description provided for @shareContactsSubmit.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Share now'**
|
||||||
|
String get shareContactsSubmit;
|
||||||
|
|
||||||
|
/// No description provided for @updateTwonlyMessage.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'To see this message, you need to update twonly.'**
|
||||||
|
String get updateTwonlyMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -1656,4 +1656,17 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get authRequestReopenImage =>
|
String get authRequestReopenImage =>
|
||||||
'Um das Bild erneut zu öffnen, musst du dich authentifizieren.';
|
'Um das Bild erneut zu öffnen, musst du dich authentifizieren.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsMenu => 'Kontakt';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsTitle => 'Kontakte auswählen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsSubmit => 'Jetzt teilen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get updateTwonlyMessage =>
|
||||||
|
'Um diese Nachricht zu sehen, musst du twonly aktualisieren.';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1644,4 +1644,17 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get authRequestReopenImage =>
|
String get authRequestReopenImage =>
|
||||||
'You must authenticate to reopen the image.';
|
'You must authenticate to reopen the image.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsMenu => 'Contact';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsTitle => 'Select contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsSubmit => 'Share now';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get updateTwonlyMessage =>
|
||||||
|
'To see this message, you need to update twonly.';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1644,4 +1644,17 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get authRequestReopenImage =>
|
String get authRequestReopenImage =>
|
||||||
'You must authenticate to reopen the image.';
|
'You must authenticate to reopen the image.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsMenu => 'Contact';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsTitle => 'Select contacts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get shareContactsSubmit => 'Share now';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get updateTwonlyMessage =>
|
||||||
|
'To see this message, you need to update twonly.';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4caaa3d91aaf1ac2f13160ba770a2880c26bd229
|
Subproject commit 69d295db737253e0c1b68aedc39bf757e8d642e6
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
|
message SharedContact {
|
||||||
|
int64 user_id = 1;
|
||||||
|
bytes public_identity_key = 2;
|
||||||
|
string display_name = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message AdditionalMessageData {
|
message AdditionalMessageData {
|
||||||
enum Type {
|
enum Type {
|
||||||
LINK = 0;
|
LINK = 0;
|
||||||
|
CONTACTS = 1;
|
||||||
}
|
}
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
|
|
||||||
optional string link = 2;
|
optional string link = 2;
|
||||||
|
repeated SharedContact contacts = 3;
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
import 'dart:core' as $core;
|
import 'dart:core' as $core;
|
||||||
|
|
||||||
|
import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||||
import 'package:protobuf/protobuf.dart' as $pb;
|
import 'package:protobuf/protobuf.dart' as $pb;
|
||||||
|
|
||||||
import 'data.pbenum.dart';
|
import 'data.pbenum.dart';
|
||||||
|
|
@ -20,14 +21,96 @@ export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
|
||||||
|
|
||||||
export 'data.pbenum.dart';
|
export 'data.pbenum.dart';
|
||||||
|
|
||||||
|
class SharedContact extends $pb.GeneratedMessage {
|
||||||
|
factory SharedContact({
|
||||||
|
$fixnum.Int64? userId,
|
||||||
|
$core.List<$core.int>? publicIdentityKey,
|
||||||
|
$core.String? displayName,
|
||||||
|
}) {
|
||||||
|
final result = create();
|
||||||
|
if (userId != null) result.userId = userId;
|
||||||
|
if (publicIdentityKey != null) result.publicIdentityKey = publicIdentityKey;
|
||||||
|
if (displayName != null) result.displayName = displayName;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedContact._();
|
||||||
|
|
||||||
|
factory SharedContact.fromBuffer($core.List<$core.int> data,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromBuffer(data, registry);
|
||||||
|
factory SharedContact.fromJson($core.String json,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromJson(json, registry);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
_omitMessageNames ? '' : 'SharedContact',
|
||||||
|
createEmptyInstance: create)
|
||||||
|
..aInt64(1, _omitFieldNames ? '' : 'userId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
2, _omitFieldNames ? '' : 'publicIdentityKey', $pb.PbFieldType.OY)
|
||||||
|
..aOS(3, _omitFieldNames ? '' : 'displayName')
|
||||||
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
SharedContact clone() => SharedContact()..mergeFromMessage(this);
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
SharedContact copyWith(void Function(SharedContact) updates) =>
|
||||||
|
super.copyWith((message) => updates(message as SharedContact))
|
||||||
|
as SharedContact;
|
||||||
|
|
||||||
|
@$core.override
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static SharedContact create() => SharedContact._();
|
||||||
|
@$core.override
|
||||||
|
SharedContact createEmptyInstance() => create();
|
||||||
|
static $pb.PbList<SharedContact> createRepeated() =>
|
||||||
|
$pb.PbList<SharedContact>();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static SharedContact getDefault() => _defaultInstance ??=
|
||||||
|
$pb.GeneratedMessage.$_defaultFor<SharedContact>(create);
|
||||||
|
static SharedContact? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$fixnum.Int64 get userId => $_getI64(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set userId($fixnum.Int64 value) => $_setInt64(0, value);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasUserId() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearUserId() => $_clearField(1);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.List<$core.int> get publicIdentityKey => $_getN(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set publicIdentityKey($core.List<$core.int> value) => $_setBytes(1, value);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasPublicIdentityKey() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearPublicIdentityKey() => $_clearField(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.String get displayName => $_getSZ(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set displayName($core.String value) => $_setString(2, value);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasDisplayName() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearDisplayName() => $_clearField(3);
|
||||||
|
}
|
||||||
|
|
||||||
class AdditionalMessageData extends $pb.GeneratedMessage {
|
class AdditionalMessageData extends $pb.GeneratedMessage {
|
||||||
factory AdditionalMessageData({
|
factory AdditionalMessageData({
|
||||||
AdditionalMessageData_Type? type,
|
AdditionalMessageData_Type? type,
|
||||||
$core.String? link,
|
$core.String? link,
|
||||||
|
$core.Iterable<SharedContact>? contacts,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (type != null) result.type = type;
|
if (type != null) result.type = type;
|
||||||
if (link != null) result.link = link;
|
if (link != null) result.link = link;
|
||||||
|
if (contacts != null) result.contacts.addAll(contacts);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,6 +132,9 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
||||||
valueOf: AdditionalMessageData_Type.valueOf,
|
valueOf: AdditionalMessageData_Type.valueOf,
|
||||||
enumValues: AdditionalMessageData_Type.values)
|
enumValues: AdditionalMessageData_Type.values)
|
||||||
..aOS(2, _omitFieldNames ? '' : 'link')
|
..aOS(2, _omitFieldNames ? '' : 'link')
|
||||||
|
..pc<SharedContact>(
|
||||||
|
3, _omitFieldNames ? '' : 'contacts', $pb.PbFieldType.PM,
|
||||||
|
subBuilder: SharedContact.create)
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
|
@ -91,6 +177,9 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
||||||
$core.bool hasLink() => $_has(1);
|
$core.bool hasLink() => $_has(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
void clearLink() => $_clearField(2);
|
void clearLink() => $_clearField(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$pb.PbList<SharedContact> get contacts => $_getList(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
const $core.bool _omitFieldNames =
|
const $core.bool _omitFieldNames =
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,17 @@ import 'package:protobuf/protobuf.dart' as $pb;
|
||||||
class AdditionalMessageData_Type extends $pb.ProtobufEnum {
|
class AdditionalMessageData_Type extends $pb.ProtobufEnum {
|
||||||
static const AdditionalMessageData_Type LINK =
|
static const AdditionalMessageData_Type LINK =
|
||||||
AdditionalMessageData_Type._(0, _omitEnumNames ? '' : 'LINK');
|
AdditionalMessageData_Type._(0, _omitEnumNames ? '' : 'LINK');
|
||||||
|
static const AdditionalMessageData_Type CONTACTS =
|
||||||
|
AdditionalMessageData_Type._(1, _omitEnumNames ? '' : 'CONTACTS');
|
||||||
|
|
||||||
static const $core.List<AdditionalMessageData_Type> values =
|
static const $core.List<AdditionalMessageData_Type> values =
|
||||||
<AdditionalMessageData_Type>[
|
<AdditionalMessageData_Type>[
|
||||||
LINK,
|
LINK,
|
||||||
|
CONTACTS,
|
||||||
];
|
];
|
||||||
|
|
||||||
static final $core.List<AdditionalMessageData_Type?> _byValue =
|
static final $core.List<AdditionalMessageData_Type?> _byValue =
|
||||||
$pb.ProtobufEnum.$_initByValueList(values, 0);
|
$pb.ProtobufEnum.$_initByValueList(values, 1);
|
||||||
static AdditionalMessageData_Type? valueOf($core.int value) =>
|
static AdditionalMessageData_Type? valueOf($core.int value) =>
|
||||||
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,28 @@ import 'dart:convert' as $convert;
|
||||||
import 'dart:core' as $core;
|
import 'dart:core' as $core;
|
||||||
import 'dart:typed_data' as $typed_data;
|
import 'dart:typed_data' as $typed_data;
|
||||||
|
|
||||||
|
@$core.Deprecated('Use sharedContactDescriptor instead')
|
||||||
|
const SharedContact$json = {
|
||||||
|
'1': 'SharedContact',
|
||||||
|
'2': [
|
||||||
|
{'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'},
|
||||||
|
{
|
||||||
|
'1': 'public_identity_key',
|
||||||
|
'3': 2,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'publicIdentityKey'
|
||||||
|
},
|
||||||
|
{'1': 'display_name', '3': 3, '4': 1, '5': 9, '10': 'displayName'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `SharedContact`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List sharedContactDescriptor = $convert.base64Decode(
|
||||||
|
'Cg1TaGFyZWRDb250YWN0EhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIuChNwdWJsaWNfaWRlbn'
|
||||||
|
'RpdHlfa2V5GAIgASgMUhFwdWJsaWNJZGVudGl0eUtleRIhCgxkaXNwbGF5X25hbWUYAyABKAlS'
|
||||||
|
'C2Rpc3BsYXlOYW1l');
|
||||||
|
|
||||||
@$core.Deprecated('Use additionalMessageDataDescriptor instead')
|
@$core.Deprecated('Use additionalMessageDataDescriptor instead')
|
||||||
const AdditionalMessageData$json = {
|
const AdditionalMessageData$json = {
|
||||||
'1': 'AdditionalMessageData',
|
'1': 'AdditionalMessageData',
|
||||||
|
|
@ -27,6 +49,14 @@ const AdditionalMessageData$json = {
|
||||||
'10': 'type'
|
'10': 'type'
|
||||||
},
|
},
|
||||||
{'1': 'link', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'link', '17': true},
|
{'1': 'link', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'link', '17': true},
|
||||||
|
{
|
||||||
|
'1': 'contacts',
|
||||||
|
'3': 3,
|
||||||
|
'4': 3,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.SharedContact',
|
||||||
|
'10': 'contacts'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'4': [AdditionalMessageData_Type$json],
|
'4': [AdditionalMessageData_Type$json],
|
||||||
'8': [
|
'8': [
|
||||||
|
|
@ -39,11 +69,13 @@ const AdditionalMessageData_Type$json = {
|
||||||
'1': 'Type',
|
'1': 'Type',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'LINK', '2': 0},
|
{'1': 'LINK', '2': 0},
|
||||||
|
{'1': 'CONTACTS', '2': 1},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `AdditionalMessageData`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `AdditionalMessageData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List additionalMessageDataDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List additionalMessageDataDescriptor = $convert.base64Decode(
|
||||||
'ChVBZGRpdGlvbmFsTWVzc2FnZURhdGESLwoEdHlwZRgBIAEoDjIbLkFkZGl0aW9uYWxNZXNzYW'
|
'ChVBZGRpdGlvbmFsTWVzc2FnZURhdGESLwoEdHlwZRgBIAEoDjIbLkFkZGl0aW9uYWxNZXNzYW'
|
||||||
'dlRGF0YS5UeXBlUgR0eXBlEhcKBGxpbmsYAiABKAlIAFIEbGlua4gBASIQCgRUeXBlEggKBExJ'
|
'dlRGF0YS5UeXBlUgR0eXBlEhcKBGxpbmsYAiABKAlIAFIEbGlua4gBARIqCghjb250YWN0cxgD'
|
||||||
'TksQAEIHCgVfbGluaw==');
|
'IAMoCzIOLlNoYXJlZENvbnRhY3RSCGNvbnRhY3RzIh4KBFR5cGUSCAoETElOSxAAEgwKCENPTl'
|
||||||
|
'RBQ1RTEAFCBwoFX2xpbms=');
|
||||||
|
|
|
||||||
|
|
@ -767,6 +767,106 @@ class EncryptedContent_TextMessage extends $pb.GeneratedMessage {
|
||||||
void clearQuoteMessageId() => $_clearField(4);
|
void clearQuoteMessageId() => $_clearField(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EncryptedContent_AdditionalDataMessage extends $pb.GeneratedMessage {
|
||||||
|
factory EncryptedContent_AdditionalDataMessage({
|
||||||
|
$core.String? senderMessageId,
|
||||||
|
$fixnum.Int64? timestamp,
|
||||||
|
$core.String? type,
|
||||||
|
$core.List<$core.int>? additionalMessageData,
|
||||||
|
}) {
|
||||||
|
final result = create();
|
||||||
|
if (senderMessageId != null) result.senderMessageId = senderMessageId;
|
||||||
|
if (timestamp != null) result.timestamp = timestamp;
|
||||||
|
if (type != null) result.type = type;
|
||||||
|
if (additionalMessageData != null)
|
||||||
|
result.additionalMessageData = additionalMessageData;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptedContent_AdditionalDataMessage._();
|
||||||
|
|
||||||
|
factory EncryptedContent_AdditionalDataMessage.fromBuffer(
|
||||||
|
$core.List<$core.int> data,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromBuffer(data, registry);
|
||||||
|
factory EncryptedContent_AdditionalDataMessage.fromJson($core.String json,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromJson(json, registry);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
_omitMessageNames ? '' : 'EncryptedContent.AdditionalDataMessage',
|
||||||
|
createEmptyInstance: create)
|
||||||
|
..aOS(1, _omitFieldNames ? '' : 'senderMessageId')
|
||||||
|
..aInt64(2, _omitFieldNames ? '' : 'timestamp')
|
||||||
|
..aOS(3, _omitFieldNames ? '' : 'type')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
4, _omitFieldNames ? '' : 'additionalMessageData', $pb.PbFieldType.OY)
|
||||||
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
EncryptedContent_AdditionalDataMessage clone() =>
|
||||||
|
EncryptedContent_AdditionalDataMessage()..mergeFromMessage(this);
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
EncryptedContent_AdditionalDataMessage copyWith(
|
||||||
|
void Function(EncryptedContent_AdditionalDataMessage) updates) =>
|
||||||
|
super.copyWith((message) =>
|
||||||
|
updates(message as EncryptedContent_AdditionalDataMessage))
|
||||||
|
as EncryptedContent_AdditionalDataMessage;
|
||||||
|
|
||||||
|
@$core.override
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static EncryptedContent_AdditionalDataMessage create() =>
|
||||||
|
EncryptedContent_AdditionalDataMessage._();
|
||||||
|
@$core.override
|
||||||
|
EncryptedContent_AdditionalDataMessage createEmptyInstance() => create();
|
||||||
|
static $pb.PbList<EncryptedContent_AdditionalDataMessage> createRepeated() =>
|
||||||
|
$pb.PbList<EncryptedContent_AdditionalDataMessage>();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static EncryptedContent_AdditionalDataMessage getDefault() =>
|
||||||
|
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
|
||||||
|
EncryptedContent_AdditionalDataMessage>(create);
|
||||||
|
static EncryptedContent_AdditionalDataMessage? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.String get senderMessageId => $_getSZ(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set senderMessageId($core.String value) => $_setString(0, value);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasSenderMessageId() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearSenderMessageId() => $_clearField(1);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$fixnum.Int64 get timestamp => $_getI64(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set timestamp($fixnum.Int64 value) => $_setInt64(1, value);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasTimestamp() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearTimestamp() => $_clearField(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.String get type => $_getSZ(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set type($core.String value) => $_setString(2, value);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasType() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearType() => $_clearField(3);
|
||||||
|
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.List<$core.int> get additionalMessageData => $_getN(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
set additionalMessageData($core.List<$core.int> value) =>
|
||||||
|
$_setBytes(3, value);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.bool hasAdditionalMessageData() => $_has(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
void clearAdditionalMessageData() => $_clearField(4);
|
||||||
|
}
|
||||||
|
|
||||||
class EncryptedContent_Reaction extends $pb.GeneratedMessage {
|
class EncryptedContent_Reaction extends $pb.GeneratedMessage {
|
||||||
factory EncryptedContent_Reaction({
|
factory EncryptedContent_Reaction({
|
||||||
$core.String? targetMessageId,
|
$core.String? targetMessageId,
|
||||||
|
|
@ -1611,6 +1711,7 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
||||||
EncryptedContent_GroupUpdate? groupUpdate,
|
EncryptedContent_GroupUpdate? groupUpdate,
|
||||||
EncryptedContent_ResendGroupPublicKey? resendGroupPublicKey,
|
EncryptedContent_ResendGroupPublicKey? resendGroupPublicKey,
|
||||||
EncryptedContent_ErrorMessages? errorMessages,
|
EncryptedContent_ErrorMessages? errorMessages,
|
||||||
|
EncryptedContent_AdditionalDataMessage? additionalDataMessage,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (groupId != null) result.groupId = groupId;
|
if (groupId != null) result.groupId = groupId;
|
||||||
|
|
@ -1632,6 +1733,8 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
||||||
if (resendGroupPublicKey != null)
|
if (resendGroupPublicKey != null)
|
||||||
result.resendGroupPublicKey = resendGroupPublicKey;
|
result.resendGroupPublicKey = resendGroupPublicKey;
|
||||||
if (errorMessages != null) result.errorMessages = errorMessages;
|
if (errorMessages != null) result.errorMessages = errorMessages;
|
||||||
|
if (additionalDataMessage != null)
|
||||||
|
result.additionalDataMessage = additionalDataMessage;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1695,6 +1798,9 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
||||||
..aOM<EncryptedContent_ErrorMessages>(
|
..aOM<EncryptedContent_ErrorMessages>(
|
||||||
18, _omitFieldNames ? '' : 'errorMessages',
|
18, _omitFieldNames ? '' : 'errorMessages',
|
||||||
subBuilder: EncryptedContent_ErrorMessages.create)
|
subBuilder: EncryptedContent_ErrorMessages.create)
|
||||||
|
..aOM<EncryptedContent_AdditionalDataMessage>(
|
||||||
|
19, _omitFieldNames ? '' : 'additionalDataMessage',
|
||||||
|
subBuilder: EncryptedContent_AdditionalDataMessage.create)
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
|
@ -1905,6 +2011,20 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
||||||
void clearErrorMessages() => $_clearField(18);
|
void clearErrorMessages() => $_clearField(18);
|
||||||
@$pb.TagNumber(18)
|
@$pb.TagNumber(18)
|
||||||
EncryptedContent_ErrorMessages ensureErrorMessages() => $_ensure(16);
|
EncryptedContent_ErrorMessages ensureErrorMessages() => $_ensure(16);
|
||||||
|
|
||||||
|
@$pb.TagNumber(19)
|
||||||
|
EncryptedContent_AdditionalDataMessage get additionalDataMessage =>
|
||||||
|
$_getN(17);
|
||||||
|
@$pb.TagNumber(19)
|
||||||
|
set additionalDataMessage(EncryptedContent_AdditionalDataMessage value) =>
|
||||||
|
$_setField(19, value);
|
||||||
|
@$pb.TagNumber(19)
|
||||||
|
$core.bool hasAdditionalDataMessage() => $_has(17);
|
||||||
|
@$pb.TagNumber(19)
|
||||||
|
void clearAdditionalDataMessage() => $_clearField(19);
|
||||||
|
@$pb.TagNumber(19)
|
||||||
|
EncryptedContent_AdditionalDataMessage ensureAdditionalDataMessage() =>
|
||||||
|
$_ensure(17);
|
||||||
}
|
}
|
||||||
|
|
||||||
const $core.bool _omitFieldNames =
|
const $core.bool _omitFieldNames =
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,16 @@ const EncryptedContent$json = {
|
||||||
'10': 'errorMessages',
|
'10': 'errorMessages',
|
||||||
'17': true
|
'17': true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'additional_data_message',
|
||||||
|
'3': 19,
|
||||||
|
'4': 1,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.EncryptedContent.AdditionalDataMessage',
|
||||||
|
'9': 17,
|
||||||
|
'10': 'additionalDataMessage',
|
||||||
|
'17': true
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'3': [
|
'3': [
|
||||||
EncryptedContent_ErrorMessages$json,
|
EncryptedContent_ErrorMessages$json,
|
||||||
|
|
@ -324,6 +334,7 @@ const EncryptedContent$json = {
|
||||||
EncryptedContent_ResendGroupPublicKey$json,
|
EncryptedContent_ResendGroupPublicKey$json,
|
||||||
EncryptedContent_GroupUpdate$json,
|
EncryptedContent_GroupUpdate$json,
|
||||||
EncryptedContent_TextMessage$json,
|
EncryptedContent_TextMessage$json,
|
||||||
|
EncryptedContent_AdditionalDataMessage$json,
|
||||||
EncryptedContent_Reaction$json,
|
EncryptedContent_Reaction$json,
|
||||||
EncryptedContent_MessageUpdate$json,
|
EncryptedContent_MessageUpdate$json,
|
||||||
EncryptedContent_Media$json,
|
EncryptedContent_Media$json,
|
||||||
|
|
@ -351,6 +362,7 @@ const EncryptedContent$json = {
|
||||||
{'1': '_groupUpdate'},
|
{'1': '_groupUpdate'},
|
||||||
{'1': '_resendGroupPublicKey'},
|
{'1': '_resendGroupPublicKey'},
|
||||||
{'1': '_error_messages'},
|
{'1': '_error_messages'},
|
||||||
|
{'1': '_additional_data_message'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -470,6 +482,28 @@ const EncryptedContent_TextMessage$json = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use encryptedContentDescriptor instead')
|
||||||
|
const EncryptedContent_AdditionalDataMessage$json = {
|
||||||
|
'1': 'AdditionalDataMessage',
|
||||||
|
'2': [
|
||||||
|
{'1': 'sender_message_id', '3': 1, '4': 1, '5': 9, '10': 'senderMessageId'},
|
||||||
|
{'1': 'timestamp', '3': 2, '4': 1, '5': 3, '10': 'timestamp'},
|
||||||
|
{'1': 'type', '3': 3, '4': 1, '5': 9, '10': 'type'},
|
||||||
|
{
|
||||||
|
'1': 'additional_message_data',
|
||||||
|
'3': 4,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'9': 0,
|
||||||
|
'10': 'additionalMessageData',
|
||||||
|
'17': true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'8': [
|
||||||
|
{'1': '_additional_message_data'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
@$core.Deprecated('Use encryptedContentDescriptor instead')
|
@$core.Deprecated('Use encryptedContentDescriptor instead')
|
||||||
const EncryptedContent_Reaction$json = {
|
const EncryptedContent_Reaction$json = {
|
||||||
'1': 'Reaction',
|
'1': 'Reaction',
|
||||||
|
|
@ -827,63 +861,69 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
||||||
'5SC2dyb3VwVXBkYXRliAEBEl8KFHJlc2VuZEdyb3VwUHVibGljS2V5GBEgASgLMiYuRW5jcnlw'
|
'5SC2dyb3VwVXBkYXRliAEBEl8KFHJlc2VuZEdyb3VwUHVibGljS2V5GBEgASgLMiYuRW5jcnlw'
|
||||||
'dGVkQ29udGVudC5SZXNlbmRHcm91cFB1YmxpY0tleUgPUhRyZXNlbmRHcm91cFB1YmxpY0tleY'
|
'dGVkQ29udGVudC5SZXNlbmRHcm91cFB1YmxpY0tleUgPUhRyZXNlbmRHcm91cFB1YmxpY0tleY'
|
||||||
'gBARJLCg5lcnJvcl9tZXNzYWdlcxgSIAEoCzIfLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNz'
|
'gBARJLCg5lcnJvcl9tZXNzYWdlcxgSIAEoCzIfLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNz'
|
||||||
'YWdlc0gQUg1lcnJvck1lc3NhZ2VziAEBGtcBCg1FcnJvck1lc3NhZ2VzEjgKBHR5cGUYASABKA'
|
'YWdlc0gQUg1lcnJvck1lc3NhZ2VziAEBEmQKF2FkZGl0aW9uYWxfZGF0YV9tZXNzYWdlGBMgAS'
|
||||||
'4yJC5FbmNyeXB0ZWRDb250ZW50LkVycm9yTWVzc2FnZXMuVHlwZVIEdHlwZRIsChJyZWxhdGVk'
|
'gLMicuRW5jcnlwdGVkQ29udGVudC5BZGRpdGlvbmFsRGF0YU1lc3NhZ2VIEVIVYWRkaXRpb25h'
|
||||||
'X3JlY2VpcHRfaWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0SWQiXgoEVHlwZRI8CjhFUlJPUl9QUk'
|
'bERhdGFNZXNzYWdliAEBGtcBCg1FcnJvck1lc3NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeX'
|
||||||
'9DRVNTSU5HX01FU1NBR0VfQ1JFQVRFRF9BQ0NPVU5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVO'
|
'B0ZWRDb250ZW50LkVycm9yTWVzc2FnZXMuVHlwZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRf'
|
||||||
'S05PV05fTUVTU0FHRV9UWVBFEAIaUQoLR3JvdXBDcmVhdGUSGgoIc3RhdGVLZXkYAyABKAxSCH'
|
'aWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0SWQiXgoEVHlwZRI8CjhFUlJPUl9QUk9DRVNTSU5HX0'
|
||||||
'N0YXRlS2V5EiYKDmdyb3VwUHVibGljS2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRozCglHcm91'
|
'1FU1NBR0VfQ1JFQVRFRF9BQ0NPVU5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVOS05PV05fTUVT'
|
||||||
'cEpvaW4SJgoOZ3JvdXBQdWJsaWNLZXkYASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZE'
|
'U0FHRV9UWVBFEAIaUQoLR3JvdXBDcmVhdGUSGgoIc3RhdGVLZXkYAyABKAxSCHN0YXRlS2V5Ei'
|
||||||
'dyb3VwUHVibGljS2V5GrYCCgtHcm91cFVwZGF0ZRIoCg9ncm91cEFjdGlvblR5cGUYASABKAlS'
|
'YKDmdyb3VwUHVibGljS2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRozCglHcm91cEpvaW4SJgoO'
|
||||||
'D2dyb3VwQWN0aW9uVHlwZRIxChFhZmZlY3RlZENvbnRhY3RJZBgCIAEoA0gAUhFhZmZlY3RlZE'
|
'Z3JvdXBQdWJsaWNLZXkYASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZEdyb3VwUHVibG'
|
||||||
'NvbnRhY3RJZIgBARInCgxuZXdHcm91cE5hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElMK'
|
'ljS2V5GrYCCgtHcm91cFVwZGF0ZRIoCg9ncm91cEFjdGlvblR5cGUYASABKAlSD2dyb3VwQWN0'
|
||||||
'Im5ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaWxsaXNlY29uZHMYBCABKANIAlIibmV3RGVsZXRlTW'
|
'aW9uVHlwZRIxChFhZmZlY3RlZENvbnRhY3RJZBgCIAEoA0gAUhFhZmZlY3RlZENvbnRhY3RJZI'
|
||||||
'Vzc2FnZXNBZnRlck1pbGxpc2Vjb25kc4gBAUIUChJfYWZmZWN0ZWRDb250YWN0SWRCDwoNX25l'
|
'gBARInCgxuZXdHcm91cE5hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElMKIm5ld0RlbGV0'
|
||||||
'd0dyb3VwTmFtZUIlCiNfbmV3RGVsZXRlTWVzc2FnZXNBZnRlck1pbGxpc2Vjb25kcxqpAQoLVG'
|
'ZU1lc3NhZ2VzQWZ0ZXJNaWxsaXNlY29uZHMYBCABKANIAlIibmV3RGVsZXRlTWVzc2FnZXNBZn'
|
||||||
'V4dE1lc3NhZ2USKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSEgoE'
|
'Rlck1pbGxpc2Vjb25kc4gBAUIUChJfYWZmZWN0ZWRDb250YWN0SWRCDwoNX25ld0dyb3VwTmFt'
|
||||||
'dGV4dBgCIAEoCVIEdGV4dBIcCgl0aW1lc3RhbXAYAyABKANSCXRpbWVzdGFtcBIrCg5xdW90ZU'
|
'ZUIlCiNfbmV3RGVsZXRlTWVzc2FnZXNBZnRlck1pbGxpc2Vjb25kcxqpAQoLVGV4dE1lc3NhZ2'
|
||||||
'1lc3NhZ2VJZBgEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUIRCg9fcXVvdGVNZXNzYWdlSWQa'
|
'USKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSEgoEdGV4dBgCIAEo'
|
||||||
'YgoIUmVhY3Rpb24SKAoPdGFyZ2V0TWVzc2FnZUlkGAEgASgJUg90YXJnZXRNZXNzYWdlSWQSFA'
|
'CVIEdGV4dBIcCgl0aW1lc3RhbXAYAyABKANSCXRpbWVzdGFtcBIrCg5xdW90ZU1lc3NhZ2VJZB'
|
||||||
'oFZW1vamkYAiABKAlSBWVtb2ppEhYKBnJlbW92ZRgDIAEoCFIGcmVtb3ZlGrcCCg1NZXNzYWdl'
|
'gEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUIRCg9fcXVvdGVNZXNzYWdlSWQazgEKFUFkZGl0'
|
||||||
'VXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50Lk1lc3NhZ2VVcGRhdGUuVH'
|
'aW9uYWxEYXRhTWVzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2VuZGVyTWVzc2'
|
||||||
'lwZVIEdHlwZRItCg9zZW5kZXJNZXNzYWdlSWQYAiABKAlIAFIPc2VuZGVyTWVzc2FnZUlkiAEB'
|
'FnZUlkEhwKCXRpbWVzdGFtcBgCIAEoA1IJdGltZXN0YW1wEhIKBHR5cGUYAyABKAlSBHR5cGUS'
|
||||||
'EjoKGG11bHRpcGxlVGFyZ2V0TWVzc2FnZUlkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZXNzYW'
|
'OwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYBCABKAxIAFIVYWRkaXRpb25hbE1lc3NhZ2VEYX'
|
||||||
'dlSWRzEhcKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVz'
|
'RhiAEBQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRpiCghSZWFjdGlvbhIoCg90YXJnZXRN'
|
||||||
'dGFtcCItCgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQhIKEF'
|
'ZXNzYWdlSWQYASABKAlSD3RhcmdldE1lc3NhZ2VJZBIUCgVlbW9qaRgCIAEoCVIFZW1vamkSFg'
|
||||||
'9zZW5kZXJNZXNzYWdlSWRCBwoFX3RleHQa8AUKBU1lZGlhEigKD3NlbmRlck1lc3NhZ2VJZBgB'
|
'oGcmVtb3ZlGAMgASgIUgZyZW1vdmUatwIKDU1lc3NhZ2VVcGRhdGUSOAoEdHlwZRgBIAEoDjIk'
|
||||||
'IAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW50Lk'
|
'LkVuY3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS5UeXBlUgR0eXBlEi0KD3NlbmRlck1lc3'
|
||||||
'1lZGlhLlR5cGVSBHR5cGUSQwoaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHMYAyABKANIAFIa'
|
'NhZ2VJZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESOgoYbXVsdGlwbGVUYXJnZXRNZXNz'
|
||||||
'ZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNgoWcmVxdWlyZXNBdXRoZW50aWNhdGlvbh'
|
'YWdlSWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZHMSFwoEdGV4dBgEIAEoCUgBUg'
|
||||||
'gEIAEoCFIWcmVxdWlyZXNBdXRoZW50aWNhdGlvbhIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVz'
|
'R0ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wIi0KBFR5cGUSCgoGREVMRVRF'
|
||||||
'dGFtcBIrCg5xdW90ZU1lc3NhZ2VJZBgGIAEoCUgBUg5xdW90ZU1lc3NhZ2VJZIgBARIpCg1kb3'
|
'EAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCEgoQX3NlbmRlck1lc3NhZ2VJZEIHCgVfdG'
|
||||||
'dubG9hZFRva2VuGAcgASgMSAJSDWRvd25sb2FkVG9rZW6IAQESKQoNZW5jcnlwdGlvbktleRgI'
|
'V4dBrwBQoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQS'
|
||||||
'IAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEikKDWVuY3J5cHRpb25NYWMYCSABKAxIBFINZW5jcn'
|
'MAoEdHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlwZVIEdHlwZRJDChpkaX'
|
||||||
'lwdGlvbk1hY4gBARItCg9lbmNyeXB0aW9uTm9uY2UYCiABKAxIBVIPZW5jcnlwdGlvbk5vbmNl'
|
'NwbGF5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TGltaXRJbk1pbGxpc2Vj'
|
||||||
'iAEBEjsKF2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGAsgASgMSAZSFWFkZGl0aW9uYWxNZXNzYW'
|
'b25kc4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZyZXF1aXJlc0F1dGhlbn'
|
||||||
'dlRGF0YYgBASI+CgRUeXBlEgwKCFJFVVBMT0FEEAASCQoFSU1BR0UQARIJCgVWSURFTxACEgcK'
|
'RpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2FnZUlk'
|
||||||
'A0dJRhADEgkKBUFVRElPEARCHQobX2Rpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRzQhEKD19xdW'
|
'GAYgASgJSAFSDnF1b3RlTWVzc2FnZUlkiAEBEikKDWRvd25sb2FkVG9rZW4YByABKAxIAlINZG'
|
||||||
'90ZU1lc3NhZ2VJZEIQCg5fZG93bmxvYWRUb2tlbkIQCg5fZW5jcnlwdGlvbktleUIQCg5fZW5j'
|
'93bmxvYWRUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAggASgMSANSDWVuY3J5cHRpb25LZXmI'
|
||||||
'cnlwdGlvbk1hY0ISChBfZW5jcnlwdGlvbk5vbmNlQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZG'
|
'AQESKQoNZW5jcnlwdGlvbk1hYxgJIAEoDEgEUg1lbmNyeXB0aW9uTWFjiAEBEi0KD2VuY3J5cH'
|
||||||
'F0YRqnAQoLTWVkaWFVcGRhdGUSNgoEdHlwZRgBIAEoDjIiLkVuY3J5cHRlZENvbnRlbnQuTWVk'
|
'Rpb25Ob25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm9uY2WIAQESOwoXYWRkaXRpb25hbF9tZXNz'
|
||||||
'aWFVcGRhdGUuVHlwZVIEdHlwZRIoCg90YXJnZXRNZXNzYWdlSWQYAiABKAlSD3RhcmdldE1lc3'
|
'YWdlX2RhdGEYCyABKAxIBlIVYWRkaXRpb25hbE1lc3NhZ2VEYXRhiAEBIj4KBFR5cGUSDAoIUk'
|
||||||
'NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEAASCgoGU1RPUkVEEAESFAoQREVDUllQVElPTl9F'
|
'VVUExPQUQQABIJCgVJTUFHRRABEgkKBVZJREVPEAISBwoDR0lGEAMSCQoFQVVESU8QBEIdChtf'
|
||||||
'UlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkKBHR5cGUYASABKA4yJS5FbmNyeXB0ZWRDb250ZW'
|
'ZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHNCEQoPX3F1b3RlTWVzc2FnZUlkQhAKDl9kb3dubG'
|
||||||
'50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cGUiKwoEVHlwZRILCgdSRVFVRVNUEAASCgoGUkVK'
|
'9hZFRva2VuQhAKDl9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW9uTWFjQhIKEF9lbmNyeXB0'
|
||||||
'RUNUEAESCgoGQUNDRVBUEAIangIKDUNvbnRhY3RVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLkVuY3'
|
'aW9uTm9uY2VCGgoYX2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGqcBCgtNZWRpYVVwZGF0ZRI2Cg'
|
||||||
'J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS5UeXBlUgR0eXBlEjUKE2F2YXRhclN2Z0NvbXBy'
|
'R0eXBlGAEgASgOMiIuRW5jcnlwdGVkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eXBlEigK'
|
||||||
'ZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZIgBARIfCgh1c2VybmFtZRgDIAEoCU'
|
'D3RhcmdldE1lc3NhZ2VJZBgCIAEoCVIPdGFyZ2V0TWVzc2FnZUlkIjYKBFR5cGUSDAoIUkVPUE'
|
||||||
'gBUgh1c2VybmFtZYgBARIlCgtkaXNwbGF5TmFtZRgEIAEoCUgCUgtkaXNwbGF5TmFtZYgBASIf'
|
'VORUQQABIKCgZTVE9SRUQQARIUChBERUNSWVBUSU9OX0VSUk9SEAIaeAoOQ29udGFjdFJlcXVl'
|
||||||
'CgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIWChRfYXZhdGFyU3ZnQ29tcHJlc3NlZE'
|
'c3QSOQoEdHlwZRgBIAEoDjIlLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFJlcXVlc3QuVHlwZV'
|
||||||
'ILCglfdXNlcm5hbWVCDgoMX2Rpc3BsYXlOYW1lGtUBCghQdXNoS2V5cxIzCgR0eXBlGAEgASgO'
|
'IEdHlwZSIrCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZSRUpFQ1QQARIKCgZBQ0NFUFQQAhqeAgoN'
|
||||||
'Mh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGAIgASgDSA'
|
'Q29udGFjdFVwZGF0ZRI4CgR0eXBlGAEgASgOMiQuRW5jcnlwdGVkQ29udGVudC5Db250YWN0VX'
|
||||||
'BSBWtleUlkiAEBEhUKA2tleRgDIAEoDEgBUgNrZXmIAQESIQoJY3JlYXRlZEF0GAQgASgDSAJS'
|
'BkYXRlLlR5cGVSBHR5cGUSNQoTYXZhdGFyU3ZnQ29tcHJlc3NlZBgCIAEoDEgAUhNhdmF0YXJT'
|
||||||
'CWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIICgZfa2V5SW'
|
'dmdDb21wcmVzc2VkiAEBEh8KCHVzZXJuYW1lGAMgASgJSAFSCHVzZXJuYW1liAEBEiUKC2Rpc3'
|
||||||
'RCBgoEX2tleUIMCgpfY3JlYXRlZEF0GqkBCglGbGFtZVN5bmMSIgoMZmxhbWVDb3VudGVyGAEg'
|
'BsYXlOYW1lGAQgASgJSAJSC2Rpc3BsYXlOYW1liAEBIh8KBFR5cGUSCwoHUkVRVUVTVBAAEgoK'
|
||||||
'ASgDUgxmbGFtZUNvdW50ZXISNgoWbGFzdEZsYW1lQ291bnRlckNoYW5nZRgCIAEoA1IWbGFzdE'
|
'BlVQREFURRABQhYKFF9hdmF0YXJTdmdDb21wcmVzc2VkQgsKCV91c2VybmFtZUIOCgxfZGlzcG'
|
||||||
'ZsYW1lQ291bnRlckNoYW5nZRIeCgpiZXN0RnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEiAKC2Zv'
|
'xheU5hbWUa1QEKCFB1c2hLZXlzEjMKBHR5cGUYASABKA4yHy5FbmNyeXB0ZWRDb250ZW50LlB1'
|
||||||
'cmNlVXBkYXRlGAQgASgIUgtmb3JjZVVwZGF0ZUIKCghfZ3JvdXBJZEIPCg1faXNEaXJlY3RDaG'
|
'c2hLZXlzLlR5cGVSBHR5cGUSGQoFa2V5SWQYAiABKANIAFIFa2V5SWSIAQESFQoDa2V5GAMgAS'
|
||||||
'F0QhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIQCg5fbWVzc2FnZVVwZGF0ZUIICgZfbWVkaWFC'
|
'gMSAFSA2tleYgBARIhCgljcmVhdGVkQXQYBCABKANIAlIJY3JlYXRlZEF0iAEBIh8KBFR5cGUS'
|
||||||
'DgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YWN0UmVxdWVzdEIMCg'
|
'CwoHUkVRVUVTVBAAEgoKBlVQREFURRABQggKBl9rZXlJZEIGCgRfa2V5QgwKCl9jcmVhdGVkQX'
|
||||||
'pfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb25CDgoMX3RleHRNZXNzYWdlQg4K'
|
'QaqQEKCUZsYW1lU3luYxIiCgxmbGFtZUNvdW50ZXIYASABKANSDGZsYW1lQ291bnRlchI2ChZs'
|
||||||
'DF9ncm91cENyZWF0ZUIMCgpfZ3JvdXBKb2luQg4KDF9ncm91cFVwZGF0ZUIXChVfcmVzZW5kR3'
|
'YXN0RmxhbWVDb3VudGVyQ2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlEh4KCm'
|
||||||
'JvdXBQdWJsaWNLZXlCEQoPX2Vycm9yX21lc3NhZ2Vz');
|
'Jlc3RGcmllbmQYAyABKAhSCmJlc3RGcmllbmQSIAoLZm9yY2VVcGRhdGUYBCABKAhSC2ZvcmNl'
|
||||||
|
'VXBkYXRlQgoKCF9ncm91cElkQg8KDV9pc0RpcmVjdENoYXRCFwoVX3NlbmRlclByb2ZpbGVDb3'
|
||||||
|
'VudGVyQhAKDl9tZXNzYWdlVXBkYXRlQggKBl9tZWRpYUIOCgxfbWVkaWFVcGRhdGVCEAoOX2Nv'
|
||||||
|
'bnRhY3RVcGRhdGVCEQoPX2NvbnRhY3RSZXF1ZXN0QgwKCl9mbGFtZVN5bmNCCwoJX3B1c2hLZX'
|
||||||
|
'lzQgsKCV9yZWFjdGlvbkIOCgxfdGV4dE1lc3NhZ2VCDgoMX2dyb3VwQ3JlYXRlQgwKCl9ncm91'
|
||||||
|
'cEpvaW5CDgoMX2dyb3VwVXBkYXRlQhcKFV9yZXNlbmRHcm91cFB1YmxpY0tleUIRCg9fZXJyb3'
|
||||||
|
'JfbWVzc2FnZXNCGgoYX2FkZGl0aW9uYWxfZGF0YV9tZXNzYWdl');
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ message EncryptedContent {
|
||||||
optional GroupUpdate groupUpdate = 16;
|
optional GroupUpdate groupUpdate = 16;
|
||||||
optional ResendGroupPublicKey resendGroupPublicKey = 17;
|
optional ResendGroupPublicKey resendGroupPublicKey = 17;
|
||||||
optional ErrorMessages error_messages = 18;
|
optional ErrorMessages error_messages = 18;
|
||||||
|
optional AdditionalDataMessage additional_data_message = 19;
|
||||||
|
|
||||||
message ErrorMessages {
|
message ErrorMessages {
|
||||||
enum Type {
|
enum Type {
|
||||||
|
|
@ -93,6 +93,13 @@ message EncryptedContent {
|
||||||
optional string quoteMessageId = 4;
|
optional string quoteMessageId = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message AdditionalDataMessage {
|
||||||
|
string sender_message_id = 1;
|
||||||
|
int64 timestamp = 2;
|
||||||
|
string type = 3;
|
||||||
|
optional bytes additional_message_data = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message Reaction {
|
message Reaction {
|
||||||
string targetMessageId = 1;
|
string targetMessageId = 1;
|
||||||
string emoji = 2;
|
string emoji = 2;
|
||||||
|
|
|
||||||
285
lib/src/providers/routing.provider.dart
Normal file
285
lib/src/providers/routing.provider.dart
Normal file
|
|
@ -0,0 +1,285 @@
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:twonly/app.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/views/camera/camera_qr_scanner.view.dart';
|
||||||
|
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
||||||
|
import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
||||||
|
import 'package:twonly/src/views/chats/archived_chats.view.dart';
|
||||||
|
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
||||||
|
import 'package:twonly/src/views/chats/media_viewer.view.dart';
|
||||||
|
import 'package:twonly/src/views/chats/start_new_chat.view.dart';
|
||||||
|
import 'package:twonly/src/views/contact/contact.view.dart';
|
||||||
|
import 'package:twonly/src/views/groups/group.view.dart';
|
||||||
|
import 'package:twonly/src/views/groups/group_create_select_members.view.dart';
|
||||||
|
import 'package:twonly/src/views/onboarding/recover.view.dart';
|
||||||
|
import 'package:twonly/src/views/public_profile.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/account.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/appearance.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/backup/backup.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/backup/backup_server.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/backup/setup_backup.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/chat/chat_reactions.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/chat/chat_settings.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/data_and_storage.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/data_and_storage/export_media.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/data_and_storage/import_media.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/developer/automated_testing.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/developer/developer.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/developer/retransmission_data.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/help/changelog.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/help/contact_us.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/help/credits.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/help/diagnostics.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/help/faq.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/help/help.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/notification.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/privacy.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/privacy_view_block.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/profile/modify_avatar.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/profile/profile.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/settings_main.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/share_with_friends.view.dart';
|
||||||
|
import 'package:twonly/src/views/settings/subscription/subscription.view.dart';
|
||||||
|
import 'package:twonly/src/views/user_study/user_study_questionnaire.view.dart';
|
||||||
|
import 'package:twonly/src/views/user_study/user_study_welcome.view.dart';
|
||||||
|
|
||||||
|
final routerProvider = GoRouter(
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: Routes.home,
|
||||||
|
builder: (context, state) => const AppMainWidget(initialPage: 1),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Chats
|
||||||
|
GoRoute(
|
||||||
|
path: Routes.chats,
|
||||||
|
builder: (context, state) => const AppMainWidget(initialPage: 0),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'add_new_user',
|
||||||
|
builder: (context, state) => const AddNewUserView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'archived',
|
||||||
|
builder: (context, state) => const ArchivedChatsView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'start_new_chat',
|
||||||
|
builder: (context, state) => const StartNewChatView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'camera_send_to',
|
||||||
|
builder: (context, state) {
|
||||||
|
final group = state.extra! as Group;
|
||||||
|
return CameraSendToView(group);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'media_viewer',
|
||||||
|
builder: (context, state) {
|
||||||
|
final group = state.extra! as Group;
|
||||||
|
return MediaViewerView(group);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'messages',
|
||||||
|
builder: (context, state) {
|
||||||
|
final group = state.extra! as Group;
|
||||||
|
return ChatMessagesView(group);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
GoRoute(
|
||||||
|
path: '/profile/contact/:contactId',
|
||||||
|
builder: (context, state) {
|
||||||
|
final contactId = state.pathParameters['contactId']!;
|
||||||
|
return ContactView(int.parse(contactId));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
GoRoute(
|
||||||
|
path: '/profile/group/:groupId',
|
||||||
|
builder: (context, state) {
|
||||||
|
final groupId = state.pathParameters['groupId']!;
|
||||||
|
return GroupView(groupId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
GoRoute(
|
||||||
|
path: '/group/create/select_member',
|
||||||
|
builder: (context, state) {
|
||||||
|
return const GroupCreateSelectMembersView();
|
||||||
|
},
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: ':groupId',
|
||||||
|
builder: (context, state) {
|
||||||
|
final groupId = state.pathParameters['groupId'];
|
||||||
|
return GroupCreateSelectMembersView(groupId: groupId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
GoRoute(
|
||||||
|
path: Routes.cameraQRScanner,
|
||||||
|
builder: (context, state) {
|
||||||
|
return const QrCodeScannerView();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
// settings
|
||||||
|
GoRoute(
|
||||||
|
path: Routes.settings,
|
||||||
|
builder: (context, state) => const SettingsMainView(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'profile',
|
||||||
|
builder: (context, state) => const ProfileView(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'modify_avatar',
|
||||||
|
builder: (context, state) => const ModifyAvatarView(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'public_profile',
|
||||||
|
builder: (context, state) => const PublicProfileView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'account',
|
||||||
|
builder: (context, state) => const AccountView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'subscription',
|
||||||
|
builder: (context, state) => const SubscriptionView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'backup',
|
||||||
|
builder: (context, state) => const BackupView(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'server',
|
||||||
|
builder: (context, state) => const BackupServerView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'recovery',
|
||||||
|
builder: (context, state) => const BackupRecoveryView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'setup',
|
||||||
|
builder: (context, state) => SetupBackupView(
|
||||||
|
isPasswordChangeOnly: state.extra as bool? ?? false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'appearance',
|
||||||
|
builder: (context, state) => const AppearanceView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'chats',
|
||||||
|
builder: (context, state) => const ChatSettingsView(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'reactions',
|
||||||
|
builder: (context, state) => const ChatReactionSelectionView(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'privacy',
|
||||||
|
builder: (context, state) => const PrivacyView(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'block_users',
|
||||||
|
builder: (context, state) => const PrivacyViewBlockUsersView(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'notification',
|
||||||
|
builder: (context, state) => const NotificationView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'storage_data',
|
||||||
|
builder: (context, state) => const DataAndStorageView(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'import',
|
||||||
|
builder: (context, state) => const ImportMediaView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'export',
|
||||||
|
builder: (context, state) => const ExportMediaView(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'help',
|
||||||
|
builder: (context, state) => const HelpView(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'faq',
|
||||||
|
builder: (context, state) => const FaqView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'contact_us',
|
||||||
|
builder: (context, state) => const ContactUsView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'diagnostics',
|
||||||
|
builder: (context, state) => const DiagnosticsView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'user_study',
|
||||||
|
builder: (context, state) => UserStudyWelcomeView(
|
||||||
|
wasOpenedAutomatic: state.extra as bool? ?? false,
|
||||||
|
),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'questionnaire',
|
||||||
|
builder: (context, state) =>
|
||||||
|
const UserStudyQuestionnaireView(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'credits',
|
||||||
|
builder: (context, state) => const CreditsView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'changelog',
|
||||||
|
builder: (context, state) => ChangeLogView(
|
||||||
|
changeLog: state.extra as String?,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'developer',
|
||||||
|
builder: (context, state) => const DeveloperSettingsView(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'retransmission_database',
|
||||||
|
builder: (context, state) => const RetransmissionDataView(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'automated_testing',
|
||||||
|
builder: (context, state) => const AutomatedTestingView(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'invite',
|
||||||
|
builder: (context, state) => const ShareWithFriendsView(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
@ -153,9 +153,8 @@ class ApiService {
|
||||||
if (connectivitySubscription != null) {
|
if (connectivitySubscription != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
connectivitySubscription = Connectivity()
|
connectivitySubscription =
|
||||||
.onConnectivityChanged
|
Connectivity().onConnectivityChanged.listen((result) async {
|
||||||
.listen((List<ConnectivityResult> result) async {
|
|
||||||
if (!result.contains(ConnectivityResult.none)) {
|
if (!result.contains(ConnectivityResult.none)) {
|
||||||
await connect();
|
await connect();
|
||||||
}
|
}
|
||||||
|
|
@ -189,8 +188,7 @@ class ApiService {
|
||||||
bool get isConnected => _channel != null && _channel!.closeCode != null;
|
bool get isConnected => _channel != null && _channel!.closeCode != null;
|
||||||
|
|
||||||
Future<void> _onDone() async {
|
Future<void> _onDone() async {
|
||||||
Log.info('websocket closed without error');
|
_reconnectionDelay = 3;
|
||||||
_reconnectionDelay = 60 * 2; // the server closed the connection...
|
|
||||||
await onClosed();
|
await onClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -408,7 +406,7 @@ class ApiService {
|
||||||
}
|
}
|
||||||
if (result.isError) {
|
if (result.isError) {
|
||||||
if (result.error != ErrorCode.AuthTokenNotValid) {
|
if (result.error != ErrorCode.AuthTokenNotValid) {
|
||||||
Log.error('got error while authenticating to the server', result);
|
Log.error('got error while authenticating to the server: $result');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
lib/src/services/api/client2client/additional_data.c2c.dart
Normal file
36
lib/src/services/api/client2client/additional_data.c2c.dart
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import 'package:clock/clock.dart' show clock;
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
|
Future<void> handleAdditionalDataMessage(
|
||||||
|
int fromUserId,
|
||||||
|
String groupId,
|
||||||
|
EncryptedContent_AdditionalDataMessage message,
|
||||||
|
) async {
|
||||||
|
Log.info(
|
||||||
|
'Got a additional data message: ${message.senderMessageId} from $groupId',
|
||||||
|
);
|
||||||
|
final msg = await twonlyDB.messagesDao.insertMessage(
|
||||||
|
MessagesCompanion(
|
||||||
|
messageId: Value(message.senderMessageId),
|
||||||
|
senderId: Value(fromUserId),
|
||||||
|
groupId: Value(groupId),
|
||||||
|
type: Value(message.type),
|
||||||
|
additionalMessageData:
|
||||||
|
Value(Uint8List.fromList(message.additionalMessageData)),
|
||||||
|
createdAt: Value(fromTimestamp(message.timestamp)),
|
||||||
|
ackByServer: Value(clock.now()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await twonlyDB.groupsDao.increaseLastMessageExchange(
|
||||||
|
groupId,
|
||||||
|
fromTimestamp(message.timestamp),
|
||||||
|
);
|
||||||
|
if (msg != null) {
|
||||||
|
Log.info('Inserted a new text message with ID: ${msg.messageId}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -71,12 +71,14 @@ Future<bool> handleContactRequest(
|
||||||
final contact = await twonlyDB.contactsDao
|
final contact = await twonlyDB.contactsDao
|
||||||
.getContactByUserId(fromUserId)
|
.getContactByUserId(fromUserId)
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
|
if (contact != null) {
|
||||||
await twonlyDB.groupsDao.createNewDirectChat(
|
await twonlyDB.groupsDao.createNewDirectChat(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
GroupsCompanion(
|
GroupsCompanion(
|
||||||
groupName: Value(getContactDisplayName(contact!)),
|
groupName: Value(getContactDisplayName(contact)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
case EncryptedContent_ContactRequest_Type.REJECT:
|
case EncryptedContent_ContactRequest_Type.REJECT:
|
||||||
Log.info('Got a contact reject from $fromUserId');
|
Log.info('Got a contact reject from $fromUserId');
|
||||||
await twonlyDB.contactsDao.updateContact(
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,15 @@ Future<void> handleMedia(
|
||||||
if (message == null ||
|
if (message == null ||
|
||||||
message.senderId != fromUserId ||
|
message.senderId != fromUserId ||
|
||||||
message.mediaId == null) {
|
message.mediaId == null) {
|
||||||
|
Log.warn(
|
||||||
|
'Got reupload for a message that either does not exists or sender != fromUserId or not a media file',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// in case there was already a downloaded file delete it...
|
// in case there was already a downloaded file delete it...
|
||||||
final mediaService = await MediaFileService.fromMediaId(message.mediaId!);
|
final mediaService = await MediaFileService.fromMediaId(message.mediaId!);
|
||||||
if (mediaService != null) {
|
if (mediaService != null && mediaService.tempPath.existsSync()) {
|
||||||
mediaService.tempPath.deleteSync();
|
mediaService.tempPath.deleteSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +71,14 @@ Future<void> handleMedia(
|
||||||
mediaType = MediaType.audio;
|
mediaType = MediaType.audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final messageTmp = await twonlyDB.messagesDao
|
||||||
|
.getMessageById(media.senderMessageId)
|
||||||
|
.getSingleOrNull();
|
||||||
|
if (messageTmp != null) {
|
||||||
|
Log.warn('This message already exit. Message is dropped.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int? displayLimitInMilliseconds;
|
int? displayLimitInMilliseconds;
|
||||||
if (media.hasDisplayLimitInMilliseconds()) {
|
if (media.hasDisplayLimitInMilliseconds()) {
|
||||||
if (media.displayLimitInMilliseconds.toInt() < 1000) {
|
if (media.displayLimitInMilliseconds.toInt() < 1000) {
|
||||||
|
|
@ -105,7 +116,7 @@ Future<void> handleMedia(
|
||||||
senderId: Value(fromUserId),
|
senderId: Value(fromUserId),
|
||||||
groupId: Value(groupId),
|
groupId: Value(groupId),
|
||||||
mediaId: Value(mediaFile.mediaId),
|
mediaId: Value(mediaFile.mediaId),
|
||||||
type: const Value(MessageType.media),
|
type: Value(MessageType.media.name),
|
||||||
additionalMessageData: Value.absentIfNull(
|
additionalMessageData: Value.absentIfNull(
|
||||||
media.hasAdditionalMessageData()
|
media.hasAdditionalMessageData()
|
||||||
? Uint8List.fromList(media.additionalMessageData)
|
? Uint8List.fromList(media.additionalMessageData)
|
||||||
|
|
@ -139,12 +150,21 @@ Future<void> handleMediaUpdate(
|
||||||
.getMessageById(mediaUpdate.targetMessageId)
|
.getMessageById(mediaUpdate.targetMessageId)
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
Log.error(
|
// this can happen, in case the message was already deleted.
|
||||||
|
Log.info(
|
||||||
'Got media update to message ${mediaUpdate.targetMessageId} but message not found.',
|
'Got media update to message ${mediaUpdate.targetMessageId} but message not found.',
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.mediaId == null) {
|
||||||
|
// this can happen, in case the message was already deleted.
|
||||||
|
Log.warn(
|
||||||
|
'Got media update for message ${mediaUpdate.targetMessageId} which does not have a mediaId defined.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
final mediaFile =
|
final mediaFile =
|
||||||
await twonlyDB.mediaFilesDao.getMediaFileById(message!.mediaId!);
|
await twonlyDB.mediaFilesDao.getMediaFileById(message.mediaId!);
|
||||||
if (mediaFile == null) {
|
if (mediaFile == null) {
|
||||||
Log.info(
|
Log.info(
|
||||||
'Got media file update, but media file was not found ${message.mediaId}',
|
'Got media file update, but media file was not found ${message.mediaId}',
|
||||||
|
|
@ -183,6 +203,13 @@ Future<void> handleMediaUpdate(
|
||||||
reuploadRequestedBy: Value(reuploadRequestedBy),
|
reuploadRequestedBy: Value(reuploadRequestedBy),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
unawaited(startBackgroundMediaUpload(MediaFileService(mediaFile)));
|
final mediaFileUpdated =
|
||||||
|
await MediaFileService.fromMediaId(mediaFile.mediaId);
|
||||||
|
if (mediaFileUpdated != null) {
|
||||||
|
if (mediaFileUpdated.uploadRequestPath.existsSync()) {
|
||||||
|
mediaFileUpdated.uploadRequestPath.deleteSync();
|
||||||
|
}
|
||||||
|
unawaited(startBackgroundMediaUpload(mediaFileUpdated));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ Future<void> handleTextMessage(
|
||||||
senderId: Value(fromUserId),
|
senderId: Value(fromUserId),
|
||||||
groupId: Value(groupId),
|
groupId: Value(groupId),
|
||||||
content: Value(textMessage.text),
|
content: Value(textMessage.text),
|
||||||
type: const Value(MessageType.text),
|
type: Value(MessageType.text.name),
|
||||||
quotesMessageId: Value(
|
quotesMessageId: Value(
|
||||||
textMessage.hasQuoteMessageId() ? textMessage.quoteMessageId : null,
|
textMessage.hasQuoteMessageId() ? textMessage.quoteMessageId : null,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -231,9 +231,7 @@ Future<void> downloadFileFast(
|
||||||
await handleEncryptedFile(media.mediaId);
|
await handleEncryptedFile(media.mediaId);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (response.statusCode == 404 ||
|
if (response.statusCode == 404 || response.statusCode == 403) {
|
||||||
response.statusCode == 403 ||
|
|
||||||
response.statusCode == 400) {
|
|
||||||
Log.error(
|
Log.error(
|
||||||
'Got ${response.statusCode} from server. Requesting upload again',
|
'Got ${response.statusCode} from server. Requesting upload again',
|
||||||
);
|
);
|
||||||
|
|
@ -329,3 +327,11 @@ Future<void> handleEncryptedFile(String mediaId) async {
|
||||||
|
|
||||||
unawaited(apiService.downloadDone(mediaService.mediaFile.downloadToken!));
|
unawaited(apiService.downloadDone(mediaService.mediaFile.downloadToken!));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> makeMigrationToVersion91() async {
|
||||||
|
final messages =
|
||||||
|
await twonlyDB.mediaFilesDao.getAllMediaFilesReuploadRequested();
|
||||||
|
for (final message in messages) {
|
||||||
|
await requestMediaReupload(message.mediaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ Future<void> insertMediaFileInMessagesTable(
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
groupId: Value(groupId),
|
groupId: Value(groupId),
|
||||||
mediaId: Value(mediaService.mediaFile.mediaId),
|
mediaId: Value(mediaService.mediaFile.mediaId),
|
||||||
type: const Value(MessageType.media),
|
type: Value(MessageType.media.name),
|
||||||
additionalMessageData:
|
additionalMessageData:
|
||||||
Value.absentIfNull(additionalData?.writeToBuffer()),
|
Value.absentIfNull(additionalData?.writeToBuffer()),
|
||||||
),
|
),
|
||||||
|
|
@ -160,9 +160,11 @@ Future<void> startBackgroundMediaUpload(MediaFileService mediaService) async {
|
||||||
if (mediaService.uploadRequestPath.existsSync()) {
|
if (mediaService.uploadRequestPath.existsSync()) {
|
||||||
await mediaService.setUploadState(UploadState.uploading);
|
await mediaService.setUploadState(UploadState.uploading);
|
||||||
// at this point the original file is not used any more, so it can be deleted
|
// at this point the original file is not used any more, so it can be deleted
|
||||||
|
if (mediaService.originalPath.existsSync()) {
|
||||||
mediaService.originalPath.deleteSync();
|
mediaService.originalPath.deleteSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mediaService.mediaFile.uploadState == UploadState.uploading ||
|
if (mediaService.mediaFile.uploadState == UploadState.uploading ||
|
||||||
mediaService.mediaFile.uploadState == UploadState.uploadLimitReached) {
|
mediaService.mediaFile.uploadState == UploadState.uploadLimitReached) {
|
||||||
|
|
@ -238,6 +240,10 @@ Future<void> _createUploadRequest(MediaFileService media) async {
|
||||||
type = EncryptedContent_Media_Type.VIDEO;
|
type = EncryptedContent_Media_Type.VIDEO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (media.mediaFile.reuploadRequestedBy != null) {
|
||||||
|
type = EncryptedContent_Media_Type.REUPLOAD;
|
||||||
|
}
|
||||||
|
|
||||||
final notEncryptedContent = EncryptedContent(
|
final notEncryptedContent = EncryptedContent(
|
||||||
groupId: message.groupId,
|
groupId: message.groupId,
|
||||||
media: EncryptedContent_Media(
|
media: EncryptedContent_Media(
|
||||||
|
|
@ -372,7 +378,7 @@ Future<void> uploadFileFastOrEnqueue(
|
||||||
try {
|
try {
|
||||||
Log.info('Uploading fast: ${task.taskId}');
|
Log.info('Uploading fast: ${task.taskId}');
|
||||||
final response =
|
final response =
|
||||||
await requestMultipart.send().timeout(const Duration(seconds: 4));
|
await requestMultipart.send().timeout(const Duration(seconds: 8));
|
||||||
var status = TaskStatus.failed;
|
var status = TaskStatus.failed;
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
status = TaskStatus.complete;
|
status = TaskStatus.complete;
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,17 @@ import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
||||||
as pb;
|
as pb;
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
||||||
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
|
|
@ -89,7 +92,7 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.info('Uploading $receiptId (Message to ${receipt.contactId})');
|
Log.info('Uploading $receiptId');
|
||||||
|
|
||||||
final message = pb.Message.fromBuffer(receipt.message)
|
final message = pb.Message.fromBuffer(receipt.message)
|
||||||
..receiptId = receiptId;
|
..receiptId = receiptId;
|
||||||
|
|
@ -204,7 +207,7 @@ Future<void> insertAndSendTextMessage(
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
groupId: Value(groupId),
|
groupId: Value(groupId),
|
||||||
content: Value(textMessage),
|
content: Value(textMessage),
|
||||||
type: const Value(MessageType.text),
|
type: Value(MessageType.text.name),
|
||||||
quotesMessageId: Value(quotesMessageId),
|
quotesMessageId: Value(quotesMessageId),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -232,6 +235,61 @@ Future<void> insertAndSendTextMessage(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> insertAndSendContactShareMessage(
|
||||||
|
String groupId,
|
||||||
|
List<int> contactsToShare,
|
||||||
|
) async {
|
||||||
|
final contacts = <SharedContact>[];
|
||||||
|
|
||||||
|
for (final contactId in contactsToShare) {
|
||||||
|
final contact = await twonlyDB.contactsDao.getContactById(contactId);
|
||||||
|
if (contact != null) {
|
||||||
|
final publicIdentityKey = await getPublicKeyFromContact(contactId);
|
||||||
|
|
||||||
|
contacts.add(
|
||||||
|
SharedContact(
|
||||||
|
userId: Int64(contact.userId),
|
||||||
|
publicIdentityKey: publicIdentityKey,
|
||||||
|
displayName: getContactDisplayName(contact),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final additionalMessageData = AdditionalMessageData(
|
||||||
|
type: AdditionalMessageData_Type.CONTACTS,
|
||||||
|
contacts: contacts,
|
||||||
|
);
|
||||||
|
|
||||||
|
final message = await twonlyDB.messagesDao.insertMessage(
|
||||||
|
MessagesCompanion(
|
||||||
|
groupId: Value(groupId),
|
||||||
|
type: Value(MessageType.contacts.name),
|
||||||
|
additionalMessageData: Value(additionalMessageData.writeToBuffer()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (message == null) {
|
||||||
|
Log.error('Could not insert message into database');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final encryptedContent = pb.EncryptedContent(
|
||||||
|
additionalDataMessage: pb.EncryptedContent_AdditionalDataMessage(
|
||||||
|
senderMessageId: message.messageId,
|
||||||
|
additionalMessageData: additionalMessageData.writeToBuffer(),
|
||||||
|
timestamp: Int64(message.createdAt.millisecondsSinceEpoch),
|
||||||
|
type: MessageType.contacts.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await sendCipherTextToGroup(
|
||||||
|
groupId,
|
||||||
|
encryptedContent,
|
||||||
|
messageId: message.messageId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> sendCipherTextToGroup(
|
Future<void> sendCipherTextToGroup(
|
||||||
String groupId,
|
String groupId,
|
||||||
pb.EncryptedContent encryptedContent, {
|
pb.EncryptedContent encryptedContent, {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart
|
||||||
as server;
|
as server;
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pbserver.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pbserver.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
|
import 'package:twonly/src/services/api/client2client/additional_data.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/contact.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/contact.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/errors.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/errors.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/groups.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/groups.c2c.dart';
|
||||||
|
|
@ -90,6 +91,11 @@ Future<void> handleClient2ClientMessage(NewMessage newMessage) async {
|
||||||
var retry = false;
|
var retry = false;
|
||||||
if (message.hasPlaintextContent()) {
|
if (message.hasPlaintextContent()) {
|
||||||
if (message.plaintextContent.hasDecryptionErrorMessage()) {
|
if (message.plaintextContent.hasDecryptionErrorMessage()) {
|
||||||
|
if (message.plaintextContent.decryptionErrorMessage.type ==
|
||||||
|
PlaintextContent_DecryptionErrorMessage_Type.PREKEY_UNKNOWN) {
|
||||||
|
// in case there was a pre key error remove all downloaded pre keys. New pre keys will be fetched automatically.
|
||||||
|
await twonlyDB.signalDao.purgePreKeysFromContact(fromUserId);
|
||||||
|
}
|
||||||
Log.info(
|
Log.info(
|
||||||
'Got decryption error: ${message.plaintextContent.decryptionErrorMessage.type} for $receiptId',
|
'Got decryption error: ${message.plaintextContent.decryptionErrorMessage.type} for $receiptId',
|
||||||
);
|
);
|
||||||
|
|
@ -369,6 +375,15 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (content.hasAdditionalDataMessage()) {
|
||||||
|
await handleAdditionalDataMessage(
|
||||||
|
fromUserId,
|
||||||
|
content.groupId,
|
||||||
|
content.additionalDataMessage,
|
||||||
|
);
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
if (content.hasTextMessage()) {
|
if (content.hasTextMessage()) {
|
||||||
await handleTextMessage(
|
await handleTextMessage(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart'
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart'
|
||||||
hide Message;
|
hide Message;
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
|
|
||||||
class Result<T, E> {
|
class Result<T, E> {
|
||||||
Result.error(this.error) : value = null;
|
Result.error(this.error) : value = null;
|
||||||
|
|
@ -78,3 +80,23 @@ Future<void> handleMediaError(MediaFile media) async {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> importSignalContactAndCreateRequest(
|
||||||
|
server.Response_UserData userdata,
|
||||||
|
) async {
|
||||||
|
if (await createNewSignalSession(userdata)) {
|
||||||
|
// 1. Setup notifications keys with the other user
|
||||||
|
await setupNotificationWithUsers(
|
||||||
|
forceContact: userdata.userId.toInt(),
|
||||||
|
);
|
||||||
|
// 2. Then send user request
|
||||||
|
await sendCipherText(
|
||||||
|
userdata.userId.toInt(),
|
||||||
|
EncryptedContent(
|
||||||
|
contactRequest: EncryptedContent_ContactRequest(
|
||||||
|
type: EncryptedContent_ContactRequest_Type.REQUEST,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ Future<void> initFCMService() async {
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||||
initLogger();
|
initLogger();
|
||||||
Log.info('Handling a background message: ${message.messageId}');
|
// Log.info('Handling a background message: ${message.messageId}');
|
||||||
await handleRemoteMessage(message);
|
await handleRemoteMessage(message);
|
||||||
// make sure every thing run...
|
// make sure every thing run...
|
||||||
await Future.delayed(const Duration(milliseconds: 2000));
|
await Future.delayed(const Duration(milliseconds: 2000));
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,11 @@ import 'dart:typed_data';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_sharing_intent/flutter_sharing_intent.dart';
|
||||||
import 'package:flutter_sharing_intent/model/sharing_file.dart';
|
import 'package:flutter_sharing_intent/model/sharing_file.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
|
|
@ -16,8 +19,6 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||||
import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
|
||||||
import 'package:twonly/src/views/public_profile.view.dart';
|
|
||||||
|
|
||||||
Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
||||||
if (!uri.scheme.startsWith('http')) return false;
|
if (!uri.scheme.startsWith('http')) return false;
|
||||||
|
|
@ -32,14 +33,7 @@ Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
||||||
if (!context.mounted) return false;
|
if (!context.mounted) return false;
|
||||||
|
|
||||||
if (username == gUser.username) {
|
if (username == gUser.username) {
|
||||||
await Navigator.push(
|
await context.push(Routes.settingsPublicProfile);
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const PublicProfileView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,14 +85,7 @@ Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await Navigator.push(
|
await context.push(Routes.profileContact(contact.userId));
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return ContactView(contact.userId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await showAlertDialog(
|
await showAlertDialog(
|
||||||
|
|
@ -149,6 +136,27 @@ Future<void> handleIntentMediaFile(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StreamSubscription<List<SharedFile>> initIntentStreams(
|
||||||
|
BuildContext context,
|
||||||
|
void Function(Uri) onUrlCallBack,
|
||||||
|
) {
|
||||||
|
FlutterSharingIntent.instance.getInitialSharing().then((f) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
handleIntentSharedFile(context, f, onUrlCallBack);
|
||||||
|
});
|
||||||
|
|
||||||
|
return FlutterSharingIntent.instance.getMediaStream().listen(
|
||||||
|
(f) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
handleIntentSharedFile(context, f, onUrlCallBack);
|
||||||
|
},
|
||||||
|
// ignore: inference_failure_on_untyped_parameter
|
||||||
|
onError: (err) {
|
||||||
|
Log.error('getIntentDataStream error: $err');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> handleIntentSharedFile(
|
Future<void> handleIntentSharedFile(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
List<SharedFile> files,
|
List<SharedFile> files,
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,12 @@ Future<void> createThumbnailsForVideo(
|
||||||
) async {
|
) async {
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
|
if (destinationFile.existsSync()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final command =
|
final command =
|
||||||
'-i "${sourceFile.path}" -ss 00:00:00 -vframes 1 -vf "scale=iw:ih:flags=lanczos" -c:v libwebp -q:v 100 -compression_level 6 "${destinationFile.path}"';
|
'-y -i "${sourceFile.path}" -ss 00:00:00 -vframes 1 -vf "scale=iw:ih:flags=lanczos" -c:v libwebp -q:v 100 -compression_level 6 "${destinationFile.path}"';
|
||||||
|
|
||||||
final session = await FFmpegKit.execute(command);
|
final session = await FFmpegKit.execute(command);
|
||||||
final returnCode = await session.getReturnCode();
|
final returnCode = await session.getReturnCode();
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,11 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
|
||||||
additionalContent = group.groupName;
|
additionalContent = group.groupName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (content.hasAdditionalDataMessage()) {
|
||||||
|
kind = PushKind.text;
|
||||||
|
}
|
||||||
|
|
||||||
if (content.hasMedia()) {
|
if (content.hasMedia()) {
|
||||||
switch (content.media.type) {
|
switch (content.media.type) {
|
||||||
case EncryptedContent_Media_Type.REUPLOAD:
|
case EncryptedContent_Media_Type.REUPLOAD:
|
||||||
|
|
|
||||||
13
lib/src/themes/dark.dart
Normal file
13
lib/src/themes/dark.dart
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final ThemeData darkTheme = ThemeData.dark().copyWith(
|
||||||
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
seedColor: const Color(0xFF57CC99),
|
||||||
|
surface: const Color.fromARGB(255, 20, 18, 23),
|
||||||
|
surfaceContainer: const Color.fromARGB(255, 33, 30, 39),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
);
|
||||||
10
lib/src/themes/light.dart
Normal file
10
lib/src/themes/light.dart
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final ThemeData lightTheme = ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFF57CC99),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
@ -2,7 +2,6 @@ import 'dart:io';
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -66,8 +65,6 @@ class Log {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex writeToLogGuard = Mutex();
|
|
||||||
|
|
||||||
Future<String> loadLogFile() async {
|
Future<String> loadLogFile() async {
|
||||||
final directory = await getApplicationSupportDirectory();
|
final directory = await getApplicationSupportDirectory();
|
||||||
final logFile = File('${directory.path}/app.log');
|
final logFile = File('${directory.path}/app.log');
|
||||||
|
|
@ -95,14 +92,23 @@ Future<void> _writeLogToFile(LogRecord record) async {
|
||||||
logFile.createSync(recursive: true);
|
logFile.createSync(recursive: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the log message
|
|
||||||
final logMessage =
|
final logMessage =
|
||||||
'${clock.now().toString().split(".")[0]} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n';
|
'${clock.now().toString().split(".")[0]} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n';
|
||||||
|
|
||||||
await writeToLogGuard.protect(() async {
|
final raf = await logFile.open(mode: FileMode.writeOnlyAppend);
|
||||||
// Append the log message to the file
|
|
||||||
await logFile.writeAsString(logMessage, mode: FileMode.append);
|
try {
|
||||||
});
|
// Use FileLock.blockingExclusive to wait until the lock is available
|
||||||
|
await raf.lock(FileLock.blockingExclusive);
|
||||||
|
await raf.writeString(logMessage);
|
||||||
|
await raf.flush();
|
||||||
|
} catch (e) {
|
||||||
|
// ignore: avoid_print
|
||||||
|
print('Error during file access: $e');
|
||||||
|
} finally {
|
||||||
|
await raf.unlock();
|
||||||
|
await raf.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> cleanLogFile() async {
|
Future<void> cleanLogFile() async {
|
||||||
|
|
@ -112,10 +118,10 @@ Future<void> cleanLogFile() async {
|
||||||
if (logFile.existsSync()) {
|
if (logFile.existsSync()) {
|
||||||
final lines = await logFile.readAsLines();
|
final lines = await logFile.readAsLines();
|
||||||
|
|
||||||
if (lines.length <= 5000) return;
|
if (lines.length <= 10000) return;
|
||||||
|
|
||||||
final removeCount = lines.length - 5000;
|
final removeCount = lines.length - 10000;
|
||||||
final remaining = lines.sublist(removeCount);
|
final remaining = lines.sublist(removeCount, lines.length);
|
||||||
|
|
||||||
final sink = logFile.openWrite()..writeAll(remaining, '\n');
|
final sink = logFile.openWrite()..writeAll(remaining, '\n');
|
||||||
await sink.close();
|
await sink.close();
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,16 @@ extension ShortCutsExtension on BuildContext {
|
||||||
AppLocalizations get lang => AppLocalizations.of(this)!;
|
AppLocalizations get lang => AppLocalizations.of(this)!;
|
||||||
TwonlyDB get db => Provider.of<TwonlyDB>(this);
|
TwonlyDB get db => Provider.of<TwonlyDB>(this);
|
||||||
ColorScheme get color => Theme.of(this).colorScheme;
|
ColorScheme get color => Theme.of(this).colorScheme;
|
||||||
|
Future<dynamic> navPush(Widget route) async {
|
||||||
|
return Navigator.push(
|
||||||
|
this,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return route;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> saveImageToGallery(Uint8List imageBytes) async {
|
Future<String?> saveImageToGallery(Uint8List imageBytes) async {
|
||||||
|
|
@ -292,7 +302,7 @@ Color getMessageColorFromType(
|
||||||
) {
|
) {
|
||||||
Color color;
|
Color color;
|
||||||
|
|
||||||
if (message.type == MessageType.text) {
|
if (message.type == MessageType.text.name) {
|
||||||
color = Colors.blueAccent;
|
color = Colors.blueAccent;
|
||||||
} else if (mediaFile != null) {
|
} else if (mediaFile != null) {
|
||||||
if (mediaFile.requiresAuthentication) {
|
if (mediaFile.requiresAuthentication) {
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,9 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
|
||||||
import 'package:twonly/src/services/signal/identity.signal.dart';
|
import 'package:twonly/src/services/signal/identity.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
|
||||||
import 'package:twonly/src/services/signal/utils.signal.dart';
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
|
|
||||||
Future<Uint8List> getProfileQrCodeData() async {
|
Future<Uint8List> getProfileQrCodeData() async {
|
||||||
|
|
@ -80,21 +77,5 @@ Future<void> addNewContactFromPublicProfile(PublicProfile profile) async {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (added > 0) {
|
if (added > 0) await importSignalContactAndCreateRequest(userdata);
|
||||||
if (await createNewSignalSession(userdata)) {
|
|
||||||
// 1. Setup notifications keys with the other user
|
|
||||||
await setupNotificationWithUsers(
|
|
||||||
forceContact: userdata.userId.toInt(),
|
|
||||||
);
|
|
||||||
// 2. Then send user request
|
|
||||||
await sendCipherText(
|
|
||||||
userdata.userId.toInt(),
|
|
||||||
EncryptedContent(
|
|
||||||
contactRequest: EncryptedContent_ContactRequest(
|
|
||||||
type: EncryptedContent_ContactRequest_Type.REQUEST,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ import 'package:twonly/src/views/camera/camera_preview_components/camera_preview
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
|
|
||||||
class QrCodeScanner extends StatefulWidget {
|
class QrCodeScannerView extends StatefulWidget {
|
||||||
const QrCodeScanner({super.key});
|
const QrCodeScannerView({super.key});
|
||||||
@override
|
@override
|
||||||
State<QrCodeScanner> createState() => QrCodeScannerState();
|
State<QrCodeScannerView> createState() => QrCodeScannerViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class QrCodeScannerState extends State<QrCodeScanner> {
|
class QrCodeScannerViewState extends State<QrCodeScannerView> {
|
||||||
final MainCameraController _mainCameraController = MainCameraController();
|
final MainCameraController _mainCameraController = MainCameraController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
// ignore_for_file: strict_raw_type
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -208,7 +206,7 @@ class _ShareImageView extends State<ShareImageView> {
|
||||||
child: Checkbox(
|
child: Checkbox(
|
||||||
value: !hideArchivedUsers,
|
value: !hideArchivedUsers,
|
||||||
side: WidgetStateBorderSide.resolveWith(
|
side: WidgetStateBorderSide.resolveWith(
|
||||||
(Set states) {
|
(states) {
|
||||||
if (states.contains(WidgetState.selected)) {
|
if (states.contains(WidgetState.selected)) {
|
||||||
return const BorderSide(width: 0);
|
return const BorderSide(width: 0);
|
||||||
}
|
}
|
||||||
|
|
@ -344,7 +342,7 @@ class UserList extends StatelessWidget {
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
restorationId: 'new_message_users_list',
|
restorationId: 'new_message_users_list',
|
||||||
itemCount: groups.length,
|
itemCount: groups.length,
|
||||||
itemBuilder: (BuildContext context, int i) {
|
itemBuilder: (context, i) {
|
||||||
final group = groups[i];
|
final group = groups[i];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
key: ValueKey(group.groupId),
|
key: ValueKey(group.groupId),
|
||||||
|
|
@ -364,14 +362,14 @@ class UserList extends StatelessWidget {
|
||||||
trailing: Checkbox(
|
trailing: Checkbox(
|
||||||
value: selectedGroupIds.contains(group.groupId),
|
value: selectedGroupIds.contains(group.groupId),
|
||||||
side: WidgetStateBorderSide.resolveWith(
|
side: WidgetStateBorderSide.resolveWith(
|
||||||
(Set states) {
|
(states) {
|
||||||
if (states.contains(WidgetState.selected)) {
|
if (states.contains(WidgetState.selected)) {
|
||||||
return const BorderSide(width: 0);
|
return const BorderSide(width: 0);
|
||||||
}
|
}
|
||||||
return BorderSide(color: Theme.of(context).colorScheme.outline);
|
return BorderSide(color: Theme.of(context).colorScheme.outline);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
onChanged: (bool? value) {
|
onChanged: (value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
updateSelectedGroupIds(group.groupId, value);
|
updateSelectedGroupIds(group.groupId, value);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -163,8 +163,7 @@ class UserCheckbox extends StatelessWidget {
|
||||||
Checkbox(
|
Checkbox(
|
||||||
value: isChecked,
|
value: isChecked,
|
||||||
side: WidgetStateBorderSide.resolveWith(
|
side: WidgetStateBorderSide.resolveWith(
|
||||||
// ignore: strict_raw_type
|
(states) {
|
||||||
(Set states) {
|
|
||||||
if (states.contains(WidgetState.selected)) {
|
if (states.contains(WidgetState.selected)) {
|
||||||
return const BorderSide(width: 0);
|
return const BorderSide(width: 0);
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +172,7 @@ class UserCheckbox extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
onChanged: (bool? value) {
|
onChanged: (value) {
|
||||||
onChanged(group.groupId, value ?? false);
|
onChanged(group.groupId, value ?? false);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class _SelectShowTimeState extends State<SelectShowTime> {
|
||||||
scrollController: FixedExtentScrollController(
|
scrollController: FixedExtentScrollController(
|
||||||
initialItem: widget.initialItem,
|
initialItem: widget.initialItem,
|
||||||
),
|
),
|
||||||
onSelectedItemChanged: (int selectedItem) {
|
onSelectedItemChanged: (selectedItem) {
|
||||||
_selectedItem = selectedItem;
|
_selectedItem = selectedItem;
|
||||||
widget.setMaxShowTime(
|
widget.setMaxShowTime(
|
||||||
widget.options[selectedItem],
|
widget.options[selectedItem],
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return SelectShowTime(
|
return SelectShowTime(
|
||||||
initialItem: initialItem,
|
initialItem: initialItem,
|
||||||
setMaxShowTime: _setMaxShowTime,
|
setMaxShowTime: _setMaxShowTime,
|
||||||
|
|
@ -249,7 +249,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
final layer = await showModalBottomSheet(
|
final layer = await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return const EmojiPickerBottom();
|
return const EmojiPickerBottom();
|
||||||
},
|
},
|
||||||
) as Layer?;
|
) as Layer?;
|
||||||
|
|
@ -315,7 +315,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
Future<bool?> _showBackDialog() {
|
Future<bool?> _showBackDialog() {
|
||||||
return showDialog<bool>(
|
return showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(
|
title: Text(
|
||||||
context.lang.dialogAskDeleteMediaFilePopTitle,
|
context.lang.dialogAskDeleteMediaFilePopTitle,
|
||||||
|
|
@ -559,7 +559,6 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
if (imageLayer.imageLoaded) {
|
if (imageLayer.imageLoaded) {
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
Future.delayed(const Duration(milliseconds: 50), () {
|
Future.delayed(const Duration(milliseconds: 50), () {
|
||||||
Log.info(imageLayer.imageLoaded);
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
sendingOrLoadingImage = false;
|
sendingOrLoadingImage = false;
|
||||||
|
|
@ -615,7 +614,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
|
|
||||||
return PopScope<bool?>(
|
return PopScope<bool?>(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvokedWithResult: (bool didPop, bool? result) async {
|
onPopInvokedWithResult: (didPop, result) async {
|
||||||
if (didPop) return;
|
if (didPop) return;
|
||||||
await askToCloseThenClose();
|
await askToCloseThenClose();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
|
|
@ -110,23 +109,7 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (added > 0) {
|
if (added > 0) await importSignalContactAndCreateRequest(userdata);
|
||||||
if (await createNewSignalSession(userdata)) {
|
|
||||||
// 1. Setup notifications keys with the other user
|
|
||||||
await setupNotificationWithUsers(
|
|
||||||
forceContact: userdata.userId.toInt(),
|
|
||||||
);
|
|
||||||
// 2. Then send user request
|
|
||||||
await sendCipherText(
|
|
||||||
userdata.userId.toInt(),
|
|
||||||
EncryptedContent(
|
|
||||||
contactRequest: EncryptedContent_ContactRequest(
|
|
||||||
type: EncryptedContent_ContactRequest_Type.REQUEST,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDecoration getInputDecoration(String hintText) {
|
InputDecoration getInputDecoration(String hintText) {
|
||||||
|
|
@ -212,7 +195,7 @@ class ContactsListView extends StatelessWidget {
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: const FaIcon(Icons.archive_outlined, size: 15),
|
icon: const FaIcon(Icons.archive_outlined, size: 15),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
const update = ContactsCompanion(requested: Value(false));
|
const update = ContactsCompanion(deletedByUser: Value(true));
|
||||||
await twonlyDB.contactsDao.updateContact(contact.userId, update);
|
await twonlyDB.contactsDao.updateContact(contact.userId, update);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -4,28 +4,21 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/providers/connection.provider.dart';
|
import 'package:twonly/src/providers/connection.provider.dart';
|
||||||
import 'package:twonly/src/providers/purchases.provider.dart';
|
import 'package:twonly/src/providers/purchases.provider.dart';
|
||||||
import 'package:twonly/src/services/subscription.service.dart';
|
import 'package:twonly/src/services/subscription.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
|
||||||
import 'package:twonly/src/views/chats/archived_chats.view.dart';
|
|
||||||
import 'package:twonly/src/views/chats/chat_list_components/connection_info.comp.dart';
|
import 'package:twonly/src/views/chats/chat_list_components/connection_info.comp.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_list_components/feedback_btn.dart';
|
import 'package:twonly/src/views/chats/chat_list_components/feedback_btn.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_list_components/group_list_item.dart';
|
import 'package:twonly/src/views/chats/chat_list_components/group_list_item.dart';
|
||||||
import 'package:twonly/src/views/chats/start_new_chat.view.dart';
|
|
||||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
import 'package:twonly/src/views/components/notification_badge.dart';
|
import 'package:twonly/src/views/components/notification_badge.dart';
|
||||||
import 'package:twonly/src/views/public_profile.view.dart';
|
|
||||||
import 'package:twonly/src/views/settings/help/changelog.view.dart';
|
|
||||||
import 'package:twonly/src/views/settings/profile/profile.view.dart';
|
|
||||||
import 'package:twonly/src/views/settings/settings_main.view.dart';
|
|
||||||
import 'package:twonly/src/views/settings/subscription/subscription.view.dart';
|
|
||||||
import 'package:twonly/src/views/user_study/user_study_welcome.view.dart';
|
|
||||||
|
|
||||||
class ChatListView extends StatefulWidget {
|
class ChatListView extends StatefulWidget {
|
||||||
const ChatListView({super.key});
|
const ChatListView({super.key});
|
||||||
|
|
@ -64,15 +57,9 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
if (gUser.subscriptionPlan == SubscriptionPlan.Tester.name &&
|
if (gUser.subscriptionPlan == SubscriptionPlan.Tester.name &&
|
||||||
!gUser.askedForUserStudyPermission) {
|
!gUser.askedForUserStudyPermission) {
|
||||||
await Navigator.push(
|
await context.push(
|
||||||
context,
|
Routes.settingsHelpUserStudy,
|
||||||
MaterialPageRoute(
|
extra: true,
|
||||||
builder: (context) {
|
|
||||||
return const UserStudyWelcomeView(
|
|
||||||
wasOpenedAutomatic: true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,15 +76,9 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
// only show changelog to people who already have contacts
|
// only show changelog to people who already have contacts
|
||||||
// this prevents that this is shown directly after the user registered
|
// this prevents that this is shown directly after the user registered
|
||||||
if (_groupsNotPinned.isNotEmpty) {
|
if (_groupsNotPinned.isNotEmpty) {
|
||||||
await Navigator.push(
|
await context.push(
|
||||||
context,
|
Routes.settingsHelpChangelog,
|
||||||
MaterialPageRoute(
|
extra: changeLog,
|
||||||
builder: (context) {
|
|
||||||
return ChangeLogView(
|
|
||||||
changeLog: changeLog,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -120,14 +101,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await Navigator.push(
|
await context.push(Routes.settingsProfile);
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const ProfileView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {}); // gUser has updated
|
setState(() {}); // gUser has updated
|
||||||
},
|
},
|
||||||
|
|
@ -141,16 +115,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
const Text('twonly '),
|
const Text('twonly '),
|
||||||
if (plan != SubscriptionPlan.Free)
|
if (plan != SubscriptionPlan.Free)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () => context.push(Routes.settingsSubscription),
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const SubscriptionView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.color.primary,
|
color: context.color.primary,
|
||||||
|
|
@ -184,28 +149,15 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
key: searchForOtherUsers,
|
key: searchForOtherUsers,
|
||||||
icon: const FaIcon(FontAwesomeIcons.userPlus, size: 18),
|
icon: const FaIcon(FontAwesomeIcons.userPlus, size: 18),
|
||||||
onPressed: () {
|
onPressed: () => context.push(Routes.chatsAddNewUser),
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const AddNewUserView(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await Navigator.push(
|
await context.push(Routes.settings);
|
||||||
context,
|
if (mounted) setState(() {}); // gUser may has changed...
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const SettingsMainView(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (!mounted) return;
|
|
||||||
setState(() {}); // gUser may has changed...
|
|
||||||
},
|
},
|
||||||
icon: const FaIcon(FontAwesomeIcons.gear, size: 19),
|
icon: const FaIcon(FontAwesomeIcons.gear, size: 19),
|
||||||
),
|
),
|
||||||
|
|
@ -234,14 +186,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
icon: const Icon(Icons.person_add),
|
icon: const Icon(Icons.person_add),
|
||||||
onPressed: () {
|
onPressed: () => context.push(Routes.chatsAddNewUser),
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const AddNewUserView(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
label: Text(
|
label: Text(
|
||||||
context.lang.chatListViewSearchUserNameBtn,
|
context.lang.chatListViewSearchUserNameBtn,
|
||||||
),
|
),
|
||||||
|
|
@ -265,16 +210,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: 13),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () => context.push(Routes.chatsArchived),
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const ArchivedChatsView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Check if the index is for the pinned users
|
// Check if the index is for the pinned users
|
||||||
|
|
@ -320,16 +256,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
color: context.color.primary,
|
color: context.color.primary,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
onTap: () {
|
onTap: () => context.push(Routes.settingsPublicProfile),
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const PublicProfileView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 45,
|
width: 45,
|
||||||
height: 45,
|
height: 45,
|
||||||
|
|
@ -345,16 +272,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
backgroundColor: context.color.primary,
|
backgroundColor: context.color.primary,
|
||||||
onPressed: () {
|
onPressed: () => context.push(Routes.chatsStartNewChat),
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const StartNewChatView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: FaIcon(
|
child: FaIcon(
|
||||||
FontAwesomeIcons.penToSquare,
|
FontAwesomeIcons.penToSquare,
|
||||||
color: isDarkMode(context) ? Colors.black : Colors.white,
|
color: isDarkMode(context) ? Colors.black : Colors.white,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/settings/help/contact_us.view.dart';
|
|
||||||
|
|
||||||
class FeedbackIconButton extends StatefulWidget {
|
class FeedbackIconButton extends StatefulWidget {
|
||||||
const FeedbackIconButton({super.key});
|
const FeedbackIconButton({super.key});
|
||||||
|
|
@ -37,14 +37,7 @@ class _FeedbackIconButtonState extends State<FeedbackIconButton> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () async {
|
onPressed: () => context.push(Routes.settingsHelpContactUs),
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const ContactUsView(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
tooltip: context.lang.feedbackTooltip,
|
tooltip: context.lang.feedbackTooltip,
|
||||||
icon: const FaIcon(FontAwesomeIcons.commentDots, size: 19),
|
icon: const FaIcon(FontAwesomeIcons.commentDots, size: 19),
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/download.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/download.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
|
||||||
import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart';
|
import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/message_send_state_icon.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/message_send_state_icon.dart';
|
||||||
import 'package:twonly/src/views/chats/media_viewer.view.dart';
|
|
||||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
import 'package:twonly/src/views/components/flame.dart';
|
import 'package:twonly/src/views/components/flame.dart';
|
||||||
import 'package:twonly/src/views/components/group_context_menu.component.dart';
|
import 'package:twonly/src/views/components/group_context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
|
||||||
import 'package:twonly/src/views/groups/group.view.dart';
|
|
||||||
|
|
||||||
class GroupListItem extends StatefulWidget {
|
class GroupListItem extends StatefulWidget {
|
||||||
const GroupListItem({
|
const GroupListItem({
|
||||||
|
|
@ -131,10 +129,11 @@ class _UserListItem extends State<GroupListItem> {
|
||||||
_previewMessages = [newLastMessage];
|
_previewMessages = [newLastMessage];
|
||||||
}
|
}
|
||||||
|
|
||||||
final msgs =
|
final msgs = _previewMessages
|
||||||
_previewMessages.where((x) => x.type == MessageType.media).toList();
|
.where((x) => x.type == MessageType.media.name)
|
||||||
|
.toList();
|
||||||
if (msgs.isNotEmpty &&
|
if (msgs.isNotEmpty &&
|
||||||
msgs.first.type == MessageType.media &&
|
msgs.first.type == MessageType.media.name &&
|
||||||
!msgs.first.isDeletedFromSender &&
|
!msgs.first.isDeletedFromSender &&
|
||||||
msgs.first.senderId != null &&
|
msgs.first.senderId != null &&
|
||||||
msgs.first.openedAt == null) {
|
msgs.first.openedAt == null) {
|
||||||
|
|
@ -161,20 +160,17 @@ class _UserListItem extends State<GroupListItem> {
|
||||||
|
|
||||||
Future<void> onTap() async {
|
Future<void> onTap() async {
|
||||||
if (_currentMessage == null && widget.group.totalMediaCounter == 0) {
|
if (_currentMessage == null && widget.group.totalMediaCounter == 0) {
|
||||||
await Navigator.push(
|
await context.push(
|
||||||
context,
|
Routes.chatsCameraSendTo,
|
||||||
MaterialPageRoute(
|
extra: widget.group,
|
||||||
builder: (context) {
|
|
||||||
return CameraSendToView(widget.group);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_hasNonOpenedMediaFile) {
|
if (_hasNonOpenedMediaFile) {
|
||||||
final msgs =
|
final msgs = _previewMessages
|
||||||
_previewMessages.where((x) => x.type == MessageType.media).toList();
|
.where((x) => x.type == MessageType.media.name)
|
||||||
|
.toList();
|
||||||
final mediaFile =
|
final mediaFile =
|
||||||
await twonlyDB.mediaFilesDao.getMediaFileById(msgs.first.mediaId!);
|
await twonlyDB.mediaFilesDao.getMediaFileById(msgs.first.mediaId!);
|
||||||
if (mediaFile?.type != MediaType.audio) {
|
if (mediaFile?.type != MediaType.audio) {
|
||||||
|
|
@ -185,26 +181,18 @@ class _UserListItem extends State<GroupListItem> {
|
||||||
}
|
}
|
||||||
if (mediaFile.downloadState! == DownloadState.ready) {
|
if (mediaFile.downloadState! == DownloadState.ready) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
await Navigator.push(
|
await context.push(
|
||||||
context,
|
Routes.chatsMediaViewer,
|
||||||
MaterialPageRoute(
|
extra: widget.group,
|
||||||
builder: (context) {
|
|
||||||
return MediaViewerView(widget.group);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
await Navigator.push(
|
await context.push(
|
||||||
context,
|
Routes.chatsMessages,
|
||||||
MaterialPageRoute(
|
extra: widget.group,
|
||||||
builder: (context) {
|
|
||||||
return ChatMessagesView(widget.group);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,42 +238,27 @@ class _UserListItem extends State<GroupListItem> {
|
||||||
),
|
),
|
||||||
leading: GestureDetector(
|
leading: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Widget pushWidget = GroupView(widget.group);
|
|
||||||
|
|
||||||
if (widget.group.isDirectChat) {
|
if (widget.group.isDirectChat) {
|
||||||
final contacts = await twonlyDB.groupsDao
|
final contacts = await twonlyDB.groupsDao
|
||||||
.getGroupContact(widget.group.groupId);
|
.getGroupContact(widget.group.groupId);
|
||||||
pushWidget = ContactView(contacts.first.userId);
|
|
||||||
}
|
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
await Navigator.push(
|
await context.push(Routes.profileContact(contacts.first.userId));
|
||||||
context,
|
return;
|
||||||
MaterialPageRoute(
|
} else {
|
||||||
builder: (context) {
|
await context.push(Routes.profileGroup(widget.group.groupId));
|
||||||
return pushWidget;
|
}
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: AvatarIcon(group: widget.group),
|
child: AvatarIcon(group: widget.group),
|
||||||
),
|
),
|
||||||
trailing: (widget.group.leftGroup)
|
trailing: (widget.group.leftGroup)
|
||||||
? null
|
? null
|
||||||
: IconButton(
|
: IconButton(
|
||||||
onPressed: () {
|
onPressed: () => context.push(
|
||||||
Navigator.push(
|
_hasNonOpenedMediaFile
|
||||||
context,
|
? Routes.chatsMessages
|
||||||
MaterialPageRoute(
|
: Routes.chatsCameraSendTo,
|
||||||
builder: (context) {
|
extra: widget.group,
|
||||||
if (_hasNonOpenedMediaFile) {
|
|
||||||
return ChatMessagesView(widget.group);
|
|
||||||
} else {
|
|
||||||
return CameraSendToView(widget.group);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: FaIcon(
|
icon: FaIcon(
|
||||||
_hasNonOpenedMediaFile
|
_hasNonOpenedMediaFile
|
||||||
? FontAwesomeIcons.solidComments
|
? FontAwesomeIcons.solidComments
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
|
@ -21,8 +22,6 @@ import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
import 'package:twonly/src/views/components/blink.component.dart';
|
import 'package:twonly/src/views/components/blink.component.dart';
|
||||||
import 'package:twonly/src/views/components/flame.dart';
|
import 'package:twonly/src/views/components/flame.dart';
|
||||||
import 'package:twonly/src/views/components/verified_shield.dart';
|
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
|
||||||
import 'package:twonly/src/views/groups/group.view.dart';
|
|
||||||
|
|
||||||
Color getMessageColor(Message message) {
|
Color getMessageColor(Message message) {
|
||||||
return (message.senderId == null)
|
return (message.senderId == null)
|
||||||
|
|
@ -201,7 +200,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index += 1;
|
index += 1;
|
||||||
if (msg.type == MessageType.text &&
|
if (msg.type != MessageType.media.name &&
|
||||||
msg.senderId != null &&
|
msg.senderId != null &&
|
||||||
msg.openedAt == null) {
|
msg.openedAt == null) {
|
||||||
if (openedMessages[msg.senderId!] == null) {
|
if (openedMessages[msg.senderId!] == null) {
|
||||||
|
|
@ -210,7 +209,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
openedMessages[msg.senderId!]!.add(msg.messageId);
|
openedMessages[msg.senderId!]!.add(msg.messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.type == MessageType.media && msg.mediaStored) {
|
if (msg.type == MessageType.media.name && msg.mediaStored) {
|
||||||
storedMediaFiles.add(msg);
|
storedMediaFiles.add(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,23 +290,10 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||||
await twonlyDB.groupsDao.getAllGroupMembers(group.groupId);
|
await twonlyDB.groupsDao.getAllGroupMembers(group.groupId);
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
if (member.isEmpty) return;
|
if (member.isEmpty) return;
|
||||||
await Navigator.push(
|
await context
|
||||||
context,
|
.push(Routes.profileContact(member.first.contactId));
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return ContactView(member.first.contactId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
await Navigator.push(
|
await context.push(Routes.profileGroup(group.groupId));
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return GroupView(group);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/shared/select_contacts.view.dart';
|
||||||
|
|
||||||
|
class ShareAdditionalView extends StatefulWidget {
|
||||||
|
const ShareAdditionalView({required this.group, super.key});
|
||||||
|
|
||||||
|
final Group group;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ShareAdditionalView> createState() => _ShareAdditionalViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ShareAdditionalViewState extends State<ShareAdditionalView> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> openShareContactView() async {
|
||||||
|
final selectedContacts = await context.navPush(
|
||||||
|
SelectContactsView(
|
||||||
|
text: SelectedContactViewText(
|
||||||
|
title: context.lang.shareContactsTitle,
|
||||||
|
submitButton: (_, __) => context.lang.shareContactsSubmit,
|
||||||
|
submitIcon: FontAwesomeIcons.shareNodes,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) as List<int>?;
|
||||||
|
if (selectedContacts != null && selectedContacts.isNotEmpty) {
|
||||||
|
await insertAndSendContactShareMessage(
|
||||||
|
widget.group.groupId,
|
||||||
|
selectedContacts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
height: 220,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(32),
|
||||||
|
topRight: Radius.circular(32),
|
||||||
|
),
|
||||||
|
color: context.color.surface,
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
blurRadius: 10.9,
|
||||||
|
color: Color.fromRGBO(0, 0, 0, 0.1),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.only(top: 30),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(32),
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
height: 3,
|
||||||
|
width: 60,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: openShareContactView,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.color.surfaceContainer,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: const FaIcon(FontAwesomeIcons.circleUser),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
context.lang.shareContactsMenu,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart'
|
import 'package:twonly/src/database/tables/messages.table.dart'
|
||||||
hide MessageActions;
|
hide MessageActions;
|
||||||
|
|
@ -10,14 +12,15 @@ import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/chat_reaction_row.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/chat_reaction_row.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
|
||||||
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_contacts.entry.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_media_entry.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_media_entry.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_text_entry.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_text_entry.dart';
|
||||||
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_unkown.entry.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/common.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/common.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/message_actions.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/message_actions.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/message_context_menu.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/message_context_menu.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
||||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
|
||||||
|
|
||||||
class ChatListEntry extends StatefulWidget {
|
class ChatListEntry extends StatefulWidget {
|
||||||
const ChatListEntry({
|
const ChatListEntry({
|
||||||
|
|
@ -89,6 +92,49 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget? _getChatEntry(BorderRadius borderRadius, int reactionsForWidth) {
|
||||||
|
if (widget.message.type == MessageType.text.name) {
|
||||||
|
return ChatTextEntry(
|
||||||
|
message: widget.message,
|
||||||
|
nextMessage: widget.nextMessage,
|
||||||
|
prevMessage: widget.prevMessage,
|
||||||
|
userIdToContact: widget.userIdToContact,
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
minWidth: reactionsForWidth * 43,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.message.type == MessageType.media.name) {
|
||||||
|
if (mediaService == null) return null;
|
||||||
|
if (mediaService!.mediaFile.type == MediaType.audio) {
|
||||||
|
return ChatAudioEntry(
|
||||||
|
message: widget.message,
|
||||||
|
nextMessage: widget.nextMessage,
|
||||||
|
prevMessage: widget.prevMessage,
|
||||||
|
mediaService: mediaService!,
|
||||||
|
userIdToContact: widget.userIdToContact,
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
minWidth: reactionsForWidth * 43,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ChatMediaEntry(
|
||||||
|
message: widget.message,
|
||||||
|
group: widget.group,
|
||||||
|
mediaService: mediaService!,
|
||||||
|
galleryItems: widget.galleryItems,
|
||||||
|
minWidth: reactionsForWidth * 43,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.message.type == MessageType.contacts.name) {
|
||||||
|
return ChatContactsEntry(
|
||||||
|
message: widget.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const ChatUnknownEntry();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final right = widget.message.senderId == null;
|
final right = widget.message.senderId == null;
|
||||||
|
|
@ -128,34 +174,7 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
||||||
mediaService: mediaService,
|
mediaService: mediaService,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
scrollToMessage: widget.scrollToMessage,
|
scrollToMessage: widget.scrollToMessage,
|
||||||
child: (widget.message.type == MessageType.text)
|
child: _getChatEntry(borderRadius, reactionsForWidth),
|
||||||
? ChatTextEntry(
|
|
||||||
message: widget.message,
|
|
||||||
nextMessage: widget.nextMessage,
|
|
||||||
prevMessage: widget.prevMessage,
|
|
||||||
userIdToContact: widget.userIdToContact,
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
minWidth: reactionsForWidth * 43,
|
|
||||||
)
|
|
||||||
: (mediaService == null)
|
|
||||||
? null
|
|
||||||
: (mediaService!.mediaFile.type == MediaType.audio)
|
|
||||||
? ChatAudioEntry(
|
|
||||||
message: widget.message,
|
|
||||||
nextMessage: widget.nextMessage,
|
|
||||||
prevMessage: widget.prevMessage,
|
|
||||||
mediaService: mediaService!,
|
|
||||||
userIdToContact: widget.userIdToContact,
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
minWidth: reactionsForWidth * 43,
|
|
||||||
)
|
|
||||||
: ChatMediaEntry(
|
|
||||||
message: widget.message,
|
|
||||||
group: widget.group,
|
|
||||||
mediaService: mediaService!,
|
|
||||||
galleryItems: widget.galleryItems,
|
|
||||||
minWidth: reactionsForWidth * 43,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (reactionsForWidth > 0) const SizedBox(height: 20, width: 10),
|
if (reactionsForWidth > 0) const SizedBox(height: 20, width: 10),
|
||||||
],
|
],
|
||||||
|
|
@ -204,15 +223,9 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
||||||
hideContactAvatar
|
hideContactAvatar
|
||||||
? const SizedBox(width: 24)
|
? const SizedBox(width: 24)
|
||||||
: GestureDetector(
|
: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () => context.push(
|
||||||
await Navigator.push(
|
Routes.profileContact(widget.message.senderId!),
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
ContactView(widget.message.senderId!),
|
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
child: AvatarIcon(
|
child: AvatarIcon(
|
||||||
contactId: widget.message.senderId,
|
contactId: widget.message.senderId,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class ReactionRow extends StatelessWidget {
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AllReactionsView(
|
return AllReactionsView(
|
||||||
message: message,
|
message: message,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
|
||||||
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/common.dart';
|
||||||
|
import 'package:twonly/src/views/components/better_text.dart';
|
||||||
|
|
||||||
|
class ChatContactsEntry extends StatefulWidget {
|
||||||
|
const ChatContactsEntry({
|
||||||
|
required this.message,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Message message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChatContactsEntry> createState() => _ChatContactsEntryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChatContactsEntryState extends State<ChatContactsEntry> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
AdditionalMessageData? data;
|
||||||
|
|
||||||
|
if (widget.message.additionalMessageData != null) {
|
||||||
|
try {
|
||||||
|
data = AdditionalMessageData.fromBuffer(
|
||||||
|
widget.message.additionalMessageData!,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == null || data.contacts.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
final info = getBubbleInfo(
|
||||||
|
context,
|
||||||
|
widget.message,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: info.color,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: IntrinsicWidth(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
for (var i = 0; i < data.contacts.length; i++) ...[
|
||||||
|
if (i > 0)
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.white.withValues(alpha: 0.2),
|
||||||
|
),
|
||||||
|
_ContactRow(
|
||||||
|
contact: data.contacts[i],
|
||||||
|
message: widget.message,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContactRow extends StatefulWidget {
|
||||||
|
const _ContactRow({
|
||||||
|
required this.contact,
|
||||||
|
required this.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
final SharedContact contact;
|
||||||
|
final Message message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_ContactRow> createState() => _ContactRowState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContactRowState extends State<_ContactRow> {
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
Future<void> _onContactClick() async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final userdata =
|
||||||
|
await apiService.getUserById(widget.contact.userId.toInt());
|
||||||
|
if (userdata == null) return;
|
||||||
|
|
||||||
|
var verified = false;
|
||||||
|
if (userdata.publicIdentityKey == widget.contact.publicIdentityKey) {
|
||||||
|
final sender =
|
||||||
|
await twonlyDB.contactsDao.getContactById(widget.message.senderId!);
|
||||||
|
// in case the sender is verified and the public keys are the same, this trust can be transferred
|
||||||
|
verified = sender != null && sender.verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
final added = await twonlyDB.contactsDao.insertOnConflictUpdate(
|
||||||
|
ContactsCompanion(
|
||||||
|
username: Value(utf8.decode(userdata.username)),
|
||||||
|
userId: Value(userdata.userId.toInt()),
|
||||||
|
requested: const Value(false),
|
||||||
|
blocked: const Value(false),
|
||||||
|
deletedByUser: const Value(false),
|
||||||
|
verified: Value(
|
||||||
|
verified,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (added > 0) await importSignalContactAndCreateRequest(userdata);
|
||||||
|
} catch (e) {
|
||||||
|
Log.error(e);
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return StreamBuilder<Contact?>(
|
||||||
|
stream: twonlyDB.contactsDao.watchContact(widget.contact.userId.toInt()),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final contactInDb = snapshot.data;
|
||||||
|
final isAdded = contactInDb != null ||
|
||||||
|
widget.contact.userId.toInt() == gUser.userId;
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: (widget.message.senderId == null || isAdded || _isLoading)
|
||||||
|
? null
|
||||||
|
: _onContactClick,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const FaIcon(
|
||||||
|
FontAwesomeIcons.user,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Flexible(
|
||||||
|
child: BetterText(
|
||||||
|
text: widget.contact.displayName,
|
||||||
|
textColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (widget.message.senderId != null && !isAdded) ...[
|
||||||
|
const Spacer(),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
if (_isLoading)
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const FaIcon(
|
||||||
|
FontAwesomeIcons.userPlus,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -117,7 +117,7 @@ class _ChatMediaEntryState extends State<ChatMediaEntry> {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
key: reopenMediaFile,
|
key: reopenMediaFile,
|
||||||
onDoubleTap: onDoubleTap,
|
onDoubleTap: onDoubleTap,
|
||||||
onTap: (widget.message.type == MessageType.media) ? onTap : null,
|
onTap: (widget.message.type == MessageType.media.name) ? onTap : null,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: (widget.minWidth > 150) ? widget.minWidth : 150,
|
width: (widget.minWidth > 150) ? widget.minWidth : 150,
|
||||||
height: (widget.message.mediaStored &&
|
height: (widget.message.mediaStored &&
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/components/better_text.dart';
|
||||||
|
|
||||||
|
class ChatUnknownEntry extends StatelessWidget {
|
||||||
|
const ChatUnknownEntry({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isDarkMode(context) ? Colors.black : Colors.grey,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: BetterText(
|
||||||
|
text: context.lang.updateTwonlyMessage,
|
||||||
|
textColor: isDarkMode(context)
|
||||||
|
? const Color.fromARGB(255, 99, 99, 99)
|
||||||
|
: Colors.black,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -85,8 +85,8 @@ double measureTextWidth(
|
||||||
bool combineTextMessageWithNext(Message message, Message? nextMessage) {
|
bool combineTextMessageWithNext(Message message, Message? nextMessage) {
|
||||||
if (nextMessage != null && nextMessage.content != null) {
|
if (nextMessage != null && nextMessage.content != null) {
|
||||||
if (nextMessage.senderId == message.senderId) {
|
if (nextMessage.senderId == message.senderId) {
|
||||||
if (nextMessage.type == MessageType.text &&
|
if (nextMessage.type == MessageType.text.name &&
|
||||||
message.type == MessageType.text) {
|
message.type == MessageType.text.name) {
|
||||||
if (!EmojiAnimation.supported(nextMessage.content!)) {
|
if (!EmojiAnimation.supported(nextMessage.content!)) {
|
||||||
final diff =
|
final diff =
|
||||||
nextMessage.createdAt.difference(message.createdAt).inMinutes;
|
nextMessage.createdAt.difference(message.createdAt).inMinutes;
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
final layer = await showModalBottomSheet(
|
final layer = await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return const EmojiPickerBottom();
|
return const EmojiPickerBottom();
|
||||||
},
|
},
|
||||||
) as EmojiLayerData?;
|
) as EmojiLayerData?;
|
||||||
|
|
@ -127,7 +127,7 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
),
|
),
|
||||||
if (!message.isDeletedFromSender &&
|
if (!message.isDeletedFromSender &&
|
||||||
message.senderId == null &&
|
message.senderId == null &&
|
||||||
message.type == MessageType.text)
|
message.type == MessageType.text.name)
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
title: context.lang.edit,
|
title: context.lang.edit,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
|
@ -210,10 +210,10 @@ Future<void> editTextMessage(BuildContext context, Message message) async {
|
||||||
final controller = TextEditingController(text: message.content);
|
final controller = TextEditingController(text: message.content);
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
content: StatefulBuilder(
|
content: StatefulBuilder(
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
builder: (context, setState) {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
||||||
|
import 'package:twonly/src/views/chats/chat_messages_components/bottom_sheets/share_additional.bottom_sheet.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
|
||||||
|
|
||||||
class MessageInput extends StatefulWidget {
|
class MessageInput extends StatefulWidget {
|
||||||
|
|
@ -167,6 +168,19 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _showAdditionalShareModal(BuildContext context) async {
|
||||||
|
// ignore: inference_failure_on_function_invocation
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
builder: (context) {
|
||||||
|
return ShareAdditionalView(
|
||||||
|
group: widget.group,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
|
|
@ -312,6 +326,20 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (_textFieldController.text == '')
|
||||||
|
IconButton(
|
||||||
|
icon: const FaIcon(FontAwesomeIcons.camera),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return CameraSendToView(widget.group);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
if (_textFieldController.text == '')
|
if (_textFieldController.text == '')
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onLongPressMoveUpdate: (details) {
|
onLongPressMoveUpdate: (details) {
|
||||||
|
|
@ -452,18 +480,9 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const FaIcon(FontAwesomeIcons.camera),
|
icon: const FaIcon(FontAwesomeIcons.plus),
|
||||||
padding: const EdgeInsets.all(15),
|
padding: const EdgeInsets.all(15),
|
||||||
onPressed: () {
|
onPressed: () => _showAdditionalShareModal(context),
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return CameraSendToView(widget.group);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@ import 'dart:collection';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
import 'package:twonly/src/views/settings/subscription/subscription.view.dart';
|
|
||||||
|
|
||||||
enum MessageSendState {
|
enum MessageSendState {
|
||||||
received,
|
received,
|
||||||
|
|
@ -86,7 +87,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
||||||
var text = '';
|
var text = '';
|
||||||
Widget? textWidget;
|
Widget? textWidget;
|
||||||
textWidget = null;
|
textWidget = null;
|
||||||
final kindsAlreadyShown = HashSet<MessageType>();
|
final kindsAlreadyShown = HashSet<String>();
|
||||||
|
|
||||||
var hasLoader = false;
|
var hasLoader = false;
|
||||||
GestureTapCallback? onTap;
|
GestureTapCallback? onTap;
|
||||||
|
|
@ -132,7 +133,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
||||||
case MessageSendState.received:
|
case MessageSendState.received:
|
||||||
icon = Icon(Icons.square_rounded, size: 14, color: color);
|
icon = Icon(Icons.square_rounded, size: 14, color: color);
|
||||||
text = context.lang.messageSendState_Received;
|
text = context.lang.messageSendState_Received;
|
||||||
if (message.type == MessageType.media && mediaFile != null) {
|
if (message.type == MessageType.media.name && mediaFile != null) {
|
||||||
if (mediaFile.downloadState == DownloadState.pending) {
|
if (mediaFile.downloadState == DownloadState.pending) {
|
||||||
text = context.lang.messageSendState_TapToLoad;
|
text = context.lang.messageSendState_TapToLoad;
|
||||||
}
|
}
|
||||||
|
|
@ -163,16 +164,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
||||||
style: const TextStyle(fontSize: 9),
|
style: const TextStyle(fontSize: 9),
|
||||||
);
|
);
|
||||||
|
|
||||||
onTap = () async {
|
onTap = () => context.push(Routes.settingsSubscription);
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const SubscriptionView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (mediaFile.uploadState == UploadState.preprocessing ||
|
if (mediaFile.uploadState == UploadState.preprocessing ||
|
||||||
mediaFile.uploadState == UploadState.initialized) {
|
mediaFile.uploadState == UploadState.initialized) {
|
||||||
|
|
@ -218,7 +210,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type == MessageType.media) {
|
if (message.type == MessageType.media.name) {
|
||||||
icons.insert(0, icon);
|
icons.insert(0, icon);
|
||||||
} else {
|
} else {
|
||||||
icons.add(icon);
|
icons.add(icon);
|
||||||
|
|
|
||||||
|
|
@ -169,12 +169,12 @@ class _ResponsePreviewState extends State<ResponsePreview> {
|
||||||
var color = const Color.fromARGB(233, 68, 137, 255);
|
var color = const Color.fromARGB(233, 68, 137, 255);
|
||||||
|
|
||||||
if (_message != null) {
|
if (_message != null) {
|
||||||
if (_message!.type == MessageType.text) {
|
if (_message!.type == MessageType.text.name) {
|
||||||
if (_message!.content != null) {
|
if (_message!.content != null) {
|
||||||
subtitle = truncateString(_message!.content!);
|
subtitle = truncateString(_message!.content!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_message!.type == MessageType.media && _mediaService != null) {
|
if (_message!.type == MessageType.media.name && _mediaService != null) {
|
||||||
switch (_mediaService!.mediaFile.type) {
|
switch (_mediaService!.mediaFile.type) {
|
||||||
case MediaType.image:
|
case MediaType.image:
|
||||||
subtitle = context.lang.image;
|
subtitle = context.lang.image;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:lottie/lottie.dart';
|
import 'package:lottie/lottie.dart';
|
||||||
import 'package:no_screenshot/no_screenshot.dart';
|
import 'package:no_screenshot/no_screenshot.dart';
|
||||||
|
import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart'
|
import 'package:twonly/src/database/tables/mediafiles.table.dart'
|
||||||
|
|
@ -536,15 +537,22 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
children: [
|
children: [
|
||||||
if (videoController != null)
|
if (videoController != null)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
|
child: PhotoView.customChild(
|
||||||
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
|
minScale: PhotoViewComputedScale.contained,
|
||||||
child: VideoPlayer(videoController!),
|
child: VideoPlayer(videoController!),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else if (currentMedia != null &&
|
else if (currentMedia != null &&
|
||||||
currentMedia!.mediaFile.type == MediaType.image ||
|
currentMedia!.mediaFile.type == MediaType.image ||
|
||||||
currentMedia!.mediaFile.type == MediaType.gif)
|
currentMedia!.mediaFile.type == MediaType.gif)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Image.file(
|
child: PhotoView(
|
||||||
|
imageProvider: FileImage(
|
||||||
currentMedia!.tempPath,
|
currentMedia!.tempPath,
|
||||||
fit: BoxFit.contain,
|
),
|
||||||
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
|
minScale: PhotoViewComputedScale.contained,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ class AdditionalMessageContent extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// ignore: no_default_cases
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
// ignore: empty_catches
|
// ignore: empty_catches
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class _ReactionButtonsState extends State<ReactionButtons> {
|
||||||
final layer = await showModalBottomSheet(
|
final layer = await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: context.color.surface,
|
backgroundColor: context.color.surface,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return const EmojiPickerBottom();
|
return const EmojiPickerBottom();
|
||||||
},
|
},
|
||||||
) as EmojiLayerData?;
|
) as EmojiLayerData?;
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ class _MessageInfoViewState extends State<MessageInfoView> {
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return MessageHistoryView(
|
return MessageHistoryView(
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
changes: messageHistory,
|
changes: messageHistory,
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,17 @@ import 'dart:async';
|
||||||
import 'package:drift/drift.dart' hide Column;
|
import 'package:drift/drift.dart' hide Column;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
|
||||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
||||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
import 'package:twonly/src/views/components/flame.dart';
|
import 'package:twonly/src/views/components/flame.dart';
|
||||||
import 'package:twonly/src/views/components/group_context_menu.component.dart';
|
import 'package:twonly/src/views/components/group_context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/components/user_context_menu.component.dart';
|
import 'package:twonly/src/views/components/user_context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/groups/group_create_select_members.view.dart';
|
|
||||||
|
|
||||||
class StartNewChatView extends StatefulWidget {
|
class StartNewChatView extends StatefulWidget {
|
||||||
const StartNewChatView({super.key});
|
const StartNewChatView({super.key});
|
||||||
|
|
@ -154,7 +154,7 @@ class _StartNewChatView extends State<StartNewChatView> {
|
||||||
restorationId: 'new_message_users_list',
|
restorationId: 'new_message_users_list',
|
||||||
itemCount:
|
itemCount:
|
||||||
filteredContacts.length + 3 + filteredGroups.length,
|
filteredContacts.length + 3 + filteredGroups.length,
|
||||||
itemBuilder: (BuildContext context, int i) {
|
itemBuilder: (context, i) {
|
||||||
if (searchUserName.text.isEmpty) {
|
if (searchUserName.text.isEmpty) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
|
|
@ -165,15 +165,8 @@ class _StartNewChatView extends State<StartNewChatView> {
|
||||||
size: 13,
|
size: 13,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () => context
|
||||||
await Navigator.push(
|
.push(Routes.groupCreateSelectMember(null)),
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
const GroupCreateSelectMembersView(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
|
|
@ -185,14 +178,7 @@ class _StartNewChatView extends State<StartNewChatView> {
|
||||||
size: 13,
|
size: 13,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () => context.push(Routes.chatsAddNewUser),
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const AddNewUserView(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ Future<bool> showAlertDialog(
|
||||||
// ignore: inference_failure_on_function_invocation
|
// ignore: inference_failure_on_function_invocation
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return alert;
|
return alert;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class _ContextMenuState extends State<ContextMenu> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onLongPress: _showCustomMenu,
|
onLongPress: _showCustomMenu,
|
||||||
onTapDown: (TapDownDetails details) {
|
onTapDown: (details) {
|
||||||
_tapPosition = details.globalPosition;
|
_tapPosition = details.globalPosition;
|
||||||
},
|
},
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import 'package:drift/drift.dart' hide Column;
|
import 'package:drift/drift.dart' hide Column;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/components/context_menu.component.dart';
|
import 'package:twonly/src/views/components/context_menu.component.dart';
|
||||||
|
|
||||||
|
|
@ -45,16 +46,10 @@ class GroupContextMenu extends StatelessWidget {
|
||||||
),
|
),
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
title: context.lang.contextMenuOpenChat,
|
title: context.lang.contextMenuOpenChat,
|
||||||
onTap: () async {
|
onTap: () => context.push(
|
||||||
await Navigator.push(
|
Routes.chatsMessages,
|
||||||
context,
|
extra: group,
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return ChatMessagesView(group);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: FontAwesomeIcons.comments,
|
icon: FontAwesomeIcons.comments,
|
||||||
),
|
),
|
||||||
if (!group.archived)
|
if (!group.archived)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ import 'dart:async';
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/flame.service.dart';
|
import 'package:twonly/src/services/flame.service.dart';
|
||||||
import 'package:twonly/src/services/subscription.service.dart';
|
import 'package:twonly/src/services/subscription.service.dart';
|
||||||
|
|
@ -10,7 +12,6 @@ import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
import 'package:twonly/src/views/components/better_list_title.dart';
|
import 'package:twonly/src/views/components/better_list_title.dart';
|
||||||
import 'package:twonly/src/views/settings/subscription/subscription.view.dart';
|
|
||||||
|
|
||||||
class MaxFlameListTitle extends StatefulWidget {
|
class MaxFlameListTitle extends StatefulWidget {
|
||||||
const MaxFlameListTitle({
|
const MaxFlameListTitle({
|
||||||
|
|
@ -46,14 +47,7 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
||||||
|
|
||||||
Future<void> _restoreFlames() async {
|
Future<void> _restoreFlames() async {
|
||||||
if (!isPayingUser(getCurrentPlan())) {
|
if (!isPayingUser(getCurrentPlan())) {
|
||||||
await Navigator.push(
|
await context.push(Routes.settingsSubscription);
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const SubscriptionView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.info(
|
Log.info(
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class _SelectChatDeletionTimeListTitleState
|
||||||
Future<void> _showDialog(Widget child) async {
|
Future<void> _showDialog(Widget child) async {
|
||||||
await showCupertinoModalPopup<void>(
|
await showCupertinoModalPopup<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => Container(
|
builder: (context) => Container(
|
||||||
height: 216,
|
height: 216,
|
||||||
padding: const EdgeInsets.only(top: 6),
|
padding: const EdgeInsets.only(top: 6),
|
||||||
// The Bottom margin is provided to align the popup above the system navigation bar.
|
// The Bottom margin is provided to align the popup above the system navigation bar.
|
||||||
|
|
@ -138,13 +138,13 @@ class _SelectChatDeletionTimeListTitleState
|
||||||
initialItem: _selectedDeletionTime,
|
initialItem: _selectedDeletionTime,
|
||||||
),
|
),
|
||||||
// This is called when selected item is changed.
|
// This is called when selected item is changed.
|
||||||
onSelectedItemChanged: (int selectedItem) {
|
onSelectedItemChanged: (selectedItem) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedDeletionTime = selectedItem;
|
_selectedDeletionTime = selectedItem;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
children:
|
children:
|
||||||
List<Widget>.generate(_getOptions().length, (int index) {
|
List<Widget>.generate(_getOptions().length, (index) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(_getOptions()[index].$2),
|
child: Text(_getOptions()[index].$2),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/context_menu.component.dart';
|
import 'package:twonly/src/views/components/context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
|
||||||
|
|
||||||
class UserContextMenu extends StatelessWidget {
|
class UserContextMenu extends StatelessWidget {
|
||||||
const UserContextMenu({
|
const UserContextMenu({
|
||||||
|
|
@ -20,16 +21,7 @@ class UserContextMenu extends StatelessWidget {
|
||||||
items: [
|
items: [
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
title: context.lang.contextMenuUserProfile,
|
title: context.lang.contextMenuUserProfile,
|
||||||
onTap: () async {
|
onTap: () => context.push(Routes.profileContact(contact.userId)),
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return ContactView(contact.userId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: FontAwesomeIcons.user,
|
icon: FontAwesomeIcons.user,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/views/public_profile.view.dart';
|
|
||||||
|
|
||||||
class VerifiedShield extends StatefulWidget {
|
class VerifiedShield extends StatefulWidget {
|
||||||
const VerifiedShield({
|
const VerifiedShield({
|
||||||
|
|
@ -58,16 +59,7 @@ class _VerifiedShieldState extends State<VerifiedShield> {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: (contact == null)
|
onTap: (contact == null)
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () => context.push(Routes.settingsPublicProfile),
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const PublicProfileView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: isVerified
|
message: isVerified
|
||||||
? 'You verified this contact'
|
? 'You verified this contact'
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ import 'dart:async';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
@ -14,7 +16,6 @@ import 'package:twonly/src/views/components/max_flame_list_title.dart';
|
||||||
import 'package:twonly/src/views/components/select_chat_deletion_time.comp.dart';
|
import 'package:twonly/src/views/components/select_chat_deletion_time.comp.dart';
|
||||||
import 'package:twonly/src/views/components/verified_shield.dart';
|
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||||
import 'package:twonly/src/views/groups/group.view.dart';
|
import 'package:twonly/src/views/groups/group.view.dart';
|
||||||
import 'package:twonly/src/views/public_profile.view.dart';
|
|
||||||
|
|
||||||
class ContactView extends StatefulWidget {
|
class ContactView extends StatefulWidget {
|
||||||
const ContactView(this.userId, {super.key});
|
const ContactView(this.userId, {super.key});
|
||||||
|
|
@ -187,14 +188,7 @@ class _ContactViewState extends State<ContactView> {
|
||||||
icon: FontAwesomeIcons.shieldHeart,
|
icon: FontAwesomeIcons.shieldHeart,
|
||||||
text: context.lang.contactVerifyNumberTitle,
|
text: context.lang.contactVerifyNumberTitle,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await Navigator.push(
|
await context.push(Routes.settingsPublicProfile);
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const PublicProfileView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -250,7 +244,7 @@ Future<String?> showNicknameChangeDialog(
|
||||||
|
|
||||||
return showDialog<String>(
|
return showDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(context.lang.contactNickname),
|
title: Text(context.lang.contactNickname),
|
||||||
content: TextField(
|
content: TextField(
|
||||||
|
|
@ -287,7 +281,7 @@ Future<String?> showReportDialog(
|
||||||
|
|
||||||
return showDialog<String>(
|
return showDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title:
|
title:
|
||||||
Text(context.lang.reportUserTitle(getContactDisplayName(contact))),
|
Text(context.lang.reportUserTitle(getContactDisplayName(contact))),
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,16 @@ import 'package:twonly/src/views/groups/group_member.context.dart';
|
||||||
import 'package:twonly/src/views/settings/profile/profile.view.dart';
|
import 'package:twonly/src/views/settings/profile/profile.view.dart';
|
||||||
|
|
||||||
class GroupView extends StatefulWidget {
|
class GroupView extends StatefulWidget {
|
||||||
const GroupView(this.group, {super.key});
|
const GroupView(this.groupId, {super.key});
|
||||||
|
|
||||||
final Group group;
|
final String groupId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GroupView> createState() => _GroupViewState();
|
State<GroupView> createState() => _GroupViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GroupViewState extends State<GroupView> {
|
class _GroupViewState extends State<GroupView> {
|
||||||
late Group group;
|
Group? _group;
|
||||||
|
|
||||||
List<(Contact, GroupMember)> members = [];
|
List<(Contact, GroupMember)> members = [];
|
||||||
|
|
||||||
|
|
@ -37,7 +37,6 @@ class _GroupViewState extends State<GroupView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
group = widget.group;
|
|
||||||
initAsync();
|
initAsync();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
@ -50,16 +49,15 @@ class _GroupViewState extends State<GroupView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initAsync() async {
|
Future<void> initAsync() async {
|
||||||
final groupStream = twonlyDB.groupsDao.watchGroup(widget.group.groupId);
|
final groupStream = twonlyDB.groupsDao.watchGroup(widget.groupId);
|
||||||
groupSub = groupStream.listen((update) {
|
groupSub = groupStream.listen((update) {
|
||||||
if (update != null) {
|
if (update != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
group = update;
|
_group = update;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final membersStream =
|
final membersStream = twonlyDB.groupsDao.watchGroupMembers(widget.groupId);
|
||||||
twonlyDB.groupsDao.watchGroupMembers(widget.group.groupId);
|
|
||||||
membersSub = membersStream.listen((update) {
|
membersSub = membersStream.listen((update) {
|
||||||
setState(() {
|
setState(() {
|
||||||
members = update;
|
members = update;
|
||||||
|
|
@ -71,13 +69,13 @@ class _GroupViewState extends State<GroupView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateGroupName() async {
|
Future<void> _updateGroupName() async {
|
||||||
final newGroupName = await showGroupNameChangeDialog(context, group);
|
final newGroupName = await showGroupNameChangeDialog(context, _group!);
|
||||||
|
|
||||||
if (context.mounted &&
|
if (context.mounted &&
|
||||||
newGroupName != null &&
|
newGroupName != null &&
|
||||||
newGroupName != '' &&
|
newGroupName != '' &&
|
||||||
newGroupName != group.groupName) {
|
newGroupName != _group!.groupName) {
|
||||||
if (!await updateGroupName(group, newGroupName)) {
|
if (!await updateGroupName(_group!, newGroupName)) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
showNetworkIssue(context);
|
showNetworkIssue(context);
|
||||||
}
|
}
|
||||||
|
|
@ -89,11 +87,13 @@ class _GroupViewState extends State<GroupView> {
|
||||||
final selectedUserIds = await Navigator.push(
|
final selectedUserIds = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => GroupCreateSelectMembersView(group: group),
|
builder: (context) => GroupCreateSelectMembersView(
|
||||||
|
groupId: _group?.groupId,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) as List<int>?;
|
) as List<int>?;
|
||||||
if (selectedUserIds == null) return;
|
if (selectedUserIds == null) return;
|
||||||
if (!await addNewGroupMembers(group, selectedUserIds)) {
|
if (!await addNewGroupMembers(_group!, selectedUserIds)) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
showNetworkIssue(context);
|
showNetworkIssue(context);
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +115,7 @@ class _GroupViewState extends State<GroupView> {
|
||||||
if (members.isNotEmpty) {
|
if (members.isNotEmpty) {
|
||||||
// In case there are other members, check that there is at least one other admin before I leave the group.
|
// In case there are other members, check that there is at least one other admin before I leave the group.
|
||||||
|
|
||||||
if (group.isGroupAdmin) {
|
if (_group!.isGroupAdmin) {
|
||||||
if (!members.any((m) => m.$2.memberState == MemberState.admin)) {
|
if (!members.any((m) => m.$2.memberState == MemberState.admin)) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
await showAlertDialog(
|
await showAlertDialog(
|
||||||
|
|
@ -131,16 +131,17 @@ class _GroupViewState extends State<GroupView> {
|
||||||
|
|
||||||
late bool success;
|
late bool success;
|
||||||
|
|
||||||
if (group.isGroupAdmin) {
|
if (_group!.isGroupAdmin) {
|
||||||
// Current user is a admin, to the state can be updated by the user him self.
|
// Current user is a admin, to the state can be updated by the user him self.
|
||||||
final keyPair = IdentityKeyPair.fromSerialized(group.myGroupPrivateKey!);
|
final keyPair =
|
||||||
|
IdentityKeyPair.fromSerialized(_group!.myGroupPrivateKey!);
|
||||||
success = await removeMemberFromGroup(
|
success = await removeMemberFromGroup(
|
||||||
group,
|
_group!,
|
||||||
keyPair.getPublicKey().serialize(),
|
keyPair.getPublicKey().serialize(),
|
||||||
gUser.userId,
|
gUser.userId,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
success = await leaveAsNonAdminFromGroup(group);
|
success = await leaveAsNonAdminFromGroup(_group!);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|
@ -153,6 +154,9 @@ class _GroupViewState extends State<GroupView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (_group == null) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text(''),
|
title: const Text(''),
|
||||||
|
|
@ -162,7 +166,7 @@ class _GroupViewState extends State<GroupView> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: AvatarIcon(
|
child: AvatarIcon(
|
||||||
group: group,
|
group: _group,
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -171,24 +175,24 @@ class _GroupViewState extends State<GroupView> {
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 10),
|
padding: const EdgeInsets.only(right: 10),
|
||||||
child: VerifiedShield(key: Key(group.groupId), group: group),
|
child: VerifiedShield(key: Key(_group!.groupId), group: _group),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
substringBy(group.groupName, 25),
|
substringBy(_group!.groupName, 25),
|
||||||
style: const TextStyle(fontSize: 20),
|
style: const TextStyle(fontSize: 20),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 50),
|
const SizedBox(height: 50),
|
||||||
if (group.isGroupAdmin && !group.leftGroup)
|
if (_group!.isGroupAdmin && !_group!.leftGroup)
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.pencil,
|
icon: FontAwesomeIcons.pencil,
|
||||||
text: context.lang.groupNameInput,
|
text: context.lang.groupNameInput,
|
||||||
onTap: _updateGroupName,
|
onTap: _updateGroupName,
|
||||||
),
|
),
|
||||||
SelectChatDeletionTimeListTitle(
|
SelectChatDeletionTimeListTitle(
|
||||||
groupId: widget.group.groupId,
|
groupId: widget.groupId,
|
||||||
disabled: !group.isGroupAdmin,
|
disabled: !_group!.isGroupAdmin,
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|
@ -203,7 +207,7 @@ class _GroupViewState extends State<GroupView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (group.isGroupAdmin && !group.leftGroup)
|
if (_group!.isGroupAdmin && !_group!.leftGroup)
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.plus,
|
icon: FontAwesomeIcons.plus,
|
||||||
text: context.lang.addMember,
|
text: context.lang.addMember,
|
||||||
|
|
@ -216,7 +220,7 @@ class _GroupViewState extends State<GroupView> {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
text: context.lang.you,
|
text: context.lang.you,
|
||||||
trailing: (group.isGroupAdmin) ? Text(context.lang.admin) : null,
|
trailing: (_group!.isGroupAdmin) ? Text(context.lang.admin) : null,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await Navigator.push(
|
await Navigator.push(
|
||||||
context,
|
context,
|
||||||
|
|
@ -229,7 +233,7 @@ class _GroupViewState extends State<GroupView> {
|
||||||
...members.map((member) {
|
...members.map((member) {
|
||||||
return GroupMemberContextMenu(
|
return GroupMemberContextMenu(
|
||||||
key: ValueKey(member.$1.userId),
|
key: ValueKey(member.$1.userId),
|
||||||
group: group,
|
group: _group!,
|
||||||
contact: member.$1,
|
contact: member.$1,
|
||||||
member: member.$2,
|
member: member.$2,
|
||||||
child: BetterListTile(
|
child: BetterListTile(
|
||||||
|
|
@ -256,7 +260,7 @@ class _GroupViewState extends State<GroupView> {
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
if (!group.leftGroup)
|
if (!_group!.leftGroup)
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.rightFromBracket,
|
icon: FontAwesomeIcons.rightFromBracket,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
|
|
@ -289,7 +293,7 @@ Future<String?> showGroupNameChangeDialog(
|
||||||
|
|
||||||
return showDialog<String>(
|
return showDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(context.lang.groupNameInput),
|
title: Text(context.lang.groupNameInput),
|
||||||
content: TextField(
|
content: TextField(
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ class _GroupCreateSelectGroupNameViewState
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
restorationId: 'new_message_users_list',
|
restorationId: 'new_message_users_list',
|
||||||
itemCount: widget.selectedUsers.length,
|
itemCount: widget.selectedUsers.length,
|
||||||
itemBuilder: (BuildContext context, int i) {
|
itemBuilder: (context, i) {
|
||||||
final user = widget.selectedUsers[i];
|
final user = widget.selectedUsers[i];
|
||||||
return UserContextMenu(
|
return UserContextMenu(
|
||||||
key: ValueKey(user.userId),
|
key: ValueKey(user.userId),
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ import 'package:twonly/src/views/components/user_context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/groups/group_create_select_group_name.view.dart';
|
import 'package:twonly/src/views/groups/group_create_select_group_name.view.dart';
|
||||||
|
|
||||||
class GroupCreateSelectMembersView extends StatefulWidget {
|
class GroupCreateSelectMembersView extends StatefulWidget {
|
||||||
const GroupCreateSelectMembersView({this.group, super.key});
|
const GroupCreateSelectMembersView({this.groupId, super.key});
|
||||||
final Group? group;
|
final String? groupId;
|
||||||
@override
|
@override
|
||||||
State<GroupCreateSelectMembersView> createState() => _StartNewChatView();
|
State<GroupCreateSelectMembersView> createState() => _StartNewChatView();
|
||||||
}
|
}
|
||||||
|
|
@ -46,9 +46,8 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initAsync() async {
|
Future<void> initAsync() async {
|
||||||
if (widget.group != null) {
|
if (widget.groupId != null) {
|
||||||
final members =
|
final members = await twonlyDB.groupsDao.getGroupContact(widget.groupId!);
|
||||||
await twonlyDB.groupsDao.getGroupContact(widget.group!.groupId);
|
|
||||||
for (final member in members) {
|
for (final member in members) {
|
||||||
alreadyInGroup.add(member.userId);
|
alreadyInGroup.add(member.userId);
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +100,7 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> submitChanges() async {
|
Future<void> submitChanges() async {
|
||||||
if (widget.group != null) {
|
if (widget.groupId != null) {
|
||||||
Navigator.pop(context, selectedUsers.toList());
|
Navigator.pop(context, selectedUsers.toList());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +124,7 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
widget.group == null
|
widget.groupId == null
|
||||||
? context.lang.selectMembers
|
? context.lang.selectMembers
|
||||||
: context.lang.addMember,
|
: context.lang.addMember,
|
||||||
),
|
),
|
||||||
|
|
@ -133,7 +132,9 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
floatingActionButton: FilledButton.icon(
|
floatingActionButton: FilledButton.icon(
|
||||||
onPressed: selectedUsers.isEmpty ? null : submitChanges,
|
onPressed: selectedUsers.isEmpty ? null : submitChanges,
|
||||||
label: Text(
|
label: Text(
|
||||||
widget.group == null ? context.lang.next : context.lang.updateGroup,
|
widget.groupId == null
|
||||||
|
? context.lang.next
|
||||||
|
: context.lang.updateGroup,
|
||||||
),
|
),
|
||||||
icon: const FaIcon(FontAwesomeIcons.penToSquare),
|
icon: const FaIcon(FontAwesomeIcons.penToSquare),
|
||||||
),
|
),
|
||||||
|
|
@ -162,7 +163,7 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
restorationId: 'new_message_users_list',
|
restorationId: 'new_message_users_list',
|
||||||
itemCount:
|
itemCount:
|
||||||
contacts.length + (selectedUsers.isEmpty ? 0 : 2),
|
contacts.length + (selectedUsers.isEmpty ? 0 : 2),
|
||||||
itemBuilder: (BuildContext context, int i) {
|
itemBuilder: (context, i) {
|
||||||
if (selectedUsers.isNotEmpty) {
|
if (selectedUsers.isNotEmpty) {
|
||||||
final selected = selectedUsers.toList();
|
final selected = selectedUsers.toList();
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
|
@ -229,7 +230,7 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
onChanged: (bool? value) {
|
onChanged: (value) {
|
||||||
toggleSelectedUser(user.userId);
|
toggleSelectedUser(user.userId);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/groups.table.dart';
|
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
|
@ -9,7 +11,6 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/services/group.services.dart';
|
import 'package:twonly/src/services/group.services.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/components/context_menu.component.dart';
|
import 'package:twonly/src/views/components/context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/groups/group.view.dart';
|
import 'package:twonly/src/views/groups/group.view.dart';
|
||||||
|
|
@ -128,12 +129,7 @@ class GroupMemberContextMenu extends StatelessWidget {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
await Navigator.push(
|
await context.push(Routes.chatsMessages, extra: directChat);
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => ChatMessagesView(directChat),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
icon: FontAwesomeIcons.message,
|
icon: FontAwesomeIcons.message,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:app_links/app_links.dart';
|
import 'package:app_links/app_links.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
||||||
import 'package:flutter_sharing_intent/flutter_sharing_intent.dart';
|
|
||||||
import 'package:flutter_sharing_intent/model/sharing_file.dart';
|
import 'package:flutter_sharing_intent/model/sharing_file.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -107,8 +105,7 @@ class HomeViewState extends State<HomeView> {
|
||||||
activePageIdx = index;
|
activePageIdx = index;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
selectNotificationStream.stream
|
selectNotificationStream.stream.listen((response) async {
|
||||||
.listen((NotificationResponse? response) async {
|
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
globalUpdateOfHomeViewPageIndex(0);
|
||||||
});
|
});
|
||||||
unawaited(_mainCameraController.selectCamera(0, true));
|
unawaited(_mainCameraController.selectCamera(0, true));
|
||||||
|
|
@ -126,32 +123,11 @@ class HomeViewState extends State<HomeView> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_intentStreamSub = FlutterSharingIntent.instance.getMediaStream().listen(
|
_intentStreamSub = initIntentStreams(
|
||||||
(f) {
|
|
||||||
if (mounted) {
|
|
||||||
handleIntentSharedFile(
|
|
||||||
context,
|
context,
|
||||||
f,
|
|
||||||
_mainCameraController.setSharedLinkForPreview,
|
_mainCameraController.setSharedLinkForPreview,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
// ignore: inference_failure_on_untyped_parameter
|
|
||||||
onError: (err) {
|
|
||||||
Log.error('getIntentDataStream error: $err');
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
FlutterSharingIntent.instance.getInitialSharing().then((f) {
|
|
||||||
if (mounted) {
|
|
||||||
handleIntentSharedFile(
|
|
||||||
context,
|
|
||||||
f,
|
|
||||||
_mainCameraController.setSharedLinkForPreview,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
|
@ -261,7 +237,7 @@ class HomeViewState extends State<HomeView> {
|
||||||
label: '',
|
label: '',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onTap: (int index) async {
|
onTap: (index) async {
|
||||||
activePageIdx = index;
|
activePageIdx = index;
|
||||||
await homeViewPageController.animateToPage(
|
await homeViewPageController.animateToPage(
|
||||||
index,
|
index,
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
Positioned(
|
Positioned(
|
||||||
right: 5,
|
right: 5,
|
||||||
child: PopupMenuButton<String>(
|
child: PopupMenuButton<String>(
|
||||||
onSelected: (String result) async {
|
onSelected: (result) async {
|
||||||
if (result == 'delete') {
|
if (result == 'delete') {
|
||||||
await deleteFile();
|
await deleteFile();
|
||||||
}
|
}
|
||||||
|
|
@ -203,8 +203,7 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
await exportFile();
|
await exportFile();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (context) => <PopupMenuEntry<String>>[
|
||||||
<PopupMenuEntry<String>>[
|
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: 'delete',
|
value: 'delete',
|
||||||
child: Text(context.lang.galleryDelete),
|
child: Text(context.lang.galleryDelete),
|
||||||
|
|
@ -243,7 +242,6 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
child: VideoPlayerWrapper(
|
child: VideoPlayerWrapper(
|
||||||
videoPath: filePath,
|
videoPath: filePath,
|
||||||
),
|
),
|
||||||
// childSize: const Size(300, 300),
|
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
maxScale: PhotoViewComputedScale.covered * 4.1,
|
maxScale: PhotoViewComputedScale.covered * 4.1,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:restart_app/restart_app.dart';
|
import 'package:restart_app/restart_app.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/model/json/userdata.dart';
|
import 'package:twonly/src/model/json/userdata.dart';
|
||||||
import 'package:twonly/src/services/twonly_safe/restore.twonly_safe.dart';
|
import 'package:twonly/src/services/twonly_safe/restore.twonly_safe.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/settings/backup/twonly_safe_server.view.dart';
|
|
||||||
|
|
||||||
class BackupRecoveryView extends StatefulWidget {
|
class BackupRecoveryView extends StatefulWidget {
|
||||||
const BackupRecoveryView({super.key});
|
const BackupRecoveryView({super.key});
|
||||||
|
|
@ -135,14 +136,8 @@ class _BackupRecoveryViewState extends State<BackupRecoveryView> {
|
||||||
Center(
|
Center(
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
backupServer = await Navigator.push(
|
backupServer =
|
||||||
context,
|
await context.push(Routes.settingsBackupServer);
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const TwonlySafeServerView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
child: Text(context.lang.backupExpertSettings),
|
child: Text(context.lang.backupExpertSettings),
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||||
import 'package:twonly/src/model/json/userdata.dart';
|
import 'package:twonly/src/model/json/userdata.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||||
|
|
@ -17,7 +18,6 @@ import 'package:twonly/src/utils/pow.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/groups/group.view.dart';
|
import 'package:twonly/src/views/groups/group.view.dart';
|
||||||
import 'package:twonly/src/views/onboarding/recover.view.dart';
|
|
||||||
|
|
||||||
class RegisterView extends StatefulWidget {
|
class RegisterView extends StatefulWidget {
|
||||||
const RegisterView({
|
const RegisterView({
|
||||||
|
|
@ -302,16 +302,8 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
onPressed: () async {
|
onPressed: () =>
|
||||||
await Navigator.push(
|
context.push(Routes.settingsBackupRecovery),
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const BackupRecoveryView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
label: Text(context.lang.twonlySafeRecoverBtn),
|
label: Text(context.lang.twonlySafeRecoverBtn),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/utils/avatars.dart';
|
import 'package:twonly/src/utils/avatars.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/qr.dart';
|
import 'package:twonly/src/utils/qr.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_qr_scanner.view.dart';
|
|
||||||
import 'package:twonly/src/views/components/better_list_title.dart';
|
import 'package:twonly/src/views/components/better_list_title.dart';
|
||||||
|
|
||||||
class PublicProfileView extends StatefulWidget {
|
class PublicProfileView extends StatefulWidget {
|
||||||
|
|
@ -96,14 +96,7 @@ class _PublicProfileViewState extends State<PublicProfileView> {
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
leading: const FaIcon(FontAwesomeIcons.qrcode),
|
leading: const FaIcon(FontAwesomeIcons.qrcode),
|
||||||
text: context.lang.scanOtherProfile,
|
text: context.lang.scanOtherProfile,
|
||||||
onTap: () {
|
onTap: () => context.push(Routes.cameraQRScanner),
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const QrCodeScanner(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
leading: const FaIcon(
|
leading: const FaIcon(
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
// ignore: inference_failure_on_function_invocation
|
// ignore: inference_failure_on_function_invocation
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(context.lang.settingsAppearanceTheme),
|
title: Text(context.lang.settingsAppearanceTheme),
|
||||||
content: Column(
|
content: Column(
|
||||||
|
|
@ -37,7 +37,7 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
value: ThemeMode.system,
|
value: ThemeMode.system,
|
||||||
groupValue: selectedValue,
|
groupValue: selectedValue,
|
||||||
label: 'System default',
|
label: 'System default',
|
||||||
onChanged: (ThemeMode? value) {
|
onChanged: (value) {
|
||||||
selectedValue = value;
|
selectedValue = value;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
|
@ -46,7 +46,7 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
value: ThemeMode.light,
|
value: ThemeMode.light,
|
||||||
groupValue: selectedValue,
|
groupValue: selectedValue,
|
||||||
label: 'Light',
|
label: 'Light',
|
||||||
onChanged: (ThemeMode? value) {
|
onChanged: (value) {
|
||||||
selectedValue = value;
|
selectedValue = value;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
|
@ -55,7 +55,7 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
value: ThemeMode.dark,
|
value: ThemeMode.dark,
|
||||||
groupValue: selectedValue,
|
groupValue: selectedValue,
|
||||||
label: 'Dark',
|
label: 'Dark',
|
||||||
onChanged: (ThemeMode? value) {
|
onChanged: (value) {
|
||||||
selectedValue = value;
|
selectedValue = value;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/model/json/userdata.dart';
|
import 'package:twonly/src/model/json/userdata.dart';
|
||||||
import 'package:twonly/src/services/twonly_safe/create_backup.twonly_safe.dart';
|
import 'package:twonly/src/services/twonly_safe/create_backup.twonly_safe.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/settings/backup/twonly_safe_backup.view.dart';
|
|
||||||
|
|
||||||
void Function() gUpdateBackupView = () {};
|
void Function() gUpdateBackupView = () {};
|
||||||
|
|
||||||
|
|
@ -60,16 +61,7 @@ class _BackupViewState extends State<BackupView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeTwonlySafePassword() async {
|
Future<void> changeTwonlySafePassword() async {
|
||||||
await Navigator.push(
|
await context.push(Routes.settingsBackupSetup, extra: true);
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const TwonlyIdentityBackupView(
|
|
||||||
isPasswordChangeOnly: true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
setState(() {
|
setState(() {
|
||||||
// gUser was updated
|
// gUser was updated
|
||||||
});
|
});
|
||||||
|
|
@ -205,7 +197,7 @@ class _BackupViewState extends State<BackupView> {
|
||||||
label: context.lang.backupData,
|
label: context.lang.backupData,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onTap: (int index) async {
|
onTap: (index) async {
|
||||||
activePageIdx = index;
|
activePageIdx = index;
|
||||||
await pageController.animateToPage(
|
await pageController.animateToPage(
|
||||||
index,
|
index,
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,14 @@ import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
|
|
||||||
class TwonlySafeServerView extends StatefulWidget {
|
class BackupServerView extends StatefulWidget {
|
||||||
const TwonlySafeServerView({super.key});
|
const BackupServerView({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TwonlySafeServerView> createState() => _TwonlySafeServerViewState();
|
State<BackupServerView> createState() => _BackupServerViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TwonlySafeServerViewState extends State<TwonlySafeServerView> {
|
class _BackupServerViewState extends State<BackupServerView> {
|
||||||
final TextEditingController _urlController = TextEditingController();
|
final TextEditingController _urlController = TextEditingController();
|
||||||
final TextEditingController _usernameController = TextEditingController();
|
final TextEditingController _usernameController = TextEditingController();
|
||||||
final TextEditingController _passwordController = TextEditingController();
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
|
|
@ -2,13 +2,14 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart' show rootBundle;
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart';
|
import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/settings/backup/twonly_safe_server.view.dart';
|
|
||||||
|
|
||||||
class TwonlyIdentityBackupView extends StatefulWidget {
|
class SetupBackupView extends StatefulWidget {
|
||||||
const TwonlyIdentityBackupView({
|
const SetupBackupView({
|
||||||
this.isPasswordChangeOnly = false,
|
this.isPasswordChangeOnly = false,
|
||||||
this.callBack,
|
this.callBack,
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -20,11 +21,10 @@ class TwonlyIdentityBackupView extends StatefulWidget {
|
||||||
final bool isPasswordChangeOnly;
|
final bool isPasswordChangeOnly;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TwonlyIdentityBackupView> createState() =>
|
State<SetupBackupView> createState() => _SetupBackupViewState();
|
||||||
_TwonlyIdentityBackupViewState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
|
class _SetupBackupViewState extends State<SetupBackupView> {
|
||||||
bool obscureText = true;
|
bool obscureText = true;
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
final TextEditingController passwordCtrl = TextEditingController();
|
final TextEditingController passwordCtrl = TextEditingController();
|
||||||
|
|
@ -179,16 +179,7 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Center(
|
Center(
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: () async {
|
onPressed: () => context.push(Routes.settingsBackupServer),
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const TwonlySafeServerView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text(context.lang.backupExpertSettings),
|
child: Text(context.lang.backupExpertSettings),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue