mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-03-03 17:06:47 +00:00
Merge pull request #377 from twonlyapp/dev
- Allows to reopen send images (if send without time limit or enabled auth) - Added support for front camera zoom - Several bug fixes
This commit is contained in:
commit
0ce197ce55
38 changed files with 7987 additions and 374 deletions
|
|
@ -1,5 +1,11 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.0.86
|
||||||
|
|
||||||
|
- Allows to reopen send images (if send without time limit or enabled auth)
|
||||||
|
- Added support for front camera zoom
|
||||||
|
- Several bug fixes
|
||||||
|
|
||||||
## 0.0.83
|
## 0.0.83
|
||||||
|
|
||||||
- Improved view of the diagnostic log
|
- Improved view of the diagnostic log
|
||||||
|
|
|
||||||
45
README.md
45
README.md
|
|
@ -2,31 +2,26 @@
|
||||||
|
|
||||||
<a href="https://twonly.eu" rel="some text"><img src="docs/header.webp" alt="twonly, a privacy-friendly way to connect with friends through secure, spontaneous image sharing." /></a>
|
<a href="https://twonly.eu" rel="some text"><img src="docs/header.webp" alt="twonly, a privacy-friendly way to connect with friends through secure, spontaneous image sharing." /></a>
|
||||||
|
|
||||||
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) app. twonly is a replacement for Snapchat, but its purpose is not to replace instant messaging apps, as there are already [many fantastic alternatives](https://www.messenger-matrix.de/messenger-matrix-en.html) out there. It was started because I liked the basic features of Snapchat, such as opening with the camera, the easy-to-use image editor, and the focus on sending fun pictures to friends. But I was annoyed by Snapchat's forced AI chat, receiving random messages to follow strangers, and not knowing how my sent images/text messages were encrypted, if at all. I am also very critical of the direction in which the US is currently moving and therefore try to avoid US providers wherever possible.
|
||||||
|
|
||||||
<!-- <a href="https://testflight.apple.com/join/U9B3v2rk" >
|
<div style="margin: 10px 20px 10px 20px">
|
||||||
<img alt="Get it on Testflight button" src="https://twonly.eu/assets/buttons/get-it-on-testflight.png"
|
<a href="https://apps.apple.com/de/app/twonly/id6743774441">
|
||||||
width="100px" />
|
<img alt="Get it on App Store button" src="https://twonly.eu/assets/buttons/download-on-the-app-store.svg"
|
||||||
</a> -->
|
width="100px" />
|
||||||
<div class="my-5 space-x-4">
|
</a>
|
||||||
<div class="flex gap-5 items-center justify-center">
|
<a href="https://play.google.com/store/apps/details?id=eu.twonly">
|
||||||
<a href="https://apps.apple.com/de/app/twonly/id6743774441">
|
<img alt="Get it on Google-Play button" src="https://twonly.eu/assets/buttons/get-it-in-google-play.webp"
|
||||||
<img alt="Get it on App Store button" src="https://twonly.eu/assets/buttons/download-on-the-app-store.svg"
|
width="110px" />
|
||||||
width="100px" />
|
</a>
|
||||||
</a>
|
<a href="https://github.com/twonlyapp/twonly-app/releases">
|
||||||
<a href="https://play.google.com/store/apps/details?id=eu.twonly">
|
<img alt="Get it on Github button" src="https://twonly.eu/assets/buttons/get-it-on-github.webp" width="110px" />
|
||||||
<img alt="Get it on Google-Play button" src="https://twonly.eu/assets/buttons/get-it-in-google-play.webp"
|
</a>
|
||||||
width="110px" />
|
<a href="https://releases.twonly.eu/fdroid/repo/">
|
||||||
</a>
|
<img alt="Get it on F-Droid button" src="https://twonly.eu/assets/buttons/get-it-on-f-droid.webp" width="105px" />
|
||||||
<a href="https://github.com/twonlyapp/twonly-app/releases">
|
</a>
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
|
If you decide to give twonly a try, please keep in mind that it is still in its early stages and is currently being developed by a single student. So if you are not satisfied at the moment, please come back later, as it is constantly being improved, and I may one day be able to develop it full-time.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
@ -42,6 +37,8 @@ This repository contains the complete source code of the [twonly](https://twonly
|
||||||
- For Android: Optional support for [UnifiedPush](https://unifiedpush.org/)
|
- For Android: Optional support for [UnifiedPush](https://unifiedpush.org/)
|
||||||
- For Android: Reproducible Builds
|
- For Android: Reproducible Builds
|
||||||
- Implementing [Sealed Sender](https://signal.org/blog/sealed-sender/) to minimize metadata
|
- Implementing [Sealed Sender](https://signal.org/blog/sealed-sender/) to minimize metadata
|
||||||
|
- Switch from the Signal-Protocol to [MLS](https://github.com/openmls/openmls) for Post-Quantum-Crypto support
|
||||||
|
- And, of course, many more features such as dog filters, E2EE cloud backup, and more.
|
||||||
|
|
||||||
## Security Issues
|
## Security Issues
|
||||||
|
|
||||||
|
|
@ -49,6 +46,10 @@ If you discover a security issue in twonly, please adhere to the coordinated vul
|
||||||
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 :/
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
|
||||||
|
If you have any questions or feature requests, feel free to start a new discussion. Issues are limited to bugs, and for maintainers only.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,14 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
||||||
.watch();
|
.watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<List<GroupMember>> watchContactGroupMember(int contactId) {
|
||||||
|
return (select(groupMembers)
|
||||||
|
..where(
|
||||||
|
(g) => g.contactId.equals(contactId),
|
||||||
|
))
|
||||||
|
.watch();
|
||||||
|
}
|
||||||
|
|
||||||
Stream<Group?> watchGroup(String groupId) {
|
Stream<Group?> watchGroup(String groupId) {
|
||||||
return (select(groups)..where((t) => t.groupId.equals(groupId)))
|
return (select(groups)..where((t) => t.groupId.equals(groupId)))
|
||||||
.watchSingleOrNull();
|
.watchSingleOrNull();
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,9 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
||||||
..where(
|
..where(
|
||||||
(t) =>
|
(t) =>
|
||||||
t.ackByServerAt.isNull() |
|
t.ackByServerAt.isNull() |
|
||||||
t.markForRetry.isSmallerThanValue(markedRetriesTime),
|
t.markForRetry.isSmallerThanValue(markedRetriesTime) |
|
||||||
|
t.markForRetryAfterAccepted
|
||||||
|
.isSmallerThanValue(markedRetriesTime),
|
||||||
))
|
))
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
@ -109,6 +111,19 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
||||||
.write(updates);
|
.write(updates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateReceiptWidthUserId(
|
||||||
|
int fromUserId,
|
||||||
|
String receiptId,
|
||||||
|
ReceiptsCompanion updates,
|
||||||
|
) async {
|
||||||
|
await (update(receipts)
|
||||||
|
..where(
|
||||||
|
(c) =>
|
||||||
|
c.receiptId.equals(receiptId) & c.contactId.equals(fromUserId),
|
||||||
|
))
|
||||||
|
.write(updates);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> markMessagesForRetry(int contactId) async {
|
Future<void> markMessagesForRetry(int contactId) async {
|
||||||
await (update(receipts)..where((c) => c.contactId.equals(contactId))).write(
|
await (update(receipts)..where((c) => c.contactId.equals(contactId))).write(
|
||||||
ReceiptsCompanion(
|
ReceiptsCompanion(
|
||||||
|
|
|
||||||
1
lib/src/database/schemas/twonly_db/drift_schema_v6.json
Normal file
1
lib/src/database/schemas/twonly_db/drift_schema_v6.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -21,6 +21,7 @@ class Receipts extends Table {
|
||||||
boolean().withDefault(const Constant(true))();
|
boolean().withDefault(const Constant(true))();
|
||||||
|
|
||||||
DateTimeColumn get markForRetry => dateTime().nullable()();
|
DateTimeColumn get markForRetry => dateTime().nullable()();
|
||||||
|
DateTimeColumn get markForRetryAfterAccepted => dateTime().nullable()();
|
||||||
|
|
||||||
DateTimeColumn get ackByServerAt => dateTime().nullable()();
|
DateTimeColumn get ackByServerAt => dateTime().nullable()();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 5;
|
int get schemaVersion => 6;
|
||||||
|
|
||||||
static QueryExecutor _openConnection() {
|
static QueryExecutor _openConnection() {
|
||||||
return driftDatabase(
|
return driftDatabase(
|
||||||
|
|
@ -111,6 +111,12 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
schema.mediaFiles.storedFileHash,
|
schema.mediaFiles.storedFileHash,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
from5To6: (m, schema) async {
|
||||||
|
await m.addColumn(
|
||||||
|
schema.receipts,
|
||||||
|
schema.receipts.markForRetryAfterAccepted,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4598,6 +4598,13 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
|
||||||
late final GeneratedColumn<DateTime> markForRetry = GeneratedColumn<DateTime>(
|
late final GeneratedColumn<DateTime> markForRetry = GeneratedColumn<DateTime>(
|
||||||
'mark_for_retry', aliasedName, true,
|
'mark_for_retry', aliasedName, true,
|
||||||
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
||||||
|
static const VerificationMeta _markForRetryAfterAcceptedMeta =
|
||||||
|
const VerificationMeta('markForRetryAfterAccepted');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<DateTime> markForRetryAfterAccepted =
|
||||||
|
GeneratedColumn<DateTime>(
|
||||||
|
'mark_for_retry_after_accepted', aliasedName, true,
|
||||||
|
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
||||||
static const VerificationMeta _ackByServerAtMeta =
|
static const VerificationMeta _ackByServerAtMeta =
|
||||||
const VerificationMeta('ackByServerAt');
|
const VerificationMeta('ackByServerAt');
|
||||||
@override
|
@override
|
||||||
|
|
@ -4634,6 +4641,7 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
|
||||||
message,
|
message,
|
||||||
contactWillSendsReceipt,
|
contactWillSendsReceipt,
|
||||||
markForRetry,
|
markForRetry,
|
||||||
|
markForRetryAfterAccepted,
|
||||||
ackByServerAt,
|
ackByServerAt,
|
||||||
retryCount,
|
retryCount,
|
||||||
lastRetry,
|
lastRetry,
|
||||||
|
|
@ -4684,6 +4692,13 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
|
||||||
markForRetry.isAcceptableOrUnknown(
|
markForRetry.isAcceptableOrUnknown(
|
||||||
data['mark_for_retry']!, _markForRetryMeta));
|
data['mark_for_retry']!, _markForRetryMeta));
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('mark_for_retry_after_accepted')) {
|
||||||
|
context.handle(
|
||||||
|
_markForRetryAfterAcceptedMeta,
|
||||||
|
markForRetryAfterAccepted.isAcceptableOrUnknown(
|
||||||
|
data['mark_for_retry_after_accepted']!,
|
||||||
|
_markForRetryAfterAcceptedMeta));
|
||||||
|
}
|
||||||
if (data.containsKey('ack_by_server_at')) {
|
if (data.containsKey('ack_by_server_at')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_ackByServerAtMeta,
|
_ackByServerAtMeta,
|
||||||
|
|
@ -4726,6 +4741,9 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
|
||||||
data['${effectivePrefix}contact_will_sends_receipt'])!,
|
data['${effectivePrefix}contact_will_sends_receipt'])!,
|
||||||
markForRetry: attachedDatabase.typeMapping.read(
|
markForRetry: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.dateTime, data['${effectivePrefix}mark_for_retry']),
|
DriftSqlType.dateTime, data['${effectivePrefix}mark_for_retry']),
|
||||||
|
markForRetryAfterAccepted: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.dateTime,
|
||||||
|
data['${effectivePrefix}mark_for_retry_after_accepted']),
|
||||||
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
|
||||||
|
|
@ -4752,6 +4770,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
final Uint8List message;
|
final Uint8List message;
|
||||||
final bool contactWillSendsReceipt;
|
final bool contactWillSendsReceipt;
|
||||||
final DateTime? markForRetry;
|
final DateTime? markForRetry;
|
||||||
|
final DateTime? markForRetryAfterAccepted;
|
||||||
final DateTime? ackByServerAt;
|
final DateTime? ackByServerAt;
|
||||||
final int retryCount;
|
final int retryCount;
|
||||||
final DateTime? lastRetry;
|
final DateTime? lastRetry;
|
||||||
|
|
@ -4763,6 +4782,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.contactWillSendsReceipt,
|
required this.contactWillSendsReceipt,
|
||||||
this.markForRetry,
|
this.markForRetry,
|
||||||
|
this.markForRetryAfterAccepted,
|
||||||
this.ackByServerAt,
|
this.ackByServerAt,
|
||||||
required this.retryCount,
|
required this.retryCount,
|
||||||
this.lastRetry,
|
this.lastRetry,
|
||||||
|
|
@ -4780,6 +4800,10 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
if (!nullToAbsent || markForRetry != null) {
|
if (!nullToAbsent || markForRetry != null) {
|
||||||
map['mark_for_retry'] = Variable<DateTime>(markForRetry);
|
map['mark_for_retry'] = Variable<DateTime>(markForRetry);
|
||||||
}
|
}
|
||||||
|
if (!nullToAbsent || markForRetryAfterAccepted != null) {
|
||||||
|
map['mark_for_retry_after_accepted'] =
|
||||||
|
Variable<DateTime>(markForRetryAfterAccepted);
|
||||||
|
}
|
||||||
if (!nullToAbsent || ackByServerAt != null) {
|
if (!nullToAbsent || ackByServerAt != null) {
|
||||||
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt);
|
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt);
|
||||||
}
|
}
|
||||||
|
|
@ -4803,6 +4827,10 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
markForRetry: markForRetry == null && nullToAbsent
|
markForRetry: markForRetry == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(markForRetry),
|
: Value(markForRetry),
|
||||||
|
markForRetryAfterAccepted:
|
||||||
|
markForRetryAfterAccepted == null && nullToAbsent
|
||||||
|
? const Value.absent()
|
||||||
|
: Value(markForRetryAfterAccepted),
|
||||||
ackByServerAt: ackByServerAt == null && nullToAbsent
|
ackByServerAt: ackByServerAt == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(ackByServerAt),
|
: Value(ackByServerAt),
|
||||||
|
|
@ -4825,6 +4853,8 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
contactWillSendsReceipt:
|
contactWillSendsReceipt:
|
||||||
serializer.fromJson<bool>(json['contactWillSendsReceipt']),
|
serializer.fromJson<bool>(json['contactWillSendsReceipt']),
|
||||||
markForRetry: serializer.fromJson<DateTime?>(json['markForRetry']),
|
markForRetry: serializer.fromJson<DateTime?>(json['markForRetry']),
|
||||||
|
markForRetryAfterAccepted:
|
||||||
|
serializer.fromJson<DateTime?>(json['markForRetryAfterAccepted']),
|
||||||
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']),
|
||||||
|
|
@ -4842,6 +4872,8 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
'contactWillSendsReceipt':
|
'contactWillSendsReceipt':
|
||||||
serializer.toJson<bool>(contactWillSendsReceipt),
|
serializer.toJson<bool>(contactWillSendsReceipt),
|
||||||
'markForRetry': serializer.toJson<DateTime?>(markForRetry),
|
'markForRetry': serializer.toJson<DateTime?>(markForRetry),
|
||||||
|
'markForRetryAfterAccepted':
|
||||||
|
serializer.toJson<DateTime?>(markForRetryAfterAccepted),
|
||||||
'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),
|
||||||
|
|
@ -4856,6 +4888,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
Uint8List? message,
|
Uint8List? message,
|
||||||
bool? contactWillSendsReceipt,
|
bool? contactWillSendsReceipt,
|
||||||
Value<DateTime?> markForRetry = const Value.absent(),
|
Value<DateTime?> markForRetry = const Value.absent(),
|
||||||
|
Value<DateTime?> markForRetryAfterAccepted = 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(),
|
||||||
|
|
@ -4869,6 +4902,9 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
|
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
|
||||||
markForRetry:
|
markForRetry:
|
||||||
markForRetry.present ? markForRetry.value : this.markForRetry,
|
markForRetry.present ? markForRetry.value : this.markForRetry,
|
||||||
|
markForRetryAfterAccepted: markForRetryAfterAccepted.present
|
||||||
|
? markForRetryAfterAccepted.value
|
||||||
|
: this.markForRetryAfterAccepted,
|
||||||
ackByServerAt:
|
ackByServerAt:
|
||||||
ackByServerAt.present ? ackByServerAt.value : this.ackByServerAt,
|
ackByServerAt.present ? ackByServerAt.value : this.ackByServerAt,
|
||||||
retryCount: retryCount ?? this.retryCount,
|
retryCount: retryCount ?? this.retryCount,
|
||||||
|
|
@ -4887,6 +4923,9 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
markForRetry: data.markForRetry.present
|
markForRetry: data.markForRetry.present
|
||||||
? data.markForRetry.value
|
? data.markForRetry.value
|
||||||
: this.markForRetry,
|
: this.markForRetry,
|
||||||
|
markForRetryAfterAccepted: data.markForRetryAfterAccepted.present
|
||||||
|
? data.markForRetryAfterAccepted.value
|
||||||
|
: this.markForRetryAfterAccepted,
|
||||||
ackByServerAt: data.ackByServerAt.present
|
ackByServerAt: data.ackByServerAt.present
|
||||||
? data.ackByServerAt.value
|
? data.ackByServerAt.value
|
||||||
: this.ackByServerAt,
|
: this.ackByServerAt,
|
||||||
|
|
@ -4906,6 +4945,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
..write('message: $message, ')
|
..write('message: $message, ')
|
||||||
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
|
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
|
||||||
..write('markForRetry: $markForRetry, ')
|
..write('markForRetry: $markForRetry, ')
|
||||||
|
..write('markForRetryAfterAccepted: $markForRetryAfterAccepted, ')
|
||||||
..write('ackByServerAt: $ackByServerAt, ')
|
..write('ackByServerAt: $ackByServerAt, ')
|
||||||
..write('retryCount: $retryCount, ')
|
..write('retryCount: $retryCount, ')
|
||||||
..write('lastRetry: $lastRetry, ')
|
..write('lastRetry: $lastRetry, ')
|
||||||
|
|
@ -4922,6 +4962,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
$driftBlobEquality.hash(message),
|
$driftBlobEquality.hash(message),
|
||||||
contactWillSendsReceipt,
|
contactWillSendsReceipt,
|
||||||
markForRetry,
|
markForRetry,
|
||||||
|
markForRetryAfterAccepted,
|
||||||
ackByServerAt,
|
ackByServerAt,
|
||||||
retryCount,
|
retryCount,
|
||||||
lastRetry,
|
lastRetry,
|
||||||
|
|
@ -4936,6 +4977,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
|
||||||
$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.markForRetry == this.markForRetry &&
|
||||||
|
other.markForRetryAfterAccepted == this.markForRetryAfterAccepted &&
|
||||||
other.ackByServerAt == this.ackByServerAt &&
|
other.ackByServerAt == this.ackByServerAt &&
|
||||||
other.retryCount == this.retryCount &&
|
other.retryCount == this.retryCount &&
|
||||||
other.lastRetry == this.lastRetry &&
|
other.lastRetry == this.lastRetry &&
|
||||||
|
|
@ -4949,6 +4991,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
final Value<Uint8List> message;
|
final Value<Uint8List> message;
|
||||||
final Value<bool> contactWillSendsReceipt;
|
final Value<bool> contactWillSendsReceipt;
|
||||||
final Value<DateTime?> markForRetry;
|
final Value<DateTime?> markForRetry;
|
||||||
|
final Value<DateTime?> markForRetryAfterAccepted;
|
||||||
final Value<DateTime?> ackByServerAt;
|
final Value<DateTime?> ackByServerAt;
|
||||||
final Value<int> retryCount;
|
final Value<int> retryCount;
|
||||||
final Value<DateTime?> lastRetry;
|
final Value<DateTime?> lastRetry;
|
||||||
|
|
@ -4961,6 +5004,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
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.markForRetry = const Value.absent(),
|
||||||
|
this.markForRetryAfterAccepted = 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(),
|
||||||
|
|
@ -4974,6 +5018,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
required Uint8List message,
|
required Uint8List message,
|
||||||
this.contactWillSendsReceipt = const Value.absent(),
|
this.contactWillSendsReceipt = const Value.absent(),
|
||||||
this.markForRetry = const Value.absent(),
|
this.markForRetry = const Value.absent(),
|
||||||
|
this.markForRetryAfterAccepted = 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(),
|
||||||
|
|
@ -4989,6 +5034,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
Expression<Uint8List>? message,
|
Expression<Uint8List>? message,
|
||||||
Expression<bool>? contactWillSendsReceipt,
|
Expression<bool>? contactWillSendsReceipt,
|
||||||
Expression<DateTime>? markForRetry,
|
Expression<DateTime>? markForRetry,
|
||||||
|
Expression<DateTime>? markForRetryAfterAccepted,
|
||||||
Expression<DateTime>? ackByServerAt,
|
Expression<DateTime>? ackByServerAt,
|
||||||
Expression<int>? retryCount,
|
Expression<int>? retryCount,
|
||||||
Expression<DateTime>? lastRetry,
|
Expression<DateTime>? lastRetry,
|
||||||
|
|
@ -5003,6 +5049,8 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
if (contactWillSendsReceipt != null)
|
if (contactWillSendsReceipt != null)
|
||||||
'contact_will_sends_receipt': contactWillSendsReceipt,
|
'contact_will_sends_receipt': contactWillSendsReceipt,
|
||||||
if (markForRetry != null) 'mark_for_retry': markForRetry,
|
if (markForRetry != null) 'mark_for_retry': markForRetry,
|
||||||
|
if (markForRetryAfterAccepted != null)
|
||||||
|
'mark_for_retry_after_accepted': markForRetryAfterAccepted,
|
||||||
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,
|
||||||
|
|
@ -5018,6 +5066,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
Value<Uint8List>? message,
|
Value<Uint8List>? message,
|
||||||
Value<bool>? contactWillSendsReceipt,
|
Value<bool>? contactWillSendsReceipt,
|
||||||
Value<DateTime?>? markForRetry,
|
Value<DateTime?>? markForRetry,
|
||||||
|
Value<DateTime?>? markForRetryAfterAccepted,
|
||||||
Value<DateTime?>? ackByServerAt,
|
Value<DateTime?>? ackByServerAt,
|
||||||
Value<int>? retryCount,
|
Value<int>? retryCount,
|
||||||
Value<DateTime?>? lastRetry,
|
Value<DateTime?>? lastRetry,
|
||||||
|
|
@ -5031,6 +5080,8 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
contactWillSendsReceipt:
|
contactWillSendsReceipt:
|
||||||
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
|
contactWillSendsReceipt ?? this.contactWillSendsReceipt,
|
||||||
markForRetry: markForRetry ?? this.markForRetry,
|
markForRetry: markForRetry ?? this.markForRetry,
|
||||||
|
markForRetryAfterAccepted:
|
||||||
|
markForRetryAfterAccepted ?? this.markForRetryAfterAccepted,
|
||||||
ackByServerAt: ackByServerAt ?? this.ackByServerAt,
|
ackByServerAt: ackByServerAt ?? this.ackByServerAt,
|
||||||
retryCount: retryCount ?? this.retryCount,
|
retryCount: retryCount ?? this.retryCount,
|
||||||
lastRetry: lastRetry ?? this.lastRetry,
|
lastRetry: lastRetry ?? this.lastRetry,
|
||||||
|
|
@ -5061,6 +5112,10 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
if (markForRetry.present) {
|
if (markForRetry.present) {
|
||||||
map['mark_for_retry'] = Variable<DateTime>(markForRetry.value);
|
map['mark_for_retry'] = Variable<DateTime>(markForRetry.value);
|
||||||
}
|
}
|
||||||
|
if (markForRetryAfterAccepted.present) {
|
||||||
|
map['mark_for_retry_after_accepted'] =
|
||||||
|
Variable<DateTime>(markForRetryAfterAccepted.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);
|
||||||
}
|
}
|
||||||
|
|
@ -5088,6 +5143,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
|
||||||
..write('message: $message, ')
|
..write('message: $message, ')
|
||||||
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
|
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
|
||||||
..write('markForRetry: $markForRetry, ')
|
..write('markForRetry: $markForRetry, ')
|
||||||
|
..write('markForRetryAfterAccepted: $markForRetryAfterAccepted, ')
|
||||||
..write('ackByServerAt: $ackByServerAt, ')
|
..write('ackByServerAt: $ackByServerAt, ')
|
||||||
..write('retryCount: $retryCount, ')
|
..write('retryCount: $retryCount, ')
|
||||||
..write('lastRetry: $lastRetry, ')
|
..write('lastRetry: $lastRetry, ')
|
||||||
|
|
@ -11771,6 +11827,7 @@ typedef $$ReceiptsTableCreateCompanionBuilder = ReceiptsCompanion Function({
|
||||||
required Uint8List message,
|
required Uint8List message,
|
||||||
Value<bool> contactWillSendsReceipt,
|
Value<bool> contactWillSendsReceipt,
|
||||||
Value<DateTime?> markForRetry,
|
Value<DateTime?> markForRetry,
|
||||||
|
Value<DateTime?> markForRetryAfterAccepted,
|
||||||
Value<DateTime?> ackByServerAt,
|
Value<DateTime?> ackByServerAt,
|
||||||
Value<int> retryCount,
|
Value<int> retryCount,
|
||||||
Value<DateTime?> lastRetry,
|
Value<DateTime?> lastRetry,
|
||||||
|
|
@ -11784,6 +11841,7 @@ typedef $$ReceiptsTableUpdateCompanionBuilder = ReceiptsCompanion Function({
|
||||||
Value<Uint8List> message,
|
Value<Uint8List> message,
|
||||||
Value<bool> contactWillSendsReceipt,
|
Value<bool> contactWillSendsReceipt,
|
||||||
Value<DateTime?> markForRetry,
|
Value<DateTime?> markForRetry,
|
||||||
|
Value<DateTime?> markForRetryAfterAccepted,
|
||||||
Value<DateTime?> ackByServerAt,
|
Value<DateTime?> ackByServerAt,
|
||||||
Value<int> retryCount,
|
Value<int> retryCount,
|
||||||
Value<DateTime?> lastRetry,
|
Value<DateTime?> lastRetry,
|
||||||
|
|
@ -11848,6 +11906,10 @@ class $$ReceiptsTableFilterComposer
|
||||||
ColumnFilters<DateTime> get markForRetry => $composableBuilder(
|
ColumnFilters<DateTime> get markForRetry => $composableBuilder(
|
||||||
column: $table.markForRetry, builder: (column) => ColumnFilters(column));
|
column: $table.markForRetry, builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
|
ColumnFilters<DateTime> get markForRetryAfterAccepted => $composableBuilder(
|
||||||
|
column: $table.markForRetryAfterAccepted,
|
||||||
|
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));
|
||||||
|
|
||||||
|
|
@ -11924,6 +11986,10 @@ class $$ReceiptsTableOrderingComposer
|
||||||
column: $table.markForRetry,
|
column: $table.markForRetry,
|
||||||
builder: (column) => ColumnOrderings(column));
|
builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
|
ColumnOrderings<DateTime> get markForRetryAfterAccepted => $composableBuilder(
|
||||||
|
column: $table.markForRetryAfterAccepted,
|
||||||
|
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));
|
||||||
|
|
@ -11999,6 +12065,9 @@ class $$ReceiptsTableAnnotationComposer
|
||||||
GeneratedColumn<DateTime> get markForRetry => $composableBuilder(
|
GeneratedColumn<DateTime> get markForRetry => $composableBuilder(
|
||||||
column: $table.markForRetry, builder: (column) => column);
|
column: $table.markForRetry, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumn<DateTime> get markForRetryAfterAccepted => $composableBuilder(
|
||||||
|
column: $table.markForRetryAfterAccepted, 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);
|
||||||
|
|
||||||
|
|
@ -12081,6 +12150,7 @@ class $$ReceiptsTableTableManager extends RootTableManager<
|
||||||
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?> markForRetry = const Value.absent(),
|
||||||
|
Value<DateTime?> markForRetryAfterAccepted = 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(),
|
||||||
|
|
@ -12094,6 +12164,7 @@ class $$ReceiptsTableTableManager extends RootTableManager<
|
||||||
message: message,
|
message: message,
|
||||||
contactWillSendsReceipt: contactWillSendsReceipt,
|
contactWillSendsReceipt: contactWillSendsReceipt,
|
||||||
markForRetry: markForRetry,
|
markForRetry: markForRetry,
|
||||||
|
markForRetryAfterAccepted: markForRetryAfterAccepted,
|
||||||
ackByServerAt: ackByServerAt,
|
ackByServerAt: ackByServerAt,
|
||||||
retryCount: retryCount,
|
retryCount: retryCount,
|
||||||
lastRetry: lastRetry,
|
lastRetry: lastRetry,
|
||||||
|
|
@ -12107,6 +12178,7 @@ class $$ReceiptsTableTableManager extends RootTableManager<
|
||||||
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?> markForRetry = const Value.absent(),
|
||||||
|
Value<DateTime?> markForRetryAfterAccepted = 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(),
|
||||||
|
|
@ -12120,6 +12192,7 @@ class $$ReceiptsTableTableManager extends RootTableManager<
|
||||||
message: message,
|
message: message,
|
||||||
contactWillSendsReceipt: contactWillSendsReceipt,
|
contactWillSendsReceipt: contactWillSendsReceipt,
|
||||||
markForRetry: markForRetry,
|
markForRetry: markForRetry,
|
||||||
|
markForRetryAfterAccepted: markForRetryAfterAccepted,
|
||||||
ackByServerAt: ackByServerAt,
|
ackByServerAt: ackByServerAt,
|
||||||
retryCount: retryCount,
|
retryCount: retryCount,
|
||||||
lastRetry: lastRetry,
|
lastRetry: lastRetry,
|
||||||
|
|
|
||||||
|
|
@ -2393,11 +2393,423 @@ class Shape19 extends i0.VersionedTable {
|
||||||
i1.GeneratedColumn<DateTime> _column_103(String aliasedName) =>
|
i1.GeneratedColumn<DateTime> _column_103(String aliasedName) =>
|
||||||
i1.GeneratedColumn<DateTime>('mark_for_retry', aliasedName, true,
|
i1.GeneratedColumn<DateTime>('mark_for_retry', aliasedName, true,
|
||||||
type: i1.DriftSqlType.dateTime);
|
type: i1.DriftSqlType.dateTime);
|
||||||
|
|
||||||
|
final class Schema6 extends i0.VersionedSchema {
|
||||||
|
Schema6({required super.database}) : super(version: 6);
|
||||||
|
@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 Shape20 receipts = Shape20(
|
||||||
|
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_104,
|
||||||
|
_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 Shape20 extends i0.VersionedTable {
|
||||||
|
Shape20({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 markForRetryAfterAccepted =>
|
||||||
|
columnsByName['mark_for_retry_after_accepted']!
|
||||||
|
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_104(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<DateTime>(
|
||||||
|
'mark_for_retry_after_accepted', 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,
|
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
|
||||||
}) {
|
}) {
|
||||||
return (currentVersion, database) async {
|
return (currentVersion, database) async {
|
||||||
switch (currentVersion) {
|
switch (currentVersion) {
|
||||||
|
|
@ -2421,6 +2833,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||||
final migrator = i1.Migrator(database, schema);
|
final migrator = i1.Migrator(database, schema);
|
||||||
await from4To5(migrator, schema);
|
await from4To5(migrator, schema);
|
||||||
return 5;
|
return 5;
|
||||||
|
case 5:
|
||||||
|
final schema = Schema6(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from5To6(migrator, schema);
|
||||||
|
return 6;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
}
|
}
|
||||||
|
|
@ -2432,6 +2849,7 @@ i1.OnUpgrade stepByStep({
|
||||||
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,
|
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
|
||||||
}) =>
|
}) =>
|
||||||
i0.VersionedSchema.stepByStepHelper(
|
i0.VersionedSchema.stepByStepHelper(
|
||||||
step: migrationSteps(
|
step: migrationSteps(
|
||||||
|
|
@ -2439,4 +2857,5 @@ i1.OnUpgrade stepByStep({
|
||||||
from2To3: from2To3,
|
from2To3: from2To3,
|
||||||
from3To4: from3To4,
|
from3To4: from3To4,
|
||||||
from4To5: from4To5,
|
from4To5: from4To5,
|
||||||
|
from5To6: from5To6,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -508,6 +508,12 @@ abstract class AppLocalizations {
|
||||||
/// **'Unpin'**
|
/// **'Unpin'**
|
||||||
String get contextMenuUnpin;
|
String get contextMenuUnpin;
|
||||||
|
|
||||||
|
/// No description provided for @contextMenuViewAgain.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'View again'**
|
||||||
|
String get contextMenuViewAgain;
|
||||||
|
|
||||||
/// No description provided for @mediaViewerAuthReason.
|
/// No description provided for @mediaViewerAuthReason.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -1021,7 +1027,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @contactRemoveBody.
|
/// No description provided for @contactRemoveBody.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Remove the user and permanently delete the chat and all associated media files. This will also delete YOUR ACCOUNT FROM YOUR CONTACT\'S PHONE.'**
|
/// **'Permanently remove the user. If the user tries to send you a new message, you will have to accept the user again first.'**
|
||||||
String get contactRemoveBody;
|
String get contactRemoveBody;
|
||||||
|
|
||||||
/// No description provided for @undo.
|
/// No description provided for @undo.
|
||||||
|
|
@ -2887,8 +2893,14 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @additionalUserAddError.
|
/// No description provided for @additionalUserAddError.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Could not add additional user. Try again later.'**
|
/// **'{username} could not be added, please try again later.'**
|
||||||
String get additionalUserAddError;
|
String additionalUserAddError(Object username);
|
||||||
|
|
||||||
|
/// No description provided for @additionalUserAddErrorNotInFreePlan.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{username} is already on a paid plan and therefore could not be added.'**
|
||||||
|
String additionalUserAddErrorNotInFreePlan(Object username);
|
||||||
|
|
||||||
/// No description provided for @additionalUserAddButton.
|
/// No description provided for @additionalUserAddButton.
|
||||||
///
|
///
|
||||||
|
|
@ -2925,6 +2937,24 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Store as default'**
|
/// **'Store as default'**
|
||||||
String get storeAsDefault;
|
String get storeAsDefault;
|
||||||
|
|
||||||
|
/// No description provided for @deleteUserErrorMessage.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'You can only delete the contact once the direct chat has been deleted and the contact is no longer a member of a group.'**
|
||||||
|
String get deleteUserErrorMessage;
|
||||||
|
|
||||||
|
/// No description provided for @groupSizeLimitError.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Currently, group size is limited to {size} people!'**
|
||||||
|
String groupSizeLimitError(Object size);
|
||||||
|
|
||||||
|
/// No description provided for @authRequestReopenImage.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'You must authenticate to reopen the image.'**
|
||||||
|
String get authRequestReopenImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get contextMenuUnpin => 'Lösen';
|
String get contextMenuUnpin => 'Lösen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contextMenuViewAgain => 'Nochmal anschauen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mediaViewerAuthReason =>
|
String get mediaViewerAuthReason =>
|
||||||
'Bitte authentifiziere dich, um diesen twonly zu sehen!';
|
'Bitte authentifiziere dich, um diesen twonly zu sehen!';
|
||||||
|
|
@ -517,7 +520,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contactRemoveBody =>
|
String get 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.';
|
'Den Benutzer dauerhaft entfernen. Wenn der Benutzer versucht, dir eine neue Nachricht zu senden, musst du den Benutzer erst wieder akzeptieren.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get undo => 'Rückgängig';
|
String get undo => 'Rückgängig';
|
||||||
|
|
@ -1601,8 +1604,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get privacyPolicy => 'Datenschutzerklärung';
|
String get privacyPolicy => 'Datenschutzerklärung';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get additionalUserAddError =>
|
String additionalUserAddError(Object username) {
|
||||||
'Es konnte kein zusätzlicher Nutzer hinzugefügt werden. Versuche es später noch einmal.';
|
return '$username konnte nicht hinzugefügt werden, bitte versuche es später noch einmal.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String additionalUserAddErrorNotInFreePlan(Object username) {
|
||||||
|
return '$username hat bereits einen bezahlten Tarif und konnte daher nicht hinzugefügt werden.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String additionalUserAddButton(Object limit, Object used) {
|
String additionalUserAddButton(Object limit, Object used) {
|
||||||
|
|
@ -1627,4 +1636,17 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storeAsDefault => 'Als Standard speichern';
|
String get storeAsDefault => 'Als Standard speichern';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteUserErrorMessage =>
|
||||||
|
'Du kannst den Kontakt erst löschen, wenn der direkte Chat gelöscht wurde und der Kontakt nicht mehr Mitglied einer Gruppe ist.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String groupSizeLimitError(Object size) {
|
||||||
|
return 'Derzeit ist die Gruppengröße auf $size Personen begrenzt!';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get authRequestReopenImage =>
|
||||||
|
'Um das Bild erneut zu öffnen, musst du dich authentifizieren.';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get contextMenuUnpin => 'Unpin';
|
String get contextMenuUnpin => 'Unpin';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contextMenuViewAgain => 'View again';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mediaViewerAuthReason => 'Please authenticate to see this twonly!';
|
String get mediaViewerAuthReason => 'Please authenticate to see this twonly!';
|
||||||
|
|
||||||
|
|
@ -512,7 +515,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contactRemoveBody =>
|
String get 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.';
|
'Permanently remove the user. If the user tries to send you a new message, you will have to accept the user again first.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get undo => 'Undo';
|
String get undo => 'Undo';
|
||||||
|
|
@ -1590,8 +1593,14 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get privacyPolicy => 'Privacy policy';
|
String get privacyPolicy => 'Privacy policy';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get additionalUserAddError =>
|
String additionalUserAddError(Object username) {
|
||||||
'Could not add additional user. Try again later.';
|
return '$username could not be added, please try again later.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String additionalUserAddErrorNotInFreePlan(Object username) {
|
||||||
|
return '$username is already on a paid plan and therefore could not be added.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String additionalUserAddButton(Object limit, Object used) {
|
String additionalUserAddButton(Object limit, Object used) {
|
||||||
|
|
@ -1615,4 +1624,17 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storeAsDefault => 'Store as default';
|
String get storeAsDefault => 'Store as default';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteUserErrorMessage =>
|
||||||
|
'You can only delete the contact once the direct chat has been deleted and the contact is no longer a member of a group.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String groupSizeLimitError(Object size) {
|
||||||
|
return 'Currently, group size is limited to $size people!';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get authRequestReopenImage =>
|
||||||
|
'You must authenticate to reopen the image.';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,9 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get contextMenuUnpin => 'Unpin';
|
String get contextMenuUnpin => 'Unpin';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contextMenuViewAgain => 'View again';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mediaViewerAuthReason => 'Please authenticate to see this twonly!';
|
String get mediaViewerAuthReason => 'Please authenticate to see this twonly!';
|
||||||
|
|
||||||
|
|
@ -512,7 +515,7 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contactRemoveBody =>
|
String get 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.';
|
'Permanently remove the user. If the user tries to send you a new message, you will have to accept the user again first.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get undo => 'Undo';
|
String get undo => 'Undo';
|
||||||
|
|
@ -1590,8 +1593,14 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||||
String get privacyPolicy => 'Privacy policy';
|
String get privacyPolicy => 'Privacy policy';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get additionalUserAddError =>
|
String additionalUserAddError(Object username) {
|
||||||
'Could not add additional user. Try again later.';
|
return '$username could not be added, please try again later.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String additionalUserAddErrorNotInFreePlan(Object username) {
|
||||||
|
return '$username is already on a paid plan and therefore could not be added.';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String additionalUserAddButton(Object limit, Object used) {
|
String additionalUserAddButton(Object limit, Object used) {
|
||||||
|
|
@ -1615,4 +1624,17 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get storeAsDefault => 'Store as default';
|
String get storeAsDefault => 'Store as default';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteUserErrorMessage =>
|
||||||
|
'You can only delete the contact once the direct chat has been deleted and the contact is no longer a member of a group.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String groupSizeLimitError(Object size) {
|
||||||
|
return 'Currently, group size is limited to $size people!';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get authRequestReopenImage =>
|
||||||
|
'You must authenticate to reopen the image.';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 775c0ffd9523177478681ecff4e8c4613bf57ee3
|
Subproject commit 20f3c2f0a49e4c9be452ecbc84d98054c92974e1
|
||||||
|
|
@ -311,6 +311,82 @@ class PlaintextContent extends $pb.GeneratedMessage {
|
||||||
PlaintextContent_RetryErrorMessage ensureRetryControlError() => $_ensure(1);
|
PlaintextContent_RetryErrorMessage ensureRetryControlError() => $_ensure(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EncryptedContent_ErrorMessages extends $pb.GeneratedMessage {
|
||||||
|
factory EncryptedContent_ErrorMessages({
|
||||||
|
EncryptedContent_ErrorMessages_Type? type,
|
||||||
|
$core.String? relatedReceiptId,
|
||||||
|
}) {
|
||||||
|
final result = create();
|
||||||
|
if (type != null) result.type = type;
|
||||||
|
if (relatedReceiptId != null) result.relatedReceiptId = relatedReceiptId;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptedContent_ErrorMessages._();
|
||||||
|
|
||||||
|
factory EncryptedContent_ErrorMessages.fromBuffer($core.List<$core.int> data,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromBuffer(data, registry);
|
||||||
|
factory EncryptedContent_ErrorMessages.fromJson($core.String json,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromJson(json, registry);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
_omitMessageNames ? '' : 'EncryptedContent.ErrorMessages',
|
||||||
|
createEmptyInstance: create)
|
||||||
|
..e<EncryptedContent_ErrorMessages_Type>(
|
||||||
|
1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE,
|
||||||
|
defaultOrMaker: EncryptedContent_ErrorMessages_Type
|
||||||
|
.ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD,
|
||||||
|
valueOf: EncryptedContent_ErrorMessages_Type.valueOf,
|
||||||
|
enumValues: EncryptedContent_ErrorMessages_Type.values)
|
||||||
|
..aOS(2, _omitFieldNames ? '' : 'relatedReceiptId')
|
||||||
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
EncryptedContent_ErrorMessages clone() =>
|
||||||
|
EncryptedContent_ErrorMessages()..mergeFromMessage(this);
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
EncryptedContent_ErrorMessages copyWith(
|
||||||
|
void Function(EncryptedContent_ErrorMessages) updates) =>
|
||||||
|
super.copyWith(
|
||||||
|
(message) => updates(message as EncryptedContent_ErrorMessages))
|
||||||
|
as EncryptedContent_ErrorMessages;
|
||||||
|
|
||||||
|
@$core.override
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static EncryptedContent_ErrorMessages create() =>
|
||||||
|
EncryptedContent_ErrorMessages._();
|
||||||
|
@$core.override
|
||||||
|
EncryptedContent_ErrorMessages createEmptyInstance() => create();
|
||||||
|
static $pb.PbList<EncryptedContent_ErrorMessages> createRepeated() =>
|
||||||
|
$pb.PbList<EncryptedContent_ErrorMessages>();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static EncryptedContent_ErrorMessages getDefault() => _defaultInstance ??=
|
||||||
|
$pb.GeneratedMessage.$_defaultFor<EncryptedContent_ErrorMessages>(create);
|
||||||
|
static EncryptedContent_ErrorMessages? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
EncryptedContent_ErrorMessages_Type get type => $_getN(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set type(EncryptedContent_ErrorMessages_Type value) => $_setField(1, value);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasType() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearType() => $_clearField(1);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.String get relatedReceiptId => $_getSZ(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set relatedReceiptId($core.String value) => $_setString(1, value);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasRelatedReceiptId() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearRelatedReceiptId() => $_clearField(2);
|
||||||
|
}
|
||||||
|
|
||||||
class EncryptedContent_GroupCreate extends $pb.GeneratedMessage {
|
class EncryptedContent_GroupCreate extends $pb.GeneratedMessage {
|
||||||
factory EncryptedContent_GroupCreate({
|
factory EncryptedContent_GroupCreate({
|
||||||
$core.List<$core.int>? stateKey,
|
$core.List<$core.int>? stateKey,
|
||||||
|
|
@ -1519,6 +1595,7 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
||||||
EncryptedContent_GroupJoin? groupJoin,
|
EncryptedContent_GroupJoin? groupJoin,
|
||||||
EncryptedContent_GroupUpdate? groupUpdate,
|
EncryptedContent_GroupUpdate? groupUpdate,
|
||||||
EncryptedContent_ResendGroupPublicKey? resendGroupPublicKey,
|
EncryptedContent_ResendGroupPublicKey? resendGroupPublicKey,
|
||||||
|
EncryptedContent_ErrorMessages? errorMessages,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (groupId != null) result.groupId = groupId;
|
if (groupId != null) result.groupId = groupId;
|
||||||
|
|
@ -1539,6 +1616,7 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
||||||
if (groupUpdate != null) result.groupUpdate = groupUpdate;
|
if (groupUpdate != null) result.groupUpdate = groupUpdate;
|
||||||
if (resendGroupPublicKey != null)
|
if (resendGroupPublicKey != null)
|
||||||
result.resendGroupPublicKey = resendGroupPublicKey;
|
result.resendGroupPublicKey = resendGroupPublicKey;
|
||||||
|
if (errorMessages != null) result.errorMessages = errorMessages;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1599,6 +1677,9 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
||||||
17, _omitFieldNames ? '' : 'resendGroupPublicKey',
|
17, _omitFieldNames ? '' : 'resendGroupPublicKey',
|
||||||
protoName: 'resendGroupPublicKey',
|
protoName: 'resendGroupPublicKey',
|
||||||
subBuilder: EncryptedContent_ResendGroupPublicKey.create)
|
subBuilder: EncryptedContent_ResendGroupPublicKey.create)
|
||||||
|
..aOM<EncryptedContent_ErrorMessages>(
|
||||||
|
18, _omitFieldNames ? '' : 'errorMessages',
|
||||||
|
subBuilder: EncryptedContent_ErrorMessages.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.')
|
||||||
|
|
@ -1797,6 +1878,18 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(17)
|
@$pb.TagNumber(17)
|
||||||
EncryptedContent_ResendGroupPublicKey ensureResendGroupPublicKey() =>
|
EncryptedContent_ResendGroupPublicKey ensureResendGroupPublicKey() =>
|
||||||
$_ensure(15);
|
$_ensure(15);
|
||||||
|
|
||||||
|
@$pb.TagNumber(18)
|
||||||
|
EncryptedContent_ErrorMessages get errorMessages => $_getN(16);
|
||||||
|
@$pb.TagNumber(18)
|
||||||
|
set errorMessages(EncryptedContent_ErrorMessages value) =>
|
||||||
|
$_setField(18, value);
|
||||||
|
@$pb.TagNumber(18)
|
||||||
|
$core.bool hasErrorMessages() => $_has(16);
|
||||||
|
@$pb.TagNumber(18)
|
||||||
|
void clearErrorMessages() => $_clearField(18);
|
||||||
|
@$pb.TagNumber(18)
|
||||||
|
EncryptedContent_ErrorMessages ensureErrorMessages() => $_ensure(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
const $core.bool _omitFieldNames =
|
const $core.bool _omitFieldNames =
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,32 @@ class PlaintextContent_DecryptionErrorMessage_Type extends $pb.ProtobufEnum {
|
||||||
const PlaintextContent_DecryptionErrorMessage_Type._(super.value, super.name);
|
const PlaintextContent_DecryptionErrorMessage_Type._(super.value, super.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EncryptedContent_ErrorMessages_Type extends $pb.ProtobufEnum {
|
||||||
|
static const EncryptedContent_ErrorMessages_Type
|
||||||
|
ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD =
|
||||||
|
EncryptedContent_ErrorMessages_Type._(
|
||||||
|
0,
|
||||||
|
_omitEnumNames
|
||||||
|
? ''
|
||||||
|
: 'ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD');
|
||||||
|
static const EncryptedContent_ErrorMessages_Type UNKNOWN_MESSAGE_TYPE =
|
||||||
|
EncryptedContent_ErrorMessages_Type._(
|
||||||
|
2, _omitEnumNames ? '' : 'UNKNOWN_MESSAGE_TYPE');
|
||||||
|
|
||||||
|
static const $core.List<EncryptedContent_ErrorMessages_Type> values =
|
||||||
|
<EncryptedContent_ErrorMessages_Type>[
|
||||||
|
ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD,
|
||||||
|
UNKNOWN_MESSAGE_TYPE,
|
||||||
|
];
|
||||||
|
|
||||||
|
static final $core.Map<$core.int, EncryptedContent_ErrorMessages_Type>
|
||||||
|
_byValue = $pb.ProtobufEnum.initByValue(values);
|
||||||
|
static EncryptedContent_ErrorMessages_Type? valueOf($core.int value) =>
|
||||||
|
_byValue[value];
|
||||||
|
|
||||||
|
const EncryptedContent_ErrorMessages_Type._(super.value, super.name);
|
||||||
|
}
|
||||||
|
|
||||||
class EncryptedContent_MessageUpdate_Type extends $pb.ProtobufEnum {
|
class EncryptedContent_MessageUpdate_Type extends $pb.ProtobufEnum {
|
||||||
static const EncryptedContent_MessageUpdate_Type DELETE =
|
static const EncryptedContent_MessageUpdate_Type DELETE =
|
||||||
EncryptedContent_MessageUpdate_Type._(0, _omitEnumNames ? '' : 'DELETE');
|
EncryptedContent_MessageUpdate_Type._(0, _omitEnumNames ? '' : 'DELETE');
|
||||||
|
|
|
||||||
|
|
@ -306,8 +306,19 @@ const EncryptedContent$json = {
|
||||||
'10': 'resendGroupPublicKey',
|
'10': 'resendGroupPublicKey',
|
||||||
'17': true
|
'17': true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'error_messages',
|
||||||
|
'3': 18,
|
||||||
|
'4': 1,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.EncryptedContent.ErrorMessages',
|
||||||
|
'9': 16,
|
||||||
|
'10': 'errorMessages',
|
||||||
|
'17': true
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'3': [
|
'3': [
|
||||||
|
EncryptedContent_ErrorMessages$json,
|
||||||
EncryptedContent_GroupCreate$json,
|
EncryptedContent_GroupCreate$json,
|
||||||
EncryptedContent_GroupJoin$json,
|
EncryptedContent_GroupJoin$json,
|
||||||
EncryptedContent_ResendGroupPublicKey$json,
|
EncryptedContent_ResendGroupPublicKey$json,
|
||||||
|
|
@ -339,6 +350,39 @@ const EncryptedContent$json = {
|
||||||
{'1': '_groupJoin'},
|
{'1': '_groupJoin'},
|
||||||
{'1': '_groupUpdate'},
|
{'1': '_groupUpdate'},
|
||||||
{'1': '_resendGroupPublicKey'},
|
{'1': '_resendGroupPublicKey'},
|
||||||
|
{'1': '_error_messages'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use encryptedContentDescriptor instead')
|
||||||
|
const EncryptedContent_ErrorMessages$json = {
|
||||||
|
'1': 'ErrorMessages',
|
||||||
|
'2': [
|
||||||
|
{
|
||||||
|
'1': 'type',
|
||||||
|
'3': 1,
|
||||||
|
'4': 1,
|
||||||
|
'5': 14,
|
||||||
|
'6': '.EncryptedContent.ErrorMessages.Type',
|
||||||
|
'10': 'type'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'related_receipt_id',
|
||||||
|
'3': 2,
|
||||||
|
'4': 1,
|
||||||
|
'5': 9,
|
||||||
|
'10': 'relatedReceiptId'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'4': [EncryptedContent_ErrorMessages_Type$json],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use encryptedContentDescriptor instead')
|
||||||
|
const EncryptedContent_ErrorMessages_Type$json = {
|
||||||
|
'1': 'Type',
|
||||||
|
'2': [
|
||||||
|
{'1': 'ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD', '2': 0},
|
||||||
|
{'1': 'UNKNOWN_MESSAGE_TYPE', '2': 2},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -772,57 +816,62 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
||||||
'AQESRAoLZ3JvdXBVcGRhdGUYECABKAsyHS5FbmNyeXB0ZWRDb250ZW50Lkdyb3VwVXBkYXRlSA'
|
'AQESRAoLZ3JvdXBVcGRhdGUYECABKAsyHS5FbmNyeXB0ZWRDb250ZW50Lkdyb3VwVXBkYXRlSA'
|
||||||
'5SC2dyb3VwVXBkYXRliAEBEl8KFHJlc2VuZEdyb3VwUHVibGljS2V5GBEgASgLMiYuRW5jcnlw'
|
'5SC2dyb3VwVXBkYXRliAEBEl8KFHJlc2VuZEdyb3VwUHVibGljS2V5GBEgASgLMiYuRW5jcnlw'
|
||||||
'dGVkQ29udGVudC5SZXNlbmRHcm91cFB1YmxpY0tleUgPUhRyZXNlbmRHcm91cFB1YmxpY0tleY'
|
'dGVkQ29udGVudC5SZXNlbmRHcm91cFB1YmxpY0tleUgPUhRyZXNlbmRHcm91cFB1YmxpY0tleY'
|
||||||
'gBARpRCgtHcm91cENyZWF0ZRIaCghzdGF0ZUtleRgDIAEoDFIIc3RhdGVLZXkSJgoOZ3JvdXBQ'
|
'gBARJLCg5lcnJvcl9tZXNzYWdlcxgSIAEoCzIfLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNz'
|
||||||
'dWJsaWNLZXkYBCABKAxSDmdyb3VwUHVibGljS2V5GjMKCUdyb3VwSm9pbhImCg5ncm91cFB1Ym'
|
'YWdlc0gQUg1lcnJvck1lc3NhZ2VziAEBGtcBCg1FcnJvck1lc3NhZ2VzEjgKBHR5cGUYASABKA'
|
||||||
'xpY0tleRgBIAEoDFIOZ3JvdXBQdWJsaWNLZXkaFgoUUmVzZW5kR3JvdXBQdWJsaWNLZXkatgIK'
|
'4yJC5FbmNyeXB0ZWRDb250ZW50LkVycm9yTWVzc2FnZXMuVHlwZVIEdHlwZRIsChJyZWxhdGVk'
|
||||||
'C0dyb3VwVXBkYXRlEigKD2dyb3VwQWN0aW9uVHlwZRgBIAEoCVIPZ3JvdXBBY3Rpb25UeXBlEj'
|
'X3JlY2VpcHRfaWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0SWQiXgoEVHlwZRI8CjhFUlJPUl9QUk'
|
||||||
'EKEWFmZmVjdGVkQ29udGFjdElkGAIgASgDSABSEWFmZmVjdGVkQ29udGFjdElkiAEBEicKDG5l'
|
'9DRVNTSU5HX01FU1NBR0VfQ1JFQVRFRF9BQ0NPVU5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVO'
|
||||||
'd0dyb3VwTmFtZRgDIAEoCUgBUgxuZXdHcm91cE5hbWWIAQESUwoibmV3RGVsZXRlTWVzc2FnZX'
|
'S05PV05fTUVTU0FHRV9UWVBFEAIaUQoLR3JvdXBDcmVhdGUSGgoIc3RhdGVLZXkYAyABKAxSCH'
|
||||||
'NBZnRlck1pbGxpc2Vjb25kcxgEIAEoA0gCUiJuZXdEZWxldGVNZXNzYWdlc0FmdGVyTWlsbGlz'
|
'N0YXRlS2V5EiYKDmdyb3VwUHVibGljS2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRozCglHcm91'
|
||||||
'ZWNvbmRziAEBQhQKEl9hZmZlY3RlZENvbnRhY3RJZEIPCg1fbmV3R3JvdXBOYW1lQiUKI19uZX'
|
'cEpvaW4SJgoOZ3JvdXBQdWJsaWNLZXkYASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZE'
|
||||||
'dEZWxldGVNZXNzYWdlc0FmdGVyTWlsbGlzZWNvbmRzGqkBCgtUZXh0TWVzc2FnZRIoCg9zZW5k'
|
'dyb3VwUHVibGljS2V5GrYCCgtHcm91cFVwZGF0ZRIoCg9ncm91cEFjdGlvblR5cGUYASABKAlS'
|
||||||
'ZXJNZXNzYWdlSWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBISCgR0ZXh0GAIgASgJUgR0ZXh0Eh'
|
'D2dyb3VwQWN0aW9uVHlwZRIxChFhZmZlY3RlZENvbnRhY3RJZBgCIAEoA0gAUhFhZmZlY3RlZE'
|
||||||
'wKCXRpbWVzdGFtcBgDIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2FnZUlkGAQgASgJSABS'
|
'NvbnRhY3RJZIgBARInCgxuZXdHcm91cE5hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElMK'
|
||||||
'DnF1b3RlTWVzc2FnZUlkiAEBQhEKD19xdW90ZU1lc3NhZ2VJZBpiCghSZWFjdGlvbhIoCg90YX'
|
'Im5ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaWxsaXNlY29uZHMYBCABKANIAlIibmV3RGVsZXRlTW'
|
||||||
'JnZXRNZXNzYWdlSWQYASABKAlSD3RhcmdldE1lc3NhZ2VJZBIUCgVlbW9qaRgCIAEoCVIFZW1v'
|
'Vzc2FnZXNBZnRlck1pbGxpc2Vjb25kc4gBAUIUChJfYWZmZWN0ZWRDb250YWN0SWRCDwoNX25l'
|
||||||
'amkSFgoGcmVtb3ZlGAMgASgIUgZyZW1vdmUatwIKDU1lc3NhZ2VVcGRhdGUSOAoEdHlwZRgBIA'
|
'd0dyb3VwTmFtZUIlCiNfbmV3RGVsZXRlTWVzc2FnZXNBZnRlck1pbGxpc2Vjb25kcxqpAQoLVG'
|
||||||
'EoDjIkLkVuY3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS5UeXBlUgR0eXBlEi0KD3NlbmRl'
|
'V4dE1lc3NhZ2USKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSEgoE'
|
||||||
'ck1lc3NhZ2VJZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESOgoYbXVsdGlwbGVUYXJnZX'
|
'dGV4dBgCIAEoCVIEdGV4dBIcCgl0aW1lc3RhbXAYAyABKANSCXRpbWVzdGFtcBIrCg5xdW90ZU'
|
||||||
'RNZXNzYWdlSWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZHMSFwoEdGV4dBgEIAEo'
|
'1lc3NhZ2VJZBgEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUIRCg9fcXVvdGVNZXNzYWdlSWQa'
|
||||||
'CUgBUgR0ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wIi0KBFR5cGUSCgoGRE'
|
'YgoIUmVhY3Rpb24SKAoPdGFyZ2V0TWVzc2FnZUlkGAEgASgJUg90YXJnZXRNZXNzYWdlSWQSFA'
|
||||||
'VMRVRFEAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCEgoQX3NlbmRlck1lc3NhZ2VJZEIH'
|
'oFZW1vamkYAiABKAlSBWVtb2ppEhYKBnJlbW92ZRgDIAEoCFIGcmVtb3ZlGrcCCg1NZXNzYWdl'
|
||||||
'CgVfdGV4dBqXBQoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYW'
|
'VXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50Lk1lc3NhZ2VVcGRhdGUuVH'
|
||||||
'dlSWQSMAoEdHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlwZVIEdHlwZRJD'
|
'lwZVIEdHlwZRItCg9zZW5kZXJNZXNzYWdlSWQYAiABKAlIAFIPc2VuZGVyTWVzc2FnZUlkiAEB'
|
||||||
'ChpkaXNwbGF5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TGltaXRJbk1pbG'
|
'EjoKGG11bHRpcGxlVGFyZ2V0TWVzc2FnZUlkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZXNzYW'
|
||||||
'xpc2Vjb25kc4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZyZXF1aXJlc0F1'
|
'dlSWRzEhcKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVz'
|
||||||
'dGhlbnRpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2'
|
'dGFtcCItCgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQhIKEF'
|
||||||
'FnZUlkGAYgASgJSAFSDnF1b3RlTWVzc2FnZUlkiAEBEikKDWRvd25sb2FkVG9rZW4YByABKAxI'
|
'9zZW5kZXJNZXNzYWdlSWRCBwoFX3RleHQalwUKBU1lZGlhEigKD3NlbmRlck1lc3NhZ2VJZBgB'
|
||||||
'AlINZG93bmxvYWRUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAggASgMSANSDWVuY3J5cHRpb2'
|
'IAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW50Lk'
|
||||||
'5LZXmIAQESKQoNZW5jcnlwdGlvbk1hYxgJIAEoDEgEUg1lbmNyeXB0aW9uTWFjiAEBEi0KD2Vu'
|
'1lZGlhLlR5cGVSBHR5cGUSQwoaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHMYAyABKANIAFIa'
|
||||||
'Y3J5cHRpb25Ob25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm9uY2WIAQEiPgoEVHlwZRIMCghSRV'
|
'ZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNgoWcmVxdWlyZXNBdXRoZW50aWNhdGlvbh'
|
||||||
'VQTE9BRBAAEgkKBUlNQUdFEAESCQoFVklERU8QAhIHCgNHSUYQAxIJCgVBVURJTxAEQh0KG19k'
|
'gEIAEoCFIWcmVxdWlyZXNBdXRoZW50aWNhdGlvbhIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVz'
|
||||||
'aXNwbGF5TGltaXRJbk1pbGxpc2Vjb25kc0IRCg9fcXVvdGVNZXNzYWdlSWRCEAoOX2Rvd25sb2'
|
'dGFtcBIrCg5xdW90ZU1lc3NhZ2VJZBgGIAEoCUgBUg5xdW90ZU1lc3NhZ2VJZIgBARIpCg1kb3'
|
||||||
'FkVG9rZW5CEAoOX2VuY3J5cHRpb25LZXlCEAoOX2VuY3J5cHRpb25NYWNCEgoQX2VuY3J5cHRp'
|
'dubG9hZFRva2VuGAcgASgMSAJSDWRvd25sb2FkVG9rZW6IAQESKQoNZW5jcnlwdGlvbktleRgI'
|
||||||
'b25Ob25jZRqnAQoLTWVkaWFVcGRhdGUSNgoEdHlwZRgBIAEoDjIiLkVuY3J5cHRlZENvbnRlbn'
|
'IAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEikKDWVuY3J5cHRpb25NYWMYCSABKAxIBFINZW5jcn'
|
||||||
'QuTWVkaWFVcGRhdGUuVHlwZVIEdHlwZRIoCg90YXJnZXRNZXNzYWdlSWQYAiABKAlSD3Rhcmdl'
|
'lwdGlvbk1hY4gBARItCg9lbmNyeXB0aW9uTm9uY2UYCiABKAxIBVIPZW5jcnlwdGlvbk5vbmNl'
|
||||||
'dE1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEAASCgoGU1RPUkVEEAESFAoQREVDUllQVE'
|
'iAEBIj4KBFR5cGUSDAoIUkVVUExPQUQQABIJCgVJTUFHRRABEgkKBVZJREVPEAISBwoDR0lGEA'
|
||||||
'lPTl9FUlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkKBHR5cGUYASABKA4yJS5FbmNyeXB0ZWRD'
|
'MSCQoFQVVESU8QBEIdChtfZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHNCEQoPX3F1b3RlTWVz'
|
||||||
'b250ZW50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cGUiKwoEVHlwZRILCgdSRVFVRVNUEAASCg'
|
'c2FnZUlkQhAKDl9kb3dubG9hZFRva2VuQhAKDl9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW'
|
||||||
'oGUkVKRUNUEAESCgoGQUNDRVBUEAIangIKDUNvbnRhY3RVcGRhdGUSOAoEdHlwZRgBIAEoDjIk'
|
'9uTWFjQhIKEF9lbmNyeXB0aW9uTm9uY2UapwEKC01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4y'
|
||||||
'LkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS5UeXBlUgR0eXBlEjUKE2F2YXRhclN2Z0'
|
'Ii5FbmNyeXB0ZWRDb250ZW50Lk1lZGlhVXBkYXRlLlR5cGVSBHR5cGUSKAoPdGFyZ2V0TWVzc2'
|
||||||
'NvbXByZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZIgBARIfCgh1c2VybmFtZRgD'
|
'FnZUlkGAIgASgJUg90YXJnZXRNZXNzYWdlSWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNU'
|
||||||
'IAEoCUgBUgh1c2VybmFtZYgBARIlCgtkaXNwbGF5TmFtZRgEIAEoCUgCUgtkaXNwbGF5TmFtZY'
|
'T1JFRBABEhQKEERFQ1JZUFRJT05fRVJST1IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGA'
|
||||||
'gBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIWChRfYXZhdGFyU3ZnQ29tcHJl'
|
'EgASgOMiUuRW5jcnlwdGVkQ29udGVudC5Db250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5'
|
||||||
'c3NlZEILCglfdXNlcm5hbWVCDgoMX2Rpc3BsYXlOYW1lGtUBCghQdXNoS2V5cxIzCgR0eXBlGA'
|
'cGUSCwoHUkVRVUVTVBAAEgoKBlJFSkVDVBABEgoKBkFDQ0VQVBACGp4CCg1Db250YWN0VXBkYX'
|
||||||
'EgASgOMh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGAIg'
|
'RlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIE'
|
||||||
'ASgDSABSBWtleUlkiAEBEhUKA2tleRgDIAEoDEgBUgNrZXmIAQESIQoJY3JlYXRlZEF0GAQgAS'
|
'dHlwZRI1ChNhdmF0YXJTdmdDb21wcmVzc2VkGAIgASgMSABSE2F2YXRhclN2Z0NvbXByZXNzZW'
|
||||||
'gDSAJSCWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIICgZf'
|
'SIAQESHwoIdXNlcm5hbWUYAyABKAlIAVIIdXNlcm5hbWWIAQESJQoLZGlzcGxheU5hbWUYBCAB'
|
||||||
'a2V5SWRCBgoEX2tleUIMCgpfY3JlYXRlZEF0GqkBCglGbGFtZVN5bmMSIgoMZmxhbWVDb3VudG'
|
'KAlIAlILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCFg'
|
||||||
'VyGAEgASgDUgxmbGFtZUNvdW50ZXISNgoWbGFzdEZsYW1lQ291bnRlckNoYW5nZRgCIAEoA1IW'
|
'oUX2F2YXRhclN2Z0NvbXByZXNzZWRCCwoJX3VzZXJuYW1lQg4KDF9kaXNwbGF5TmFtZRrVAQoI'
|
||||||
'bGFzdEZsYW1lQ291bnRlckNoYW5nZRIeCgpiZXN0RnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEi'
|
'UHVzaEtleXMSMwoEdHlwZRgBIAEoDjIfLkVuY3J5cHRlZENvbnRlbnQuUHVzaEtleXMuVHlwZV'
|
||||||
'AKC2ZvcmNlVXBkYXRlGAQgASgIUgtmb3JjZVVwZGF0ZUIKCghfZ3JvdXBJZEIPCg1faXNEaXJl'
|
'IEdHlwZRIZCgVrZXlJZBgCIAEoA0gAUgVrZXlJZIgBARIVCgNrZXkYAyABKAxIAVIDa2V5iAEB'
|
||||||
'Y3RDaGF0QhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIQCg5fbWVzc2FnZVVwZGF0ZUIICgZfbW'
|
'EiEKCWNyZWF0ZWRBdBgEIAEoA0gCUgljcmVhdGVkQXSIAQEiHwoEVHlwZRILCgdSRVFVRVNUEA'
|
||||||
'VkaWFCDgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YWN0UmVxdWVz'
|
'ASCgoGVVBEQVRFEAFCCAoGX2tleUlkQgYKBF9rZXlCDAoKX2NyZWF0ZWRBdBqpAQoJRmxhbWVT'
|
||||||
'dEIMCgpfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb25CDgoMX3RleHRNZXNzYW'
|
'eW5jEiIKDGZsYW1lQ291bnRlchgBIAEoA1IMZmxhbWVDb3VudGVyEjYKFmxhc3RGbGFtZUNvdW'
|
||||||
'dlQg4KDF9ncm91cENyZWF0ZUIMCgpfZ3JvdXBKb2luQg4KDF9ncm91cFVwZGF0ZUIXChVfcmVz'
|
'50ZXJDaGFuZ2UYAiABKANSFmxhc3RGbGFtZUNvdW50ZXJDaGFuZ2USHgoKYmVzdEZyaWVuZBgD'
|
||||||
'ZW5kR3JvdXBQdWJsaWNLZXk=');
|
'IAEoCFIKYmVzdEZyaWVuZBIgCgtmb3JjZVVwZGF0ZRgEIAEoCFILZm9yY2VVcGRhdGVCCgoIX2'
|
||||||
|
'dyb3VwSWRCDwoNX2lzRGlyZWN0Q2hhdEIXChVfc2VuZGVyUHJvZmlsZUNvdW50ZXJCEAoOX21l'
|
||||||
|
'c3NhZ2VVcGRhdGVCCAoGX21lZGlhQg4KDF9tZWRpYVVwZGF0ZUIQCg5fY29udGFjdFVwZGF0ZU'
|
||||||
|
'IRCg9fY29udGFjdFJlcXVlc3RCDAoKX2ZsYW1lU3luY0ILCglfcHVzaEtleXNCCwoJX3JlYWN0'
|
||||||
|
'aW9uQg4KDF90ZXh0TWVzc2FnZUIOCgxfZ3JvdXBDcmVhdGVCDAoKX2dyb3VwSm9pbkIOCgxfZ3'
|
||||||
|
'JvdXBVcGRhdGVCFwoVX3Jlc2VuZEdyb3VwUHVibGljS2V5QhEKD19lcnJvcl9tZXNzYWdlcw==');
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,17 @@ message EncryptedContent {
|
||||||
optional GroupJoin groupJoin = 15;
|
optional GroupJoin groupJoin = 15;
|
||||||
optional GroupUpdate groupUpdate = 16;
|
optional GroupUpdate groupUpdate = 16;
|
||||||
optional ResendGroupPublicKey resendGroupPublicKey = 17;
|
optional ResendGroupPublicKey resendGroupPublicKey = 17;
|
||||||
|
optional ErrorMessages error_messages = 18;
|
||||||
|
|
||||||
|
|
||||||
|
message ErrorMessages {
|
||||||
|
enum Type {
|
||||||
|
ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD = 0;
|
||||||
|
UNKNOWN_MESSAGE_TYPE = 2;
|
||||||
|
}
|
||||||
|
Type type = 1;
|
||||||
|
string related_receipt_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
message GroupCreate {
|
message GroupCreate {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,43 @@ import 'package:twonly/src/utils/avatars.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';
|
||||||
|
|
||||||
|
Future<bool> handleNewContactRequest(int fromUserId) async {
|
||||||
|
final contact = await twonlyDB.contactsDao
|
||||||
|
.getContactByUserId(fromUserId)
|
||||||
|
.getSingleOrNull();
|
||||||
|
if (contact != null) {
|
||||||
|
if (contact.accepted) {
|
||||||
|
// contact was already accepted, so just accept the request in the background.
|
||||||
|
await sendCipherText(
|
||||||
|
contact.userId,
|
||||||
|
EncryptedContent(
|
||||||
|
contactRequest: EncryptedContent_ContactRequest(
|
||||||
|
type: EncryptedContent_ContactRequest_Type.ACCEPT,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Request the username by the server so an attacker can not
|
||||||
|
// forge the displayed username in the contact request
|
||||||
|
final user = await apiService.getUserById(fromUserId);
|
||||||
|
if (user == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
await twonlyDB.contactsDao.insertOnConflictUpdate(
|
||||||
|
ContactsCompanion(
|
||||||
|
username: Value(utf8.decode(user.username)),
|
||||||
|
userId: Value(fromUserId),
|
||||||
|
requested: const Value(true),
|
||||||
|
deletedByUser: const Value(false),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await setupNotificationWithUsers();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> handleContactRequest(
|
Future<bool> handleContactRequest(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
EncryptedContent_ContactRequest contactRequest,
|
EncryptedContent_ContactRequest contactRequest,
|
||||||
|
|
@ -20,38 +57,7 @@ Future<bool> handleContactRequest(
|
||||||
switch (contactRequest.type) {
|
switch (contactRequest.type) {
|
||||||
case EncryptedContent_ContactRequest_Type.REQUEST:
|
case EncryptedContent_ContactRequest_Type.REQUEST:
|
||||||
Log.info('Got a contact request from $fromUserId');
|
Log.info('Got a contact request from $fromUserId');
|
||||||
final contact = await twonlyDB.contactsDao
|
return handleNewContactRequest(fromUserId);
|
||||||
.getContactByUserId(fromUserId)
|
|
||||||
.getSingleOrNull();
|
|
||||||
if (contact != null) {
|
|
||||||
if (contact.accepted) {
|
|
||||||
// contact was already accepted, so just accept the request in the background.
|
|
||||||
await sendCipherText(
|
|
||||||
contact.userId,
|
|
||||||
EncryptedContent(
|
|
||||||
contactRequest: EncryptedContent_ContactRequest(
|
|
||||||
type: EncryptedContent_ContactRequest_Type.ACCEPT,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Request the username by the server so an attacker can not
|
|
||||||
// forge the displayed username in the contact request
|
|
||||||
final user = await apiService.getUserById(fromUserId);
|
|
||||||
if (user == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
await twonlyDB.contactsDao.insertOnConflictUpdate(
|
|
||||||
ContactsCompanion(
|
|
||||||
username: Value(utf8.decode(user.username)),
|
|
||||||
userId: Value(fromUserId),
|
|
||||||
requested: const Value(true),
|
|
||||||
deletedByUser: const Value(false),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await setupNotificationWithUsers();
|
|
||||||
case EncryptedContent_ContactRequest_Type.ACCEPT:
|
case EncryptedContent_ContactRequest_Type.ACCEPT:
|
||||||
Log.info('Got a contact accept from $fromUserId');
|
Log.info('Got a contact accept from $fromUserId');
|
||||||
await twonlyDB.contactsDao.updateContact(
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
|
|
||||||
29
lib/src/services/api/client2client/errors.c2c.dart
Normal file
29
lib/src/services/api/client2client/errors.c2c.dart
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import 'package:clock/clock.dart';
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart';
|
||||||
|
|
||||||
|
Future<void> handleErrorMessage(
|
||||||
|
int fromUserId,
|
||||||
|
EncryptedContent_ErrorMessages error,
|
||||||
|
) async {
|
||||||
|
switch (error.type) {
|
||||||
|
case EncryptedContent_ErrorMessages_Type.UNKNOWN_MESSAGE_TYPE:
|
||||||
|
break;
|
||||||
|
case EncryptedContent_ErrorMessages_Type
|
||||||
|
.ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD:
|
||||||
|
await twonlyDB.receiptsDao.updateReceiptWidthUserId(
|
||||||
|
fromUserId,
|
||||||
|
error.relatedReceiptId,
|
||||||
|
ReceiptsCompanion(markForRetryAfterAccepted: Value(clock.now())),
|
||||||
|
);
|
||||||
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
fromUserId,
|
||||||
|
const ContactsCompanion(
|
||||||
|
accepted: Value(false),
|
||||||
|
requested: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,29 @@ Future<void> tryTransmitMessages() async {
|
||||||
|
|
||||||
Log.info('Reuploading ${receipts.length} messages to the server.');
|
Log.info('Reuploading ${receipts.length} messages to the server.');
|
||||||
|
|
||||||
|
final contacts = <int, Contact>{};
|
||||||
|
|
||||||
for (final receipt in receipts) {
|
for (final receipt in receipts) {
|
||||||
|
if (receipt.markForRetryAfterAccepted != null) {
|
||||||
|
if (!contacts.containsKey(receipt.contactId)) {
|
||||||
|
final contact = await twonlyDB.contactsDao
|
||||||
|
.getContactByUserId(receipt.contactId)
|
||||||
|
.getSingleOrNull();
|
||||||
|
if (contact == null) {
|
||||||
|
Log.error(
|
||||||
|
'Contact does not exists, but has a record in receipts, this should not be possible, because of the DELETE CASCADE relation.',
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
contacts[receipt.contactId] = contact;
|
||||||
|
}
|
||||||
|
if (!(contacts[receipt.contactId]?.accepted ?? true)) {
|
||||||
|
Log.warn(
|
||||||
|
'Could not send message as contact has still not yet accepted.',
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
await tryToSendCompleteMessage(receipt: receipt);
|
await tryToSendCompleteMessage(receipt: receipt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
import 'package:clock/clock.dart';
|
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';
|
||||||
|
|
@ -15,6 +14,7 @@ import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pbserver.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pbserver.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/contact.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/contact.c2c.dart';
|
||||||
|
import 'package:twonly/src/services/api/client2client/errors.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/groups.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/groups.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/media.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/media.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/messages.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/messages.c2c.dart';
|
||||||
|
|
@ -23,6 +23,7 @@ import 'package:twonly/src/services/api/client2client/pushkeys.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/reaction.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/reaction.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/client2client/text_message.c2c.dart';
|
import 'package:twonly/src/services/api/client2client/text_message.c2c.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
|
import 'package:twonly/src/services/group.services.dart';
|
||||||
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
@ -117,47 +118,60 @@ Future<void> handleClient2ClientMessage(NewMessage newMessage) async {
|
||||||
case Message_Type.CIPHERTEXT:
|
case Message_Type.CIPHERTEXT:
|
||||||
case Message_Type.PREKEY_BUNDLE:
|
case Message_Type.PREKEY_BUNDLE:
|
||||||
if (message.hasEncryptedContent()) {
|
if (message.hasEncryptedContent()) {
|
||||||
|
Value<String>? receiptIdDB;
|
||||||
|
|
||||||
final encryptedContentRaw =
|
final encryptedContentRaw =
|
||||||
Uint8List.fromList(message.encryptedContent);
|
Uint8List.fromList(message.encryptedContent);
|
||||||
|
|
||||||
if (await twonlyDB.contactsDao
|
Message? response;
|
||||||
.getContactByUserId(fromUserId)
|
|
||||||
.getSingleOrNull() ==
|
|
||||||
null) {
|
|
||||||
final user = await apiService.getUserById(fromUserId);
|
|
||||||
|
|
||||||
/// In case the user does not exists, just create a dummy user which was deleted by the user, so the message
|
final user = await twonlyDB.contactsDao
|
||||||
/// can be inserted into the receipts database
|
.getContactByUserId(fromUserId)
|
||||||
await twonlyDB.contactsDao.insertContact(
|
.getSingleOrNull();
|
||||||
ContactsCompanion(
|
|
||||||
userId: Value(fromUserId),
|
if (user == null) {
|
||||||
deletedByUser: const Value(true),
|
if (!await addNewHiddenContact(fromUserId)) {
|
||||||
username: Value(
|
// in case the user could not be added, send a retry error message as this error should only happen in case
|
||||||
user == null ? '[Unknown]' : utf8.decode(user.username),
|
// it was not possible to load the user from the server
|
||||||
|
response = Message(
|
||||||
|
receiptId: receiptId,
|
||||||
|
type: Message_Type.PLAINTEXT_CONTENT,
|
||||||
|
plaintextContent: PlaintextContent(
|
||||||
|
retryControlError: PlaintextContent_RetryErrorMessage(),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final responsePlaintextContent = await handleEncryptedMessage(
|
if (response == null) {
|
||||||
fromUserId,
|
final (encryptedContent, plainTextContent) =
|
||||||
encryptedContentRaw,
|
await handleEncryptedMessage(
|
||||||
message.type,
|
fromUserId,
|
||||||
);
|
encryptedContentRaw,
|
||||||
Message response;
|
message.type,
|
||||||
if (responsePlaintextContent != null) {
|
receiptId,
|
||||||
response = Message()
|
);
|
||||||
..receiptId = receiptId
|
if (plainTextContent != null) {
|
||||||
..type = Message_Type.PLAINTEXT_CONTENT
|
response = Message(
|
||||||
..plaintextContent = responsePlaintextContent;
|
receiptId: receiptId,
|
||||||
} else {
|
type: Message_Type.PLAINTEXT_CONTENT,
|
||||||
response = Message()..type = Message_Type.SENDER_DELIVERY_RECEIPT;
|
plaintextContent: plainTextContent,
|
||||||
|
);
|
||||||
|
} else if (encryptedContent != null) {
|
||||||
|
response = Message(
|
||||||
|
type: Message_Type.CIPHERTEXT,
|
||||||
|
encryptedContent: encryptedContent.writeToBuffer(),
|
||||||
|
);
|
||||||
|
receiptIdDB = const Value.absent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response ??= Message(type: Message_Type.SENDER_DELIVERY_RECEIPT);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await twonlyDB.receiptsDao.insertReceipt(
|
await twonlyDB.receiptsDao.insertReceipt(
|
||||||
ReceiptsCompanion(
|
ReceiptsCompanion(
|
||||||
receiptId: Value(receiptId),
|
receiptId: receiptIdDB ?? Value(receiptId),
|
||||||
contactId: Value(fromUserId),
|
contactId: Value(fromUserId),
|
||||||
message: Value(response.writeToBuffer()),
|
message: Value(response.writeToBuffer()),
|
||||||
contactWillSendsReceipt: const Value(false),
|
contactWillSendsReceipt: const Value(false),
|
||||||
|
|
@ -173,10 +187,11 @@ Future<void> handleClient2ClientMessage(NewMessage newMessage) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PlaintextContent?> handleEncryptedMessage(
|
Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
Uint8List encryptedContentRaw,
|
Uint8List encryptedContentRaw,
|
||||||
Message_Type messageType,
|
Message_Type messageType,
|
||||||
|
String receiptId,
|
||||||
) async {
|
) async {
|
||||||
final (content, decryptionErrorType) = await signalDecryptMessage(
|
final (content, decryptionErrorType) = await signalDecryptMessage(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
|
|
@ -185,9 +200,12 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
return PlaintextContent()
|
return (
|
||||||
..decryptionErrorMessage = (PlaintextContent_DecryptionErrorMessage()
|
null,
|
||||||
..type = decryptionErrorType!);
|
PlaintextContent()
|
||||||
|
..decryptionErrorMessage = (PlaintextContent_DecryptionErrorMessage()
|
||||||
|
..type = decryptionErrorType!)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We got a valid message fromUserId, so mark all messages which where
|
// We got a valid message fromUserId, so mark all messages which where
|
||||||
|
|
@ -203,10 +221,21 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
|
|
||||||
if (content.hasContactRequest()) {
|
if (content.hasContactRequest()) {
|
||||||
if (!await handleContactRequest(fromUserId, content.contactRequest)) {
|
if (!await handleContactRequest(fromUserId, content.contactRequest)) {
|
||||||
return PlaintextContent()
|
return (
|
||||||
..retryControlError = PlaintextContent_RetryErrorMessage();
|
null,
|
||||||
|
PlaintextContent()
|
||||||
|
..retryControlError = PlaintextContent_RetryErrorMessage()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.hasErrorMessages()) {
|
||||||
|
await handleErrorMessage(
|
||||||
|
fromUserId,
|
||||||
|
content.errorMessages,
|
||||||
|
);
|
||||||
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasContactUpdate()) {
|
if (content.hasContactUpdate()) {
|
||||||
|
|
@ -215,17 +244,17 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
content.contactUpdate,
|
content.contactUpdate,
|
||||||
senderProfileCounter,
|
senderProfileCounter,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasFlameSync()) {
|
if (content.hasFlameSync()) {
|
||||||
await handleFlameSync(fromUserId, content.flameSync);
|
await handleFlameSync(fromUserId, content.flameSync);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasPushKeys()) {
|
if (content.hasPushKeys()) {
|
||||||
await handlePushKey(fromUserId, content.pushKeys);
|
await handlePushKey(fromUserId, content.pushKeys);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasMessageUpdate()) {
|
if (content.hasMessageUpdate()) {
|
||||||
|
|
@ -233,7 +262,7 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
content.messageUpdate,
|
content.messageUpdate,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasMediaUpdate()) {
|
if (content.hasMediaUpdate()) {
|
||||||
|
|
@ -241,12 +270,12 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
content.mediaUpdate,
|
content.mediaUpdate,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!content.hasGroupId()) {
|
if (!content.hasGroupId()) {
|
||||||
Log.error('Messages should have a groupId $fromUserId.');
|
Log.error('Messages should have a groupId $fromUserId.');
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasGroupCreate()) {
|
if (content.hasGroupCreate()) {
|
||||||
|
|
@ -255,7 +284,7 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
content.groupId,
|
content.groupId,
|
||||||
content.groupCreate,
|
content.groupCreate,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that the user is (still) in that group...
|
/// Verify that the user is (still) in that group...
|
||||||
|
|
@ -265,10 +294,20 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
.getContactByUserId(fromUserId)
|
.getContactByUserId(fromUserId)
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
if (contact == null || contact.deletedByUser) {
|
if (contact == null || contact.deletedByUser) {
|
||||||
|
await handleNewContactRequest(fromUserId);
|
||||||
Log.error(
|
Log.error(
|
||||||
'User tries to send message to direct chat while the user does not exists !',
|
'User tries to send message to direct chat while the user does not exists !',
|
||||||
);
|
);
|
||||||
return null;
|
return (
|
||||||
|
EncryptedContent(
|
||||||
|
errorMessages: EncryptedContent_ErrorMessages(
|
||||||
|
type: EncryptedContent_ErrorMessages_Type
|
||||||
|
.ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD,
|
||||||
|
relatedReceiptId: receiptId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Log.info(
|
Log.info(
|
||||||
'Creating new DirectChat between two users',
|
'Creating new DirectChat between two users',
|
||||||
|
|
@ -285,12 +324,15 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
'Got group join message, but group does not exists yet, retry later. As probably the GroupCreate was not yet received.',
|
'Got group join message, but group does not exists yet, retry later. As probably the GroupCreate was not yet received.',
|
||||||
);
|
);
|
||||||
// In case the group join was received before the GroupCreate the sender should send it later again.
|
// In case the group join was received before the GroupCreate the sender should send it later again.
|
||||||
return PlaintextContent()
|
return (
|
||||||
..retryControlError = PlaintextContent_RetryErrorMessage();
|
null,
|
||||||
|
PlaintextContent()
|
||||||
|
..retryControlError = PlaintextContent_RetryErrorMessage()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.error('User $fromUserId tried to access group ${content.groupId}.');
|
Log.error('User $fromUserId tried to access group ${content.groupId}.');
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,7 +342,7 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
content.groupId,
|
content.groupId,
|
||||||
content.groupUpdate,
|
content.groupUpdate,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasGroupJoin()) {
|
if (content.hasGroupJoin()) {
|
||||||
|
|
@ -309,10 +351,13 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
content.groupId,
|
content.groupId,
|
||||||
content.groupJoin,
|
content.groupJoin,
|
||||||
)) {
|
)) {
|
||||||
return PlaintextContent()
|
return (
|
||||||
..retryControlError = PlaintextContent_RetryErrorMessage();
|
null,
|
||||||
|
PlaintextContent()
|
||||||
|
..retryControlError = PlaintextContent_RetryErrorMessage()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasResendGroupPublicKey()) {
|
if (content.hasResendGroupPublicKey()) {
|
||||||
|
|
@ -321,7 +366,7 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
content.groupId,
|
content.groupId,
|
||||||
content.groupJoin,
|
content.groupJoin,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasTextMessage()) {
|
if (content.hasTextMessage()) {
|
||||||
|
|
@ -330,7 +375,7 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
content.groupId,
|
content.groupId,
|
||||||
content.textMessage,
|
content.textMessage,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasReaction()) {
|
if (content.hasReaction()) {
|
||||||
|
|
@ -339,7 +384,7 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
content.groupId,
|
content.groupId,
|
||||||
content.reaction,
|
content.reaction,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.hasMedia()) {
|
if (content.hasMedia()) {
|
||||||
|
|
@ -348,8 +393,8 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
||||||
content.groupId,
|
content.groupId,
|
||||||
content.media,
|
content.media,
|
||||||
);
|
);
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ class MediaFileService {
|
||||||
} else if (service.mediaFile.requiresAuthentication ||
|
} else if (service.mediaFile.requiresAuthentication ||
|
||||||
service.mediaFile.displayLimitInMilliseconds != null) {
|
service.mediaFile.displayLimitInMilliseconds != null) {
|
||||||
// Message was opened by all persons, and they can not reopen the image.
|
// Message was opened by all persons, and they can not reopen the image.
|
||||||
|
// This branch will prevent to reach the next if condition, with would otherwise store the image for two days
|
||||||
// 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!
|
||||||
|
|
|
||||||
|
|
@ -436,7 +436,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
CameraLensDirection.front;
|
CameraLensDirection.front;
|
||||||
|
|
||||||
Future<void> onPanUpdate(dynamic details) async {
|
Future<void> onPanUpdate(dynamic details) async {
|
||||||
if (isFront || details == null) {
|
if (details == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mc.cameraController == null ||
|
if (mc.cameraController == null ||
|
||||||
|
|
@ -603,9 +603,6 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
bottomNavigation: Container(),
|
bottomNavigation: Container(),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onPanStart: (details) async {
|
onPanStart: (details) async {
|
||||||
if (isFront) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_basePanY = details.localPosition.dy;
|
_basePanY = details.localPosition.dy;
|
||||||
_baseScaleFactor = mc.selectedCameraDetails.scaleFactor;
|
_baseScaleFactor = mc.selectedCameraDetails.scaleFactor;
|
||||||
|
|
@ -721,12 +718,11 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
children: [
|
children: [
|
||||||
if (mc.cameraController!.value.isInitialized &&
|
if (mc.cameraController!.value.isInitialized &&
|
||||||
mc.selectedCameraDetails.isZoomAble &&
|
mc.selectedCameraDetails.isZoomAble &&
|
||||||
!isFront &&
|
|
||||||
!_isVideoRecording)
|
!_isVideoRecording)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 120,
|
width: 120,
|
||||||
child: CameraZoomButtons(
|
child: CameraZoomButtons(
|
||||||
key: widget.key,
|
key: mc.zoomButtonKey,
|
||||||
scaleFactor: mc.selectedCameraDetails.scaleFactor,
|
scaleFactor: mc.selectedCameraDetails.scaleFactor,
|
||||||
updateScaleFactor: updateScaleFactor,
|
updateScaleFactor: updateScaleFactor,
|
||||||
selectCamera: mc.selectCamera,
|
selectCamera: mc.selectCamera,
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ class MainCameraController {
|
||||||
Map<int, ScannedVerifiedContact> contactsVerified = {};
|
Map<int, ScannedVerifiedContact> contactsVerified = {};
|
||||||
Map<int, ScannedNewProfile> scannedNewProfiles = {};
|
Map<int, ScannedNewProfile> scannedNewProfiles = {};
|
||||||
String? scannedUrl;
|
String? scannedUrl;
|
||||||
|
GlobalKey zoomButtonKey = GlobalKey();
|
||||||
|
|
||||||
Future<void> closeCamera() async {
|
Future<void> closeCamera() async {
|
||||||
contactsVerified = {};
|
contactsVerified = {};
|
||||||
|
|
@ -76,6 +77,7 @@ class MainCameraController {
|
||||||
CameraLensDirection.back) {
|
CameraLensDirection.back) {
|
||||||
await cameraController?.startImageStream(_processCameraImage);
|
await cameraController?.startImageStream(_processCameraImage);
|
||||||
}
|
}
|
||||||
|
zoomButtonKey = GlobalKey();
|
||||||
setState();
|
setState();
|
||||||
return cameraController;
|
return cameraController;
|
||||||
}
|
}
|
||||||
|
|
@ -89,10 +91,11 @@ class MainCameraController {
|
||||||
try {
|
try {
|
||||||
await cameraController!.stopImageStream();
|
await cameraController!.stopImageStream();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.warn(e);
|
// Log.warn(e);
|
||||||
}
|
}
|
||||||
await cameraController!.dispose();
|
final tmp = cameraController;
|
||||||
cameraController = null;
|
cameraController = null;
|
||||||
|
await tmp!.dispose();
|
||||||
await selectCamera((selectedCameraDetails.cameraId + 1) % 2, false);
|
await selectCamera((selectedCameraDetails.cameraId + 1) % 2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:clock/clock.dart';
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
class SaveToGalleryButton extends StatefulWidget {
|
class SaveToGalleryButton extends StatefulWidget {
|
||||||
const SaveToGalleryButton({
|
const SaveToGalleryButton({
|
||||||
required this.storeImageAsOriginal,
|
|
||||||
required this.isLoading,
|
required this.isLoading,
|
||||||
required this.displayButtonLabel,
|
required this.displayButtonLabel,
|
||||||
required this.mediaService,
|
required this.mediaService,
|
||||||
|
this.storeImageAsOriginal,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
final Future<Uint8List?> Function() storeImageAsOriginal;
|
final Future<Uint8List?> Function()? storeImageAsOriginal;
|
||||||
final bool displayButtonLabel;
|
final bool displayButtonLabel;
|
||||||
final MediaFileService mediaService;
|
final MediaFileService mediaService;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
|
|
@ -44,8 +48,32 @@ class SaveToGalleryButtonState extends State<SaveToGalleryButton> {
|
||||||
_imageSaving = true;
|
_imageSaving = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
await widget.storeImageAsOriginal();
|
if (widget.storeImageAsOriginal != null) {
|
||||||
await widget.mediaService.storeMediaFile();
|
await widget.storeImageAsOriginal!();
|
||||||
|
}
|
||||||
|
|
||||||
|
final newMediaFile = await twonlyDB.mediaFilesDao.insertMedia(
|
||||||
|
MediaFilesCompanion(
|
||||||
|
type: Value(widget.mediaService.mediaFile.type),
|
||||||
|
createdAt: Value(clock.now()),
|
||||||
|
stored: const Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newMediaFile != null) {
|
||||||
|
final newService = MediaFileService(newMediaFile);
|
||||||
|
|
||||||
|
if (widget.mediaService.tempPath.existsSync()) {
|
||||||
|
widget.mediaService.tempPath.copySync(
|
||||||
|
newService.tempPath.path,
|
||||||
|
);
|
||||||
|
} else if (widget.mediaService.originalPath.existsSync()) {
|
||||||
|
widget.mediaService.originalPath.copySync(
|
||||||
|
newService.originalPath.path,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await newService.storeMediaFile();
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_imageSaved = true;
|
_imageSaved = true;
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,16 @@ class _CameraZoomButtonsState extends State<CameraZoomButtons> {
|
||||||
_wideCameraIndex = index;
|
_wideCameraIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!showWideAngleZoom && Platform.isIOS && _wideCameraIndex != null) {
|
final isFront = widget.controller.description.lensDirection ==
|
||||||
|
CameraLensDirection.front;
|
||||||
|
|
||||||
|
if (!showWideAngleZoom &&
|
||||||
|
Platform.isIOS &&
|
||||||
|
_wideCameraIndex != null &&
|
||||||
|
!isFront) {
|
||||||
showWideAngleZoomIOS = true;
|
showWideAngleZoomIOS = true;
|
||||||
|
} else {
|
||||||
|
showWideAngleZoomIOS = false;
|
||||||
}
|
}
|
||||||
if (_isDisposed) return;
|
if (_isDisposed) return;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'package:clock/clock.dart';
|
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
@ -487,20 +486,6 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case the image was already stored, then rename the stored image.
|
|
||||||
if (mediaService.storedPath.existsSync()) {
|
|
||||||
final mediaFile = await twonlyDB.mediaFilesDao.insertMedia(
|
|
||||||
MediaFilesCompanion(
|
|
||||||
type: Value(mediaService.mediaFile.type),
|
|
||||||
createdAt: Value(clock.now()),
|
|
||||||
stored: const Value(true),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (mediaFile != null) {
|
|
||||||
mediaService.storedPath
|
|
||||||
.renameSync(MediaFileService(mediaFile).storedPath.path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,7 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
||||||
group: widget.group,
|
group: widget.group,
|
||||||
onResponseTriggered: widget.onResponseTriggered!,
|
onResponseTriggered: widget.onResponseTriggered!,
|
||||||
galleryItems: widget.galleryItems,
|
galleryItems: widget.galleryItems,
|
||||||
|
mediaFileService: mediaService,
|
||||||
child: Container(
|
child: Container(
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,14 @@ import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart'
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dart'
|
||||||
as pb;
|
as pb;
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
||||||
import 'package:twonly/src/views/chats/message_info.view.dart';
|
import 'package:twonly/src/views/chats/message_info.view.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/components/context_menu.component.dart';
|
import 'package:twonly/src/views/components/context_menu.component.dart';
|
||||||
|
import 'package:twonly/src/views/memories/memories_photo_slider.view.dart';
|
||||||
|
|
||||||
class MessageContextMenu extends StatelessWidget {
|
class MessageContextMenu extends StatelessWidget {
|
||||||
const MessageContextMenu({
|
const MessageContextMenu({
|
||||||
|
|
@ -26,16 +28,55 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.onResponseTriggered,
|
required this.onResponseTriggered,
|
||||||
required this.galleryItems,
|
required this.galleryItems,
|
||||||
|
required this.mediaFileService,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
final Group group;
|
final Group group;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Message message;
|
final Message message;
|
||||||
final List<MemoryItem> galleryItems;
|
final List<MemoryItem> galleryItems;
|
||||||
|
final MediaFileService? mediaFileService;
|
||||||
final VoidCallback onResponseTriggered;
|
final VoidCallback onResponseTriggered;
|
||||||
|
|
||||||
|
Future<void> reopenMediaFile(BuildContext context) async {
|
||||||
|
final isAuth = await authenticateUser(
|
||||||
|
context.lang.authRequestReopenImage,
|
||||||
|
force: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isAuth && context.mounted && mediaFileService != null) {
|
||||||
|
final galleryItems = [
|
||||||
|
MemoryItem(mediaService: mediaFileService!, messages: []),
|
||||||
|
];
|
||||||
|
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
PageRouteBuilder(
|
||||||
|
opaque: false,
|
||||||
|
pageBuilder: (context, a1, a2) => MemoriesPhotoSliderView(
|
||||||
|
galleryItems: galleryItems,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var canBeOpenedAgain = false;
|
||||||
|
// in case this is a media send from this user...
|
||||||
|
if (mediaFileService != null && message.senderId == null) {
|
||||||
|
// and the media was send with unlimited display limit time and without auth required...
|
||||||
|
if (!mediaFileService!.mediaFile.requiresAuthentication &&
|
||||||
|
mediaFileService!.mediaFile.displayLimitInMilliseconds == null) {
|
||||||
|
// and the temp media file still exists
|
||||||
|
if (mediaFileService!.tempPath.existsSync()) {
|
||||||
|
// the media file can be opened again...
|
||||||
|
canBeOpenedAgain = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ContextMenu(
|
return ContextMenu(
|
||||||
items: [
|
items: [
|
||||||
if (!message.isDeletedFromSender)
|
if (!message.isDeletedFromSender)
|
||||||
|
|
@ -70,6 +111,12 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
},
|
},
|
||||||
icon: FontAwesomeIcons.faceLaugh,
|
icon: FontAwesomeIcons.faceLaugh,
|
||||||
),
|
),
|
||||||
|
if (canBeOpenedAgain)
|
||||||
|
ContextMenuItem(
|
||||||
|
title: context.lang.contextMenuViewAgain,
|
||||||
|
onTap: () => reopenMediaFile(context),
|
||||||
|
icon: FontAwesomeIcons.clockRotateLeft,
|
||||||
|
),
|
||||||
if (!message.isDeletedFromSender)
|
if (!message.isDeletedFromSender)
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
title: context.lang.reply,
|
title: context.lang.reply,
|
||||||
|
|
|
||||||
|
|
@ -83,13 +83,16 @@ class GroupContextMenu extends StatelessWidget {
|
||||||
);
|
);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
await twonlyDB.messagesDao.deleteMessagesByGroupId(group.groupId);
|
await twonlyDB.messagesDao.deleteMessagesByGroupId(group.groupId);
|
||||||
// await twonlyDB.groupsDao.deleteGroup(group.groupId);
|
if (group.isDirectChat) {
|
||||||
await twonlyDB.groupsDao.updateGroup(
|
await twonlyDB.groupsDao.deleteGroup(group.groupId);
|
||||||
group.groupId,
|
} else {
|
||||||
const GroupsCompanion(
|
await twonlyDB.groupsDao.updateGroup(
|
||||||
deletedContent: Value(true),
|
group.groupId,
|
||||||
),
|
const GroupsCompanion(
|
||||||
);
|
deletedContent: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,48 @@ class ContactView extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ContactViewState extends State<ContactView> {
|
class _ContactViewState extends State<ContactView> {
|
||||||
|
Contact? _contact;
|
||||||
|
bool _contactIsStillAGroupMember = true;
|
||||||
|
|
||||||
|
late StreamSubscription<Contact?> _contactSub;
|
||||||
|
late StreamSubscription<List<GroupMember>> _groupMemberSub;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_contactSub =
|
||||||
|
twonlyDB.contactsDao.watchContact(widget.userId).listen((update) {
|
||||||
|
setState(() {
|
||||||
|
_contact = update;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
_groupMemberSub = twonlyDB.groupsDao
|
||||||
|
.watchContactGroupMember(widget.userId)
|
||||||
|
.listen((update) {
|
||||||
|
setState(() {
|
||||||
|
_contactIsStillAGroupMember = update.isNotEmpty;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_contactSub.cancel();
|
||||||
|
_groupMemberSub.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> handleUserRemoveRequest(Contact contact) async {
|
Future<void> handleUserRemoveRequest(Contact contact) async {
|
||||||
|
if (_contactIsStillAGroupMember) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(context.lang.deleteUserErrorMessage),
|
||||||
|
duration: const Duration(seconds: 8),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final remove = await showAlertDialog(
|
final remove = await showAlertDialog(
|
||||||
context,
|
context,
|
||||||
context.lang
|
context.lang
|
||||||
|
|
@ -84,128 +125,117 @@ class _ContactViewState extends State<ContactView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final contact = twonlyDB.contactsDao
|
if (_contact == null) return Container();
|
||||||
.getContactByUserId(widget.userId)
|
final contact = _contact!;
|
||||||
.watchSingleOrNull();
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text(''),
|
title: const Text(''),
|
||||||
),
|
),
|
||||||
body: StreamBuilder(
|
body: ListView(
|
||||||
stream: contact,
|
key: ValueKey(contact.userId),
|
||||||
builder: (context, snapshot) {
|
children: [
|
||||||
if (!snapshot.hasData || snapshot.data == null) {
|
Padding(
|
||||||
return Container();
|
padding: const EdgeInsets.all(10),
|
||||||
}
|
child: AvatarIcon(contactId: contact.userId, fontSize: 30),
|
||||||
final contact = snapshot.data!;
|
),
|
||||||
return ListView(
|
Row(
|
||||||
key: ValueKey(contact.userId),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.only(right: 10),
|
||||||
child: AvatarIcon(contactId: contact.userId, fontSize: 30),
|
child: VerifiedShield(
|
||||||
|
key: GlobalKey(),
|
||||||
|
contact: contact,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Text(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
getContactDisplayName(contact, maxLength: 20),
|
||||||
children: [
|
style: const TextStyle(fontSize: 20),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 10),
|
|
||||||
child: VerifiedShield(
|
|
||||||
key: GlobalKey(),
|
|
||||||
contact: contact,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
getContactDisplayName(contact, maxLength: 20),
|
|
||||||
style: const TextStyle(fontSize: 20),
|
|
||||||
),
|
|
||||||
FlameCounterWidget(
|
|
||||||
contactId: contact.userId,
|
|
||||||
prefix: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
if (getContactDisplayName(contact) != contact.username)
|
FlameCounterWidget(
|
||||||
Center(child: Text('(${contact.username})')),
|
contactId: contact.userId,
|
||||||
const SizedBox(height: 50),
|
prefix: true,
|
||||||
BetterListTile(
|
|
||||||
icon: FontAwesomeIcons.pencil,
|
|
||||||
text: context.lang.contactNickname,
|
|
||||||
onTap: () async {
|
|
||||||
final nickName =
|
|
||||||
await showNicknameChangeDialog(context, contact);
|
|
||||||
|
|
||||||
if (context.mounted && nickName != null && nickName != '') {
|
|
||||||
final update = ContactsCompanion(nickName: Value(nickName));
|
|
||||||
await twonlyDB.contactsDao
|
|
||||||
.updateContact(contact.userId, update);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const Divider(),
|
|
||||||
SelectChatDeletionTimeListTitle(
|
|
||||||
groupId: getUUIDforDirectChat(widget.userId, gUser.userId),
|
|
||||||
),
|
|
||||||
const Divider(),
|
|
||||||
MaxFlameListTitle(
|
|
||||||
contactId: widget.userId,
|
|
||||||
),
|
|
||||||
BetterListTile(
|
|
||||||
icon: FontAwesomeIcons.shieldHeart,
|
|
||||||
text: context.lang.contactVerifyNumberTitle,
|
|
||||||
onTap: () async {
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const PublicProfileView();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// BetterListTile(
|
|
||||||
// icon: FontAwesomeIcons.eraser,
|
|
||||||
// iconSize: 16,
|
|
||||||
// text: context.lang.deleteAllContactMessages,
|
|
||||||
// onTap: () async {
|
|
||||||
// final block = await showAlertDialog(
|
|
||||||
// context,
|
|
||||||
// context.lang.deleteAllContactMessages,
|
|
||||||
// context.lang.deleteAllContactMessagesBody(
|
|
||||||
// getContactDisplayName(contact),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// if (block) {
|
|
||||||
// if (context.mounted) {
|
|
||||||
// await twonlyDB.messagesDao
|
|
||||||
// .deleteMessagesByContactId(contact.userId);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
BetterListTile(
|
|
||||||
icon: FontAwesomeIcons.flag,
|
|
||||||
text: context.lang.reportUser,
|
|
||||||
onTap: () => handleReportUser(contact),
|
|
||||||
),
|
|
||||||
BetterListTile(
|
|
||||||
icon: FontAwesomeIcons.ban,
|
|
||||||
text: context.lang.contactBlock,
|
|
||||||
onTap: () => handleUserBlockRequest(contact),
|
|
||||||
),
|
|
||||||
// BetterListTile(
|
|
||||||
// icon: FontAwesomeIcons.userMinus,
|
|
||||||
// iconSize: 16,
|
|
||||||
// color: Colors.red,
|
|
||||||
// text: context.lang.contactRemove,
|
|
||||||
// onTap: () => handleUserRemoveRequest(contact),
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
},
|
if (getContactDisplayName(contact) != contact.username)
|
||||||
|
Center(child: Text('(${contact.username})')),
|
||||||
|
const SizedBox(height: 50),
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.pencil,
|
||||||
|
text: context.lang.contactNickname,
|
||||||
|
onTap: () async {
|
||||||
|
final nickName = await showNicknameChangeDialog(context, contact);
|
||||||
|
|
||||||
|
if (context.mounted && nickName != null && nickName != '') {
|
||||||
|
final update = ContactsCompanion(nickName: Value(nickName));
|
||||||
|
await twonlyDB.contactsDao
|
||||||
|
.updateContact(contact.userId, update);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
SelectChatDeletionTimeListTitle(
|
||||||
|
groupId: getUUIDforDirectChat(widget.userId, gUser.userId),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
MaxFlameListTitle(
|
||||||
|
contactId: widget.userId,
|
||||||
|
),
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.shieldHeart,
|
||||||
|
text: context.lang.contactVerifyNumberTitle,
|
||||||
|
onTap: () async {
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return const PublicProfileView();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// BetterListTile(
|
||||||
|
// icon: FontAwesomeIcons.eraser,
|
||||||
|
// iconSize: 16,
|
||||||
|
// text: context.lang.deleteAllContactMessages,
|
||||||
|
// onTap: () async {
|
||||||
|
// final block = await showAlertDialog(
|
||||||
|
// context,
|
||||||
|
// context.lang.deleteAllContactMessages,
|
||||||
|
// context.lang.deleteAllContactMessagesBody(
|
||||||
|
// getContactDisplayName(contact),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// if (block) {
|
||||||
|
// if (context.mounted) {
|
||||||
|
// await twonlyDB.messagesDao
|
||||||
|
// .deleteMessagesByContactId(contact.userId);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.flag,
|
||||||
|
text: context.lang.reportUser,
|
||||||
|
onTap: () => handleReportUser(contact),
|
||||||
|
),
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.ban,
|
||||||
|
text: context.lang.contactBlock,
|
||||||
|
onTap: () => handleUserBlockRequest(contact),
|
||||||
|
),
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.userMinus,
|
||||||
|
iconSize: 16,
|
||||||
|
color: Colors.red,
|
||||||
|
text: context.lang.contactRemove,
|
||||||
|
onTap: () => handleUserRemoveRequest(contact),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,15 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
void toggleSelectedUser(int userId) {
|
void toggleSelectedUser(int userId) {
|
||||||
if (alreadyInGroup.contains(userId)) return;
|
if (alreadyInGroup.contains(userId)) return;
|
||||||
if (!selectedUsers.contains(userId)) {
|
if (!selectedUsers.contains(userId)) {
|
||||||
|
if (selectedUsers.length + alreadyInGroup.length > 256) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(context.lang.groupSizeLimitError(256)),
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
selectedUsers.add(userId);
|
selectedUsers.add(userId);
|
||||||
} else {
|
} else {
|
||||||
selectedUsers.remove(userId);
|
selectedUsers.remove(userId);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/camera/camera_preview_components/save_to_gallery.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||||
|
|
@ -92,8 +93,36 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> shareMediaFile() async {
|
||||||
|
final orgMediaService = widget.galleryItems[currentIndex].mediaService;
|
||||||
|
|
||||||
|
final newMediaService = await initializeMediaUpload(
|
||||||
|
orgMediaService.mediaFile.type,
|
||||||
|
gUser.defaultShowTime,
|
||||||
|
);
|
||||||
|
if (newMediaService == null) {
|
||||||
|
Log.error('Could not create new mediaFIle');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
orgMediaService.storedPath.copySync(newMediaService.originalPath.path);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ShareImageEditorView(
|
||||||
|
mediaFileService: newMediaService,
|
||||||
|
sharedFromGallery: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final orgMediaService = widget.galleryItems[currentIndex].mediaService;
|
||||||
return Dismissible(
|
return Dismissible(
|
||||||
key: key,
|
key: key,
|
||||||
direction: DismissDirection.vertical,
|
direction: DismissDirection.vertical,
|
||||||
|
|
@ -117,36 +146,18 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
if (!orgMediaService.storedPath.existsSync())
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 12),
|
||||||
|
child: SaveToGalleryButton(
|
||||||
|
isLoading: false,
|
||||||
|
displayButtonLabel: true,
|
||||||
|
mediaService: orgMediaService,
|
||||||
|
),
|
||||||
|
),
|
||||||
FilledButton.icon(
|
FilledButton.icon(
|
||||||
icon: const FaIcon(FontAwesomeIcons.solidPaperPlane),
|
icon: const FaIcon(FontAwesomeIcons.solidPaperPlane),
|
||||||
onPressed: () async {
|
onPressed: shareMediaFile,
|
||||||
final orgMediaService =
|
|
||||||
widget.galleryItems[currentIndex].mediaService;
|
|
||||||
|
|
||||||
final newMediaService = await initializeMediaUpload(
|
|
||||||
orgMediaService.mediaFile.type,
|
|
||||||
gUser.defaultShowTime,
|
|
||||||
);
|
|
||||||
if (newMediaService == null) {
|
|
||||||
Log.error('Could not create new mediaFIle');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
orgMediaService.storedPath
|
|
||||||
.copySync(newMediaService.originalPath.path);
|
|
||||||
|
|
||||||
if (!context.mounted) return;
|
|
||||||
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => ShareImageEditorView(
|
|
||||||
mediaFileService: newMediaService,
|
|
||||||
sharedFromGallery: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: WidgetStateProperty.all<EdgeInsets>(
|
padding: WidgetStateProperty.all<EdgeInsets>(
|
||||||
const EdgeInsets.symmetric(
|
const EdgeInsets.symmetric(
|
||||||
|
|
@ -217,10 +228,16 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
|
|
||||||
PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {
|
PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {
|
||||||
final item = widget.galleryItems[index];
|
final item = widget.galleryItems[index];
|
||||||
|
|
||||||
|
var filePath = item.mediaService.storedPath;
|
||||||
|
if (!filePath.existsSync()) {
|
||||||
|
filePath = item.mediaService.tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
return item.mediaService.mediaFile.type == MediaType.video
|
return item.mediaService.mediaFile.type == MediaType.video
|
||||||
? PhotoViewGalleryPageOptions.customChild(
|
? PhotoViewGalleryPageOptions.customChild(
|
||||||
child: VideoPlayerWrapper(
|
child: VideoPlayerWrapper(
|
||||||
videoPath: item.mediaService.storedPath,
|
videoPath: filePath,
|
||||||
),
|
),
|
||||||
// childSize: const Size(300, 300),
|
// childSize: const Size(300, 300),
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
|
|
@ -231,7 +248,7 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: PhotoViewGalleryPageOptions(
|
: PhotoViewGalleryPageOptions(
|
||||||
imageProvider: FileImage(item.mediaService.storedPath),
|
imageProvider: FileImage(filePath),
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
maxScale: PhotoViewComputedScale.covered * 4.1,
|
maxScale: PhotoViewComputedScale.covered * 4.1,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -81,10 +82,7 @@ class _ContactUsState extends State<ContactUsView> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> _getFeedbackText() async {
|
Future<String?> _getFeedbackText() async {
|
||||||
setState(() {
|
|
||||||
isLoading = true;
|
|
||||||
});
|
|
||||||
var osVersion = '';
|
var osVersion = '';
|
||||||
final locale = context.lang.localeName;
|
final locale = context.lang.localeName;
|
||||||
final deviceInfo = DeviceInfoPlugin();
|
final deviceInfo = DeviceInfoPlugin();
|
||||||
|
|
@ -95,7 +93,11 @@ class _ContactUsState extends State<ContactUsView> {
|
||||||
final feedback = _controller.text;
|
final feedback = _controller.text;
|
||||||
var debugLogToken = '';
|
var debugLogToken = '';
|
||||||
|
|
||||||
if (!mounted) return '';
|
if (!mounted) return null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
// Get device information
|
// Get device information
|
||||||
if (Theme.of(context).platform == TargetPlatform.android) {
|
if (Theme.of(context).platform == TargetPlatform.android) {
|
||||||
|
|
@ -109,18 +111,23 @@ class _ContactUsState extends State<ContactUsView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeDebugLog) {
|
if (includeDebugLog) {
|
||||||
|
String? token;
|
||||||
try {
|
try {
|
||||||
final token = await uploadDebugLog();
|
token = await uploadDebugLog();
|
||||||
if (token != null) {
|
|
||||||
debugLogToken =
|
|
||||||
'Debug Log: https://api.twonly.eu/api/download/$token';
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return '';
|
Log.error(e);
|
||||||
|
}
|
||||||
|
if (token == null) {
|
||||||
|
if (!mounted) return null;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Could not upload the debug log!')),
|
const SnackBar(content: Text('Could not upload the debug log!')),
|
||||||
);
|
);
|
||||||
|
setState(() {
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
debugLogToken = 'Debug Log: https://api.twonly.eu/api/download/$token';
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -238,12 +245,22 @@ $debugLogToken
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton.icon(
|
||||||
|
icon: isLoading
|
||||||
|
? SizedBox(
|
||||||
|
height: 12,
|
||||||
|
width: 12,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: Theme.of(context).colorScheme.inversePrimary,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const FaIcon(FontAwesomeIcons.angleRight),
|
||||||
onPressed: isLoading
|
onPressed: isLoading
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
final fullMessage = await _getFeedbackText();
|
final fullMessage = await _getFeedbackText();
|
||||||
if (!context.mounted) return;
|
if (!context.mounted || fullMessage == null) return;
|
||||||
|
|
||||||
final feedbackSend = await Navigator.push(
|
final feedbackSend = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
|
|
@ -260,7 +277,7 @@ $debugLogToken
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text(context.lang.next),
|
label: Text(context.lang.next),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,30 @@ class _AdditionalUsersViewState extends State<AdditionalUsersView> {
|
||||||
for (final selectedUserId in selectedUserIds) {
|
for (final selectedUserId in selectedUserIds) {
|
||||||
final res = await apiService.addAdditionalUser(Int64(selectedUserId));
|
final res = await apiService.addAdditionalUser(Int64(selectedUserId));
|
||||||
if (res.isError && mounted) {
|
if (res.isError && mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
final contact =
|
||||||
SnackBar(content: Text(context.lang.additionalUserAddError)),
|
await twonlyDB.contactsDao.getContactById(selectedUserId);
|
||||||
);
|
if (contact != null && mounted) {
|
||||||
|
if (res.error == ErrorCode.UserIsNotInFreePlan) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
context.lang.additionalUserAddErrorNotInFreePlan(
|
||||||
|
getContactDisplayName(contact),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
context.lang
|
||||||
|
.additionalUserAddError(getContactDisplayName(contact)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await initAsync(force: true);
|
await initAsync(force: true);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import 'schema_v2.dart' as v2;
|
||||||
import 'schema_v3.dart' as v3;
|
import 'schema_v3.dart' as v3;
|
||||||
import 'schema_v4.dart' as v4;
|
import 'schema_v4.dart' as v4;
|
||||||
import 'schema_v5.dart' as v5;
|
import 'schema_v5.dart' as v5;
|
||||||
|
import 'schema_v6.dart' as v6;
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
@override
|
@override
|
||||||
|
|
@ -23,10 +24,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
return v4.DatabaseAtV4(db);
|
return v4.DatabaseAtV4(db);
|
||||||
case 5:
|
case 5:
|
||||||
return v5.DatabaseAtV5(db);
|
return v5.DatabaseAtV5(db);
|
||||||
|
case 6:
|
||||||
|
return v6.DatabaseAtV6(db);
|
||||||
default:
|
default:
|
||||||
throw MissingSchemaException(version, versions);
|
throw MissingSchemaException(version, versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const versions = const [1, 2, 3, 4, 5];
|
static const versions = const [1, 2, 3, 4, 5, 6];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6537
test/drift/twonly_db/generated/schema_v6.dart
Normal file
6537
test/drift/twonly_db/generated/schema_v6.dart
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue