Compare commits

..

No commits in common. "main" and "v0.0.74" have entirely different histories.

181 changed files with 2957 additions and 21288 deletions

4
.gitmodules vendored
View file

@ -2,7 +2,3 @@
[submodule "dependencies"]
path = dependencies
url = https://github.com/twonlyapp/twonly-app-dependencies.git
[submodule "lib/src/localization/translations"]
path = lib/src/localization/translations
# url = ssh://git@git.twonly.eu:22222/twonly/twonly-translations.git
url = https://git.twonly.eu/twonly/twonly-translations

View file

@ -1,30 +1,5 @@
# Changelog
## 0.0.83
- Improved view of the diagnostic log
- Several bug fixes
## 0.0.82
- Added an option in the settings to automatically save all sent images
- Hides duplicate images in the memory
- Fixes a bug where messages were not being received
- Several other minor improvements
## 0.0.81
- Fixes the issue where black/blank images were sometimes received
- Fixes an issue in the image editor
## 0.0.80
- Share images/videos directly from other applications
- More customization options in the appearance settings
- Improved UI for changing the display time of images
- Several minor UI improvements
- Several bug fixes
## 0.0.74
- Improving uploading speed

View file

@ -4,35 +4,20 @@
This repository contains the complete source code of the [twonly](https://twonly.eu) apps.
<!-- <a href="https://testflight.apple.com/join/U9B3v2rk" >
<a href="https://testflight.apple.com/join/U9B3v2rk" >
<img alt="Get it on Testflight button" src="https://twonly.eu/assets/buttons/get-it-on-testflight.png"
width="100px" />
</a> -->
<div class="my-5 space-x-4">
<div class="flex gap-5 items-center justify-center">
<a href="https://apps.apple.com/de/app/twonly/id6743774441">
<img alt="Get it on App Store button" src="https://twonly.eu/assets/buttons/download-on-the-app-store.svg"
width="100px" />
</a>
<a href="https://play.google.com/store/apps/details?id=eu.twonly">
<img alt="Get it on Google-Play button" src="https://twonly.eu/assets/buttons/get-it-in-google-play.webp"
width="110px" />
</a>
<a href="https://github.com/twonlyapp/twonly-app/releases">
<img alt="Get it on Github button" src="https://twonly.eu/assets/buttons/get-it-on-github.webp" width="110px" />
</a>
<a href="https://releases.twonly.eu/fdroid/repo/">
<img alt="Get it on F-Droid button" src="https://twonly.eu/assets/buttons/get-it-on-f-droid.webp" width="105px" />
</a>
</div>
</div>
</a>
<a href="https://releases.twonly.eu/fdroid/repo/">
<img alt="Get it on F-Droid button" src="https://twonly.eu/assets/buttons/get-it-on-f-droid.webp" width="100px" />
</a>
## Features
- Offer a Snapchat™ like experience
- End-to-End encryption using the [Signal Protocol](https://de.wikipedia.org/wiki/Signal-Protokoll)
- Open Source and can be downloaded directly from GitHub
- twonly is Open Source and can be downloaded directly from GitHub
- Developed by humans not by AI or Vibe Coding
- No email or phone number required to register
- Privacy friendly - Everything is stored on the device
- The backend is hosted exclusively in Europe
@ -44,7 +29,6 @@ This repository contains the complete source code of the [twonly](https://twonly
- Implementing [Sealed Sender](https://signal.org/blog/sealed-sender/) to minimize metadata
## Security Issues
If you discover a security issue in twonly, please adhere to the coordinated vulnerability disclosure model. Please send
us your report to security@twonly.eu. We also offer for critical security issues a small bug bounties, but we can not
guarantee a bounty currently :/

View file

@ -33,16 +33,6 @@
<data android:scheme="http" android:host="me.twonly.eu" />
<data android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
@ -56,11 +46,19 @@
android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService"
tools:node="remove">
</service>
<!-- <service
android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
android:foregroundServiceType="dataSync|remoteMessaging"
android:exported="false" /> -->
<meta-data
android:name="eu.twonly.service.TWONLY_LOGO"
android:resource="@drawable/ic_launcher_foreground" />
</application>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
@ -68,7 +66,6 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.android.vending.BILLING" />
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit 7930d9727019344238297d810661bc3e8f724c37
Subproject commit fb66274bf729cde6f7184ec6f7f9ea89f12450fd

View file

@ -209,11 +209,11 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
let systemLanguage = Locale.current.language.languageCode?.identifier ?? "en" // Get the current system language
var pushNotificationText: [PushKind: String] = [:]
var title = "[Unknown]"
var title = "Someone"
// Define the messages based on the system language
if systemLanguage.contains("de") { // German
title = "[Unbekannt]"
title = "Jemand"
pushNotificationText = [
.text: "hat eine Nachricht{inGroup} gesendet.",
.twonly: "hat ein twonly{inGroup} gesendet.",

View file

@ -43,13 +43,6 @@ target 'Runner' do
target 'RunnerTests' do
inherit! :search_paths
end
# Share Extension is name of Extension which you created which is in this case 'Share Extension'
target 'ShareExtension' do
inherit! :search_paths
# flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
end
post_install do |installer|

View file

@ -54,55 +54,55 @@ PODS:
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Firebase (12.6.0):
- Firebase/Core (= 12.6.0)
- Firebase/Core (12.6.0):
- Firebase (12.4.0):
- Firebase/Core (= 12.4.0)
- Firebase/Core (12.4.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 12.6.0)
- Firebase/CoreOnly (12.6.0):
- FirebaseCore (~> 12.6.0)
- Firebase/Messaging (12.6.0):
- FirebaseAnalytics (~> 12.4.0)
- Firebase/CoreOnly (12.4.0):
- FirebaseCore (~> 12.4.0)
- Firebase/Messaging (12.4.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 12.6.0)
- firebase_core (4.3.0):
- Firebase/CoreOnly (= 12.6.0)
- FirebaseMessaging (~> 12.4.0)
- firebase_core (4.2.1):
- Firebase/CoreOnly (= 12.4.0)
- Flutter
- firebase_messaging (16.1.0):
- Firebase/Messaging (= 12.6.0)
- firebase_messaging (16.0.4):
- Firebase/Messaging (= 12.4.0)
- firebase_core
- Flutter
- FirebaseAnalytics (12.6.0):
- FirebaseAnalytics/Default (= 12.6.0)
- FirebaseCore (~> 12.6.0)
- FirebaseInstallations (~> 12.6.0)
- FirebaseAnalytics (12.4.0):
- FirebaseAnalytics/Default (= 12.4.0)
- FirebaseCore (~> 12.4.0)
- FirebaseInstallations (~> 12.4.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseAnalytics/Default (12.6.0):
- FirebaseCore (~> 12.6.0)
- FirebaseInstallations (~> 12.6.0)
- GoogleAppMeasurement/Default (= 12.6.0)
- FirebaseAnalytics/Default (12.4.0):
- FirebaseCore (~> 12.4.0)
- FirebaseInstallations (~> 12.4.0)
- GoogleAppMeasurement/Default (= 12.4.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseCore (12.6.0):
- FirebaseCoreInternal (~> 12.6.0)
- FirebaseCore (12.4.0):
- FirebaseCoreInternal (~> 12.4.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreInternal (12.6.0):
- FirebaseCoreInternal (12.4.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseInstallations (12.6.0):
- FirebaseCore (~> 12.6.0)
- FirebaseInstallations (12.4.0):
- FirebaseCore (~> 12.4.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4)
- FirebaseMessaging (12.6.0):
- FirebaseCore (~> 12.6.0)
- FirebaseInstallations (~> 12.6.0)
- FirebaseMessaging (12.4.0):
- FirebaseCore (~> 12.4.0)
- FirebaseInstallations (~> 12.4.0)
- GoogleDataTransport (~> 10.1)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1)
@ -122,8 +122,6 @@ PODS:
- flutter_secure_storage_darwin (10.0.0):
- Flutter
- FlutterMacOS
- flutter_sharing_intent (1.0.1):
- Flutter
- flutter_volume_controller (0.0.1):
- Flutter
- gal (1.0.0):
@ -136,28 +134,28 @@ PODS:
- google_mlkit_commons (0.11.0):
- Flutter
- MLKitVision
- GoogleAdsOnDeviceConversion (3.2.0):
- GoogleAdsOnDeviceConversion (3.1.0):
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Core (12.6.0):
- GoogleAppMeasurement/Core (12.4.0):
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Default (12.6.0):
- GoogleAdsOnDeviceConversion (~> 3.2.0)
- GoogleAppMeasurement/Core (= 12.6.0)
- GoogleAppMeasurement/IdentitySupport (= 12.6.0)
- GoogleAppMeasurement/Default (12.4.0):
- GoogleAdsOnDeviceConversion (~> 3.1.0)
- GoogleAppMeasurement/Core (= 12.4.0)
- GoogleAppMeasurement/IdentitySupport (= 12.4.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/IdentitySupport (12.6.0):
- GoogleAppMeasurement/Core (= 12.6.0)
- GoogleAppMeasurement/IdentitySupport (12.4.0):
- GoogleAppMeasurement/Core (= 12.4.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
@ -219,9 +217,6 @@ PODS:
- GTMSessionFetcher/Core (3.5.0)
- image_picker_ios (0.0.1):
- Flutter
- in_app_purchase_storekit (0.0.1):
- Flutter
- FlutterMacOS
- libwebp (1.5.0):
- libwebp/demux (= 1.5.0)
- libwebp/mux (= 1.5.0)
@ -262,7 +257,7 @@ PODS:
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- no_screenshot (0.3.2-beta.3):
- "no_screenshot (0.0.1+4)":
- Flutter
- ScreenProtectorKit (~> 1.3.1)
- objective_c (0.0.1):
@ -278,10 +273,10 @@ PODS:
- restart_app (0.0.1):
- Flutter
- ScreenProtectorKit (1.3.1)
- SDWebImage (5.21.5):
- SDWebImage/Core (= 5.21.5)
- SDWebImage/Core (5.21.5)
- SDWebImageWebPCoder (0.15.0):
- SDWebImage (5.21.3):
- SDWebImage/Core (= 5.21.3)
- SDWebImage/Core (5.21.3)
- SDWebImageWebPCoder (0.14.6):
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.17)
- Sentry/HybridSDK (8.56.2)
@ -322,7 +317,7 @@ PODS:
- sqlite3/perf-threadsafe
- sqlite3/rtree
- sqlite3/session
- SwiftProtobuf (1.33.3)
- SwiftProtobuf (1.33.1)
- SwiftyGif (5.4.5)
- url_launcher_ios (0.0.1):
- Flutter
@ -352,14 +347,12 @@ DEPENDENCIES:
- flutter_keyboard_visibility_temp_fork (from `.symlinks/plugins/flutter_keyboard_visibility_temp_fork/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage_darwin (from `.symlinks/plugins/flutter_secure_storage_darwin/darwin`)
- flutter_sharing_intent (from `.symlinks/plugins/flutter_sharing_intent/ios`)
- flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/ios`)
- gal (from `.symlinks/plugins/gal/darwin`)
- google_mlkit_barcode_scanning (from `.symlinks/plugins/google_mlkit_barcode_scanning/ios`)
- google_mlkit_commons (from `.symlinks/plugins/google_mlkit_commons/ios`)
- GoogleUtilities
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
- no_screenshot (from `.symlinks/plugins/no_screenshot/ios`)
- objective_c (from `.symlinks/plugins/objective_c/ios`)
@ -444,8 +437,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_secure_storage_darwin:
:path: ".symlinks/plugins/flutter_secure_storage_darwin/darwin"
flutter_sharing_intent:
:path: ".symlinks/plugins/flutter_sharing_intent/ios"
flutter_volume_controller:
:path: ".symlinks/plugins/flutter_volume_controller/ios"
gal:
@ -456,8 +447,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/google_mlkit_commons/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
in_app_purchase_storekit:
:path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
local_auth_darwin:
:path: ".symlinks/plugins/local_auth_darwin/darwin"
no_screenshot:
@ -500,33 +489,31 @@ SPEC CHECKSUMS:
emoji_picker_flutter: ece213fc274bdddefb77d502d33080dc54e616cc
ffmpeg_kit_flutter_new: 12426a19f10ac81186c67c6ebc4717f8f4364b7f
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Firebase: a451a7b61536298fd5cbfe3a746fd40443a50679
firebase_core: ba00a168e719694f38960502ceb560285603d073
firebase_messaging: bf0e29321927edc02a563c984dbfa5b063864b15
FirebaseAnalytics: d0a97a0db6425e5a5d966340b87f92ca7b13a557
FirebaseCore: 0e38ad5d62d980a47a64b8e9301ffa311457be04
FirebaseCoreInternal: 69bf1306a05b8ac43004f6cc1f804bb7b05b229e
FirebaseInstallations: 631b38da2e11a83daa4bfb482f79d286a5dfa7ad
FirebaseMessaging: a61bc42dcab3f7a346d94bbb54dab2c9435b18b2
Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e
firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594
firebase_messaging: c17a29984eafce4b2997fe078bb0a9e0b06f5dde
FirebaseAnalytics: 0fc2b20091f0ddd21bf73397cf8f0eb5346dc24f
FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3
FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6
FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2
FirebaseMessaging: d33971b7bb252745ea6cd31ab190d1a1df4b8ed5
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
flutter_keyboard_visibility_temp_fork: 95b2d534bacf6ac62e7fcbe5c2a9e2c2a17ce06f
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
flutter_secure_storage_darwin: acdb3f316ed05a3e68f856e0353b133eec373a23
flutter_sharing_intent: 0c1e53949f09fa8df8ac2268505687bde8ff264c
flutter_secure_storage_darwin: ce237a8775b39723566dc72571190a3769d70468
flutter_volume_controller: c2be490cb0487e8b88d0d9fc2b7e1c139a4ebccb
gal: baecd024ebfd13c441269ca7404792a7152fde89
google_mlkit_barcode_scanning: 8f5987f244a43fe1167689c548342a5174108159
google_mlkit_commons: 2abe6a70e1824e431d16a51085cb475b672c8aab
GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f
GoogleAppMeasurement: 3bf40aff49a601af5da1c3345702fcb4991d35ee
GoogleAdsOnDeviceConversion: e03a386840803ea7eef3fd22a061930142c039c1
GoogleAppMeasurement: 1e718274b7e015cefd846ac1fcf7820c70dc017d
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
@ -535,7 +522,7 @@ SPEC CHECKSUMS:
MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d
MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
no_screenshot: 89e778ede9f1e39cc3fb9404d782a42712f2a0b2
no_screenshot: 6d183496405a3ab709a67a54e5cd0f639e94729e
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
@ -543,8 +530,8 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
restart_app: 9cda5378aacc5000e3f66ee76a9201534e7d3ecf
ScreenProtectorKit: 83a6281b02c7a5902ee6eac4f5045f674e902ae4
SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838
SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377
SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
Sentry: b53951377b78e21a734f5dc8318e333dbfc682d7
sentry_flutter: 4c33648b7e83310aa1fdb1b10c5491027d9643f0
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
@ -552,11 +539,11 @@ SPEC CHECKSUMS:
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
SwiftProtobuf: e1b437c8e31a4c5577b643249a0bb62ed4f02153
SwiftProtobuf: 533a18409c3ca3a6156b2b1e46afd0f69e751aba
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
PODFILE CHECKSUM: ae041999f13ba7b2285ff9ad9bc688ed647bbcb7
PODFILE CHECKSUM: c0c524475498435108850efecde62ba98e081c25
COCOAPODS: 1.16.2

View file

@ -10,7 +10,6 @@
05CF222065FC24670B05B6D0 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC1EE71614E1B4F84D6FDC2D /* Pods_RunnerTests.framework */; };
06AA21445BEAF2C45DC9DCDF /* Pods_NotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A198C9B5D90584C4F96206B2 /* Pods_NotificationService.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
30EBDD0F93DC44E774F3B785 /* Pods_ShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E190E82D9973B318A389650B /* Pods_ShareExtension.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
@ -19,8 +18,6 @@
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
CA4FDF5DD8F229C30DE512AF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE2CCFEE4ABECF33852F7735 /* Pods_Runner.framework */; };
D21FCEAB2D9F2B750088701D /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D21FCEA42D9F2B750088701D /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
D25D4D1E2EF626E30029F805 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D25D4D1D2EF626E30029F805 /* StoreKit.framework */; };
D25D4D7A2EFF41DB0029F805 /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D25D4D702EFF41DB0029F805 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
F3C66D726A2EB28484DF0B10 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 16FBC6F5B58E1C6646F5D447 /* GoogleService-Info.plist */; };
/* End PBXBuildFile section */
@ -39,13 +36,6 @@
remoteGlobalIDString = D21FCEA32D9F2B750088701D;
remoteInfo = NotificationService;
};
D25D4D782EFF41DB0029F805 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = D25D4D6F2EFF41DB0029F805;
remoteInfo = ShareExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@ -65,7 +55,6 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
D25D4D7A2EFF41DB0029F805 /* ShareExtension.appex in Embed Foundation Extensions */,
D21FCEAB2D9F2B750088701D /* NotificationService.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
@ -77,12 +66,10 @@
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
1581CC44342D555EFB889768 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
15CEF849B61A620CFB2DC5F1 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
16FBC6F5B58E1C6646F5D447 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
35366FD433E0EFC6EF19A452 /* Pods-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.release.xcconfig"; sourceTree = "<group>"; };
39FB86A38393489D58A01B0B /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4D78471482626812FE2468E9 /* Pods-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.debug.xcconfig"; sourceTree = "<group>"; };
6EB462F87F0A23758713308F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
@ -99,15 +86,10 @@
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A198C9B5D90584C4F96206B2 /* Pods_NotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A22BD564F16069E5FCB60767 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = "<group>"; };
B3B27B7FBEEA31DB7793A0C2 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
D21FCEA42D9F2B750088701D /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
D2265DD42D920142000D99BB /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
D25D4D1D2EF626E30029F805 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
D25D4D702EFF41DB0029F805 /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
D25D4D802EFF437F0029F805 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = "<group>"; };
DC1EE71614E1B4F84D6FDC2D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E190E82D9973B318A389650B /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E96A5ACA32A7118204F050A5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
EE2CCFEE4ABECF33852F7735 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F02F7A1D63544AA9F23A1085 /* Pods-NotificationService.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.profile.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.profile.xcconfig"; sourceTree = "<group>"; };
@ -121,13 +103,6 @@
);
target = D21FCEA32D9F2B750088701D /* NotificationService */;
};
D25D4D7E2EFF41DB0029F805 /* Exceptions for "ShareExtension" folder in "ShareExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = D25D4D6F2EFF41DB0029F805 /* ShareExtension */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
@ -139,14 +114,6 @@
path = NotificationService;
sourceTree = "<group>";
};
D25D4D712EFF41DB0029F805 /* ShareExtension */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
D25D4D7E2EFF41DB0029F805 /* Exceptions for "ShareExtension" folder in "ShareExtension" target */,
);
path = ShareExtension;
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
@ -163,7 +130,6 @@
buildActionMask = 2147483647;
files = (
CA4FDF5DD8F229C30DE512AF /* Pods_Runner.framework in Frameworks */,
D25D4D1E2EF626E30029F805 /* StoreKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -175,14 +141,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D25D4D6D2EFF41DB0029F805 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
30EBDD0F93DC44E774F3B785 /* Pods_ShareExtension.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@ -211,7 +169,6 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
D21FCEA52D9F2B750088701D /* NotificationService */,
D25D4D712EFF41DB0029F805 /* ShareExtension */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
16FBC6F5B58E1C6646F5D447 /* GoogleService-Info.plist */,
@ -226,7 +183,6 @@
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
D21FCEA42D9F2B750088701D /* NotificationService.appex */,
D25D4D702EFF41DB0029F805 /* ShareExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@ -234,7 +190,6 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
D25D4D802EFF437F0029F805 /* RunnerDebug.entitlements */,
D2265DD42D920142000D99BB /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
@ -251,11 +206,9 @@
E5079CCEE4804DB65AA3F23F /* Frameworks */ = {
isa = PBXGroup;
children = (
D25D4D1D2EF626E30029F805 /* StoreKit.framework */,
A198C9B5D90584C4F96206B2 /* Pods_NotificationService.framework */,
EE2CCFEE4ABECF33852F7735 /* Pods_Runner.framework */,
DC1EE71614E1B4F84D6FDC2D /* Pods_RunnerTests.framework */,
E190E82D9973B318A389650B /* Pods_ShareExtension.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -272,9 +225,6 @@
4D78471482626812FE2468E9 /* Pods-NotificationService.debug.xcconfig */,
35366FD433E0EFC6EF19A452 /* Pods-NotificationService.release.xcconfig */,
F02F7A1D63544AA9F23A1085 /* Pods-NotificationService.profile.xcconfig */,
15CEF849B61A620CFB2DC5F1 /* Pods-ShareExtension.debug.xcconfig */,
A22BD564F16069E5FCB60767 /* Pods-ShareExtension.release.xcconfig */,
39FB86A38393489D58A01B0B /* Pods-ShareExtension.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -320,7 +270,6 @@
);
dependencies = (
D21FCEAA2D9F2B750088701D /* PBXTargetDependency */,
D25D4D792EFF41DB0029F805 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
@ -348,27 +297,6 @@
productReference = D21FCEA42D9F2B750088701D /* NotificationService.appex */;
productType = "com.apple.product-type.app-extension";
};
D25D4D6F2EFF41DB0029F805 /* ShareExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = D25D4D7F2EFF41DB0029F805 /* Build configuration list for PBXNativeTarget "ShareExtension" */;
buildPhases = (
627F39EA1643E08048D23996 /* [CP] Check Pods Manifest.lock */,
D25D4D6C2EFF41DB0029F805 /* Sources */,
D25D4D6D2EFF41DB0029F805 /* Frameworks */,
D25D4D6E2EFF41DB0029F805 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
D25D4D712EFF41DB0029F805 /* ShareExtension */,
);
name = ShareExtension;
productName = ShareExtension;
productReference = D25D4D702EFF41DB0029F805 /* ShareExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -376,7 +304,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 2610;
LastSwiftUpdateCheck = 1620;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
@ -391,9 +319,6 @@
D21FCEA32D9F2B750088701D = {
CreatedOnToolsVersion = 16.2;
};
D25D4D6F2EFF41DB0029F805 = {
CreatedOnToolsVersion = 26.1.1;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
@ -412,7 +337,6 @@
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
D21FCEA32D9F2B750088701D /* NotificationService */,
D25D4D6F2EFF41DB0029F805 /* ShareExtension */,
);
};
/* End PBXProject section */
@ -444,13 +368,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D25D4D6E2EFF41DB0029F805 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@ -531,28 +448,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
};
627F39EA1643E08048D23996 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-ShareExtension-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@ -634,13 +529,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D25D4D6C2EFF41DB0029F805 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@ -654,11 +542,6 @@
target = D21FCEA32D9F2B750088701D /* NotificationService */;
targetProxy = D21FCEA92D9F2B750088701D /* PBXContainerItemProxy */;
};
D25D4D792EFF41DB0029F805 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D25D4D6F2EFF41DB0029F805 /* ShareExtension */;
targetProxy = D25D4D782EFF41DB0029F805 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@ -744,7 +627,6 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
CUSTOM_GROUP_ID = group.eu.twonly.shareIntent;
DEVELOPMENT_TEAM = CN332ZUGRP;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@ -941,9 +823,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
CUSTOM_GROUP_ID = group.eu.twonly.shareIntent;
DEVELOPMENT_TEAM = CN332ZUGRP;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@ -978,7 +859,6 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
CUSTOM_GROUP_ID = group.eu.twonly.shareIntent;
DEVELOPMENT_TEAM = CN332ZUGRP;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@ -1120,133 +1000,6 @@
};
name = Profile;
};
D25D4D7B2EFF41DB0029F805 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 15CEF849B61A620CFB2DC5F1 /* Pods-ShareExtension.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtensionDebug.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CUSTOM_GROUP_ID = group.eu.twonly.shareIntent;
DEVELOPMENT_TEAM = CN332ZUGRP;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 26.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = eu.twonly.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
D25D4D7C2EFF41DB0029F805 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A22BD564F16069E5FCB60767 /* Pods-ShareExtension.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CUSTOM_GROUP_ID = group.eu.twonly.shareIntent;
DEVELOPMENT_TEAM = CN332ZUGRP;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 26.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = eu.twonly.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
D25D4D7D2EFF41DB0029F805 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 39FB86A38393489D58A01B0B /* Pods-ShareExtension.profile.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CUSTOM_GROUP_ID = group.eu.twonly.shareIntent;
DEVELOPMENT_TEAM = CN332ZUGRP;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 26.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = eu.twonly.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Profile;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -1290,16 +1043,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D25D4D7F2EFF41DB0029F805 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D25D4D7B2EFF41DB0029F805 /* Debug */,
D25D4D7C2EFF41DB0029F805 /* Release */,
D25D4D7D2EFF41DB0029F805 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;

View file

@ -3,7 +3,6 @@ import Flutter
import Foundation
import UIKit
import UserNotifications
import flutter_sharing_intent
@main
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
@ -15,17 +14,6 @@ import flutter_sharing_intent
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let sharingIntent = SwiftFlutterSharingIntentPlugin.instance
if sharingIntent.hasSameSchemePrefix(url: url) {
return sharingIntent.application(app, open: url, options: options)
}
// Proceed url handling for other Flutter libraries like app_links
return super.application(app, open: url, options:options)
}
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}

View file

@ -87,21 +87,9 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<!--Disable Firebase Telemetry-->
<key>firebase_performance_collection_deactivated</key>
<true/>
<key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>SharingMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
</array>
<!--...-->
</dict>
</plist>

View file

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:me.twonly.eu</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>group.eu.twonly.shareIntent</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)eu.twonly.shared</string>
</array>
</dict>
</plist>

View file

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Share View Controller-->
<scene sceneID="ceB-am-kn3">
<objects>
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View file

@ -1,553 +0,0 @@
// SOURCE: https://github.com/bhagat-techind/flutter_sharing_intent/blob/main/example/ios/Share%20Extension/FSIShareViewController.swift
// FSIShareViewController.swift
// Merged, optimized controller: uses RSI architecture with all FSI features preserved
// Uses model name `SharingFile` (same fields as SharedMediaFile) where `value` = path
import AVFoundation
import MobileCoreServices
import Social
import UIKit
import UniformTypeIdentifiers
public let kSchemePrefix = "SharingMedia"
public let kUserDefaultsKey = "SharingKey"
public let kUserDefaultsMessageKey = "SharingMessageKey"
public let kAppGroupIdKey = "AppGroupId"
public let kAppChannel = "flutter_sharing_intent"
@available(swift, introduced: 5.0)
open class FSIShareViewController: SLComposeServiceViewController {
// MARK: - Config
private(set) var hostAppBundleIdentifier: String = ""
private(set) var appGroupId: String = ""
// Results
private var sharedMedia: [SharingFile] = []
// Debug
private let debugLogs = false
// MARK: - Lifecycle
open override func viewDidLoad() {
super.viewDidLoad()
loadIds()
}
open override func isContentValid() -> Bool {
return true
}
open override func didSelectPost() {
if self.sharedMedia.isEmpty {
if let text = self.contentText, !text.isEmpty {
self.sharedMedia.append(
SharingFile(value: text, thumbnail: nil, duration: nil, type: .text)
)
self.saveAndRedirect(message: text)
return
}
self.completeAndExit()
} else {
self.saveAndRedirect()
}
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Process attachments automatically on appear like original FSI
processAttachments()
}
// MARK: - Load Ids
private func loadIds() {
let shareExtId = Bundle.main.bundleIdentifier ?? ""
if let idx = shareExtId.lastIndex(of: ".") {
hostAppBundleIdentifier = String(shareExtId[..<idx])
} else {
hostAppBundleIdentifier = shareExtId
}
let custom = Bundle.main.object(forInfoDictionaryKey: kAppGroupIdKey) as? String
appGroupId = custom ?? "group.\(hostAppBundleIdentifier)"
log("loaded host=\(hostAppBundleIdentifier) group=\(appGroupId)")
}
// MARK: - Attachment processing (clean RSI style, preserve FSI features)
private func processAttachments() {
guard let content = extensionContext?.inputItems.first as? NSExtensionItem else {
completeAndExit()
return
}
guard let attachments = content.attachments, !attachments.isEmpty else {
completeAndExit()
return
}
// Use DispatchGroup to wait for async loads
let group = DispatchGroup()
for (index, provider) in attachments.enumerated() {
group.enter()
// Try all SharedMediaType options similar to RSI but preserve explicit FSI order
if provider.isImage {
provider.loadItem(forTypeIdentifier: UType.image, options: nil) { [weak self] data, error in
defer { group.leave() }
guard let self = self, error == nil else { self?.dismissWithError(); return }
self.handleImageItem(data: data, index: index, total: attachments.count)
}
continue
}
if provider.isMovie {
provider.loadItem(forTypeIdentifier: UType.movie, options: nil) { [weak self] data, error in
defer { group.leave() }
guard let self = self, error == nil else { self?.dismissWithError(); return }
self.handleVideoItem(data: data, index: index, total: attachments.count)
}
continue
}
if provider.isFile {
provider.loadItem(forTypeIdentifier: UType.fileURL, options: nil) { [weak self] data, error in
defer { group.leave() }
guard let self = self, error == nil else { self?.dismissWithError(); return }
self.handleFileItem(data: data, index: index, total: attachments.count)
}
continue
}
if provider.isURL {
provider.loadItem(forTypeIdentifier: UType.url, options: nil) { [weak self] data, error in
defer { group.leave() }
guard let self = self, error == nil else { self?.dismissWithError(); return }
self.handleUrlItem(data: data, index: index, total: attachments.count)
}
continue
}
if provider.isText {
let id = provider.hasItemConformingToTypeIdentifier(UType.plainText)
? UType.plainText
: UType.text
provider.loadItem(forTypeIdentifier: id, options: nil) { [weak self] data, error in
defer { group.leave() }
guard let self = self, error == nil else { self?.dismissWithError(); return }
self.handleTextItem(data: data, index: index, total: attachments.count)
}
continue
}
if provider.isData {
provider.loadItem(forTypeIdentifier: UType.data, options: nil) { [weak self] data, error in
defer { group.leave() }
guard let self = self, error == nil else { self?.dismissWithError(); return }
self.handleFileItem(data: data, index: index, total: attachments.count)
}
continue
}
if provider.isItem {
provider.loadItem(forTypeIdentifier: UType.item, options: nil) { [weak self] data, error in
defer { group.leave() }
guard let self = self, error == nil else { self?.dismissWithError(); return }
self.handleFileItem(data: data, index: index, total: attachments.count)
}
continue
}
log("Unknown provider type: \(provider.registeredTypeIdentifiers)")
// Unknown type: just leave
group.leave()
}
group.notify(queue: .main) { [weak self] in
guard let self = self else { return }
// if we have media -> media, else fallback to complete
if !self.sharedMedia.isEmpty {
self.saveAndRedirect()
} else {
print("FSIShare: No shared media → stopping.")
self.completeAndExit()
}
}
}
// MARK: - Individual handlers (preserve FSI behavior)
private func handleTextItem(data: NSSecureCoding?, index: Int, total: Int) {
if let s = data as? String {
sharedMedia.append(SharingFile(value: s, thumbnail: nil, duration: nil, type: .text))
} else if let url = data as? URL {
sharedMedia.append(SharingFile(value: url.absoluteString, thumbnail: nil, duration: nil, type: .url))
}
}
private func handleUrlItem(data: NSSecureCoding?, index: Int, total: Int) {
if let url = data as? URL {
sharedMedia.append(SharingFile(value: url.absoluteString, thumbnail: nil, duration: nil, type: .url))
} else if let s = data as? String {
sharedMedia.append(SharingFile(value: s, thumbnail: nil, duration: nil, type: .text))
}
}
private func handleImageItem(data: NSSecureCoding?, index: Int, total: Int) {
// data can be URL, UIImage, or Data
if let url = data as? URL {
let filename = getFileName(from: url, type: .image)
if let dst = containerURL()?.appendingPathComponent(filename) {
if copyFile(at: url, to: dst) {
sharedMedia.append(SharingFile(value: dst.absoluteString, mimeType: url.mimeType(), thumbnail: nil, duration: nil, type: .image))
}
}
} else if let img = data as? UIImage {
if let saved = writeTempImage(img) {
sharedMedia.append(saved)
}
} else if let raw = data as? Data, let img = UIImage(data: raw) {
if let saved = writeTempImage(img) {
sharedMedia.append(saved)
}
}
}
private func handleVideoItem(data: NSSecureCoding?, index: Int, total: Int) {
if let url = data as? URL {
let filename = getFileName(from: url, type: .video)
if let dst = containerURL()?.appendingPathComponent(filename) {
if copyFile(at: url, to: dst) {
if let m = getSharedMediaFile(forVideo: dst) {
sharedMedia.append(m)
}
}
}
}
}
private func handleFileItem(data: NSSecureCoding?, index: Int, total: Int) {
if let url = data as? URL {
let filename = getFileName(from: url, type: .file)
if let dst = containerURL()?.appendingPathComponent(filename) {
if copyFile(at: url, to: dst) {
sharedMedia.append(SharingFile(value: dst.absoluteString, mimeType: url.mimeType(), thumbnail: nil, duration: nil, type: .file))
}
}
}
else if let raw = data as? Data {
let filename = "File_\(UUID().uuidString)"
if let dst = containerURL()?.appendingPathComponent(filename) {
do {
try raw.write(to: dst)
sharedMedia.append(SharingFile(value: dst.absoluteString, mimeType: "application/octet-stream", thumbnail: nil, duration: nil, type: .file))
} catch {}
}
}
}
// MARK: - Helpers: write temp image
private func writeTempImage(_ image: UIImage) -> SharingFile? {
guard let container = containerURL() else { return nil }
let tempName = "TempImage_\(UUID().uuidString).png"
let dst = container.appendingPathComponent(tempName)
do {
if let d = image.pngData() {
try d.write(to: dst)
let decoded = dst.absoluteString.removingPercentEncoding ?? dst.absoluteString
return SharingFile(value: decoded, mimeType: "image/png", thumbnail: nil, duration: nil, type: .image)
}
} catch {
log("writeTempImage error: \(error)")
}
return nil
}
private func saveAndRedirect(message: String? = nil) {
let ud = UserDefaults(suiteName: appGroupId)
if !sharedMedia.isEmpty {
if let data = try? JSONEncoder().encode(sharedMedia) {
ud?.set(data, forKey: kUserDefaultsKey)
}
}
ud?.set(message, forKey: kUserDefaultsMessageKey)
ud?.synchronize()
redirectToHostApp()
}
private func redirectToHostApp() {
// kept for compatibility (RSI style)
loadIds()
// let raw = "\(kSchemePrefix)-\(hostAppBundleIdentifier):share"
let raw = "\(kSchemePrefix)-\(hostAppBundleIdentifier)://dataUrl=\(kUserDefaultsKey)"
guard let url = URL(string: raw.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? raw) else { completeAndExit(); return }
var responder: UIResponder? = self
if #available(iOS 18.0, *) {
while responder != nil {
if let app = responder as? UIApplication { app.open(url, options: [:], completionHandler: nil) }
responder = responder?.next
}
} else {
let sel = sel_registerName("openURL:")
while responder != nil {
if responder?.responds(to: sel) ?? false { _ = responder?.perform(sel, with: url) }
responder = responder?.next
}
}
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
}
// MARK: - File / thumbnail / metadata helpers
func getExtension(from url: URL, type: SharingFileType) -> String {
let parts = url.lastPathComponent.components(separatedBy: ".")
var ex: String? = nil
if parts.count > 1 { ex = parts.last }
if ex == nil {
switch type {
case .image: ex = "png"
case .video: ex = "mp4"
case .file: ex = "txt"
case .text: ex = "txt"
case .url: ex = "txt"
}
}
return ex ?? "bin"
}
func getFileName(from url: URL, type: SharingFileType) -> String {
var name = url.lastPathComponent
if name.isEmpty { name = UUID().uuidString + "." + getExtension(from: url, type: type) }
return name
}
func copyFile(at srcURL: URL, to dstURL: URL) -> Bool {
do {
if FileManager.default.fileExists(atPath: dstURL.path) { try FileManager.default.removeItem(at: dstURL) }
try FileManager.default.copyItem(at: srcURL, to: dstURL)
return true
} catch {
log("copyFile error: \(error)")
return false
}
}
private func getSharedMediaFile(forVideo: URL) -> SharingFile? {
let asset = AVAsset(url: forVideo)
let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded()
let thumbnailPath = getThumbnailPath(for: forVideo)
if FileManager.default.fileExists(atPath: thumbnailPath.path) {
return SharingFile(value: forVideo.absoluteString, mimeType: forVideo.mimeType(), thumbnail: thumbnailPath.absoluteString, duration: Int(duration), type: .video)
}
let gen = AVAssetImageGenerator(asset: asset)
gen.appliesPreferredTrackTransform = true
gen.maximumSize = CGSize(width: 360, height: 360)
// Use first second or zero
let time = CMTime(seconds: min(1.0, CMTimeGetSeconds(asset.duration)), preferredTimescale: 600)
do {
let cg = try gen.copyCGImage(at: time, actualTime: nil)
if let data = UIImage(cgImage: cg).jpegData(compressionQuality: 0.8) {
try data.write(to: thumbnailPath)
return SharingFile(value: forVideo.absoluteString, mimeType: forVideo.mimeType(), thumbnail: thumbnailPath.absoluteString, duration: Int(duration), type: .video)
}
} catch {
log("getSharedMediaFile thumbnail error: \(error)")
}
// fallback
return SharingFile(value: forVideo.absoluteString, mimeType: forVideo.mimeType(), thumbnail: nil, duration: Int(duration), type: .video)
}
private func getThumbnailPath(for url: URL) -> URL {
guard let container = containerURL() else { fatalError("App group not configured or missing") }
let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: "=", with: "")
return container.appendingPathComponent("\(fileName).jpg")
}
private func containerURL() -> URL? {
FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupId)
}
private func completeAndExit() {
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
}
private func dismissWithError() {
log("[ERROR] Error loading data!")
let alert = UIAlertController(title: "Error", message: "Error loading data", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel) { _ in self.dismiss(animated: true, completion: nil) })
present(alert, animated: true, completion: nil)
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
}
private func writeTempFile(_ image: UIImage, to dstURL: URL) -> Bool {
do {
if FileManager.default.fileExists(atPath: dstURL.path) { try FileManager.default.removeItem(at: dstURL) }
let pngData = image.pngData()
try pngData?.write(to: dstURL)
return true
} catch (let error) {
log("writeTempFile error: \(error)")
return false
}
}
private func saveToUserDefaults(data: [SharingFile]) {
let ud = UserDefaults(suiteName: appGroupId)
if let enc = try? JSONEncoder().encode(data) { ud?.set(enc, forKey: kUserDefaultsKey); ud?.synchronize() }
}
// MARK: - Logging
private func log(_ s: String) { if debugLogs { print("[FSIShareVC] \(s)") } }
}
// MARK: - Extensions
extension URL {
func mimeType() -> String {
if #available(iOS 14.0, *) {
if let ut = UTType(filenameExtension: self.pathExtension), let m = ut.preferredMIMEType { return m }
} else {
if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, self.pathExtension as NSString, nil)?.takeRetainedValue() {
if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() { return mimetype as String }
}
}
return "application/octet-stream"
}
}
extension NSItemProvider {
var isImage: Bool { return hasItemConformingToTypeIdentifier(UType.image) }
var isMovie: Bool { return hasItemConformingToTypeIdentifier(UType.movie) }
var isText: Bool {
hasItemConformingToTypeIdentifier(UType.plainText) || hasItemConformingToTypeIdentifier(UType.text)
}
var isURL: Bool { return hasItemConformingToTypeIdentifier(UType.url) }
var isFile: Bool { return hasItemConformingToTypeIdentifier(UType.fileURL) }
var isData:Bool { return hasItemConformingToTypeIdentifier(UType.data) }
var isItem: Bool { hasItemConformingToTypeIdentifier(UType.item) }
}
extension Array {
subscript(safe index: UInt) -> Element? { return Int(index) < count ? self[Int(index)] : nil }
}
class SharingFile: Codable {
var value: String
var mimeType: String?
var thumbnail: String?; // video thumbnail
var duration: Int?; // video duration in milliseconds
var type: SharingFileType;
var message: String? // post message
enum CodingKeys: String, CodingKey {
case value
case mimeType
case thumbnail
case duration
case type
case message
}
init(value: String, mimeType: String? = nil, thumbnail: String?, duration: Int?,
type: SharingFileType, message: String?=nil) {
self.value = value
self.mimeType = mimeType
self.thumbnail = thumbnail
self.duration = duration
self.type = type
self.message = message
}
// Debug method to print out SharedMediaFile details in the console
func toString() {
print("[SharingFile] \n\tvalue: \(self.value)\n\tthumbnail: \(self.thumbnail ?? "--" )\n\tduration: \(self.duration ?? 0)\n\ttype: \(self.type)\n\tmimeType: \(String(describing: self.mimeType))\n\tmessage: \(String(describing: self.message))")
}
}
enum SharingFileType: Int, Codable {
case text
case url
case image
case video
case file
}
// Unified UTType works on iOS 1118
enum UType {
static var image: String {
if #available(iOS 14.0, *) {
return UTType.image.identifier
} else {
return kUTTypeImage as String // old API
}
}
static var movie: String {
if #available(iOS 14.0, *) {
return UTType.movie.identifier
} else {
return kUTTypeMovie as String
}
}
static var url: String {
if #available(iOS 14.0, *) {
return UTType.url.identifier
} else {
return kUTTypeURL as String
}
}
static var fileURL: String {
if #available(iOS 14.0, *) {
return UTType.fileURL.identifier
} else {
return kUTTypeFileURL as String
}
}
static var text: String {
if #available(iOS 14.0, *) {
return UTType.text.identifier
} else {
return kUTTypeText as String
}
}
static var plainText: String {
if #available(iOS 14.0, *) {
return UTType.plainText.identifier
} else {
return kUTTypePlainText as String
}
}
static var data: String {
if #available(iOS 14.0, *) {
return UTType.data.identifier
} else {
return kUTTypeData as String
}
}
static var item: String {
if #available(iOS 14.0, *) {
return UTType.item.identifier
} else {
return kUTTypeItem as String
}
}
}

View file

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
<key>NSExtensionAttributes</key>
<dict>
<key>PHSupportedMediaTypes</key>
<array>
<string>Video</string>
<string>Image</string>
</array>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
</dict>
</dict>
</plist>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.eu.twonly.shareIntent</string>
</array>
</dict>
</plist>

View file

@ -1,3 +0,0 @@
class ShareViewController: FSIShareViewController {
}

View file

@ -1,5 +1,5 @@
arb-dir: lib/src/localization/translations
template-arb-file: en.arb
arb-dir: lib/src/localization
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
untranslated-messages-file: build/l10n.log
output-dir: lib/src/localization/generated

View file

@ -1,12 +1,10 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/localization/generated/app_localizations.dart';
import 'package:twonly/src/providers/connection.provider.dart';
import 'package:twonly/src/providers/purchases.provider.dart';
import 'package:twonly/src/providers/settings.provider.dart';
import 'package:twonly/src/services/subscription.service.dart';
import 'package:twonly/src/utils/log.dart';
@ -41,8 +39,8 @@ class _AppState extends State<App> with WidgetsBindingObserver {
await setUserPlan();
};
globalCallbackUpdatePlan = (SubscriptionPlan plan) {
context.read<PurchasesProvider>().updatePlan(plan);
globalCallbackUpdatePlan = (SubscriptionPlan plan) async {
await context.read<CustomChangeProvider>().updatePlan(plan);
};
unawaited(initAsync());
@ -52,7 +50,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
final user = await getUser();
if (user != null && mounted) {
if (mounted) {
context.read<PurchasesProvider>().updatePlan(
await context.read<CustomChangeProvider>().updatePlan(
planFromString(user.subscriptionPlan),
);
}
@ -61,7 +59,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
Future<void> initAsync() async {
await setUserPlan();
await apiService.connect();
await apiService.connect(force: true);
await apiService.listenToNetworkChanges();
}
@ -72,7 +70,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
if (wasPaused) {
globalIsAppInBackground = false;
twonlyDB.markUpdated();
unawaited(apiService.connect());
unawaited(apiService.connect(force: true));
}
} else if (state == AppLifecycleState.paused) {
wasPaused = true;
@ -94,7 +92,6 @@ class _AppState extends State<App> with WidgetsBindingObserver {
listenable: context.watch<SettingsChangeProvider>(),
builder: (BuildContext context, Widget? child) {
return MaterialApp(
scaffoldMessengerKey: globalRootScaffoldMessengerKey,
restorationScopeId: 'app',
localizationsDelegates: const [
AppLocalizations.delegate,
@ -159,13 +156,11 @@ class _AppMainWidgetState extends State<AppMainWidget> {
bool _showOnboarding = true;
bool _isLoaded = false;
bool _skipBackup = false;
int _initialPage = 0;
(Future<int>?, bool) _proofOfWork = (null, false);
@override
void initState() {
_initialPage = widget.initialPage;
initAsync();
super.initState();
}
@ -177,9 +172,6 @@ class _AppMainWidgetState extends State<AppMainWidget> {
if (gUser.appVersion < 62) {
_showDatabaseMigration = true;
}
if (!gUser.startWithCameraOpen) {
_initialPage = 0;
}
}
if (!_isUserCreated && !_showDatabaseMigration) {
@ -212,7 +204,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
if (_showDatabaseMigration) {
child = const DatabaseMigrationView();
} else if (_isUserCreated) {
if (gUser.twonlySafeBackup == null && !_skipBackup && kReleaseMode) {
if (gUser.twonlySafeBackup == null && !_skipBackup) {
child = TwonlyIdentityBackupView(
callBack: () {
_skipBackup = true;
@ -221,7 +213,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
);
} else {
child = HomeView(
initialPage: _initialPage,
initialPage: widget.initialPage,
);
}
} else if (_showOnboarding) {

View file

@ -1,5 +1,6 @@
import 'dart:ui';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/services/api.service.dart';
@ -36,6 +37,3 @@ bool globalAllowErrorTrackingViaSentry = false;
late String globalApplicationCacheDirectory;
late String globalApplicationSupportDirectory;
final GlobalKey<ScaffoldMessengerState> globalRootScaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState>();

View file

@ -10,7 +10,6 @@ import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/providers/connection.provider.dart';
import 'package:twonly/src/providers/image_editor.provider.dart';
import 'package:twonly/src/providers/purchases.provider.dart';
import 'package:twonly/src/providers/settings.provider.dart';
import 'package:twonly/src/services/api.service.dart';
import 'package:twonly/src/services/api/mediafiles/media_background.service.dart';
@ -26,8 +25,6 @@ import 'package:twonly/src/utils/storage.dart';
void main() async {
SentryWidgetsFlutterBinding.ensureInitialized();
await initFCMService();
final user = await getUser();
if (user != null) {
gUser = user;
@ -46,6 +43,8 @@ void main() async {
unawaited(performTwonlySafeBackup());
}
await initFCMService();
globalApplicationCacheDirectory = (await getApplicationCacheDirectory()).path;
globalApplicationSupportDirectory =
(await getApplicationSupportDirectory()).path;
@ -55,7 +54,9 @@ void main() async {
final settingsController = SettingsChangeProvider();
await settingsController.loadSettings();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp],
);
unawaited(setupPushNotification());
@ -78,7 +79,6 @@ void main() async {
ChangeNotifierProvider(create: (_) => settingsController),
ChangeNotifierProvider(create: (_) => CustomChangeProvider()),
ChangeNotifierProvider(create: (_) => ImageEditorProvider()),
ChangeNotifierProvider(create: (_) => PurchasesProvider()),
],
child: const App(),
),

View file

@ -1,6 +0,0 @@
class SubscriptionKeys {
static const String proMonthly = 'pro_monthly';
static const String proYearly = 'pro_yearly';
// static const String familyMonthly = 'family_monthly';
static const String familyYearly = 'family_yearly';
}

View file

@ -42,15 +42,8 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
.getSingleOrNull();
}
Future<List<Contact>> getContactsByUsername(
String username, {
String username2 = '_______',
}) async {
return (select(contacts)
..where(
(t) => t.username.equals(username) | t.username.equals(username2),
))
.get();
Future<List<Contact>> getContactsByUsername(String username) async {
return (select(contacts)..where((t) => t.username.equals(username))).get();
}
Future<void> deleteContactByUserId(int userId) {
@ -65,8 +58,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
.write(updatedValues);
if (updatedValues.blocked.present ||
updatedValues.displayName.present ||
updatedValues.nickName.present ||
updatedValues.username.present) {
updatedValues.nickName.present) {
final contact = await getContactByUserId(userId).getSingleOrNull();
if (contact != null) {
await updatePushUser(contact);
@ -99,7 +91,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
.watchSingleOrNull();
}
Future<List<Contact>> getAllContacts() {
Future<List<Contact>> getAllNotBlockedContacts() {
return select(contacts).get();
}
@ -126,12 +118,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
Stream<List<Contact>> watchAllAcceptedContacts() {
return (select(contacts)
..where(
(t) =>
t.blocked.equals(false) &
t.accepted.equals(true) &
t.accountDeleted.equals(false),
))
..where((t) => t.blocked.equals(false) & t.accepted.equals(true)))
.watch();
}

View file

@ -3,7 +3,6 @@ import 'package:hashlib/random.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/groups.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/flame.service.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
@ -279,6 +278,89 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
return query.map((row) => row.readTable(groups)).getSingleOrNull();
}
Future<void> incFlameCounter(
String groupId,
bool received,
DateTime timestamp,
) async {
final group = await (select(groups)
..where((t) => t.groupId.equals(groupId)))
.getSingle();
final totalMediaCounter = group.totalMediaCounter + 1;
var flameCounter = group.flameCounter;
var maxFlameCounter = group.maxFlameCounter;
var maxFlameCounterFrom = group.maxFlameCounterFrom;
if (group.lastMessageReceived != null && group.lastMessageSend != null) {
final now = DateTime.now();
final startOfToday = DateTime(now.year, now.month, now.day);
final twoDaysAgo = startOfToday.subtract(const Duration(days: 2));
if (group.lastMessageSend!.isBefore(twoDaysAgo) ||
group.lastMessageReceived!.isBefore(twoDaysAgo)) {
flameCounter = 0;
}
}
var lastMessageSend = const Value<DateTime?>.absent();
var lastMessageReceived = const Value<DateTime?>.absent();
var lastFlameCounterChange = const Value<DateTime?>.absent();
if (group.lastFlameCounterChange != null) {
final now = DateTime.now();
final startOfToday = DateTime(now.year, now.month, now.day);
if (group.lastFlameCounterChange!.isBefore(startOfToday)) {
// last flame update was yesterday. check if it can be updated.
var updateFlame = false;
if (received) {
if (group.lastMessageSend != null &&
group.lastMessageSend!.isAfter(startOfToday)) {
// today a message was already send -> update flame
updateFlame = true;
}
} else if (group.lastMessageReceived != null &&
group.lastMessageReceived!.isAfter(startOfToday)) {
// today a message was already received -> update flame
updateFlame = true;
}
if (updateFlame) {
flameCounter += 1;
lastFlameCounterChange = Value(timestamp);
// Overwrite max flame counter either the current is bigger or the th max flame counter is older then 4 days
if (flameCounter >= maxFlameCounter ||
maxFlameCounterFrom == null ||
maxFlameCounterFrom
.isBefore(DateTime.now().subtract(const Duration(days: 5)))) {
maxFlameCounter = flameCounter;
maxFlameCounterFrom = DateTime.now();
}
}
}
} else {
// There where no message until no...
lastFlameCounterChange = Value(timestamp);
}
if (received) {
lastMessageReceived = Value(timestamp);
} else {
lastMessageSend = Value(timestamp);
}
await (update(groups)..where((t) => t.groupId.equals(groupId))).write(
GroupsCompanion(
totalMediaCounter: Value(totalMediaCounter),
lastFlameCounterChange: lastFlameCounterChange,
lastMessageReceived: lastMessageReceived,
lastMessageSend: lastMessageSend,
flameCounter: Value(flameCounter),
maxFlameCounter: Value(maxFlameCounter),
maxFlameCounterFrom: Value(maxFlameCounterFrom),
),
);
}
Stream<int> watchSumTotalMediaCounter() {
final query = selectOnly(groups)
..addColumns([groups.totalMediaCounter.sum()]);
@ -301,3 +383,19 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
.write(GroupsCompanion(lastMessageExchange: Value(newLastMessage)));
}
}
int getFlameCounterFromGroup(Group? group) {
if (group == null) return 0;
if (group.lastMessageSend == null || group.lastMessageReceived == null) {
return 0;
}
final now = DateTime.now();
final startOfToday = DateTime(now.year, now.month, now.day);
final twoDaysAgo = startOfToday.subtract(const Duration(days: 2));
if (group.lastMessageSend!.isAfter(twoDaysAgo) &&
group.lastMessageReceived!.isAfter(twoDaysAgo)) {
return group.flameCounter + 1;
} else {
return 0;
}
}

View file

@ -100,14 +100,6 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
.get();
}
Future<List<MediaFile>> getAllNonHashedStoredMediaFiles() async {
return (select(mediaFiles)
..where(
(t) => t.stored.equals(true) & t.storedFileHash.isNull(),
))
.get();
}
Future<List<MediaFile>> getAllMediaFilesPendingUpload() async {
return (select(mediaFiles)
..where(
@ -119,10 +111,7 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
}
Stream<List<MediaFile>> watchAllStoredMediaFiles() {
final query = (select(mediaFiles)..where((t) => t.stored.equals(true)))
.join([])
..groupBy([mediaFiles.storedFileHash]);
return query.map((row) => row.readTable(mediaFiles)).watch();
return (select(mediaFiles)..where((t) => t.stored.equals(true))).watch();
}
Stream<List<MediaFile>> watchNewestMediaFiles() {

View file

@ -1,4 +1,3 @@
import 'package:clock/clock.dart';
import 'package:drift/drift.dart';
import 'package:hashlib/random.dart';
import 'package:twonly/globals.dart';
@ -111,11 +110,11 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
final allGroups = await select(groups).get();
for (final group in allGroups) {
final deletionTime = clock.now().subtract(
Duration(
milliseconds: group.deleteMessagesAfterMilliseconds,
),
);
final deletionTime = DateTime.now().subtract(
Duration(
milliseconds: group.deleteMessagesAfterMilliseconds,
),
);
await (delete(messages)
..where(
(m) =>
@ -151,7 +150,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
// t.messageOtherId.isNull() &
// t.errorWhileSending.equals(false) &
// t.sendAt.isBiggerThanValue(
// clock.now().subtract(const Duration(minutes: 10)),
// DateTime.now().subtract(const Duration(minutes: 10)),
// ),
// ))
// .get();
@ -179,7 +178,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
// }
Future<void> openedAllTextMessages(String groupId) {
final updates = MessagesCompanion(openedAt: Value(clock.now()));
final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
return (update(messages)
..where(
(t) =>
@ -273,12 +272,12 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
// Directly show as message opened as soon as one person has opened it
final openedByAll =
await haveAllMembers(messageId, MessageActionType.openedAt)
? clock.now()
? DateTime.now()
: null;
await twonlyDB.messagesDao.updateMessageId(
messageId,
MessagesCompanion(
openedAt: Value(clock.now()),
openedAt: Value(DateTime.now()),
openedByAll: Value(openedByAll),
),
);
@ -299,7 +298,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
);
await twonlyDB.messagesDao.updateMessageId(
messageId,
MessagesCompanion(ackByServer: Value(clock.now())),
MessagesCompanion(ackByServer: Value(DateTime.now())),
);
}
@ -379,7 +378,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
await twonlyDB.groupsDao.updateGroup(
message.groupId.value,
GroupsCompanion(
lastMessageExchange: Value(clock.now()),
lastMessageExchange: Value(DateTime.now()),
archived: const Value(false),
deletedContent: const Value(false),
),
@ -390,7 +389,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
message.groupId.value,
message.senderId.value!,
GroupMembersCompanion(
lastMessage: Value(clock.now()),
lastMessage: Value(DateTime.now()),
),
);
}

View file

@ -1,4 +1,3 @@
import 'package:clock/clock.dart';
import 'package:drift/drift.dart';
import 'package:hashlib/random.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
@ -81,18 +80,10 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
}
}
Future<List<Receipt>> getReceiptsForRetransmission() async {
final markedRetriesTime = clock.now().subtract(
const Duration(
// give the server time to transmit all messages to the client
seconds: 20,
),
);
Future<List<Receipt>> getReceiptsNotAckByServer() async {
return (select(receipts)
..where(
(t) =>
t.ackByServerAt.isNull() |
t.markForRetry.isSmallerThanValue(markedRetriesTime),
(t) => t.ackByServerAt.isNull(),
))
.get();
}
@ -109,14 +100,6 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
.write(updates);
}
Future<void> markMessagesForRetry(int contactId) async {
await (update(receipts)..where((c) => c.contactId.equals(contactId))).write(
ReceiptsCompanion(
markForRetry: Value(clock.now()),
),
);
}
Future<bool> isDuplicated(String receiptId) async {
return await (select(receivedReceipts)
..where((t) => t.receiptId.equals(receiptId)))

View file

@ -1,4 +1,3 @@
import 'package:clock/clock.dart';
import 'package:drift/drift.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/signal_contact_prekey.table.dart';
@ -108,9 +107,9 @@ class SignalDao extends DatabaseAccessor<TwonlyDB> with _$SignalDaoMixin {
await (delete(signalContactPreKeys)
..where(
(t) => (t.createdAt.isSmallerThanValue(
clock.now().subtract(
const Duration(days: 100),
),
DateTime.now().subtract(
const Duration(days: 100),
),
)),
))
.go();
@ -118,9 +117,9 @@ class SignalDao extends DatabaseAccessor<TwonlyDB> with _$SignalDaoMixin {
await (delete(twonlyDB.signalPreKeyStores)
..where(
(t) => (t.createdAt.isSmallerThanValue(
clock.now().subtract(
const Duration(days: 365),
),
DateTime.now().subtract(
const Duration(days: 365),
),
)),
))
.go();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -90,7 +90,8 @@ class GroupHistories extends Table {
IntColumn get contactId =>
integer().nullable().references(Contacts, #userId)();
IntColumn get affectedContactId => integer().nullable()();
IntColumn get affectedContactId =>
integer().nullable().references(Contacts, #userId)();
TextColumn get oldGroupName => text().nullable()();
TextColumn get newGroupName => text().nullable()();

View file

@ -59,8 +59,6 @@ class MediaFiles extends Table {
BlobColumn get encryptionMac => blob().nullable()();
BlobColumn get encryptionNonce => blob().nullable()();
BlobColumn get storedFileHash => blob().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override

View file

@ -20,8 +20,6 @@ class Receipts extends Table {
BoolColumn get contactWillSendsReceipt =>
boolean().withDefault(const Constant(true))();
DateTimeColumn get markForRetry => dateTime().nullable()();
DateTimeColumn get ackByServerAt => dateTime().nullable()();
IntColumn get retryCount => integer().withDefault(const Constant(0))();

View file

@ -1,4 +1,3 @@
import 'package:clock/clock.dart';
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart'
show DriftNativeOptions, driftDatabase;
@ -68,7 +67,7 @@ class TwonlyDB extends _$TwonlyDB {
TwonlyDB.forTesting(DatabaseConnection super.connection);
@override
int get schemaVersion => 5;
int get schemaVersion => 3;
static QueryExecutor _openConnection() {
return driftDatabase(
@ -93,24 +92,6 @@ class TwonlyDB extends _$TwonlyDB {
from2To3: (m, schema) async {
await m.addColumn(schema.groups, schema.groups.draftMessage);
},
from3To4: (m, schema) async {
await m.alterTable(
TableMigration(
schema.groupHistories,
columnTransformer: {
schema.groupHistories.affectedContactId:
schema.groupHistories.affectedContactId,
},
),
);
},
from4To5: (m, schema) async {
await m.addColumn(schema.receipts, schema.receipts.markForRetry);
await m.addColumn(
schema.mediaFiles,
schema.mediaFiles.storedFileHash,
);
},
),
);
}
@ -161,9 +142,9 @@ class TwonlyDB extends _$TwonlyDB {
await (delete(signalPreKeyStores)
..where(
(t) => (t.createdAt.isSmallerThanValue(
clock.now().subtract(
const Duration(days: 25),
),
DateTime.now().subtract(
const Duration(days: 25),
),
)),
))
.go();

View file

@ -1996,12 +1996,6 @@ class $MediaFilesTable extends MediaFiles
late final GeneratedColumn<Uint8List> encryptionNonce =
GeneratedColumn<Uint8List>('encryption_nonce', aliasedName, true,
type: DriftSqlType.blob, requiredDuringInsert: false);
static const VerificationMeta _storedFileHashMeta =
const VerificationMeta('storedFileHash');
@override
late final GeneratedColumn<Uint8List> storedFileHash =
GeneratedColumn<Uint8List>('stored_file_hash', aliasedName, true,
type: DriftSqlType.blob, requiredDuringInsert: false);
static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt');
@override
@ -2026,7 +2020,6 @@ class $MediaFilesTable extends MediaFiles
encryptionKey,
encryptionMac,
encryptionNonce,
storedFileHash,
createdAt
];
@override
@ -2098,12 +2091,6 @@ class $MediaFilesTable extends MediaFiles
encryptionNonce.isAcceptableOrUnknown(
data['encryption_nonce']!, _encryptionNonceMeta));
}
if (data.containsKey('stored_file_hash')) {
context.handle(
_storedFileHashMeta,
storedFileHash.isAcceptableOrUnknown(
data['stored_file_hash']!, _storedFileHashMeta));
}
if (data.containsKey('created_at')) {
context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
@ -2150,8 +2137,6 @@ class $MediaFilesTable extends MediaFiles
.read(DriftSqlType.blob, data['${effectivePrefix}encryption_mac']),
encryptionNonce: attachedDatabase.typeMapping
.read(DriftSqlType.blob, data['${effectivePrefix}encryption_nonce']),
storedFileHash: attachedDatabase.typeMapping
.read(DriftSqlType.blob, data['${effectivePrefix}stored_file_hash']),
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
);
@ -2196,7 +2181,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
final Uint8List? encryptionKey;
final Uint8List? encryptionMac;
final Uint8List? encryptionNonce;
final Uint8List? storedFileHash;
final DateTime createdAt;
const MediaFile(
{required this.mediaId,
@ -2213,7 +2197,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
this.encryptionKey,
this.encryptionMac,
this.encryptionNonce,
this.storedFileHash,
required this.createdAt});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
@ -2258,9 +2241,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
if (!nullToAbsent || encryptionNonce != null) {
map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce);
}
if (!nullToAbsent || storedFileHash != null) {
map['stored_file_hash'] = Variable<Uint8List>(storedFileHash);
}
map['created_at'] = Variable<DateTime>(createdAt);
return map;
}
@ -2300,9 +2280,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
encryptionNonce: encryptionNonce == null && nullToAbsent
? const Value.absent()
: Value(encryptionNonce),
storedFileHash: storedFileHash == null && nullToAbsent
? const Value.absent()
: Value(storedFileHash),
createdAt: Value(createdAt),
);
}
@ -2331,7 +2308,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
encryptionKey: serializer.fromJson<Uint8List?>(json['encryptionKey']),
encryptionMac: serializer.fromJson<Uint8List?>(json['encryptionMac']),
encryptionNonce: serializer.fromJson<Uint8List?>(json['encryptionNonce']),
storedFileHash: serializer.fromJson<Uint8List?>(json['storedFileHash']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
);
}
@ -2357,7 +2333,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
'encryptionKey': serializer.toJson<Uint8List?>(encryptionKey),
'encryptionMac': serializer.toJson<Uint8List?>(encryptionMac),
'encryptionNonce': serializer.toJson<Uint8List?>(encryptionNonce),
'storedFileHash': serializer.toJson<Uint8List?>(storedFileHash),
'createdAt': serializer.toJson<DateTime>(createdAt),
};
}
@ -2377,7 +2352,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
Value<Uint8List?> encryptionKey = const Value.absent(),
Value<Uint8List?> encryptionMac = const Value.absent(),
Value<Uint8List?> encryptionNonce = const Value.absent(),
Value<Uint8List?> storedFileHash = const Value.absent(),
DateTime? createdAt}) =>
MediaFile(
mediaId: mediaId ?? this.mediaId,
@ -2405,8 +2379,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
encryptionNonce: encryptionNonce.present
? encryptionNonce.value
: this.encryptionNonce,
storedFileHash:
storedFileHash.present ? storedFileHash.value : this.storedFileHash,
createdAt: createdAt ?? this.createdAt,
);
MediaFile copyWithCompanion(MediaFilesCompanion data) {
@ -2445,9 +2417,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
encryptionNonce: data.encryptionNonce.present
? data.encryptionNonce.value
: this.encryptionNonce,
storedFileHash: data.storedFileHash.present
? data.storedFileHash.value
: this.storedFileHash,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
);
}
@ -2469,7 +2438,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
..write('encryptionKey: $encryptionKey, ')
..write('encryptionMac: $encryptionMac, ')
..write('encryptionNonce: $encryptionNonce, ')
..write('storedFileHash: $storedFileHash, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
@ -2491,7 +2459,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
$driftBlobEquality.hash(encryptionKey),
$driftBlobEquality.hash(encryptionMac),
$driftBlobEquality.hash(encryptionNonce),
$driftBlobEquality.hash(storedFileHash),
createdAt);
@override
bool operator ==(Object other) =>
@ -2512,8 +2479,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
$driftBlobEquality.equals(other.encryptionMac, this.encryptionMac) &&
$driftBlobEquality.equals(
other.encryptionNonce, this.encryptionNonce) &&
$driftBlobEquality.equals(
other.storedFileHash, this.storedFileHash) &&
other.createdAt == this.createdAt);
}
@ -2532,7 +2497,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
final Value<Uint8List?> encryptionKey;
final Value<Uint8List?> encryptionMac;
final Value<Uint8List?> encryptionNonce;
final Value<Uint8List?> storedFileHash;
final Value<DateTime> createdAt;
final Value<int> rowid;
const MediaFilesCompanion({
@ -2550,7 +2514,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
this.encryptionKey = const Value.absent(),
this.encryptionMac = const Value.absent(),
this.encryptionNonce = const Value.absent(),
this.storedFileHash = const Value.absent(),
this.createdAt = const Value.absent(),
this.rowid = const Value.absent(),
});
@ -2569,7 +2532,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
this.encryptionKey = const Value.absent(),
this.encryptionMac = const Value.absent(),
this.encryptionNonce = const Value.absent(),
this.storedFileHash = const Value.absent(),
this.createdAt = const Value.absent(),
this.rowid = const Value.absent(),
}) : mediaId = Value(mediaId),
@ -2589,7 +2551,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
Expression<Uint8List>? encryptionKey,
Expression<Uint8List>? encryptionMac,
Expression<Uint8List>? encryptionNonce,
Expression<Uint8List>? storedFileHash,
Expression<DateTime>? createdAt,
Expression<int>? rowid,
}) {
@ -2611,7 +2572,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
if (encryptionKey != null) 'encryption_key': encryptionKey,
if (encryptionMac != null) 'encryption_mac': encryptionMac,
if (encryptionNonce != null) 'encryption_nonce': encryptionNonce,
if (storedFileHash != null) 'stored_file_hash': storedFileHash,
if (createdAt != null) 'created_at': createdAt,
if (rowid != null) 'rowid': rowid,
});
@ -2632,7 +2592,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
Value<Uint8List?>? encryptionKey,
Value<Uint8List?>? encryptionMac,
Value<Uint8List?>? encryptionNonce,
Value<Uint8List?>? storedFileHash,
Value<DateTime>? createdAt,
Value<int>? rowid}) {
return MediaFilesCompanion(
@ -2652,7 +2611,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
encryptionKey: encryptionKey ?? this.encryptionKey,
encryptionMac: encryptionMac ?? this.encryptionMac,
encryptionNonce: encryptionNonce ?? this.encryptionNonce,
storedFileHash: storedFileHash ?? this.storedFileHash,
createdAt: createdAt ?? this.createdAt,
rowid: rowid ?? this.rowid,
);
@ -2710,9 +2668,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
if (encryptionNonce.present) {
map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce.value);
}
if (storedFileHash.present) {
map['stored_file_hash'] = Variable<Uint8List>(storedFileHash.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
@ -2739,7 +2694,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
..write('encryptionKey: $encryptionKey, ')
..write('encryptionMac: $encryptionMac, ')
..write('encryptionNonce: $encryptionNonce, ')
..write('storedFileHash: $storedFileHash, ')
..write('createdAt: $createdAt, ')
..write('rowid: $rowid')
..write(')'))
@ -4592,12 +4546,6 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
defaultConstraints: GeneratedColumn.constraintIsAlways(
'CHECK ("contact_will_sends_receipt" IN (0, 1))'),
defaultValue: const Constant(true));
static const VerificationMeta _markForRetryMeta =
const VerificationMeta('markForRetry');
@override
late final GeneratedColumn<DateTime> markForRetry = GeneratedColumn<DateTime>(
'mark_for_retry', aliasedName, true,
type: DriftSqlType.dateTime, requiredDuringInsert: false);
static const VerificationMeta _ackByServerAtMeta =
const VerificationMeta('ackByServerAt');
@override
@ -4633,7 +4581,6 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
messageId,
message,
contactWillSendsReceipt,
markForRetry,
ackByServerAt,
retryCount,
lastRetry,
@ -4678,12 +4625,6 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
data['contact_will_sends_receipt']!,
_contactWillSendsReceiptMeta));
}
if (data.containsKey('mark_for_retry')) {
context.handle(
_markForRetryMeta,
markForRetry.isAcceptableOrUnknown(
data['mark_for_retry']!, _markForRetryMeta));
}
if (data.containsKey('ack_by_server_at')) {
context.handle(
_ackByServerAtMeta,
@ -4724,8 +4665,6 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
contactWillSendsReceipt: attachedDatabase.typeMapping.read(
DriftSqlType.bool,
data['${effectivePrefix}contact_will_sends_receipt'])!,
markForRetry: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime, data['${effectivePrefix}mark_for_retry']),
ackByServerAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime, data['${effectivePrefix}ack_by_server_at']),
retryCount: attachedDatabase.typeMapping
@ -4751,7 +4690,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
/// This is the protobuf 'Message'
final Uint8List message;
final bool contactWillSendsReceipt;
final DateTime? markForRetry;
final DateTime? ackByServerAt;
final int retryCount;
final DateTime? lastRetry;
@ -4762,7 +4700,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
this.messageId,
required this.message,
required this.contactWillSendsReceipt,
this.markForRetry,
this.ackByServerAt,
required this.retryCount,
this.lastRetry,
@ -4777,9 +4714,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
}
map['message'] = Variable<Uint8List>(message);
map['contact_will_sends_receipt'] = Variable<bool>(contactWillSendsReceipt);
if (!nullToAbsent || markForRetry != null) {
map['mark_for_retry'] = Variable<DateTime>(markForRetry);
}
if (!nullToAbsent || ackByServerAt != null) {
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt);
}
@ -4800,9 +4734,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
: Value(messageId),
message: Value(message),
contactWillSendsReceipt: Value(contactWillSendsReceipt),
markForRetry: markForRetry == null && nullToAbsent
? const Value.absent()
: Value(markForRetry),
ackByServerAt: ackByServerAt == null && nullToAbsent
? const Value.absent()
: Value(ackByServerAt),
@ -4824,7 +4755,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
message: serializer.fromJson<Uint8List>(json['message']),
contactWillSendsReceipt:
serializer.fromJson<bool>(json['contactWillSendsReceipt']),
markForRetry: serializer.fromJson<DateTime?>(json['markForRetry']),
ackByServerAt: serializer.fromJson<DateTime?>(json['ackByServerAt']),
retryCount: serializer.fromJson<int>(json['retryCount']),
lastRetry: serializer.fromJson<DateTime?>(json['lastRetry']),
@ -4841,7 +4771,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
'message': serializer.toJson<Uint8List>(message),
'contactWillSendsReceipt':
serializer.toJson<bool>(contactWillSendsReceipt),
'markForRetry': serializer.toJson<DateTime?>(markForRetry),
'ackByServerAt': serializer.toJson<DateTime?>(ackByServerAt),
'retryCount': serializer.toJson<int>(retryCount),
'lastRetry': serializer.toJson<DateTime?>(lastRetry),
@ -4855,7 +4784,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
Value<String?> messageId = const Value.absent(),
Uint8List? message,
bool? contactWillSendsReceipt,
Value<DateTime?> markForRetry = const Value.absent(),
Value<DateTime?> ackByServerAt = const Value.absent(),
int? retryCount,
Value<DateTime?> lastRetry = const Value.absent(),
@ -4867,8 +4795,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
message: message ?? this.message,
contactWillSendsReceipt:
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
markForRetry:
markForRetry.present ? markForRetry.value : this.markForRetry,
ackByServerAt:
ackByServerAt.present ? ackByServerAt.value : this.ackByServerAt,
retryCount: retryCount ?? this.retryCount,
@ -4884,9 +4810,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
contactWillSendsReceipt: data.contactWillSendsReceipt.present
? data.contactWillSendsReceipt.value
: this.contactWillSendsReceipt,
markForRetry: data.markForRetry.present
? data.markForRetry.value
: this.markForRetry,
ackByServerAt: data.ackByServerAt.present
? data.ackByServerAt.value
: this.ackByServerAt,
@ -4905,7 +4828,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
..write('messageId: $messageId, ')
..write('message: $message, ')
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
..write('markForRetry: $markForRetry, ')
..write('ackByServerAt: $ackByServerAt, ')
..write('retryCount: $retryCount, ')
..write('lastRetry: $lastRetry, ')
@ -4921,7 +4843,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
messageId,
$driftBlobEquality.hash(message),
contactWillSendsReceipt,
markForRetry,
ackByServerAt,
retryCount,
lastRetry,
@ -4935,7 +4856,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
other.messageId == this.messageId &&
$driftBlobEquality.equals(other.message, this.message) &&
other.contactWillSendsReceipt == this.contactWillSendsReceipt &&
other.markForRetry == this.markForRetry &&
other.ackByServerAt == this.ackByServerAt &&
other.retryCount == this.retryCount &&
other.lastRetry == this.lastRetry &&
@ -4948,7 +4868,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
final Value<String?> messageId;
final Value<Uint8List> message;
final Value<bool> contactWillSendsReceipt;
final Value<DateTime?> markForRetry;
final Value<DateTime?> ackByServerAt;
final Value<int> retryCount;
final Value<DateTime?> lastRetry;
@ -4960,7 +4879,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
this.messageId = const Value.absent(),
this.message = const Value.absent(),
this.contactWillSendsReceipt = const Value.absent(),
this.markForRetry = const Value.absent(),
this.ackByServerAt = const Value.absent(),
this.retryCount = const Value.absent(),
this.lastRetry = const Value.absent(),
@ -4973,7 +4891,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
this.messageId = const Value.absent(),
required Uint8List message,
this.contactWillSendsReceipt = const Value.absent(),
this.markForRetry = const Value.absent(),
this.ackByServerAt = const Value.absent(),
this.retryCount = const Value.absent(),
this.lastRetry = const Value.absent(),
@ -4988,7 +4905,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
Expression<String>? messageId,
Expression<Uint8List>? message,
Expression<bool>? contactWillSendsReceipt,
Expression<DateTime>? markForRetry,
Expression<DateTime>? ackByServerAt,
Expression<int>? retryCount,
Expression<DateTime>? lastRetry,
@ -5002,7 +4918,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
if (message != null) 'message': message,
if (contactWillSendsReceipt != null)
'contact_will_sends_receipt': contactWillSendsReceipt,
if (markForRetry != null) 'mark_for_retry': markForRetry,
if (ackByServerAt != null) 'ack_by_server_at': ackByServerAt,
if (retryCount != null) 'retry_count': retryCount,
if (lastRetry != null) 'last_retry': lastRetry,
@ -5017,7 +4932,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
Value<String?>? messageId,
Value<Uint8List>? message,
Value<bool>? contactWillSendsReceipt,
Value<DateTime?>? markForRetry,
Value<DateTime?>? ackByServerAt,
Value<int>? retryCount,
Value<DateTime?>? lastRetry,
@ -5030,7 +4944,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
message: message ?? this.message,
contactWillSendsReceipt:
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
markForRetry: markForRetry ?? this.markForRetry,
ackByServerAt: ackByServerAt ?? this.ackByServerAt,
retryCount: retryCount ?? this.retryCount,
lastRetry: lastRetry ?? this.lastRetry,
@ -5058,9 +4971,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
map['contact_will_sends_receipt'] =
Variable<bool>(contactWillSendsReceipt.value);
}
if (markForRetry.present) {
map['mark_for_retry'] = Variable<DateTime>(markForRetry.value);
}
if (ackByServerAt.present) {
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt.value);
}
@ -5087,7 +4997,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
..write('messageId: $messageId, ')
..write('message: $message, ')
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
..write('markForRetry: $markForRetry, ')
..write('ackByServerAt: $ackByServerAt, ')
..write('retryCount: $retryCount, ')
..write('lastRetry: $lastRetry, ')
@ -7202,7 +7111,10 @@ class $GroupHistoriesTable extends GroupHistories
@override
late final GeneratedColumn<int> affectedContactId = GeneratedColumn<int>(
'affected_contact_id', aliasedName, true,
type: DriftSqlType.int, requiredDuringInsert: false);
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints:
GeneratedColumn.constraintIsAlways('REFERENCES contacts (user_id)'));
static const VerificationMeta _oldGroupNameMeta =
const VerificationMeta('oldGroupName');
@override
@ -7984,22 +7896,6 @@ final class $$ContactsTableReferences
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: cache));
}
static MultiTypedResultKey<$GroupHistoriesTable, List<GroupHistory>>
_groupHistoriesRefsTable(_$TwonlyDB db) =>
MultiTypedResultKey.fromTable(db.groupHistories,
aliasName: $_aliasNameGenerator(
db.contacts.userId, db.groupHistories.contactId));
$$GroupHistoriesTableProcessedTableManager get groupHistoriesRefs {
final manager = $$GroupHistoriesTableTableManager($_db, $_db.groupHistories)
.filter(
(f) => f.contactId.userId.sqlEquals($_itemColumn<int>('user_id')!));
final cache = $_typedResult.readTableOrNull(_groupHistoriesRefsTable($_db));
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: cache));
}
}
class $$ContactsTableFilterComposer
@ -8182,27 +8078,6 @@ class $$ContactsTableFilterComposer
));
return f(composer);
}
Expression<bool> groupHistoriesRefs(
Expression<bool> Function($$GroupHistoriesTableFilterComposer f) f) {
final $$GroupHistoriesTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: $db.groupHistories,
getReferencedColumn: (t) => t.contactId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$GroupHistoriesTableFilterComposer(
$db: $db,
$table: $db.groupHistories,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return f(composer);
}
}
class $$ContactsTableOrderingComposer
@ -8436,27 +8311,6 @@ class $$ContactsTableAnnotationComposer
));
return f(composer);
}
Expression<T> groupHistoriesRefs<T extends Object>(
Expression<T> Function($$GroupHistoriesTableAnnotationComposer a) f) {
final $$GroupHistoriesTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: $db.groupHistories,
getReferencedColumn: (t) => t.contactId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$GroupHistoriesTableAnnotationComposer(
$db: $db,
$table: $db.groupHistories,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return f(composer);
}
}
class $$ContactsTableTableManager extends RootTableManager<
@ -8476,8 +8330,7 @@ class $$ContactsTableTableManager extends RootTableManager<
bool groupMembersRefs,
bool receiptsRefs,
bool signalContactPreKeysRefs,
bool signalContactSignedPreKeysRefs,
bool groupHistoriesRefs})> {
bool signalContactSignedPreKeysRefs})> {
$$ContactsTableTableManager(_$TwonlyDB db, $ContactsTable table)
: super(TableManagerState(
db: db,
@ -8558,8 +8411,7 @@ class $$ContactsTableTableManager extends RootTableManager<
groupMembersRefs = false,
receiptsRefs = false,
signalContactPreKeysRefs = false,
signalContactSignedPreKeysRefs = false,
groupHistoriesRefs = false}) {
signalContactSignedPreKeysRefs = false}) {
return PrefetchHooks(
db: db,
explicitlyWatchedTables: [
@ -8569,8 +8421,7 @@ class $$ContactsTableTableManager extends RootTableManager<
if (receiptsRefs) db.receipts,
if (signalContactPreKeysRefs) db.signalContactPreKeys,
if (signalContactSignedPreKeysRefs)
db.signalContactSignedPreKeys,
if (groupHistoriesRefs) db.groupHistories
db.signalContactSignedPreKeys
],
addJoins: null,
getPrefetchedDataCallback: (items) async {
@ -8650,19 +8501,6 @@ class $$ContactsTableTableManager extends RootTableManager<
referencedItemsForCurrentItem:
(item, referencedItems) => referencedItems
.where((e) => e.contactId == item.userId),
typedResults: items),
if (groupHistoriesRefs)
await $_getPrefetchedData<Contact, $ContactsTable,
GroupHistory>(
currentTable: table,
referencedTable: $$ContactsTableReferences
._groupHistoriesRefsTable(db),
managerFromTypedResult: (p0) =>
$$ContactsTableReferences(db, table, p0)
.groupHistoriesRefs,
referencedItemsForCurrentItem:
(item, referencedItems) => referencedItems
.where((e) => e.contactId == item.userId),
typedResults: items)
];
},
@ -8688,8 +8526,7 @@ typedef $$ContactsTableProcessedTableManager = ProcessedTableManager<
bool groupMembersRefs,
bool receiptsRefs,
bool signalContactPreKeysRefs,
bool signalContactSignedPreKeysRefs,
bool groupHistoriesRefs})>;
bool signalContactSignedPreKeysRefs})>;
typedef $$GroupsTableCreateCompanionBuilder = GroupsCompanion Function({
required String groupId,
Value<bool> isGroupAdmin,
@ -9436,7 +9273,6 @@ typedef $$MediaFilesTableCreateCompanionBuilder = MediaFilesCompanion Function({
Value<Uint8List?> encryptionKey,
Value<Uint8List?> encryptionMac,
Value<Uint8List?> encryptionNonce,
Value<Uint8List?> storedFileHash,
Value<DateTime> createdAt,
Value<int> rowid,
});
@ -9455,7 +9291,6 @@ typedef $$MediaFilesTableUpdateCompanionBuilder = MediaFilesCompanion Function({
Value<Uint8List?> encryptionKey,
Value<Uint8List?> encryptionMac,
Value<Uint8List?> encryptionNonce,
Value<Uint8List?> storedFileHash,
Value<DateTime> createdAt,
Value<int> rowid,
});
@ -9542,10 +9377,6 @@ class $$MediaFilesTableFilterComposer
column: $table.encryptionNonce,
builder: (column) => ColumnFilters(column));
ColumnFilters<Uint8List> get storedFileHash => $composableBuilder(
column: $table.storedFileHash,
builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnFilters(column));
@ -9631,10 +9462,6 @@ class $$MediaFilesTableOrderingComposer
column: $table.encryptionNonce,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<Uint8List> get storedFileHash => $composableBuilder(
column: $table.storedFileHash,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnOrderings(column));
}
@ -9693,9 +9520,6 @@ class $$MediaFilesTableAnnotationComposer
GeneratedColumn<Uint8List> get encryptionNonce => $composableBuilder(
column: $table.encryptionNonce, builder: (column) => column);
GeneratedColumn<Uint8List> get storedFileHash => $composableBuilder(
column: $table.storedFileHash, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
@ -9758,7 +9582,6 @@ class $$MediaFilesTableTableManager extends RootTableManager<
Value<Uint8List?> encryptionKey = const Value.absent(),
Value<Uint8List?> encryptionMac = const Value.absent(),
Value<Uint8List?> encryptionNonce = const Value.absent(),
Value<Uint8List?> storedFileHash = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) =>
@ -9777,7 +9600,6 @@ class $$MediaFilesTableTableManager extends RootTableManager<
encryptionKey: encryptionKey,
encryptionMac: encryptionMac,
encryptionNonce: encryptionNonce,
storedFileHash: storedFileHash,
createdAt: createdAt,
rowid: rowid,
),
@ -9796,7 +9618,6 @@ class $$MediaFilesTableTableManager extends RootTableManager<
Value<Uint8List?> encryptionKey = const Value.absent(),
Value<Uint8List?> encryptionMac = const Value.absent(),
Value<Uint8List?> encryptionNonce = const Value.absent(),
Value<Uint8List?> storedFileHash = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) =>
@ -9815,7 +9636,6 @@ class $$MediaFilesTableTableManager extends RootTableManager<
encryptionKey: encryptionKey,
encryptionMac: encryptionMac,
encryptionNonce: encryptionNonce,
storedFileHash: storedFileHash,
createdAt: createdAt,
rowid: rowid,
),
@ -11770,7 +11590,6 @@ typedef $$ReceiptsTableCreateCompanionBuilder = ReceiptsCompanion Function({
Value<String?> messageId,
required Uint8List message,
Value<bool> contactWillSendsReceipt,
Value<DateTime?> markForRetry,
Value<DateTime?> ackByServerAt,
Value<int> retryCount,
Value<DateTime?> lastRetry,
@ -11783,7 +11602,6 @@ typedef $$ReceiptsTableUpdateCompanionBuilder = ReceiptsCompanion Function({
Value<String?> messageId,
Value<Uint8List> message,
Value<bool> contactWillSendsReceipt,
Value<DateTime?> markForRetry,
Value<DateTime?> ackByServerAt,
Value<int> retryCount,
Value<DateTime?> lastRetry,
@ -11845,9 +11663,6 @@ class $$ReceiptsTableFilterComposer
column: $table.contactWillSendsReceipt,
builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get markForRetry => $composableBuilder(
column: $table.markForRetry, builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get ackByServerAt => $composableBuilder(
column: $table.ackByServerAt, builder: (column) => ColumnFilters(column));
@ -11920,10 +11735,6 @@ class $$ReceiptsTableOrderingComposer
column: $table.contactWillSendsReceipt,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get markForRetry => $composableBuilder(
column: $table.markForRetry,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get ackByServerAt => $composableBuilder(
column: $table.ackByServerAt,
builder: (column) => ColumnOrderings(column));
@ -11996,9 +11807,6 @@ class $$ReceiptsTableAnnotationComposer
GeneratedColumn<bool> get contactWillSendsReceipt => $composableBuilder(
column: $table.contactWillSendsReceipt, builder: (column) => column);
GeneratedColumn<DateTime> get markForRetry => $composableBuilder(
column: $table.markForRetry, builder: (column) => column);
GeneratedColumn<DateTime> get ackByServerAt => $composableBuilder(
column: $table.ackByServerAt, builder: (column) => column);
@ -12080,7 +11888,6 @@ class $$ReceiptsTableTableManager extends RootTableManager<
Value<String?> messageId = const Value.absent(),
Value<Uint8List> message = const Value.absent(),
Value<bool> contactWillSendsReceipt = const Value.absent(),
Value<DateTime?> markForRetry = const Value.absent(),
Value<DateTime?> ackByServerAt = const Value.absent(),
Value<int> retryCount = const Value.absent(),
Value<DateTime?> lastRetry = const Value.absent(),
@ -12093,7 +11900,6 @@ class $$ReceiptsTableTableManager extends RootTableManager<
messageId: messageId,
message: message,
contactWillSendsReceipt: contactWillSendsReceipt,
markForRetry: markForRetry,
ackByServerAt: ackByServerAt,
retryCount: retryCount,
lastRetry: lastRetry,
@ -12106,7 +11912,6 @@ class $$ReceiptsTableTableManager extends RootTableManager<
Value<String?> messageId = const Value.absent(),
required Uint8List message,
Value<bool> contactWillSendsReceipt = const Value.absent(),
Value<DateTime?> markForRetry = const Value.absent(),
Value<DateTime?> ackByServerAt = const Value.absent(),
Value<int> retryCount = const Value.absent(),
Value<DateTime?> lastRetry = const Value.absent(),
@ -12119,7 +11924,6 @@ class $$ReceiptsTableTableManager extends RootTableManager<
messageId: messageId,
message: message,
contactWillSendsReceipt: contactWillSendsReceipt,
markForRetry: markForRetry,
ackByServerAt: ackByServerAt,
retryCount: retryCount,
lastRetry: lastRetry,
@ -13796,6 +13600,21 @@ final class $$GroupHistoriesTableReferences
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
static $ContactsTable _affectedContactIdTable(_$TwonlyDB db) =>
db.contacts.createAlias($_aliasNameGenerator(
db.groupHistories.affectedContactId, db.contacts.userId));
$$ContactsTableProcessedTableManager? get affectedContactId {
final $_column = $_itemColumn<int>('affected_contact_id');
if ($_column == null) return null;
final manager = $$ContactsTableTableManager($_db, $_db.contacts)
.filter((f) => f.userId.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_affectedContactIdTable($_db));
if (item == null) return manager;
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
}
class $$GroupHistoriesTableFilterComposer
@ -13811,10 +13630,6 @@ class $$GroupHistoriesTableFilterComposer
column: $table.groupHistoryId,
builder: (column) => ColumnFilters(column));
ColumnFilters<int> get affectedContactId => $composableBuilder(
column: $table.affectedContactId,
builder: (column) => ColumnFilters(column));
ColumnFilters<String> get oldGroupName => $composableBuilder(
column: $table.oldGroupName, builder: (column) => ColumnFilters(column));
@ -13873,6 +13688,26 @@ class $$GroupHistoriesTableFilterComposer
));
return composer;
}
$$ContactsTableFilterComposer get affectedContactId {
final $$ContactsTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.affectedContactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableFilterComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$GroupHistoriesTableOrderingComposer
@ -13888,10 +13723,6 @@ class $$GroupHistoriesTableOrderingComposer
column: $table.groupHistoryId,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<int> get affectedContactId => $composableBuilder(
column: $table.affectedContactId,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get oldGroupName => $composableBuilder(
column: $table.oldGroupName,
builder: (column) => ColumnOrderings(column));
@ -13950,6 +13781,26 @@ class $$GroupHistoriesTableOrderingComposer
));
return composer;
}
$$ContactsTableOrderingComposer get affectedContactId {
final $$ContactsTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.affectedContactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableOrderingComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$GroupHistoriesTableAnnotationComposer
@ -13964,9 +13815,6 @@ class $$GroupHistoriesTableAnnotationComposer
GeneratedColumn<String> get groupHistoryId => $composableBuilder(
column: $table.groupHistoryId, builder: (column) => column);
GeneratedColumn<int> get affectedContactId => $composableBuilder(
column: $table.affectedContactId, builder: (column) => column);
GeneratedColumn<String> get oldGroupName => $composableBuilder(
column: $table.oldGroupName, builder: (column) => column);
@ -14023,6 +13871,26 @@ class $$GroupHistoriesTableAnnotationComposer
));
return composer;
}
$$ContactsTableAnnotationComposer get affectedContactId {
final $$ContactsTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.affectedContactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableAnnotationComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$GroupHistoriesTableTableManager extends RootTableManager<
@ -14036,7 +13904,8 @@ class $$GroupHistoriesTableTableManager extends RootTableManager<
$$GroupHistoriesTableUpdateCompanionBuilder,
(GroupHistory, $$GroupHistoriesTableReferences),
GroupHistory,
PrefetchHooks Function({bool groupId, bool contactId})> {
PrefetchHooks Function(
{bool groupId, bool contactId, bool affectedContactId})> {
$$GroupHistoriesTableTableManager(_$TwonlyDB db, $GroupHistoriesTable table)
: super(TableManagerState(
db: db,
@ -14105,7 +13974,8 @@ class $$GroupHistoriesTableTableManager extends RootTableManager<
$$GroupHistoriesTableReferences(db, table, e)
))
.toList(),
prefetchHooksCallback: ({groupId = false, contactId = false}) {
prefetchHooksCallback: (
{groupId = false, contactId = false, affectedContactId = false}) {
return PrefetchHooks(
db: db,
explicitlyWatchedTables: [],
@ -14144,6 +14014,17 @@ class $$GroupHistoriesTableTableManager extends RootTableManager<
.userId,
) as T;
}
if (affectedContactId) {
state = state.withJoin(
currentTable: table,
currentColumn: table.affectedContactId,
referencedTable: $$GroupHistoriesTableReferences
._affectedContactIdTable(db),
referencedColumn: $$GroupHistoriesTableReferences
._affectedContactIdTable(db)
.userId,
) as T;
}
return state;
},
@ -14166,7 +14047,8 @@ typedef $$GroupHistoriesTableProcessedTableManager = ProcessedTableManager<
$$GroupHistoriesTableUpdateCompanionBuilder,
(GroupHistory, $$GroupHistoriesTableReferences),
GroupHistory,
PrefetchHooks Function({bool groupId, bool contactId})>;
PrefetchHooks Function(
{bool groupId, bool contactId, bool affectedContactId})>;
class $TwonlyDBManager {
final _$TwonlyDB _db;

View file

@ -1566,838 +1566,9 @@ class Shape17 extends i0.VersionedTable {
i1.GeneratedColumn<String> _column_100(String aliasedName) =>
i1.GeneratedColumn<String>('draft_message', aliasedName, true,
type: i1.DriftSqlType.string);
final class Schema4 extends i0.VersionedSchema {
Schema4({required super.database}) : super(version: 4);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
contacts,
groups,
mediaFiles,
messages,
messageHistories,
reactions,
groupMembers,
receipts,
receivedReceipts,
signalIdentityKeyStores,
signalPreKeyStores,
signalSenderKeyStores,
signalSessionStores,
signalContactPreKeys,
signalContactSignedPreKeys,
messageActions,
groupHistories,
];
late final Shape0 contacts = Shape0(
source: i0.VersionedTable(
entityName: 'contacts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(user_id)',
],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_4,
_column_5,
_column_6,
_column_7,
_column_8,
_column_9,
_column_10,
_column_11,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape17 groups = Shape17(
source: i0.VersionedTable(
entityName: 'groups',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_id)',
],
columns: [
_column_13,
_column_14,
_column_15,
_column_16,
_column_17,
_column_18,
_column_19,
_column_20,
_column_21,
_column_22,
_column_23,
_column_24,
_column_100,
_column_25,
_column_26,
_column_27,
_column_12,
_column_28,
_column_29,
_column_30,
_column_31,
_column_32,
_column_33,
_column_34,
_column_35,
],
attachedDatabase: database,
),
alias: null);
late final Shape2 mediaFiles = Shape2(
source: i0.VersionedTable(
entityName: 'media_files',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(media_id)',
],
columns: [
_column_36,
_column_37,
_column_38,
_column_39,
_column_40,
_column_41,
_column_42,
_column_43,
_column_44,
_column_45,
_column_46,
_column_47,
_column_48,
_column_49,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape3 messages = Shape3(
source: i0.VersionedTable(
entityName: 'messages',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id)',
],
columns: [
_column_50,
_column_51,
_column_52,
_column_37,
_column_53,
_column_54,
_column_55,
_column_56,
_column_46,
_column_57,
_column_58,
_column_59,
_column_60,
_column_12,
_column_61,
_column_62,
_column_63,
],
attachedDatabase: database,
),
alias: null);
late final Shape4 messageHistories = Shape4(
source: i0.VersionedTable(
entityName: 'message_histories',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(id)',
],
columns: [
_column_64,
_column_65,
_column_66,
_column_53,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape5 reactions = Shape5(
source: i0.VersionedTable(
entityName: 'reactions',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id, sender_id, emoji)',
],
columns: [
_column_65,
_column_67,
_column_68,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape6 groupMembers = Shape6(
source: i0.VersionedTable(
entityName: 'group_members',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_id, contact_id)',
],
columns: [
_column_50,
_column_69,
_column_70,
_column_71,
_column_72,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape7 receipts = Shape7(
source: i0.VersionedTable(
entityName: 'receipts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(receipt_id)',
],
columns: [
_column_73,
_column_74,
_column_75,
_column_76,
_column_77,
_column_78,
_column_79,
_column_80,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape8 receivedReceipts = Shape8(
source: i0.VersionedTable(
entityName: 'received_receipts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(receipt_id)',
],
columns: [
_column_73,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape9 signalIdentityKeyStores = Shape9(
source: i0.VersionedTable(
entityName: 'signal_identity_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(device_id, name)',
],
columns: [
_column_81,
_column_82,
_column_83,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape10 signalPreKeyStores = Shape10(
source: i0.VersionedTable(
entityName: 'signal_pre_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(pre_key_id)',
],
columns: [
_column_84,
_column_85,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape11 signalSenderKeyStores = Shape11(
source: i0.VersionedTable(
entityName: 'signal_sender_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(sender_key_name)',
],
columns: [
_column_86,
_column_87,
],
attachedDatabase: database,
),
alias: null);
late final Shape12 signalSessionStores = Shape12(
source: i0.VersionedTable(
entityName: 'signal_session_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(device_id, name)',
],
columns: [
_column_81,
_column_82,
_column_88,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape13 signalContactPreKeys = Shape13(
source: i0.VersionedTable(
entityName: 'signal_contact_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id, pre_key_id)',
],
columns: [
_column_74,
_column_84,
_column_85,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape14 signalContactSignedPreKeys = Shape14(
source: i0.VersionedTable(
entityName: 'signal_contact_signed_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id)',
],
columns: [
_column_74,
_column_89,
_column_90,
_column_91,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape15 messageActions = Shape15(
source: i0.VersionedTable(
entityName: 'message_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id, contact_id, type)',
],
columns: [
_column_65,
_column_92,
_column_37,
_column_93,
],
attachedDatabase: database,
),
alias: null);
late final Shape16 groupHistories = Shape16(
source: i0.VersionedTable(
entityName: 'group_histories',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_history_id)',
],
columns: [
_column_94,
_column_50,
_column_95,
_column_101,
_column_97,
_column_98,
_column_99,
_column_37,
_column_93,
],
attachedDatabase: database,
),
alias: null);
}
i1.GeneratedColumn<int> _column_101(String aliasedName) =>
i1.GeneratedColumn<int>('affected_contact_id', aliasedName, true,
type: i1.DriftSqlType.int);
final class Schema5 extends i0.VersionedSchema {
Schema5({required super.database}) : super(version: 5);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
contacts,
groups,
mediaFiles,
messages,
messageHistories,
reactions,
groupMembers,
receipts,
receivedReceipts,
signalIdentityKeyStores,
signalPreKeyStores,
signalSenderKeyStores,
signalSessionStores,
signalContactPreKeys,
signalContactSignedPreKeys,
messageActions,
groupHistories,
];
late final Shape0 contacts = Shape0(
source: i0.VersionedTable(
entityName: 'contacts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(user_id)',
],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_4,
_column_5,
_column_6,
_column_7,
_column_8,
_column_9,
_column_10,
_column_11,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape17 groups = Shape17(
source: i0.VersionedTable(
entityName: 'groups',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_id)',
],
columns: [
_column_13,
_column_14,
_column_15,
_column_16,
_column_17,
_column_18,
_column_19,
_column_20,
_column_21,
_column_22,
_column_23,
_column_24,
_column_100,
_column_25,
_column_26,
_column_27,
_column_12,
_column_28,
_column_29,
_column_30,
_column_31,
_column_32,
_column_33,
_column_34,
_column_35,
],
attachedDatabase: database,
),
alias: null);
late final Shape18 mediaFiles = Shape18(
source: i0.VersionedTable(
entityName: 'media_files',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(media_id)',
],
columns: [
_column_36,
_column_37,
_column_38,
_column_39,
_column_40,
_column_41,
_column_42,
_column_43,
_column_44,
_column_45,
_column_46,
_column_47,
_column_48,
_column_49,
_column_102,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape3 messages = Shape3(
source: i0.VersionedTable(
entityName: 'messages',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id)',
],
columns: [
_column_50,
_column_51,
_column_52,
_column_37,
_column_53,
_column_54,
_column_55,
_column_56,
_column_46,
_column_57,
_column_58,
_column_59,
_column_60,
_column_12,
_column_61,
_column_62,
_column_63,
],
attachedDatabase: database,
),
alias: null);
late final Shape4 messageHistories = Shape4(
source: i0.VersionedTable(
entityName: 'message_histories',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(id)',
],
columns: [
_column_64,
_column_65,
_column_66,
_column_53,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape5 reactions = Shape5(
source: i0.VersionedTable(
entityName: 'reactions',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id, sender_id, emoji)',
],
columns: [
_column_65,
_column_67,
_column_68,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape6 groupMembers = Shape6(
source: i0.VersionedTable(
entityName: 'group_members',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_id, contact_id)',
],
columns: [
_column_50,
_column_69,
_column_70,
_column_71,
_column_72,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape19 receipts = Shape19(
source: i0.VersionedTable(
entityName: 'receipts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(receipt_id)',
],
columns: [
_column_73,
_column_74,
_column_75,
_column_76,
_column_77,
_column_103,
_column_78,
_column_79,
_column_80,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape8 receivedReceipts = Shape8(
source: i0.VersionedTable(
entityName: 'received_receipts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(receipt_id)',
],
columns: [
_column_73,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape9 signalIdentityKeyStores = Shape9(
source: i0.VersionedTable(
entityName: 'signal_identity_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(device_id, name)',
],
columns: [
_column_81,
_column_82,
_column_83,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape10 signalPreKeyStores = Shape10(
source: i0.VersionedTable(
entityName: 'signal_pre_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(pre_key_id)',
],
columns: [
_column_84,
_column_85,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape11 signalSenderKeyStores = Shape11(
source: i0.VersionedTable(
entityName: 'signal_sender_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(sender_key_name)',
],
columns: [
_column_86,
_column_87,
],
attachedDatabase: database,
),
alias: null);
late final Shape12 signalSessionStores = Shape12(
source: i0.VersionedTable(
entityName: 'signal_session_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(device_id, name)',
],
columns: [
_column_81,
_column_82,
_column_88,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape13 signalContactPreKeys = Shape13(
source: i0.VersionedTable(
entityName: 'signal_contact_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id, pre_key_id)',
],
columns: [
_column_74,
_column_84,
_column_85,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape14 signalContactSignedPreKeys = Shape14(
source: i0.VersionedTable(
entityName: 'signal_contact_signed_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id)',
],
columns: [
_column_74,
_column_89,
_column_90,
_column_91,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape15 messageActions = Shape15(
source: i0.VersionedTable(
entityName: 'message_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id, contact_id, type)',
],
columns: [
_column_65,
_column_92,
_column_37,
_column_93,
],
attachedDatabase: database,
),
alias: null);
late final Shape16 groupHistories = Shape16(
source: i0.VersionedTable(
entityName: 'group_histories',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_history_id)',
],
columns: [
_column_94,
_column_50,
_column_95,
_column_101,
_column_97,
_column_98,
_column_99,
_column_37,
_column_93,
],
attachedDatabase: database,
),
alias: null);
}
class Shape18 extends i0.VersionedTable {
Shape18({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get mediaId =>
columnsByName['media_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get type =>
columnsByName['type']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get uploadState =>
columnsByName['upload_state']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get downloadState =>
columnsByName['download_state']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<bool> get requiresAuthentication =>
columnsByName['requires_authentication']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get stored =>
columnsByName['stored']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get isDraftMedia =>
columnsByName['is_draft_media']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<String> get reuploadRequestedBy =>
columnsByName['reupload_requested_by']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get displayLimitInMilliseconds =>
columnsByName['display_limit_in_milliseconds']!
as i1.GeneratedColumn<int>;
i1.GeneratedColumn<bool> get removeAudio =>
columnsByName['remove_audio']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<i2.Uint8List> get downloadToken =>
columnsByName['download_token']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get encryptionKey =>
columnsByName['encryption_key']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get encryptionMac =>
columnsByName['encryption_mac']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get encryptionNonce =>
columnsByName['encryption_nonce']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get storedFileHash =>
columnsByName['stored_file_hash']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<i2.Uint8List> _column_102(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List>('stored_file_hash', aliasedName, true,
type: i1.DriftSqlType.blob);
class Shape19 extends i0.VersionedTable {
Shape19({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get receiptId =>
columnsByName['receipt_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get contactId =>
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get messageId =>
columnsByName['message_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<i2.Uint8List> get message =>
columnsByName['message']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<bool> get contactWillSendsReceipt =>
columnsByName['contact_will_sends_receipt']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<DateTime> get markForRetry =>
columnsByName['mark_for_retry']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get ackByServerAt =>
columnsByName['ack_by_server_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<int> get retryCount =>
columnsByName['retry_count']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get lastRetry =>
columnsByName['last_retry']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<DateTime> _column_103(String aliasedName) =>
i1.GeneratedColumn<DateTime>('mark_for_retry', aliasedName, true,
type: i1.DriftSqlType.dateTime);
i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
}) {
return (currentVersion, database) async {
switch (currentVersion) {
@ -2411,16 +1582,6 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema);
await from2To3(migrator, schema);
return 3;
case 3:
final schema = Schema4(database: database);
final migrator = i1.Migrator(database, schema);
await from3To4(migrator, schema);
return 4;
case 4:
final schema = Schema5(database: database);
final migrator = i1.Migrator(database, schema);
await from4To5(migrator, schema);
return 5;
default:
throw ArgumentError.value('Unknown migration from $currentVersion');
}
@ -2430,13 +1591,9 @@ i0.MigrationStepWithVersion migrationSteps({
i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
}) =>
i0.VersionedSchema.stepByStepHelper(
step: migrationSteps(
from1To2: from1To2,
from2To3: from2To3,
from3To4: from3To4,
from4To5: from4To5,
));

View file

@ -1,4 +1,3 @@
import 'package:clock/clock.dart';
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart'
show DriftNativeOptions, driftDatabase;
@ -192,9 +191,9 @@ class TwonlyDatabaseOld extends _$TwonlyDatabaseOld {
await (delete(signalPreKeyStores)
..where(
(t) => (t.createdAt.isSmallerThanValue(
clock.now().subtract(
const Duration(days: 25),
),
DateTime.now().subtract(
const Duration(days: 25),
),
)),
))
.go();

View file

@ -0,0 +1,455 @@
{
"@@locale": "de",
"registerTitle": "Willkommen bei twonly!",
"registerSlogan": "twonly, eine private und sichere Möglichkeit um mit Freunden in Kontakt zu bleiben.",
"onboardingWelcomeTitle": "Willkommen bei twonly!",
"onboardingWelcomeBody": "Erlebe eine private und sichere Möglichkeit mit Freunden in Kontakt zu bleiben, indem du spontane Bilder teilst.",
"onboardingE2eTitle": "Unbekümmert teilen",
"onboardingE2eBody": "Genieße durch die Ende-zu-Ende-Verschlüsselung die Gewissheit, dass nur du und deine Freunde die geteilten Momente sehen können.",
"onboardingFocusTitle": "Fokussiere dich auf das Teilen von Momenten",
"onboardingFocusBody": "Verabschiede dich von süchtig machenden Funktionen! twonly wurde für das Teilen von Momenten ohne nutzlose Ablenkungen oder Werbung entwickelt.",
"onboardingSendTwonliesTitle": "twonlies senden",
"onboardingSendTwonliesBody": "Teile Momente sicher mit deinem Partner. twonly stellt sicher, dass nur dein Partner sie öffnen kann, sodass deine Momente mit deinem Partner eine two(o)nly Sache bleiben!",
"onboardingNotProductTitle": "Du bist nicht das Produkt!",
"onboardingNotProductBody": "twonly wird durch Spenden und ein optionales Abonnement finanziert. Deine Daten werden niemals verkauft.",
"onboardingBuyOneGetTwoTitle": "Kaufe eins, bekomme zwei",
"onboardingBuyOneGetTwoBody": "twonly benötigt immer mindestens zwei Personen, daher erhältst du beim Kauf eine zweite kostenlose Lizenz für deinen twonly-Partner.",
"onboardingGetStartedTitle": "Auf geht's",
"onboardingGetStartedBody": "Du kannst twonly kostenlos im Preview-Modus testen. In diesem Modus kannst du von anderen gefunden werden und Bilder oder Videos empfangen, aber du kannst selbst keine senden.",
"onboardingTryForFree": "Jetzt registrieren",
"registerUsernameSlogan": "Bitte wähle einen Benutzernamen, damit dich andere finden können!",
"registerUsernameDecoration": "Benutzername",
"registerUsernameLimits": "Der Benutzername muss mindestens 3 Zeichen lang sein.",
"registerSubmitButton": "Jetzt registrieren!",
"registerTwonlyCodeText": "Hast du einen twonly-Code erhalten? Dann löse ihn entweder direkt hier oder später ein!",
"registerTwonlyCodeLabel": "twonly-Code",
"newMessageTitle": "Neue Nachricht",
"chatsTapToSend": "Klicke, um dein erstes Bild zu teilen.",
"cameraPreviewSendTo": "Senden an",
"shareImageTitle": "Teilen mit",
"shareImageBestFriends": "Beste Freunde",
"shareImagePinnedContacts": "Angeheftet",
"shareImagedEditorSendImage": "Senden",
"shareImagedEditorShareWith": "Teilen mit",
"shareImagedEditorSaveImage": "Speichern",
"shareImagedEditorSavedImage": "Gespeichert",
"shareImagedSelectAll": "Alle auswählen",
"shareImageAllUsers": "Alle Kontakte",
"shareImageAllTwonlyWarning": "twonlies können nur an verifizierte Kontakte gesendet werden!",
"shareImageSearchAllContacts": "Alle Kontakte durchsuchen",
"shareImageUserNotVerified": "Benutzer ist nicht verifiziert",
"shareImageUserNotVerifiedDesc": "twonlies können nur an verifizierte Nutzer gesendet werden. Um einen Nutzer zu verifizieren, gehe auf sein Profil und auf „Sicherheitsnummer verifizieren“.",
"shareImageShowArchived": "Archivierte Benutzer anzeigen",
"startNewChatSearchHint": "Name, Benutzername oder Gruppenname",
"searchUsernameInput": "Benutzername",
"searchUsernameTitle": "Benutzernamen suchen",
"searchUserNamePreview": "Um dich und andere twonly Benutzer vor Spam und Missbrauch zu schützen, ist es nicht möglich, im Preview-Modus nach anderen Personen zu suchen. Andere Benutzer können dich finden und deren Anfragen werden dann hier angezeigt!",
"selectSubscription": "Abo auswählen",
"searchUsernameNotFound": "Benutzername nicht gefunden",
"searchUsernameNotFoundBody": "Es wurde kein Benutzer mit dem Benutzernamen \"{username}\" gefunden.",
"searchUsernameNewFollowerTitle": "Folgeanfragen",
"searchUsernameQrCodeBtn": "QR-Code scannen",
"searchUserNamePending": "Ausstehend",
"searchUserNameBlockUserTooltip": "Benutzer ohne Benachrichtigung blockieren.",
"searchUserNameRejectUserTooltip": "Die Anfrage ablehnen und den Anfragenden informieren.",
"searchUserNameArchiveUserTooltip": "Benutzer archivieren. Du wirst informiert sobald er deine Anfrage akzeptiert.",
"userFound": "{username} gefunden",
"userFoundBody": "Möchtest du eine Folgeanfrage stellen?",
"chatListViewSearchUserNameBtn": "Füge deinen ersten twonly-Kontakt hinzu!",
"chatListViewSendFirstTwonly": "Sende dein erstes twonly!",
"chatListDetailInput": "Nachricht eingeben",
"userDeletedAccount": "Der Nutzer hat sein Konto gelöscht.",
"contextMenuUserProfile": "Userprofil",
"contextMenuVerifyUser": "Verifizieren",
"contextMenuArchiveUser": "Archivieren",
"contextMenuUndoArchiveUser": "Archivierung aufheben",
"startNewChatTitle": "Kontakt wählen",
"startNewChatNewContact": "Neuer Kontakt",
"startNewChatYourContacts": "Deine Kontakte",
"contextMenuOpenChat": "Chat",
"contextMenuPin": "Anheften",
"contextMenuUnpin": "Lösen",
"mediaViewerAuthReason": "Bitte authentifiziere dich, um diesen twonly zu sehen!",
"mediaViewerTwonlyTapToOpen": "Tippe um den twonly zu öffnen!",
"messageSendState_Received": "Empfangen",
"messageSendState_Opened": "Geöffnet",
"messageSendState_Send": "Gesendet",
"messageSendState_Sending": "Wird gesendet",
"messageSendState_TapToLoad": "Tippe zum Laden",
"messageSendState_Loading": "Herunterladen",
"messageStoredInGallery": "Gespeichert",
"messageReopened": "Erneut geöffnet",
"imageEditorDrawOk": "Zeichnung machen",
"settingsTitle": "Einstellungen",
"settingsChats": "Chats",
"settingsStorageData": "Daten und Speicher",
"settingsStorageDataStoreInGTitle": "In der Galerie speichern",
"settingsStorageDataStoreInGSubtitle": "Speichere Bilder zusätzlich in der Systemgalerie.",
"settingsStorageDataMediaAutoDownload": "Automatischer Mediendownload",
"settingsStorageDataAutoDownMobile": "Bei Nutzung mobiler Daten",
"settingsStorageDataAutoDownWifi": "Bei Nutzung von WLAN",
"settingsPreSelectedReactions": "Vorgewählte Reaktions-Emojis",
"settingsPreSelectedReactionsError": "Es können maximal 12 Reaktionen ausgewählt werden.",
"settingsProfile": "Profil",
"settingsProfileCustomizeAvatar": "Avatar anpassen",
"settingsProfileEditDisplayName": "Anzeigename",
"settingsProfileEditDisplayNameNew": "Neuer Anzeigename",
"settingsAccount": "Konto",
"settingsSubscription": "Abonnement",
"settingsAppearance": "Erscheinungsbild",
"settingsPrivacy": "Datenschutz",
"settingsPrivacyBlockUsers": "Benutzer blockieren",
"settingsPrivacyBlockUsersDesc": "Blockierte Benutzer können nicht mit dir kommunizieren. Du kannst einen blockierten Benutzer jederzeit wieder entsperren.",
"settingsPrivacyBlockUsersCount": "{len} Kontakt(e)",
"settingsNotification": "Benachrichtigung",
"settingsNotifyTroubleshooting": "Fehlersuche",
"settingsNotifyTroubleshootingDesc": "Hier klicken, wenn Probleme beim Empfang von Push-Benachrichtigungen auftreten.",
"settingsNotifyTroubleshootingNoProblem": "Kein Problem festgestellt",
"settingsNotifyTroubleshootingNoProblemDesc": "Klicke auf OK, um eine Testbenachrichtigung zu erhalten. Wenn du auch nach 10 Minuten warten keine Nachricht erhältst, sende uns bitte dein Diagnoseprotokoll unter Einstellungen > Hilfe > Diagnoseprotokoll, damit wir uns das Problem ansehen können.",
"settingsHelp": "Hilfe",
"settingsHelpFAQ": "FAQ",
"feedbackTooltip": "Feedback zur Verbesserung von twonly geben.",
"settingsHelpContactUs": "Kontaktiere uns",
"contactUsFaq": "FAQ schon gelesen?",
"contactUsEmojis": "Wie fühlst du dich? (optional)",
"contactUsSelectOption": "Bitte wähle eine Option",
"contactUsReason": "Sag uns, warum du uns kontaktierst",
"contactUsMessage": "Wenn du eine Antwort erhalten möchtest, füge bitte deine E-Mail-Adresse hinzu, damit wir dich kontaktieren können.",
"contactUsYourMessage": "Deine Nachricht",
"contactUsMessageTitle": "Erzähl uns, was los ist",
"contactUsReasonNotWorking": "Etwas funktioniert nicht",
"contactUsReasonFeatureRequest": "Funktionsanfrage",
"contactUsReasonQuestion": "Frage",
"contactUsReasonFeedback": "Feedback",
"contactUsReasonOther": "Sonstiges",
"contactUsIncludeLog": "Debug-Protokoll anhängen.",
"contactUsWhatsThat": "Was ist das?",
"contactUsLastWarning": "Dies sind die Informationen, die an uns gesendet werden. Bitte prüfen Sie sie und klicke dann auf „Abschicken“.",
"contactUsSuccess": "Feedback erfolgreich übermittelt!",
"contactUsShortcut": "Feedback-Symbol ausblenden",
"settingsHelpDiagnostics": "Diagnoseprotokoll",
"settingsHelpVersion": "Version",
"settingsHelpLicenses": "Lizenzen (Source-Code)",
"settingsHelpCredits": "Lizenzen (Bilder)",
"settingsHelpImprint": "Impressum & Datenschutzrichtlinie",
"settingsHelpTerms": "Nutzungsbedingungen",
"settingsAppearanceTheme": "Theme",
"settingsAccountDeleteAccount": "Konto löschen",
"settingsAccountDeleteAccountWithBallance": "Im nächsten Schritt kannst du auswählen, was du mit dem Restguthaben ({credit}) machen willst.",
"settingsAccountDeleteAccountNoInternet": "Zum Löschen deines Accounts ist eine Internetverbindung erforderlich.",
"settingsAccountDeleteAccountNoBallance": "Wenn du dein Konto gelöscht hast, gibt es keinen Weg zurück.",
"settingsAccountDeleteModalTitle": "Bist du sicher?",
"settingsAccountDeleteModalBody": "Dein Konto wird gelöscht. Es gibt keine Möglichkeit, es wiederherzustellen.",
"contactVerifyNumberTitle": "Sicherheitsnummer verifizieren",
"contactVerifyNumberTapToScan": "Zum Scannen tippen",
"contactVerifyNumberMarkAsVerified": "Als verifiziert markieren",
"contactVerifyNumberClearVerification": "Verifizierung aufheben",
"contactVerifyNumberLongDesc": "Um die Ende-zu-Ende-Verschlüsselung mit {username} zu verifizieren, vergleiche die Zahlen mit ihrem Gerät. Die Person kann auch deinen Code mit ihrem Gerät scannen.",
"contactNickname": "Spitzname",
"contactNicknameNew": "Neuer Spitzname",
"contactBlock": "Blockieren",
"contactRemove": "Benutzer löschen",
"contactRemoveTitle": "{username} löschen?",
"contactRemoveBody": "Entferne den Benutzer und lösche den Chat sowie alle zugehörigen Mediendateien dauerhaft. Dadurch wird auch DEIN KONTO VON DEM TELEFON DEINES KONTAKTS gelöscht.",
"deleteAllContactMessages": "Textnachrichten löschen",
"deleteAllContactMessagesBody": "Dadurch werden alle Nachrichten, ausgenommen gespeicherte Mediendateien, in deinem Chat mit {username} gelöscht. Dies löscht NICHT die auf dem Gerät von {username} gespeicherten Nachrichten!",
"contactBlockTitle": "Blockiere {username}",
"contactBlockBody": "Ein blockierter Benutzer kann dir keine Nachrichten mehr senden, und sein Profil ist nicht mehr sichtbar. Um die Blockierung eines Benutzers aufzuheben, navigiere einfach zu Einstellungen > Datenschutz > Blockierte Benutzer.",
"undo": "Rückgängig",
"redo": "Wiederholen",
"next": "Weiter",
"submit": "Abschicken",
"close": "Schließen",
"cancel": "Abbrechen",
"edit": "Bearbeiten",
"ok": "Ok",
"now": "Jetzt",
"you": "Du",
"minutesShort": "Min.",
"image": "Bild",
"video": "Video",
"react": "Reagieren",
"reply": "Antworten",
"copy": "Kopieren",
"delete": "Löschen",
"info": "Info",
"disable": "Deaktiviern",
"enable": "Aktivieren",
"switchFrontAndBackCamera": "Zwischen Front- und Rückkamera wechseln.",
"addTextItem": "Text",
"protectAsARealTwonly": "Als echtes twonly senden!",
"addDrawing": "Zeichnung",
"addEmoji": "Emoji",
"toggleFlashLight": "Taschenlampe umschalten",
"toggleHighQuality": "Bessere Auflösung umschalten",
"searchUsernameNotFoundLong": "\"{username}\" ist kein twonly-Benutzer. Bitte überprüfe den Benutzernamen und versuche es erneut.",
"errorUnknown": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es später erneut.",
"errorBadRequest": "Die Anfrage konnte vom Server aufgrund einer fehlerhaften Syntax nicht verstanden werden. Bitte überprüfe deine Eingabe und versuche es erneut.",
"errorTooManyRequests": "Du hast in kurzer Zeit zu viele Anfragen gestellt. Bitte warte einen Moment, bevor du es erneut versuchst.",
"errorInternalError": "Der Server ist derzeit nicht verfügbar. Bitte versuche es später erneut.",
"errorInvalidInvitationCode": "Der von dir angegebene Einladungscode ist ungültig. Bitte überprüfe den Code und versuche es erneut.",
"errorUsernameAlreadyTaken": "Der Benutzername ist bereits vergeben.",
"errorSignatureNotValid": "Die bereitgestellte Signatur ist nicht gültig. Bitte überprüfe deine Anmeldeinformationen und versuche es erneut.",
"errorUsernameNotFound": "Der eingegebene Benutzername existiert nicht. Bitte überprüfe die Schreibweise oder erstelle ein neues Konto.",
"errorUsernameNotValid": "Der von dir angegebene Benutzername entspricht nicht den erforderlichen Kriterien. Bitte wähle einen gültigen Benutzernamen.",
"errorInvalidPublicKey": "Der von dir angegebene öffentliche Schlüssel ist ungültig. Bitte überprüfe den Schlüssel und versuche es erneut.",
"errorSessionAlreadyAuthenticated": "Du bist bereits angemeldet. Bitte melde dich ab, wenn du dich mit einem anderen Konto anmelden möchtest.",
"errorSessionNotAuthenticated": "Deine Sitzung ist nicht authentifiziert. Bitte melde dich an, um fortzufahren.",
"errorOnlyOneSessionAllowed": "Es ist nur eine aktive Sitzung pro Benutzer erlaubt. Bitte melde dich von anderen Geräten ab, um fortzufahren.",
"upgradeToPaidPlan": "Upgrade auf einen kostenpflichtigen Plan.",
"upgradeToPaidPlanButton": "Auf {planId} upgraden",
"partOfPaidPlanOf": "Du bist Teil des bezahlten Plans von {username}!",
"errorNotEnoughCredit": "Du hast nicht genügend twonly-Guthaben.",
"errorPlanLimitReached": "Du hast das Limit deines Plans erreicht. Bitte upgrade deinen Plan.",
"errorPlanNotAllowed": "Dieses Feature ist in deinem aktuellen Plan nicht verfügbar.",
"errorVoucherInvalid": "Der eingegebene Gutschein-Code ist nicht gültig.",
"errorPlanUpgradeNotYearly": "Das Upgrade des Plans muss jährlich bezahlt werden, da der aktuelle Plan ebenfalls jährlich abgerechnet wird.",
"proFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
"proFeature2": "1 zusätzlicher Plus Benutzer",
"proFeature3": "Flammen wiederherstellen",
"proFeature4": "Cloud-Backup verschlüsselt (coming-soon)",
"year": "year",
"month": "month",
"familyFeature1": "✓ Alles von Pro",
"familyFeature2": "4 zusätzliche Plus Benutzer",
"redeemUserInviteCode": "Oder löse einen twonly-Code ein.",
"freeFeature1": "10 Medien-Datei-Uploads pro Tag",
"plusFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
"plusFeature2": "Zusatzfunktionen (coming-soon)",
"transactionHistory": "Transaktionshistorie",
"currentBalance": "Dein Guthaben",
"manageAdditionalUsers": "Zusätzliche Benutzer verwalten",
"manageSubscription": "Abonnement verwalten",
"nextPayment": "Nächste Zahlung",
"open": "Offene",
"buy": "Kaufen",
"createOrRedeemVoucher": "Gutschein erstellen oder einlösen",
"subscriptionRefund": "Wenn du ein Upgrade durchführst, erhältst du eine Rückerstattung von {refund} für dein aktuelles Abonnement.",
"createVoucher": "Gutschein kaufen",
"createVoucherDesc": "Wähle den Wert des Gutscheins. Der Wert des Gutschein wird von deinem twonly-Guthaben abgezogen.",
"redeemVoucher": "Gutschein einlösen",
"redeemUserInviteCodeTitle": "twonly-Code einlösen",
"redeemUserInviteCodeSuccess": "Dein Plan wurde erfolgreich angepasst.",
"voucherCreated": "Gutschein wurde erstellt",
"openVouchers": "Offene Gutscheine",
"enterVoucherCode": "Gutschein Code eingeben",
"voucherRedeemed": "Gutschein eingelöst",
"requestedVouchers": "Beantragte Gutscheine",
"redeemedVouchers": "Eingelöste Gutscheine",
"transactionCash": "Bargeldtransaktion",
"transactionPlanUpgrade": "Planupgrade",
"transactionRefund": "Rückerstattung",
"transactionAutoRenewal": "Automatische Verlängerung",
"refund": "Rückerstattung",
"transactionThanksForTesting": "Danke fürs Testen",
"transactionUnknown": "Unbekannte Transaktion",
"transactionVoucherCreated": "Gutschein erstellt",
"transactionVoucherRedeemed": "Gutschein eingelöst",
"checkoutOptions": "Optionen",
"checkoutPayYearly": "Jährlich bezahlen",
"checkoutTotal": "Gesamt",
"selectPaymentMethod": "Zahlungsmethode auswählen",
"twonlyCredit": "twonly-Guthaben",
"notEnoughCredit": "Du hast nicht genügend Guthaben!",
"chargeCredit": "Guthaben aufladen",
"autoRenewal": "Automatische Verlängerung",
"autoRenewalDesc": "Du kannst dies jederzeit ändern.",
"autoRenewalLongDesc": "Wenn dein Abonnement ausläuft, wirst du automatisch auf den Preview-Plan zurückgestuft. Wenn du die automatische Verlängerung aktivierst, vergewissere dich bitte, dass du über genügend Guthaben für die automatische Erneuerung verfügst. Wir werden dich rechtzeitig vor der automatischen Erneuerung benachrichtigen.",
"planSuccessUpgraded": "Dein Plan wurde erfolgreich aktualisiert.",
"checkoutSubmit": "Kostenpflichtig bestellen",
"additionalUsersList": "Ihre zusätzlichen Benutzer",
"additionalUsersPlusTokens": "twonly-Codes für \"Plus\"-Benutzer",
"additionalUsersFreeTokens": "twonly-Codes für \"Free\"-Benutzer",
"planNotAllowed": "In deinem aktuellen Plan kannst du keine Mediendateien versenden. Aktualisiere deinen Plan jetzt, um die Mediendatei zu senden.",
"planLimitReached": "Du hast dein Planlimit für heute erreicht. Aktualisiere deinen Plan jetzt, um die Mediendatei zu senden.",
"galleryDelete": "Datei löschen",
"galleryExport": "In Galerie exportieren",
"galleryExportSuccess": "Erfolgreich in der Gallery gespeichert.",
"galleryDetails": "Details anzeigen",
"settingsResetTutorials": "Tutorials erneut anzeigen",
"settingsResetTutorialsDesc": "Klicke hier, um bereits angezeigte Tutorials erneut anzuzeigen.",
"settingsResetTutorialsSuccess": "Tutorials werden erneut angezeigt.",
"tutorialChatListSearchUsersTitle": "Freunde finden und Freundschaftsanfragen verwalten",
"tutorialChatListSearchUsersDesc": "Wenn du die Benutzernamen deiner Freunde kennst, kannst du sie hier suchen und eine Freundschaftsanfrage senden. Außerdem siehst du hier alle Anfragen von anderen Nutzern, die du annehmen oder blockieren kannst.",
"tutorialChatListContextMenuTitle": "Klicke lange auf den Kontakt, um das Kontextmenü zu öffnen.",
"tutorialChatListContextMenuDesc": "Mit dem Kontextmenü kannst du deine Kontakte anheften, archivieren und verschiedene Aktionen durchführen. Halte dazu einfach den Kontakt lange gedrückt und bewege dann deinen Finger auf die gewünschte Option oder tippe direkt darauf.",
"tutorialChatMessagesVerifyShieldTitle": "Verifiziere deine Kontakte!",
"tutorialChatMessagesVerifyShieldDesc": "twonly nutzt das Signal-Protokoll für eine sichere Ende-zu-Ende Verschlüsselung. Bei der ersten Kontaktaufnahme wird dafür der öffentliche Identitätsschlüssel von deinem Kontakt heruntergeladen. Um sicherzustellen, dass dieser Schlüssel nicht von Dritten ausgetauscht wurde, solltest du ihn mit deinem Freund vergleichen, wenn ihr euch persönlich trefft. Sobald du den Benutzer verifiziert hast, kannst du auch beim verschicken von Bildern und Videos den twonly-Modus aktivieren.",
"tutorialChatMessagesReopenMessageTitle": "Bilder und Videos erneut öffnen",
"tutorialChatMessagesReopenMessageDesc": "Wenn dein Freund dir ein Bild oder Video mit unendlicher Anzeigezeit gesendet hat, kannst du es bis zum Neustart der App jederzeit erneut öffnen. Um dies zu tun, musst du einfach doppelt auf die Nachricht klicken. Dein Freund erhält dann eine Benachrichtigung, dass du das Bild erneut angesehen hast.",
"memoriesEmpty": "Sobald du Bilder oder Videos speicherst, landen sie hier in deinen Erinnerungen.",
"deleteTitle": "Bist du dir sicher?",
"deleteOkBtnForAll": "Für alle löschen",
"deleteOkBtnForMe": "Für mich löschen",
"deleteImageTitle": "Bist du dir sicher?",
"deleteImageBody": "Das Bild wird unwiderruflich gelöscht.",
"backupNoticeTitle": "Kein Backup konfiguriert",
"backupNoticeDesc": "Wenn du dein Gerät wechselst oder verlierst, kann ohne Backup niemand dein Account wiederherstellen. Sichere deshalb deine Daten.",
"backupNoticeLater": "Später erinnern",
"backupNoticeOpenBackup": "Backup erstellen",
"backupPending": "Ausstehend",
"backupFailed": "Fehlgeschlagen",
"backupSuccess": "Erfolgreich",
"backupTwonlySafeDesc": "Sichere deine twonly-Identität, da dies die einzige Möglichkeit ist, dein Konto wiederherzustellen, wenn du die App deinstallierst oder dein Handy verlierst.",
"backupServer": "Server",
"backupMaxBackupSize": "max. Backup-Größe",
"backupStorageRetention": "Speicheraufbewahrung",
"backupLastBackupDate": "Letztes Backup",
"backupLastBackupSize": "Backup-Größe",
"backupLastBackupResult": "Ergebnis",
"deleteBackupTitle": "Bist du sicher?",
"backupNoPasswordRecovery": "Aufgrund des Sicherheitssystems von twonly gibt es (derzeit) keine Funktion zur Wiederherstellung des Passworts. Daher musst du dir dein Passwort merken oder, besser noch, aufschreiben.",
"deleteBackupBody": "Ohne ein Backup kannst du dein Benutzerkonto nicht wiederherstellen.",
"backupData": "Daten-Backup",
"backupDataDesc": "Das Daten-Backup enthält neben deiner twonly-Identität auch alle deine Mediendateien. Dieses Backup ist ebenfalls verschlüsselt, wird jedoch lokal gespeichert. Du musst es dann manuell auf deinen Laptop oder ein Gerät deiner Wahl kopieren.",
"backupInsecurePassword": "Unsicheres Passwort",
"backupInsecurePasswordDesc": "Das gewählte Passwort ist sehr unsicher und kann daher leicht von Angreifern erraten werden. Bitte wähle ein sicheres Passwort.",
"backupInsecurePasswordOk": "Trotzdem fortfahren",
"backupInsecurePasswordCancel": "Erneut versuchen",
"backupTwonlySafeLongDesc": "twonly hat keine zentralen Benutzerkonten. Während der Installation wird ein Schlüsselpaar erstellt, das aus einem öffentlichen und einem privaten Schlüssel besteht. Der private Schlüssel wird nur auf deinem Gerät gespeichert, um ihn vor unbefugtem Zugriff zu schützen. Der öffentliche Schlüssel wird auf den Server hochgeladen und mit deinem gewählten Benutzernamen verknüpft, damit andere dich finden können.\n\ntwonly Backup erstellt regelmäßig ein verschlüsseltes, anonymes Backup deines privaten Schlüssels zusammen mit deinen Kontakten und Einstellungen. Dein Benutzername und das gewählte Passwort reichen aus, um diese Daten auf einem anderen Gerät wiederherzustellen.",
"backupSelectStrongPassword": "Wähle ein sicheres Passwort. Dies ist erforderlich, wenn du dein twonly Backup wiederherstellen möchtest.",
"password": "Passwort",
"passwordRepeated": "Passwort wiederholen",
"passwordRepeatedNotEqual": "Passwörter stimmen nicht überein.",
"backupPasswordRequirement": "Das Passwort muss mindestens 8 Zeichen lang sein.",
"backupExpertSettings": "Experteneinstellungen",
"backupEnableBackup": "Automatische Sicherung aktivieren",
"backupOwnServerDesc": "Speichere dein twonly Backup auf einem Server deiner Wahl.",
"backupUseOwnServer": "Server verwenden",
"backupResetServer": "Standardserver verwenden",
"backupTwonlySaveNow": "Jetzt speichern",
"backupChangePassword": "Password ändern",
"inviteFriends": "Freunde einladen",
"inviteFriendsShareBtn": "Teilen",
"inviteFriendsShareText": "Wechseln wir zu twonly: {url}",
"appOutdated": "Deine Version von twonly ist veraltet.",
"appOutdatedBtn": "Jetzt aktualisieren.",
"doubleClickToReopen": "Doppelklicken zum\nerneuten Öffnen.",
"uploadLimitReached": "Das Upload-Limit wurde\nerreicht. Upgrade auf Pro\noder warte bis morgen.",
"retransmissionRequested": "Wird erneut versucht.",
"testPaymentMethod": "Vielen Dank für dein Interesse an einem kostenpflichtigen Tarif. Die kostenpflichtigen Pläne sind derzeit noch deaktiviert. Sie werden aber bald aktiviert!",
"openChangeLog": "Changelog automatisch öffnen",
"reportUserTitle": "Melde {username}",
"reportUserReason": "Meldegrund",
"reportUser": "Benutzer melden",
"newDeviceRegistered": "Du hast dich auf einem anderen Gerät angemeldet. Daher wurdest du hier abgemeldet.",
"tabToRemoveEmoji": "Tippen um zu entfernen",
"quotedMessageWasDeleted": "Die zitierte Nachricht wurde gelöscht.",
"messageWasDeleted": "Nachricht wurde gelöscht.",
"messageWasDeletedShort": "Gelöscht",
"sent": "Versendet",
"sentTo": "Zugestellt an",
"received": "Empfangen",
"opened": "Geöffnet",
"waitingForInternet": "Warten auf Internet",
"editHistory": "Bearbeitungshistorie",
"archivedChats": "Archivierte Chats",
"durationShortSecond": "Sek.",
"durationShortMinute": "Min.",
"durationShortHour": "Std.",
"durationShortDays": "{count, plural, =1{1 Tag} other{{count} Tage}}",
"contacts": "Kontakte",
"groups": "Gruppen",
"newGroup": "Neue Gruppe",
"selectMembers": "Mitglieder auswählen",
"selectGroupName": "Gruppennamen wählen",
"groupNameInput": "Gruppennamen",
"groupMembers": "Mitglieder",
"createGroup": "Gruppe erstellen",
"addMember": "Mitglied hinzufügen",
"leaveGroup": "Gruppe verlassen",
"createContactRequest": "Kontaktanfrage erstellen",
"contactRequestSend": "Kontakanfrage gesendet",
"makeAdmin": "Zum Admin machen",
"removeAdmin": "Als Admin entfernen",
"removeFromGroup": "Aus Gruppe entfernen",
"admin": "Admin",
"revokeAdminRightsTitle": "Adminrechte von {username} entfernen?",
"revokeAdminRightsOkBtn": "Als Admin entfernen",
"makeAdminRightsTitle": "{username} zum Admin machen?",
"makeAdminRightsBody": "{username} wird diese Gruppe und ihre Mitglieder bearbeiten können.",
"makeAdminRightsOkBtn": "Zum Admin machen",
"updateGroup": "Gruppe aktualisieren",
"alreadyInGroup": "Bereits Mitglied",
"removeContactFromGroupTitle": "{username} aus dieser Gruppe entfernen?",
"youChangedGroupName": "Du hast den Gruppennamen zu „{newGroupName}“ geändert.",
"makerChangedGroupName": "{maker} hat den Gruppennamen zu „{newGroupName}“ geändert.",
"youCreatedGroup": "Du hast die Gruppe erstellt.",
"makerCreatedGroup": "{maker} hat die Gruppe erstellt.",
"youRemovedMember": "Du hast {affected} aus der Gruppe entfernt.",
"makerRemovedMember": "{maker} hat {affected} aus der Gruppe entfernt.",
"youAddedMember": "Du hast {affected} zur Gruppe hinzugefügt.",
"makerAddedMember": "{maker} hat {affected} zur Gruppe hinzugefügt.",
"youMadeAdmin": "Du hast {affected} zum Administrator gemacht.",
"makerMadeAdmin": "{maker} hat {affected} zum Administrator gemacht.",
"youRevokedAdminRights": "Du hast {affectedR} die Administratorrechte entzogen.",
"makerRevokedAdminRights": "{maker} hat {affectedR} die Administratorrechte entzogen.",
"youLeftGroup": "Du hast die Gruppe verlassen.",
"makerLeftGroup": "{maker} hat die Gruppe verlassen.",
"groupActionYou": "dich",
"groupActionYour": "deine",
"settingsBackup": "Backup",
"twonlySafeRecoverTitle": "Recovery",
"twonlySafeRecoverDesc": "Wenn du ein Backup mit twonly Backup erstellt hast, kannst du es hier wiederherstellen.",
"twonlySafeRecoverBtn": "Backup wiederherstellen",
"notificationFillerIn": "in",
"notificationText": "hat eine Nachricht{inGroup} gesendet.",
"notificationTwonly": "hat ein twonly{inGroup} gesendet.",
"notificationVideo": "hat ein Video{inGroup} gesendet.",
"notificationImage": "hat ein Bild{inGroup} gesendet.",
"notificationAudio": "hat eine Sprachnachricht{inGroup} gesendet.",
"notificationAddedToGroup": "hat dich zu \"{groupname}\" hinzugefügt.",
"notificationContactRequest": "möchte sich mit dir vernetzen.",
"notificationAcceptRequest": "ist jetzt mit dir vernetzt.",
"notificationStoredMediaFile": "hat dein Bild gespeichert.",
"notificationReaction": "hat auf dein Bild reagiert.",
"notificationReopenedMedia": "hat dein Bild erneut geöffnet.",
"notificationReactionToVideo": "hat mit {reaction} auf dein Video reagiert.",
"notificationReactionToText": "hat mit {reaction} auf deine Nachricht reagiert.",
"notificationReactionToImage": "hat mit {reaction} auf dein Bild reagiert.",
"notificationReactionToAudio": "hat mit {reaction} auf deine Sprachnachricht reagiert.",
"notificationResponse": "hat dir{inGroup} geantwortet.",
"notificationTitleUnknownUser": "Jemand",
"notificationCategoryMessageTitle": "Nachrichten",
"notificationCategoryMessageDesc": "Nachrichten von anderen Benutzern.",
"groupContextMenuDeleteGroup": "Dadurch werden alle Nachrichten in diesem Chat dauerhaft gelöscht.",
"groupYouAreNowLongerAMember": "Du bist nicht mehr Mitglied dieser Gruppe.",
"groupNetworkIssue": "Netzwerkproblem. Bitte probiere es später noch einmal.",
"leaveGroupSelectOtherAdminTitle": "Einen Admin auswählen",
"leaveGroupSelectOtherAdminBody": "Um die Gruppe zu verlassen, musst du zuerst einen neuen Administrator auswählen.",
"leaveGroupSureTitle": "Gruppe verlassen",
"leaveGroupSureBody": "Willst du die Gruppe wirklich verlassen?",
"leaveGroupSureOkBtn": "Gruppe verlassen",
"changeDisplayMaxTime": "Chats werden ab jetzt nach {time} gelöscht ({username}).",
"youChangedDisplayMaxTime": "Chats werden ab jetzt nach {time} gelöscht.",
"userGotReported": "Benutzer wurde gemeldet.",
"deleteChatAfter": "Chat löschen nach...",
"deleteChatAfterAnHour": "einer Stunde.",
"deleteChatAfterADay": "einem Tag.",
"deleteChatAfterAWeek": "einer Woche.",
"deleteChatAfterAMonth": "einem Monat.",
"deleteChatAfterAYear": "einem Jahr.",
"yourTwonlyScore": "Dein twonly-Score",
"registrationClosed": "Aufgrund des aktuell sehr hohen Aufkommens haben wir die Registrierung vorübergehend deaktiviert, damit der Dienst zuverlässig bleibt. Bitte versuche es in ein paar Tagen noch einmal.",
"dialogAskDeleteMediaFilePopTitle": "Bist du sicher, dass du dein Meisterwerk löschen möchtest?",
"dialogAskDeleteMediaFilePopDelete": "Löschen",
"allowErrorTracking": "Fehler und Crashes mit uns teilen",
"allowErrorTrackingSubtitle": "Wenn twonly abstürzt oder Fehler auftreten, werden diese automatisch an unsere selbst gehostete Glitchtip-Instanz gemeldet. Persönliche Daten wie Nachrichten oder Bilder werden niemals hochgeladen.",
"avatarSaveChanges": "Möchtest du die Änderungen speichern?",
"avatarSaveChangesStore": "Speichern",
"avatarSaveChangesDiscard": "Verwerfen",
"inProcess": "Wird verarbeitet",
"draftMessage": "Entwurf",
"exportMemories": "Memories exportieren (Beta)",
"importMemories": "Memories importieren (Beta)",
"voiceMessageSlideToCancel": "Zum Abbrechen ziehen",
"voiceMessageCancel": "Abbrechen",
"shareYourProfile": "Teile dein Profil",
"scanOtherProfile": "Scanne ein anderes Profil",
"skipForNow": "Vorerst überspringen",
"linkFromUsername": "Ist der Link von {username}?",
"linkFromUsernameLong": "Wenn du den Link von der Person direkt erhalten hast, kannst du den Kontakt als verifiziert markieren, da der öffentliche Schlüssel im Link mit dem bereits für diesen Benutzer gespeicherten öffentlichen Schlüssel übereinstimmt.",
"gotLinkFromFriend": "Ja, der Link kommt direkt von der Person.",
"couldNotVerifyUsername": "{username} konnte nicht verifiziert werden",
"linkPubkeyDoesNotMatch": "Der öffentliche Schlüssel im Link stimmt nicht mit dem für diesen Kontakt gespeicherten öffentlichen Schlüssel überein. Triff die Person persönlich und scanne den QR-Code direkt!"
}

View file

@ -0,0 +1,485 @@
{
"@@locale": "en",
"registerTitle": "Welcome to twonly!",
"registerSlogan": "twonly, a privacy friendly way to connect with friends through secure, spontaneous image sharing",
"onboardingWelcomeTitle": "Welcome to twonly!",
"onboardingWelcomeBody": "Experience a private and secure way to stay in touch with friends by sharing instant pictures.",
"onboardingE2eTitle": "Carefree sharing",
"onboardingE2eBody": "With end-to-end encryption, enjoy the peace of mind that only you and your friends can see the moments you share.",
"onboardingFocusTitle": "Focus on sharing moments",
"onboardingFocusBody": "Say goodbye to addictive features! twonly was created for sharing moments, free from useless distractions or ads.",
"onboardingSendTwonliesTitle": "Send twonlies",
"onboardingSendTwonliesBody": "Share moments securely with your partner. twonly ensures that only your partner can open it, keeping your moments with your partner a two(o)nly thing!",
"onboardingNotProductTitle": "You are not the product!",
"onboardingNotProductBody": "twonly is financed by donations and an optional subscription. Your data will never be sold.",
"onboardingBuyOneGetTwoTitle": "Buy one get two",
"onboardingBuyOneGetTwoBody": "twonly always requires at least two people, which is why you receive a second free license for your twonly partner with your purchase.",
"onboardingGetStartedTitle": "Let's go!",
"onboardingGetStartedBody": "You can test twonly free of charge in preview mode. In this mode you can be found by others and receive pictures or videos but you cannot send any yourself.",
"onboardingTryForFree": "Try for free",
"registerUsernameSlogan": "Please select a username so others can find you!",
"registerUsernameDecoration": "Username",
"registerUsernameLimits": "Your username must be at least 3 characters long.",
"registerSubmitButton": "Register now!",
"registerTwonlyCodeText": "Have you received a twonly code? Then redeem it either directly here or later!",
"registerTwonlyCodeLabel": "twonly-Code",
"newMessageTitle": "New message",
"chatsTapToSend": "Click to send your first image",
"cameraPreviewSendTo": "Send to",
"shareImageTitle": "Share with",
"shareImageBestFriends": "Best friends",
"shareImagePinnedContacts": "Pinnded",
"shareImagedEditorSendImage": "Send",
"shareImagedEditorShareWith": "Share with",
"shareImagedEditorSaveImage": "Save",
"shareImagedEditorSavedImage": "Saved",
"shareImageSearchAllContacts": "Search all contacts",
"startNewChatSearchHint": "Name, username or groupname",
"shareImagedSelectAll": "Select all",
"startNewChatTitle": "Select Contact",
"startNewChatNewContact": "New Contact",
"startNewChatYourContacts": "Your Contacts",
"shareImageAllUsers": "All contacts",
"shareImageAllTwonlyWarning": "twonlies can only be send to verified contacts!",
"shareImageUserNotVerified": "User is not verified",
"shareImageUserNotVerifiedDesc": "twonlies can only be sent to verified users. To verify a user, go to their profile and to verify security number.",
"shareImageShowArchived": "Show archived users",
"searchUsernameInput": "Username",
"searchUsernameTitle": "Search username",
"searchUserNamePreview": "To protect you and other twonly users from spam and abuse, it is not possible to search for other people in preview mode. Other users can find you and their requests will be displayed here!",
"selectSubscription": "Select subscription",
"searchUserNamePending": "Pending",
"searchUserNameBlockUserTooltip": "Block the user without informing.",
"searchUserNameRejectUserTooltip": "Reject the request and let the requester know.",
"searchUserNameArchiveUserTooltip": "Archive the user. He will appear again as soon as he accepts your request.",
"searchUsernameNotFound": "Username not found",
"searchUsernameNotFoundBody": "There is no user with the username \"{username}\" registered",
"@searchUsernameNotFoundBody": {
"placeholders": {
"username": {}
}
},
"searchUsernameNewFollowerTitle": "Follow requests",
"searchUsernameQrCodeBtn": "Scan QR code",
"chatListViewSearchUserNameBtn": "Add your first twonly contact!",
"chatListViewSendFirstTwonly": "Send your first twonly!",
"chatListDetailInput": "Type a message",
"userDeletedAccount": "The user has deleted its account.",
"contextMenuUserProfile": "User profile",
"contextMenuVerifyUser": "Verify",
"contextMenuArchiveUser": "Archive",
"contextMenuUndoArchiveUser": "Undo archiving",
"contextMenuOpenChat": "Open chat",
"contextMenuPin": "Pin",
"contextMenuUnpin": "Unpin",
"mediaViewerAuthReason": "Please authenticate to see this twonly!",
"mediaViewerTwonlyTapToOpen": "Tap to open your twonly!",
"messageSendState_Received": "Received",
"messageSendState_Opened": "Opened",
"messageSendState_Send": "Sent",
"messageSendState_Sending": "Sending",
"messageSendState_TapToLoad": "Tap to load",
"messageSendState_Loading": "Downloading",
"messageStoredInGallery": "Stored in gallery",
"messageReopened": "Re-opened",
"imageEditorDrawOk": "Take drawing",
"settingsTitle": "Settings",
"settingsChats": "Chats",
"settingsPreSelectedReactions": "Preselected reaction emojis",
"settingsPreSelectedReactionsError": "A maximum of 12 reactions can be selected.",
"settingsProfile": "Profile",
"settingsStorageData": "Data and storage",
"settingsStorageDataStoreInGTitle": "Store in Gallery",
"settingsStorageDataStoreInGSubtitle": "Store saved images additional in the systems gallery.",
"settingsStorageDataMediaAutoDownload": "Media auto-download",
"settingsStorageDataAutoDownMobile": "When using mobile data",
"settingsStorageDataAutoDownWifi": "When using WI-FI",
"settingsProfileCustomizeAvatar": "Customize your avatar",
"settingsProfileEditDisplayName": "Displayname",
"settingsProfileEditDisplayNameNew": "New Displayname",
"settingsAccount": "Konto",
"settingsSubscription": "Subscription",
"settingsAppearance": "Appearance",
"settingsPrivacy": "Privacy",
"settingsPrivacyBlockUsers": "Block users",
"settingsPrivacyBlockUsersDesc": "Blocked users will not be able to communicate with you. You can unblock a blocked user at any time.",
"settingsPrivacyBlockUsersCount": "{len} contact(s)",
"@settingsPrivacyBlockUsersCount": {
"placeholders": {
"len": {}
}
},
"settingsNotification": "Notification",
"settingsNotifyTroubleshooting": "Troubleshooting",
"settingsNotifyTroubleshootingDesc": "Click here if you have problems receiving push notifications.",
"settingsNotifyTroubleshootingNoProblem": "No problem detected",
"settingsNotifyTroubleshootingNoProblemDesc": "Press OK to receive a test notification. When you receive no message even after waiting for 10 minutes, please send us your debug log in Settings > Help > Debug log, so we can look at that issue.",
"settingsHelp": "Help",
"settingsHelpDiagnostics": "Diagnostic protocol",
"settingsHelpFAQ": "FAQ",
"feedbackTooltip": "Give Feedback to improve twonly.",
"settingsHelpContactUs": "Contact us",
"settingsHelpVersion": "Version",
"settingsHelpLicenses": "Licenses (Source-Code)",
"settingsHelpCredits": "Licenses (Images)",
"settingsHelpImprint": "Imprint & Privacy Policy",
"contactUsFaq": "Have you read our FAQ yet?",
"contactUsEmojis": "How do you feel? (optional)",
"contactUsSelectOption": "Please select an option",
"contactUsReason": "Tell us why you're reaching out",
"contactUsMessage": "If you want to receive an answer, please add your e-mail address so we can contact you.",
"contactUsYourMessage": "Your message",
"contactUsMessageTitle": "Tell us what's going on",
"contactUsReasonNotWorking": "Something's not working",
"contactUsReasonFeatureRequest": "Feature request",
"contactUsReasonQuestion": "Question",
"contactUsReasonFeedback": "Feedback",
"contactUsReasonOther": "Other",
"contactUsIncludeLog": "Include debug log",
"contactUsWhatsThat": "What's that?",
"contactUsLastWarning": "This are the information's which will be send to us. Please verify them and then press submit.",
"contactUsSuccess": "Feedback submitted successfully!",
"contactUsShortcut": "Hide Feedback Icon",
"settingsHelpTerms": "Terms of Service",
"settingsAppearanceTheme": "Theme",
"settingsAccountDeleteAccount": "Delete account",
"settingsAccountDeleteAccountWithBallance": "In the next step, you can select what you want to to with the remaining credit ({credit}).",
"settingsAccountDeleteAccountNoBallance": "Once you delete your account, there is no going back.",
"settingsAccountDeleteAccountNoInternet": "An Internet connection is required to delete your account.",
"settingsAccountDeleteModalTitle": "Are you sure?",
"settingsAccountDeleteModalBody": "Your account will be deleted. There is no change to restore it.",
"contactVerifyNumberTitle": "Verify safety number",
"contactVerifyNumberTapToScan": "Tap to scan",
"contactVerifyNumberMarkAsVerified": "Mark as verified",
"contactVerifyNumberClearVerification": "Clear verification",
"contactVerifyNumberLongDesc": "To verify the end-to-end encryption with {username}, compare the numbers with their device. The person can also scan your code with their device.",
"@contactVerifyNumberLongDesc": {
"placeholders": {
"username": {}
}
},
"contactNickname": "Nickname",
"contactNicknameNew": "New nickname",
"deleteAllContactMessages": "Delete all text-messages",
"deleteAllContactMessagesBody": "This will remove all messages, except stored media files, in your chat with {username}. This will NOT delete the messages stored at {username}s device!",
"@deleteAllContactMessagesBody": {
"placeholders": {
"username": {}
}
},
"contactBlock": "Block",
"contactBlockTitle": "Block {username}",
"@contactBlockTitle": {
"placeholders": {
"username": {}
}
},
"contactBlockBody": "A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users.",
"contactRemove": "Remove user",
"contactRemoveTitle": "Remove {username}",
"contactRemoveBody": "Remove the user and permanently delete the chat and all associated media files. This will also delete YOUR ACCOUNT FROM YOUR CONTACT'S PHONE.",
"undo": "Undo",
"redo": "Redo",
"next": "Next",
"submit": "Submit",
"close": "Close",
"disable": "Disable",
"enable": "Enable",
"cancel": "Cancel",
"now": "Now",
"you": "You",
"minutesShort": "min.",
"image": "Image",
"video": "Video",
"react": "React",
"reply": "Reply",
"copy": "Copy",
"edit": "Edit",
"delete": "Delete",
"info": "Info",
"ok": "Ok",
"switchFrontAndBackCamera": "Switch between front and back camera.",
"addTextItem": "Text",
"protectAsARealTwonly": "Send as real twonly!",
"addDrawing": "Drawing",
"addEmoji": "Emoji",
"toggleFlashLight": "Toggle the flash light",
"toggleHighQuality": "Toggle better resolution",
"userFound": "{username} found",
"userFoundBody": "Do you want to create a follow request?",
"searchUsernameNotFoundLong": "\"{username}\" is not a twonly user. Please check the username and try again.",
"@searchUsernameNotFoundLong": {
"placeholders": {
"username": {}
}
},
"errorUnknown": "An unexpected error has occurred. Please try again later.",
"errorBadRequest": "The request could not be understood by the server due to malformed syntax. Please check your input and try again.",
"errorTooManyRequests": "You have made too many requests in a short period. Please wait a moment before trying again.",
"errorInternalError": "The server is currently not available. Please try again later.",
"errorInvalidInvitationCode": "The invitation code you provided is invalid. Please check the code and try again.",
"errorUsernameAlreadyTaken": "The username is already taken.",
"errorSignatureNotValid": "The provided signature is not valid. Please check your credentials and try again.",
"errorUsernameNotFound": "The username you entered does not exist. Please check the spelling or create a new account.",
"errorUsernameNotValid": "The username you provided does not meet the required criteria. Please choose a valid username.",
"errorInvalidPublicKey": "The public key you provided is invalid. Please check the key and try again.",
"errorSessionAlreadyAuthenticated": "You are already logged in. Please log out if you want to log in with a different account.",
"errorSessionNotAuthenticated": "Your session is not authenticated. Please log in to continue.",
"errorOnlyOneSessionAllowed": "Only one active session is allowed per user. Please log out from other devices to continue.",
"errorNotEnoughCredit": "You do not have enough twonly-credit.",
"errorVoucherInvalid": "The voucher code you entered is not valid.",
"errorPlanLimitReached": "You have reached your plans limit. Please upgrade your plan.",
"errorPlanNotAllowed": "This feature is not available in your current plan.",
"errorPlanUpgradeNotYearly": "The plan upgrade must be paid for annually, as the current plan is also billed annually.",
"upgradeToPaidPlan": "Upgrade to a paid plan.",
"upgradeToPaidPlanButton": "Upgrade subscription to {planId}",
"partOfPaidPlanOf": "You are part of the paid plan of {username}!",
"year": "year",
"month": "month",
"proFeature1": "✓ Unlimited media file uploads",
"proFeature2": "1 additional Plus user",
"proFeature3": "Cloud-Backup encrypted (coming-soon)",
"proFeature4": "Additional features (coming-soon)",
"familyFeature1": "✓ All from Pro",
"familyFeature2": "4 additional Plus users",
"redeemUserInviteCode": "Or redeem a twonly-Code.",
"redeemUserInviteCodeTitle": "Redeem twonly-Code",
"redeemUserInviteCodeSuccess": "Your plan has been successfully adjusted.",
"freeFeature1": "10 Media file uploads per day",
"plusFeature1": "✓ Unlimited media file uploads",
"plusFeature2": "Additional features (coming-soon)",
"transactionHistory": "Your transaction history",
"manageSubscription": "Manage your subscription",
"nextPayment": "Next payment",
"currentBalance": "Current balance",
"manageAdditionalUsers": "Manage additional users",
"open": "Open",
"createOrRedeemVoucher": "Buy or redeem voucher",
"createVoucher": "Buy voucher",
"createVoucherDesc": "Choose the value of the voucher. The value of the voucher will be deducted from your twonly balance.",
"redeemVoucher": "Redeem voucher",
"openVouchers": "Open vouchers",
"voucherCreated": "Voucher created",
"voucherRedeemed": "Voucher redeemed",
"enterVoucherCode": "Enter Voucher Code",
"requestedVouchers": "Requested vouchers",
"redeemedVouchers": "Redeemed vouchers",
"buy": "Buy",
"subscriptionRefund": "When you upgrade, you will receive a refund of {refund} for your current subscription.",
"transactionCash": "Cash transaction",
"transactionPlanUpgrade": "Plan upgrade",
"transactionRefund": "Refund transaction",
"transactionThanksForTesting": "Thank you for testing",
"transactionUnknown": "Unknown transaction",
"transactionVoucherCreated": "Voucher created",
"transactionVoucherRedeemed": "Voucher redeemed",
"transactionAutoRenewal": "Automatic renewal",
"checkoutOptions": "Options",
"refund": "Refund",
"checkoutPayYearly": "Pay yearly",
"checkoutTotal": "Total",
"selectPaymentMethod": "Select Payment Method",
"twonlyCredit": "twonly-Credit",
"notEnoughCredit": "You do not have enough credit!",
"chargeCredit": "Charge credit",
"autoRenewal": "Auto renewal",
"autoRenewalDesc": "You can change this at any time.",
"autoRenewalLongDesc": "When your subscription expires, you will automatically be downgraded to the Preview plan. If you activate the automatic renewal, please make sure that you have enough credit for the automatic renewal. We will notify you in good time before the automatic renewal.",
"planSuccessUpgraded": "Successfully upgraded your plan.",
"checkoutSubmit": "Order with a fee.",
"additionalUsersList": "Your additional users",
"additionalUsersPlusTokens": "twonly-Codes für \"Plus\" user",
"additionalUsersFreeTokens": "twonly-Codes für \"Free\" user",
"planLimitReached": "You have reached your plan limit for today. Upgrade your plan now to send the media file.",
"planNotAllowed": "You cannot send media files with your current tariff. Upgrade your plan now to send the media file.",
"galleryDelete": "Delete file",
"galleryDetails": "Show details",
"galleryExport": "Export to gallery",
"galleryExportSuccess": "Successfully saved in the Gallery.",
"settingsResetTutorials": "Show tutorials again",
"settingsResetTutorialsDesc": "Click here to show already displayed tutorials again.",
"settingsResetTutorialsSuccess": "Tutorials will be displayed again.",
"tutorialChatListSearchUsersTitle": "Find Friends and Manage Friend Requests",
"tutorialChatListSearchUsersDesc": "If you know your friends' usernames, you can search for them here and send a friend request. You will also see all requests from other users that you can accept or block.",
"tutorialChatListContextMenuTitle": "Long press on the contact to open the context menu.",
"tutorialChatListContextMenuDesc": "With the context menu, you can pin, archive, and perform various actions on your contacts. Simply long press the contact and then move your finger to the desired option or tap directly on it.",
"tutorialChatMessagesVerifyShieldTitle": "Verify your contacts!",
"tutorialChatMessagesVerifyShieldDesc": "twonly uses the Signal protocol for secure end-to-end encryption. When you first contact someone, their public identity key is downloaded. To ensure that this key has not been tampered with by third parties, you should compare it with your friend when you meet in person. Once you have verified the user, you can also enable the twonly mode when sending images and videos.",
"tutorialChatMessagesReopenMessageTitle": "Reopen Images and Videos",
"tutorialChatMessagesReopenMessageDesc": "If your friend has sent you a picture or video with infinite display time, you can open it again at any time until you restart the app. To do this, simply double-click on the message. Your friend will then receive a notification that you have viewed the picture again.",
"memoriesEmpty": "As soon as you save pictures or videos, they end up here in your memories.",
"deleteTitle": "Are you sure?",
"deleteOkBtnForAll": "Delete for all",
"deleteOkBtnForMe": "Delete for me",
"deleteImageTitle": "Are you sure?",
"deleteImageBody": "The image will be irrevocably deleted.",
"settingsBackup": "Backup",
"backupNoticeTitle": "No backup configured",
"backupNoticeDesc": "If you change or lose your device, no one can restore your account without a backup. Therefore, back up your data.",
"backupNoticeLater": "Remind later",
"backupNoticeOpenBackup": "Create backup",
"backupPending": "Pending",
"backupFailed": "Failed",
"backupSuccess": "Success",
"backupTwonlySafeDesc": "Back up your twonly identity, as this is the only way to restore your account if you uninstall the app or lose your phone.",
"backupNoPasswordRecovery": "Due to twonly's security system, there is (currently) no password recovery function. Therefore, you must remember your password or, better yet, write it down.",
"backupServer": "Server",
"backupMaxBackupSize": "max. backup size",
"backupStorageRetention": "Storage retention",
"backupLastBackupDate": "Last backup",
"backupLastBackupSize": "Backup size",
"backupLastBackupResult": "Result",
"deleteBackupTitle": "Are you sure?",
"deleteBackupBody": "Without an backup, you can not restore your user account.",
"backupData": "Data-Backup",
"backupDataDesc": "This backup contains besides of your twonly-Identity also all of your media files. This backup will is also encrypted but stored locally. You then have to ensure to manually copy it onto your laptop or device of your choice.",
"backupInsecurePassword": "Insecure password",
"backupInsecurePasswordDesc": "The chosen password is very insecure and can therefore easily be guessed by attackers. Please choose a secure password.",
"backupInsecurePasswordOk": "Continue anyway",
"backupInsecurePasswordCancel": "Try again",
"backupTwonlySafeLongDesc": "twonly does not have any central user accounts. A key pair is created during installation, which consists of a public and a private key. The private key is only stored on your device to protect it from unauthorized access. The public key is uploaded to the server and linked to your chosen username so that others can find you.\n\ntwonly Backup regularly creates an encrypted, anonymous backup of your private key together with your contacts and settings. Your username and chosen password are enough to restore this data on another device.",
"backupSelectStrongPassword": "Choose a secure password. This is required if you want to restore your twonly Backup.",
"password": "Password",
"passwordRepeated": "Repeat password",
"passwordRepeatedNotEqual": "Passwords do not match.",
"backupPasswordRequirement": "Password must be at least 8 characters long.",
"backupExpertSettings": "Expert settings",
"backupEnableBackup": "Activate automatic backup",
"backupOwnServerDesc": "Save your twonly Backup at twonly or on any server of your choice.",
"backupUseOwnServer": "Use server",
"backupResetServer": "Use standard server",
"backupTwonlySaveNow": "Save now",
"backupChangePassword": "Change password",
"twonlySafeRecoverTitle": "Recovery",
"twonlySafeRecoverDesc": "If you have created a backup with twonly Backup, you can restore it here.",
"twonlySafeRecoverBtn": "Restore backup",
"inviteFriends": "Invite your friends",
"inviteFriendsShareBtn": "Share",
"inviteFriendsShareText": "Let's switch to twonly: {url}",
"appOutdated": "Your version of twonly is out of date.",
"appOutdatedBtn": "Update Now",
"doubleClickToReopen": "Double-click\nto open again",
"uploadLimitReached": "The upload limit has\been reached. Upgrade to Pro\nor wait until tomorrow.",
"retransmissionRequested": "Retransmission requested",
"testPaymentMethod": "Thanks for the interest in a paid plan. Currently the paid plans are still deactivated. But they will be activated soon!",
"openChangeLog": "Open changelog automatically",
"reportUserTitle": "Report {username}",
"reportUserReason": "Reporting reason",
"reportUser": "Report user",
"newDeviceRegistered": "You have logged in on another device. You have therefore been logged out here.",
"tabToRemoveEmoji": "Tab to remove",
"quotedMessageWasDeleted": "The quoted message has been deleted.",
"messageWasDeleted": "Message has been deleted.",
"messageWasDeletedShort": "Deleted",
"sent": "Delivered",
"sentTo": "Delivered to",
"received": "Received",
"opened": "Opened",
"waitingForInternet": "Waiting for internet",
"editHistory": "Edit history",
"archivedChats": "Archived chats",
"durationShortSecond": "Sec.",
"durationShortMinute": "Min.",
"durationShortHour": "Hrs.",
"durationShortDays": "{count, plural, =1{1 Day} other{{count} Days}}",
"contacts": "Contacts",
"groups": "Groups",
"newGroup": "New group",
"selectMembers": "Select members",
"selectGroupName": "Select group name",
"groupNameInput": "Group name",
"groupMembers": "Members",
"addMember": "Add member",
"createGroup": "Create group",
"leaveGroup": "Leave group",
"createContactRequest": "Create contact request",
"contactRequestSend": "Contact request send",
"makeAdmin": "Make admin",
"removeAdmin": "Remove as admin",
"removeFromGroup": "Remove from group",
"admin": "Admin",
"revokeAdminRightsTitle": "Revoke {username}'s admin rights?",
"revokeAdminRightsOkBtn": "Remove as admin",
"makeAdminRightsTitle": "Make {username} an admin?",
"makeAdminRightsBody": "{username} will be able to edit this group and its members.",
"makeAdminRightsOkBtn": "Make admin",
"updateGroup": "Update group",
"alreadyInGroup": "Already in Group",
"removeContactFromGroupTitle": "Remove {username} from this group?",
"youChangedGroupName": "You have changed the group name to \"{newGroupName}\".",
"makerChangedGroupName": "{maker} has changed the group name to \"{newGroupName}\".",
"youCreatedGroup": "You have created the group.",
"makerCreatedGroup": "{maker} has created the group.",
"youRemovedMember": "You have removed {affected} from the group.",
"makerRemovedMember": "{maker} has removed {affected} from the group.",
"youAddedMember": "You have added {affected} to the group.",
"makerAddedMember": "{maker} has added {affected} to the group.",
"youMadeAdmin": "You made {affected} an admin.",
"makerMadeAdmin": "{maker} made {affected} an admin.",
"youRevokedAdminRights": "You revoked {affectedR} admin rights.",
"makerRevokedAdminRights": "{maker} revoked {affectedR} admin rights.",
"youLeftGroup": "You have left the group.",
"makerLeftGroup": "{maker} has left the group.",
"groupActionYou": "you",
"groupActionYour": "your",
"notificationFillerIn": "in",
"notificationText": "sent a message{inGroup}.",
"notificationTwonly": "sent a twonly{inGroup}.",
"notificationVideo": "sent a video{inGroup}.",
"notificationImage": "sent an image{inGroup}.",
"notificationAudio": "sent a voice message{inGroup}.",
"notificationAddedToGroup": "has added you to \"{groupname}\"",
"notificationContactRequest": "wants to connect with you.",
"notificationAcceptRequest": "is now connected with you.",
"notificationStoredMediaFile": "has stored your image.",
"notificationReaction": "has reacted to your image.",
"notificationReopenedMedia": "has reopened your image.",
"notificationReactionToVideo": "has reacted with {reaction} to your video.",
"notificationReactionToText": "has reacted with {reaction} to your message.",
"notificationReactionToImage": "has reacted with {reaction} to your image.",
"notificationReactionToAudio": "has reacted with {reaction} to your audio message.",
"notificationResponse": "has responded{inGroup}.",
"notificationTitleUnknownUser": "Someone",
"notificationCategoryMessageTitle": "Messages",
"notificationCategoryMessageDesc": "Messages from other users.",
"groupContextMenuDeleteGroup": "This will permanently delete all messages in this chat.",
"groupYouAreNowLongerAMember": "You are no longer part of this group.",
"groupNetworkIssue": "Network issue. Try again later.",
"leaveGroupSelectOtherAdminTitle": "Select another admin",
"leaveGroupSelectOtherAdminBody": "To leave the group, you must first select a new administrator.",
"leaveGroupSureTitle": "Leave group",
"leaveGroupSureBody": "Do you really want to leave the group?",
"leaveGroupSureOkBtn": "Leave group",
"changeDisplayMaxTime": "Chats will now be deleted after {time} ({username}).",
"youChangedDisplayMaxTime": "Chats will now be deleted after {time}.",
"userGotReported": "User has been reported.",
"deleteChatAfter": "Delete chat after...",
"deleteChatAfterAnHour": "one hour.",
"deleteChatAfterADay": "one day.",
"deleteChatAfterAWeek": "one week.",
"deleteChatAfterAMonth": "one month.",
"deleteChatAfterAYear": "one year.",
"yourTwonlyScore": "Your twonly-Score",
"registrationClosed": "Due to the current high volume of registrations, we have temporarily disabled registration to ensure that the service remains reliable. Please try again in a few days.",
"dialogAskDeleteMediaFilePopTitle": "Are you sure you want to delete your masterpiece?",
"dialogAskDeleteMediaFilePopDelete": "Delete",
"allowErrorTracking": "Share errors and crashes with us",
"allowErrorTrackingSubtitle": "If twonly crashes or errors occur, these are automatically reported to our self-hosted Glitchtip instance. Personal data such as messages or images are never uploaded.",
"avatarSaveChanges": "Would you like to save the changes?",
"avatarSaveChangesStore": "Save",
"avatarSaveChangesDiscard": "Discard",
"inProcess": "In process",
"draftMessage": "Draft",
"exportMemories": "Export memories (Beta)",
"importMemories": "Import memories (Beta)",
"voiceMessageSlideToCancel": "Slide to cancel",
"voiceMessageCancel": "Cancel",
"shareYourProfile": "Share your profile",
"scanOtherProfile": "Scan other profile",
"skipForNow": "Skip for now",
"linkFromUsername": "Is the link from {username}?",
"linkFromUsernameLong": "If you received the link from your friend, you can mark the user as verified, as the public key in the link matches the public key already stored for that user?",
"gotLinkFromFriend": "Yes, I got the link from my friend!",
"couldNotVerifyUsername": "Could not verify {username}",
"linkPubkeyDoesNotMatch": "The public key in the link does not match the public key stored for this contact. Try to meet your friend in person and scan the QR code directly!"
}

View file

@ -7,7 +7,6 @@ import 'package:intl/intl.dart' as intl;
import 'app_localizations_de.dart';
import 'app_localizations_en.dart';
import 'app_localizations_sv.dart';
// ignore_for_file: type=lint
@ -96,8 +95,7 @@ abstract class AppLocalizations {
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[
Locale('de'),
Locale('en'),
Locale('sv')
Locale('en')
];
/// No description provided for @registerTitle.
@ -220,12 +218,6 @@ abstract class AppLocalizations {
/// **'Your username must be at least 3 characters long.'**
String get registerUsernameLimits;
/// No description provided for @registerProofOfWorkFailed.
///
/// In en, this message translates to:
/// **'There was an issue with the captcha test. Please try again.'**
String get registerProofOfWorkFailed;
/// No description provided for @registerSubmitButton.
///
/// In en, this message translates to:
@ -1321,8 +1313,8 @@ abstract class AppLocalizations {
/// No description provided for @upgradeToPaidPlanButton.
///
/// In en, this message translates to:
/// **'Upgrade to {planId}{sufix}'**
String upgradeToPaidPlanButton(Object planId, Object sufix);
/// **'Upgrade subscription to {planId}'**
String upgradeToPaidPlanButton(Object planId);
/// No description provided for @partOfPaidPlanOf.
///
@ -1336,24 +1328,12 @@ abstract class AppLocalizations {
/// **'year'**
String get year;
/// No description provided for @yearly.
///
/// In en, this message translates to:
/// **'Yearly'**
String get yearly;
/// No description provided for @month.
///
/// In en, this message translates to:
/// **'month'**
String get month;
/// No description provided for @monthly.
///
/// In en, this message translates to:
/// **'Monthly'**
String get monthly;
/// No description provided for @proFeature1.
///
/// In en, this message translates to:
@ -1363,49 +1343,55 @@ abstract class AppLocalizations {
/// No description provided for @proFeature2.
///
/// In en, this message translates to:
/// **'1 additional Plus user'**
/// **'1 additional Plus user'**
String get proFeature2;
/// No description provided for @proFeature3.
///
/// In en, this message translates to:
/// **'✓ Restore flames'**
/// **'Cloud-Backup encrypted (coming-soon)'**
String get proFeature3;
/// No description provided for @proFeature4.
///
/// In en, this message translates to:
/// **'✓ Support twonly'**
/// **'Additional features (coming-soon)'**
String get proFeature4;
/// No description provided for @familyFeature1.
///
/// In en, this message translates to:
/// **'Unlimited media file uploads'**
/// **'All from Pro'**
String get familyFeature1;
/// No description provided for @familyFeature2.
///
/// In en, this message translates to:
/// **'4 additional Plus user'**
/// **'4 additional Plus users'**
String get familyFeature2;
/// No description provided for @familyFeature3.
/// No description provided for @redeemUserInviteCode.
///
/// In en, this message translates to:
/// **'✓ Restore flames'**
String get familyFeature3;
/// **'Or redeem a twonly-Code.'**
String get redeemUserInviteCode;
/// No description provided for @familyFeature4.
/// No description provided for @redeemUserInviteCodeTitle.
///
/// In en, this message translates to:
/// **'✓ Support twonly'**
String get familyFeature4;
/// **'Redeem twonly-Code'**
String get redeemUserInviteCodeTitle;
/// No description provided for @redeemUserInviteCodeSuccess.
///
/// In en, this message translates to:
/// **'Your plan has been successfully adjusted.'**
String get redeemUserInviteCodeSuccess;
/// No description provided for @freeFeature1.
///
/// In en, this message translates to:
/// **'✓ 10 Media file uploads per day'**
/// **'10 Media file uploads per day'**
String get freeFeature1;
/// No description provided for @plusFeature1.
@ -1417,7 +1403,7 @@ abstract class AppLocalizations {
/// No description provided for @plusFeature2.
///
/// In en, this message translates to:
/// **'Additional features (coming-soon)'**
/// **'Additional features (coming-soon)'**
String get plusFeature2;
/// No description provided for @transactionHistory.
@ -1429,7 +1415,7 @@ abstract class AppLocalizations {
/// No description provided for @manageSubscription.
///
/// In en, this message translates to:
/// **'Manage subscription'**
/// **'Manage your subscription'**
String get manageSubscription;
/// No description provided for @nextPayment.
@ -2089,7 +2075,7 @@ abstract class AppLocalizations {
/// No description provided for @uploadLimitReached.
///
/// In en, this message translates to:
/// **'The upload limit has\nbeen reached. Upgrade to Pro\nor wait until tomorrow.'**
/// **'The upload limit has\been reached. Upgrade to Pro\nor wait until tomorrow.'**
String get uploadLimitReached;
/// No description provided for @retransmissionRequested.
@ -2569,7 +2555,7 @@ abstract class AppLocalizations {
/// No description provided for @notificationTitleUnknownUser.
///
/// In en, this message translates to:
/// **'[Unknown]'**
/// **'Someone'**
String get notificationTitleUnknownUser;
/// No description provided for @notificationCategoryMessageTitle.
@ -2823,108 +2809,6 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'The public key in the link does not match the public key stored for this contact. Try to meet your friend in person and scan the QR code directly!'**
String get linkPubkeyDoesNotMatch;
/// No description provided for @startWithCameraOpen.
///
/// In en, this message translates to:
/// **'Start with camera open'**
String get startWithCameraOpen;
/// No description provided for @showImagePreviewWhenSending.
///
/// In en, this message translates to:
/// **'Display image preview when selecting recipients'**
String get showImagePreviewWhenSending;
/// No description provided for @verifiedPublicKey.
///
/// In en, this message translates to:
/// **'The public key of {username} has been verified and is valid.'**
String verifiedPublicKey(Object username);
/// No description provided for @memoriesAYearAgo.
///
/// In en, this message translates to:
/// **'One year ago'**
String get memoriesAYearAgo;
/// No description provided for @memoriesXYearsAgo.
///
/// In en, this message translates to:
/// **'{years} years ago'**
String memoriesXYearsAgo(Object years);
/// No description provided for @migrationOfMemories.
///
/// In en, this message translates to:
/// **'Migration of media files: {open} still to be processed.'**
String migrationOfMemories(Object open);
/// No description provided for @autoStoreAllSendUnlimitedMediaFiles.
///
/// In en, this message translates to:
/// **'Save all sent media'**
String get autoStoreAllSendUnlimitedMediaFiles;
/// No description provided for @autoStoreAllSendUnlimitedMediaFilesSubtitle.
///
/// In en, this message translates to:
/// **'If you enable this option, all images you send will be saved as long as they were sent with an infinite countdown and not in twonly mode.'**
String get autoStoreAllSendUnlimitedMediaFilesSubtitle;
/// No description provided for @termsOfService.
///
/// In en, this message translates to:
/// **'Terms of service'**
String get termsOfService;
/// No description provided for @privacyPolicy.
///
/// In en, this message translates to:
/// **'Privacy policy'**
String get privacyPolicy;
/// No description provided for @additionalUserAddError.
///
/// In en, this message translates to:
/// **'Could not add additional user. Try again later.'**
String get additionalUserAddError;
/// No description provided for @additionalUserAddButton.
///
/// In en, this message translates to:
/// **'Add additional user ({used}/{limit})'**
String additionalUserAddButton(Object limit, Object used);
/// No description provided for @additionalUserRemoveTitle.
///
/// In en, this message translates to:
/// **'Remove this additional user'**
String get additionalUserRemoveTitle;
/// No description provided for @additionalUserRemoveDesc.
///
/// In en, this message translates to:
/// **'After removal, the additional user will automatically be downgraded to the free plan, and you can add another person.'**
String get additionalUserRemoveDesc;
/// No description provided for @additionalUserSelectTitle.
///
/// In en, this message translates to:
/// **'Select additional users'**
String get additionalUserSelectTitle;
/// No description provided for @additionalUserSelectButton.
///
/// In en, this message translates to:
/// **'Select users ({used}/{limit})'**
String additionalUserSelectButton(Object limit, Object used);
/// No description provided for @storeAsDefault.
///
/// In en, this message translates to:
/// **'Store as default'**
String get storeAsDefault;
}
class _AppLocalizationsDelegate
@ -2938,7 +2822,7 @@ class _AppLocalizationsDelegate
@override
bool isSupported(Locale locale) =>
<String>['de', 'en', 'sv'].contains(locale.languageCode);
<String>['de', 'en'].contains(locale.languageCode);
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
@ -2951,8 +2835,6 @@ AppLocalizations lookupAppLocalizations(Locale locale) {
return AppLocalizationsDe();
case 'en':
return AppLocalizationsEn();
case 'sv':
return AppLocalizationsSv();
}
throw FlutterError(

View file

@ -79,10 +79,6 @@ class AppLocalizationsDe extends AppLocalizations {
String get registerUsernameLimits =>
'Der Benutzername muss mindestens 3 Zeichen lang sein.';
@override
String get registerProofOfWorkFailed =>
'Beim Captcha-Test gab es ein Problem. Bitte versuche es erneut.';
@override
String get registerSubmitButton => 'Jetzt registrieren!';
@ -689,8 +685,8 @@ class AppLocalizationsDe extends AppLocalizations {
String get upgradeToPaidPlan => 'Upgrade auf einen kostenpflichtigen Plan.';
@override
String upgradeToPaidPlanButton(Object planId, Object sufix) {
return 'Auf $planId upgraden$sufix';
String upgradeToPaidPlanButton(Object planId) {
return 'Auf $planId upgraden';
}
@override
@ -699,49 +695,47 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
String get year => 'Jahr';
String get year => 'year';
@override
String get yearly => 'Jährlich';
@override
String get month => 'Monat';
@override
String get monthly => 'Monatlich';
String get month => 'month';
@override
String get proFeature1 => '✓ Unbegrenzte Medien-Datei-Uploads';
@override
String get proFeature2 => '1 zusätzlicher Plus Benutzer';
String get proFeature2 => '1 zusätzlicher Plus Benutzer';
@override
String get proFeature3 => 'Flammen wiederherstellen';
String get proFeature3 => 'Flammen wiederherstellen';
@override
String get proFeature4 => '✓ twonly unterstützen';
String get proFeature4 => 'Cloud-Backup verschlüsselt (coming-soon)';
@override
String get familyFeature1 => 'Unbegrenzte Medien-Datei-Uploads';
String get familyFeature1 => 'Alles von Pro';
@override
String get familyFeature2 => '4 zusätzliche Plus Benutzer';
String get familyFeature2 => '4 zusätzliche Plus Benutzer';
@override
String get familyFeature3 => '✓ Flammen wiederherstellen';
String get redeemUserInviteCode => 'Oder löse einen twonly-Code ein.';
@override
String get familyFeature4 => '✓ twonly unterstützen';
String get redeemUserInviteCodeTitle => 'twonly-Code einlösen';
@override
String get freeFeature1 => '✓ 10 Medien-Datei-Uploads pro Tag';
String get redeemUserInviteCodeSuccess =>
'Dein Plan wurde erfolgreich angepasst.';
@override
String get freeFeature1 => '10 Medien-Datei-Uploads pro Tag';
@override
String get plusFeature1 => '✓ Unbegrenzte Medien-Datei-Uploads';
@override
String get plusFeature2 => 'Zusatzfunktionen (coming-soon)';
String get plusFeature2 => 'Zusatzfunktionen (coming-soon)';
@override
String get transactionHistory => 'Transaktionshistorie';
@ -865,7 +859,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get checkoutSubmit => 'Kostenpflichtig bestellen';
@override
String get additionalUsersList => 'Deine zusätzlichen Benutzer';
String get additionalUsersList => 'Ihre zusätzlichen Benutzer';
@override
String get additionalUsersPlusTokens => 'twonly-Codes für \"Plus\"-Benutzer';
@ -1415,7 +1409,7 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
String get notificationTitleUnknownUser => '[Unbekannt]';
String get notificationTitleUnknownUser => 'Jemand';
@override
String get notificationCategoryMessageTitle => 'Nachrichten';
@ -1560,71 +1554,4 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get linkPubkeyDoesNotMatch =>
'Der öffentliche Schlüssel im Link stimmt nicht mit dem für diesen Kontakt gespeicherten öffentlichen Schlüssel überein. Triff die Person persönlich und scanne den QR-Code direkt!';
@override
String get startWithCameraOpen => 'Mit geöffneter Kamera starten';
@override
String get showImagePreviewWhenSending =>
'Bildvorschau bei der Auswahl von Empfängern anzeigen';
@override
String verifiedPublicKey(Object username) {
return 'Der öffentliche Schlüssel von $username wurde überprüft und ist gültig.';
}
@override
String get memoriesAYearAgo => 'Vor einem Jahr';
@override
String memoriesXYearsAgo(Object years) {
return 'Vor $years Jahren';
}
@override
String migrationOfMemories(Object open) {
return 'Migration von Mediendateien: $open noch offen.';
}
@override
String get autoStoreAllSendUnlimitedMediaFiles =>
'Alle gesendeten Medien speichern';
@override
String get autoStoreAllSendUnlimitedMediaFilesSubtitle =>
'Wenn du diese Option aktivierst, werden alle Bilder, die du sendest, gespeichert, sofern sie mit einem unendlichen Countdown und nicht im twonly-Modus gesendet wurden.';
@override
String get termsOfService => 'Allgemeine Geschäftsbedingungen';
@override
String get privacyPolicy => 'Datenschutzerklärung';
@override
String get additionalUserAddError =>
'Es konnte kein zusätzlicher Nutzer hinzugefügt werden. Versuche es später noch einmal.';
@override
String additionalUserAddButton(Object limit, Object used) {
return 'Zusätzlichen Benutzer hinzufügen ($used/$limit)';
}
@override
String get additionalUserRemoveTitle =>
'Diesen zusätzlichen Benutzer entfernen';
@override
String get additionalUserRemoveDesc =>
'Der zusätzliche Nutzer wird nach der Entfernung automatisch auf den kostenlosen Tarif zurückgestuft und du kannst eine andere Person hinzufügen.';
@override
String get additionalUserSelectTitle => 'Zusätzliche Benutzer auswählen';
@override
String additionalUserSelectButton(Object limit, Object used) {
return 'Benutzer auswählen ($used/$limit)';
}
@override
String get storeAsDefault => 'Als Standard speichern';
}

View file

@ -78,10 +78,6 @@ class AppLocalizationsEn extends AppLocalizations {
String get registerUsernameLimits =>
'Your username must be at least 3 characters long.';
@override
String get registerProofOfWorkFailed =>
'There was an issue with the captcha test. Please try again.';
@override
String get registerSubmitButton => 'Register now!';
@ -683,8 +679,8 @@ class AppLocalizationsEn extends AppLocalizations {
String get upgradeToPaidPlan => 'Upgrade to a paid plan.';
@override
String upgradeToPaidPlanButton(Object planId, Object sufix) {
return 'Upgrade to $planId$sufix';
String upgradeToPaidPlanButton(Object planId) {
return 'Upgrade subscription to $planId';
}
@override
@ -695,53 +691,51 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get year => 'year';
@override
String get yearly => 'Yearly';
@override
String get month => 'month';
@override
String get monthly => 'Monthly';
@override
String get proFeature1 => '✓ Unlimited media file uploads';
@override
String get proFeature2 => '1 additional Plus user';
String get proFeature2 => '1 additional Plus user';
@override
String get proFeature3 => '✓ Restore flames';
String get proFeature3 => 'Cloud-Backup encrypted (coming-soon)';
@override
String get proFeature4 => '✓ Support twonly';
String get proFeature4 => 'Additional features (coming-soon)';
@override
String get familyFeature1 => 'Unlimited media file uploads';
String get familyFeature1 => 'All from Pro';
@override
String get familyFeature2 => '4 additional Plus user';
String get familyFeature2 => '4 additional Plus users';
@override
String get familyFeature3 => '✓ Restore flames';
String get redeemUserInviteCode => 'Or redeem a twonly-Code.';
@override
String get familyFeature4 => '✓ Support twonly';
String get redeemUserInviteCodeTitle => 'Redeem twonly-Code';
@override
String get freeFeature1 => '✓ 10 Media file uploads per day';
String get redeemUserInviteCodeSuccess =>
'Your plan has been successfully adjusted.';
@override
String get freeFeature1 => '10 Media file uploads per day';
@override
String get plusFeature1 => '✓ Unlimited media file uploads';
@override
String get plusFeature2 => 'Additional features (coming-soon)';
String get plusFeature2 => 'Additional features (coming-soon)';
@override
String get transactionHistory => 'Your transaction history';
@override
String get manageSubscription => 'Manage subscription';
String get manageSubscription => 'Manage your subscription';
@override
String get nextPayment => 'Next payment';
@ -1101,7 +1095,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get uploadLimitReached =>
'The upload limit has\nbeen reached. Upgrade to Pro\nor wait until tomorrow.';
'The upload limit has\been reached. Upgrade to Pro\nor wait until tomorrow.';
@override
String get retransmissionRequested => 'Retransmission requested';
@ -1407,7 +1401,7 @@ class AppLocalizationsEn extends AppLocalizations {
}
@override
String get notificationTitleUnknownUser => '[Unknown]';
String get notificationTitleUnknownUser => 'Someone';
@override
String get notificationCategoryMessageTitle => 'Messages';
@ -1550,69 +1544,4 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get linkPubkeyDoesNotMatch =>
'The public key in the link does not match the public key stored for this contact. Try to meet your friend in person and scan the QR code directly!';
@override
String get startWithCameraOpen => 'Start with camera open';
@override
String get showImagePreviewWhenSending =>
'Display image preview when selecting recipients';
@override
String verifiedPublicKey(Object username) {
return 'The public key of $username has been verified and is valid.';
}
@override
String get memoriesAYearAgo => 'One year ago';
@override
String memoriesXYearsAgo(Object years) {
return '$years years ago';
}
@override
String migrationOfMemories(Object open) {
return 'Migration of media files: $open still to be processed.';
}
@override
String get autoStoreAllSendUnlimitedMediaFiles => 'Save all sent media';
@override
String get autoStoreAllSendUnlimitedMediaFilesSubtitle =>
'If you enable this option, all images you send will be saved as long as they were sent with an infinite countdown and not in twonly mode.';
@override
String get termsOfService => 'Terms of service';
@override
String get privacyPolicy => 'Privacy policy';
@override
String get additionalUserAddError =>
'Could not add additional user. Try again later.';
@override
String additionalUserAddButton(Object limit, Object used) {
return 'Add additional user ($used/$limit)';
}
@override
String get additionalUserRemoveTitle => 'Remove this additional user';
@override
String get additionalUserRemoveDesc =>
'After removal, the additional user will automatically be downgraded to the free plan, and you can add another person.';
@override
String get additionalUserSelectTitle => 'Select additional users';
@override
String additionalUserSelectButton(Object limit, Object used) {
return 'Select users ($used/$limit)';
}
@override
String get storeAsDefault => 'Store as default';
}

File diff suppressed because it is too large Load diff

@ -1 +0,0 @@
Subproject commit 775c0ffd9523177478681ecff4e8c4613bf57ee3

View file

@ -41,8 +41,6 @@ class UserData {
@JsonKey(defaultValue: 'Free')
String subscriptionPlan;
String? subscriptionPlanIdStore;
DateTime? lastImageSend;
int? todaysImageCounter;
@ -59,12 +57,6 @@ class UserData {
@JsonKey(defaultValue: true)
bool showFeedbackShortcut = true;
@JsonKey(defaultValue: false)
bool showShowImagePreviewWhenSending = false;
@JsonKey(defaultValue: true)
bool startWithCameraOpen = true;
List<String>? preSelectedEmojies;
Map<String, List<String>>? autoDownloadOptions;
@ -72,9 +64,6 @@ class UserData {
@JsonKey(defaultValue: false)
bool storeMediaFilesInGallery = false;
@JsonKey(defaultValue: false)
bool autoStoreAllSendUnlimitedMediaFiles = false;
String? lastPlanBallance;
String? additionalUserInvites;
@ -97,11 +86,8 @@ class UserData {
List<int>? lastChangeLogHash;
@JsonKey(defaultValue: true)
bool hideChangeLog = true;
@JsonKey(defaultValue: true)
bool updateFCMToken = true;
@JsonKey(defaultValue: false)
bool hideChangeLog = false;
// --- BACKUP ---

View file

@ -20,7 +20,6 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
..disableVideoCompression =
json['disableVideoCompression'] as bool? ?? false
..deviceId = (json['deviceId'] as num?)?.toInt() ?? 0
..subscriptionPlanIdStore = json['subscriptionPlanIdStore'] as String?
..lastImageSend = json['lastImageSend'] == null
? null
: DateTime.parse(json['lastImageSend'] as String)
@ -32,9 +31,6 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
..requestedAudioPermission =
json['requestedAudioPermission'] as bool? ?? false
..showFeedbackShortcut = json['showFeedbackShortcut'] as bool? ?? true
..showShowImagePreviewWhenSending =
json['showShowImagePreviewWhenSending'] as bool? ?? false
..startWithCameraOpen = json['startWithCameraOpen'] as bool? ?? true
..preSelectedEmojies = (json['preSelectedEmojies'] as List<dynamic>?)
?.map((e) => e as String)
.toList()
@ -45,8 +41,6 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
)
..storeMediaFilesInGallery =
json['storeMediaFilesInGallery'] as bool? ?? false
..autoStoreAllSendUnlimitedMediaFiles =
json['autoStoreAllSendUnlimitedMediaFiles'] as bool? ?? false
..lastPlanBallance = json['lastPlanBallance'] as String?
..additionalUserInvites = json['additionalUserInvites'] as String?
..tutorialDisplayed = (json['tutorialDisplayed'] as List<dynamic>?)
@ -66,8 +60,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
..lastChangeLogHash = (json['lastChangeLogHash'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
.toList()
..hideChangeLog = json['hideChangeLog'] as bool? ?? true
..updateFCMToken = json['updateFCMToken'] as bool? ?? true
..hideChangeLog = json['hideChangeLog'] as bool? ?? false
..nextTimeToShowBackupNotice = json['nextTimeToShowBackupNotice'] == null
? null
: DateTime.parse(json['nextTimeToShowBackupNotice'] as String)
@ -91,21 +84,15 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
'disableVideoCompression': instance.disableVideoCompression,
'deviceId': instance.deviceId,
'subscriptionPlan': instance.subscriptionPlan,
'subscriptionPlanIdStore': instance.subscriptionPlanIdStore,
'lastImageSend': instance.lastImageSend?.toIso8601String(),
'todaysImageCounter': instance.todaysImageCounter,
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
'defaultShowTime': instance.defaultShowTime,
'requestedAudioPermission': instance.requestedAudioPermission,
'showFeedbackShortcut': instance.showFeedbackShortcut,
'showShowImagePreviewWhenSending':
instance.showShowImagePreviewWhenSending,
'startWithCameraOpen': instance.startWithCameraOpen,
'preSelectedEmojies': instance.preSelectedEmojies,
'autoDownloadOptions': instance.autoDownloadOptions,
'storeMediaFilesInGallery': instance.storeMediaFilesInGallery,
'autoStoreAllSendUnlimitedMediaFiles':
instance.autoStoreAllSendUnlimitedMediaFiles,
'lastPlanBallance': instance.lastPlanBallance,
'additionalUserInvites': instance.additionalUserInvites,
'tutorialDisplayed': instance.tutorialDisplayed,
@ -117,7 +104,6 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
'currentSignedPreKeyIndexStart': instance.currentSignedPreKeyIndexStart,
'lastChangeLogHash': instance.lastChangeLogHash,
'hideChangeLog': instance.hideChangeLog,
'updateFCMToken': instance.updateFCMToken,
'nextTimeToShowBackupNotice':
instance.nextTimeToShowBackupNotice?.toIso8601String(),
'backupServer': instance.backupServer,

View file

@ -2085,136 +2085,6 @@ class ApplicationData_ReportUser extends $pb.GeneratedMessage {
void clearReason() => $_clearField(2);
}
class ApplicationData_IPAPurchase extends $pb.GeneratedMessage {
factory ApplicationData_IPAPurchase({
$core.String? productId,
$core.String? source,
$core.String? verificationData,
}) {
final result = create();
if (productId != null) result.productId = productId;
if (source != null) result.source = source;
if (verificationData != null) result.verificationData = verificationData;
return result;
}
ApplicationData_IPAPurchase._();
factory ApplicationData_IPAPurchase.fromBuffer($core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory ApplicationData_IPAPurchase.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'ApplicationData.IPAPurchase',
package:
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
createEmptyInstance: create)
..aOS(1, _omitFieldNames ? '' : 'productId')
..aOS(2, _omitFieldNames ? '' : 'source')
..aOS(3, _omitFieldNames ? '' : 'verificationData')
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ApplicationData_IPAPurchase clone() =>
ApplicationData_IPAPurchase()..mergeFromMessage(this);
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ApplicationData_IPAPurchase copyWith(
void Function(ApplicationData_IPAPurchase) updates) =>
super.copyWith(
(message) => updates(message as ApplicationData_IPAPurchase))
as ApplicationData_IPAPurchase;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ApplicationData_IPAPurchase create() =>
ApplicationData_IPAPurchase._();
@$core.override
ApplicationData_IPAPurchase createEmptyInstance() => create();
static $pb.PbList<ApplicationData_IPAPurchase> createRepeated() =>
$pb.PbList<ApplicationData_IPAPurchase>();
@$core.pragma('dart2js:noInline')
static ApplicationData_IPAPurchase getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ApplicationData_IPAPurchase>(create);
static ApplicationData_IPAPurchase? _defaultInstance;
@$pb.TagNumber(1)
$core.String get productId => $_getSZ(0);
@$pb.TagNumber(1)
set productId($core.String value) => $_setString(0, value);
@$pb.TagNumber(1)
$core.bool hasProductId() => $_has(0);
@$pb.TagNumber(1)
void clearProductId() => $_clearField(1);
@$pb.TagNumber(2)
$core.String get source => $_getSZ(1);
@$pb.TagNumber(2)
set source($core.String value) => $_setString(1, value);
@$pb.TagNumber(2)
$core.bool hasSource() => $_has(1);
@$pb.TagNumber(2)
void clearSource() => $_clearField(2);
@$pb.TagNumber(3)
$core.String get verificationData => $_getSZ(2);
@$pb.TagNumber(3)
set verificationData($core.String value) => $_setString(2, value);
@$pb.TagNumber(3)
$core.bool hasVerificationData() => $_has(2);
@$pb.TagNumber(3)
void clearVerificationData() => $_clearField(3);
}
class ApplicationData_IPAForceCheck extends $pb.GeneratedMessage {
factory ApplicationData_IPAForceCheck() => create();
ApplicationData_IPAForceCheck._();
factory ApplicationData_IPAForceCheck.fromBuffer($core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory ApplicationData_IPAForceCheck.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'ApplicationData.IPAForceCheck',
package:
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
createEmptyInstance: create)
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ApplicationData_IPAForceCheck clone() =>
ApplicationData_IPAForceCheck()..mergeFromMessage(this);
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ApplicationData_IPAForceCheck copyWith(
void Function(ApplicationData_IPAForceCheck) updates) =>
super.copyWith(
(message) => updates(message as ApplicationData_IPAForceCheck))
as ApplicationData_IPAForceCheck;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ApplicationData_IPAForceCheck create() =>
ApplicationData_IPAForceCheck._();
@$core.override
ApplicationData_IPAForceCheck createEmptyInstance() => create();
static $pb.PbList<ApplicationData_IPAForceCheck> createRepeated() =>
$pb.PbList<ApplicationData_IPAForceCheck>();
@$core.pragma('dart2js:noInline')
static ApplicationData_IPAForceCheck getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ApplicationData_IPAForceCheck>(create);
static ApplicationData_IPAForceCheck? _defaultInstance;
}
class ApplicationData_DeleteAccount extends $pb.GeneratedMessage {
factory ApplicationData_DeleteAccount() => create();
@ -2260,69 +2130,6 @@ class ApplicationData_DeleteAccount extends $pb.GeneratedMessage {
static ApplicationData_DeleteAccount? _defaultInstance;
}
class ApplicationData_AddAdditionalUser extends $pb.GeneratedMessage {
factory ApplicationData_AddAdditionalUser({
$fixnum.Int64? userId,
}) {
final result = create();
if (userId != null) result.userId = userId;
return result;
}
ApplicationData_AddAdditionalUser._();
factory ApplicationData_AddAdditionalUser.fromBuffer(
$core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory ApplicationData_AddAdditionalUser.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'ApplicationData.AddAdditionalUser',
package:
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
createEmptyInstance: create)
..aInt64(1, _omitFieldNames ? '' : 'userId')
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ApplicationData_AddAdditionalUser clone() =>
ApplicationData_AddAdditionalUser()..mergeFromMessage(this);
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ApplicationData_AddAdditionalUser copyWith(
void Function(ApplicationData_AddAdditionalUser) updates) =>
super.copyWith((message) =>
updates(message as ApplicationData_AddAdditionalUser))
as ApplicationData_AddAdditionalUser;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ApplicationData_AddAdditionalUser create() =>
ApplicationData_AddAdditionalUser._();
@$core.override
ApplicationData_AddAdditionalUser createEmptyInstance() => create();
static $pb.PbList<ApplicationData_AddAdditionalUser> createRepeated() =>
$pb.PbList<ApplicationData_AddAdditionalUser>();
@$core.pragma('dart2js:noInline')
static ApplicationData_AddAdditionalUser getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ApplicationData_AddAdditionalUser>(
create);
static ApplicationData_AddAdditionalUser? _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);
}
enum ApplicationData_ApplicationData {
textMessage,
getUserByUsername,
@ -2346,9 +2153,6 @@ enum ApplicationData_ApplicationData {
deleteAccount,
reportUser,
changeUsername,
ipaPurchase,
ipaForceCheck,
addAdditionalUser,
notSet
}
@ -2376,9 +2180,6 @@ class ApplicationData extends $pb.GeneratedMessage {
ApplicationData_DeleteAccount? deleteAccount,
ApplicationData_ReportUser? reportUser,
ApplicationData_ChangeUsername? changeUsername,
ApplicationData_IPAPurchase? ipaPurchase,
ApplicationData_IPAForceCheck? ipaForceCheck,
ApplicationData_AddAdditionalUser? addAdditionalUser,
}) {
final result = create();
if (textMessage != null) result.textMessage = textMessage;
@ -2411,9 +2212,6 @@ class ApplicationData extends $pb.GeneratedMessage {
if (deleteAccount != null) result.deleteAccount = deleteAccount;
if (reportUser != null) result.reportUser = reportUser;
if (changeUsername != null) result.changeUsername = changeUsername;
if (ipaPurchase != null) result.ipaPurchase = ipaPurchase;
if (ipaForceCheck != null) result.ipaForceCheck = ipaForceCheck;
if (addAdditionalUser != null) result.addAdditionalUser = addAdditionalUser;
return result;
}
@ -2450,9 +2248,6 @@ class ApplicationData extends $pb.GeneratedMessage {
24: ApplicationData_ApplicationData.deleteAccount,
25: ApplicationData_ApplicationData.reportUser,
26: ApplicationData_ApplicationData.changeUsername,
27: ApplicationData_ApplicationData.ipaPurchase,
28: ApplicationData_ApplicationData.ipaForceCheck,
29: ApplicationData_ApplicationData.addAdditionalUser,
0: ApplicationData_ApplicationData.notSet
};
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
@ -2482,10 +2277,7 @@ class ApplicationData extends $pb.GeneratedMessage {
23,
24,
25,
26,
27,
28,
29
26
])
..aOM<ApplicationData_TextMessage>(1, _omitFieldNames ? '' : 'textMessage',
protoName: 'textMessage',
@ -2569,17 +2361,6 @@ class ApplicationData extends $pb.GeneratedMessage {
26, _omitFieldNames ? '' : 'changeUsername',
protoName: 'changeUsername',
subBuilder: ApplicationData_ChangeUsername.create)
..aOM<ApplicationData_IPAPurchase>(27, _omitFieldNames ? '' : 'ipaPurchase',
protoName: 'ipaPurchase',
subBuilder: ApplicationData_IPAPurchase.create)
..aOM<ApplicationData_IPAForceCheck>(
28, _omitFieldNames ? '' : 'ipaForceCheck',
protoName: 'ipaForceCheck',
subBuilder: ApplicationData_IPAForceCheck.create)
..aOM<ApplicationData_AddAdditionalUser>(
29, _omitFieldNames ? '' : 'addAdditionalUser',
protoName: 'addAdditionalUser',
subBuilder: ApplicationData_AddAdditionalUser.create)
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -2871,41 +2652,6 @@ class ApplicationData extends $pb.GeneratedMessage {
void clearChangeUsername() => $_clearField(26);
@$pb.TagNumber(26)
ApplicationData_ChangeUsername ensureChangeUsername() => $_ensure(21);
@$pb.TagNumber(27)
ApplicationData_IPAPurchase get ipaPurchase => $_getN(22);
@$pb.TagNumber(27)
set ipaPurchase(ApplicationData_IPAPurchase value) => $_setField(27, value);
@$pb.TagNumber(27)
$core.bool hasIpaPurchase() => $_has(22);
@$pb.TagNumber(27)
void clearIpaPurchase() => $_clearField(27);
@$pb.TagNumber(27)
ApplicationData_IPAPurchase ensureIpaPurchase() => $_ensure(22);
@$pb.TagNumber(28)
ApplicationData_IPAForceCheck get ipaForceCheck => $_getN(23);
@$pb.TagNumber(28)
set ipaForceCheck(ApplicationData_IPAForceCheck value) =>
$_setField(28, value);
@$pb.TagNumber(28)
$core.bool hasIpaForceCheck() => $_has(23);
@$pb.TagNumber(28)
void clearIpaForceCheck() => $_clearField(28);
@$pb.TagNumber(28)
ApplicationData_IPAForceCheck ensureIpaForceCheck() => $_ensure(23);
@$pb.TagNumber(29)
ApplicationData_AddAdditionalUser get addAdditionalUser => $_getN(24);
@$pb.TagNumber(29)
set addAdditionalUser(ApplicationData_AddAdditionalUser value) =>
$_setField(29, value);
@$pb.TagNumber(29)
$core.bool hasAddAdditionalUser() => $_has(24);
@$pb.TagNumber(29)
void clearAddAdditionalUser() => $_clearField(29);
@$pb.TagNumber(29)
ApplicationData_AddAdditionalUser ensureAddAdditionalUser() => $_ensure(24);
}
class Response_PreKey extends $pb.GeneratedMessage {

View file

@ -389,6 +389,15 @@ const ApplicationData$json = {
'9': 0,
'10': 'redeemAdditionalCode'
},
{
'1': 'removeAdditionalUser',
'3': 18,
'4': 1,
'5': 11,
'6': '.client_to_server.ApplicationData.RemoveAdditionalUser',
'9': 0,
'10': 'removeAdditionalUser'
},
{
'1': 'updatePlanOptions',
'3': 19,
@ -452,42 +461,6 @@ const ApplicationData$json = {
'9': 0,
'10': 'changeUsername'
},
{
'1': 'ipaPurchase',
'3': 27,
'4': 1,
'5': 11,
'6': '.client_to_server.ApplicationData.IPAPurchase',
'9': 0,
'10': 'ipaPurchase'
},
{
'1': 'ipaForceCheck',
'3': 28,
'4': 1,
'5': 11,
'6': '.client_to_server.ApplicationData.IPAForceCheck',
'9': 0,
'10': 'ipaForceCheck'
},
{
'1': 'removeAdditionalUser',
'3': 18,
'4': 1,
'5': 11,
'6': '.client_to_server.ApplicationData.RemoveAdditionalUser',
'9': 0,
'10': 'removeAdditionalUser'
},
{
'1': 'addAdditionalUser',
'3': 29,
'4': 1,
'5': 11,
'6': '.client_to_server.ApplicationData.AddAdditionalUser',
'9': 0,
'10': 'addAdditionalUser'
},
],
'3': [
ApplicationData_TextMessage$json,
@ -511,10 +484,7 @@ const ApplicationData$json = {
ApplicationData_UpdateSignedPreKey$json,
ApplicationData_DownloadDone$json,
ApplicationData_ReportUser$json,
ApplicationData_IPAPurchase$json,
ApplicationData_IPAForceCheck$json,
ApplicationData_DeleteAccount$json,
ApplicationData_AddAdditionalUser$json
ApplicationData_DeleteAccount$json
],
'8': [
{'1': 'ApplicationData'},
@ -698,40 +668,11 @@ const ApplicationData_ReportUser$json = {
],
};
@$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_IPAPurchase$json = {
'1': 'IPAPurchase',
'2': [
{'1': 'product_id', '3': 1, '4': 1, '5': 9, '10': 'productId'},
{'1': 'source', '3': 2, '4': 1, '5': 9, '10': 'source'},
{
'1': 'verification_data',
'3': 3,
'4': 1,
'5': 9,
'10': 'verificationData'
},
],
};
@$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_IPAForceCheck$json = {
'1': 'IPAForceCheck',
};
@$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_DeleteAccount$json = {
'1': 'DeleteAccount',
};
@$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_AddAdditionalUser$json = {
'1': 'AddAdditionalUser',
'2': [
{'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'},
],
};
/// Descriptor for `ApplicationData`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
'Cg9BcHBsaWNhdGlvbkRhdGESUQoLdGV4dE1lc3NhZ2UYASABKAsyLS5jbGllbnRfdG9fc2Vydm'
@ -758,51 +699,43 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
'VudHNJbnZpdGVzGBAgASgLMjcuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuR2V0'
'QWRkQWNjb3VudHNJbnZpdGVzSABSFWdldEFkZGFjY291bnRzSW52aXRlcxJsChRyZWRlZW1BZG'
'RpdGlvbmFsQ29kZRgRIAEoCzI2LmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLlJl'
'ZGVlbUFkZGl0aW9uYWxDb2RlSABSFHJlZGVlbUFkZGl0aW9uYWxDb2RlEmMKEXVwZGF0ZVBsYW'
'5PcHRpb25zGBMgASgLMjMuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuVXBkYXRl'
'UGxhbk9wdGlvbnNIAFIRdXBkYXRlUGxhbk9wdGlvbnMSVAoMZG93bmxvYWREb25lGBQgASgLMi'
'4uY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuRG93bmxvYWREb25lSABSDGRvd25s'
'b2FkRG9uZRJ1ChdnZXRTaWduZWRQcmVrZXlCeVVzZXJpZBgWIAEoCzI5LmNsaWVudF90b19zZX'
'J2ZXIuQXBwbGljYXRpb25EYXRhLkdldFNpZ25lZFByZUtleUJ5VXNlcklkSABSF2dldFNpZ25l'
'ZFByZWtleUJ5VXNlcmlkEmYKEnVwZGF0ZVNpZ25lZFByZWtleRgXIAEoCzI0LmNsaWVudF90b1'
'9zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLlVwZGF0ZVNpZ25lZFByZUtleUgAUhJ1cGRhdGVTaWdu'
'ZWRQcmVrZXkSVwoNZGVsZXRlQWNjb3VudBgYIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuQXBwbG'
'ljYXRpb25EYXRhLkRlbGV0ZUFjY291bnRIAFINZGVsZXRlQWNjb3VudBJOCgpyZXBvcnRVc2Vy'
'GBkgASgLMiwuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVwb3J0VXNlckgAUg'
'pyZXBvcnRVc2VyEloKDmNoYW5nZVVzZXJuYW1lGBogASgLMjAuY2xpZW50X3RvX3NlcnZlci5B'
'cHBsaWNhdGlvbkRhdGEuQ2hhbmdlVXNlcm5hbWVIAFIOY2hhbmdlVXNlcm5hbWUSUQoLaXBhUH'
'VyY2hhc2UYGyABKAsyLS5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5JUEFQdXJj'
'aGFzZUgAUgtpcGFQdXJjaGFzZRJXCg1pcGFGb3JjZUNoZWNrGBwgASgLMi8uY2xpZW50X3RvX3'
'NlcnZlci5BcHBsaWNhdGlvbkRhdGEuSVBBRm9yY2VDaGVja0gAUg1pcGFGb3JjZUNoZWNrEmwK'
'FHJlbW92ZUFkZGl0aW9uYWxVc2VyGBIgASgLMjYuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdG'
'lvbkRhdGEuUmVtb3ZlQWRkaXRpb25hbFVzZXJIAFIUcmVtb3ZlQWRkaXRpb25hbFVzZXISYwoR'
'YWRkQWRkaXRpb25hbFVzZXIYHSABKAsyMy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRG'
'F0YS5BZGRBZGRpdGlvbmFsVXNlckgAUhFhZGRBZGRpdGlvbmFsVXNlchpqCgtUZXh0TWVzc2Fn'
'ZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgDIAEoDFIEYm9keRIgCglwdXNoX2'
'RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YRovChFHZXRVc2VyQnlVc2Vy'
'bmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaLAoOQ2hhbmdlVXNlcm5hbWUSGgoIdX'
'Nlcm5hbWUYASABKAlSCHVzZXJuYW1lGjUKFFVwZGF0ZUdvb2dsZUZjbVRva2VuEh0KCmdvb2ds'
'ZV9mY20YASABKAlSCWdvb2dsZUZjbRomCgtHZXRVc2VyQnlJZBIXCgd1c2VyX2lkGAEgASgDUg'
'Z1c2VySWQaKQoNUmVkZWVtVm91Y2hlchIYCgd2b3VjaGVyGAEgASgJUgd2b3VjaGVyGnAKEVN3'
'aXRjaFRvUGF5ZWRQbGFuEhcKB3BsYW5faWQYASABKAlSBnBsYW5JZBIfCgtwYXlfbW9udGhseR'
'gCIAEoCFIKcGF5TW9udGhseRIhCgxhdXRvX3JlbmV3YWwYAyABKAhSC2F1dG9SZW5ld2FsGjYK'
'EVVwZGF0ZVBsYW5PcHRpb25zEiEKDGF1dG9fcmVuZXdhbBgBIAEoCFILYXV0b1JlbmV3YWwaMA'
'oNQ3JlYXRlVm91Y2hlchIfCgt2YWx1ZV9jZW50cxgBIAEoDVIKdmFsdWVDZW50cxoNCgtHZXRM'
'b2NhdGlvbhoNCgtHZXRWb3VjaGVycxoTChFHZXRBdmFpbGFibGVQbGFucxoXChVHZXRBZGRBY2'
'NvdW50c0ludml0ZXMaFQoTR2V0Q3VycmVudFBsYW5JbmZvcxo3ChRSZWRlZW1BZGRpdGlvbmFs'
'Q29kZRIfCgtpbnZpdGVfY29kZRgCIAEoCVIKaW52aXRlQ29kZRovChRSZW1vdmVBZGRpdGlvbm'
'FsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaLQoSR2V0UHJla2V5c0J5VXNlcklkEhcK'
'B3VzZXJfaWQYASABKANSBnVzZXJJZBoyChdHZXRTaWduZWRQcmVLZXlCeVVzZXJJZBIXCgd1c2'
'VyX2lkGAEgASgDUgZ1c2VySWQamwEKElVwZGF0ZVNpZ25lZFByZUtleRIoChBzaWduZWRfcHJl'
'a2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZXlJZBIjCg1zaWduZWRfcHJla2V5GAIgASgMUgxzaW'
'duZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaWduYXR1cmUYAyABKAxSFXNpZ25lZFByZWtl'
'eVNpZ25hdHVyZRo1CgxEb3dubG9hZERvbmUSJQoOZG93bmxvYWRfdG9rZW4YASABKAxSDWRvd2'
'5sb2FkVG9rZW4aTgoKUmVwb3J0VXNlchIoChByZXBvcnRlZF91c2VyX2lkGAEgASgDUg5yZXBv'
'cnRlZFVzZXJJZBIWCgZyZWFzb24YAiABKAlSBnJlYXNvbhpxCgtJUEFQdXJjaGFzZRIdCgpwcm'
'9kdWN0X2lkGAEgASgJUglwcm9kdWN0SWQSFgoGc291cmNlGAIgASgJUgZzb3VyY2USKwoRdmVy'
'aWZpY2F0aW9uX2RhdGEYAyABKAlSEHZlcmlmaWNhdGlvbkRhdGEaDwoNSVBBRm9yY2VDaGVjax'
'oPCg1EZWxldGVBY2NvdW50GiwKEUFkZEFkZGl0aW9uYWxVc2VyEhcKB3VzZXJfaWQYASABKANS'
'BnVzZXJJZEIRCg9BcHBsaWNhdGlvbkRhdGE=');
'ZGVlbUFkZGl0aW9uYWxDb2RlSABSFHJlZGVlbUFkZGl0aW9uYWxDb2RlEmwKFHJlbW92ZUFkZG'
'l0aW9uYWxVc2VyGBIgASgLMjYuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVt'
'b3ZlQWRkaXRpb25hbFVzZXJIAFIUcmVtb3ZlQWRkaXRpb25hbFVzZXISYwoRdXBkYXRlUGxhbk'
'9wdGlvbnMYEyABKAsyMy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5VcGRhdGVQ'
'bGFuT3B0aW9uc0gAUhF1cGRhdGVQbGFuT3B0aW9ucxJUCgxkb3dubG9hZERvbmUYFCABKAsyLi'
'5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5Eb3dubG9hZERvbmVIAFIMZG93bmxv'
'YWREb25lEnUKF2dldFNpZ25lZFByZWtleUJ5VXNlcmlkGBYgASgLMjkuY2xpZW50X3RvX3Nlcn'
'Zlci5BcHBsaWNhdGlvbkRhdGEuR2V0U2lnbmVkUHJlS2V5QnlVc2VySWRIAFIXZ2V0U2lnbmVk'
'UHJla2V5QnlVc2VyaWQSZgoSdXBkYXRlU2lnbmVkUHJla2V5GBcgASgLMjQuY2xpZW50X3RvX3'
'NlcnZlci5BcHBsaWNhdGlvbkRhdGEuVXBkYXRlU2lnbmVkUHJlS2V5SABSEnVwZGF0ZVNpZ25l'
'ZFByZWtleRJXCg1kZWxldGVBY2NvdW50GBggASgLMi8uY2xpZW50X3RvX3NlcnZlci5BcHBsaW'
'NhdGlvbkRhdGEuRGVsZXRlQWNjb3VudEgAUg1kZWxldGVBY2NvdW50Ek4KCnJlcG9ydFVzZXIY'
'GSABKAsyLC5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5SZXBvcnRVc2VySABSCn'
'JlcG9ydFVzZXISWgoOY2hhbmdlVXNlcm5hbWUYGiABKAsyMC5jbGllbnRfdG9fc2VydmVyLkFw'
'cGxpY2F0aW9uRGF0YS5DaGFuZ2VVc2VybmFtZUgAUg5jaGFuZ2VVc2VybmFtZRpqCgtUZXh0TW'
'Vzc2FnZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgDIAEoDFIEYm9keRIgCglw'
'dXNoX2RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YRovChFHZXRVc2VyQn'
'lVc2VybmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaLAoOQ2hhbmdlVXNlcm5hbWUS'
'GgoIdXNlcm5hbWUYASABKAlSCHVzZXJuYW1lGjUKFFVwZGF0ZUdvb2dsZUZjbVRva2VuEh0KCm'
'dvb2dsZV9mY20YASABKAlSCWdvb2dsZUZjbRomCgtHZXRVc2VyQnlJZBIXCgd1c2VyX2lkGAEg'
'ASgDUgZ1c2VySWQaKQoNUmVkZWVtVm91Y2hlchIYCgd2b3VjaGVyGAEgASgJUgd2b3VjaGVyGn'
'AKEVN3aXRjaFRvUGF5ZWRQbGFuEhcKB3BsYW5faWQYASABKAlSBnBsYW5JZBIfCgtwYXlfbW9u'
'dGhseRgCIAEoCFIKcGF5TW9udGhseRIhCgxhdXRvX3JlbmV3YWwYAyABKAhSC2F1dG9SZW5ld2'
'FsGjYKEVVwZGF0ZVBsYW5PcHRpb25zEiEKDGF1dG9fcmVuZXdhbBgBIAEoCFILYXV0b1JlbmV3'
'YWwaMAoNQ3JlYXRlVm91Y2hlchIfCgt2YWx1ZV9jZW50cxgBIAEoDVIKdmFsdWVDZW50cxoNCg'
'tHZXRMb2NhdGlvbhoNCgtHZXRWb3VjaGVycxoTChFHZXRBdmFpbGFibGVQbGFucxoXChVHZXRB'
'ZGRBY2NvdW50c0ludml0ZXMaFQoTR2V0Q3VycmVudFBsYW5JbmZvcxo3ChRSZWRlZW1BZGRpdG'
'lvbmFsQ29kZRIfCgtpbnZpdGVfY29kZRgCIAEoCVIKaW52aXRlQ29kZRovChRSZW1vdmVBZGRp'
'dGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaLQoSR2V0UHJla2V5c0J5VXNlck'
'lkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBoyChdHZXRTaWduZWRQcmVLZXlCeVVzZXJJZBIX'
'Cgd1c2VyX2lkGAEgASgDUgZ1c2VySWQamwEKElVwZGF0ZVNpZ25lZFByZUtleRIoChBzaWduZW'
'RfcHJla2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZXlJZBIjCg1zaWduZWRfcHJla2V5GAIgASgM'
'UgxzaWduZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaWduYXR1cmUYAyABKAxSFXNpZ25lZF'
'ByZWtleVNpZ25hdHVyZRo1CgxEb3dubG9hZERvbmUSJQoOZG93bmxvYWRfdG9rZW4YASABKAxS'
'DWRvd25sb2FkVG9rZW4aTgoKUmVwb3J0VXNlchIoChByZXBvcnRlZF91c2VyX2lkGAEgASgDUg'
'5yZXBvcnRlZFVzZXJJZBIWCgZyZWFzb24YAiABKAlSBnJlYXNvbhoPCg1EZWxldGVBY2NvdW50'
'QhEKD0FwcGxpY2F0aW9uRGF0YQ==');
@$core.Deprecated('Use responseDescriptor instead')
const Response$json = {

View file

@ -87,10 +87,6 @@ class ErrorCode extends $pb.ProtobufEnum {
ErrorCode._(1032, _omitEnumNames ? '' : 'InvalidProofOfWork');
static const ErrorCode RegistrationDisabled =
ErrorCode._(1033, _omitEnumNames ? '' : 'RegistrationDisabled');
static const ErrorCode IPAPaymentExpired =
ErrorCode._(1034, _omitEnumNames ? '' : 'IPAPaymentExpired');
static const ErrorCode UserIsNotInFreePlan =
ErrorCode._(1035, _omitEnumNames ? '' : 'UserIsNotInFreePlan');
static const $core.List<ErrorCode> values = <ErrorCode>[
Unknown,
@ -129,8 +125,6 @@ class ErrorCode extends $pb.ProtobufEnum {
NewDeviceRegistered,
InvalidProofOfWork,
RegistrationDisabled,
IPAPaymentExpired,
UserIsNotInFreePlan,
];
static final $core.Map<$core.int, ErrorCode> _byValue =

View file

@ -54,8 +54,6 @@ const ErrorCode$json = {
{'1': 'NewDeviceRegistered', '2': 1031},
{'1': 'InvalidProofOfWork', '2': 1032},
{'1': 'RegistrationDisabled', '2': 1033},
{'1': 'IPAPaymentExpired', '2': 1034},
{'1': 'UserIsNotInFreePlan', '2': 1035},
],
};
@ -76,5 +74,4 @@ final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode(
'bGFuRG93bmdyYWRlEIEIEhkKFFBsYW5VcGdyYWRlTm90WWVhcmx5EIIIEhgKE0ludmFsaWRTaW'
'duZWRQcmVLZXkQgwgSEwoOVXNlcklkTm90Rm91bmQQhAgSFwoSVXNlcklkQWxyZWFkeVRha2Vu'
'EIUIEhcKEkFwcFZlcnNpb25PdXRkYXRlZBCGCBIYChNOZXdEZXZpY2VSZWdpc3RlcmVkEIcIEh'
'cKEkludmFsaWRQcm9vZk9mV29yaxCICBIZChRSZWdpc3RyYXRpb25EaXNhYmxlZBCJCBIWChFJ'
'UEFQYXltZW50RXhwaXJlZBCKCBIYChNVc2VySXNOb3RJbkZyZWVQbGFuEIsI');
'cKEkludmFsaWRQcm9vZk9mV29yaxCICBIZChRSZWdpc3RyYXRpb25EaXNhYmxlZBCJCA==');

View file

@ -92,14 +92,7 @@ class ServerToClient extends $pb.GeneratedMessage {
V0 ensureV0() => $_ensure(0);
}
enum V0_Kind {
response,
newMessage,
requestNewPreKeys,
error,
newMessages,
notSet
}
enum V0_Kind { response, newMessage, requestNewPreKeys, error, notSet }
class V0 extends $pb.GeneratedMessage {
factory V0({
@ -108,7 +101,6 @@ class V0 extends $pb.GeneratedMessage {
NewMessage? newMessage,
$core.bool? requestNewPreKeys,
$0.ErrorCode? error,
NewMessages? newMessages,
}) {
final result = create();
if (seq != null) result.seq = seq;
@ -116,7 +108,6 @@ class V0 extends $pb.GeneratedMessage {
if (newMessage != null) result.newMessage = newMessage;
if (requestNewPreKeys != null) result.requestNewPreKeys = requestNewPreKeys;
if (error != null) result.error = error;
if (newMessages != null) result.newMessages = newMessages;
return result;
}
@ -134,7 +125,6 @@ class V0 extends $pb.GeneratedMessage {
3: V0_Kind.newMessage,
4: V0_Kind.requestNewPreKeys,
6: V0_Kind.error,
7: V0_Kind.newMessages,
0: V0_Kind.notSet
};
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
@ -142,7 +132,7 @@ class V0 extends $pb.GeneratedMessage {
package:
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
createEmptyInstance: create)
..oo(0, [2, 3, 4, 6, 7])
..oo(0, [2, 3, 4, 6])
..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'seq', $pb.PbFieldType.OU6,
defaultOrMaker: $fixnum.Int64.ZERO)
..aOM<Response>(2, _omitFieldNames ? '' : 'response',
@ -155,8 +145,6 @@ class V0 extends $pb.GeneratedMessage {
defaultOrMaker: $0.ErrorCode.Unknown,
valueOf: $0.ErrorCode.valueOf,
enumValues: $0.ErrorCode.values)
..aOM<NewMessages>(7, _omitFieldNames ? '' : 'newMessages',
protoName: 'newMessages', subBuilder: NewMessages.create)
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -229,17 +217,6 @@ class V0 extends $pb.GeneratedMessage {
$core.bool hasError() => $_has(4);
@$pb.TagNumber(6)
void clearError() => $_clearField(6);
@$pb.TagNumber(7)
NewMessages get newMessages => $_getN(5);
@$pb.TagNumber(7)
set newMessages(NewMessages value) => $_setField(7, value);
@$pb.TagNumber(7)
$core.bool hasNewMessages() => $_has(5);
@$pb.TagNumber(7)
void clearNewMessages() => $_clearField(7);
@$pb.TagNumber(7)
NewMessages ensureNewMessages() => $_ensure(5);
}
class NewMessage extends $pb.GeneratedMessage {
@ -310,58 +287,6 @@ class NewMessage extends $pb.GeneratedMessage {
void clearFromUserId() => $_clearField(2);
}
class NewMessages extends $pb.GeneratedMessage {
factory NewMessages({
$core.Iterable<NewMessage>? newMessages,
}) {
final result = create();
if (newMessages != null) result.newMessages.addAll(newMessages);
return result;
}
NewMessages._();
factory NewMessages.fromBuffer($core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory NewMessages.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'NewMessages',
package:
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
createEmptyInstance: create)
..pc<NewMessage>(
1, _omitFieldNames ? '' : 'newMessages', $pb.PbFieldType.PM,
protoName: 'newMessages', subBuilder: NewMessage.create)
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
NewMessages clone() => NewMessages()..mergeFromMessage(this);
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
NewMessages copyWith(void Function(NewMessages) updates) =>
super.copyWith((message) => updates(message as NewMessages))
as NewMessages;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static NewMessages create() => NewMessages._();
@$core.override
NewMessages createEmptyInstance() => create();
static $pb.PbList<NewMessages> createRepeated() => $pb.PbList<NewMessages>();
@$core.pragma('dart2js:noInline')
static NewMessages getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<NewMessages>(create);
static NewMessages? _defaultInstance;
@$pb.TagNumber(1)
$pb.PbList<NewMessage> get newMessages => $_getList(0);
}
class Response_Authenticated extends $pb.GeneratedMessage {
factory Response_Authenticated({
$core.String? plan,
@ -428,6 +353,7 @@ class Response_Plan extends $pb.GeneratedMessage {
$fixnum.Int64? dailyMediaUploadLimit,
$fixnum.Int64? maximalUploadSizeOfSingleMediaSize,
$fixnum.Int64? additionalPlusAccounts,
$fixnum.Int64? additionalFreeAccounts,
$fixnum.Int64? monthlyCostsCent,
$fixnum.Int64? yearlyCostsCent,
$core.bool? allowedToSendTextMessages,
@ -443,6 +369,8 @@ class Response_Plan extends $pb.GeneratedMessage {
maximalUploadSizeOfSingleMediaSize;
if (additionalPlusAccounts != null)
result.additionalPlusAccounts = additionalPlusAccounts;
if (additionalFreeAccounts != null)
result.additionalFreeAccounts = additionalFreeAccounts;
if (monthlyCostsCent != null) result.monthlyCostsCent = monthlyCostsCent;
if (yearlyCostsCent != null) result.yearlyCostsCent = yearlyCostsCent;
if (allowedToSendTextMessages != null)
@ -471,6 +399,7 @@ class Response_Plan extends $pb.GeneratedMessage {
..aInt64(3, _omitFieldNames ? '' : 'dailyMediaUploadLimit')
..aInt64(4, _omitFieldNames ? '' : 'maximalUploadSizeOfSingleMediaSize')
..aInt64(5, _omitFieldNames ? '' : 'additionalPlusAccounts')
..aInt64(6, _omitFieldNames ? '' : 'additionalFreeAccounts')
..aInt64(7, _omitFieldNames ? '' : 'monthlyCostsCent')
..aInt64(8, _omitFieldNames ? '' : 'yearlyCostsCent')
..aOB(9, _omitFieldNames ? '' : 'allowedToSendTextMessages')
@ -544,39 +473,48 @@ class Response_Plan extends $pb.GeneratedMessage {
@$pb.TagNumber(5)
void clearAdditionalPlusAccounts() => $_clearField(5);
@$pb.TagNumber(6)
$fixnum.Int64 get additionalFreeAccounts => $_getI64(5);
@$pb.TagNumber(6)
set additionalFreeAccounts($fixnum.Int64 value) => $_setInt64(5, value);
@$pb.TagNumber(6)
$core.bool hasAdditionalFreeAccounts() => $_has(5);
@$pb.TagNumber(6)
void clearAdditionalFreeAccounts() => $_clearField(6);
@$pb.TagNumber(7)
$fixnum.Int64 get monthlyCostsCent => $_getI64(5);
$fixnum.Int64 get monthlyCostsCent => $_getI64(6);
@$pb.TagNumber(7)
set monthlyCostsCent($fixnum.Int64 value) => $_setInt64(5, value);
set monthlyCostsCent($fixnum.Int64 value) => $_setInt64(6, value);
@$pb.TagNumber(7)
$core.bool hasMonthlyCostsCent() => $_has(5);
$core.bool hasMonthlyCostsCent() => $_has(6);
@$pb.TagNumber(7)
void clearMonthlyCostsCent() => $_clearField(7);
@$pb.TagNumber(8)
$fixnum.Int64 get yearlyCostsCent => $_getI64(6);
$fixnum.Int64 get yearlyCostsCent => $_getI64(7);
@$pb.TagNumber(8)
set yearlyCostsCent($fixnum.Int64 value) => $_setInt64(6, value);
set yearlyCostsCent($fixnum.Int64 value) => $_setInt64(7, value);
@$pb.TagNumber(8)
$core.bool hasYearlyCostsCent() => $_has(6);
$core.bool hasYearlyCostsCent() => $_has(7);
@$pb.TagNumber(8)
void clearYearlyCostsCent() => $_clearField(8);
@$pb.TagNumber(9)
$core.bool get allowedToSendTextMessages => $_getBF(7);
$core.bool get allowedToSendTextMessages => $_getBF(8);
@$pb.TagNumber(9)
set allowedToSendTextMessages($core.bool value) => $_setBool(7, value);
set allowedToSendTextMessages($core.bool value) => $_setBool(8, value);
@$pb.TagNumber(9)
$core.bool hasAllowedToSendTextMessages() => $_has(7);
$core.bool hasAllowedToSendTextMessages() => $_has(8);
@$pb.TagNumber(9)
void clearAllowedToSendTextMessages() => $_clearField(9);
@$pb.TagNumber(10)
$core.bool get isAdditionalAccount => $_getBF(8);
$core.bool get isAdditionalAccount => $_getBF(9);
@$pb.TagNumber(10)
set isAdditionalAccount($core.bool value) => $_setBool(8, value);
set isAdditionalAccount($core.bool value) => $_setBool(9, value);
@$pb.TagNumber(10)
$core.bool hasIsAdditionalAccount() => $_has(8);
$core.bool hasIsAdditionalAccount() => $_has(9);
@$pb.TagNumber(10)
void clearIsAdditionalAccount() => $_clearField(10);
}

View file

@ -61,15 +61,6 @@ const V0$json = {
'9': 0,
'10': 'newMessage'
},
{
'1': 'newMessages',
'3': 7,
'4': 1,
'5': 11,
'6': '.server_to_client.NewMessages',
'9': 0,
'10': 'newMessages'
},
{
'1': 'RequestNewPreKeys',
'3': 4,
@ -97,10 +88,9 @@ const V0$json = {
final $typed_data.Uint8List v0Descriptor = $convert.base64Decode(
'CgJWMBIQCgNzZXEYASABKARSA3NlcRI4CghyZXNwb25zZRgCIAEoCzIaLnNlcnZlcl90b19jbG'
'llbnQuUmVzcG9uc2VIAFIIcmVzcG9uc2USPgoKbmV3TWVzc2FnZRgDIAEoCzIcLnNlcnZlcl90'
'b19jbGllbnQuTmV3TWVzc2FnZUgAUgpuZXdNZXNzYWdlEkEKC25ld01lc3NhZ2VzGAcgASgLMh'
'0uc2VydmVyX3RvX2NsaWVudC5OZXdNZXNzYWdlc0gAUgtuZXdNZXNzYWdlcxIuChFSZXF1ZXN0'
'TmV3UHJlS2V5cxgEIAEoCEgAUhFSZXF1ZXN0TmV3UHJlS2V5cxIoCgVlcnJvchgGIAEoDjIQLm'
'Vycm9yLkVycm9yQ29kZUgAUgVlcnJvckIGCgRLaW5k');
'b19jbGllbnQuTmV3TWVzc2FnZUgAUgpuZXdNZXNzYWdlEi4KEVJlcXVlc3ROZXdQcmVLZXlzGA'
'QgASgISABSEVJlcXVlc3ROZXdQcmVLZXlzEigKBWVycm9yGAYgASgOMhAuZXJyb3IuRXJyb3JD'
'b2RlSABSBWVycm9yQgYKBEtpbmQ=');
@$core.Deprecated('Use newMessageDescriptor instead')
const NewMessage$json = {
@ -116,26 +106,6 @@ final $typed_data.Uint8List newMessageDescriptor = $convert.base64Decode(
'CgpOZXdNZXNzYWdlEiAKDGZyb21fdXNlcl9pZBgCIAEoA1IKZnJvbVVzZXJJZBISCgRib2R5GA'
'EgASgMUgRib2R5');
@$core.Deprecated('Use newMessagesDescriptor instead')
const NewMessages$json = {
'1': 'NewMessages',
'2': [
{
'1': 'newMessages',
'3': 1,
'4': 3,
'5': 11,
'6': '.server_to_client.NewMessage',
'10': 'newMessages'
},
],
};
/// Descriptor for `NewMessages`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List newMessagesDescriptor = $convert.base64Decode(
'CgtOZXdNZXNzYWdlcxI+CgtuZXdNZXNzYWdlcxgBIAMoCzIcLnNlcnZlcl90b19jbGllbnQuTm'
'V3TWVzc2FnZVILbmV3TWVzc2FnZXM=');
@$core.Deprecated('Use responseDescriptor instead')
const Response$json = {
'1': 'Response',
@ -220,6 +190,13 @@ const Response_Plan$json = {
'5': 3,
'10': 'additionalPlusAccounts'
},
{
'1': 'additional_free_accounts',
'3': 6,
'4': 1,
'5': 3,
'10': 'additionalFreeAccounts'
},
{
'1': 'monthly_costs_cent',
'3': 7,
@ -706,77 +683,78 @@ const Response_TransactionTypes$json = {
final $typed_data.Uint8List responseDescriptor = $convert.base64Decode(
'CghSZXNwb25zZRIvCgJvaxgBIAEoCzIdLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuT2tIAF'
'ICb2sSKAoFZXJyb3IYAiABKA4yEC5lcnJvci5FcnJvckNvZGVIAFIFZXJyb3IaIwoNQXV0aGVu'
'dGljYXRlZBISCgRwbGFuGAEgASgJUgRwbGFuGuQDCgRQbGFuEhcKB3BsYW5faWQYASABKAlSBn'
'dGljYXRlZBISCgRwbGFuGAEgASgJUgRwbGFuGp4ECgRQbGFuEhcKB3BsYW5faWQYASABKAlSBn'
'BsYW5JZBIqChF1cGxvYWRfc2l6ZV9saW1pdBgCIAEoA1IPdXBsb2FkU2l6ZUxpbWl0EjcKGGRh'
'aWx5X21lZGlhX3VwbG9hZF9saW1pdBgDIAEoA1IVZGFpbHlNZWRpYVVwbG9hZExpbWl0ElQKKG'
'1heGltYWxfdXBsb2FkX3NpemVfb2Zfc2luZ2xlX21lZGlhX3NpemUYBCABKANSIm1heGltYWxV'
'cGxvYWRTaXplT2ZTaW5nbGVNZWRpYVNpemUSOAoYYWRkaXRpb25hbF9wbHVzX2FjY291bnRzGA'
'UgASgDUhZhZGRpdGlvbmFsUGx1c0FjY291bnRzEiwKEm1vbnRobHlfY29zdHNfY2VudBgHIAEo'
'A1IQbW9udGhseUNvc3RzQ2VudBIqChF5ZWFybHlfY29zdHNfY2VudBgIIAEoA1IPeWVhcmx5Q2'
'9zdHNDZW50EkAKHWFsbG93ZWRfdG9fc2VuZF90ZXh0X21lc3NhZ2VzGAkgASgIUhlhbGxvd2Vk'
'VG9TZW5kVGV4dE1lc3NhZ2VzEjIKFWlzX2FkZGl0aW9uYWxfYWNjb3VudBgKIAEoCFITaXNBZG'
'RpdGlvbmFsQWNjb3VudBo+CgVQbGFucxI1CgVwbGFucxgBIAMoCzIfLnNlcnZlcl90b19jbGll'
'bnQuUmVzcG9uc2UuUGxhblIFcGxhbnMaTQoRQWRkQWNjb3VudHNJbnZpdGUSFwoHcGxhbl9pZB'
'gBIAEoCVIGcGxhbklkEh8KC2ludml0ZV9jb2RlGAIgASgJUgppbnZpdGVDb2RlGlwKEkFkZEFj'
'Y291bnRzSW52aXRlcxJGCgdpbnZpdGVzGAEgAygLMiwuc2VydmVyX3RvX2NsaWVudC5SZXNwb2'
'5zZS5BZGRBY2NvdW50c0ludml0ZVIHaW52aXRlcxrFAQoLVHJhbnNhY3Rpb24SIwoNZGVwb3Np'
'dF9jZW50cxgBIAEoA1IMZGVwb3NpdENlbnRzElYKEHRyYW5zYWN0aW9uX3R5cGUYAiABKA4yKy'
'5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlRyYW5zYWN0aW9uVHlwZXNSD3RyYW5zYWN0aW9u'
'VHlwZRI5ChljcmVhdGVkX2F0X3VuaXhfdGltZXN0YW1wGAMgASgDUhZjcmVhdGVkQXRVbml4VG'
'ltZXN0YW1wGkUKEUFkZGl0aW9uYWxBY2NvdW50EhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIX'
'CgdwbGFuX2lkGAMgASgJUgZwbGFuSWQavgEKB1ZvdWNoZXISHQoKdm91Y2hlcl9pZBgBIAEoCV'
'IJdm91Y2hlcklkEh8KC3ZhbHVlX2NlbnRzGAIgASgDUgp2YWx1ZUNlbnRzEhoKCHJlZGVlbWVk'
'GAMgASgIUghyZWRlZW1lZBIcCglyZXF1ZXN0ZWQYBCABKAhSCXJlcXVlc3RlZBI5ChljcmVhdG'
'VkX2F0X3VuaXhfdGltZXN0YW1wGAUgASgDUhZjcmVhdGVkQXRVbml4VGltZXN0YW1wGkoKCFZv'
'dWNoZXJzEj4KCHZvdWNoZXJzGAEgAygLMiIuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5Wb3'
'VjaGVyUgh2b3VjaGVycxqXBQoMUGxhbkJhbGxhbmNlEkAKHXVzZWRfZGFpbHlfbWVkaWFfdXBs'
'b2FkX2xpbWl0GAEgASgDUhl1c2VkRGFpbHlNZWRpYVVwbG9hZExpbWl0Ej4KHHVzZWRfdXBsb2'
'FkX21lZGlhX3NpemVfbGltaXQYAiABKANSGHVzZWRVcGxvYWRNZWRpYVNpemVMaW1pdBIzChNw'
'YXltZW50X3BlcmlvZF9kYXlzGAMgASgDSABSEXBheW1lbnRQZXJpb2REYXlziAEBEksKIGxhc3'
'RfcGF5bWVudF9kb25lX3VuaXhfdGltZXN0YW1wGAQgASgDSAFSHGxhc3RQYXltZW50RG9uZVVu'
'aXhUaW1lc3RhbXCIAQESSgoMdHJhbnNhY3Rpb25zGAUgAygLMiYuc2VydmVyX3RvX2NsaWVudC'
'5SZXNwb25zZS5UcmFuc2FjdGlvblIMdHJhbnNhY3Rpb25zEl0KE2FkZGl0aW9uYWxfYWNjb3Vu'
'dHMYBiADKAsyLC5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkFkZGl0aW9uYWxBY2NvdW50Uh'
'JhZGRpdGlvbmFsQWNjb3VudHMSJgoMYXV0b19yZW5ld2FsGAcgASgISAJSC2F1dG9SZW5ld2Fs'
'iAEBEkIKG2FkZGl0aW9uYWxfYWNjb3VudF9vd25lcl9pZBgIIAEoA0gDUhhhZGRpdGlvbmFsQW'
'Njb3VudE93bmVySWSIAQFCFgoUX3BheW1lbnRfcGVyaW9kX2RheXNCIwohX2xhc3RfcGF5bWVu'
'dF9kb25lX3VuaXhfdGltZXN0YW1wQg8KDV9hdXRvX3JlbmV3YWxCHgocX2FkZGl0aW9uYWxfYW'
'Njb3VudF9vd25lcl9pZBpOCghMb2NhdGlvbhIWCgZjb3VudHkYASABKAlSBmNvdW50eRIWCgZy'
'ZWdpb24YAiABKAlSBnJlZ2lvbhISCgRjaXR5GAMgASgJUgRjaXR5GjAKBlByZUtleRIOCgJpZB'
'gBIAEoA1ICaWQSFgoGcHJla2V5GAIgASgMUgZwcmVrZXkalQEKDFNpZ25lZFByZUtleRIoChBz'
'aWduZWRfcHJla2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZXlJZBIjCg1zaWduZWRfcHJla2V5GA'
'IgASgMUgxzaWduZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaWduYXR1cmUYAyABKAxSFXNp'
'Z25lZFByZWtleVNpZ25hdHVyZRr2AwoIVXNlckRhdGESFwoHdXNlcl9pZBgBIAEoA1IGdXNlck'
'lkEjsKB3ByZWtleXMYAiADKAsyIS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlByZUtleVIH'
'cHJla2V5cxIfCgh1c2VybmFtZRgHIAEoDEgAUgh1c2VybmFtZYgBARIzChNwdWJsaWNfaWRlbn'
'RpdHlfa2V5GAMgASgMSAFSEXB1YmxpY0lkZW50aXR5S2V5iAEBEigKDXNpZ25lZF9wcmVrZXkY'
'BCABKAxIAlIMc2lnbmVkUHJla2V5iAEBEjsKF3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAUgAS'
'gMSANSFXNpZ25lZFByZWtleVNpZ25hdHVyZYgBARItChBzaWduZWRfcHJla2V5X2lkGAYgASgD'
'SARSDnNpZ25lZFByZWtleUlkiAEBEiwKD3JlZ2lzdHJhdGlvbl9pZBgIIAEoA0gFUg5yZWdpc3'
'RyYXRpb25JZIgBAUILCglfdXNlcm5hbWVCFgoUX3B1YmxpY19pZGVudGl0eV9rZXlCEAoOX3Np'
'Z25lZF9wcmVrZXlCGgoYX3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlQhMKEV9zaWduZWRfcHJla2'
'V5X2lkQhIKEF9yZWdpc3RyYXRpb25faWQaWQoLVXBsb2FkVG9rZW4SIQoMdXBsb2FkX3Rva2Vu'
'GAEgASgMUgt1cGxvYWRUb2tlbhInCg9kb3dubG9hZF90b2tlbnMYAiADKAxSDmRvd25sb2FkVG'
'9rZW5zGjkKDkRvd25sb2FkVG9rZW5zEicKD2Rvd25sb2FkX3Rva2VucxgBIAMoDFIOZG93bmxv'
'YWRUb2tlbnMaRQoLUHJvb2ZPZldvcmsSFgoGcHJlZml4GAEgASgJUgZwcmVmaXgSHgoKZGlmZm'
'ljdWx0eRgCIAEoA1IKZGlmZmljdWx0eRrDBwoCT2sSFAoETm9uZRgBIAEoCEgAUgROb25lEhgK'
'BnVzZXJpZBgCIAEoA0gAUgZ1c2VyaWQSJgoNYXV0aGNoYWxsZW5nZRgDIAEoDEgAUg1hdXRoY2'
'hhbGxlbmdlEkoKC3VwbG9hZHRva2VuGAQgASgLMiYuc2VydmVyX3RvX2NsaWVudC5SZXNwb25z'
'ZS5VcGxvYWRUb2tlbkgAUgt1cGxvYWR0b2tlbhJBCgh1c2VyZGF0YRgFIAEoCzIjLnNlcnZlcl'
'90b19jbGllbnQuUmVzcG9uc2UuVXNlckRhdGFIAFIIdXNlcmRhdGESHgoJYXV0aHRva2VuGAYg'
'ASgMSABSCWF1dGh0b2tlbhJBCghsb2NhdGlvbhgHIAEoCzIjLnNlcnZlcl90b19jbGllbnQuUm'
'VzcG9uc2UuTG9jYXRpb25IAFIIbG9jYXRpb24SUAoNYXV0aGVudGljYXRlZBgIIAEoCzIoLnNl'
'cnZlcl90b19jbGllbnQuUmVzcG9uc2UuQXV0aGVudGljYXRlZEgAUg1hdXRoZW50aWNhdGVkEj'
'gKBXBsYW5zGAkgASgLMiAuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5QbGFuc0gAUgVwbGFu'
'cxJNCgxwbGFuYmFsbGFuY2UYCiABKAsyJy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlBsYW'
'5CYWxsYW5jZUgAUgxwbGFuYmFsbGFuY2USQQoIdm91Y2hlcnMYCyABKAsyIy5zZXJ2ZXJfdG9f'
'Y2xpZW50LlJlc3BvbnNlLlZvdWNoZXJzSABSCHZvdWNoZXJzEl8KEmFkZGFjY291bnRzaW52aX'
'RlcxgMIAEoCzItLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuQWRkQWNjb3VudHNJbnZpdGVz'
'SABSEmFkZGFjY291bnRzaW52aXRlcxJTCg5kb3dubG9hZHRva2VucxgNIAEoCzIpLnNlcnZlcl'
'90b19jbGllbnQuUmVzcG9uc2UuRG93bmxvYWRUb2tlbnNIAFIOZG93bmxvYWR0b2tlbnMSTQoM'
'c2lnbmVkcHJla2V5GA4gASgLMicuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5TaWduZWRQcm'
'VLZXlIAFIMc2lnbmVkcHJla2V5EkoKC3Byb29mT2ZXb3JrGA8gASgLMiYuc2VydmVyX3RvX2Ns'
'aWVudC5SZXNwb25zZS5Qcm9vZk9mV29ya0gAUgtwcm9vZk9mV29ya0IECgJPayKWAQoQVHJhbn'
'NhY3Rpb25UeXBlcxIKCgZSZWZ1bmQQABITCg9Wb3VjaGVyUmVkZWVtZWQQARISCg5Wb3VjaGVy'
'Q3JlYXRlZBACEggKBENhc2gQAxIPCgtQbGFuVXBncmFkZRAEEgsKB1Vua25vd24QBRIUChBUaG'
'Fua3NGb3JUZXN0aW5nEAYSDwoLQXV0b1JlbmV3YWwQB0IKCghSZXNwb25zZQ==');
'UgASgDUhZhZGRpdGlvbmFsUGx1c0FjY291bnRzEjgKGGFkZGl0aW9uYWxfZnJlZV9hY2NvdW50'
'cxgGIAEoA1IWYWRkaXRpb25hbEZyZWVBY2NvdW50cxIsChJtb250aGx5X2Nvc3RzX2NlbnQYBy'
'ABKANSEG1vbnRobHlDb3N0c0NlbnQSKgoReWVhcmx5X2Nvc3RzX2NlbnQYCCABKANSD3llYXJs'
'eUNvc3RzQ2VudBJACh1hbGxvd2VkX3RvX3NlbmRfdGV4dF9tZXNzYWdlcxgJIAEoCFIZYWxsb3'
'dlZFRvU2VuZFRleHRNZXNzYWdlcxIyChVpc19hZGRpdGlvbmFsX2FjY291bnQYCiABKAhSE2lz'
'QWRkaXRpb25hbEFjY291bnQaPgoFUGxhbnMSNQoFcGxhbnMYASADKAsyHy5zZXJ2ZXJfdG9fY2'
'xpZW50LlJlc3BvbnNlLlBsYW5SBXBsYW5zGk0KEUFkZEFjY291bnRzSW52aXRlEhcKB3BsYW5f'
'aWQYASABKAlSBnBsYW5JZBIfCgtpbnZpdGVfY29kZRgCIAEoCVIKaW52aXRlQ29kZRpcChJBZG'
'RBY2NvdW50c0ludml0ZXMSRgoHaW52aXRlcxgBIAMoCzIsLnNlcnZlcl90b19jbGllbnQuUmVz'
'cG9uc2UuQWRkQWNjb3VudHNJbnZpdGVSB2ludml0ZXMaxQEKC1RyYW5zYWN0aW9uEiMKDWRlcG'
'9zaXRfY2VudHMYASABKANSDGRlcG9zaXRDZW50cxJWChB0cmFuc2FjdGlvbl90eXBlGAIgASgO'
'Misuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5UcmFuc2FjdGlvblR5cGVzUg90cmFuc2FjdG'
'lvblR5cGUSOQoZY3JlYXRlZF9hdF91bml4X3RpbWVzdGFtcBgDIAEoA1IWY3JlYXRlZEF0VW5p'
'eFRpbWVzdGFtcBpFChFBZGRpdGlvbmFsQWNjb3VudBIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySW'
'QSFwoHcGxhbl9pZBgDIAEoCVIGcGxhbklkGr4BCgdWb3VjaGVyEh0KCnZvdWNoZXJfaWQYASAB'
'KAlSCXZvdWNoZXJJZBIfCgt2YWx1ZV9jZW50cxgCIAEoA1IKdmFsdWVDZW50cxIaCghyZWRlZW'
'1lZBgDIAEoCFIIcmVkZWVtZWQSHAoJcmVxdWVzdGVkGAQgASgIUglyZXF1ZXN0ZWQSOQoZY3Jl'
'YXRlZF9hdF91bml4X3RpbWVzdGFtcBgFIAEoA1IWY3JlYXRlZEF0VW5peFRpbWVzdGFtcBpKCg'
'hWb3VjaGVycxI+Cgh2b3VjaGVycxgBIAMoCzIiLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2Uu'
'Vm91Y2hlclIIdm91Y2hlcnMalwUKDFBsYW5CYWxsYW5jZRJACh11c2VkX2RhaWx5X21lZGlhX3'
'VwbG9hZF9saW1pdBgBIAEoA1IZdXNlZERhaWx5TWVkaWFVcGxvYWRMaW1pdBI+Chx1c2VkX3Vw'
'bG9hZF9tZWRpYV9zaXplX2xpbWl0GAIgASgDUhh1c2VkVXBsb2FkTWVkaWFTaXplTGltaXQSMw'
'oTcGF5bWVudF9wZXJpb2RfZGF5cxgDIAEoA0gAUhFwYXltZW50UGVyaW9kRGF5c4gBARJLCiBs'
'YXN0X3BheW1lbnRfZG9uZV91bml4X3RpbWVzdGFtcBgEIAEoA0gBUhxsYXN0UGF5bWVudERvbm'
'VVbml4VGltZXN0YW1wiAEBEkoKDHRyYW5zYWN0aW9ucxgFIAMoCzImLnNlcnZlcl90b19jbGll'
'bnQuUmVzcG9uc2UuVHJhbnNhY3Rpb25SDHRyYW5zYWN0aW9ucxJdChNhZGRpdGlvbmFsX2FjY2'
'91bnRzGAYgAygLMiwuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5BZGRpdGlvbmFsQWNjb3Vu'
'dFISYWRkaXRpb25hbEFjY291bnRzEiYKDGF1dG9fcmVuZXdhbBgHIAEoCEgCUgthdXRvUmVuZX'
'dhbIgBARJCChthZGRpdGlvbmFsX2FjY291bnRfb3duZXJfaWQYCCABKANIA1IYYWRkaXRpb25h'
'bEFjY291bnRPd25lcklkiAEBQhYKFF9wYXltZW50X3BlcmlvZF9kYXlzQiMKIV9sYXN0X3BheW'
'1lbnRfZG9uZV91bml4X3RpbWVzdGFtcEIPCg1fYXV0b19yZW5ld2FsQh4KHF9hZGRpdGlvbmFs'
'X2FjY291bnRfb3duZXJfaWQaTgoITG9jYXRpb24SFgoGY291bnR5GAEgASgJUgZjb3VudHkSFg'
'oGcmVnaW9uGAIgASgJUgZyZWdpb24SEgoEY2l0eRgDIAEoCVIEY2l0eRowCgZQcmVLZXkSDgoC'
'aWQYASABKANSAmlkEhYKBnByZWtleRgCIAEoDFIGcHJla2V5GpUBCgxTaWduZWRQcmVLZXkSKA'
'oQc2lnbmVkX3ByZWtleV9pZBgBIAEoA1IOc2lnbmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtl'
'eRgCIAEoDFIMc2lnbmVkUHJla2V5EjYKF3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUh'
'VzaWduZWRQcmVrZXlTaWduYXR1cmUa9gMKCFVzZXJEYXRhEhcKB3VzZXJfaWQYASABKANSBnVz'
'ZXJJZBI7CgdwcmVrZXlzGAIgAygLMiEuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5QcmVLZX'
'lSB3ByZWtleXMSHwoIdXNlcm5hbWUYByABKAxIAFIIdXNlcm5hbWWIAQESMwoTcHVibGljX2lk'
'ZW50aXR5X2tleRgDIAEoDEgBUhFwdWJsaWNJZGVudGl0eUtleYgBARIoCg1zaWduZWRfcHJla2'
'V5GAQgASgMSAJSDHNpZ25lZFByZWtleYgBARI7ChdzaWduZWRfcHJla2V5X3NpZ25hdHVyZRgF'
'IAEoDEgDUhVzaWduZWRQcmVrZXlTaWduYXR1cmWIAQESLQoQc2lnbmVkX3ByZWtleV9pZBgGIA'
'EoA0gEUg5zaWduZWRQcmVrZXlJZIgBARIsCg9yZWdpc3RyYXRpb25faWQYCCABKANIBVIOcmVn'
'aXN0cmF0aW9uSWSIAQFCCwoJX3VzZXJuYW1lQhYKFF9wdWJsaWNfaWRlbnRpdHlfa2V5QhAKDl'
'9zaWduZWRfcHJla2V5QhoKGF9zaWduZWRfcHJla2V5X3NpZ25hdHVyZUITChFfc2lnbmVkX3By'
'ZWtleV9pZEISChBfcmVnaXN0cmF0aW9uX2lkGlkKC1VwbG9hZFRva2VuEiEKDHVwbG9hZF90b2'
'tlbhgBIAEoDFILdXBsb2FkVG9rZW4SJwoPZG93bmxvYWRfdG9rZW5zGAIgAygMUg5kb3dubG9h'
'ZFRva2Vucxo5Cg5Eb3dubG9hZFRva2VucxInCg9kb3dubG9hZF90b2tlbnMYASADKAxSDmRvd2'
'5sb2FkVG9rZW5zGkUKC1Byb29mT2ZXb3JrEhYKBnByZWZpeBgBIAEoCVIGcHJlZml4Eh4KCmRp'
'ZmZpY3VsdHkYAiABKANSCmRpZmZpY3VsdHkawwcKAk9rEhQKBE5vbmUYASABKAhIAFIETm9uZR'
'IYCgZ1c2VyaWQYAiABKANIAFIGdXNlcmlkEiYKDWF1dGhjaGFsbGVuZ2UYAyABKAxIAFINYXV0'
'aGNoYWxsZW5nZRJKCgt1cGxvYWR0b2tlbhgEIAEoCzImLnNlcnZlcl90b19jbGllbnQuUmVzcG'
'9uc2UuVXBsb2FkVG9rZW5IAFILdXBsb2FkdG9rZW4SQQoIdXNlcmRhdGEYBSABKAsyIy5zZXJ2'
'ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlVzZXJEYXRhSABSCHVzZXJkYXRhEh4KCWF1dGh0b2tlbh'
'gGIAEoDEgAUglhdXRodG9rZW4SQQoIbG9jYXRpb24YByABKAsyIy5zZXJ2ZXJfdG9fY2xpZW50'
'LlJlc3BvbnNlLkxvY2F0aW9uSABSCGxvY2F0aW9uElAKDWF1dGhlbnRpY2F0ZWQYCCABKAsyKC'
'5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkF1dGhlbnRpY2F0ZWRIAFINYXV0aGVudGljYXRl'
'ZBI4CgVwbGFucxgJIAEoCzIgLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUGxhbnNIAFIFcG'
'xhbnMSTQoMcGxhbmJhbGxhbmNlGAogASgLMicuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5Q'
'bGFuQmFsbGFuY2VIAFIMcGxhbmJhbGxhbmNlEkEKCHZvdWNoZXJzGAsgASgLMiMuc2VydmVyX3'
'RvX2NsaWVudC5SZXNwb25zZS5Wb3VjaGVyc0gAUgh2b3VjaGVycxJfChJhZGRhY2NvdW50c2lu'
'dml0ZXMYDCABKAsyLS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkFkZEFjY291bnRzSW52aX'
'Rlc0gAUhJhZGRhY2NvdW50c2ludml0ZXMSUwoOZG93bmxvYWR0b2tlbnMYDSABKAsyKS5zZXJ2'
'ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkRvd25sb2FkVG9rZW5zSABSDmRvd25sb2FkdG9rZW5zEk'
'0KDHNpZ25lZHByZWtleRgOIAEoCzInLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuU2lnbmVk'
'UHJlS2V5SABSDHNpZ25lZHByZWtleRJKCgtwcm9vZk9mV29yaxgPIAEoCzImLnNlcnZlcl90b1'
'9jbGllbnQuUmVzcG9uc2UuUHJvb2ZPZldvcmtIAFILcHJvb2ZPZldvcmtCBAoCT2silgEKEFRy'
'YW5zYWN0aW9uVHlwZXMSCgoGUmVmdW5kEAASEwoPVm91Y2hlclJlZGVlbWVkEAESEgoOVm91Y2'
'hlckNyZWF0ZWQQAhIICgRDYXNoEAMSDwoLUGxhblVwZ3JhZGUQBBILCgdVbmtub3duEAUSFAoQ'
'VGhhbmtzRm9yVGVzdGluZxAGEg8KC0F1dG9SZW5ld2FsEAdCCgoIUmVzcG9uc2U=');

View file

@ -1,13 +0,0 @@
import 'package:in_app_purchase/in_app_purchase.dart';
enum ProductStatus { purchasable, purchased, pending }
class PurchasableProduct {
PurchasableProduct(this.productDetails) : status = ProductStatus.purchasable;
String get id => productDetails.id;
String get title => productDetails.title;
String get description => productDetails.description;
String get price => productDetails.price;
ProductStatus status;
ProductDetails productDetails;
}

View file

@ -1,10 +1,17 @@
import 'package:flutter/foundation.dart';
import 'package:twonly/src/services/subscription.service.dart';
class CustomChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
bool _isConnected = false;
bool get isConnected => _isConnected;
SubscriptionPlan plan = SubscriptionPlan.Free;
Future<void> updateConnectionState(bool update) async {
_isConnected = update;
notifyListeners();
}
Future<void> updatePlan(SubscriptionPlan newPlan) async {
plan = newPlan;
notifyListeners();
}
}

View file

@ -1,237 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/constants/subscription.keys.dart';
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
import 'package:twonly/src/model/purchases/purchasable_product.dart';
import 'package:twonly/src/services/subscription.service.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/storage.dart';
import 'package:url_launcher/url_launcher.dart';
// Gives the option to override in tests.
class IAPConnection {
static InAppPurchase? _instance;
static set instance(InAppPurchase value) {
_instance = value;
}
static InAppPurchase get instance {
_instance ??= InAppPurchase.instance;
return _instance!;
}
}
enum StoreState { loading, available, notAvailable }
Timer? globalForceIpaCheck;
class PurchasesProvider with ChangeNotifier, DiagnosticableTreeMixin {
PurchasesProvider() {
final purchaseUpdated = iapConnection.purchaseStream;
_subscription = purchaseUpdated.listen(
_onPurchaseUpdate,
onDone: _updateStreamOnDone,
onError: _updateStreamOnError,
);
loadPurchases();
}
SubscriptionPlan plan = SubscriptionPlan.Free;
StoreState storeState = StoreState.loading;
List<PurchasableProduct> products = [];
late StreamSubscription<List<PurchaseDetails>> _subscription;
final InAppPurchase iapConnection = IAPConnection.instance;
bool _userTriggeredBuyButton = false;
void updatePlan(SubscriptionPlan newPlan) {
plan = newPlan;
notifyListeners();
}
Future<void> loadPurchases() async {
final available = await iapConnection.isAvailable();
if (!available) {
storeState = StoreState.notAvailable;
Log.error('Store is not available');
notifyListeners();
return;
}
const ids = <String>{
SubscriptionKeys.proMonthly,
SubscriptionKeys.proYearly,
SubscriptionKeys.familyYearly,
};
final response = await iapConnection.queryProductDetails(ids);
if (response.notFoundIDs.isNotEmpty) {
Log.error(response.notFoundIDs);
}
products = response.productDetails.map(PurchasableProduct.new).toList();
if (products.isEmpty) {
Log.error('Could not load any products from the store!');
}
storeState = StoreState.available;
notifyListeners();
final user = await getUser();
if (user != null && isPayingUser(planFromString(user.subscriptionPlan))) {
Log.info('Started IPA timer for verification.');
globalForceIpaCheck = Timer(const Duration(seconds: 5), () async {
Log.warn('Force Ipa check was not stopped. Requesting forced check...');
await apiService.forceIpaCheck();
});
}
await iapConnection.restorePurchases();
}
Future<void> buy(PurchasableProduct product) async {
final purchaseParam = PurchaseParam(productDetails: product.productDetails);
switch (product.id) {
// case storeKeyConsumable:
// await iapConnection.buyConsumable(purchaseParam: purchaseParam);
case SubscriptionKeys.proMonthly:
case SubscriptionKeys.proYearly:
case SubscriptionKeys.familyYearly:
_userTriggeredBuyButton = true;
Log.info('User wants to buy ${product.id}');
await iapConnection.buyNonConsumable(purchaseParam: purchaseParam);
default:
throw ArgumentError.value(
product.productDetails,
'${product.id} is not a known product',
);
}
}
Future<void> _onPurchaseUpdate(
List<PurchaseDetails> purchaseDetailsList,
) async {
for (final purchaseDetails in purchaseDetailsList) {
await _handlePurchase(purchaseDetails);
}
notifyListeners();
}
Future<bool> _verifyPurchase(PurchaseDetails purchaseDetails) async {
Log.info('verifying _verifyPurchase');
if (Platform.isIOS) {
try {
var b64Data = purchaseDetails.verificationData.serverVerificationData
.split('.')[1];
final paddingNeeded = (4 - (b64Data.length % 4)) % 4;
b64Data += '=' * paddingNeeded;
final jsonData = base64Decode(b64Data);
final data = jsonDecode(utf8.decode(jsonData)) as Map<String, dynamic>;
final expiresDate = data['expiresDate'] as int;
final dt =
DateTime.fromMillisecondsSinceEpoch(expiresDate, isUtc: true);
if (dt.isBefore(DateTime.now())) {
Log.warn('ExpiresDate is in the past: $dt');
if (_userTriggeredBuyButton && Platform.isIOS) {
await launchUrl(
Uri.parse('https://apps.apple.com/account/subscriptions'),
mode: LaunchMode.externalApplication,
);
}
return false;
}
} catch (e) {
Log.error(e);
}
}
if (kDebugMode) {
Log.info(purchaseDetails.productID);
Log.info(purchaseDetails.verificationData.source);
}
final res = await apiService.ipaPurchase(
purchaseDetails.productID,
purchaseDetails.verificationData.source,
purchaseDetails.verificationData.serverVerificationData,
);
// plan is updated in the apiProvider, as the server updates its states and responses with
// an ok authenticated which is processed in the apiProvider...
if (res.isSuccess) {
if (Platform.isAndroid) {
await updateUserdata((u) {
u.subscriptionPlanIdStore = purchaseDetails.productID;
return u;
});
}
}
if (res.isError) {
if (res.error == ErrorCode.IPAPaymentExpired &&
_userTriggeredBuyButton &&
Platform.isIOS) {
await launchUrl(
Uri.parse('https://apps.apple.com/account/subscriptions'),
mode: LaunchMode.externalApplication,
);
}
}
return res.isSuccess;
}
Future<void> _handlePurchase(PurchaseDetails purchaseDetails) async {
Log.info(
'_handlePurchase: ${purchaseDetails.productID}, ${purchaseDetails.status}',
);
if (purchaseDetails.status == PurchaseStatus.purchased ||
(purchaseDetails.status == PurchaseStatus.restored &&
_userTriggeredBuyButton)) {
await _verifyPurchase(purchaseDetails);
}
if (purchaseDetails.status == PurchaseStatus.restored &&
purchaseDetails.error == null) {
globalForceIpaCheck?.cancel();
final user = await getUser();
if (user != null &&
(user.subscriptionPlan != SubscriptionPlan.Family.name &&
user.subscriptionPlan != SubscriptionPlan.Pro.name)) {
for (var i = 0; i < 100; i++) {
if (apiService.isAuthenticated) {
Log.info(
'current user does not have a sub: ${purchaseDetails.productID}',
);
await _verifyPurchase(purchaseDetails);
break;
}
await Future.delayed(const Duration(seconds: 1));
}
}
}
if (purchaseDetails.status == PurchaseStatus.error) {
await iapConnection.restorePurchases();
}
if (purchaseDetails.pendingCompletePurchase) {
await iapConnection.completePurchase(purchaseDetails);
}
}
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
void _updateStreamOnDone() {
_subscription.cancel();
}
void _updateStreamOnError(dynamic error) {
// Handle error here
}
}

View file

@ -7,7 +7,6 @@ import 'dart:io';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:clock/clock.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:drift/drift.dart';
import 'package:fixnum/fixnum.dart';
@ -99,13 +98,12 @@ class ApiService {
unawaited(signalHandleNewServerConnection());
unawaited(fetchGroupStatesForUnjoinedGroups());
unawaited(fetchMissingGroupPublicKey());
unawaited(checkForDeletedUsernames());
}
}
Future<void> onConnected() async {
await authenticate();
_reconnectionDelay = 1;
_reconnectionDelay = 5;
globalCallbackConnectionState(isConnected: true);
}
@ -114,21 +112,16 @@ class ApiService {
isAuthenticated = false;
globalCallbackConnectionState(isConnected: false);
await twonlyDB.mediaFilesDao.resetPendingDownloadState();
await startReconnectionTimer();
}
Future<void> startReconnectionTimer() async {
if (reconnectionTimer?.isActive ?? false) {
return;
}
reconnectionTimer?.cancel();
Log.info('Starting reconnection timer with $_reconnectionDelay s delay');
reconnectionTimer = Timer(Duration(seconds: _reconnectionDelay), () async {
Log.info('Reconnection timer triggered');
reconnectionTimer ??=
Timer(Duration(seconds: _reconnectionDelay), () async {
reconnectionTimer = null;
await connect();
await connect(force: true);
});
_reconnectionDelay = 3;
_reconnectionDelay += 5;
}
Future<void> close(Function callback) async {
@ -150,13 +143,18 @@ class ApiService {
.onConnectivityChanged
.listen((List<ConnectivityResult> result) async {
if (!result.contains(ConnectivityResult.none)) {
await connect();
await connect(force: true);
}
// Received changes in available connectivity types!
});
}
Future<bool> connect() async {
Future<bool> connect({bool force = false}) async {
if (reconnectionTimer != null && !force) {
return false;
}
reconnectionTimer?.cancel();
reconnectionTimer = null;
return lockConnecting.protect<bool>(() async {
if (_channel != null) {
return true;
@ -183,7 +181,6 @@ class ApiService {
Future<void> _onDone() async {
Log.info('websocket closed without error');
_reconnectionDelay = 60 * 2; // the server closed the connection...
await onClosed();
}
@ -207,7 +204,7 @@ class ApiService {
}
Future<server.ServerToClient?> _waitForResponse(Int64 seq) async {
final startTime = clock.now();
final startTime = DateTime.now();
const timeout = Duration(seconds: 60);
@ -217,7 +214,7 @@ class ApiService {
messagesV0.remove(seq);
return tmp;
}
if (clock.now().difference(startTime) > timeout) {
if (DateTime.now().difference(startTime) > timeout) {
Log.warn('Timeout for message $seq');
return null;
}
@ -293,7 +290,6 @@ class ApiService {
if (_channel == null) {
Log.warn('sending request while api is not connected');
if (!await connect()) {
Log.warn('could not connected again');
return Result.error(ErrorCode.InternalError);
}
if (_channel == null) {
@ -304,17 +300,6 @@ class ApiService {
_channel!.sink.add(requestBytes);
final res = asResult(await _waitForResponse(seq));
if (res.isSuccess) {
final ok = res.value as server.Response_Ok;
if (ok.hasAuthenticated()) {
final authenticated = ok.authenticated;
await updateUserdata((user) {
user.subscriptionPlan = authenticated.plan;
return user;
});
globalCallbackUpdatePlan(planFromString(authenticated.plan));
}
}
if (res.isError) {
Log.warn('Got error from server: ${res.error}');
if (res.error == ErrorCode.AppVersionOutdated) {
@ -354,8 +339,9 @@ class ApiService {
if (contact != null) {
await twonlyDB.contactsDao.updateContact(
contactId,
const ContactsCompanion(
accountDeleted: Value(true),
ContactsCompanion(
accountDeleted: const Value(true),
username: Value('${contact.username} (${contact.userId})'),
),
);
}
@ -389,6 +375,15 @@ class ApiService {
final result = await sendRequestSync(req, authenticated: false);
if (result.isSuccess) {
final ok = result.value as server.Response_Ok;
if (ok.hasAuthenticated()) {
final authenticated = ok.authenticated;
await updateUserdata((user) {
user.subscriptionPlan = authenticated.plan;
return user;
});
globalCallbackUpdatePlan(planFromString(authenticated.plan));
}
Log.info('websocket is authenticated');
unawaited(onAuthenticated());
return true;
@ -496,20 +491,6 @@ class ApiService {
return sendRequestSync(req);
}
Future<void> checkForDeletedUsernames() async {
final users = await twonlyDB.contactsDao
.getContactsByUsername('[deleted]', username2: '[Unknown]');
for (final user in users) {
final userData = await getUserById(user.userId);
if (userData != null) {
await twonlyDB.contactsDao.updateContact(
user.userId,
ContactsCompanion(username: Value(utf8.decode(userData.username))),
);
}
}
}
Future<Response_UserData?> getUserById(int userId) async {
final get = ApplicationData_GetUserById()..userId = Int64(userId);
final appData = ApplicationData()..getUserById = get;
@ -595,6 +576,20 @@ class ApiService {
return null;
}
Future<List<Response_AddAccountsInvite>?> getAdditionalUserInvites() async {
final get = ApplicationData_GetAddAccountsInvites();
final appData = ApplicationData()..getAddaccountsInvites = get;
final req = createClientToServerFromApplicationData(appData);
final res = await sendRequestSync(req);
if (res.isSuccess) {
final ok = res.value as server.Response_Ok;
if (ok.hasAddaccountsinvites()) {
return ok.addaccountsinvites.invites;
}
}
return null;
}
Future<Result> updatePlanOptions(bool autoRenewal) async {
final get = ApplicationData_UpdatePlanOptions()..autoRenewal = autoRenewal;
final appData = ApplicationData()..updatePlanOptions = get;
@ -609,13 +604,6 @@ class ApiService {
return sendRequestSync(req, contactId: userId.toInt());
}
Future<Result> addAdditionalUser(Int64 userId) async {
final get = ApplicationData_AddAdditionalUser()..userId = userId;
final appData = ApplicationData()..addAdditionalUser = get;
final req = createClientToServerFromApplicationData(appData);
return sendRequestSync(req, contactId: userId.toInt());
}
Future<Result> buyVoucher(int valueInCents) async {
final get = ApplicationData_CreateVoucher()..valueCents = valueInCents;
final appData = ApplicationData()..createVoucher = get;
@ -674,22 +662,6 @@ class ApiService {
return sendRequestSync(req);
}
Future<Result> ipaPurchase(
String productId,
String source,
String verificationData,
) async {
final appData = ApplicationData(
ipaPurchase: ApplicationData_IPAPurchase(
productId: productId,
source: source,
verificationData: verificationData,
),
);
final req = createClientToServerFromApplicationData(appData);
return sendRequestSync(req);
}
Future<Result> changeUsername(String username) async {
final get = ApplicationData_ChangeUsername()..username = username;
final appData = ApplicationData()..changeUsername = get;
@ -697,15 +669,6 @@ class ApiService {
return sendRequestSync(req);
}
Future<Result> forceIpaCheck() async {
final req = createClientToServerFromApplicationData(
ApplicationData(
ipaForceCheck: ApplicationData_IPAForceCheck(),
),
);
return sendRequestSync(req);
}
Future<Result> updateSignedPreKey(
int signedPreKeyId,
Uint8List signedPreKey,
@ -759,28 +722,6 @@ class ApiService {
return null;
}
Future<Response_PlanBallance?> loadPlanBalance({bool useCache = true}) async {
final ballance = await getPlanBallance();
if (ballance != null) {
await updateUserdata((u) {
u.lastPlanBallance = ballance.writeToJson();
return u;
});
return ballance;
}
final user = await getUser();
if (user != null && user.lastPlanBallance != null && useCache) {
try {
return Response_PlanBallance.fromJson(
user.lastPlanBallance!,
);
} catch (e) {
Log.error('from json: $e');
}
}
return ballance;
}
Future<Result> sendTextMessage(
int target,
Uint8List msg,

View file

@ -93,7 +93,7 @@ Future<void> handleContactUpdate(
switch (contactUpdate.type) {
case EncryptedContent_ContactUpdate_Type.REQUEST:
Log.info('Got a contact update request from $fromUserId');
await sendContactMyProfileData(fromUserId);
await notifyContactsAboutProfileChange(onlyToContact: fromUserId);
case EncryptedContent_ContactUpdate_Type.UPDATE:
Log.info('Got a contact update $fromUserId');

View file

@ -117,10 +117,6 @@ Future<void> handleGroupUpdate(
final group = (await twonlyDB.groupsDao.getGroup(groupId))!;
if (!group.isDirectChat) {
unawaited(fetchGroupState(group));
}
switch (actionType) {
case GroupActionType.updatedGroupName:
await twonlyDB.groupsDao.insertGroupAction(
@ -177,6 +173,10 @@ Future<void> handleGroupUpdate(
case GroupActionType.createdGroup:
break;
}
if (!group.isDirectChat) {
unawaited(fetchGroupState(group));
}
}
Future<bool> handleGroupJoin(

View file

@ -7,7 +7,6 @@ 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/mediafiles/download.service.dart';
import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/services/flame.service.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/utils/log.dart';
@ -115,7 +114,7 @@ Future<void> handleMedia(
await twonlyDB.groupsDao
.increaseLastMessageExchange(groupId, fromTimestamp(media.timestamp));
Log.info('Inserted a new media message with ID: ${message.messageId}');
await incFlameCounter(
await twonlyDB.groupsDao.incFlameCounter(
message.groupId,
true,
fromTimestamp(media.timestamp),

View file

@ -1,11 +1,10 @@
import 'dart:async';
import 'package:clock/clock.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/utils/log.dart';
DateTime lastPushKeyRequest = clock.now().subtract(const Duration(hours: 1));
DateTime lastPushKeyRequest = DateTime.now().subtract(const Duration(hours: 1));
Future<void> handlePushKey(
int contactId,
@ -15,8 +14,8 @@ Future<void> handlePushKey(
case EncryptedContent_PushKeys_Type.REQUEST:
Log.info('Got a pushkey request from $contactId');
if (lastPushKeyRequest
.isBefore(clock.now().subtract(const Duration(seconds: 60)))) {
lastPushKeyRequest = clock.now();
.isBefore(DateTime.now().subtract(const Duration(seconds: 60)))) {
lastPushKeyRequest = DateTime.now();
unawaited(setupNotificationWithUsers(forceContact: contactId));
}

View file

@ -1,4 +1,3 @@
import 'package:clock/clock.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/utils/log.dart';
@ -18,6 +17,7 @@ Future<void> handleReaction(
);
if (!reaction.remove) {
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now());
await twonlyDB.groupsDao
.increaseLastMessageExchange(groupId, DateTime.now());
}
}

View file

@ -1,4 +1,3 @@
import 'package:clock/clock.dart';
import 'package:drift/drift.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
@ -27,7 +26,7 @@ Future<void> handleTextMessage(
textMessage.hasQuoteMessageId() ? textMessage.quoteMessageId : null,
),
createdAt: Value(fromTimestamp(textMessage.timestamp)),
ackByServer: Value(clock.now()),
ackByServer: Value(DateTime.now()),
),
);
await twonlyDB.groupsDao.increaseLastMessageExchange(

Some files were not shown because too many files have changed in this diff Show more