mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 22:28:41 +00:00
Compare commits
No commits in common. "main" and "v0.0.80" have entirely different histories.
129 changed files with 2163 additions and 10913 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
|
@ -2,7 +2,3 @@
|
||||||
[submodule "dependencies"]
|
[submodule "dependencies"]
|
||||||
path = dependencies
|
path = dependencies
|
||||||
url = https://github.com/twonlyapp/twonly-app-dependencies.git
|
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
|
|
||||||
|
|
|
||||||
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -1,22 +1,5 @@
|
||||||
# Changelog
|
# 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
|
## 0.0.80
|
||||||
|
|
||||||
- Share images/videos directly from other applications
|
- Share images/videos directly from other applications
|
||||||
|
|
|
||||||
30
README.md
30
README.md
|
|
@ -4,35 +4,20 @@
|
||||||
|
|
||||||
This repository contains the complete source code of the [twonly](https://twonly.eu) apps.
|
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"
|
<img alt="Get it on Testflight button" src="https://twonly.eu/assets/buttons/get-it-on-testflight.png"
|
||||||
width="100px" />
|
width="100px" />
|
||||||
</a> -->
|
</a>
|
||||||
<div class="my-5 space-x-4">
|
<a href="https://releases.twonly.eu/fdroid/repo/">
|
||||||
<div class="flex gap-5 items-center justify-center">
|
<img alt="Get it on F-Droid button" src="https://twonly.eu/assets/buttons/get-it-on-f-droid.webp" width="100px" />
|
||||||
<a href="https://apps.apple.com/de/app/twonly/id6743774441">
|
</a>
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Offer a Snapchat™ like experience
|
- Offer a Snapchat™ like experience
|
||||||
- End-to-End encryption using the [Signal Protocol](https://de.wikipedia.org/wiki/Signal-Protokoll)
|
- 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
|
- No email or phone number required to register
|
||||||
- Privacy friendly - Everything is stored on the device
|
- Privacy friendly - Everything is stored on the device
|
||||||
- The backend is hosted exclusively in Europe
|
- 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
|
- Implementing [Sealed Sender](https://signal.org/blog/sealed-sender/) to minimize metadata
|
||||||
|
|
||||||
## Security Issues
|
## Security Issues
|
||||||
|
|
||||||
If you discover a security issue in twonly, please adhere to the coordinated vulnerability disclosure model. Please send
|
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
|
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 :/
|
guarantee a bounty currently :/
|
||||||
|
|
|
||||||
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,5 +1,5 @@
|
||||||
arb-dir: lib/src/localization/translations
|
arb-dir: lib/src/localization
|
||||||
template-arb-file: en.arb
|
template-arb-file: app_en.arb
|
||||||
output-localization-file: app_localizations.dart
|
output-localization-file: app_localizations.dart
|
||||||
untranslated-messages-file: build/l10n.log
|
untranslated-messages-file: build/l10n.log
|
||||||
output-dir: lib/src/localization/generated
|
output-dir: lib/src/localization/generated
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
|
||||||
.watchSingleOrNull();
|
.watchSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Contact>> getAllContacts() {
|
Future<List<Contact>> getAllNotBlockedContacts() {
|
||||||
return select(contacts).get();
|
return select(contacts).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'package:hashlib/random.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/tables/groups.table.dart';
|
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/flame.service.dart';
|
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
|
|
@ -279,6 +278,89 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
||||||
return query.map((row) => row.readTable(groups)).getSingleOrNull();
|
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() {
|
Stream<int> watchSumTotalMediaCounter() {
|
||||||
final query = selectOnly(groups)
|
final query = selectOnly(groups)
|
||||||
..addColumns([groups.totalMediaCounter.sum()]);
|
..addColumns([groups.totalMediaCounter.sum()]);
|
||||||
|
|
@ -301,3 +383,23 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
||||||
.write(GroupsCompanion(lastMessageExchange: Value(newLastMessage)));
|
.write(GroupsCompanion(lastMessageExchange: Value(newLastMessage)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getFlameCounterFromGroup(Group? group) {
|
||||||
|
if (group == null) return 0;
|
||||||
|
if (group.lastMessageSend == null ||
|
||||||
|
group.lastMessageReceived == null ||
|
||||||
|
group.lastFlameCounterChange == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final now = DateTime.now();
|
||||||
|
final startOfToday = DateTime(now.year, now.month, now.day);
|
||||||
|
final twoDaysAgo = startOfToday.subtract(const Duration(days: 2));
|
||||||
|
final oneDayAgo = startOfToday.subtract(const Duration(days: 1));
|
||||||
|
if (group.lastMessageSend!.isAfter(twoDaysAgo) &&
|
||||||
|
group.lastMessageReceived!.isAfter(twoDaysAgo) ||
|
||||||
|
group.lastFlameCounterChange!.isAfter(oneDayAgo)) {
|
||||||
|
return group.flameCounter + 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,14 +100,6 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<MediaFile>> getAllNonHashedStoredMediaFiles() async {
|
|
||||||
return (select(mediaFiles)
|
|
||||||
..where(
|
|
||||||
(t) => t.stored.equals(true) & t.storedFileHash.isNull(),
|
|
||||||
))
|
|
||||||
.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<MediaFile>> getAllMediaFilesPendingUpload() async {
|
Future<List<MediaFile>> getAllMediaFilesPendingUpload() async {
|
||||||
return (select(mediaFiles)
|
return (select(mediaFiles)
|
||||||
..where(
|
..where(
|
||||||
|
|
@ -119,10 +111,7 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<MediaFile>> watchAllStoredMediaFiles() {
|
Stream<List<MediaFile>> watchAllStoredMediaFiles() {
|
||||||
final query = (select(mediaFiles)..where((t) => t.stored.equals(true)))
|
return (select(mediaFiles)..where((t) => t.stored.equals(true))).watch();
|
||||||
.join([])
|
|
||||||
..groupBy([mediaFiles.storedFileHash]);
|
|
||||||
return query.map((row) => row.readTable(mediaFiles)).watch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<MediaFile>> watchNewestMediaFiles() {
|
Stream<List<MediaFile>> watchNewestMediaFiles() {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:hashlib/random.dart';
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -111,11 +110,11 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
final allGroups = await select(groups).get();
|
final allGroups = await select(groups).get();
|
||||||
|
|
||||||
for (final group in allGroups) {
|
for (final group in allGroups) {
|
||||||
final deletionTime = clock.now().subtract(
|
final deletionTime = DateTime.now().subtract(
|
||||||
Duration(
|
Duration(
|
||||||
milliseconds: group.deleteMessagesAfterMilliseconds,
|
milliseconds: group.deleteMessagesAfterMilliseconds,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await (delete(messages)
|
await (delete(messages)
|
||||||
..where(
|
..where(
|
||||||
(m) =>
|
(m) =>
|
||||||
|
|
@ -151,7 +150,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
// t.messageOtherId.isNull() &
|
// t.messageOtherId.isNull() &
|
||||||
// t.errorWhileSending.equals(false) &
|
// t.errorWhileSending.equals(false) &
|
||||||
// t.sendAt.isBiggerThanValue(
|
// t.sendAt.isBiggerThanValue(
|
||||||
// clock.now().subtract(const Duration(minutes: 10)),
|
// DateTime.now().subtract(const Duration(minutes: 10)),
|
||||||
// ),
|
// ),
|
||||||
// ))
|
// ))
|
||||||
// .get();
|
// .get();
|
||||||
|
|
@ -179,7 +178,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Future<void> openedAllTextMessages(String groupId) {
|
Future<void> openedAllTextMessages(String groupId) {
|
||||||
final updates = MessagesCompanion(openedAt: Value(clock.now()));
|
final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
|
||||||
return (update(messages)
|
return (update(messages)
|
||||||
..where(
|
..where(
|
||||||
(t) =>
|
(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
|
// Directly show as message opened as soon as one person has opened it
|
||||||
final openedByAll =
|
final openedByAll =
|
||||||
await haveAllMembers(messageId, MessageActionType.openedAt)
|
await haveAllMembers(messageId, MessageActionType.openedAt)
|
||||||
? clock.now()
|
? DateTime.now()
|
||||||
: null;
|
: null;
|
||||||
await twonlyDB.messagesDao.updateMessageId(
|
await twonlyDB.messagesDao.updateMessageId(
|
||||||
messageId,
|
messageId,
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
openedAt: Value(clock.now()),
|
openedAt: Value(DateTime.now()),
|
||||||
openedByAll: Value(openedByAll),
|
openedByAll: Value(openedByAll),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -299,7 +298,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
);
|
);
|
||||||
await twonlyDB.messagesDao.updateMessageId(
|
await twonlyDB.messagesDao.updateMessageId(
|
||||||
messageId,
|
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(
|
await twonlyDB.groupsDao.updateGroup(
|
||||||
message.groupId.value,
|
message.groupId.value,
|
||||||
GroupsCompanion(
|
GroupsCompanion(
|
||||||
lastMessageExchange: Value(clock.now()),
|
lastMessageExchange: Value(DateTime.now()),
|
||||||
archived: const Value(false),
|
archived: const Value(false),
|
||||||
deletedContent: const Value(false),
|
deletedContent: const Value(false),
|
||||||
),
|
),
|
||||||
|
|
@ -390,7 +389,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
message.groupId.value,
|
message.groupId.value,
|
||||||
message.senderId.value!,
|
message.senderId.value!,
|
||||||
GroupMembersCompanion(
|
GroupMembersCompanion(
|
||||||
lastMessage: Value(clock.now()),
|
lastMessage: Value(DateTime.now()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:hashlib/random.dart';
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.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 {
|
Future<List<Receipt>> getReceiptsNotAckByServer() async {
|
||||||
final markedRetriesTime = clock.now().subtract(
|
|
||||||
const Duration(
|
|
||||||
// give the server time to transmit all messages to the client
|
|
||||||
seconds: 20,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return (select(receipts)
|
return (select(receipts)
|
||||||
..where(
|
..where(
|
||||||
(t) =>
|
(t) => t.ackByServerAt.isNull(),
|
||||||
t.ackByServerAt.isNull() |
|
|
||||||
t.markForRetry.isSmallerThanValue(markedRetriesTime),
|
|
||||||
))
|
))
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
@ -109,14 +100,6 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
||||||
.write(updates);
|
.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 {
|
Future<bool> isDuplicated(String receiptId) async {
|
||||||
return await (select(receivedReceipts)
|
return await (select(receivedReceipts)
|
||||||
..where((t) => t.receiptId.equals(receiptId)))
|
..where((t) => t.receiptId.equals(receiptId)))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/tables/signal_contact_prekey.table.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)
|
await (delete(signalContactPreKeys)
|
||||||
..where(
|
..where(
|
||||||
(t) => (t.createdAt.isSmallerThanValue(
|
(t) => (t.createdAt.isSmallerThanValue(
|
||||||
clock.now().subtract(
|
DateTime.now().subtract(
|
||||||
const Duration(days: 100),
|
const Duration(days: 100),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
.go();
|
.go();
|
||||||
|
|
@ -118,9 +117,9 @@ class SignalDao extends DatabaseAccessor<TwonlyDB> with _$SignalDaoMixin {
|
||||||
await (delete(twonlyDB.signalPreKeyStores)
|
await (delete(twonlyDB.signalPreKeyStores)
|
||||||
..where(
|
..where(
|
||||||
(t) => (t.createdAt.isSmallerThanValue(
|
(t) => (t.createdAt.isSmallerThanValue(
|
||||||
clock.now().subtract(
|
DateTime.now().subtract(
|
||||||
const Duration(days: 365),
|
const Duration(days: 365),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
.go();
|
.go();
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -59,8 +59,6 @@ class MediaFiles extends Table {
|
||||||
BlobColumn get encryptionMac => blob().nullable()();
|
BlobColumn get encryptionMac => blob().nullable()();
|
||||||
BlobColumn get encryptionNonce => blob().nullable()();
|
BlobColumn get encryptionNonce => blob().nullable()();
|
||||||
|
|
||||||
BlobColumn get storedFileHash => blob().nullable()();
|
|
||||||
|
|
||||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,6 @@ class Receipts extends Table {
|
||||||
BoolColumn get contactWillSendsReceipt =>
|
BoolColumn get contactWillSendsReceipt =>
|
||||||
boolean().withDefault(const Constant(true))();
|
boolean().withDefault(const Constant(true))();
|
||||||
|
|
||||||
DateTimeColumn get markForRetry => dateTime().nullable()();
|
|
||||||
|
|
||||||
DateTimeColumn get ackByServerAt => dateTime().nullable()();
|
DateTimeColumn get ackByServerAt => dateTime().nullable()();
|
||||||
|
|
||||||
IntColumn get retryCount => integer().withDefault(const Constant(0))();
|
IntColumn get retryCount => integer().withDefault(const Constant(0))();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift_flutter/drift_flutter.dart'
|
import 'package:drift_flutter/drift_flutter.dart'
|
||||||
show DriftNativeOptions, driftDatabase;
|
show DriftNativeOptions, driftDatabase;
|
||||||
|
|
@ -68,7 +67,7 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 5;
|
int get schemaVersion => 4;
|
||||||
|
|
||||||
static QueryExecutor _openConnection() {
|
static QueryExecutor _openConnection() {
|
||||||
return driftDatabase(
|
return driftDatabase(
|
||||||
|
|
@ -104,13 +103,6 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
from4To5: (m, schema) async {
|
|
||||||
await m.addColumn(schema.receipts, schema.receipts.markForRetry);
|
|
||||||
await m.addColumn(
|
|
||||||
schema.mediaFiles,
|
|
||||||
schema.mediaFiles.storedFileHash,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -161,9 +153,9 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
await (delete(signalPreKeyStores)
|
await (delete(signalPreKeyStores)
|
||||||
..where(
|
..where(
|
||||||
(t) => (t.createdAt.isSmallerThanValue(
|
(t) => (t.createdAt.isSmallerThanValue(
|
||||||
clock.now().subtract(
|
DateTime.now().subtract(
|
||||||
const Duration(days: 25),
|
const Duration(days: 25),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
.go();
|
.go();
|
||||||
|
|
|
||||||
|
|
@ -1996,12 +1996,6 @@ class $MediaFilesTable extends MediaFiles
|
||||||
late final GeneratedColumn<Uint8List> encryptionNonce =
|
late final GeneratedColumn<Uint8List> encryptionNonce =
|
||||||
GeneratedColumn<Uint8List>('encryption_nonce', aliasedName, true,
|
GeneratedColumn<Uint8List>('encryption_nonce', aliasedName, true,
|
||||||
type: DriftSqlType.blob, requiredDuringInsert: false);
|
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 =
|
static const VerificationMeta _createdAtMeta =
|
||||||
const VerificationMeta('createdAt');
|
const VerificationMeta('createdAt');
|
||||||
@override
|
@override
|
||||||
|
|
@ -2026,7 +2020,6 @@ class $MediaFilesTable extends MediaFiles
|
||||||
encryptionKey,
|
encryptionKey,
|
||||||
encryptionMac,
|
encryptionMac,
|
||||||
encryptionNonce,
|
encryptionNonce,
|
||||||
storedFileHash,
|
|
||||||
createdAt
|
createdAt
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
|
|
@ -2098,12 +2091,6 @@ class $MediaFilesTable extends MediaFiles
|
||||||
encryptionNonce.isAcceptableOrUnknown(
|
encryptionNonce.isAcceptableOrUnknown(
|
||||||
data['encryption_nonce']!, _encryptionNonceMeta));
|
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')) {
|
if (data.containsKey('created_at')) {
|
||||||
context.handle(_createdAtMeta,
|
context.handle(_createdAtMeta,
|
||||||
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
|
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
|
||||||
|
|
@ -2150,8 +2137,6 @@ class $MediaFilesTable extends MediaFiles
|
||||||
.read(DriftSqlType.blob, data['${effectivePrefix}encryption_mac']),
|
.read(DriftSqlType.blob, data['${effectivePrefix}encryption_mac']),
|
||||||
encryptionNonce: attachedDatabase.typeMapping
|
encryptionNonce: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.blob, data['${effectivePrefix}encryption_nonce']),
|
.read(DriftSqlType.blob, data['${effectivePrefix}encryption_nonce']),
|
||||||
storedFileHash: attachedDatabase.typeMapping
|
|
||||||
.read(DriftSqlType.blob, data['${effectivePrefix}stored_file_hash']),
|
|
||||||
createdAt: attachedDatabase.typeMapping
|
createdAt: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
|
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
|
||||||
);
|
);
|
||||||
|
|
@ -2196,7 +2181,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
final Uint8List? encryptionKey;
|
final Uint8List? encryptionKey;
|
||||||
final Uint8List? encryptionMac;
|
final Uint8List? encryptionMac;
|
||||||
final Uint8List? encryptionNonce;
|
final Uint8List? encryptionNonce;
|
||||||
final Uint8List? storedFileHash;
|
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
const MediaFile(
|
const MediaFile(
|
||||||
{required this.mediaId,
|
{required this.mediaId,
|
||||||
|
|
@ -2213,7 +2197,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
this.encryptionKey,
|
this.encryptionKey,
|
||||||
this.encryptionMac,
|
this.encryptionMac,
|
||||||
this.encryptionNonce,
|
this.encryptionNonce,
|
||||||
this.storedFileHash,
|
|
||||||
required this.createdAt});
|
required this.createdAt});
|
||||||
@override
|
@override
|
||||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
|
@ -2258,9 +2241,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
if (!nullToAbsent || encryptionNonce != null) {
|
if (!nullToAbsent || encryptionNonce != null) {
|
||||||
map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce);
|
map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce);
|
||||||
}
|
}
|
||||||
if (!nullToAbsent || storedFileHash != null) {
|
|
||||||
map['stored_file_hash'] = Variable<Uint8List>(storedFileHash);
|
|
||||||
}
|
|
||||||
map['created_at'] = Variable<DateTime>(createdAt);
|
map['created_at'] = Variable<DateTime>(createdAt);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
@ -2300,9 +2280,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
encryptionNonce: encryptionNonce == null && nullToAbsent
|
encryptionNonce: encryptionNonce == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(encryptionNonce),
|
: Value(encryptionNonce),
|
||||||
storedFileHash: storedFileHash == null && nullToAbsent
|
|
||||||
? const Value.absent()
|
|
||||||
: Value(storedFileHash),
|
|
||||||
createdAt: Value(createdAt),
|
createdAt: Value(createdAt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -2331,7 +2308,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
encryptionKey: serializer.fromJson<Uint8List?>(json['encryptionKey']),
|
encryptionKey: serializer.fromJson<Uint8List?>(json['encryptionKey']),
|
||||||
encryptionMac: serializer.fromJson<Uint8List?>(json['encryptionMac']),
|
encryptionMac: serializer.fromJson<Uint8List?>(json['encryptionMac']),
|
||||||
encryptionNonce: serializer.fromJson<Uint8List?>(json['encryptionNonce']),
|
encryptionNonce: serializer.fromJson<Uint8List?>(json['encryptionNonce']),
|
||||||
storedFileHash: serializer.fromJson<Uint8List?>(json['storedFileHash']),
|
|
||||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -2357,7 +2333,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
'encryptionKey': serializer.toJson<Uint8List?>(encryptionKey),
|
'encryptionKey': serializer.toJson<Uint8List?>(encryptionKey),
|
||||||
'encryptionMac': serializer.toJson<Uint8List?>(encryptionMac),
|
'encryptionMac': serializer.toJson<Uint8List?>(encryptionMac),
|
||||||
'encryptionNonce': serializer.toJson<Uint8List?>(encryptionNonce),
|
'encryptionNonce': serializer.toJson<Uint8List?>(encryptionNonce),
|
||||||
'storedFileHash': serializer.toJson<Uint8List?>(storedFileHash),
|
|
||||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -2377,7 +2352,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
Value<Uint8List?> encryptionKey = const Value.absent(),
|
Value<Uint8List?> encryptionKey = const Value.absent(),
|
||||||
Value<Uint8List?> encryptionMac = const Value.absent(),
|
Value<Uint8List?> encryptionMac = const Value.absent(),
|
||||||
Value<Uint8List?> encryptionNonce = const Value.absent(),
|
Value<Uint8List?> encryptionNonce = const Value.absent(),
|
||||||
Value<Uint8List?> storedFileHash = const Value.absent(),
|
|
||||||
DateTime? createdAt}) =>
|
DateTime? createdAt}) =>
|
||||||
MediaFile(
|
MediaFile(
|
||||||
mediaId: mediaId ?? this.mediaId,
|
mediaId: mediaId ?? this.mediaId,
|
||||||
|
|
@ -2405,8 +2379,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
encryptionNonce: encryptionNonce.present
|
encryptionNonce: encryptionNonce.present
|
||||||
? encryptionNonce.value
|
? encryptionNonce.value
|
||||||
: this.encryptionNonce,
|
: this.encryptionNonce,
|
||||||
storedFileHash:
|
|
||||||
storedFileHash.present ? storedFileHash.value : this.storedFileHash,
|
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
);
|
);
|
||||||
MediaFile copyWithCompanion(MediaFilesCompanion data) {
|
MediaFile copyWithCompanion(MediaFilesCompanion data) {
|
||||||
|
|
@ -2445,9 +2417,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
encryptionNonce: data.encryptionNonce.present
|
encryptionNonce: data.encryptionNonce.present
|
||||||
? data.encryptionNonce.value
|
? data.encryptionNonce.value
|
||||||
: this.encryptionNonce,
|
: this.encryptionNonce,
|
||||||
storedFileHash: data.storedFileHash.present
|
|
||||||
? data.storedFileHash.value
|
|
||||||
: this.storedFileHash,
|
|
||||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -2469,7 +2438,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
..write('encryptionKey: $encryptionKey, ')
|
..write('encryptionKey: $encryptionKey, ')
|
||||||
..write('encryptionMac: $encryptionMac, ')
|
..write('encryptionMac: $encryptionMac, ')
|
||||||
..write('encryptionNonce: $encryptionNonce, ')
|
..write('encryptionNonce: $encryptionNonce, ')
|
||||||
..write('storedFileHash: $storedFileHash, ')
|
|
||||||
..write('createdAt: $createdAt')
|
..write('createdAt: $createdAt')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
|
|
@ -2491,7 +2459,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
$driftBlobEquality.hash(encryptionKey),
|
$driftBlobEquality.hash(encryptionKey),
|
||||||
$driftBlobEquality.hash(encryptionMac),
|
$driftBlobEquality.hash(encryptionMac),
|
||||||
$driftBlobEquality.hash(encryptionNonce),
|
$driftBlobEquality.hash(encryptionNonce),
|
||||||
$driftBlobEquality.hash(storedFileHash),
|
|
||||||
createdAt);
|
createdAt);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -2512,8 +2479,6 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
$driftBlobEquality.equals(other.encryptionMac, this.encryptionMac) &&
|
$driftBlobEquality.equals(other.encryptionMac, this.encryptionMac) &&
|
||||||
$driftBlobEquality.equals(
|
$driftBlobEquality.equals(
|
||||||
other.encryptionNonce, this.encryptionNonce) &&
|
other.encryptionNonce, this.encryptionNonce) &&
|
||||||
$driftBlobEquality.equals(
|
|
||||||
other.storedFileHash, this.storedFileHash) &&
|
|
||||||
other.createdAt == this.createdAt);
|
other.createdAt == this.createdAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2532,7 +2497,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
final Value<Uint8List?> encryptionKey;
|
final Value<Uint8List?> encryptionKey;
|
||||||
final Value<Uint8List?> encryptionMac;
|
final Value<Uint8List?> encryptionMac;
|
||||||
final Value<Uint8List?> encryptionNonce;
|
final Value<Uint8List?> encryptionNonce;
|
||||||
final Value<Uint8List?> storedFileHash;
|
|
||||||
final Value<DateTime> createdAt;
|
final Value<DateTime> createdAt;
|
||||||
final Value<int> rowid;
|
final Value<int> rowid;
|
||||||
const MediaFilesCompanion({
|
const MediaFilesCompanion({
|
||||||
|
|
@ -2550,7 +2514,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
this.encryptionKey = const Value.absent(),
|
this.encryptionKey = const Value.absent(),
|
||||||
this.encryptionMac = const Value.absent(),
|
this.encryptionMac = const Value.absent(),
|
||||||
this.encryptionNonce = const Value.absent(),
|
this.encryptionNonce = const Value.absent(),
|
||||||
this.storedFileHash = const Value.absent(),
|
|
||||||
this.createdAt = const Value.absent(),
|
this.createdAt = const Value.absent(),
|
||||||
this.rowid = const Value.absent(),
|
this.rowid = const Value.absent(),
|
||||||
});
|
});
|
||||||
|
|
@ -2569,7 +2532,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
this.encryptionKey = const Value.absent(),
|
this.encryptionKey = const Value.absent(),
|
||||||
this.encryptionMac = const Value.absent(),
|
this.encryptionMac = const Value.absent(),
|
||||||
this.encryptionNonce = const Value.absent(),
|
this.encryptionNonce = const Value.absent(),
|
||||||
this.storedFileHash = const Value.absent(),
|
|
||||||
this.createdAt = const Value.absent(),
|
this.createdAt = const Value.absent(),
|
||||||
this.rowid = const Value.absent(),
|
this.rowid = const Value.absent(),
|
||||||
}) : mediaId = Value(mediaId),
|
}) : mediaId = Value(mediaId),
|
||||||
|
|
@ -2589,7 +2551,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
Expression<Uint8List>? encryptionKey,
|
Expression<Uint8List>? encryptionKey,
|
||||||
Expression<Uint8List>? encryptionMac,
|
Expression<Uint8List>? encryptionMac,
|
||||||
Expression<Uint8List>? encryptionNonce,
|
Expression<Uint8List>? encryptionNonce,
|
||||||
Expression<Uint8List>? storedFileHash,
|
|
||||||
Expression<DateTime>? createdAt,
|
Expression<DateTime>? createdAt,
|
||||||
Expression<int>? rowid,
|
Expression<int>? rowid,
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -2611,7 +2572,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
if (encryptionKey != null) 'encryption_key': encryptionKey,
|
if (encryptionKey != null) 'encryption_key': encryptionKey,
|
||||||
if (encryptionMac != null) 'encryption_mac': encryptionMac,
|
if (encryptionMac != null) 'encryption_mac': encryptionMac,
|
||||||
if (encryptionNonce != null) 'encryption_nonce': encryptionNonce,
|
if (encryptionNonce != null) 'encryption_nonce': encryptionNonce,
|
||||||
if (storedFileHash != null) 'stored_file_hash': storedFileHash,
|
|
||||||
if (createdAt != null) 'created_at': createdAt,
|
if (createdAt != null) 'created_at': createdAt,
|
||||||
if (rowid != null) 'rowid': rowid,
|
if (rowid != null) 'rowid': rowid,
|
||||||
});
|
});
|
||||||
|
|
@ -2632,7 +2592,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
Value<Uint8List?>? encryptionKey,
|
Value<Uint8List?>? encryptionKey,
|
||||||
Value<Uint8List?>? encryptionMac,
|
Value<Uint8List?>? encryptionMac,
|
||||||
Value<Uint8List?>? encryptionNonce,
|
Value<Uint8List?>? encryptionNonce,
|
||||||
Value<Uint8List?>? storedFileHash,
|
|
||||||
Value<DateTime>? createdAt,
|
Value<DateTime>? createdAt,
|
||||||
Value<int>? rowid}) {
|
Value<int>? rowid}) {
|
||||||
return MediaFilesCompanion(
|
return MediaFilesCompanion(
|
||||||
|
|
@ -2652,7 +2611,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
encryptionKey: encryptionKey ?? this.encryptionKey,
|
encryptionKey: encryptionKey ?? this.encryptionKey,
|
||||||
encryptionMac: encryptionMac ?? this.encryptionMac,
|
encryptionMac: encryptionMac ?? this.encryptionMac,
|
||||||
encryptionNonce: encryptionNonce ?? this.encryptionNonce,
|
encryptionNonce: encryptionNonce ?? this.encryptionNonce,
|
||||||
storedFileHash: storedFileHash ?? this.storedFileHash,
|
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
rowid: rowid ?? this.rowid,
|
rowid: rowid ?? this.rowid,
|
||||||
);
|
);
|
||||||
|
|
@ -2710,9 +2668,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
if (encryptionNonce.present) {
|
if (encryptionNonce.present) {
|
||||||
map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce.value);
|
map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce.value);
|
||||||
}
|
}
|
||||||
if (storedFileHash.present) {
|
|
||||||
map['stored_file_hash'] = Variable<Uint8List>(storedFileHash.value);
|
|
||||||
}
|
|
||||||
if (createdAt.present) {
|
if (createdAt.present) {
|
||||||
map['created_at'] = Variable<DateTime>(createdAt.value);
|
map['created_at'] = Variable<DateTime>(createdAt.value);
|
||||||
}
|
}
|
||||||
|
|
@ -2739,7 +2694,6 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
..write('encryptionKey: $encryptionKey, ')
|
..write('encryptionKey: $encryptionKey, ')
|
||||||
..write('encryptionMac: $encryptionMac, ')
|
..write('encryptionMac: $encryptionMac, ')
|
||||||
..write('encryptionNonce: $encryptionNonce, ')
|
..write('encryptionNonce: $encryptionNonce, ')
|
||||||
..write('storedFileHash: $storedFileHash, ')
|
|
||||||
..write('createdAt: $createdAt, ')
|
..write('createdAt: $createdAt, ')
|
||||||
..write('rowid: $rowid')
|
..write('rowid: $rowid')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
|
|
@ -4592,12 +4546,6 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
|
||||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
'CHECK ("contact_will_sends_receipt" IN (0, 1))'),
|
'CHECK ("contact_will_sends_receipt" IN (0, 1))'),
|
||||||
defaultValue: const Constant(true));
|
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 =
|
static const VerificationMeta _ackByServerAtMeta =
|
||||||
const VerificationMeta('ackByServerAt');
|
const VerificationMeta('ackByServerAt');
|
||||||
@override
|
@override
|
||||||
|
|
@ -4633,7 +4581,6 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
|
||||||
messageId,
|
messageId,
|
||||||
message,
|
message,
|
||||||
contactWillSendsReceipt,
|
contactWillSendsReceipt,
|
||||||
markForRetry,
|
|
||||||
ackByServerAt,
|
ackByServerAt,
|
||||||
retryCount,
|
retryCount,
|
||||||
lastRetry,
|
lastRetry,
|
||||||
|
|
@ -4678,12 +4625,6 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
|
||||||
data['contact_will_sends_receipt']!,
|
data['contact_will_sends_receipt']!,
|
||||||
_contactWillSendsReceiptMeta));
|
_contactWillSendsReceiptMeta));
|
||||||
}
|
}
|
||||||
if (data.containsKey('mark_for_retry')) {
|
|
||||||
context.handle(
|
|
||||||
_markForRetryMeta,
|
|
||||||
markForRetry.isAcceptableOrUnknown(
|
|
||||||
data['mark_for_retry']!, _markForRetryMeta));
|
|
||||||
}
|
|
||||||
if (data.containsKey('ack_by_server_at')) {
|
if (data.containsKey('ack_by_server_at')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_ackByServerAtMeta,
|
_ackByServerAtMeta,
|
||||||
|
|
@ -4724,8 +4665,6 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
|
||||||
contactWillSendsReceipt: attachedDatabase.typeMapping.read(
|
contactWillSendsReceipt: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.bool,
|
DriftSqlType.bool,
|
||||||
data['${effectivePrefix}contact_will_sends_receipt'])!,
|
data['${effectivePrefix}contact_will_sends_receipt'])!,
|
||||||
markForRetry: attachedDatabase.typeMapping.read(
|
|
||||||
DriftSqlType.dateTime, data['${effectivePrefix}mark_for_retry']),
|
|
||||||
ackByServerAt: attachedDatabase.typeMapping.read(
|
ackByServerAt: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.dateTime, data['${effectivePrefix}ack_by_server_at']),
|
DriftSqlType.dateTime, data['${effectivePrefix}ack_by_server_at']),
|
||||||
retryCount: attachedDatabase.typeMapping
|
retryCount: attachedDatabase.typeMapping
|
||||||
|
|
@ -4751,7 +4690,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
/// This is the protobuf 'Message'
|
/// This is the protobuf 'Message'
|
||||||
final Uint8List message;
|
final Uint8List message;
|
||||||
final bool contactWillSendsReceipt;
|
final bool contactWillSendsReceipt;
|
||||||
final DateTime? markForRetry;
|
|
||||||
final DateTime? ackByServerAt;
|
final DateTime? ackByServerAt;
|
||||||
final int retryCount;
|
final int retryCount;
|
||||||
final DateTime? lastRetry;
|
final DateTime? lastRetry;
|
||||||
|
|
@ -4762,7 +4700,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
this.messageId,
|
this.messageId,
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.contactWillSendsReceipt,
|
required this.contactWillSendsReceipt,
|
||||||
this.markForRetry,
|
|
||||||
this.ackByServerAt,
|
this.ackByServerAt,
|
||||||
required this.retryCount,
|
required this.retryCount,
|
||||||
this.lastRetry,
|
this.lastRetry,
|
||||||
|
|
@ -4777,9 +4714,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
}
|
}
|
||||||
map['message'] = Variable<Uint8List>(message);
|
map['message'] = Variable<Uint8List>(message);
|
||||||
map['contact_will_sends_receipt'] = Variable<bool>(contactWillSendsReceipt);
|
map['contact_will_sends_receipt'] = Variable<bool>(contactWillSendsReceipt);
|
||||||
if (!nullToAbsent || markForRetry != null) {
|
|
||||||
map['mark_for_retry'] = Variable<DateTime>(markForRetry);
|
|
||||||
}
|
|
||||||
if (!nullToAbsent || ackByServerAt != null) {
|
if (!nullToAbsent || ackByServerAt != null) {
|
||||||
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt);
|
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt);
|
||||||
}
|
}
|
||||||
|
|
@ -4800,9 +4734,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
: Value(messageId),
|
: Value(messageId),
|
||||||
message: Value(message),
|
message: Value(message),
|
||||||
contactWillSendsReceipt: Value(contactWillSendsReceipt),
|
contactWillSendsReceipt: Value(contactWillSendsReceipt),
|
||||||
markForRetry: markForRetry == null && nullToAbsent
|
|
||||||
? const Value.absent()
|
|
||||||
: Value(markForRetry),
|
|
||||||
ackByServerAt: ackByServerAt == null && nullToAbsent
|
ackByServerAt: ackByServerAt == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(ackByServerAt),
|
: Value(ackByServerAt),
|
||||||
|
|
@ -4824,7 +4755,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
message: serializer.fromJson<Uint8List>(json['message']),
|
message: serializer.fromJson<Uint8List>(json['message']),
|
||||||
contactWillSendsReceipt:
|
contactWillSendsReceipt:
|
||||||
serializer.fromJson<bool>(json['contactWillSendsReceipt']),
|
serializer.fromJson<bool>(json['contactWillSendsReceipt']),
|
||||||
markForRetry: serializer.fromJson<DateTime?>(json['markForRetry']),
|
|
||||||
ackByServerAt: serializer.fromJson<DateTime?>(json['ackByServerAt']),
|
ackByServerAt: serializer.fromJson<DateTime?>(json['ackByServerAt']),
|
||||||
retryCount: serializer.fromJson<int>(json['retryCount']),
|
retryCount: serializer.fromJson<int>(json['retryCount']),
|
||||||
lastRetry: serializer.fromJson<DateTime?>(json['lastRetry']),
|
lastRetry: serializer.fromJson<DateTime?>(json['lastRetry']),
|
||||||
|
|
@ -4841,7 +4771,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
'message': serializer.toJson<Uint8List>(message),
|
'message': serializer.toJson<Uint8List>(message),
|
||||||
'contactWillSendsReceipt':
|
'contactWillSendsReceipt':
|
||||||
serializer.toJson<bool>(contactWillSendsReceipt),
|
serializer.toJson<bool>(contactWillSendsReceipt),
|
||||||
'markForRetry': serializer.toJson<DateTime?>(markForRetry),
|
|
||||||
'ackByServerAt': serializer.toJson<DateTime?>(ackByServerAt),
|
'ackByServerAt': serializer.toJson<DateTime?>(ackByServerAt),
|
||||||
'retryCount': serializer.toJson<int>(retryCount),
|
'retryCount': serializer.toJson<int>(retryCount),
|
||||||
'lastRetry': serializer.toJson<DateTime?>(lastRetry),
|
'lastRetry': serializer.toJson<DateTime?>(lastRetry),
|
||||||
|
|
@ -4855,7 +4784,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
Value<String?> messageId = const Value.absent(),
|
Value<String?> messageId = const Value.absent(),
|
||||||
Uint8List? message,
|
Uint8List? message,
|
||||||
bool? contactWillSendsReceipt,
|
bool? contactWillSendsReceipt,
|
||||||
Value<DateTime?> markForRetry = const Value.absent(),
|
|
||||||
Value<DateTime?> ackByServerAt = const Value.absent(),
|
Value<DateTime?> ackByServerAt = const Value.absent(),
|
||||||
int? retryCount,
|
int? retryCount,
|
||||||
Value<DateTime?> lastRetry = const Value.absent(),
|
Value<DateTime?> lastRetry = const Value.absent(),
|
||||||
|
|
@ -4867,8 +4795,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
message: message ?? this.message,
|
message: message ?? this.message,
|
||||||
contactWillSendsReceipt:
|
contactWillSendsReceipt:
|
||||||
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
|
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
|
||||||
markForRetry:
|
|
||||||
markForRetry.present ? markForRetry.value : this.markForRetry,
|
|
||||||
ackByServerAt:
|
ackByServerAt:
|
||||||
ackByServerAt.present ? ackByServerAt.value : this.ackByServerAt,
|
ackByServerAt.present ? ackByServerAt.value : this.ackByServerAt,
|
||||||
retryCount: retryCount ?? this.retryCount,
|
retryCount: retryCount ?? this.retryCount,
|
||||||
|
|
@ -4884,9 +4810,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
contactWillSendsReceipt: data.contactWillSendsReceipt.present
|
contactWillSendsReceipt: data.contactWillSendsReceipt.present
|
||||||
? data.contactWillSendsReceipt.value
|
? data.contactWillSendsReceipt.value
|
||||||
: this.contactWillSendsReceipt,
|
: this.contactWillSendsReceipt,
|
||||||
markForRetry: data.markForRetry.present
|
|
||||||
? data.markForRetry.value
|
|
||||||
: this.markForRetry,
|
|
||||||
ackByServerAt: data.ackByServerAt.present
|
ackByServerAt: data.ackByServerAt.present
|
||||||
? data.ackByServerAt.value
|
? data.ackByServerAt.value
|
||||||
: this.ackByServerAt,
|
: this.ackByServerAt,
|
||||||
|
|
@ -4905,7 +4828,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
..write('messageId: $messageId, ')
|
..write('messageId: $messageId, ')
|
||||||
..write('message: $message, ')
|
..write('message: $message, ')
|
||||||
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
|
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
|
||||||
..write('markForRetry: $markForRetry, ')
|
|
||||||
..write('ackByServerAt: $ackByServerAt, ')
|
..write('ackByServerAt: $ackByServerAt, ')
|
||||||
..write('retryCount: $retryCount, ')
|
..write('retryCount: $retryCount, ')
|
||||||
..write('lastRetry: $lastRetry, ')
|
..write('lastRetry: $lastRetry, ')
|
||||||
|
|
@ -4921,7 +4843,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
messageId,
|
messageId,
|
||||||
$driftBlobEquality.hash(message),
|
$driftBlobEquality.hash(message),
|
||||||
contactWillSendsReceipt,
|
contactWillSendsReceipt,
|
||||||
markForRetry,
|
|
||||||
ackByServerAt,
|
ackByServerAt,
|
||||||
retryCount,
|
retryCount,
|
||||||
lastRetry,
|
lastRetry,
|
||||||
|
|
@ -4935,7 +4856,6 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
other.messageId == this.messageId &&
|
other.messageId == this.messageId &&
|
||||||
$driftBlobEquality.equals(other.message, this.message) &&
|
$driftBlobEquality.equals(other.message, this.message) &&
|
||||||
other.contactWillSendsReceipt == this.contactWillSendsReceipt &&
|
other.contactWillSendsReceipt == this.contactWillSendsReceipt &&
|
||||||
other.markForRetry == this.markForRetry &&
|
|
||||||
other.ackByServerAt == this.ackByServerAt &&
|
other.ackByServerAt == this.ackByServerAt &&
|
||||||
other.retryCount == this.retryCount &&
|
other.retryCount == this.retryCount &&
|
||||||
other.lastRetry == this.lastRetry &&
|
other.lastRetry == this.lastRetry &&
|
||||||
|
|
@ -4948,7 +4868,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
final Value<String?> messageId;
|
final Value<String?> messageId;
|
||||||
final Value<Uint8List> message;
|
final Value<Uint8List> message;
|
||||||
final Value<bool> contactWillSendsReceipt;
|
final Value<bool> contactWillSendsReceipt;
|
||||||
final Value<DateTime?> markForRetry;
|
|
||||||
final Value<DateTime?> ackByServerAt;
|
final Value<DateTime?> ackByServerAt;
|
||||||
final Value<int> retryCount;
|
final Value<int> retryCount;
|
||||||
final Value<DateTime?> lastRetry;
|
final Value<DateTime?> lastRetry;
|
||||||
|
|
@ -4960,7 +4879,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
this.messageId = const Value.absent(),
|
this.messageId = const Value.absent(),
|
||||||
this.message = const Value.absent(),
|
this.message = const Value.absent(),
|
||||||
this.contactWillSendsReceipt = const Value.absent(),
|
this.contactWillSendsReceipt = const Value.absent(),
|
||||||
this.markForRetry = const Value.absent(),
|
|
||||||
this.ackByServerAt = const Value.absent(),
|
this.ackByServerAt = const Value.absent(),
|
||||||
this.retryCount = const Value.absent(),
|
this.retryCount = const Value.absent(),
|
||||||
this.lastRetry = const Value.absent(),
|
this.lastRetry = const Value.absent(),
|
||||||
|
|
@ -4973,7 +4891,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
this.messageId = const Value.absent(),
|
this.messageId = const Value.absent(),
|
||||||
required Uint8List message,
|
required Uint8List message,
|
||||||
this.contactWillSendsReceipt = const Value.absent(),
|
this.contactWillSendsReceipt = const Value.absent(),
|
||||||
this.markForRetry = const Value.absent(),
|
|
||||||
this.ackByServerAt = const Value.absent(),
|
this.ackByServerAt = const Value.absent(),
|
||||||
this.retryCount = const Value.absent(),
|
this.retryCount = const Value.absent(),
|
||||||
this.lastRetry = const Value.absent(),
|
this.lastRetry = const Value.absent(),
|
||||||
|
|
@ -4988,7 +4905,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
Expression<String>? messageId,
|
Expression<String>? messageId,
|
||||||
Expression<Uint8List>? message,
|
Expression<Uint8List>? message,
|
||||||
Expression<bool>? contactWillSendsReceipt,
|
Expression<bool>? contactWillSendsReceipt,
|
||||||
Expression<DateTime>? markForRetry,
|
|
||||||
Expression<DateTime>? ackByServerAt,
|
Expression<DateTime>? ackByServerAt,
|
||||||
Expression<int>? retryCount,
|
Expression<int>? retryCount,
|
||||||
Expression<DateTime>? lastRetry,
|
Expression<DateTime>? lastRetry,
|
||||||
|
|
@ -5002,7 +4918,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
if (message != null) 'message': message,
|
if (message != null) 'message': message,
|
||||||
if (contactWillSendsReceipt != null)
|
if (contactWillSendsReceipt != null)
|
||||||
'contact_will_sends_receipt': contactWillSendsReceipt,
|
'contact_will_sends_receipt': contactWillSendsReceipt,
|
||||||
if (markForRetry != null) 'mark_for_retry': markForRetry,
|
|
||||||
if (ackByServerAt != null) 'ack_by_server_at': ackByServerAt,
|
if (ackByServerAt != null) 'ack_by_server_at': ackByServerAt,
|
||||||
if (retryCount != null) 'retry_count': retryCount,
|
if (retryCount != null) 'retry_count': retryCount,
|
||||||
if (lastRetry != null) 'last_retry': lastRetry,
|
if (lastRetry != null) 'last_retry': lastRetry,
|
||||||
|
|
@ -5017,7 +4932,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
Value<String?>? messageId,
|
Value<String?>? messageId,
|
||||||
Value<Uint8List>? message,
|
Value<Uint8List>? message,
|
||||||
Value<bool>? contactWillSendsReceipt,
|
Value<bool>? contactWillSendsReceipt,
|
||||||
Value<DateTime?>? markForRetry,
|
|
||||||
Value<DateTime?>? ackByServerAt,
|
Value<DateTime?>? ackByServerAt,
|
||||||
Value<int>? retryCount,
|
Value<int>? retryCount,
|
||||||
Value<DateTime?>? lastRetry,
|
Value<DateTime?>? lastRetry,
|
||||||
|
|
@ -5030,7 +4944,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
message: message ?? this.message,
|
message: message ?? this.message,
|
||||||
contactWillSendsReceipt:
|
contactWillSendsReceipt:
|
||||||
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
|
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
|
||||||
markForRetry: markForRetry ?? this.markForRetry,
|
|
||||||
ackByServerAt: ackByServerAt ?? this.ackByServerAt,
|
ackByServerAt: ackByServerAt ?? this.ackByServerAt,
|
||||||
retryCount: retryCount ?? this.retryCount,
|
retryCount: retryCount ?? this.retryCount,
|
||||||
lastRetry: lastRetry ?? this.lastRetry,
|
lastRetry: lastRetry ?? this.lastRetry,
|
||||||
|
|
@ -5058,9 +4971,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
map['contact_will_sends_receipt'] =
|
map['contact_will_sends_receipt'] =
|
||||||
Variable<bool>(contactWillSendsReceipt.value);
|
Variable<bool>(contactWillSendsReceipt.value);
|
||||||
}
|
}
|
||||||
if (markForRetry.present) {
|
|
||||||
map['mark_for_retry'] = Variable<DateTime>(markForRetry.value);
|
|
||||||
}
|
|
||||||
if (ackByServerAt.present) {
|
if (ackByServerAt.present) {
|
||||||
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt.value);
|
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt.value);
|
||||||
}
|
}
|
||||||
|
|
@ -5087,7 +4997,6 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
..write('messageId: $messageId, ')
|
..write('messageId: $messageId, ')
|
||||||
..write('message: $message, ')
|
..write('message: $message, ')
|
||||||
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
|
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
|
||||||
..write('markForRetry: $markForRetry, ')
|
|
||||||
..write('ackByServerAt: $ackByServerAt, ')
|
..write('ackByServerAt: $ackByServerAt, ')
|
||||||
..write('retryCount: $retryCount, ')
|
..write('retryCount: $retryCount, ')
|
||||||
..write('lastRetry: $lastRetry, ')
|
..write('lastRetry: $lastRetry, ')
|
||||||
|
|
@ -9436,7 +9345,6 @@ typedef $$MediaFilesTableCreateCompanionBuilder = MediaFilesCompanion Function({
|
||||||
Value<Uint8List?> encryptionKey,
|
Value<Uint8List?> encryptionKey,
|
||||||
Value<Uint8List?> encryptionMac,
|
Value<Uint8List?> encryptionMac,
|
||||||
Value<Uint8List?> encryptionNonce,
|
Value<Uint8List?> encryptionNonce,
|
||||||
Value<Uint8List?> storedFileHash,
|
|
||||||
Value<DateTime> createdAt,
|
Value<DateTime> createdAt,
|
||||||
Value<int> rowid,
|
Value<int> rowid,
|
||||||
});
|
});
|
||||||
|
|
@ -9455,7 +9363,6 @@ typedef $$MediaFilesTableUpdateCompanionBuilder = MediaFilesCompanion Function({
|
||||||
Value<Uint8List?> encryptionKey,
|
Value<Uint8List?> encryptionKey,
|
||||||
Value<Uint8List?> encryptionMac,
|
Value<Uint8List?> encryptionMac,
|
||||||
Value<Uint8List?> encryptionNonce,
|
Value<Uint8List?> encryptionNonce,
|
||||||
Value<Uint8List?> storedFileHash,
|
|
||||||
Value<DateTime> createdAt,
|
Value<DateTime> createdAt,
|
||||||
Value<int> rowid,
|
Value<int> rowid,
|
||||||
});
|
});
|
||||||
|
|
@ -9542,10 +9449,6 @@ class $$MediaFilesTableFilterComposer
|
||||||
column: $table.encryptionNonce,
|
column: $table.encryptionNonce,
|
||||||
builder: (column) => ColumnFilters(column));
|
builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
ColumnFilters<Uint8List> get storedFileHash => $composableBuilder(
|
|
||||||
column: $table.storedFileHash,
|
|
||||||
builder: (column) => ColumnFilters(column));
|
|
||||||
|
|
||||||
ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
||||||
column: $table.createdAt, builder: (column) => ColumnFilters(column));
|
column: $table.createdAt, builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
|
|
@ -9631,10 +9534,6 @@ class $$MediaFilesTableOrderingComposer
|
||||||
column: $table.encryptionNonce,
|
column: $table.encryptionNonce,
|
||||||
builder: (column) => ColumnOrderings(column));
|
builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
ColumnOrderings<Uint8List> get storedFileHash => $composableBuilder(
|
|
||||||
column: $table.storedFileHash,
|
|
||||||
builder: (column) => ColumnOrderings(column));
|
|
||||||
|
|
||||||
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
||||||
column: $table.createdAt, builder: (column) => ColumnOrderings(column));
|
column: $table.createdAt, builder: (column) => ColumnOrderings(column));
|
||||||
}
|
}
|
||||||
|
|
@ -9693,9 +9592,6 @@ class $$MediaFilesTableAnnotationComposer
|
||||||
GeneratedColumn<Uint8List> get encryptionNonce => $composableBuilder(
|
GeneratedColumn<Uint8List> get encryptionNonce => $composableBuilder(
|
||||||
column: $table.encryptionNonce, builder: (column) => column);
|
column: $table.encryptionNonce, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumn<Uint8List> get storedFileHash => $composableBuilder(
|
|
||||||
column: $table.storedFileHash, builder: (column) => column);
|
|
||||||
|
|
||||||
GeneratedColumn<DateTime> get createdAt =>
|
GeneratedColumn<DateTime> get createdAt =>
|
||||||
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
||||||
|
|
||||||
|
|
@ -9758,7 +9654,6 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
||||||
Value<Uint8List?> encryptionKey = const Value.absent(),
|
Value<Uint8List?> encryptionKey = const Value.absent(),
|
||||||
Value<Uint8List?> encryptionMac = const Value.absent(),
|
Value<Uint8List?> encryptionMac = const Value.absent(),
|
||||||
Value<Uint8List?> encryptionNonce = const Value.absent(),
|
Value<Uint8List?> encryptionNonce = const Value.absent(),
|
||||||
Value<Uint8List?> storedFileHash = const Value.absent(),
|
|
||||||
Value<DateTime> createdAt = const Value.absent(),
|
Value<DateTime> createdAt = const Value.absent(),
|
||||||
Value<int> rowid = const Value.absent(),
|
Value<int> rowid = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
|
|
@ -9777,7 +9672,6 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
||||||
encryptionKey: encryptionKey,
|
encryptionKey: encryptionKey,
|
||||||
encryptionMac: encryptionMac,
|
encryptionMac: encryptionMac,
|
||||||
encryptionNonce: encryptionNonce,
|
encryptionNonce: encryptionNonce,
|
||||||
storedFileHash: storedFileHash,
|
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
rowid: rowid,
|
rowid: rowid,
|
||||||
),
|
),
|
||||||
|
|
@ -9796,7 +9690,6 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
||||||
Value<Uint8List?> encryptionKey = const Value.absent(),
|
Value<Uint8List?> encryptionKey = const Value.absent(),
|
||||||
Value<Uint8List?> encryptionMac = const Value.absent(),
|
Value<Uint8List?> encryptionMac = const Value.absent(),
|
||||||
Value<Uint8List?> encryptionNonce = const Value.absent(),
|
Value<Uint8List?> encryptionNonce = const Value.absent(),
|
||||||
Value<Uint8List?> storedFileHash = const Value.absent(),
|
|
||||||
Value<DateTime> createdAt = const Value.absent(),
|
Value<DateTime> createdAt = const Value.absent(),
|
||||||
Value<int> rowid = const Value.absent(),
|
Value<int> rowid = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
|
|
@ -9815,7 +9708,6 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
||||||
encryptionKey: encryptionKey,
|
encryptionKey: encryptionKey,
|
||||||
encryptionMac: encryptionMac,
|
encryptionMac: encryptionMac,
|
||||||
encryptionNonce: encryptionNonce,
|
encryptionNonce: encryptionNonce,
|
||||||
storedFileHash: storedFileHash,
|
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
rowid: rowid,
|
rowid: rowid,
|
||||||
),
|
),
|
||||||
|
|
@ -11770,7 +11662,6 @@ typedef $$ReceiptsTableCreateCompanionBuilder = ReceiptsCompanion Function({
|
||||||
Value<String?> messageId,
|
Value<String?> messageId,
|
||||||
required Uint8List message,
|
required Uint8List message,
|
||||||
Value<bool> contactWillSendsReceipt,
|
Value<bool> contactWillSendsReceipt,
|
||||||
Value<DateTime?> markForRetry,
|
|
||||||
Value<DateTime?> ackByServerAt,
|
Value<DateTime?> ackByServerAt,
|
||||||
Value<int> retryCount,
|
Value<int> retryCount,
|
||||||
Value<DateTime?> lastRetry,
|
Value<DateTime?> lastRetry,
|
||||||
|
|
@ -11783,7 +11674,6 @@ typedef $$ReceiptsTableUpdateCompanionBuilder = ReceiptsCompanion Function({
|
||||||
Value<String?> messageId,
|
Value<String?> messageId,
|
||||||
Value<Uint8List> message,
|
Value<Uint8List> message,
|
||||||
Value<bool> contactWillSendsReceipt,
|
Value<bool> contactWillSendsReceipt,
|
||||||
Value<DateTime?> markForRetry,
|
|
||||||
Value<DateTime?> ackByServerAt,
|
Value<DateTime?> ackByServerAt,
|
||||||
Value<int> retryCount,
|
Value<int> retryCount,
|
||||||
Value<DateTime?> lastRetry,
|
Value<DateTime?> lastRetry,
|
||||||
|
|
@ -11845,9 +11735,6 @@ class $$ReceiptsTableFilterComposer
|
||||||
column: $table.contactWillSendsReceipt,
|
column: $table.contactWillSendsReceipt,
|
||||||
builder: (column) => ColumnFilters(column));
|
builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
ColumnFilters<DateTime> get markForRetry => $composableBuilder(
|
|
||||||
column: $table.markForRetry, builder: (column) => ColumnFilters(column));
|
|
||||||
|
|
||||||
ColumnFilters<DateTime> get ackByServerAt => $composableBuilder(
|
ColumnFilters<DateTime> get ackByServerAt => $composableBuilder(
|
||||||
column: $table.ackByServerAt, builder: (column) => ColumnFilters(column));
|
column: $table.ackByServerAt, builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
|
|
@ -11920,10 +11807,6 @@ class $$ReceiptsTableOrderingComposer
|
||||||
column: $table.contactWillSendsReceipt,
|
column: $table.contactWillSendsReceipt,
|
||||||
builder: (column) => ColumnOrderings(column));
|
builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
ColumnOrderings<DateTime> get markForRetry => $composableBuilder(
|
|
||||||
column: $table.markForRetry,
|
|
||||||
builder: (column) => ColumnOrderings(column));
|
|
||||||
|
|
||||||
ColumnOrderings<DateTime> get ackByServerAt => $composableBuilder(
|
ColumnOrderings<DateTime> get ackByServerAt => $composableBuilder(
|
||||||
column: $table.ackByServerAt,
|
column: $table.ackByServerAt,
|
||||||
builder: (column) => ColumnOrderings(column));
|
builder: (column) => ColumnOrderings(column));
|
||||||
|
|
@ -11996,9 +11879,6 @@ class $$ReceiptsTableAnnotationComposer
|
||||||
GeneratedColumn<bool> get contactWillSendsReceipt => $composableBuilder(
|
GeneratedColumn<bool> get contactWillSendsReceipt => $composableBuilder(
|
||||||
column: $table.contactWillSendsReceipt, builder: (column) => column);
|
column: $table.contactWillSendsReceipt, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumn<DateTime> get markForRetry => $composableBuilder(
|
|
||||||
column: $table.markForRetry, builder: (column) => column);
|
|
||||||
|
|
||||||
GeneratedColumn<DateTime> get ackByServerAt => $composableBuilder(
|
GeneratedColumn<DateTime> get ackByServerAt => $composableBuilder(
|
||||||
column: $table.ackByServerAt, builder: (column) => column);
|
column: $table.ackByServerAt, builder: (column) => column);
|
||||||
|
|
||||||
|
|
@ -12080,7 +11960,6 @@ class $$ReceiptsTableTableManager extends RootTableManager<
|
||||||
Value<String?> messageId = const Value.absent(),
|
Value<String?> messageId = const Value.absent(),
|
||||||
Value<Uint8List> message = const Value.absent(),
|
Value<Uint8List> message = const Value.absent(),
|
||||||
Value<bool> contactWillSendsReceipt = const Value.absent(),
|
Value<bool> contactWillSendsReceipt = const Value.absent(),
|
||||||
Value<DateTime?> markForRetry = const Value.absent(),
|
|
||||||
Value<DateTime?> ackByServerAt = const Value.absent(),
|
Value<DateTime?> ackByServerAt = const Value.absent(),
|
||||||
Value<int> retryCount = const Value.absent(),
|
Value<int> retryCount = const Value.absent(),
|
||||||
Value<DateTime?> lastRetry = const Value.absent(),
|
Value<DateTime?> lastRetry = const Value.absent(),
|
||||||
|
|
@ -12093,7 +11972,6 @@ class $$ReceiptsTableTableManager extends RootTableManager<
|
||||||
messageId: messageId,
|
messageId: messageId,
|
||||||
message: message,
|
message: message,
|
||||||
contactWillSendsReceipt: contactWillSendsReceipt,
|
contactWillSendsReceipt: contactWillSendsReceipt,
|
||||||
markForRetry: markForRetry,
|
|
||||||
ackByServerAt: ackByServerAt,
|
ackByServerAt: ackByServerAt,
|
||||||
retryCount: retryCount,
|
retryCount: retryCount,
|
||||||
lastRetry: lastRetry,
|
lastRetry: lastRetry,
|
||||||
|
|
@ -12106,7 +11984,6 @@ class $$ReceiptsTableTableManager extends RootTableManager<
|
||||||
Value<String?> messageId = const Value.absent(),
|
Value<String?> messageId = const Value.absent(),
|
||||||
required Uint8List message,
|
required Uint8List message,
|
||||||
Value<bool> contactWillSendsReceipt = const Value.absent(),
|
Value<bool> contactWillSendsReceipt = const Value.absent(),
|
||||||
Value<DateTime?> markForRetry = const Value.absent(),
|
|
||||||
Value<DateTime?> ackByServerAt = const Value.absent(),
|
Value<DateTime?> ackByServerAt = const Value.absent(),
|
||||||
Value<int> retryCount = const Value.absent(),
|
Value<int> retryCount = const Value.absent(),
|
||||||
Value<DateTime?> lastRetry = const Value.absent(),
|
Value<DateTime?> lastRetry = const Value.absent(),
|
||||||
|
|
@ -12119,7 +11996,6 @@ class $$ReceiptsTableTableManager extends RootTableManager<
|
||||||
messageId: messageId,
|
messageId: messageId,
|
||||||
message: message,
|
message: message,
|
||||||
contactWillSendsReceipt: contactWillSendsReceipt,
|
contactWillSendsReceipt: contactWillSendsReceipt,
|
||||||
markForRetry: markForRetry,
|
|
||||||
ackByServerAt: ackByServerAt,
|
ackByServerAt: ackByServerAt,
|
||||||
retryCount: retryCount,
|
retryCount: retryCount,
|
||||||
lastRetry: lastRetry,
|
lastRetry: lastRetry,
|
||||||
|
|
|
||||||
|
|
@ -1946,458 +1946,10 @@ final class Schema4 extends i0.VersionedSchema {
|
||||||
i1.GeneratedColumn<int> _column_101(String aliasedName) =>
|
i1.GeneratedColumn<int> _column_101(String aliasedName) =>
|
||||||
i1.GeneratedColumn<int>('affected_contact_id', aliasedName, true,
|
i1.GeneratedColumn<int>('affected_contact_id', aliasedName, true,
|
||||||
type: i1.DriftSqlType.int);
|
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({
|
i0.MigrationStepWithVersion migrationSteps({
|
||||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
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, Schema3 schema) from2To3,
|
||||||
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
|
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
|
||||||
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
|
|
||||||
}) {
|
}) {
|
||||||
return (currentVersion, database) async {
|
return (currentVersion, database) async {
|
||||||
switch (currentVersion) {
|
switch (currentVersion) {
|
||||||
|
|
@ -2416,11 +1968,6 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||||
final migrator = i1.Migrator(database, schema);
|
final migrator = i1.Migrator(database, schema);
|
||||||
await from3To4(migrator, schema);
|
await from3To4(migrator, schema);
|
||||||
return 4;
|
return 4;
|
||||||
case 4:
|
|
||||||
final schema = Schema5(database: database);
|
|
||||||
final migrator = i1.Migrator(database, schema);
|
|
||||||
await from4To5(migrator, schema);
|
|
||||||
return 5;
|
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
}
|
}
|
||||||
|
|
@ -2431,12 +1978,10 @@ i1.OnUpgrade stepByStep({
|
||||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
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, Schema3 schema) from2To3,
|
||||||
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
|
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
|
||||||
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
|
|
||||||
}) =>
|
}) =>
|
||||||
i0.VersionedSchema.stepByStepHelper(
|
i0.VersionedSchema.stepByStepHelper(
|
||||||
step: migrationSteps(
|
step: migrationSteps(
|
||||||
from1To2: from1To2,
|
from1To2: from1To2,
|
||||||
from2To3: from2To3,
|
from2To3: from2To3,
|
||||||
from3To4: from3To4,
|
from3To4: from3To4,
|
||||||
from4To5: from4To5,
|
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift_flutter/drift_flutter.dart'
|
import 'package:drift_flutter/drift_flutter.dart'
|
||||||
show DriftNativeOptions, driftDatabase;
|
show DriftNativeOptions, driftDatabase;
|
||||||
|
|
@ -192,9 +191,9 @@ class TwonlyDatabaseOld extends _$TwonlyDatabaseOld {
|
||||||
await (delete(signalPreKeyStores)
|
await (delete(signalPreKeyStores)
|
||||||
..where(
|
..where(
|
||||||
(t) => (t.createdAt.isSmallerThanValue(
|
(t) => (t.createdAt.isSmallerThanValue(
|
||||||
clock.now().subtract(
|
DateTime.now().subtract(
|
||||||
const Duration(days: 25),
|
const Duration(days: 25),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
.go();
|
.go();
|
||||||
|
|
|
||||||
465
lib/src/localization/app_de.arb
Normal file
465
lib/src/localization/app_de.arb
Normal file
|
|
@ -0,0 +1,465 @@
|
||||||
|
{
|
||||||
|
"@@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.",
|
||||||
|
"registerProofOfWorkFailed": "Beim Captcha-Test gab es ein Problem. Bitte versuche es erneut.",
|
||||||
|
"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{sufix}",
|
||||||
|
"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": "✓ twonly unterstützen",
|
||||||
|
"year": "Jahr",
|
||||||
|
"month": "Monat",
|
||||||
|
"yearly": "Jährlich",
|
||||||
|
"monthly": "Monatlich",
|
||||||
|
"familyFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||||
|
"familyFeature2": "✓ 4 zusätzliche Plus Benutzer",
|
||||||
|
"familyFeature3": "✓ Flammen wiederherstellen",
|
||||||
|
"familyFeature4": "✓ twonly unterstützen",
|
||||||
|
"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": "[Unbekannt]",
|
||||||
|
"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!",
|
||||||
|
"startWithCameraOpen": "Mit geöffneter Kamera starten",
|
||||||
|
"showImagePreviewWhenSending": "Bildvorschau bei der Auswahl von Empfängern anzeigen",
|
||||||
|
"verifiedPublicKey": "Der öffentliche Schlüssel von {username} wurde überprüft und ist gültig.",
|
||||||
|
"memoriesAYearAgo": "Vor einem Jahr",
|
||||||
|
"memoriesXYearsAgo": "Vor {years} Jahren"
|
||||||
|
}
|
||||||
495
lib/src/localization/app_en.arb
Normal file
495
lib/src/localization/app_en.arb
Normal file
|
|
@ -0,0 +1,495 @@
|
||||||
|
{
|
||||||
|
"@@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.",
|
||||||
|
"registerProofOfWorkFailed": "There was an issue with the captcha test. Please try again.",
|
||||||
|
"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 to {planId}{sufix}",
|
||||||
|
"partOfPaidPlanOf": "You are part of the paid plan of {username}!",
|
||||||
|
"year": "year",
|
||||||
|
"yearly": "Yearly",
|
||||||
|
"month": "month",
|
||||||
|
"monthly": "Monthly",
|
||||||
|
"proFeature1": "✓ Unlimited media file uploads",
|
||||||
|
"proFeature2": "✓ 1 additional Plus user",
|
||||||
|
"proFeature3": "✓ Restore flames",
|
||||||
|
"proFeature4": "✓ Support twonly",
|
||||||
|
"familyFeature1": "✓ Unlimited media file uploads",
|
||||||
|
"familyFeature2": "✓ 4 additional Plus user",
|
||||||
|
"familyFeature3": "✓ Restore flames",
|
||||||
|
"familyFeature4": "✓ Support twonly",
|
||||||
|
"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": "[Unknown]",
|
||||||
|
"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!",
|
||||||
|
"startWithCameraOpen": "Start with camera open",
|
||||||
|
"showImagePreviewWhenSending": "Display image preview when selecting recipients",
|
||||||
|
"verifiedPublicKey": "The public key of {username} has been verified and is valid.",
|
||||||
|
"memoriesAYearAgo": "One year ago",
|
||||||
|
"memoriesXYearsAgo": "{years} years ago"
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ import 'package:intl/intl.dart' as intl;
|
||||||
|
|
||||||
import 'app_localizations_de.dart';
|
import 'app_localizations_de.dart';
|
||||||
import 'app_localizations_en.dart';
|
import 'app_localizations_en.dart';
|
||||||
import 'app_localizations_sv.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
|
|
||||||
|
|
@ -96,8 +95,7 @@ abstract class AppLocalizations {
|
||||||
/// A list of this localizations delegate's supported locales.
|
/// A list of this localizations delegate's supported locales.
|
||||||
static const List<Locale> supportedLocales = <Locale>[
|
static const List<Locale> supportedLocales = <Locale>[
|
||||||
Locale('de'),
|
Locale('de'),
|
||||||
Locale('en'),
|
Locale('en')
|
||||||
Locale('sv')
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/// No description provided for @registerTitle.
|
/// No description provided for @registerTitle.
|
||||||
|
|
@ -1402,6 +1400,24 @@ abstract class AppLocalizations {
|
||||||
/// **'✓ Support twonly'**
|
/// **'✓ Support twonly'**
|
||||||
String get familyFeature4;
|
String get familyFeature4;
|
||||||
|
|
||||||
|
/// No description provided for @redeemUserInviteCode.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Or redeem a twonly-Code.'**
|
||||||
|
String get redeemUserInviteCode;
|
||||||
|
|
||||||
|
/// No description provided for @redeemUserInviteCodeTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'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.
|
/// No description provided for @freeFeature1.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -1429,7 +1445,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @manageSubscription.
|
/// No description provided for @manageSubscription.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Manage subscription'**
|
/// **'Manage your subscription'**
|
||||||
String get manageSubscription;
|
String get manageSubscription;
|
||||||
|
|
||||||
/// No description provided for @nextPayment.
|
/// No description provided for @nextPayment.
|
||||||
|
|
@ -2089,7 +2105,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @uploadLimitReached.
|
/// No description provided for @uploadLimitReached.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// 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;
|
String get uploadLimitReached;
|
||||||
|
|
||||||
/// No description provided for @retransmissionRequested.
|
/// No description provided for @retransmissionRequested.
|
||||||
|
|
@ -2853,78 +2869,6 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'{years} years ago'**
|
/// **'{years} years ago'**
|
||||||
String memoriesXYearsAgo(Object years);
|
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
|
class _AppLocalizationsDelegate
|
||||||
|
|
@ -2938,7 +2882,7 @@ class _AppLocalizationsDelegate
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isSupported(Locale locale) =>
|
bool isSupported(Locale locale) =>
|
||||||
<String>['de', 'en', 'sv'].contains(locale.languageCode);
|
<String>['de', 'en'].contains(locale.languageCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
||||||
|
|
@ -2951,8 +2895,6 @@ AppLocalizations lookupAppLocalizations(Locale locale) {
|
||||||
return AppLocalizationsDe();
|
return AppLocalizationsDe();
|
||||||
case 'en':
|
case 'en':
|
||||||
return AppLocalizationsEn();
|
return AppLocalizationsEn();
|
||||||
case 'sv':
|
|
||||||
return AppLocalizationsSv();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw FlutterError(
|
throw FlutterError(
|
||||||
|
|
|
||||||
|
|
@ -734,6 +734,16 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get familyFeature4 => '✓ twonly unterstützen';
|
String get familyFeature4 => '✓ twonly unterstützen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get redeemUserInviteCode => 'Oder löse einen twonly-Code ein.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get redeemUserInviteCodeTitle => 'twonly-Code einlösen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get redeemUserInviteCodeSuccess =>
|
||||||
|
'Dein Plan wurde erfolgreich angepasst.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get freeFeature1 => '✓ 10 Medien-Datei-Uploads pro Tag';
|
String get freeFeature1 => '✓ 10 Medien-Datei-Uploads pro Tag';
|
||||||
|
|
||||||
|
|
@ -865,7 +875,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get checkoutSubmit => 'Kostenpflichtig bestellen';
|
String get checkoutSubmit => 'Kostenpflichtig bestellen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get additionalUsersList => 'Deine zusätzlichen Benutzer';
|
String get additionalUsersList => 'Ihre zusätzlichen Benutzer';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get additionalUsersPlusTokens => 'twonly-Codes für \"Plus\"-Benutzer';
|
String get additionalUsersPlusTokens => 'twonly-Codes für \"Plus\"-Benutzer';
|
||||||
|
|
@ -1580,51 +1590,4 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String memoriesXYearsAgo(Object years) {
|
String memoriesXYearsAgo(Object years) {
|
||||||
return 'Vor $years Jahren';
|
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';
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -728,6 +728,16 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get familyFeature4 => '✓ Support twonly';
|
String get familyFeature4 => '✓ Support twonly';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get redeemUserInviteCode => 'Or redeem a twonly-Code.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get redeemUserInviteCodeTitle => 'Redeem twonly-Code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get redeemUserInviteCodeSuccess =>
|
||||||
|
'Your plan has been successfully adjusted.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get freeFeature1 => '✓ 10 Media file uploads per day';
|
String get freeFeature1 => '✓ 10 Media file uploads per day';
|
||||||
|
|
||||||
|
|
@ -741,7 +751,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get transactionHistory => 'Your transaction history';
|
String get transactionHistory => 'Your transaction history';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get manageSubscription => 'Manage subscription';
|
String get manageSubscription => 'Manage your subscription';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get nextPayment => 'Next payment';
|
String get nextPayment => 'Next payment';
|
||||||
|
|
@ -1101,7 +1111,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get uploadLimitReached =>
|
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
|
@override
|
||||||
String get retransmissionRequested => 'Retransmission requested';
|
String get retransmissionRequested => 'Retransmission requested';
|
||||||
|
|
@ -1570,49 +1580,4 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String memoriesXYearsAgo(Object years) {
|
String memoriesXYearsAgo(Object years) {
|
||||||
return '$years years ago';
|
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
|
|
||||||
|
|
@ -59,8 +59,8 @@ class UserData {
|
||||||
@JsonKey(defaultValue: true)
|
@JsonKey(defaultValue: true)
|
||||||
bool showFeedbackShortcut = true;
|
bool showFeedbackShortcut = true;
|
||||||
|
|
||||||
@JsonKey(defaultValue: false)
|
@JsonKey(defaultValue: true)
|
||||||
bool showShowImagePreviewWhenSending = false;
|
bool showShowImagePreviewWhenSending = true;
|
||||||
|
|
||||||
@JsonKey(defaultValue: true)
|
@JsonKey(defaultValue: true)
|
||||||
bool startWithCameraOpen = true;
|
bool startWithCameraOpen = true;
|
||||||
|
|
@ -72,9 +72,6 @@ class UserData {
|
||||||
@JsonKey(defaultValue: false)
|
@JsonKey(defaultValue: false)
|
||||||
bool storeMediaFilesInGallery = false;
|
bool storeMediaFilesInGallery = false;
|
||||||
|
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
bool autoStoreAllSendUnlimitedMediaFiles = false;
|
|
||||||
|
|
||||||
String? lastPlanBallance;
|
String? lastPlanBallance;
|
||||||
String? additionalUserInvites;
|
String? additionalUserInvites;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
|
||||||
json['requestedAudioPermission'] as bool? ?? false
|
json['requestedAudioPermission'] as bool? ?? false
|
||||||
..showFeedbackShortcut = json['showFeedbackShortcut'] as bool? ?? true
|
..showFeedbackShortcut = json['showFeedbackShortcut'] as bool? ?? true
|
||||||
..showShowImagePreviewWhenSending =
|
..showShowImagePreviewWhenSending =
|
||||||
json['showShowImagePreviewWhenSending'] as bool? ?? false
|
json['showShowImagePreviewWhenSending'] as bool? ?? true
|
||||||
..startWithCameraOpen = json['startWithCameraOpen'] as bool? ?? true
|
..startWithCameraOpen = json['startWithCameraOpen'] as bool? ?? true
|
||||||
..preSelectedEmojies = (json['preSelectedEmojies'] as List<dynamic>?)
|
..preSelectedEmojies = (json['preSelectedEmojies'] as List<dynamic>?)
|
||||||
?.map((e) => e as String)
|
?.map((e) => e as String)
|
||||||
|
|
@ -45,8 +45,6 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
|
||||||
)
|
)
|
||||||
..storeMediaFilesInGallery =
|
..storeMediaFilesInGallery =
|
||||||
json['storeMediaFilesInGallery'] as bool? ?? false
|
json['storeMediaFilesInGallery'] as bool? ?? false
|
||||||
..autoStoreAllSendUnlimitedMediaFiles =
|
|
||||||
json['autoStoreAllSendUnlimitedMediaFiles'] as bool? ?? false
|
|
||||||
..lastPlanBallance = json['lastPlanBallance'] as String?
|
..lastPlanBallance = json['lastPlanBallance'] as String?
|
||||||
..additionalUserInvites = json['additionalUserInvites'] as String?
|
..additionalUserInvites = json['additionalUserInvites'] as String?
|
||||||
..tutorialDisplayed = (json['tutorialDisplayed'] as List<dynamic>?)
|
..tutorialDisplayed = (json['tutorialDisplayed'] as List<dynamic>?)
|
||||||
|
|
@ -104,8 +102,6 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
||||||
'preSelectedEmojies': instance.preSelectedEmojies,
|
'preSelectedEmojies': instance.preSelectedEmojies,
|
||||||
'autoDownloadOptions': instance.autoDownloadOptions,
|
'autoDownloadOptions': instance.autoDownloadOptions,
|
||||||
'storeMediaFilesInGallery': instance.storeMediaFilesInGallery,
|
'storeMediaFilesInGallery': instance.storeMediaFilesInGallery,
|
||||||
'autoStoreAllSendUnlimitedMediaFiles':
|
|
||||||
instance.autoStoreAllSendUnlimitedMediaFiles,
|
|
||||||
'lastPlanBallance': instance.lastPlanBallance,
|
'lastPlanBallance': instance.lastPlanBallance,
|
||||||
'additionalUserInvites': instance.additionalUserInvites,
|
'additionalUserInvites': instance.additionalUserInvites,
|
||||||
'tutorialDisplayed': instance.tutorialDisplayed,
|
'tutorialDisplayed': instance.tutorialDisplayed,
|
||||||
|
|
|
||||||
|
|
@ -2260,69 +2260,6 @@ class ApplicationData_DeleteAccount extends $pb.GeneratedMessage {
|
||||||
static ApplicationData_DeleteAccount? _defaultInstance;
|
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 {
|
enum ApplicationData_ApplicationData {
|
||||||
textMessage,
|
textMessage,
|
||||||
getUserByUsername,
|
getUserByUsername,
|
||||||
|
|
@ -2348,7 +2285,6 @@ enum ApplicationData_ApplicationData {
|
||||||
changeUsername,
|
changeUsername,
|
||||||
ipaPurchase,
|
ipaPurchase,
|
||||||
ipaForceCheck,
|
ipaForceCheck,
|
||||||
addAdditionalUser,
|
|
||||||
notSet
|
notSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2378,7 +2314,6 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
ApplicationData_ChangeUsername? changeUsername,
|
ApplicationData_ChangeUsername? changeUsername,
|
||||||
ApplicationData_IPAPurchase? ipaPurchase,
|
ApplicationData_IPAPurchase? ipaPurchase,
|
||||||
ApplicationData_IPAForceCheck? ipaForceCheck,
|
ApplicationData_IPAForceCheck? ipaForceCheck,
|
||||||
ApplicationData_AddAdditionalUser? addAdditionalUser,
|
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (textMessage != null) result.textMessage = textMessage;
|
if (textMessage != null) result.textMessage = textMessage;
|
||||||
|
|
@ -2413,7 +2348,6 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
if (changeUsername != null) result.changeUsername = changeUsername;
|
if (changeUsername != null) result.changeUsername = changeUsername;
|
||||||
if (ipaPurchase != null) result.ipaPurchase = ipaPurchase;
|
if (ipaPurchase != null) result.ipaPurchase = ipaPurchase;
|
||||||
if (ipaForceCheck != null) result.ipaForceCheck = ipaForceCheck;
|
if (ipaForceCheck != null) result.ipaForceCheck = ipaForceCheck;
|
||||||
if (addAdditionalUser != null) result.addAdditionalUser = addAdditionalUser;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2452,7 +2386,6 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
26: ApplicationData_ApplicationData.changeUsername,
|
26: ApplicationData_ApplicationData.changeUsername,
|
||||||
27: ApplicationData_ApplicationData.ipaPurchase,
|
27: ApplicationData_ApplicationData.ipaPurchase,
|
||||||
28: ApplicationData_ApplicationData.ipaForceCheck,
|
28: ApplicationData_ApplicationData.ipaForceCheck,
|
||||||
29: ApplicationData_ApplicationData.addAdditionalUser,
|
|
||||||
0: ApplicationData_ApplicationData.notSet
|
0: ApplicationData_ApplicationData.notSet
|
||||||
};
|
};
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
|
@ -2484,8 +2417,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
25,
|
25,
|
||||||
26,
|
26,
|
||||||
27,
|
27,
|
||||||
28,
|
28
|
||||||
29
|
|
||||||
])
|
])
|
||||||
..aOM<ApplicationData_TextMessage>(1, _omitFieldNames ? '' : 'textMessage',
|
..aOM<ApplicationData_TextMessage>(1, _omitFieldNames ? '' : 'textMessage',
|
||||||
protoName: 'textMessage',
|
protoName: 'textMessage',
|
||||||
|
|
@ -2576,10 +2508,6 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
28, _omitFieldNames ? '' : 'ipaForceCheck',
|
28, _omitFieldNames ? '' : 'ipaForceCheck',
|
||||||
protoName: 'ipaForceCheck',
|
protoName: 'ipaForceCheck',
|
||||||
subBuilder: ApplicationData_IPAForceCheck.create)
|
subBuilder: ApplicationData_IPAForceCheck.create)
|
||||||
..aOM<ApplicationData_AddAdditionalUser>(
|
|
||||||
29, _omitFieldNames ? '' : 'addAdditionalUser',
|
|
||||||
protoName: 'addAdditionalUser',
|
|
||||||
subBuilder: ApplicationData_AddAdditionalUser.create)
|
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
|
@ -2894,18 +2822,6 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
void clearIpaForceCheck() => $_clearField(28);
|
void clearIpaForceCheck() => $_clearField(28);
|
||||||
@$pb.TagNumber(28)
|
@$pb.TagNumber(28)
|
||||||
ApplicationData_IPAForceCheck ensureIpaForceCheck() => $_ensure(23);
|
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 {
|
class Response_PreKey extends $pb.GeneratedMessage {
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,15 @@ const ApplicationData$json = {
|
||||||
'9': 0,
|
'9': 0,
|
||||||
'10': 'redeemAdditionalCode'
|
'10': 'redeemAdditionalCode'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'removeAdditionalUser',
|
||||||
|
'3': 18,
|
||||||
|
'4': 1,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.client_to_server.ApplicationData.RemoveAdditionalUser',
|
||||||
|
'9': 0,
|
||||||
|
'10': 'removeAdditionalUser'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'1': 'updatePlanOptions',
|
'1': 'updatePlanOptions',
|
||||||
'3': 19,
|
'3': 19,
|
||||||
|
|
@ -470,24 +479,6 @@ const ApplicationData$json = {
|
||||||
'9': 0,
|
'9': 0,
|
||||||
'10': 'ipaForceCheck'
|
'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': [
|
'3': [
|
||||||
ApplicationData_TextMessage$json,
|
ApplicationData_TextMessage$json,
|
||||||
|
|
@ -513,8 +504,7 @@ const ApplicationData$json = {
|
||||||
ApplicationData_ReportUser$json,
|
ApplicationData_ReportUser$json,
|
||||||
ApplicationData_IPAPurchase$json,
|
ApplicationData_IPAPurchase$json,
|
||||||
ApplicationData_IPAForceCheck$json,
|
ApplicationData_IPAForceCheck$json,
|
||||||
ApplicationData_DeleteAccount$json,
|
ApplicationData_DeleteAccount$json
|
||||||
ApplicationData_AddAdditionalUser$json
|
|
||||||
],
|
],
|
||||||
'8': [
|
'8': [
|
||||||
{'1': 'ApplicationData'},
|
{'1': 'ApplicationData'},
|
||||||
|
|
@ -724,14 +714,6 @@ const ApplicationData_DeleteAccount$json = {
|
||||||
'1': 'DeleteAccount',
|
'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`.
|
/// Descriptor for `ApplicationData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
|
||||||
'Cg9BcHBsaWNhdGlvbkRhdGESUQoLdGV4dE1lc3NhZ2UYASABKAsyLS5jbGllbnRfdG9fc2Vydm'
|
'Cg9BcHBsaWNhdGlvbkRhdGESUQoLdGV4dE1lc3NhZ2UYASABKAsyLS5jbGllbnRfdG9fc2Vydm'
|
||||||
|
|
@ -758,51 +740,48 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
|
||||||
'VudHNJbnZpdGVzGBAgASgLMjcuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuR2V0'
|
'VudHNJbnZpdGVzGBAgASgLMjcuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuR2V0'
|
||||||
'QWRkQWNjb3VudHNJbnZpdGVzSABSFWdldEFkZGFjY291bnRzSW52aXRlcxJsChRyZWRlZW1BZG'
|
'QWRkQWNjb3VudHNJbnZpdGVzSABSFWdldEFkZGFjY291bnRzSW52aXRlcxJsChRyZWRlZW1BZG'
|
||||||
'RpdGlvbmFsQ29kZRgRIAEoCzI2LmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLlJl'
|
'RpdGlvbmFsQ29kZRgRIAEoCzI2LmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLlJl'
|
||||||
'ZGVlbUFkZGl0aW9uYWxDb2RlSABSFHJlZGVlbUFkZGl0aW9uYWxDb2RlEmMKEXVwZGF0ZVBsYW'
|
'ZGVlbUFkZGl0aW9uYWxDb2RlSABSFHJlZGVlbUFkZGl0aW9uYWxDb2RlEmwKFHJlbW92ZUFkZG'
|
||||||
'5PcHRpb25zGBMgASgLMjMuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuVXBkYXRl'
|
'l0aW9uYWxVc2VyGBIgASgLMjYuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVt'
|
||||||
'UGxhbk9wdGlvbnNIAFIRdXBkYXRlUGxhbk9wdGlvbnMSVAoMZG93bmxvYWREb25lGBQgASgLMi'
|
'b3ZlQWRkaXRpb25hbFVzZXJIAFIUcmVtb3ZlQWRkaXRpb25hbFVzZXISYwoRdXBkYXRlUGxhbk'
|
||||||
'4uY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuRG93bmxvYWREb25lSABSDGRvd25s'
|
'9wdGlvbnMYEyABKAsyMy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5VcGRhdGVQ'
|
||||||
'b2FkRG9uZRJ1ChdnZXRTaWduZWRQcmVrZXlCeVVzZXJpZBgWIAEoCzI5LmNsaWVudF90b19zZX'
|
'bGFuT3B0aW9uc0gAUhF1cGRhdGVQbGFuT3B0aW9ucxJUCgxkb3dubG9hZERvbmUYFCABKAsyLi'
|
||||||
'J2ZXIuQXBwbGljYXRpb25EYXRhLkdldFNpZ25lZFByZUtleUJ5VXNlcklkSABSF2dldFNpZ25l'
|
'5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5Eb3dubG9hZERvbmVIAFIMZG93bmxv'
|
||||||
'ZFByZWtleUJ5VXNlcmlkEmYKEnVwZGF0ZVNpZ25lZFByZWtleRgXIAEoCzI0LmNsaWVudF90b1'
|
'YWREb25lEnUKF2dldFNpZ25lZFByZWtleUJ5VXNlcmlkGBYgASgLMjkuY2xpZW50X3RvX3Nlcn'
|
||||||
'9zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLlVwZGF0ZVNpZ25lZFByZUtleUgAUhJ1cGRhdGVTaWdu'
|
'Zlci5BcHBsaWNhdGlvbkRhdGEuR2V0U2lnbmVkUHJlS2V5QnlVc2VySWRIAFIXZ2V0U2lnbmVk'
|
||||||
'ZWRQcmVrZXkSVwoNZGVsZXRlQWNjb3VudBgYIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuQXBwbG'
|
'UHJla2V5QnlVc2VyaWQSZgoSdXBkYXRlU2lnbmVkUHJla2V5GBcgASgLMjQuY2xpZW50X3RvX3'
|
||||||
'ljYXRpb25EYXRhLkRlbGV0ZUFjY291bnRIAFINZGVsZXRlQWNjb3VudBJOCgpyZXBvcnRVc2Vy'
|
'NlcnZlci5BcHBsaWNhdGlvbkRhdGEuVXBkYXRlU2lnbmVkUHJlS2V5SABSEnVwZGF0ZVNpZ25l'
|
||||||
'GBkgASgLMiwuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVwb3J0VXNlckgAUg'
|
'ZFByZWtleRJXCg1kZWxldGVBY2NvdW50GBggASgLMi8uY2xpZW50X3RvX3NlcnZlci5BcHBsaW'
|
||||||
'pyZXBvcnRVc2VyEloKDmNoYW5nZVVzZXJuYW1lGBogASgLMjAuY2xpZW50X3RvX3NlcnZlci5B'
|
'NhdGlvbkRhdGEuRGVsZXRlQWNjb3VudEgAUg1kZWxldGVBY2NvdW50Ek4KCnJlcG9ydFVzZXIY'
|
||||||
'cHBsaWNhdGlvbkRhdGEuQ2hhbmdlVXNlcm5hbWVIAFIOY2hhbmdlVXNlcm5hbWUSUQoLaXBhUH'
|
'GSABKAsyLC5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5SZXBvcnRVc2VySABSCn'
|
||||||
'VyY2hhc2UYGyABKAsyLS5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5JUEFQdXJj'
|
'JlcG9ydFVzZXISWgoOY2hhbmdlVXNlcm5hbWUYGiABKAsyMC5jbGllbnRfdG9fc2VydmVyLkFw'
|
||||||
'aGFzZUgAUgtpcGFQdXJjaGFzZRJXCg1pcGFGb3JjZUNoZWNrGBwgASgLMi8uY2xpZW50X3RvX3'
|
'cGxpY2F0aW9uRGF0YS5DaGFuZ2VVc2VybmFtZUgAUg5jaGFuZ2VVc2VybmFtZRJRCgtpcGFQdX'
|
||||||
'NlcnZlci5BcHBsaWNhdGlvbkRhdGEuSVBBRm9yY2VDaGVja0gAUg1pcGFGb3JjZUNoZWNrEmwK'
|
'JjaGFzZRgbIAEoCzItLmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLklQQVB1cmNo'
|
||||||
'FHJlbW92ZUFkZGl0aW9uYWxVc2VyGBIgASgLMjYuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdG'
|
'YXNlSABSC2lwYVB1cmNoYXNlElcKDWlwYUZvcmNlQ2hlY2sYHCABKAsyLy5jbGllbnRfdG9fc2'
|
||||||
'lvbkRhdGEuUmVtb3ZlQWRkaXRpb25hbFVzZXJIAFIUcmVtb3ZlQWRkaXRpb25hbFVzZXISYwoR'
|
'VydmVyLkFwcGxpY2F0aW9uRGF0YS5JUEFGb3JjZUNoZWNrSABSDWlwYUZvcmNlQ2hlY2saagoL'
|
||||||
'YWRkQWRkaXRpb25hbFVzZXIYHSABKAsyMy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRG'
|
'VGV4dE1lc3NhZ2USFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEhIKBGJvZHkYAyABKAxSBGJvZH'
|
||||||
'F0YS5BZGRBZGRpdGlvbmFsVXNlckgAUhFhZGRBZGRpdGlvbmFsVXNlchpqCgtUZXh0TWVzc2Fn'
|
'kSIAoJcHVzaF9kYXRhGAQgASgMSABSCHB1c2hEYXRhiAEBQgwKCl9wdXNoX2RhdGEaLwoRR2V0'
|
||||||
'ZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgDIAEoDFIEYm9keRIgCglwdXNoX2'
|
'VXNlckJ5VXNlcm5hbWUSGgoIdXNlcm5hbWUYASABKAlSCHVzZXJuYW1lGiwKDkNoYW5nZVVzZX'
|
||||||
'RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YRovChFHZXRVc2VyQnlVc2Vy'
|
'JuYW1lEhoKCHVzZXJuYW1lGAEgASgJUgh1c2VybmFtZRo1ChRVcGRhdGVHb29nbGVGY21Ub2tl'
|
||||||
'bmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaLAoOQ2hhbmdlVXNlcm5hbWUSGgoIdX'
|
'bhIdCgpnb29nbGVfZmNtGAEgASgJUglnb29nbGVGY20aJgoLR2V0VXNlckJ5SWQSFwoHdXNlcl'
|
||||||
'Nlcm5hbWUYASABKAlSCHVzZXJuYW1lGjUKFFVwZGF0ZUdvb2dsZUZjbVRva2VuEh0KCmdvb2ds'
|
'9pZBgBIAEoA1IGdXNlcklkGikKDVJlZGVlbVZvdWNoZXISGAoHdm91Y2hlchgBIAEoCVIHdm91'
|
||||||
'ZV9mY20YASABKAlSCWdvb2dsZUZjbRomCgtHZXRVc2VyQnlJZBIXCgd1c2VyX2lkGAEgASgDUg'
|
'Y2hlchpwChFTd2l0Y2hUb1BheWVkUGxhbhIXCgdwbGFuX2lkGAEgASgJUgZwbGFuSWQSHwoLcG'
|
||||||
'Z1c2VySWQaKQoNUmVkZWVtVm91Y2hlchIYCgd2b3VjaGVyGAEgASgJUgd2b3VjaGVyGnAKEVN3'
|
'F5X21vbnRobHkYAiABKAhSCnBheU1vbnRobHkSIQoMYXV0b19yZW5ld2FsGAMgASgIUgthdXRv'
|
||||||
'aXRjaFRvUGF5ZWRQbGFuEhcKB3BsYW5faWQYASABKAlSBnBsYW5JZBIfCgtwYXlfbW9udGhseR'
|
'UmVuZXdhbBo2ChFVcGRhdGVQbGFuT3B0aW9ucxIhCgxhdXRvX3JlbmV3YWwYASABKAhSC2F1dG'
|
||||||
'gCIAEoCFIKcGF5TW9udGhseRIhCgxhdXRvX3JlbmV3YWwYAyABKAhSC2F1dG9SZW5ld2FsGjYK'
|
'9SZW5ld2FsGjAKDUNyZWF0ZVZvdWNoZXISHwoLdmFsdWVfY2VudHMYASABKA1SCnZhbHVlQ2Vu'
|
||||||
'EVVwZGF0ZVBsYW5PcHRpb25zEiEKDGF1dG9fcmVuZXdhbBgBIAEoCFILYXV0b1JlbmV3YWwaMA'
|
'dHMaDQoLR2V0TG9jYXRpb24aDQoLR2V0Vm91Y2hlcnMaEwoRR2V0QXZhaWxhYmxlUGxhbnMaFw'
|
||||||
'oNQ3JlYXRlVm91Y2hlchIfCgt2YWx1ZV9jZW50cxgBIAEoDVIKdmFsdWVDZW50cxoNCgtHZXRM'
|
'oVR2V0QWRkQWNjb3VudHNJbnZpdGVzGhUKE0dldEN1cnJlbnRQbGFuSW5mb3MaNwoUUmVkZWVt'
|
||||||
'b2NhdGlvbhoNCgtHZXRWb3VjaGVycxoTChFHZXRBdmFpbGFibGVQbGFucxoXChVHZXRBZGRBY2'
|
'QWRkaXRpb25hbENvZGUSHwoLaW52aXRlX2NvZGUYAiABKAlSCmludml0ZUNvZGUaLwoUUmVtb3'
|
||||||
'NvdW50c0ludml0ZXMaFQoTR2V0Q3VycmVudFBsYW5JbmZvcxo3ChRSZWRlZW1BZGRpdGlvbmFs'
|
'ZlQWRkaXRpb25hbFVzZXISFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGi0KEkdldFByZWtleXNC'
|
||||||
'Q29kZRIfCgtpbnZpdGVfY29kZRgCIAEoCVIKaW52aXRlQ29kZRovChRSZW1vdmVBZGRpdGlvbm'
|
'eVVzZXJJZBIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaMgoXR2V0U2lnbmVkUHJlS2V5QnlVc2'
|
||||||
'FsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaLQoSR2V0UHJla2V5c0J5VXNlcklkEhcK'
|
'VySWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGpsBChJVcGRhdGVTaWduZWRQcmVLZXkSKAoQ'
|
||||||
'B3VzZXJfaWQYASABKANSBnVzZXJJZBoyChdHZXRTaWduZWRQcmVLZXlCeVVzZXJJZBIXCgd1c2'
|
'c2lnbmVkX3ByZWtleV9pZBgBIAEoA1IOc2lnbmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtleR'
|
||||||
'VyX2lkGAEgASgDUgZ1c2VySWQamwEKElVwZGF0ZVNpZ25lZFByZUtleRIoChBzaWduZWRfcHJl'
|
'gCIAEoDFIMc2lnbmVkUHJla2V5EjYKF3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUhVz'
|
||||||
'a2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZXlJZBIjCg1zaWduZWRfcHJla2V5GAIgASgMUgxzaW'
|
'aWduZWRQcmVrZXlTaWduYXR1cmUaNQoMRG93bmxvYWREb25lEiUKDmRvd25sb2FkX3Rva2VuGA'
|
||||||
'duZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaWduYXR1cmUYAyABKAxSFXNpZ25lZFByZWtl'
|
'EgASgMUg1kb3dubG9hZFRva2VuGk4KClJlcG9ydFVzZXISKAoQcmVwb3J0ZWRfdXNlcl9pZBgB'
|
||||||
'eVNpZ25hdHVyZRo1CgxEb3dubG9hZERvbmUSJQoOZG93bmxvYWRfdG9rZW4YASABKAxSDWRvd2'
|
'IAEoA1IOcmVwb3J0ZWRVc2VySWQSFgoGcmVhc29uGAIgASgJUgZyZWFzb24acQoLSVBBUHVyY2'
|
||||||
'5sb2FkVG9rZW4aTgoKUmVwb3J0VXNlchIoChByZXBvcnRlZF91c2VyX2lkGAEgASgDUg5yZXBv'
|
'hhc2USHQoKcHJvZHVjdF9pZBgBIAEoCVIJcHJvZHVjdElkEhYKBnNvdXJjZRgCIAEoCVIGc291'
|
||||||
'cnRlZFVzZXJJZBIWCgZyZWFzb24YAiABKAlSBnJlYXNvbhpxCgtJUEFQdXJjaGFzZRIdCgpwcm'
|
'cmNlEisKEXZlcmlmaWNhdGlvbl9kYXRhGAMgASgJUhB2ZXJpZmljYXRpb25EYXRhGg8KDUlQQU'
|
||||||
'9kdWN0X2lkGAEgASgJUglwcm9kdWN0SWQSFgoGc291cmNlGAIgASgJUgZzb3VyY2USKwoRdmVy'
|
'ZvcmNlQ2hlY2saDwoNRGVsZXRlQWNjb3VudEIRCg9BcHBsaWNhdGlvbkRhdGE=');
|
||||||
'aWZpY2F0aW9uX2RhdGEYAyABKAlSEHZlcmlmaWNhdGlvbkRhdGEaDwoNSVBBRm9yY2VDaGVjax'
|
|
||||||
'oPCg1EZWxldGVBY2NvdW50GiwKEUFkZEFkZGl0aW9uYWxVc2VyEhcKB3VzZXJfaWQYASABKANS'
|
|
||||||
'BnVzZXJJZEIRCg9BcHBsaWNhdGlvbkRhdGE=');
|
|
||||||
|
|
||||||
@$core.Deprecated('Use responseDescriptor instead')
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
const Response$json = {
|
const Response$json = {
|
||||||
|
|
|
||||||
|
|
@ -89,8 +89,6 @@ class ErrorCode extends $pb.ProtobufEnum {
|
||||||
ErrorCode._(1033, _omitEnumNames ? '' : 'RegistrationDisabled');
|
ErrorCode._(1033, _omitEnumNames ? '' : 'RegistrationDisabled');
|
||||||
static const ErrorCode IPAPaymentExpired =
|
static const ErrorCode IPAPaymentExpired =
|
||||||
ErrorCode._(1034, _omitEnumNames ? '' : 'IPAPaymentExpired');
|
ErrorCode._(1034, _omitEnumNames ? '' : 'IPAPaymentExpired');
|
||||||
static const ErrorCode UserIsNotInFreePlan =
|
|
||||||
ErrorCode._(1035, _omitEnumNames ? '' : 'UserIsNotInFreePlan');
|
|
||||||
|
|
||||||
static const $core.List<ErrorCode> values = <ErrorCode>[
|
static const $core.List<ErrorCode> values = <ErrorCode>[
|
||||||
Unknown,
|
Unknown,
|
||||||
|
|
@ -130,7 +128,6 @@ class ErrorCode extends $pb.ProtobufEnum {
|
||||||
InvalidProofOfWork,
|
InvalidProofOfWork,
|
||||||
RegistrationDisabled,
|
RegistrationDisabled,
|
||||||
IPAPaymentExpired,
|
IPAPaymentExpired,
|
||||||
UserIsNotInFreePlan,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
static final $core.Map<$core.int, ErrorCode> _byValue =
|
static final $core.Map<$core.int, ErrorCode> _byValue =
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ const ErrorCode$json = {
|
||||||
{'1': 'InvalidProofOfWork', '2': 1032},
|
{'1': 'InvalidProofOfWork', '2': 1032},
|
||||||
{'1': 'RegistrationDisabled', '2': 1033},
|
{'1': 'RegistrationDisabled', '2': 1033},
|
||||||
{'1': 'IPAPaymentExpired', '2': 1034},
|
{'1': 'IPAPaymentExpired', '2': 1034},
|
||||||
{'1': 'UserIsNotInFreePlan', '2': 1035},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -77,4 +76,4 @@ final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode(
|
||||||
'duZWRQcmVLZXkQgwgSEwoOVXNlcklkTm90Rm91bmQQhAgSFwoSVXNlcklkQWxyZWFkeVRha2Vu'
|
'duZWRQcmVLZXkQgwgSEwoOVXNlcklkTm90Rm91bmQQhAgSFwoSVXNlcklkQWxyZWFkeVRha2Vu'
|
||||||
'EIUIEhcKEkFwcFZlcnNpb25PdXRkYXRlZBCGCBIYChNOZXdEZXZpY2VSZWdpc3RlcmVkEIcIEh'
|
'EIUIEhcKEkFwcFZlcnNpb25PdXRkYXRlZBCGCBIYChNOZXdEZXZpY2VSZWdpc3RlcmVkEIcIEh'
|
||||||
'cKEkludmFsaWRQcm9vZk9mV29yaxCICBIZChRSZWdpc3RyYXRpb25EaXNhYmxlZBCJCBIWChFJ'
|
'cKEkludmFsaWRQcm9vZk9mV29yaxCICBIZChRSZWdpc3RyYXRpb25EaXNhYmxlZBCJCBIWChFJ'
|
||||||
'UEFQYXltZW50RXhwaXJlZBCKCBIYChNVc2VySXNOb3RJbkZyZWVQbGFuEIsI');
|
'UEFQYXltZW50RXhwaXJlZBCKCA==');
|
||||||
|
|
|
||||||
|
|
@ -428,6 +428,7 @@ class Response_Plan extends $pb.GeneratedMessage {
|
||||||
$fixnum.Int64? dailyMediaUploadLimit,
|
$fixnum.Int64? dailyMediaUploadLimit,
|
||||||
$fixnum.Int64? maximalUploadSizeOfSingleMediaSize,
|
$fixnum.Int64? maximalUploadSizeOfSingleMediaSize,
|
||||||
$fixnum.Int64? additionalPlusAccounts,
|
$fixnum.Int64? additionalPlusAccounts,
|
||||||
|
$fixnum.Int64? additionalFreeAccounts,
|
||||||
$fixnum.Int64? monthlyCostsCent,
|
$fixnum.Int64? monthlyCostsCent,
|
||||||
$fixnum.Int64? yearlyCostsCent,
|
$fixnum.Int64? yearlyCostsCent,
|
||||||
$core.bool? allowedToSendTextMessages,
|
$core.bool? allowedToSendTextMessages,
|
||||||
|
|
@ -443,6 +444,8 @@ class Response_Plan extends $pb.GeneratedMessage {
|
||||||
maximalUploadSizeOfSingleMediaSize;
|
maximalUploadSizeOfSingleMediaSize;
|
||||||
if (additionalPlusAccounts != null)
|
if (additionalPlusAccounts != null)
|
||||||
result.additionalPlusAccounts = additionalPlusAccounts;
|
result.additionalPlusAccounts = additionalPlusAccounts;
|
||||||
|
if (additionalFreeAccounts != null)
|
||||||
|
result.additionalFreeAccounts = additionalFreeAccounts;
|
||||||
if (monthlyCostsCent != null) result.monthlyCostsCent = monthlyCostsCent;
|
if (monthlyCostsCent != null) result.monthlyCostsCent = monthlyCostsCent;
|
||||||
if (yearlyCostsCent != null) result.yearlyCostsCent = yearlyCostsCent;
|
if (yearlyCostsCent != null) result.yearlyCostsCent = yearlyCostsCent;
|
||||||
if (allowedToSendTextMessages != null)
|
if (allowedToSendTextMessages != null)
|
||||||
|
|
@ -471,6 +474,7 @@ class Response_Plan extends $pb.GeneratedMessage {
|
||||||
..aInt64(3, _omitFieldNames ? '' : 'dailyMediaUploadLimit')
|
..aInt64(3, _omitFieldNames ? '' : 'dailyMediaUploadLimit')
|
||||||
..aInt64(4, _omitFieldNames ? '' : 'maximalUploadSizeOfSingleMediaSize')
|
..aInt64(4, _omitFieldNames ? '' : 'maximalUploadSizeOfSingleMediaSize')
|
||||||
..aInt64(5, _omitFieldNames ? '' : 'additionalPlusAccounts')
|
..aInt64(5, _omitFieldNames ? '' : 'additionalPlusAccounts')
|
||||||
|
..aInt64(6, _omitFieldNames ? '' : 'additionalFreeAccounts')
|
||||||
..aInt64(7, _omitFieldNames ? '' : 'monthlyCostsCent')
|
..aInt64(7, _omitFieldNames ? '' : 'monthlyCostsCent')
|
||||||
..aInt64(8, _omitFieldNames ? '' : 'yearlyCostsCent')
|
..aInt64(8, _omitFieldNames ? '' : 'yearlyCostsCent')
|
||||||
..aOB(9, _omitFieldNames ? '' : 'allowedToSendTextMessages')
|
..aOB(9, _omitFieldNames ? '' : 'allowedToSendTextMessages')
|
||||||
|
|
@ -544,39 +548,48 @@ class Response_Plan extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
void clearAdditionalPlusAccounts() => $_clearField(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)
|
@$pb.TagNumber(7)
|
||||||
$fixnum.Int64 get monthlyCostsCent => $_getI64(5);
|
$fixnum.Int64 get monthlyCostsCent => $_getI64(6);
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
set monthlyCostsCent($fixnum.Int64 value) => $_setInt64(5, value);
|
set monthlyCostsCent($fixnum.Int64 value) => $_setInt64(6, value);
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
$core.bool hasMonthlyCostsCent() => $_has(5);
|
$core.bool hasMonthlyCostsCent() => $_has(6);
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
void clearMonthlyCostsCent() => $_clearField(7);
|
void clearMonthlyCostsCent() => $_clearField(7);
|
||||||
|
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
$fixnum.Int64 get yearlyCostsCent => $_getI64(6);
|
$fixnum.Int64 get yearlyCostsCent => $_getI64(7);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
set yearlyCostsCent($fixnum.Int64 value) => $_setInt64(6, value);
|
set yearlyCostsCent($fixnum.Int64 value) => $_setInt64(7, value);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
$core.bool hasYearlyCostsCent() => $_has(6);
|
$core.bool hasYearlyCostsCent() => $_has(7);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
void clearYearlyCostsCent() => $_clearField(8);
|
void clearYearlyCostsCent() => $_clearField(8);
|
||||||
|
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
$core.bool get allowedToSendTextMessages => $_getBF(7);
|
$core.bool get allowedToSendTextMessages => $_getBF(8);
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
set allowedToSendTextMessages($core.bool value) => $_setBool(7, value);
|
set allowedToSendTextMessages($core.bool value) => $_setBool(8, value);
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
$core.bool hasAllowedToSendTextMessages() => $_has(7);
|
$core.bool hasAllowedToSendTextMessages() => $_has(8);
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
void clearAllowedToSendTextMessages() => $_clearField(9);
|
void clearAllowedToSendTextMessages() => $_clearField(9);
|
||||||
|
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
$core.bool get isAdditionalAccount => $_getBF(8);
|
$core.bool get isAdditionalAccount => $_getBF(9);
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
set isAdditionalAccount($core.bool value) => $_setBool(8, value);
|
set isAdditionalAccount($core.bool value) => $_setBool(9, value);
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
$core.bool hasIsAdditionalAccount() => $_has(8);
|
$core.bool hasIsAdditionalAccount() => $_has(9);
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
void clearIsAdditionalAccount() => $_clearField(10);
|
void clearIsAdditionalAccount() => $_clearField(10);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,13 @@ const Response_Plan$json = {
|
||||||
'5': 3,
|
'5': 3,
|
||||||
'10': 'additionalPlusAccounts'
|
'10': 'additionalPlusAccounts'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'additional_free_accounts',
|
||||||
|
'3': 6,
|
||||||
|
'4': 1,
|
||||||
|
'5': 3,
|
||||||
|
'10': 'additionalFreeAccounts'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'1': 'monthly_costs_cent',
|
'1': 'monthly_costs_cent',
|
||||||
'3': 7,
|
'3': 7,
|
||||||
|
|
@ -706,77 +713,78 @@ const Response_TransactionTypes$json = {
|
||||||
final $typed_data.Uint8List responseDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List responseDescriptor = $convert.base64Decode(
|
||||||
'CghSZXNwb25zZRIvCgJvaxgBIAEoCzIdLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuT2tIAF'
|
'CghSZXNwb25zZRIvCgJvaxgBIAEoCzIdLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuT2tIAF'
|
||||||
'ICb2sSKAoFZXJyb3IYAiABKA4yEC5lcnJvci5FcnJvckNvZGVIAFIFZXJyb3IaIwoNQXV0aGVu'
|
'ICb2sSKAoFZXJyb3IYAiABKA4yEC5lcnJvci5FcnJvckNvZGVIAFIFZXJyb3IaIwoNQXV0aGVu'
|
||||||
'dGljYXRlZBISCgRwbGFuGAEgASgJUgRwbGFuGuQDCgRQbGFuEhcKB3BsYW5faWQYASABKAlSBn'
|
'dGljYXRlZBISCgRwbGFuGAEgASgJUgRwbGFuGp4ECgRQbGFuEhcKB3BsYW5faWQYASABKAlSBn'
|
||||||
'BsYW5JZBIqChF1cGxvYWRfc2l6ZV9saW1pdBgCIAEoA1IPdXBsb2FkU2l6ZUxpbWl0EjcKGGRh'
|
'BsYW5JZBIqChF1cGxvYWRfc2l6ZV9saW1pdBgCIAEoA1IPdXBsb2FkU2l6ZUxpbWl0EjcKGGRh'
|
||||||
'aWx5X21lZGlhX3VwbG9hZF9saW1pdBgDIAEoA1IVZGFpbHlNZWRpYVVwbG9hZExpbWl0ElQKKG'
|
'aWx5X21lZGlhX3VwbG9hZF9saW1pdBgDIAEoA1IVZGFpbHlNZWRpYVVwbG9hZExpbWl0ElQKKG'
|
||||||
'1heGltYWxfdXBsb2FkX3NpemVfb2Zfc2luZ2xlX21lZGlhX3NpemUYBCABKANSIm1heGltYWxV'
|
'1heGltYWxfdXBsb2FkX3NpemVfb2Zfc2luZ2xlX21lZGlhX3NpemUYBCABKANSIm1heGltYWxV'
|
||||||
'cGxvYWRTaXplT2ZTaW5nbGVNZWRpYVNpemUSOAoYYWRkaXRpb25hbF9wbHVzX2FjY291bnRzGA'
|
'cGxvYWRTaXplT2ZTaW5nbGVNZWRpYVNpemUSOAoYYWRkaXRpb25hbF9wbHVzX2FjY291bnRzGA'
|
||||||
'UgASgDUhZhZGRpdGlvbmFsUGx1c0FjY291bnRzEiwKEm1vbnRobHlfY29zdHNfY2VudBgHIAEo'
|
'UgASgDUhZhZGRpdGlvbmFsUGx1c0FjY291bnRzEjgKGGFkZGl0aW9uYWxfZnJlZV9hY2NvdW50'
|
||||||
'A1IQbW9udGhseUNvc3RzQ2VudBIqChF5ZWFybHlfY29zdHNfY2VudBgIIAEoA1IPeWVhcmx5Q2'
|
'cxgGIAEoA1IWYWRkaXRpb25hbEZyZWVBY2NvdW50cxIsChJtb250aGx5X2Nvc3RzX2NlbnQYBy'
|
||||||
'9zdHNDZW50EkAKHWFsbG93ZWRfdG9fc2VuZF90ZXh0X21lc3NhZ2VzGAkgASgIUhlhbGxvd2Vk'
|
'ABKANSEG1vbnRobHlDb3N0c0NlbnQSKgoReWVhcmx5X2Nvc3RzX2NlbnQYCCABKANSD3llYXJs'
|
||||||
'VG9TZW5kVGV4dE1lc3NhZ2VzEjIKFWlzX2FkZGl0aW9uYWxfYWNjb3VudBgKIAEoCFITaXNBZG'
|
'eUNvc3RzQ2VudBJACh1hbGxvd2VkX3RvX3NlbmRfdGV4dF9tZXNzYWdlcxgJIAEoCFIZYWxsb3'
|
||||||
'RpdGlvbmFsQWNjb3VudBo+CgVQbGFucxI1CgVwbGFucxgBIAMoCzIfLnNlcnZlcl90b19jbGll'
|
'dlZFRvU2VuZFRleHRNZXNzYWdlcxIyChVpc19hZGRpdGlvbmFsX2FjY291bnQYCiABKAhSE2lz'
|
||||||
'bnQuUmVzcG9uc2UuUGxhblIFcGxhbnMaTQoRQWRkQWNjb3VudHNJbnZpdGUSFwoHcGxhbl9pZB'
|
'QWRkaXRpb25hbEFjY291bnQaPgoFUGxhbnMSNQoFcGxhbnMYASADKAsyHy5zZXJ2ZXJfdG9fY2'
|
||||||
'gBIAEoCVIGcGxhbklkEh8KC2ludml0ZV9jb2RlGAIgASgJUgppbnZpdGVDb2RlGlwKEkFkZEFj'
|
'xpZW50LlJlc3BvbnNlLlBsYW5SBXBsYW5zGk0KEUFkZEFjY291bnRzSW52aXRlEhcKB3BsYW5f'
|
||||||
'Y291bnRzSW52aXRlcxJGCgdpbnZpdGVzGAEgAygLMiwuc2VydmVyX3RvX2NsaWVudC5SZXNwb2'
|
'aWQYASABKAlSBnBsYW5JZBIfCgtpbnZpdGVfY29kZRgCIAEoCVIKaW52aXRlQ29kZRpcChJBZG'
|
||||||
'5zZS5BZGRBY2NvdW50c0ludml0ZVIHaW52aXRlcxrFAQoLVHJhbnNhY3Rpb24SIwoNZGVwb3Np'
|
'RBY2NvdW50c0ludml0ZXMSRgoHaW52aXRlcxgBIAMoCzIsLnNlcnZlcl90b19jbGllbnQuUmVz'
|
||||||
'dF9jZW50cxgBIAEoA1IMZGVwb3NpdENlbnRzElYKEHRyYW5zYWN0aW9uX3R5cGUYAiABKA4yKy'
|
'cG9uc2UuQWRkQWNjb3VudHNJbnZpdGVSB2ludml0ZXMaxQEKC1RyYW5zYWN0aW9uEiMKDWRlcG'
|
||||||
'5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlRyYW5zYWN0aW9uVHlwZXNSD3RyYW5zYWN0aW9u'
|
'9zaXRfY2VudHMYASABKANSDGRlcG9zaXRDZW50cxJWChB0cmFuc2FjdGlvbl90eXBlGAIgASgO'
|
||||||
'VHlwZRI5ChljcmVhdGVkX2F0X3VuaXhfdGltZXN0YW1wGAMgASgDUhZjcmVhdGVkQXRVbml4VG'
|
'Misuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5UcmFuc2FjdGlvblR5cGVzUg90cmFuc2FjdG'
|
||||||
'ltZXN0YW1wGkUKEUFkZGl0aW9uYWxBY2NvdW50EhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIX'
|
'lvblR5cGUSOQoZY3JlYXRlZF9hdF91bml4X3RpbWVzdGFtcBgDIAEoA1IWY3JlYXRlZEF0VW5p'
|
||||||
'CgdwbGFuX2lkGAMgASgJUgZwbGFuSWQavgEKB1ZvdWNoZXISHQoKdm91Y2hlcl9pZBgBIAEoCV'
|
'eFRpbWVzdGFtcBpFChFBZGRpdGlvbmFsQWNjb3VudBIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySW'
|
||||||
'IJdm91Y2hlcklkEh8KC3ZhbHVlX2NlbnRzGAIgASgDUgp2YWx1ZUNlbnRzEhoKCHJlZGVlbWVk'
|
'QSFwoHcGxhbl9pZBgDIAEoCVIGcGxhbklkGr4BCgdWb3VjaGVyEh0KCnZvdWNoZXJfaWQYASAB'
|
||||||
'GAMgASgIUghyZWRlZW1lZBIcCglyZXF1ZXN0ZWQYBCABKAhSCXJlcXVlc3RlZBI5ChljcmVhdG'
|
'KAlSCXZvdWNoZXJJZBIfCgt2YWx1ZV9jZW50cxgCIAEoA1IKdmFsdWVDZW50cxIaCghyZWRlZW'
|
||||||
'VkX2F0X3VuaXhfdGltZXN0YW1wGAUgASgDUhZjcmVhdGVkQXRVbml4VGltZXN0YW1wGkoKCFZv'
|
'1lZBgDIAEoCFIIcmVkZWVtZWQSHAoJcmVxdWVzdGVkGAQgASgIUglyZXF1ZXN0ZWQSOQoZY3Jl'
|
||||||
'dWNoZXJzEj4KCHZvdWNoZXJzGAEgAygLMiIuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5Wb3'
|
'YXRlZF9hdF91bml4X3RpbWVzdGFtcBgFIAEoA1IWY3JlYXRlZEF0VW5peFRpbWVzdGFtcBpKCg'
|
||||||
'VjaGVyUgh2b3VjaGVycxqXBQoMUGxhbkJhbGxhbmNlEkAKHXVzZWRfZGFpbHlfbWVkaWFfdXBs'
|
'hWb3VjaGVycxI+Cgh2b3VjaGVycxgBIAMoCzIiLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2Uu'
|
||||||
'b2FkX2xpbWl0GAEgASgDUhl1c2VkRGFpbHlNZWRpYVVwbG9hZExpbWl0Ej4KHHVzZWRfdXBsb2'
|
'Vm91Y2hlclIIdm91Y2hlcnMalwUKDFBsYW5CYWxsYW5jZRJACh11c2VkX2RhaWx5X21lZGlhX3'
|
||||||
'FkX21lZGlhX3NpemVfbGltaXQYAiABKANSGHVzZWRVcGxvYWRNZWRpYVNpemVMaW1pdBIzChNw'
|
'VwbG9hZF9saW1pdBgBIAEoA1IZdXNlZERhaWx5TWVkaWFVcGxvYWRMaW1pdBI+Chx1c2VkX3Vw'
|
||||||
'YXltZW50X3BlcmlvZF9kYXlzGAMgASgDSABSEXBheW1lbnRQZXJpb2REYXlziAEBEksKIGxhc3'
|
'bG9hZF9tZWRpYV9zaXplX2xpbWl0GAIgASgDUhh1c2VkVXBsb2FkTWVkaWFTaXplTGltaXQSMw'
|
||||||
'RfcGF5bWVudF9kb25lX3VuaXhfdGltZXN0YW1wGAQgASgDSAFSHGxhc3RQYXltZW50RG9uZVVu'
|
'oTcGF5bWVudF9wZXJpb2RfZGF5cxgDIAEoA0gAUhFwYXltZW50UGVyaW9kRGF5c4gBARJLCiBs'
|
||||||
'aXhUaW1lc3RhbXCIAQESSgoMdHJhbnNhY3Rpb25zGAUgAygLMiYuc2VydmVyX3RvX2NsaWVudC'
|
'YXN0X3BheW1lbnRfZG9uZV91bml4X3RpbWVzdGFtcBgEIAEoA0gBUhxsYXN0UGF5bWVudERvbm'
|
||||||
'5SZXNwb25zZS5UcmFuc2FjdGlvblIMdHJhbnNhY3Rpb25zEl0KE2FkZGl0aW9uYWxfYWNjb3Vu'
|
'VVbml4VGltZXN0YW1wiAEBEkoKDHRyYW5zYWN0aW9ucxgFIAMoCzImLnNlcnZlcl90b19jbGll'
|
||||||
'dHMYBiADKAsyLC5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkFkZGl0aW9uYWxBY2NvdW50Uh'
|
'bnQuUmVzcG9uc2UuVHJhbnNhY3Rpb25SDHRyYW5zYWN0aW9ucxJdChNhZGRpdGlvbmFsX2FjY2'
|
||||||
'JhZGRpdGlvbmFsQWNjb3VudHMSJgoMYXV0b19yZW5ld2FsGAcgASgISAJSC2F1dG9SZW5ld2Fs'
|
'91bnRzGAYgAygLMiwuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5BZGRpdGlvbmFsQWNjb3Vu'
|
||||||
'iAEBEkIKG2FkZGl0aW9uYWxfYWNjb3VudF9vd25lcl9pZBgIIAEoA0gDUhhhZGRpdGlvbmFsQW'
|
'dFISYWRkaXRpb25hbEFjY291bnRzEiYKDGF1dG9fcmVuZXdhbBgHIAEoCEgCUgthdXRvUmVuZX'
|
||||||
'Njb3VudE93bmVySWSIAQFCFgoUX3BheW1lbnRfcGVyaW9kX2RheXNCIwohX2xhc3RfcGF5bWVu'
|
'dhbIgBARJCChthZGRpdGlvbmFsX2FjY291bnRfb3duZXJfaWQYCCABKANIA1IYYWRkaXRpb25h'
|
||||||
'dF9kb25lX3VuaXhfdGltZXN0YW1wQg8KDV9hdXRvX3JlbmV3YWxCHgocX2FkZGl0aW9uYWxfYW'
|
'bEFjY291bnRPd25lcklkiAEBQhYKFF9wYXltZW50X3BlcmlvZF9kYXlzQiMKIV9sYXN0X3BheW'
|
||||||
'Njb3VudF9vd25lcl9pZBpOCghMb2NhdGlvbhIWCgZjb3VudHkYASABKAlSBmNvdW50eRIWCgZy'
|
'1lbnRfZG9uZV91bml4X3RpbWVzdGFtcEIPCg1fYXV0b19yZW5ld2FsQh4KHF9hZGRpdGlvbmFs'
|
||||||
'ZWdpb24YAiABKAlSBnJlZ2lvbhISCgRjaXR5GAMgASgJUgRjaXR5GjAKBlByZUtleRIOCgJpZB'
|
'X2FjY291bnRfb3duZXJfaWQaTgoITG9jYXRpb24SFgoGY291bnR5GAEgASgJUgZjb3VudHkSFg'
|
||||||
'gBIAEoA1ICaWQSFgoGcHJla2V5GAIgASgMUgZwcmVrZXkalQEKDFNpZ25lZFByZUtleRIoChBz'
|
'oGcmVnaW9uGAIgASgJUgZyZWdpb24SEgoEY2l0eRgDIAEoCVIEY2l0eRowCgZQcmVLZXkSDgoC'
|
||||||
'aWduZWRfcHJla2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZXlJZBIjCg1zaWduZWRfcHJla2V5GA'
|
'aWQYASABKANSAmlkEhYKBnByZWtleRgCIAEoDFIGcHJla2V5GpUBCgxTaWduZWRQcmVLZXkSKA'
|
||||||
'IgASgMUgxzaWduZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaWduYXR1cmUYAyABKAxSFXNp'
|
'oQc2lnbmVkX3ByZWtleV9pZBgBIAEoA1IOc2lnbmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtl'
|
||||||
'Z25lZFByZWtleVNpZ25hdHVyZRr2AwoIVXNlckRhdGESFwoHdXNlcl9pZBgBIAEoA1IGdXNlck'
|
'eRgCIAEoDFIMc2lnbmVkUHJla2V5EjYKF3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUh'
|
||||||
'lkEjsKB3ByZWtleXMYAiADKAsyIS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlByZUtleVIH'
|
'VzaWduZWRQcmVrZXlTaWduYXR1cmUa9gMKCFVzZXJEYXRhEhcKB3VzZXJfaWQYASABKANSBnVz'
|
||||||
'cHJla2V5cxIfCgh1c2VybmFtZRgHIAEoDEgAUgh1c2VybmFtZYgBARIzChNwdWJsaWNfaWRlbn'
|
'ZXJJZBI7CgdwcmVrZXlzGAIgAygLMiEuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5QcmVLZX'
|
||||||
'RpdHlfa2V5GAMgASgMSAFSEXB1YmxpY0lkZW50aXR5S2V5iAEBEigKDXNpZ25lZF9wcmVrZXkY'
|
'lSB3ByZWtleXMSHwoIdXNlcm5hbWUYByABKAxIAFIIdXNlcm5hbWWIAQESMwoTcHVibGljX2lk'
|
||||||
'BCABKAxIAlIMc2lnbmVkUHJla2V5iAEBEjsKF3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAUgAS'
|
'ZW50aXR5X2tleRgDIAEoDEgBUhFwdWJsaWNJZGVudGl0eUtleYgBARIoCg1zaWduZWRfcHJla2'
|
||||||
'gMSANSFXNpZ25lZFByZWtleVNpZ25hdHVyZYgBARItChBzaWduZWRfcHJla2V5X2lkGAYgASgD'
|
'V5GAQgASgMSAJSDHNpZ25lZFByZWtleYgBARI7ChdzaWduZWRfcHJla2V5X3NpZ25hdHVyZRgF'
|
||||||
'SARSDnNpZ25lZFByZWtleUlkiAEBEiwKD3JlZ2lzdHJhdGlvbl9pZBgIIAEoA0gFUg5yZWdpc3'
|
'IAEoDEgDUhVzaWduZWRQcmVrZXlTaWduYXR1cmWIAQESLQoQc2lnbmVkX3ByZWtleV9pZBgGIA'
|
||||||
'RyYXRpb25JZIgBAUILCglfdXNlcm5hbWVCFgoUX3B1YmxpY19pZGVudGl0eV9rZXlCEAoOX3Np'
|
'EoA0gEUg5zaWduZWRQcmVrZXlJZIgBARIsCg9yZWdpc3RyYXRpb25faWQYCCABKANIBVIOcmVn'
|
||||||
'Z25lZF9wcmVrZXlCGgoYX3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlQhMKEV9zaWduZWRfcHJla2'
|
'aXN0cmF0aW9uSWSIAQFCCwoJX3VzZXJuYW1lQhYKFF9wdWJsaWNfaWRlbnRpdHlfa2V5QhAKDl'
|
||||||
'V5X2lkQhIKEF9yZWdpc3RyYXRpb25faWQaWQoLVXBsb2FkVG9rZW4SIQoMdXBsb2FkX3Rva2Vu'
|
'9zaWduZWRfcHJla2V5QhoKGF9zaWduZWRfcHJla2V5X3NpZ25hdHVyZUITChFfc2lnbmVkX3By'
|
||||||
'GAEgASgMUgt1cGxvYWRUb2tlbhInCg9kb3dubG9hZF90b2tlbnMYAiADKAxSDmRvd25sb2FkVG'
|
'ZWtleV9pZEISChBfcmVnaXN0cmF0aW9uX2lkGlkKC1VwbG9hZFRva2VuEiEKDHVwbG9hZF90b2'
|
||||||
'9rZW5zGjkKDkRvd25sb2FkVG9rZW5zEicKD2Rvd25sb2FkX3Rva2VucxgBIAMoDFIOZG93bmxv'
|
'tlbhgBIAEoDFILdXBsb2FkVG9rZW4SJwoPZG93bmxvYWRfdG9rZW5zGAIgAygMUg5kb3dubG9h'
|
||||||
'YWRUb2tlbnMaRQoLUHJvb2ZPZldvcmsSFgoGcHJlZml4GAEgASgJUgZwcmVmaXgSHgoKZGlmZm'
|
'ZFRva2Vucxo5Cg5Eb3dubG9hZFRva2VucxInCg9kb3dubG9hZF90b2tlbnMYASADKAxSDmRvd2'
|
||||||
'ljdWx0eRgCIAEoA1IKZGlmZmljdWx0eRrDBwoCT2sSFAoETm9uZRgBIAEoCEgAUgROb25lEhgK'
|
'5sb2FkVG9rZW5zGkUKC1Byb29mT2ZXb3JrEhYKBnByZWZpeBgBIAEoCVIGcHJlZml4Eh4KCmRp'
|
||||||
'BnVzZXJpZBgCIAEoA0gAUgZ1c2VyaWQSJgoNYXV0aGNoYWxsZW5nZRgDIAEoDEgAUg1hdXRoY2'
|
'ZmZpY3VsdHkYAiABKANSCmRpZmZpY3VsdHkawwcKAk9rEhQKBE5vbmUYASABKAhIAFIETm9uZR'
|
||||||
'hhbGxlbmdlEkoKC3VwbG9hZHRva2VuGAQgASgLMiYuc2VydmVyX3RvX2NsaWVudC5SZXNwb25z'
|
'IYCgZ1c2VyaWQYAiABKANIAFIGdXNlcmlkEiYKDWF1dGhjaGFsbGVuZ2UYAyABKAxIAFINYXV0'
|
||||||
'ZS5VcGxvYWRUb2tlbkgAUgt1cGxvYWR0b2tlbhJBCgh1c2VyZGF0YRgFIAEoCzIjLnNlcnZlcl'
|
'aGNoYWxsZW5nZRJKCgt1cGxvYWR0b2tlbhgEIAEoCzImLnNlcnZlcl90b19jbGllbnQuUmVzcG'
|
||||||
'90b19jbGllbnQuUmVzcG9uc2UuVXNlckRhdGFIAFIIdXNlcmRhdGESHgoJYXV0aHRva2VuGAYg'
|
'9uc2UuVXBsb2FkVG9rZW5IAFILdXBsb2FkdG9rZW4SQQoIdXNlcmRhdGEYBSABKAsyIy5zZXJ2'
|
||||||
'ASgMSABSCWF1dGh0b2tlbhJBCghsb2NhdGlvbhgHIAEoCzIjLnNlcnZlcl90b19jbGllbnQuUm'
|
'ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlVzZXJEYXRhSABSCHVzZXJkYXRhEh4KCWF1dGh0b2tlbh'
|
||||||
'VzcG9uc2UuTG9jYXRpb25IAFIIbG9jYXRpb24SUAoNYXV0aGVudGljYXRlZBgIIAEoCzIoLnNl'
|
'gGIAEoDEgAUglhdXRodG9rZW4SQQoIbG9jYXRpb24YByABKAsyIy5zZXJ2ZXJfdG9fY2xpZW50'
|
||||||
'cnZlcl90b19jbGllbnQuUmVzcG9uc2UuQXV0aGVudGljYXRlZEgAUg1hdXRoZW50aWNhdGVkEj'
|
'LlJlc3BvbnNlLkxvY2F0aW9uSABSCGxvY2F0aW9uElAKDWF1dGhlbnRpY2F0ZWQYCCABKAsyKC'
|
||||||
'gKBXBsYW5zGAkgASgLMiAuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5QbGFuc0gAUgVwbGFu'
|
'5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkF1dGhlbnRpY2F0ZWRIAFINYXV0aGVudGljYXRl'
|
||||||
'cxJNCgxwbGFuYmFsbGFuY2UYCiABKAsyJy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlBsYW'
|
'ZBI4CgVwbGFucxgJIAEoCzIgLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUGxhbnNIAFIFcG'
|
||||||
'5CYWxsYW5jZUgAUgxwbGFuYmFsbGFuY2USQQoIdm91Y2hlcnMYCyABKAsyIy5zZXJ2ZXJfdG9f'
|
'xhbnMSTQoMcGxhbmJhbGxhbmNlGAogASgLMicuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5Q'
|
||||||
'Y2xpZW50LlJlc3BvbnNlLlZvdWNoZXJzSABSCHZvdWNoZXJzEl8KEmFkZGFjY291bnRzaW52aX'
|
'bGFuQmFsbGFuY2VIAFIMcGxhbmJhbGxhbmNlEkEKCHZvdWNoZXJzGAsgASgLMiMuc2VydmVyX3'
|
||||||
'RlcxgMIAEoCzItLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuQWRkQWNjb3VudHNJbnZpdGVz'
|
'RvX2NsaWVudC5SZXNwb25zZS5Wb3VjaGVyc0gAUgh2b3VjaGVycxJfChJhZGRhY2NvdW50c2lu'
|
||||||
'SABSEmFkZGFjY291bnRzaW52aXRlcxJTCg5kb3dubG9hZHRva2VucxgNIAEoCzIpLnNlcnZlcl'
|
'dml0ZXMYDCABKAsyLS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkFkZEFjY291bnRzSW52aX'
|
||||||
'90b19jbGllbnQuUmVzcG9uc2UuRG93bmxvYWRUb2tlbnNIAFIOZG93bmxvYWR0b2tlbnMSTQoM'
|
'Rlc0gAUhJhZGRhY2NvdW50c2ludml0ZXMSUwoOZG93bmxvYWR0b2tlbnMYDSABKAsyKS5zZXJ2'
|
||||||
'c2lnbmVkcHJla2V5GA4gASgLMicuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5TaWduZWRQcm'
|
'ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkRvd25sb2FkVG9rZW5zSABSDmRvd25sb2FkdG9rZW5zEk'
|
||||||
'VLZXlIAFIMc2lnbmVkcHJla2V5EkoKC3Byb29mT2ZXb3JrGA8gASgLMiYuc2VydmVyX3RvX2Ns'
|
'0KDHNpZ25lZHByZWtleRgOIAEoCzInLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuU2lnbmVk'
|
||||||
'aWVudC5SZXNwb25zZS5Qcm9vZk9mV29ya0gAUgtwcm9vZk9mV29ya0IECgJPayKWAQoQVHJhbn'
|
'UHJlS2V5SABSDHNpZ25lZHByZWtleRJKCgtwcm9vZk9mV29yaxgPIAEoCzImLnNlcnZlcl90b1'
|
||||||
'NhY3Rpb25UeXBlcxIKCgZSZWZ1bmQQABITCg9Wb3VjaGVyUmVkZWVtZWQQARISCg5Wb3VjaGVy'
|
'9jbGllbnQuUmVzcG9uc2UuUHJvb2ZPZldvcmtIAFILcHJvb2ZPZldvcmtCBAoCT2silgEKEFRy'
|
||||||
'Q3JlYXRlZBACEggKBENhc2gQAxIPCgtQbGFuVXBncmFkZRAEEgsKB1Vua25vd24QBRIUChBUaG'
|
'YW5zYWN0aW9uVHlwZXMSCgoGUmVmdW5kEAASEwoPVm91Y2hlclJlZGVlbWVkEAESEgoOVm91Y2'
|
||||||
'Fua3NGb3JUZXN0aW5nEAYSDwoLQXV0b1JlbmV3YWwQB0IKCghSZXNwb25zZQ==');
|
'hlckNyZWF0ZWQQAhIICgRDYXNoEAMSDwoLUGxhblVwZ3JhZGUQBBILCgdVbmtub3duEAUSFAoQ'
|
||||||
|
'VGhhbmtzRm9yVGVzdGluZxAGEg8KC0F1dG9SZW5ld2FsEAdCCgoIUmVzcG9uc2U=');
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:in_app_purchase/in_app_purchase.dart';
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||||
|
|
@ -121,36 +120,15 @@ class PurchasesProvider with ChangeNotifier, DiagnosticableTreeMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _verifyPurchase(PurchaseDetails purchaseDetails) async {
|
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) {
|
if (kDebugMode) {
|
||||||
Log.info(purchaseDetails.productID);
|
Log.info(purchaseDetails.productID);
|
||||||
|
Log.info(purchaseDetails.verificationData.serverVerificationData);
|
||||||
|
// if (Platform.isIOS) {
|
||||||
|
// final data = purchaseDetails.verificationData.serverVerificationData;
|
||||||
|
// printWrapped(data);
|
||||||
|
// final datas = data.split('.')[1];
|
||||||
|
// printWrapped(datas);
|
||||||
|
// }
|
||||||
Log.info(purchaseDetails.verificationData.source);
|
Log.info(purchaseDetails.verificationData.source);
|
||||||
}
|
}
|
||||||
final res = await apiService.ipaPurchase(
|
final res = await apiService.ipaPurchase(
|
||||||
|
|
@ -185,9 +163,7 @@ class PurchasesProvider with ChangeNotifier, DiagnosticableTreeMixin {
|
||||||
Log.info(
|
Log.info(
|
||||||
'_handlePurchase: ${purchaseDetails.productID}, ${purchaseDetails.status}',
|
'_handlePurchase: ${purchaseDetails.productID}, ${purchaseDetails.status}',
|
||||||
);
|
);
|
||||||
if (purchaseDetails.status == PurchaseStatus.purchased ||
|
if (purchaseDetails.status == PurchaseStatus.purchased) {
|
||||||
(purchaseDetails.status == PurchaseStatus.restored &&
|
|
||||||
_userTriggeredBuyButton)) {
|
|
||||||
await _verifyPurchase(purchaseDetails);
|
await _verifyPurchase(purchaseDetails);
|
||||||
}
|
}
|
||||||
if (purchaseDetails.status == PurchaseStatus.restored &&
|
if (purchaseDetails.status == PurchaseStatus.restored &&
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
|
|
@ -51,7 +50,7 @@ final lockRetransStore = Mutex();
|
||||||
/// errors or network changes.
|
/// errors or network changes.
|
||||||
class ApiService {
|
class ApiService {
|
||||||
ApiService();
|
ApiService();
|
||||||
final String apiHost = kReleaseMode ? 'api.twonly.eu' : '10.99.0.140:3030';
|
final String apiHost = kReleaseMode ? 'api.twonly.eu' : '192.168.2.178:3030';
|
||||||
// final String apiHost = kReleaseMode ? 'api.twonly.eu' : 'dev.twonly.eu';
|
// final String apiHost = kReleaseMode ? 'api.twonly.eu' : 'dev.twonly.eu';
|
||||||
final String apiSecure = kReleaseMode ? 's' : '';
|
final String apiSecure = kReleaseMode ? 's' : '';
|
||||||
|
|
||||||
|
|
@ -207,7 +206,7 @@ class ApiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<server.ServerToClient?> _waitForResponse(Int64 seq) async {
|
Future<server.ServerToClient?> _waitForResponse(Int64 seq) async {
|
||||||
final startTime = clock.now();
|
final startTime = DateTime.now();
|
||||||
|
|
||||||
const timeout = Duration(seconds: 60);
|
const timeout = Duration(seconds: 60);
|
||||||
|
|
||||||
|
|
@ -217,7 +216,7 @@ class ApiService {
|
||||||
messagesV0.remove(seq);
|
messagesV0.remove(seq);
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
if (clock.now().difference(startTime) > timeout) {
|
if (DateTime.now().difference(startTime) > timeout) {
|
||||||
Log.warn('Timeout for message $seq');
|
Log.warn('Timeout for message $seq');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -595,6 +594,20 @@ class ApiService {
|
||||||
return null;
|
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 {
|
Future<Result> updatePlanOptions(bool autoRenewal) async {
|
||||||
final get = ApplicationData_UpdatePlanOptions()..autoRenewal = autoRenewal;
|
final get = ApplicationData_UpdatePlanOptions()..autoRenewal = autoRenewal;
|
||||||
final appData = ApplicationData()..updatePlanOptions = get;
|
final appData = ApplicationData()..updatePlanOptions = get;
|
||||||
|
|
@ -609,13 +622,6 @@ class ApiService {
|
||||||
return sendRequestSync(req, contactId: userId.toInt());
|
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 {
|
Future<Result> buyVoucher(int valueInCents) async {
|
||||||
final get = ApplicationData_CreateVoucher()..valueCents = valueInCents;
|
final get = ApplicationData_CreateVoucher()..valueCents = valueInCents;
|
||||||
final appData = ApplicationData()..createVoucher = get;
|
final appData = ApplicationData()..createVoucher = get;
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ Future<void> handleContactUpdate(
|
||||||
switch (contactUpdate.type) {
|
switch (contactUpdate.type) {
|
||||||
case EncryptedContent_ContactUpdate_Type.REQUEST:
|
case EncryptedContent_ContactUpdate_Type.REQUEST:
|
||||||
Log.info('Got a contact update request from $fromUserId');
|
Log.info('Got a contact update request from $fromUserId');
|
||||||
await sendContactMyProfileData(fromUserId);
|
await notifyContactsAboutProfileChange(onlyToContact: fromUserId);
|
||||||
|
|
||||||
case EncryptedContent_ContactUpdate_Type.UPDATE:
|
case EncryptedContent_ContactUpdate_Type.UPDATE:
|
||||||
Log.info('Got a contact update $fromUserId');
|
Log.info('Got a contact update $fromUserId');
|
||||||
|
|
|
||||||
|
|
@ -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/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/download.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/download.service.dart';
|
||||||
import 'package:twonly/src/services/api/utils.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/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
|
|
@ -115,7 +114,7 @@ Future<void> handleMedia(
|
||||||
await twonlyDB.groupsDao
|
await twonlyDB.groupsDao
|
||||||
.increaseLastMessageExchange(groupId, fromTimestamp(media.timestamp));
|
.increaseLastMessageExchange(groupId, fromTimestamp(media.timestamp));
|
||||||
Log.info('Inserted a new media message with ID: ${message.messageId}');
|
Log.info('Inserted a new media message with ID: ${message.messageId}');
|
||||||
await incFlameCounter(
|
await twonlyDB.groupsDao.incFlameCounter(
|
||||||
message.groupId,
|
message.groupId,
|
||||||
true,
|
true,
|
||||||
fromTimestamp(media.timestamp),
|
fromTimestamp(media.timestamp),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
import 'package:twonly/src/utils/log.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(
|
Future<void> handlePushKey(
|
||||||
int contactId,
|
int contactId,
|
||||||
|
|
@ -15,8 +14,8 @@ Future<void> handlePushKey(
|
||||||
case EncryptedContent_PushKeys_Type.REQUEST:
|
case EncryptedContent_PushKeys_Type.REQUEST:
|
||||||
Log.info('Got a pushkey request from $contactId');
|
Log.info('Got a pushkey request from $contactId');
|
||||||
if (lastPushKeyRequest
|
if (lastPushKeyRequest
|
||||||
.isBefore(clock.now().subtract(const Duration(seconds: 60)))) {
|
.isBefore(DateTime.now().subtract(const Duration(seconds: 60)))) {
|
||||||
lastPushKeyRequest = clock.now();
|
lastPushKeyRequest = DateTime.now();
|
||||||
unawaited(setupNotificationWithUsers(forceContact: contactId));
|
unawaited(setupNotificationWithUsers(forceContact: contactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
@ -18,6 +17,7 @@ Future<void> handleReaction(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!reaction.remove) {
|
if (!reaction.remove) {
|
||||||
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now());
|
await twonlyDB.groupsDao
|
||||||
|
.increaseLastMessageExchange(groupId, DateTime.now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
|
|
@ -27,7 +26,7 @@ Future<void> handleTextMessage(
|
||||||
textMessage.hasQuoteMessageId() ? textMessage.quoteMessageId : null,
|
textMessage.hasQuoteMessageId() ? textMessage.quoteMessageId : null,
|
||||||
),
|
),
|
||||||
createdAt: Value(fromTimestamp(textMessage.timestamp)),
|
createdAt: Value(fromTimestamp(textMessage.timestamp)),
|
||||||
ackByServer: Value(clock.now()),
|
ackByServer: Value(DateTime.now()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await twonlyDB.groupsDao.increaseLastMessageExchange(
|
await twonlyDB.groupsDao.increaseLastMessageExchange(
|
||||||
|
|
|
||||||
|
|
@ -57,10 +57,15 @@ Future<bool> isAllowedToDownload(MediaType type) async {
|
||||||
if (options[ConnectivityResult.mobile.name]!
|
if (options[ConnectivityResult.mobile.name]!
|
||||||
.contains(DownloadMediaTypes.video.name)) {
|
.contains(DownloadMediaTypes.video.name)) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (type == MediaType.audio) {
|
||||||
|
if (options[ConnectivityResult.mobile.name]!
|
||||||
|
.contains(DownloadMediaTypes.audio.name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (options[ConnectivityResult.mobile.name]!
|
||||||
|
.contains(DownloadMediaTypes.image.name)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} else if (options[ConnectivityResult.mobile.name]!
|
|
||||||
.contains(DownloadMediaTypes.image.name)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (connectivityResult.contains(ConnectivityResult.wifi)) {
|
if (connectivityResult.contains(ConnectivityResult.wifi)) {
|
||||||
|
|
@ -69,6 +74,11 @@ Future<bool> isAllowedToDownload(MediaType type) async {
|
||||||
.contains(DownloadMediaTypes.video.name)) {
|
.contains(DownloadMediaTypes.video.name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (type == MediaType.audio) {
|
||||||
|
if (options[ConnectivityResult.wifi.name]!
|
||||||
|
.contains(DownloadMediaTypes.audio.name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} else if (options[ConnectivityResult.wifi.name]!
|
} else if (options[ConnectivityResult.wifi.name]!
|
||||||
.contains(DownloadMediaTypes.image.name)) {
|
.contains(DownloadMediaTypes.image.name)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -214,25 +224,29 @@ Future<void> downloadFileFast(
|
||||||
|
|
||||||
Future<void> requestMediaReupload(String mediaId) async {
|
Future<void> requestMediaReupload(String mediaId) async {
|
||||||
final messages = await twonlyDB.messagesDao.getMessagesByMediaId(mediaId);
|
final messages = await twonlyDB.messagesDao.getMessagesByMediaId(mediaId);
|
||||||
|
if (messages.length != 1 || messages.first.senderId == null) {
|
||||||
for (final message in messages) {
|
Log.error(
|
||||||
if (message.openedAt != null) continue;
|
'Media file has none or more than one sender. That is not possible',
|
||||||
await sendCipherText(
|
|
||||||
messages.first.senderId!,
|
|
||||||
EncryptedContent(
|
|
||||||
mediaUpdate: EncryptedContent_MediaUpdate(
|
|
||||||
type: EncryptedContent_MediaUpdate_Type.DECRYPTION_ERROR,
|
|
||||||
targetMessageId: messages.first.messageId,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await twonlyDB.mediaFilesDao.updateMedia(
|
|
||||||
mediaId,
|
|
||||||
const MediaFilesCompanion(
|
|
||||||
downloadState: Value(DownloadState.reuploadRequested),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await sendCipherText(
|
||||||
|
messages.first.senderId!,
|
||||||
|
EncryptedContent(
|
||||||
|
mediaUpdate: EncryptedContent_MediaUpdate(
|
||||||
|
type: EncryptedContent_MediaUpdate_Type.DECRYPTION_ERROR,
|
||||||
|
targetMessageId: messages.first.messageId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await twonlyDB.mediaFilesDao.updateMedia(
|
||||||
|
mediaId,
|
||||||
|
const MediaFilesCompanion(
|
||||||
|
downloadState: Value(DownloadState.reuploadRequested),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleEncryptedFile(String mediaId) async {
|
Future<void> handleEncryptedFile(String mediaId) async {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -93,7 +92,7 @@ Future<void> handleUploadStatusUpdate(TaskStatusUpdate update) async {
|
||||||
await twonlyDB.messagesDao.handleMessageAckByServer(
|
await twonlyDB.messagesDao.handleMessageAckByServer(
|
||||||
contact.contactId,
|
contact.contactId,
|
||||||
message.messageId,
|
message.messageId,
|
||||||
clock.now(),
|
DateTime.now(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||||
|
|
@ -19,7 +18,6 @@ import 'package:twonly/src/model/protobuf/api/http/http_requests.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/media_background.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/media_background.service.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/services/flame.service.dart';
|
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
@ -89,6 +87,7 @@ Future<MediaFileService?> initializeMediaUpload(
|
||||||
Future<void> insertMediaFileInMessagesTable(
|
Future<void> insertMediaFileInMessagesTable(
|
||||||
MediaFileService mediaService,
|
MediaFileService mediaService,
|
||||||
List<String> groupIds,
|
List<String> groupIds,
|
||||||
|
Future<Uint8List?>? imageStoreAwait,
|
||||||
) async {
|
) async {
|
||||||
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||||
const MediaFilesCompanion(
|
const MediaFilesCompanion(
|
||||||
|
|
@ -103,7 +102,8 @@ Future<void> insertMediaFileInMessagesTable(
|
||||||
type: const Value(MessageType.media),
|
type: const Value(MessageType.media),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now());
|
await twonlyDB.groupsDao
|
||||||
|
.increaseLastMessageExchange(groupId, DateTime.now());
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
// de-archive contact when sending a new message
|
// de-archive contact when sending a new message
|
||||||
await twonlyDB.groupsDao.updateGroup(
|
await twonlyDB.groupsDao.updateGroup(
|
||||||
|
|
@ -118,6 +118,13 @@ Future<void> insertMediaFileInMessagesTable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (imageStoreAwait != null) {
|
||||||
|
if (await imageStoreAwait == null) {
|
||||||
|
Log.error('image store as original did return false...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unawaited(startBackgroundMediaUpload(mediaService));
|
unawaited(startBackgroundMediaUpload(mediaService));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,15 +140,6 @@ Future<void> startBackgroundMediaUpload(MediaFileService mediaService) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the user has enabled auto storing and the file
|
|
||||||
// was send with unlimited counter not in twonly-Mode then store the file
|
|
||||||
if (gUser.autoStoreAllSendUnlimitedMediaFiles &&
|
|
||||||
!mediaService.mediaFile.requiresAuthentication &&
|
|
||||||
!mediaService.storedPath.existsSync() &&
|
|
||||||
mediaService.mediaFile.displayLimitInMilliseconds == null) {
|
|
||||||
await mediaService.storeMediaFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mediaService.encryptedPath.existsSync()) {
|
if (!mediaService.encryptedPath.existsSync()) {
|
||||||
await _encryptMediaFiles(mediaService);
|
await _encryptMediaFiles(mediaService);
|
||||||
if (!mediaService.encryptedPath.existsSync()) {
|
if (!mediaService.encryptedPath.existsSync()) {
|
||||||
|
|
@ -208,7 +206,11 @@ Future<void> _createUploadRequest(MediaFileService media) async {
|
||||||
await twonlyDB.groupsDao.getGroupNonLeftMembers(message.groupId);
|
await twonlyDB.groupsDao.getGroupNonLeftMembers(message.groupId);
|
||||||
|
|
||||||
if (media.mediaFile.reuploadRequestedBy == null) {
|
if (media.mediaFile.reuploadRequestedBy == null) {
|
||||||
await incFlameCounter(message.groupId, false, message.createdAt);
|
await twonlyDB.groupsDao.incFlameCounter(
|
||||||
|
message.groupId,
|
||||||
|
false,
|
||||||
|
message.createdAt,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final groupMember in groupMembers) {
|
for (final groupMember in groupMembers) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
|
@ -22,7 +21,7 @@ final lockRetransmission = Mutex();
|
||||||
|
|
||||||
Future<void> tryTransmitMessages() async {
|
Future<void> tryTransmitMessages() async {
|
||||||
return lockRetransmission.protect(() async {
|
return lockRetransmission.protect(() async {
|
||||||
final receipts = await twonlyDB.receiptsDao.getReceiptsForRetransmission();
|
final receipts = await twonlyDB.receiptsDao.getReceiptsNotAckByServer();
|
||||||
|
|
||||||
if (receipts.isEmpty) return;
|
if (receipts.isEmpty) return;
|
||||||
|
|
||||||
|
|
@ -51,10 +50,8 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
|
||||||
}
|
}
|
||||||
receiptId = receipt.receiptId;
|
receiptId = receipt.receiptId;
|
||||||
|
|
||||||
if (!onlyReturnEncryptedData &&
|
if (!onlyReturnEncryptedData && receipt.ackByServerAt != null) {
|
||||||
receipt.ackByServerAt != null &&
|
Log.error('message already uploaded!');
|
||||||
receipt.markForRetry == null) {
|
|
||||||
Log.error('Message already uploaded and mark for retry is not set!');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,7 +129,7 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
|
||||||
await twonlyDB.messagesDao.handleMessageAckByServer(
|
await twonlyDB.messagesDao.handleMessageAckByServer(
|
||||||
receipt.contactId,
|
receipt.contactId,
|
||||||
receipt.messageId!,
|
receipt.messageId!,
|
||||||
clock.now(),
|
DateTime.now(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!receipt.contactWillSendsReceipt) {
|
if (!receipt.contactWillSendsReceipt) {
|
||||||
|
|
@ -141,10 +138,9 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
|
||||||
await twonlyDB.receiptsDao.updateReceipt(
|
await twonlyDB.receiptsDao.updateReceipt(
|
||||||
receiptId,
|
receiptId,
|
||||||
ReceiptsCompanion(
|
ReceiptsCompanion(
|
||||||
ackByServerAt: Value(clock.now()),
|
ackByServerAt: Value(DateTime.now()),
|
||||||
retryCount: Value(receipt.retryCount + 1),
|
retryCount: Value(receipt.retryCount + 1),
|
||||||
lastRetry: Value(clock.now()),
|
lastRetry: Value(DateTime.now()),
|
||||||
markForRetry: const Value(null),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +207,7 @@ Future<void> sendCipherTextToGroup(
|
||||||
}) async {
|
}) async {
|
||||||
final groupMembers = await twonlyDB.groupsDao.getGroupNonLeftMembers(groupId);
|
final groupMembers = await twonlyDB.groupsDao.getGroupNonLeftMembers(groupId);
|
||||||
|
|
||||||
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now());
|
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, DateTime.now());
|
||||||
|
|
||||||
encryptedContent.groupId = groupId;
|
encryptedContent.groupId = groupId;
|
||||||
|
|
||||||
|
|
@ -243,7 +239,7 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
|
||||||
contactId: Value(contactId),
|
contactId: Value(contactId),
|
||||||
message: Value(response.writeToBuffer()),
|
message: Value(response.writeToBuffer()),
|
||||||
messageId: Value(messageId),
|
messageId: Value(messageId),
|
||||||
ackByServerAt: Value(onlyReturnEncryptedData ? clock.now() : null),
|
ackByServerAt: Value(onlyReturnEncryptedData ? DateTime.now() : null),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -269,7 +265,7 @@ Future<void> notifyContactAboutOpeningMessage(
|
||||||
}
|
}
|
||||||
Log.info('Opened messages: $messageOtherIds');
|
Log.info('Opened messages: $messageOtherIds');
|
||||||
|
|
||||||
final actionAt = clock.now();
|
final actionAt = DateTime.now();
|
||||||
|
|
||||||
await sendCipherText(
|
await sendCipherText(
|
||||||
contactId,
|
contactId,
|
||||||
|
|
@ -293,18 +289,26 @@ Future<void> notifyContactAboutOpeningMessage(
|
||||||
await updateLastMessageId(contactId, biggestMessageId);
|
await updateLastMessageId(contactId, biggestMessageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendContactMyProfileData(int contactId) async {
|
Future<void> notifyContactsAboutProfileChange({int? onlyToContact}) async {
|
||||||
List<int>? avatarSvgCompressed;
|
if (gUser.avatarSvg == null) return;
|
||||||
if (gUser.avatarSvg != null) {
|
|
||||||
avatarSvgCompressed = gzip.encode(utf8.encode(gUser.avatarSvg!));
|
|
||||||
}
|
|
||||||
final encryptedContent = pb.EncryptedContent(
|
final encryptedContent = pb.EncryptedContent(
|
||||||
contactUpdate: pb.EncryptedContent_ContactUpdate(
|
contactUpdate: pb.EncryptedContent_ContactUpdate(
|
||||||
type: pb.EncryptedContent_ContactUpdate_Type.UPDATE,
|
type: pb.EncryptedContent_ContactUpdate_Type.UPDATE,
|
||||||
avatarSvgCompressed: avatarSvgCompressed,
|
avatarSvgCompressed: gzip.encode(utf8.encode(gUser.avatarSvg!)),
|
||||||
displayName: gUser.displayName,
|
displayName: gUser.displayName,
|
||||||
username: gUser.username,
|
username: gUser.username,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await sendCipherText(contactId, encryptedContent);
|
|
||||||
|
if (onlyToContact != null) {
|
||||||
|
await sendCipherText(onlyToContact, encryptedContent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts();
|
||||||
|
|
||||||
|
for (final contact in contacts) {
|
||||||
|
await sendCipherText(contact.userId, encryptedContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:hashlib/random.dart';
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
|
|
@ -41,11 +40,7 @@ Future<void> handleServerMessage(server.ServerToClient msg) async {
|
||||||
await handleClient2ClientMessage(msg.v0.newMessage);
|
await handleClient2ClientMessage(msg.v0.newMessage);
|
||||||
} else if (msg.v0.hasNewMessages()) {
|
} else if (msg.v0.hasNewMessages()) {
|
||||||
for (final newMessage in msg.v0.newMessages.newMessages) {
|
for (final newMessage in msg.v0.newMessages.newMessages) {
|
||||||
try {
|
await handleClient2ClientMessage(newMessage);
|
||||||
await handleClient2ClientMessage(newMessage);
|
|
||||||
} catch (e) {
|
|
||||||
Log.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.error('Unknown server message: $msg');
|
Log.error('Unknown server message: $msg');
|
||||||
|
|
@ -61,7 +56,7 @@ Future<void> handleServerMessage(server.ServerToClient msg) async {
|
||||||
await apiService.sendResponse(ClientToServer()..v0 = v0);
|
await apiService.sendResponse(ClientToServer()..v0 = v0);
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime lastPushKeyRequest = clock.now().subtract(const Duration(hours: 1));
|
DateTime lastPushKeyRequest = DateTime.now().subtract(const Duration(hours: 1));
|
||||||
|
|
||||||
Mutex protectReceiptCheck = Mutex();
|
Mutex protectReceiptCheck = Mutex();
|
||||||
|
|
||||||
|
|
@ -190,15 +185,6 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
..type = decryptionErrorType!);
|
..type = decryptionErrorType!);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We got a valid message fromUserId, so mark all messages which where
|
|
||||||
// send to the user but not yet ACK for retransmission. All marked messages
|
|
||||||
// will be either transmitted again after a new server connection (minimum 20 seconds).
|
|
||||||
// In case the server sends the ACK before they will be deleted.
|
|
||||||
// This ensures that 1. all messages will be received by the other person and
|
|
||||||
// that they will be retransmitted in case the server deleted them as they
|
|
||||||
// where not downloaded within the 40 days
|
|
||||||
await twonlyDB.receiptsDao.markMessagesForRetry(fromUserId);
|
|
||||||
|
|
||||||
final senderProfileCounter = await checkForProfileUpdate(fromUserId, content);
|
final senderProfileCounter = await checkForProfileUpdate(fromUserId, content);
|
||||||
|
|
||||||
if (content.hasContactRequest()) {
|
if (content.hasContactRequest()) {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/daos/groups.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
|
|
@ -32,10 +32,10 @@ Future<void> syncFlameCounters({String? forceForGroup}) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final flameCounter = getFlameCounterFromGroup(group);
|
final flameCounter = getFlameCounterFromGroup(group) - 1;
|
||||||
|
|
||||||
// only sync when flame counter is higher three or when they are bestFriends
|
// only sync when flame counter is higher than three days or when they are bestFriends
|
||||||
if (flameCounter <= 2 && bestFriend.groupId != group.groupId) continue;
|
if (flameCounter < 1 && bestFriend.groupId != group.groupId) continue;
|
||||||
|
|
||||||
final groupMembers =
|
final groupMembers =
|
||||||
await twonlyDB.groupsDao.getGroupNonLeftMembers(group.groupId);
|
await twonlyDB.groupsDao.getGroupNonLeftMembers(group.groupId);
|
||||||
|
|
@ -59,125 +59,8 @@ Future<void> syncFlameCounters({String? forceForGroup}) async {
|
||||||
await twonlyDB.groupsDao.updateGroup(
|
await twonlyDB.groupsDao.updateGroup(
|
||||||
group.groupId,
|
group.groupId,
|
||||||
GroupsCompanion(
|
GroupsCompanion(
|
||||||
lastFlameSync: Value(clock.now()),
|
lastFlameSync: Value(DateTime.now()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int getFlameCounterFromGroup(Group? group) {
|
|
||||||
if (group == null) return 0;
|
|
||||||
if (group.lastMessageSend == null ||
|
|
||||||
group.lastMessageReceived == null ||
|
|
||||||
group.lastFlameCounterChange == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
final now = clock.now();
|
|
||||||
final startOfToday = DateTime(now.year, now.month, now.day);
|
|
||||||
final twoDaysAgo = startOfToday.subtract(const Duration(days: 2));
|
|
||||||
final oneDayAgo = startOfToday.subtract(const Duration(days: 1));
|
|
||||||
if (group.lastMessageSend!.isAfter(twoDaysAgo) &&
|
|
||||||
group.lastMessageReceived!.isAfter(twoDaysAgo) ||
|
|
||||||
group.lastFlameCounterChange!.isAfter(oneDayAgo)) {
|
|
||||||
return group.flameCounter;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> incFlameCounter(
|
|
||||||
String groupId,
|
|
||||||
bool received,
|
|
||||||
DateTime timestamp,
|
|
||||||
) async {
|
|
||||||
final group = await twonlyDB.groupsDao.getGroup(groupId);
|
|
||||||
if (group == null) return;
|
|
||||||
|
|
||||||
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 = clock.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();
|
|
||||||
|
|
||||||
final now = clock.now();
|
|
||||||
final startOfToday = DateTime(now.year, now.month, now.day);
|
|
||||||
|
|
||||||
if (group.lastFlameCounterChange == null ||
|
|
||||||
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;
|
|
||||||
if (group.lastFlameCounterChange == null ||
|
|
||||||
group.lastFlameCounterChange!.isBefore(timestamp)) {
|
|
||||||
// only update if the timestamp is newer
|
|
||||||
lastFlameCounterChange = Value(timestamp);
|
|
||||||
}
|
|
||||||
// Overwrite max flame counter either the current is bigger or the the max flame counter is older then 4 days
|
|
||||||
if (flameCounter >= maxFlameCounter ||
|
|
||||||
maxFlameCounterFrom == null ||
|
|
||||||
maxFlameCounterFrom
|
|
||||||
.isBefore(clock.now().subtract(const Duration(days: 5)))) {
|
|
||||||
maxFlameCounter = flameCounter;
|
|
||||||
maxFlameCounterFrom = clock.now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (received) {
|
|
||||||
if (group.lastMessageReceived == null ||
|
|
||||||
group.lastMessageReceived!.isBefore(timestamp)) {
|
|
||||||
lastMessageReceived = Value(timestamp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (group.lastMessageSend == null ||
|
|
||||||
group.lastMessageSend!.isBefore(timestamp)) {
|
|
||||||
lastMessageSend = Value(timestamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await twonlyDB.groupsDao.updateGroup(
|
|
||||||
groupId,
|
|
||||||
GroupsCompanion(
|
|
||||||
totalMediaCounter: Value(totalMediaCounter),
|
|
||||||
lastFlameCounterChange: lastFlameCounterChange,
|
|
||||||
lastMessageReceived: lastMessageReceived,
|
|
||||||
lastMessageSend: lastMessageSend,
|
|
||||||
flameCounter: Value(flameCounter),
|
|
||||||
maxFlameCounter: Value(maxFlameCounter),
|
|
||||||
maxFlameCounterFrom: Value(maxFlameCounterFrom),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isItPossibleToRestoreFlames(Group group) {
|
|
||||||
final flameCounter = getFlameCounterFromGroup(group);
|
|
||||||
return group.maxFlameCounter > 2 &&
|
|
||||||
flameCounter < group.maxFlameCounter &&
|
|
||||||
group.maxFlameCounterFrom!
|
|
||||||
.isAfter(clock.now().subtract(const Duration(days: 5)));
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||||
|
|
@ -159,7 +158,7 @@ Future<void> fetchMissingGroupPublicKey() async {
|
||||||
if (member.lastMessage == null) continue;
|
if (member.lastMessage == null) continue;
|
||||||
// only request if the users has send a message in the last two days.
|
// only request if the users has send a message in the last two days.
|
||||||
if (member.lastMessage!
|
if (member.lastMessage!
|
||||||
.isAfter(clock.now().subtract(const Duration(days: 2)))) {
|
.isAfter(DateTime.now().subtract(const Duration(days: 2)))) {
|
||||||
await sendCipherText(
|
await sendCipherText(
|
||||||
member.contactId,
|
member.contactId,
|
||||||
EncryptedContent(
|
EncryptedContent(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -9,7 +8,6 @@ import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/compression.service.dart';
|
import 'package:twonly/src/services/mediafiles/compression.service.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/thumbnail.service.dart';
|
import 'package:twonly/src/services/mediafiles/thumbnail.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
|
|
||||||
class MediaFileService {
|
class MediaFileService {
|
||||||
MediaFileService(this.mediaFile);
|
MediaFileService(this.mediaFile);
|
||||||
|
|
@ -62,7 +60,7 @@ class MediaFileService {
|
||||||
// delete = true; // do not overwrite a previous delete = false
|
// delete = true; // do not overwrite a previous delete = false
|
||||||
// this is just to make it easier to understand :)
|
// this is just to make it easier to understand :)
|
||||||
} else if (message.openedAt!
|
} else if (message.openedAt!
|
||||||
.isAfter(clock.now().subtract(const Duration(days: 2)))) {
|
.isAfter(DateTime.now().subtract(const Duration(days: 2)))) {
|
||||||
// In case the image was opened, but send with unlimited time or no authentication.
|
// In case the image was opened, but send with unlimited time or no authentication.
|
||||||
if (message.senderId == null) {
|
if (message.senderId == null) {
|
||||||
delete = false;
|
delete = false;
|
||||||
|
|
@ -219,36 +217,12 @@ class MediaFileService {
|
||||||
}
|
}
|
||||||
if (tempPath.existsSync()) {
|
if (tempPath.existsSync()) {
|
||||||
await tempPath.copy(storedPath.path);
|
await tempPath.copy(storedPath.path);
|
||||||
if (gUser.storeMediaFilesInGallery) {
|
|
||||||
if (mediaFile.type == MediaType.video) {
|
|
||||||
await saveVideoToGallery(storedPath.path);
|
|
||||||
} else {
|
|
||||||
await saveImageToGallery(
|
|
||||||
storedPath.readAsBytesSync(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Log.error(
|
Log.error(
|
||||||
'Could not store image neither as ${tempPath.path} does not exists.',
|
'Could not store image neither as ${tempPath.path} does not exists.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
unawaited(createThumbnail());
|
unawaited(createThumbnail());
|
||||||
await hashStoredMedia();
|
|
||||||
// updateFromDb is done in hashStoredMedia()
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> hashStoredMedia() async {
|
|
||||||
if (!storedPath.existsSync()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final checksum = await sha256File(storedPath);
|
|
||||||
await twonlyDB.mediaFilesDao.updateMedia(
|
|
||||||
mediaFile.mediaId,
|
|
||||||
MediaFilesCompanion(
|
|
||||||
storedFileHash: Value(Uint8List.fromList(checksum)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await updateFromDB();
|
await updateFromDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||||
|
|
@ -47,7 +46,7 @@ Future<void> setupNotificationWithUsers({
|
||||||
|
|
||||||
final random = Random.secure();
|
final random = Random.secure();
|
||||||
|
|
||||||
final contacts = await twonlyDB.contactsDao.getAllContacts();
|
final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts();
|
||||||
for (final contact in contacts) {
|
for (final contact in contacts) {
|
||||||
final pushUser =
|
final pushUser =
|
||||||
pushUsers.firstWhereOrNull((x) => x.userId == contact.userId);
|
pushUsers.firstWhereOrNull((x) => x.userId == contact.userId);
|
||||||
|
|
@ -55,7 +54,7 @@ Future<void> setupNotificationWithUsers({
|
||||||
if (pushUser != null && pushUser.pushKeys.isNotEmpty) {
|
if (pushUser != null && pushUser.pushKeys.isNotEmpty) {
|
||||||
// make it harder to predict the change of the key
|
// make it harder to predict the change of the key
|
||||||
final timeBefore =
|
final timeBefore =
|
||||||
clock.now().subtract(Duration(days: 10 + random.nextInt(5)));
|
DateTime.now().subtract(Duration(days: 5 + random.nextInt(5)));
|
||||||
final lastKey = pushUser.pushKeys.last;
|
final lastKey = pushUser.pushKeys.last;
|
||||||
final createdAt = DateTime.fromMillisecondsSinceEpoch(
|
final createdAt = DateTime.fromMillisecondsSinceEpoch(
|
||||||
lastKey.createdAtUnixTimestamp.toInt(),
|
lastKey.createdAtUnixTimestamp.toInt(),
|
||||||
|
|
@ -67,7 +66,7 @@ Future<void> setupNotificationWithUsers({
|
||||||
final pushKey = PushKey(
|
final pushKey = PushKey(
|
||||||
id: lastKey.id + random.nextInt(5),
|
id: lastKey.id + random.nextInt(5),
|
||||||
key: List<int>.generate(32, (index) => random.nextInt(256)),
|
key: List<int>.generate(32, (index) => random.nextInt(256)),
|
||||||
createdAtUnixTimestamp: Int64(clock.now().millisecondsSinceEpoch),
|
createdAtUnixTimestamp: Int64(DateTime.now().millisecondsSinceEpoch),
|
||||||
);
|
);
|
||||||
await sendNewPushKey(contact.userId, pushKey);
|
await sendNewPushKey(contact.userId, pushKey);
|
||||||
// only store a maximum of two keys
|
// only store a maximum of two keys
|
||||||
|
|
@ -87,7 +86,7 @@ Future<void> setupNotificationWithUsers({
|
||||||
final pushKey = PushKey(
|
final pushKey = PushKey(
|
||||||
id: Int64(1),
|
id: Int64(1),
|
||||||
key: List<int>.generate(32, (index) => random.nextInt(256)),
|
key: List<int>.generate(32, (index) => random.nextInt(256)),
|
||||||
createdAtUnixTimestamp: Int64(clock.now().millisecondsSinceEpoch),
|
createdAtUnixTimestamp: Int64(DateTime.now().millisecondsSinceEpoch),
|
||||||
);
|
);
|
||||||
await sendNewPushKey(contact.userId, pushKey);
|
await sendNewPushKey(contact.userId, pushKey);
|
||||||
pushUsers.add(
|
pushUsers.add(
|
||||||
|
|
@ -174,7 +173,7 @@ Future<void> handleNewPushKey(int fromUserId, int keyId, List<int> key) async {
|
||||||
PushKey(
|
PushKey(
|
||||||
id: Int64(keyId),
|
id: Int64(keyId),
|
||||||
key: key,
|
key: key,
|
||||||
createdAtUnixTimestamp: Int64(clock.now().millisecondsSinceEpoch),
|
createdAtUnixTimestamp: Int64(DateTime.now().millisecondsSinceEpoch),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -342,7 +341,7 @@ Future<Uint8List?> encryptPushNotification(
|
||||||
final createdAt = DateTime.fromMillisecondsSinceEpoch(
|
final createdAt = DateTime.fromMillisecondsSinceEpoch(
|
||||||
pushUser.pushKeys.last.createdAtUnixTimestamp.toInt(),
|
pushUser.pushKeys.last.createdAtUnixTimestamp.toInt(),
|
||||||
);
|
);
|
||||||
final timeBefore = clock.now().subtract(const Duration(days: 8));
|
final timeBefore = DateTime.now().subtract(const Duration(days: 8));
|
||||||
if (createdAt.isBefore(timeBefore)) {
|
if (createdAt.isBefore(timeBefore)) {
|
||||||
await requestNewPushKeysForUser(toUserId);
|
await requestNewPushKeysForUser(toUserId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -21,7 +21,8 @@ Future<IdentityKeyPair?> getSignalIdentityKeyPair() async {
|
||||||
// It then checks if it should update a new session key
|
// It then checks if it should update a new session key
|
||||||
Future<void> signalHandleNewServerConnection() async {
|
Future<void> signalHandleNewServerConnection() async {
|
||||||
if (gUser.signalLastSignedPreKeyUpdated != null) {
|
if (gUser.signalLastSignedPreKeyUpdated != null) {
|
||||||
final fortyEightHoursAgo = clock.now().subtract(const Duration(hours: 48));
|
final fortyEightHoursAgo =
|
||||||
|
DateTime.now().subtract(const Duration(hours: 48));
|
||||||
final isYoungerThan48Hours =
|
final isYoungerThan48Hours =
|
||||||
(gUser.signalLastSignedPreKeyUpdated!).isAfter(fortyEightHoursAgo);
|
(gUser.signalLastSignedPreKeyUpdated!).isAfter(fortyEightHoursAgo);
|
||||||
if (isYoungerThan48Hours) {
|
if (isYoungerThan48Hours) {
|
||||||
|
|
@ -35,7 +36,7 @@ Future<void> signalHandleNewServerConnection() async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await updateUserdata((user) {
|
await updateUserdata((user) {
|
||||||
user.signalLastSignedPreKeyUpdated = clock.now();
|
user.signalLastSignedPreKeyUpdated = DateTime.now();
|
||||||
return user;
|
return user;
|
||||||
});
|
});
|
||||||
final res = await apiService.updateSignedPreKey(
|
final res = await apiService.updateSignedPreKey(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -23,17 +22,17 @@ class OtherPreKeys {
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex requestNewKeys = Mutex();
|
Mutex requestNewKeys = Mutex();
|
||||||
DateTime lastPreKeyRequest = clock.now().subtract(const Duration(hours: 1));
|
DateTime lastPreKeyRequest = DateTime.now().subtract(const Duration(hours: 1));
|
||||||
DateTime lastSignedPreKeyRequest =
|
DateTime lastSignedPreKeyRequest =
|
||||||
clock.now().subtract(const Duration(hours: 1));
|
DateTime.now().subtract(const Duration(hours: 1));
|
||||||
|
|
||||||
Future<void> requestNewPrekeysForContact(int contactId) async {
|
Future<void> requestNewPrekeysForContact(int contactId) async {
|
||||||
if (lastPreKeyRequest
|
if (lastPreKeyRequest
|
||||||
.isAfter(clock.now().subtract(const Duration(seconds: 60)))) {
|
.isAfter(DateTime.now().subtract(const Duration(seconds: 60)))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.info('[PREKEY] Requesting new PREKEYS for $contactId');
|
Log.info('[PREKEY] Requesting new PREKEYS for $contactId');
|
||||||
lastPreKeyRequest = clock.now();
|
lastPreKeyRequest = DateTime.now();
|
||||||
await requestNewKeys.protect(() async {
|
await requestNewKeys.protect(() async {
|
||||||
final otherKeys = await apiService.getPreKeysByUserId(contactId);
|
final otherKeys = await apiService.getPreKeysByUserId(contactId);
|
||||||
if (otherKeys != null) {
|
if (otherKeys != null) {
|
||||||
|
|
@ -66,11 +65,11 @@ Future<SignalContactPreKey?> getPreKeyByContactId(int contactId) async {
|
||||||
|
|
||||||
Future<void> requestNewSignedPreKeyForContact(int contactId) async {
|
Future<void> requestNewSignedPreKeyForContact(int contactId) async {
|
||||||
if (lastSignedPreKeyRequest
|
if (lastSignedPreKeyRequest
|
||||||
.isAfter(clock.now().subtract(const Duration(seconds: 60)))) {
|
.isAfter(DateTime.now().subtract(const Duration(seconds: 60)))) {
|
||||||
Log.info('last signed pre request was 60s before');
|
Log.info('last signed pre request was 60s before');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastSignedPreKeyRequest = clock.now();
|
lastSignedPreKeyRequest = DateTime.now();
|
||||||
await requestNewKeys.protect(() async {
|
await requestNewKeys.protect(() async {
|
||||||
final signedPreKey = await apiService.getSignedKeyByUserId(contactId);
|
final signedPreKey = await apiService.getSignedKeyByUserId(contactId);
|
||||||
if (signedPreKey != null) {
|
if (signedPreKey != null) {
|
||||||
|
|
@ -97,7 +96,8 @@ Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(
|
||||||
await twonlyDB.signalDao.getSignedPreKeyByContactId(contactId);
|
await twonlyDB.signalDao.getSignedPreKeyByContactId(contactId);
|
||||||
|
|
||||||
if (signedPreKey != null) {
|
if (signedPreKey != null) {
|
||||||
final fortyEightHoursAgo = clock.now().subtract(const Duration(hours: 48));
|
final fortyEightHoursAgo =
|
||||||
|
DateTime.now().subtract(const Duration(hours: 48));
|
||||||
final isOlderThan48Hours =
|
final isOlderThan48Hours =
|
||||||
signedPreKey.createdAt.isBefore(fortyEightHoursAgo);
|
signedPreKey.createdAt.isBefore(fortyEightHoursAgo);
|
||||||
if (isOlderThan48Hours) {
|
if (isOlderThan48Hours) {
|
||||||
|
|
@ -105,7 +105,7 @@ Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unawaited(requestNewSignedPreKeyForContact(contactId));
|
unawaited(requestNewSignedPreKeyForContact(contactId));
|
||||||
Log.warn('Contact $contactId does not have a signed pre key!');
|
Log.error('Contact $contactId does not have a signed pre key!');
|
||||||
}
|
}
|
||||||
return signedPreKey;
|
return signedPreKey;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
@ -35,7 +34,8 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
|
||||||
|
|
||||||
final lastUpdateTime = gUser.twonlySafeBackup!.lastBackupDone;
|
final lastUpdateTime = gUser.twonlySafeBackup!.lastBackupDone;
|
||||||
if (!force && lastUpdateTime != null) {
|
if (!force && lastUpdateTime != null) {
|
||||||
if (lastUpdateTime.isAfter(clock.now().subtract(const Duration(days: 1)))) {
|
if (lastUpdateTime
|
||||||
|
.isAfter(DateTime.now().subtract(const Duration(days: 1)))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +118,7 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
|
||||||
|
|
||||||
if (gUser.twonlySafeBackup!.lastBackupDone == null ||
|
if (gUser.twonlySafeBackup!.lastBackupDone == null ||
|
||||||
gUser.twonlySafeBackup!.lastBackupDone!
|
gUser.twonlySafeBackup!.lastBackupDone!
|
||||||
.isAfter(clock.now().subtract(const Duration(days: 90)))) {
|
.isAfter(DateTime.now().subtract(const Duration(days: 90)))) {
|
||||||
force = true;
|
force = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +190,7 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
|
||||||
Log.info('Starting upload from twonly Backup.');
|
Log.info('Starting upload from twonly Backup.');
|
||||||
await updateUserdata((user) {
|
await updateUserdata((user) {
|
||||||
user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.pending;
|
user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.pending;
|
||||||
user.twonlySafeBackup!.lastBackupDone = clock.now();
|
user.twonlySafeBackup!.lastBackupDone = DateTime.now();
|
||||||
user.twonlySafeBackup!.lastBackupSize = encryptedBackupBytes.length;
|
user.twonlySafeBackup!.lastBackupSize = encryptedBackupBytes.length;
|
||||||
return user;
|
return user;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
Future<void> createPushAvatars() async {
|
Future<void> createPushAvatars() async {
|
||||||
final contacts = await twonlyDB.contactsDao.getAllContacts();
|
final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts();
|
||||||
|
|
||||||
for (final contact in contacts) {
|
for (final contact in contacts) {
|
||||||
if (contact.avatarSvgCompressed == null) continue;
|
if (contact.avatarSvgCompressed == null) continue;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
|
|
@ -97,7 +96,7 @@ Future<void> _writeLogToFile(LogRecord record) async {
|
||||||
|
|
||||||
// Prepare the log message
|
// Prepare the log message
|
||||||
final logMessage =
|
final logMessage =
|
||||||
'${clock.now().toString().split(".")[0]} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n';
|
'${DateTime.now().toString().split(".")[0]} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n';
|
||||||
|
|
||||||
await writeToLogGuard.protect(() async {
|
await writeToLogGuard.protect(() async {
|
||||||
// Append the log message to the file
|
// Append the log message to the file
|
||||||
|
|
@ -150,6 +149,5 @@ String _getCallerSourceCodeFilename() {
|
||||||
firstLine.split('/').last.split(':').first; // Extract the file name
|
firstLine.split('/').last.split(':').first; // Extract the file name
|
||||||
lineNumber = firstLine.split(':')[1]; // Extract the line number
|
lineNumber = firstLine.split(':')[1]; // Extract the line number
|
||||||
}
|
}
|
||||||
lineNumber = lineNumber.replaceAll(')', '');
|
|
||||||
return '$fileName:$lineNumber';
|
return '$fileName:$lineNumber';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:convert/convert.dart';
|
|
||||||
import 'package:crypto/crypto.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||||
|
|
@ -198,7 +195,7 @@ bool isDarkMode(BuildContext context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isToday(DateTime lastImageSend) {
|
bool isToday(DateTime lastImageSend) {
|
||||||
final now = clock.now();
|
final now = DateTime.now();
|
||||||
return lastImageSend.year == now.year &&
|
return lastImageSend.year == now.year &&
|
||||||
lastImageSend.month == now.month &&
|
lastImageSend.month == now.month &&
|
||||||
lastImageSend.day == now.day;
|
lastImageSend.day == now.day;
|
||||||
|
|
@ -236,7 +233,7 @@ String formatDateTime(BuildContext context, DateTime? dateTime) {
|
||||||
if (dateTime == null) {
|
if (dateTime == null) {
|
||||||
return 'Never';
|
return 'Never';
|
||||||
}
|
}
|
||||||
final now = clock.now();
|
final now = DateTime.now();
|
||||||
final difference = now.difference(dateTime);
|
final difference = now.difference(dateTime);
|
||||||
|
|
||||||
final date = DateFormat.yMd(Localizations.localeOf(context).toLanguageTag())
|
final date = DateFormat.yMd(Localizations.localeOf(context).toLanguageTag())
|
||||||
|
|
@ -370,12 +367,3 @@ void printWrapped(String text) {
|
||||||
// ignore: avoid_print
|
// ignore: avoid_print
|
||||||
pattern.allMatches(text).forEach((match) => print(match.group(0)));
|
pattern.allMatches(text).forEach((match) => print(match.group(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<int>> sha256File(File file) async {
|
|
||||||
final input = file.openRead();
|
|
||||||
final sha256Sink = AccumulatorSink<Digest>();
|
|
||||||
final converter = sha256.startChunkedConversion(sha256Sink);
|
|
||||||
await input.forEach(converter.add);
|
|
||||||
converter.close();
|
|
||||||
return sha256Sink.events.single.bytes;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
@ -166,7 +165,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
DateTime? _videoRecordingStarted;
|
DateTime? _videoRecordingStarted;
|
||||||
Timer? _videoRecordingTimer;
|
Timer? _videoRecordingTimer;
|
||||||
|
|
||||||
DateTime _currentTime = clock.now();
|
DateTime _currentTime = DateTime.now();
|
||||||
final GlobalKey keyTriggerButton = GlobalKey();
|
final GlobalKey keyTriggerButton = GlobalKey();
|
||||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
|
@ -520,7 +519,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
_videoRecordingTimer =
|
_videoRecordingTimer =
|
||||||
Timer.periodic(const Duration(milliseconds: 15), (timer) {
|
Timer.periodic(const Duration(milliseconds: 15), (timer) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentTime = clock.now();
|
_currentTime = DateTime.now();
|
||||||
});
|
});
|
||||||
if (_videoRecordingStarted != null &&
|
if (_videoRecordingStarted != null &&
|
||||||
_currentTime.difference(_videoRecordingStarted!).inSeconds >=
|
_currentTime.difference(_videoRecordingStarted!).inSeconds >=
|
||||||
|
|
@ -531,7 +530,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setState(() {
|
setState(() {
|
||||||
_videoRecordingStarted = clock.now();
|
_videoRecordingStarted = DateTime.now();
|
||||||
_isVideoRecording = true;
|
_isVideoRecording = true;
|
||||||
});
|
});
|
||||||
} on CameraException catch (e) {
|
} on CameraException catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
class SaveToGalleryButton extends StatefulWidget {
|
class SaveToGalleryButton extends StatefulWidget {
|
||||||
|
|
@ -45,10 +48,37 @@ class SaveToGalleryButtonState extends State<SaveToGalleryButton> {
|
||||||
});
|
});
|
||||||
|
|
||||||
await widget.storeImageAsOriginal();
|
await widget.storeImageAsOriginal();
|
||||||
|
|
||||||
|
String? res;
|
||||||
|
|
||||||
|
final storedMediaPath = widget.mediaService.storedPath;
|
||||||
|
|
||||||
await widget.mediaService.storeMediaFile();
|
await widget.mediaService.storeMediaFile();
|
||||||
|
|
||||||
|
if (gUser.storeMediaFilesInGallery) {
|
||||||
|
if (widget.mediaService.mediaFile.type == MediaType.video) {
|
||||||
|
res = await saveVideoToGallery(storedMediaPath.path);
|
||||||
|
} else {
|
||||||
|
res = await saveImageToGallery(
|
||||||
|
storedMediaPath.readAsBytesSync(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == null) {
|
||||||
|
setState(() {
|
||||||
|
_imageSaved = true;
|
||||||
|
});
|
||||||
|
} else if (mounted && context.mounted) {
|
||||||
|
Log.error('Could not store media file in the gallery.');
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(res),
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_imageSaved = true;
|
|
||||||
_imageSaving = false;
|
_imageSaving = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class VideoRecordingTimer extends StatelessWidget {
|
class VideoRecordingTimer extends StatelessWidget {
|
||||||
|
|
@ -13,7 +12,7 @@ class VideoRecordingTimer extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (videoRecordingStarted != null) {
|
if (videoRecordingStarted != null) {
|
||||||
final currentTime = clock.now();
|
final currentTime = DateTime.now();
|
||||||
return Positioned(
|
return Positioned(
|
||||||
top: 50,
|
top: 50,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ class BackgroundLayerData extends Layer {
|
||||||
required this.image,
|
required this.image,
|
||||||
});
|
});
|
||||||
ImageItem image;
|
ImageItem image;
|
||||||
bool imageLoaded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FilterLayerData extends Layer {
|
class FilterLayerData extends Layer {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||||
|
|
||||||
|
/// Main layer
|
||||||
class BackgroundLayer extends StatefulWidget {
|
class BackgroundLayer extends StatefulWidget {
|
||||||
const BackgroundLayer({
|
const BackgroundLayer({
|
||||||
required this.layerData,
|
required this.layerData,
|
||||||
|
|
@ -22,17 +23,7 @@ class _BackgroundLayerState extends State<BackgroundLayer> {
|
||||||
height: widget.layerData.image.height.toDouble(),
|
height: widget.layerData.image.height.toDouble(),
|
||||||
// color: Theme.of(context).colorScheme.surface,
|
// color: Theme.of(context).colorScheme.surface,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
child: Image.memory(
|
child: Image.memory(widget.layerData.image.bytes),
|
||||||
widget.layerData.image.bytes,
|
|
||||||
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
|
|
||||||
if (wasSynchronouslyLoaded || frame != null) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
widget.layerData.imageLoaded = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
||||||
|
|
@ -10,8 +9,8 @@ class DateTimeFilter extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final currentTime = DateFormat('HH:mm').format(clock.now());
|
final currentTime = DateFormat('HH:mm').format(DateTime.now());
|
||||||
final currentDate = DateFormat('dd.MM.yyyy').format(clock.now());
|
final currentDate = DateFormat('dd.MM.yyyy').format(DateTime.now());
|
||||||
return FilterSkeleton(
|
return FilterSkeleton(
|
||||||
child: Positioned(
|
child: Positioned(
|
||||||
bottom: 80,
|
bottom: 80,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
@ -134,7 +133,7 @@ Future<List<Sticker>> getStickerIndex() async {
|
||||||
|
|
||||||
if (indexFile.existsSync() && kReleaseMode) {
|
if (indexFile.existsSync() && kReleaseMode) {
|
||||||
final lastModified = indexFile.lastModifiedSync();
|
final lastModified = indexFile.lastModifiedSync();
|
||||||
final difference = clock.now().difference(lastModified);
|
final difference = DateTime.now().difference(lastModified);
|
||||||
final content = await indexFile.readAsString();
|
final content = await indexFile.readAsString();
|
||||||
final jsonList = json.decode(content) as List;
|
final jsonList = json.decode(content) as List;
|
||||||
res = jsonList
|
res = jsonList
|
||||||
|
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
|
|
||||||
class SelectShowTime extends StatefulWidget {
|
|
||||||
const SelectShowTime({
|
|
||||||
required this.initialItem,
|
|
||||||
required this.setMaxShowTime,
|
|
||||||
required this.options,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final int initialItem;
|
|
||||||
final void Function(int?, bool) setMaxShowTime;
|
|
||||||
final List<int?> options;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<SelectShowTime> createState() => _SelectShowTimeState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SelectShowTimeState extends State<SelectShowTime> {
|
|
||||||
bool _storeAsDefault = false;
|
|
||||||
int? _selectedItem;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
height: 350,
|
|
||||||
padding: const EdgeInsets.only(top: 6),
|
|
||||||
margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
|
||||||
color: CupertinoColors.systemBackground.resolveFrom(context),
|
|
||||||
child: SafeArea(
|
|
||||||
top: false,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.all(10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(32),
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
height: 3,
|
|
||||||
width: 60,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: CupertinoPicker(
|
|
||||||
magnification: 1.22,
|
|
||||||
squeeze: 1.2,
|
|
||||||
useMagnifier: true,
|
|
||||||
itemExtent: 32,
|
|
||||||
scrollController: FixedExtentScrollController(
|
|
||||||
initialItem: widget.initialItem,
|
|
||||||
),
|
|
||||||
onSelectedItemChanged: (int selectedItem) {
|
|
||||||
_selectedItem = selectedItem;
|
|
||||||
widget.setMaxShowTime(
|
|
||||||
widget.options[selectedItem],
|
|
||||||
_storeAsDefault,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
children: widget.options.map((e) {
|
|
||||||
return Center(
|
|
||||||
child: Text(e == null ? '∞' : '${e ~/ 1000}s'),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Checkbox(
|
|
||||||
value: _storeAsDefault,
|
|
||||||
onChanged: (value) => setState(() {
|
|
||||||
_storeAsDefault = !_storeAsDefault;
|
|
||||||
if (_selectedItem != null) {
|
|
||||||
widget.setMaxShowTime(
|
|
||||||
widget.options[_selectedItem!],
|
|
||||||
_storeAsDefault,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
Text(context.lang.storeAsDefault),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue