mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 13:08:42 +00:00
handle text and media server messages, update pushkeys
This commit is contained in:
parent
a4ccefec75
commit
2f3f927914
41 changed files with 1172 additions and 790 deletions
|
|
@ -128,8 +128,8 @@ struct PushNotification: Sendable {
|
||||||
|
|
||||||
var kind: PushKind = .reaction
|
var kind: PushKind = .reaction
|
||||||
|
|
||||||
var messageID: Int64 {
|
var messageID: String {
|
||||||
get {return _messageID ?? 0}
|
get {return _messageID ?? String()}
|
||||||
set {_messageID = newValue}
|
set {_messageID = newValue}
|
||||||
}
|
}
|
||||||
/// Returns true if `messageID` has been explicitly set.
|
/// Returns true if `messageID` has been explicitly set.
|
||||||
|
|
@ -150,7 +150,7 @@ struct PushNotification: Sendable {
|
||||||
|
|
||||||
init() {}
|
init() {}
|
||||||
|
|
||||||
fileprivate var _messageID: Int64? = nil
|
fileprivate var _messageID: String? = nil
|
||||||
fileprivate var _reactionContent: String? = nil
|
fileprivate var _reactionContent: String? = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,8 +177,8 @@ struct PushUser: Sendable {
|
||||||
|
|
||||||
var blocked: Bool = false
|
var blocked: Bool = false
|
||||||
|
|
||||||
var lastMessageID: Int64 {
|
var lastMessageID: String {
|
||||||
get {return _lastMessageID ?? 0}
|
get {return _lastMessageID ?? String()}
|
||||||
set {_lastMessageID = newValue}
|
set {_lastMessageID = newValue}
|
||||||
}
|
}
|
||||||
/// Returns true if `lastMessageID` has been explicitly set.
|
/// Returns true if `lastMessageID` has been explicitly set.
|
||||||
|
|
@ -192,7 +192,7 @@ struct PushUser: Sendable {
|
||||||
|
|
||||||
init() {}
|
init() {}
|
||||||
|
|
||||||
fileprivate var _lastMessageID: Int64? = nil
|
fileprivate var _lastMessageID: String? = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PushKey: Sendable {
|
struct PushKey: Sendable {
|
||||||
|
|
@ -273,7 +273,7 @@ extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
|
||||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||||
switch fieldNumber {
|
switch fieldNumber {
|
||||||
case 1: try { try decoder.decodeSingularEnumField(value: &self.kind) }()
|
case 1: try { try decoder.decodeSingularEnumField(value: &self.kind) }()
|
||||||
case 2: try { try decoder.decodeSingularInt64Field(value: &self._messageID) }()
|
case 2: try { try decoder.decodeSingularStringField(value: &self._messageID) }()
|
||||||
case 3: try { try decoder.decodeSingularStringField(value: &self._reactionContent) }()
|
case 3: try { try decoder.decodeSingularStringField(value: &self._reactionContent) }()
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
|
|
@ -289,7 +289,7 @@ extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
|
||||||
try visitor.visitSingularEnumField(value: self.kind, fieldNumber: 1)
|
try visitor.visitSingularEnumField(value: self.kind, fieldNumber: 1)
|
||||||
}
|
}
|
||||||
try { if let v = self._messageID {
|
try { if let v = self._messageID {
|
||||||
try visitor.visitSingularInt64Field(value: v, fieldNumber: 2)
|
try visitor.visitSingularStringField(value: v, fieldNumber: 2)
|
||||||
} }()
|
} }()
|
||||||
try { if let v = self._reactionContent {
|
try { if let v = self._reactionContent {
|
||||||
try visitor.visitSingularStringField(value: v, fieldNumber: 3)
|
try visitor.visitSingularStringField(value: v, fieldNumber: 3)
|
||||||
|
|
@ -349,7 +349,7 @@ extension PushUser: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
||||||
case 1: try { try decoder.decodeSingularInt64Field(value: &self.userID) }()
|
case 1: try { try decoder.decodeSingularInt64Field(value: &self.userID) }()
|
||||||
case 2: try { try decoder.decodeSingularStringField(value: &self.displayName) }()
|
case 2: try { try decoder.decodeSingularStringField(value: &self.displayName) }()
|
||||||
case 3: try { try decoder.decodeSingularBoolField(value: &self.blocked) }()
|
case 3: try { try decoder.decodeSingularBoolField(value: &self.blocked) }()
|
||||||
case 4: try { try decoder.decodeSingularInt64Field(value: &self._lastMessageID) }()
|
case 4: try { try decoder.decodeSingularStringField(value: &self._lastMessageID) }()
|
||||||
case 5: try { try decoder.decodeRepeatedMessageField(value: &self.pushKeys) }()
|
case 5: try { try decoder.decodeRepeatedMessageField(value: &self.pushKeys) }()
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
|
|
@ -371,7 +371,7 @@ extension PushUser: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
||||||
try visitor.visitSingularBoolField(value: self.blocked, fieldNumber: 3)
|
try visitor.visitSingularBoolField(value: self.blocked, fieldNumber: 3)
|
||||||
}
|
}
|
||||||
try { if let v = self._lastMessageID {
|
try { if let v = self._lastMessageID {
|
||||||
try visitor.visitSingularInt64Field(value: v, fieldNumber: 4)
|
try visitor.visitSingularStringField(value: v, fieldNumber: 4)
|
||||||
} }()
|
} }()
|
||||||
if !self.pushKeys.isEmpty {
|
if !self.pushKeys.isEmpty {
|
||||||
try visitor.visitRepeatedMessageField(value: self.pushKeys, fieldNumber: 5)
|
try visitor.visitRepeatedMessageField(value: self.pushKeys, fieldNumber: 5)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@ class SecureStorageKeys {
|
||||||
static const String userData = 'userData';
|
static const String userData = 'userData';
|
||||||
static const String twonlySafeLastBackupHash = 'twonly_safe_last_backup_hash';
|
static const String twonlySafeLastBackupHash = 'twonly_safe_last_backup_hash';
|
||||||
|
|
||||||
static const String receivingPushKeys = 'receiving_push_keys';
|
static const String receivingPushKeys = 'push_keys_receiving';
|
||||||
static const String sendingPushKeys = 'sending_push_keys';
|
static const String sendingPushKeys = 'push_keys_sending';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,10 +120,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
|
||||||
Stream<List<Contact>> watchNotAcceptedContacts() {
|
Stream<List<Contact>> watchNotAcceptedContacts() {
|
||||||
return (select(contacts)
|
return (select(contacts)
|
||||||
..where(
|
..where(
|
||||||
(t) =>
|
(t) => t.accepted.equals(false) & t.blocked.equals(false),
|
||||||
t.accepted.equals(false) &
|
|
||||||
t.archived.equals(false) &
|
|
||||||
t.blocked.equals(false),
|
|
||||||
))
|
))
|
||||||
.watch();
|
.watch();
|
||||||
// return (select(contacts)).watch();
|
// return (select(contacts)).watch();
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,18 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
||||||
GroupsDao(super.db);
|
GroupsDao(super.db);
|
||||||
|
|
||||||
Future<bool> isContactInGroup(int contactId, String groupId) async {
|
Future<bool> isContactInGroup(int contactId, String groupId) async {
|
||||||
final entry = await (select(groupMembers)
|
final entry = await (select(groupMembers)..where(
|
||||||
..where(
|
// ignore: require_trailing_commas
|
||||||
(t) => t.contactId.equals(contactId) & t.groupId.equals(groupId)))
|
(t) => t.contactId.equals(contactId) & t.groupId.equals(groupId)))
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
return entry != null;
|
return entry != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateGroup(
|
||||||
|
String groupId,
|
||||||
|
GroupsCompanion updates,
|
||||||
|
) async {
|
||||||
|
await (update(groups)..where((c) => c.groupId.equals(groupId)))
|
||||||
|
.write(updates);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
54
lib/src/database/daos/mediafiles.dao.dart
Normal file
54
lib/src/database/daos/mediafiles.dao.dart
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
|
part 'mediafiles.dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(tables: [MediaFiles])
|
||||||
|
class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
||||||
|
with _$MediaFilesDaoMixin {
|
||||||
|
// this constructor is required so that the main database can create an instance
|
||||||
|
// of this object.
|
||||||
|
// ignore: matching_super_parameters
|
||||||
|
MediaFilesDao(super.db);
|
||||||
|
|
||||||
|
Future<MediaFile?> insertMedia(MediaFilesCompanion mediaFile) async {
|
||||||
|
try {
|
||||||
|
final rowId = await into(mediaFiles).insert(mediaFile);
|
||||||
|
|
||||||
|
return await (select(mediaFiles)..where((t) => t.rowId.equals(rowId)))
|
||||||
|
.getSingle();
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Could not insert media file: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateMedia(
|
||||||
|
String mediaId,
|
||||||
|
MediaFilesCompanion updates,
|
||||||
|
) async {
|
||||||
|
await (update(mediaFiles)..where((c) => c.mediaId.equals(mediaId)))
|
||||||
|
.write(updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<MediaFile?> getMediaFileById(String mediaId) async {
|
||||||
|
return (select(mediaFiles)..where((t) => t.mediaId.equals(mediaId)))
|
||||||
|
.getSingleOrNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> resetPendingDownloadState() async {
|
||||||
|
await (update(mediaFiles)
|
||||||
|
..where(
|
||||||
|
(c) => c.downloadState.equals(
|
||||||
|
DownloadState.downloading.name,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.write(
|
||||||
|
const MediaFilesCompanion(
|
||||||
|
downloadState: Value(DownloadState.pending),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
lib/src/database/daos/mediafiles.dao.g.dart
Normal file
8
lib/src/database/daos/mediafiles.dao.g.dart
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'mediafiles.dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$MediaFilesDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
|
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,24 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/tables/contacts.table.dart';
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
|
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafile.service.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
part 'messages.dao.g.dart';
|
part 'messages.dao.g.dart';
|
||||||
|
|
||||||
@DriftAccessor(tables: [Messages, Contacts, MediaFiles, MessageHistories])
|
@DriftAccessor(
|
||||||
|
tables: [
|
||||||
|
Messages,
|
||||||
|
Contacts,
|
||||||
|
MediaFiles,
|
||||||
|
MessageHistories,
|
||||||
|
Groups,
|
||||||
|
],
|
||||||
|
)
|
||||||
class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
// this constructor is required so that the main database can create an instance
|
// this constructor is required so that the main database can create an instance
|
||||||
// of this object.
|
// of this object.
|
||||||
|
|
@ -156,22 +167,6 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
// .write(updates);
|
// .write(updates);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Future<void> resetPendingDownloadState() {
|
|
||||||
// // All media files in the downloading state are reset to the pending state
|
|
||||||
// // When the app is used in mobile network, they will not be downloaded at the start
|
|
||||||
// // if they are not yet downloaded...
|
|
||||||
// const updates =
|
|
||||||
// MessagesCompanion(downloadState: Value(DownloadState.pending));
|
|
||||||
// return (update(messages)
|
|
||||||
// ..where(
|
|
||||||
// (t) =>
|
|
||||||
// t.messageOtherId.isNotNull() &
|
|
||||||
// t.downloadState.equals(DownloadState.downloading.index) &
|
|
||||||
// t.kind.equals(MessageKind.media.name),
|
|
||||||
// ))
|
|
||||||
// .write(updates);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Future<void> handleMessageDeletion(
|
Future<void> handleMessageDeletion(
|
||||||
int contactId,
|
int contactId,
|
||||||
String messageId,
|
String messageId,
|
||||||
|
|
@ -278,28 +273,33 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
// .write(updatedValues);
|
// .write(updatedValues);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Future<void> updateMessageByMessageId(
|
Future<void> updateMessageId(
|
||||||
// int messageId,
|
String messageId,
|
||||||
// MessagesCompanion updatedValues,
|
MessagesCompanion updatedValues,
|
||||||
// ) {
|
) {
|
||||||
// return (update(messages)..where((c) => c.messageId.equals(messageId)))
|
return (update(messages)..where((c) => c.messageId.equals(messageId)))
|
||||||
// .write(updatedValues);
|
.write(updatedValues);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Future<int?> insertMessage(MessagesCompanion message) async {
|
Future<Message?> insertMessage(MessagesCompanion message) async {
|
||||||
// try {
|
try {
|
||||||
// await (update(contacts)
|
final rowId = await into(messages).insert(message);
|
||||||
// ..where(
|
|
||||||
// (c) => c.userId.equals(message.contactId.value),
|
|
||||||
// ))
|
|
||||||
// .write(ContactsCompanion(lastMessageExchange: Value(DateTime.now())));
|
|
||||||
|
|
||||||
// return await into(messages).insert(message);
|
await twonlyDB.groupsDao.updateGroup(
|
||||||
// } catch (e) {
|
message.groupId.value,
|
||||||
// Log.error('Error while inserting message: $e');
|
GroupsCompanion(
|
||||||
// return null;
|
lastMessageExchange: Value(DateTime.now()),
|
||||||
// }
|
archived: const Value(false),
|
||||||
// }
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return await (select(messages)..where((t) => t.rowId.equals(rowId)))
|
||||||
|
.getSingle();
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Could not insert message: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Future<void> deleteMessagesByContactId(int contactId) {
|
// Future<void> deleteMessagesByContactId(int contactId) {
|
||||||
// return (delete(messages)
|
// return (delete(messages)
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,5 @@ mixin _$MessagesDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
$MessagesTable get messages => attachedDatabase.messages;
|
$MessagesTable get messages => attachedDatabase.messages;
|
||||||
$MessageHistoriesTable get messageHistories =>
|
$MessageHistoriesTable get messageHistories =>
|
||||||
attachedDatabase.messageHistories;
|
attachedDatabase.messageHistories;
|
||||||
|
$GroupsTable get groups => attachedDatabase.groups;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,13 @@ class ReactionsDao extends DatabaseAccessor<TwonlyDB> with _$ReactionsDaoMixin {
|
||||||
))
|
))
|
||||||
.go();
|
.go();
|
||||||
if (emoji != null) {
|
if (emoji != null) {
|
||||||
await into(reactions).insert(ReactionsCompanion(
|
await into(reactions).insert(
|
||||||
|
ReactionsCompanion(
|
||||||
messageId: Value(messageId),
|
messageId: Value(messageId),
|
||||||
emoji: Value(emoji),
|
emoji: Value(emoji),
|
||||||
senderId: Value(contactId),
|
senderId: Value(contactId),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,10 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
||||||
|
|
||||||
Future<void> confirmReceipt(String receiptId, int fromUserId) async {
|
Future<void> confirmReceipt(String receiptId, int fromUserId) async {
|
||||||
final receipt = await (select(receipts)
|
final receipt = await (select(receipts)
|
||||||
..where((t) =>
|
..where(
|
||||||
t.receiptId.equals(receiptId) & t.contactId.equals(fromUserId)))
|
(t) =>
|
||||||
|
t.receiptId.equals(receiptId) & t.contactId.equals(fromUserId),
|
||||||
|
))
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
|
|
||||||
if (receipt == null) return;
|
if (receipt == null) return;
|
||||||
|
|
@ -26,7 +28,7 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
||||||
..where((t) => t.messageId.equals(receipt.messageId!)))
|
..where((t) => t.messageId.equals(receipt.messageId!)))
|
||||||
.write(
|
.write(
|
||||||
const MessagesCompanion(
|
const MessagesCompanion(
|
||||||
acknowledgeByUser: Value(true),
|
ackByUser: Value(true),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +41,14 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
||||||
.go();
|
.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteReceipt(String receiptId) async {
|
||||||
|
await (delete(receipts)
|
||||||
|
..where(
|
||||||
|
(t) => t.receiptId.equals(receiptId),
|
||||||
|
))
|
||||||
|
.go();
|
||||||
|
}
|
||||||
|
|
||||||
Future<Receipt?> insertReceipt(ReceiptsCompanion entry) async {
|
Future<Receipt?> insertReceipt(ReceiptsCompanion entry) async {
|
||||||
try {
|
try {
|
||||||
final id = await into(receipts).insert(entry);
|
final id = await into(receipts).insert(entry);
|
||||||
|
|
@ -49,4 +59,33 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Receipt?> getReceiptById(String receiptId) async {
|
||||||
|
try {
|
||||||
|
return await (select(receipts)
|
||||||
|
..where(
|
||||||
|
(t) => t.receiptId.equals(receiptId),
|
||||||
|
))
|
||||||
|
.getSingleOrNull();
|
||||||
|
} catch (e) {
|
||||||
|
Log.error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Receipt>> getReceiptsNotAckByServer() async {
|
||||||
|
return (select(receipts)
|
||||||
|
..where(
|
||||||
|
(t) => t.ackByServerAt.isNull(),
|
||||||
|
))
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateReceipt(
|
||||||
|
String receiptId,
|
||||||
|
ReceiptsCompanion updates,
|
||||||
|
) async {
|
||||||
|
await (update(receipts)..where((c) => c.receiptId.equals(receiptId)))
|
||||||
|
.write(updates);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ class Contacts extends Table {
|
||||||
BoolColumn get hidden => boolean().withDefault(const Constant(false))();
|
BoolColumn get hidden => boolean().withDefault(const Constant(false))();
|
||||||
BoolColumn get blocked => boolean().withDefault(const Constant(false))();
|
BoolColumn get blocked => boolean().withDefault(const Constant(false))();
|
||||||
BoolColumn get verified => boolean().withDefault(const Constant(false))();
|
BoolColumn get verified => boolean().withDefault(const Constant(false))();
|
||||||
BoolColumn get archived => boolean().withDefault(const Constant(false))();
|
|
||||||
BoolColumn get deleted => boolean().withDefault(const Constant(false))();
|
BoolColumn get deleted => boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
BoolColumn get alsoBestFriend =>
|
BoolColumn get alsoBestFriend =>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ class Groups extends Table {
|
||||||
BoolColumn get isGroupAdmin => boolean()();
|
BoolColumn get isGroupAdmin => boolean()();
|
||||||
BoolColumn get isGroupOfTwo => boolean()();
|
BoolColumn get isGroupOfTwo => boolean()();
|
||||||
BoolColumn get pinned => boolean().withDefault(const Constant(false))();
|
BoolColumn get pinned => boolean().withDefault(const Constant(false))();
|
||||||
|
BoolColumn get archived => boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
DateTimeColumn get lastMessageExchange =>
|
DateTimeColumn get lastMessageExchange =>
|
||||||
dateTime().withDefault(currentDateAndTime)();
|
dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:hashlib/random.dart';
|
import 'package:hashlib/random.dart';
|
||||||
|
|
||||||
|
|
@ -14,13 +16,11 @@ enum UploadState {
|
||||||
receiverNotified,
|
receiverNotified,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DownloadState {
|
enum DownloadState { pending, downloading }
|
||||||
pending,
|
|
||||||
}
|
|
||||||
|
|
||||||
@DataClassName('MediaFile')
|
@DataClassName('MediaFile')
|
||||||
class MediaFiles extends Table {
|
class MediaFiles extends Table {
|
||||||
TextColumn get mediaId => text().clientDefault(() => uuid.v4())();
|
TextColumn get mediaId => text().clientDefault(() => uuid.v7())();
|
||||||
|
|
||||||
TextColumn get type => textEnum<MediaType>()();
|
TextColumn get type => textEnum<MediaType>()();
|
||||||
|
|
||||||
|
|
@ -34,6 +34,9 @@ class MediaFiles extends Table {
|
||||||
BoolColumn get storedByContact =>
|
BoolColumn get storedByContact =>
|
||||||
boolean().withDefault(const Constant(false))();
|
boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
|
TextColumn get reuploadRequestedBy =>
|
||||||
|
text().map(IntListTypeConverter()).nullable()();
|
||||||
|
|
||||||
IntColumn get displayLimitInMilliseconds => integer().nullable()();
|
IntColumn get displayLimitInMilliseconds => integer().nullable()();
|
||||||
|
|
||||||
BlobColumn get downloadToken => blob().nullable()();
|
BlobColumn get downloadToken => blob().nullable()();
|
||||||
|
|
@ -46,3 +49,15 @@ class MediaFiles extends Table {
|
||||||
@override
|
@override
|
||||||
Set<Column> get primaryKey => {mediaId};
|
Set<Column> get primaryKey => {mediaId};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IntListTypeConverter extends TypeConverter<List<int>, String> {
|
||||||
|
@override
|
||||||
|
List<int> fromSql(String fromDb) {
|
||||||
|
return List<int>.from(jsonDecode(fromDb) as Iterable<dynamic>);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toSql(List<int> value) {
|
||||||
|
return json.encode(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:twonly/src/database/tables/contacts.table.dart';
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
|
|
||||||
@DataClassName('Message')
|
@DataClassName('Message')
|
||||||
class Messages extends Table {
|
class Messages extends Table {
|
||||||
TextColumn get groupId => text()();
|
TextColumn get groupId => text()();
|
||||||
TextColumn get messageId => text()();
|
TextColumn get messageId => text().clientDefault(() => uuid.v7())();
|
||||||
|
|
||||||
// in case senderId is null, it was send by user itself
|
// in case senderId is null, it was send by user itself
|
||||||
IntColumn get senderId =>
|
IntColumn get senderId =>
|
||||||
|
|
@ -23,10 +24,8 @@ class Messages extends Table {
|
||||||
|
|
||||||
BoolColumn get isEdited => boolean().withDefault(const Constant(false))();
|
BoolColumn get isEdited => boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
BoolColumn get acknowledgeByUser =>
|
BoolColumn get ackByUser => boolean().withDefault(const Constant(false))();
|
||||||
boolean().withDefault(const Constant(false))();
|
BoolColumn get ackByServer => boolean().withDefault(const Constant(false))();
|
||||||
BoolColumn get acknowledgeByServer =>
|
|
||||||
boolean().withDefault(const Constant(false))();
|
|
||||||
|
|
||||||
IntColumn get openedByCounter => integer().withDefault(const Constant(0))();
|
IntColumn get openedByCounter => integer().withDefault(const Constant(0))();
|
||||||
DateTimeColumn get openedAt => dateTime().nullable()();
|
DateTimeColumn get openedAt => dateTime().nullable()();
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,14 @@ class Receipts extends Table {
|
||||||
.nullable()
|
.nullable()
|
||||||
.references(Messages, #messageId, onDelete: KeyAction.cascade)();
|
.references(Messages, #messageId, onDelete: KeyAction.cascade)();
|
||||||
|
|
||||||
|
/// This is the protobuf 'Message'
|
||||||
BlobColumn get message => blob()();
|
BlobColumn get message => blob()();
|
||||||
|
|
||||||
BoolColumn get contactWillSendsReceipt =>
|
BoolColumn get contactWillSendsReceipt =>
|
||||||
boolean().withDefault(const Constant(true))();
|
boolean().withDefault(const Constant(true))();
|
||||||
|
|
||||||
|
DateTimeColumn get ackByServerAt => dateTime().nullable()();
|
||||||
|
|
||||||
IntColumn get retryCount => integer().withDefault(const Constant(0))();
|
IntColumn get retryCount => integer().withDefault(const Constant(0))();
|
||||||
DateTimeColumn get lastRetry => dateTime().nullable()();
|
DateTimeColumn get lastRetry => dateTime().nullable()();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import 'package:drift_flutter/drift_flutter.dart'
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/groups.dao.dart';
|
import 'package:twonly/src/database/daos/groups.dao.dart';
|
||||||
|
import 'package:twonly/src/database/daos/mediafiles.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/messages.dao.dart';
|
import 'package:twonly/src/database/daos/messages.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/reactions.dao.dart';
|
import 'package:twonly/src/database/daos/reactions.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/receipts.dao.dart';
|
import 'package:twonly/src/database/daos/receipts.dao.dart';
|
||||||
|
|
@ -48,7 +49,8 @@ part 'twonly.db.g.dart';
|
||||||
SignalDao,
|
SignalDao,
|
||||||
ReceiptsDao,
|
ReceiptsDao,
|
||||||
GroupsDao,
|
GroupsDao,
|
||||||
ReactionsDao
|
ReactionsDao,
|
||||||
|
MediaFilesDao,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class TwonlyDB extends _$TwonlyDB {
|
class TwonlyDB extends _$TwonlyDB {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -218,6 +218,7 @@ class EncryptedContent_TextMessage extends $pb.GeneratedMessage {
|
||||||
factory EncryptedContent_TextMessage({
|
factory EncryptedContent_TextMessage({
|
||||||
$core.String? senderMessageId,
|
$core.String? senderMessageId,
|
||||||
$core.String? text,
|
$core.String? text,
|
||||||
|
$fixnum.Int64? timestamp,
|
||||||
$core.String? quoteMessageId,
|
$core.String? quoteMessageId,
|
||||||
}) {
|
}) {
|
||||||
final $result = create();
|
final $result = create();
|
||||||
|
|
@ -227,6 +228,9 @@ class EncryptedContent_TextMessage extends $pb.GeneratedMessage {
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
$result.text = text;
|
$result.text = text;
|
||||||
}
|
}
|
||||||
|
if (timestamp != null) {
|
||||||
|
$result.timestamp = timestamp;
|
||||||
|
}
|
||||||
if (quoteMessageId != null) {
|
if (quoteMessageId != null) {
|
||||||
$result.quoteMessageId = quoteMessageId;
|
$result.quoteMessageId = quoteMessageId;
|
||||||
}
|
}
|
||||||
|
|
@ -239,7 +243,8 @@ class EncryptedContent_TextMessage extends $pb.GeneratedMessage {
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.TextMessage', createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.TextMessage', createEmptyInstance: create)
|
||||||
..aOS(1, _omitFieldNames ? '' : 'senderMessageId', protoName: 'senderMessageId')
|
..aOS(1, _omitFieldNames ? '' : 'senderMessageId', protoName: 'senderMessageId')
|
||||||
..aOS(2, _omitFieldNames ? '' : 'text')
|
..aOS(2, _omitFieldNames ? '' : 'text')
|
||||||
..aOS(3, _omitFieldNames ? '' : 'quoteMessageId', protoName: 'quoteMessageId')
|
..aInt64(3, _omitFieldNames ? '' : 'timestamp')
|
||||||
|
..aOS(4, _omitFieldNames ? '' : 'quoteMessageId', protoName: 'quoteMessageId')
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -283,13 +288,22 @@ class EncryptedContent_TextMessage extends $pb.GeneratedMessage {
|
||||||
void clearText() => clearField(2);
|
void clearText() => clearField(2);
|
||||||
|
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$core.String get quoteMessageId => $_getSZ(2);
|
$fixnum.Int64 get timestamp => $_getI64(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
set quoteMessageId($core.String v) { $_setString(2, v); }
|
set timestamp($fixnum.Int64 v) { $_setInt64(2, v); }
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$core.bool hasQuoteMessageId() => $_has(2);
|
$core.bool hasTimestamp() => $_has(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
void clearQuoteMessageId() => clearField(3);
|
void clearTimestamp() => clearField(3);
|
||||||
|
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.String get quoteMessageId => $_getSZ(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
set quoteMessageId($core.String v) { $_setString(3, v); }
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.bool hasQuoteMessageId() => $_has(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
void clearQuoteMessageId() => clearField(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
class EncryptedContent_Reaction extends $pb.GeneratedMessage {
|
class EncryptedContent_Reaction extends $pb.GeneratedMessage {
|
||||||
|
|
@ -374,6 +388,7 @@ class EncryptedContent_MessageUpdate extends $pb.GeneratedMessage {
|
||||||
factory EncryptedContent_MessageUpdate({
|
factory EncryptedContent_MessageUpdate({
|
||||||
EncryptedContent_MessageUpdate_Type? type,
|
EncryptedContent_MessageUpdate_Type? type,
|
||||||
$core.String? senderMessageId,
|
$core.String? senderMessageId,
|
||||||
|
$core.Iterable<$core.String>? multipleSenderMessageIds,
|
||||||
$core.String? text,
|
$core.String? text,
|
||||||
$fixnum.Int64? timestamp,
|
$fixnum.Int64? timestamp,
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -384,6 +399,9 @@ class EncryptedContent_MessageUpdate extends $pb.GeneratedMessage {
|
||||||
if (senderMessageId != null) {
|
if (senderMessageId != null) {
|
||||||
$result.senderMessageId = senderMessageId;
|
$result.senderMessageId = senderMessageId;
|
||||||
}
|
}
|
||||||
|
if (multipleSenderMessageIds != null) {
|
||||||
|
$result.multipleSenderMessageIds.addAll(multipleSenderMessageIds);
|
||||||
|
}
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
$result.text = text;
|
$result.text = text;
|
||||||
}
|
}
|
||||||
|
|
@ -399,8 +417,9 @@ class EncryptedContent_MessageUpdate extends $pb.GeneratedMessage {
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.MessageUpdate', createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.MessageUpdate', createEmptyInstance: create)
|
||||||
..e<EncryptedContent_MessageUpdate_Type>(1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: EncryptedContent_MessageUpdate_Type.DELETE, valueOf: EncryptedContent_MessageUpdate_Type.valueOf, enumValues: EncryptedContent_MessageUpdate_Type.values)
|
..e<EncryptedContent_MessageUpdate_Type>(1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: EncryptedContent_MessageUpdate_Type.DELETE, valueOf: EncryptedContent_MessageUpdate_Type.valueOf, enumValues: EncryptedContent_MessageUpdate_Type.values)
|
||||||
..aOS(2, _omitFieldNames ? '' : 'senderMessageId', protoName: 'senderMessageId')
|
..aOS(2, _omitFieldNames ? '' : 'senderMessageId', protoName: 'senderMessageId')
|
||||||
..aOS(3, _omitFieldNames ? '' : 'text')
|
..pPS(3, _omitFieldNames ? '' : 'multipleSenderMessageIds', protoName: 'multipleSenderMessageIds')
|
||||||
..aInt64(4, _omitFieldNames ? '' : 'timestamp')
|
..aOS(4, _omitFieldNames ? '' : 'text')
|
||||||
|
..aInt64(5, _omitFieldNames ? '' : 'timestamp')
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -444,22 +463,25 @@ class EncryptedContent_MessageUpdate extends $pb.GeneratedMessage {
|
||||||
void clearSenderMessageId() => clearField(2);
|
void clearSenderMessageId() => clearField(2);
|
||||||
|
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$core.String get text => $_getSZ(2);
|
$core.List<$core.String> get multipleSenderMessageIds => $_getList(2);
|
||||||
@$pb.TagNumber(3)
|
|
||||||
set text($core.String v) { $_setString(2, v); }
|
|
||||||
@$pb.TagNumber(3)
|
|
||||||
$core.bool hasText() => $_has(2);
|
|
||||||
@$pb.TagNumber(3)
|
|
||||||
void clearText() => clearField(3);
|
|
||||||
|
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
$fixnum.Int64 get timestamp => $_getI64(3);
|
$core.String get text => $_getSZ(3);
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
set timestamp($fixnum.Int64 v) { $_setInt64(3, v); }
|
set text($core.String v) { $_setString(3, v); }
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
$core.bool hasTimestamp() => $_has(3);
|
$core.bool hasText() => $_has(3);
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
void clearTimestamp() => clearField(4);
|
void clearText() => clearField(4);
|
||||||
|
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$fixnum.Int64 get timestamp => $_getI64(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
set timestamp($fixnum.Int64 v) { $_setInt64(4, v); }
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.bool hasTimestamp() => $_has(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
void clearTimestamp() => clearField(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
class EncryptedContent_Media extends $pb.GeneratedMessage {
|
class EncryptedContent_Media extends $pb.GeneratedMessage {
|
||||||
|
|
@ -468,6 +490,8 @@ class EncryptedContent_Media extends $pb.GeneratedMessage {
|
||||||
EncryptedContent_Media_Type? type,
|
EncryptedContent_Media_Type? type,
|
||||||
$fixnum.Int64? displayLimitInMilliseconds,
|
$fixnum.Int64? displayLimitInMilliseconds,
|
||||||
$core.bool? requiresAuthentication,
|
$core.bool? requiresAuthentication,
|
||||||
|
$fixnum.Int64? timestamp,
|
||||||
|
$core.String? quoteMessageId,
|
||||||
$core.List<$core.int>? downloadToken,
|
$core.List<$core.int>? downloadToken,
|
||||||
$core.List<$core.int>? encryptionKey,
|
$core.List<$core.int>? encryptionKey,
|
||||||
$core.List<$core.int>? encryptionMac,
|
$core.List<$core.int>? encryptionMac,
|
||||||
|
|
@ -486,6 +510,12 @@ class EncryptedContent_Media extends $pb.GeneratedMessage {
|
||||||
if (requiresAuthentication != null) {
|
if (requiresAuthentication != null) {
|
||||||
$result.requiresAuthentication = requiresAuthentication;
|
$result.requiresAuthentication = requiresAuthentication;
|
||||||
}
|
}
|
||||||
|
if (timestamp != null) {
|
||||||
|
$result.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
if (quoteMessageId != null) {
|
||||||
|
$result.quoteMessageId = quoteMessageId;
|
||||||
|
}
|
||||||
if (downloadToken != null) {
|
if (downloadToken != null) {
|
||||||
$result.downloadToken = downloadToken;
|
$result.downloadToken = downloadToken;
|
||||||
}
|
}
|
||||||
|
|
@ -506,13 +536,15 @@ class EncryptedContent_Media extends $pb.GeneratedMessage {
|
||||||
|
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.Media', createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EncryptedContent.Media', createEmptyInstance: create)
|
||||||
..aOS(1, _omitFieldNames ? '' : 'senderMessageId', protoName: 'senderMessageId')
|
..aOS(1, _omitFieldNames ? '' : 'senderMessageId', protoName: 'senderMessageId')
|
||||||
..e<EncryptedContent_Media_Type>(2, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: EncryptedContent_Media_Type.IMAGE, valueOf: EncryptedContent_Media_Type.valueOf, enumValues: EncryptedContent_Media_Type.values)
|
..e<EncryptedContent_Media_Type>(2, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: EncryptedContent_Media_Type.REUPLOAD, valueOf: EncryptedContent_Media_Type.valueOf, enumValues: EncryptedContent_Media_Type.values)
|
||||||
..aInt64(3, _omitFieldNames ? '' : 'displayLimitInMilliseconds', protoName: 'displayLimitInMilliseconds')
|
..aInt64(3, _omitFieldNames ? '' : 'displayLimitInMilliseconds', protoName: 'displayLimitInMilliseconds')
|
||||||
..aOB(4, _omitFieldNames ? '' : 'requiresAuthentication', protoName: 'requiresAuthentication')
|
..aOB(4, _omitFieldNames ? '' : 'requiresAuthentication', protoName: 'requiresAuthentication')
|
||||||
..a<$core.List<$core.int>>(5, _omitFieldNames ? '' : 'downloadToken', $pb.PbFieldType.OY, protoName: 'downloadToken')
|
..aInt64(5, _omitFieldNames ? '' : 'timestamp')
|
||||||
..a<$core.List<$core.int>>(6, _omitFieldNames ? '' : 'encryptionKey', $pb.PbFieldType.OY, protoName: 'encryptionKey')
|
..aOS(6, _omitFieldNames ? '' : 'quoteMessageId', protoName: 'quoteMessageId')
|
||||||
..a<$core.List<$core.int>>(7, _omitFieldNames ? '' : 'encryptionMac', $pb.PbFieldType.OY, protoName: 'encryptionMac')
|
..a<$core.List<$core.int>>(7, _omitFieldNames ? '' : 'downloadToken', $pb.PbFieldType.OY, protoName: 'downloadToken')
|
||||||
..a<$core.List<$core.int>>(8, _omitFieldNames ? '' : 'encryptionNonce', $pb.PbFieldType.OY, protoName: 'encryptionNonce')
|
..a<$core.List<$core.int>>(8, _omitFieldNames ? '' : 'encryptionKey', $pb.PbFieldType.OY, protoName: 'encryptionKey')
|
||||||
|
..a<$core.List<$core.int>>(9, _omitFieldNames ? '' : 'encryptionMac', $pb.PbFieldType.OY, protoName: 'encryptionMac')
|
||||||
|
..a<$core.List<$core.int>>(10, _omitFieldNames ? '' : 'encryptionNonce', $pb.PbFieldType.OY, protoName: 'encryptionNonce')
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -574,40 +606,58 @@ class EncryptedContent_Media extends $pb.GeneratedMessage {
|
||||||
void clearRequiresAuthentication() => clearField(4);
|
void clearRequiresAuthentication() => clearField(4);
|
||||||
|
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
$core.List<$core.int> get downloadToken => $_getN(4);
|
$fixnum.Int64 get timestamp => $_getI64(4);
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
set downloadToken($core.List<$core.int> v) { $_setBytes(4, v); }
|
set timestamp($fixnum.Int64 v) { $_setInt64(4, v); }
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
$core.bool hasDownloadToken() => $_has(4);
|
$core.bool hasTimestamp() => $_has(4);
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
void clearDownloadToken() => clearField(5);
|
void clearTimestamp() => clearField(5);
|
||||||
|
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
$core.List<$core.int> get encryptionKey => $_getN(5);
|
$core.String get quoteMessageId => $_getSZ(5);
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
set encryptionKey($core.List<$core.int> v) { $_setBytes(5, v); }
|
set quoteMessageId($core.String v) { $_setString(5, v); }
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
$core.bool hasEncryptionKey() => $_has(5);
|
$core.bool hasQuoteMessageId() => $_has(5);
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
void clearEncryptionKey() => clearField(6);
|
void clearQuoteMessageId() => clearField(6);
|
||||||
|
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
$core.List<$core.int> get encryptionMac => $_getN(6);
|
$core.List<$core.int> get downloadToken => $_getN(6);
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
set encryptionMac($core.List<$core.int> v) { $_setBytes(6, v); }
|
set downloadToken($core.List<$core.int> v) { $_setBytes(6, v); }
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
$core.bool hasEncryptionMac() => $_has(6);
|
$core.bool hasDownloadToken() => $_has(6);
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
void clearEncryptionMac() => clearField(7);
|
void clearDownloadToken() => clearField(7);
|
||||||
|
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
$core.List<$core.int> get encryptionNonce => $_getN(7);
|
$core.List<$core.int> get encryptionKey => $_getN(7);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
set encryptionNonce($core.List<$core.int> v) { $_setBytes(7, v); }
|
set encryptionKey($core.List<$core.int> v) { $_setBytes(7, v); }
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
$core.bool hasEncryptionNonce() => $_has(7);
|
$core.bool hasEncryptionKey() => $_has(7);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
void clearEncryptionNonce() => clearField(8);
|
void clearEncryptionKey() => clearField(8);
|
||||||
|
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
$core.List<$core.int> get encryptionMac => $_getN(8);
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
set encryptionMac($core.List<$core.int> v) { $_setBytes(8, v); }
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
$core.bool hasEncryptionMac() => $_has(8);
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
void clearEncryptionMac() => clearField(9);
|
||||||
|
|
||||||
|
@$pb.TagNumber(10)
|
||||||
|
$core.List<$core.int> get encryptionNonce => $_getN(9);
|
||||||
|
@$pb.TagNumber(10)
|
||||||
|
set encryptionNonce($core.List<$core.int> v) { $_setBytes(9, v); }
|
||||||
|
@$pb.TagNumber(10)
|
||||||
|
$core.bool hasEncryptionNonce() => $_has(9);
|
||||||
|
@$pb.TagNumber(10)
|
||||||
|
void clearEncryptionNonce() => clearField(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
class EncryptedContent_MediaUpdate extends $pb.GeneratedMessage {
|
class EncryptedContent_MediaUpdate extends $pb.GeneratedMessage {
|
||||||
|
|
@ -807,6 +857,7 @@ class EncryptedContent_PushKeys extends $pb.GeneratedMessage {
|
||||||
EncryptedContent_PushKeys_Type? type,
|
EncryptedContent_PushKeys_Type? type,
|
||||||
$fixnum.Int64? keyId,
|
$fixnum.Int64? keyId,
|
||||||
$core.List<$core.int>? key,
|
$core.List<$core.int>? key,
|
||||||
|
$fixnum.Int64? createdAt,
|
||||||
}) {
|
}) {
|
||||||
final $result = create();
|
final $result = create();
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
|
|
@ -818,6 +869,9 @@ class EncryptedContent_PushKeys extends $pb.GeneratedMessage {
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
$result.key = key;
|
$result.key = key;
|
||||||
}
|
}
|
||||||
|
if (createdAt != null) {
|
||||||
|
$result.createdAt = createdAt;
|
||||||
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
EncryptedContent_PushKeys._() : super();
|
EncryptedContent_PushKeys._() : super();
|
||||||
|
|
@ -828,6 +882,7 @@ class EncryptedContent_PushKeys extends $pb.GeneratedMessage {
|
||||||
..e<EncryptedContent_PushKeys_Type>(1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: EncryptedContent_PushKeys_Type.REQUEST, valueOf: EncryptedContent_PushKeys_Type.valueOf, enumValues: EncryptedContent_PushKeys_Type.values)
|
..e<EncryptedContent_PushKeys_Type>(1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: EncryptedContent_PushKeys_Type.REQUEST, valueOf: EncryptedContent_PushKeys_Type.valueOf, enumValues: EncryptedContent_PushKeys_Type.values)
|
||||||
..aInt64(2, _omitFieldNames ? '' : 'keyId', protoName: 'keyId')
|
..aInt64(2, _omitFieldNames ? '' : 'keyId', protoName: 'keyId')
|
||||||
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'key', $pb.PbFieldType.OY)
|
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'key', $pb.PbFieldType.OY)
|
||||||
|
..aInt64(4, _omitFieldNames ? '' : 'createdAt', protoName: 'createdAt')
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -878,6 +933,15 @@ class EncryptedContent_PushKeys extends $pb.GeneratedMessage {
|
||||||
$core.bool hasKey() => $_has(2);
|
$core.bool hasKey() => $_has(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
void clearKey() => clearField(3);
|
void clearKey() => clearField(3);
|
||||||
|
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$fixnum.Int64 get createdAt => $_getI64(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
set createdAt($fixnum.Int64 v) { $_setInt64(3, v); }
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.bool hasCreatedAt() => $_has(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
void clearCreatedAt() => clearField(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
class EncryptedContent_FlameSync extends $pb.GeneratedMessage {
|
class EncryptedContent_FlameSync extends $pb.GeneratedMessage {
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,14 @@ class Message_Type extends $pb.ProtobufEnum {
|
||||||
static const Message_Type PLAINTEXT_CONTENT = Message_Type._(1, _omitEnumNames ? '' : 'PLAINTEXT_CONTENT');
|
static const Message_Type PLAINTEXT_CONTENT = Message_Type._(1, _omitEnumNames ? '' : 'PLAINTEXT_CONTENT');
|
||||||
static const Message_Type CIPHERTEXT = Message_Type._(2, _omitEnumNames ? '' : 'CIPHERTEXT');
|
static const Message_Type CIPHERTEXT = Message_Type._(2, _omitEnumNames ? '' : 'CIPHERTEXT');
|
||||||
static const Message_Type PREKEY_BUNDLE = Message_Type._(3, _omitEnumNames ? '' : 'PREKEY_BUNDLE');
|
static const Message_Type PREKEY_BUNDLE = Message_Type._(3, _omitEnumNames ? '' : 'PREKEY_BUNDLE');
|
||||||
|
static const Message_Type TEST_NOTIFICATION = Message_Type._(4, _omitEnumNames ? '' : 'TEST_NOTIFICATION');
|
||||||
|
|
||||||
static const $core.List<Message_Type> values = <Message_Type> [
|
static const $core.List<Message_Type> values = <Message_Type> [
|
||||||
SENDER_DELIVERY_RECEIPT,
|
SENDER_DELIVERY_RECEIPT,
|
||||||
PLAINTEXT_CONTENT,
|
PLAINTEXT_CONTENT,
|
||||||
CIPHERTEXT,
|
CIPHERTEXT,
|
||||||
PREKEY_BUNDLE,
|
PREKEY_BUNDLE,
|
||||||
|
TEST_NOTIFICATION,
|
||||||
];
|
];
|
||||||
|
|
||||||
static final $core.Map<$core.int, Message_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
|
static final $core.Map<$core.int, Message_Type> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||||
|
|
@ -65,11 +67,13 @@ class EncryptedContent_MessageUpdate_Type extends $pb.ProtobufEnum {
|
||||||
}
|
}
|
||||||
|
|
||||||
class EncryptedContent_Media_Type extends $pb.ProtobufEnum {
|
class EncryptedContent_Media_Type extends $pb.ProtobufEnum {
|
||||||
static const EncryptedContent_Media_Type IMAGE = EncryptedContent_Media_Type._(0, _omitEnumNames ? '' : 'IMAGE');
|
static const EncryptedContent_Media_Type REUPLOAD = EncryptedContent_Media_Type._(0, _omitEnumNames ? '' : 'REUPLOAD');
|
||||||
static const EncryptedContent_Media_Type VIDEO = EncryptedContent_Media_Type._(1, _omitEnumNames ? '' : 'VIDEO');
|
static const EncryptedContent_Media_Type IMAGE = EncryptedContent_Media_Type._(1, _omitEnumNames ? '' : 'IMAGE');
|
||||||
static const EncryptedContent_Media_Type GIF = EncryptedContent_Media_Type._(2, _omitEnumNames ? '' : 'GIF');
|
static const EncryptedContent_Media_Type VIDEO = EncryptedContent_Media_Type._(2, _omitEnumNames ? '' : 'VIDEO');
|
||||||
|
static const EncryptedContent_Media_Type GIF = EncryptedContent_Media_Type._(3, _omitEnumNames ? '' : 'GIF');
|
||||||
|
|
||||||
static const $core.List<EncryptedContent_Media_Type> values = <EncryptedContent_Media_Type> [
|
static const $core.List<EncryptedContent_Media_Type> values = <EncryptedContent_Media_Type> [
|
||||||
|
REUPLOAD,
|
||||||
IMAGE,
|
IMAGE,
|
||||||
VIDEO,
|
VIDEO,
|
||||||
GIF,
|
GIF,
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ const Message_Type$json = {
|
||||||
{'1': 'PLAINTEXT_CONTENT', '2': 1},
|
{'1': 'PLAINTEXT_CONTENT', '2': 1},
|
||||||
{'1': 'CIPHERTEXT', '2': 2},
|
{'1': 'CIPHERTEXT', '2': 2},
|
||||||
{'1': 'PREKEY_BUNDLE', '2': 3},
|
{'1': 'PREKEY_BUNDLE', '2': 3},
|
||||||
|
{'1': 'TEST_NOTIFICATION', '2': 4},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -45,9 +46,10 @@ final $typed_data.Uint8List messageDescriptor = $convert.base64Decode(
|
||||||
'CgdNZXNzYWdlEiEKBHR5cGUYASABKA4yDS5NZXNzYWdlLlR5cGVSBHR5cGUSHAoJcmVjZWlwdE'
|
'CgdNZXNzYWdlEiEKBHR5cGUYASABKA4yDS5NZXNzYWdlLlR5cGVSBHR5cGUSHAoJcmVjZWlwdE'
|
||||||
'lkGAIgASgJUglyZWNlaXB0SWQSLwoQZW5jcnlwdGVkQ29udGVudBgDIAEoDEgAUhBlbmNyeXB0'
|
'lkGAIgASgJUglyZWNlaXB0SWQSLwoQZW5jcnlwdGVkQ29udGVudBgDIAEoDEgAUhBlbmNyeXB0'
|
||||||
'ZWRDb250ZW50iAEBEkIKEHBsYWludGV4dENvbnRlbnQYBCABKAsyES5QbGFpbnRleHRDb250ZW'
|
'ZWRDb250ZW50iAEBEkIKEHBsYWludGV4dENvbnRlbnQYBCABKAsyES5QbGFpbnRleHRDb250ZW'
|
||||||
'50SAFSEHBsYWludGV4dENvbnRlbnSIAQEiXQoEVHlwZRIbChdTRU5ERVJfREVMSVZFUllfUkVD'
|
'50SAFSEHBsYWludGV4dENvbnRlbnSIAQEidAoEVHlwZRIbChdTRU5ERVJfREVMSVZFUllfUkVD'
|
||||||
'RUlQVBAAEhUKEVBMQUlOVEVYVF9DT05URU5UEAESDgoKQ0lQSEVSVEVYVBACEhEKDVBSRUtFWV'
|
'RUlQVBAAEhUKEVBMQUlOVEVYVF9DT05URU5UEAESDgoKQ0lQSEVSVEVYVBACEhEKDVBSRUtFWV'
|
||||||
'9CVU5ETEUQA0ITChFfZW5jcnlwdGVkQ29udGVudEITChFfcGxhaW50ZXh0Q29udGVudA==');
|
'9CVU5ETEUQAxIVChFURVNUX05PVElGSUNBVElPThAEQhMKEV9lbmNyeXB0ZWRDb250ZW50QhMK'
|
||||||
|
'EV9wbGFpbnRleHRDb250ZW50');
|
||||||
|
|
||||||
@$core.Deprecated('Use plaintextContentDescriptor instead')
|
@$core.Deprecated('Use plaintextContentDescriptor instead')
|
||||||
const PlaintextContent$json = {
|
const PlaintextContent$json = {
|
||||||
|
|
@ -126,7 +128,8 @@ const EncryptedContent_TextMessage$json = {
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'senderMessageId', '3': 1, '4': 1, '5': 9, '10': 'senderMessageId'},
|
{'1': 'senderMessageId', '3': 1, '4': 1, '5': 9, '10': 'senderMessageId'},
|
||||||
{'1': 'text', '3': 2, '4': 1, '5': 9, '10': 'text'},
|
{'1': 'text', '3': 2, '4': 1, '5': 9, '10': 'text'},
|
||||||
{'1': 'quoteMessageId', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'quoteMessageId', '17': true},
|
{'1': 'timestamp', '3': 3, '4': 1, '5': 3, '10': 'timestamp'},
|
||||||
|
{'1': 'quoteMessageId', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'quoteMessageId', '17': true},
|
||||||
],
|
],
|
||||||
'8': [
|
'8': [
|
||||||
{'1': '_quoteMessageId'},
|
{'1': '_quoteMessageId'},
|
||||||
|
|
@ -152,14 +155,15 @@ const EncryptedContent_MessageUpdate$json = {
|
||||||
'1': 'MessageUpdate',
|
'1': 'MessageUpdate',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.MessageUpdate.Type', '10': 'type'},
|
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.MessageUpdate.Type', '10': 'type'},
|
||||||
{'1': 'senderMessageId', '3': 2, '4': 1, '5': 9, '10': 'senderMessageId'},
|
{'1': 'senderMessageId', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'senderMessageId', '17': true},
|
||||||
{'1': 'text', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'text', '17': true},
|
{'1': 'multipleSenderMessageIds', '3': 3, '4': 3, '5': 9, '10': 'multipleSenderMessageIds'},
|
||||||
{'1': 'timestamp', '3': 4, '4': 1, '5': 3, '9': 1, '10': 'timestamp', '17': true},
|
{'1': 'text', '3': 4, '4': 1, '5': 9, '9': 1, '10': 'text', '17': true},
|
||||||
|
{'1': 'timestamp', '3': 5, '4': 1, '5': 3, '10': 'timestamp'},
|
||||||
],
|
],
|
||||||
'4': [EncryptedContent_MessageUpdate_Type$json],
|
'4': [EncryptedContent_MessageUpdate_Type$json],
|
||||||
'8': [
|
'8': [
|
||||||
|
{'1': '_senderMessageId'},
|
||||||
{'1': '_text'},
|
{'1': '_text'},
|
||||||
{'1': '_timestamp'},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -181,14 +185,17 @@ const EncryptedContent_Media$json = {
|
||||||
{'1': 'type', '3': 2, '4': 1, '5': 14, '6': '.EncryptedContent.Media.Type', '10': 'type'},
|
{'1': 'type', '3': 2, '4': 1, '5': 14, '6': '.EncryptedContent.Media.Type', '10': 'type'},
|
||||||
{'1': 'displayLimitInMilliseconds', '3': 3, '4': 1, '5': 3, '9': 0, '10': 'displayLimitInMilliseconds', '17': true},
|
{'1': 'displayLimitInMilliseconds', '3': 3, '4': 1, '5': 3, '9': 0, '10': 'displayLimitInMilliseconds', '17': true},
|
||||||
{'1': 'requiresAuthentication', '3': 4, '4': 1, '5': 8, '10': 'requiresAuthentication'},
|
{'1': 'requiresAuthentication', '3': 4, '4': 1, '5': 8, '10': 'requiresAuthentication'},
|
||||||
{'1': 'downloadToken', '3': 5, '4': 1, '5': 12, '9': 1, '10': 'downloadToken', '17': true},
|
{'1': 'timestamp', '3': 5, '4': 1, '5': 3, '10': 'timestamp'},
|
||||||
{'1': 'encryptionKey', '3': 6, '4': 1, '5': 12, '9': 2, '10': 'encryptionKey', '17': true},
|
{'1': 'quoteMessageId', '3': 6, '4': 1, '5': 9, '9': 1, '10': 'quoteMessageId', '17': true},
|
||||||
{'1': 'encryptionMac', '3': 7, '4': 1, '5': 12, '9': 3, '10': 'encryptionMac', '17': true},
|
{'1': 'downloadToken', '3': 7, '4': 1, '5': 12, '9': 2, '10': 'downloadToken', '17': true},
|
||||||
{'1': 'encryptionNonce', '3': 8, '4': 1, '5': 12, '9': 4, '10': 'encryptionNonce', '17': true},
|
{'1': 'encryptionKey', '3': 8, '4': 1, '5': 12, '9': 3, '10': 'encryptionKey', '17': true},
|
||||||
|
{'1': 'encryptionMac', '3': 9, '4': 1, '5': 12, '9': 4, '10': 'encryptionMac', '17': true},
|
||||||
|
{'1': 'encryptionNonce', '3': 10, '4': 1, '5': 12, '9': 5, '10': 'encryptionNonce', '17': true},
|
||||||
],
|
],
|
||||||
'4': [EncryptedContent_Media_Type$json],
|
'4': [EncryptedContent_Media_Type$json],
|
||||||
'8': [
|
'8': [
|
||||||
{'1': '_displayLimitInMilliseconds'},
|
{'1': '_displayLimitInMilliseconds'},
|
||||||
|
{'1': '_quoteMessageId'},
|
||||||
{'1': '_downloadToken'},
|
{'1': '_downloadToken'},
|
||||||
{'1': '_encryptionKey'},
|
{'1': '_encryptionKey'},
|
||||||
{'1': '_encryptionMac'},
|
{'1': '_encryptionMac'},
|
||||||
|
|
@ -200,9 +207,10 @@ const EncryptedContent_Media$json = {
|
||||||
const EncryptedContent_Media_Type$json = {
|
const EncryptedContent_Media_Type$json = {
|
||||||
'1': 'Type',
|
'1': 'Type',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'IMAGE', '2': 0},
|
{'1': 'REUPLOAD', '2': 0},
|
||||||
{'1': 'VIDEO', '2': 1},
|
{'1': 'IMAGE', '2': 1},
|
||||||
{'1': 'GIF', '2': 2},
|
{'1': 'VIDEO', '2': 2},
|
||||||
|
{'1': 'GIF', '2': 3},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -276,11 +284,13 @@ const EncryptedContent_PushKeys$json = {
|
||||||
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.PushKeys.Type', '10': 'type'},
|
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.EncryptedContent.PushKeys.Type', '10': 'type'},
|
||||||
{'1': 'keyId', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'keyId', '17': true},
|
{'1': 'keyId', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'keyId', '17': true},
|
||||||
{'1': 'key', '3': 3, '4': 1, '5': 12, '9': 1, '10': 'key', '17': true},
|
{'1': 'key', '3': 3, '4': 1, '5': 12, '9': 1, '10': 'key', '17': true},
|
||||||
|
{'1': 'createdAt', '3': 4, '4': 1, '5': 3, '9': 2, '10': 'createdAt', '17': true},
|
||||||
],
|
],
|
||||||
'4': [EncryptedContent_PushKeys_Type$json],
|
'4': [EncryptedContent_PushKeys_Type$json],
|
||||||
'8': [
|
'8': [
|
||||||
{'1': '_keyId'},
|
{'1': '_keyId'},
|
||||||
{'1': '_key'},
|
{'1': '_key'},
|
||||||
|
{'1': '_createdAt'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -317,42 +327,47 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
||||||
'N0UmVxdWVzdEgHUg5jb250YWN0UmVxdWVzdIgBARI+CglmbGFtZVN5bmMYCiABKAsyGy5FbmNy'
|
'N0UmVxdWVzdEgHUg5jb250YWN0UmVxdWVzdIgBARI+CglmbGFtZVN5bmMYCiABKAsyGy5FbmNy'
|
||||||
'eXB0ZWRDb250ZW50LkZsYW1lU3luY0gIUglmbGFtZVN5bmOIAQESOwoIcHVzaEtleXMYCyABKA'
|
'eXB0ZWRDb250ZW50LkZsYW1lU3luY0gIUglmbGFtZVN5bmOIAQESOwoIcHVzaEtleXMYCyABKA'
|
||||||
'syGi5FbmNyeXB0ZWRDb250ZW50LlB1c2hLZXlzSAlSCHB1c2hLZXlziAEBEjsKCHJlYWN0aW9u'
|
'syGi5FbmNyeXB0ZWRDb250ZW50LlB1c2hLZXlzSAlSCHB1c2hLZXlziAEBEjsKCHJlYWN0aW9u'
|
||||||
'GAwgASgLMhouRW5jcnlwdGVkQ29udGVudC5SZWFjdGlvbkgKUghyZWFjdGlvbogBARqLAQoLVG'
|
'GAwgASgLMhouRW5jcnlwdGVkQ29udGVudC5SZWFjdGlvbkgKUghyZWFjdGlvbogBARqpAQoLVG'
|
||||||
'V4dE1lc3NhZ2USKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSEgoE'
|
'V4dE1lc3NhZ2USKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSEgoE'
|
||||||
'dGV4dBgCIAEoCVIEdGV4dBIrCg5xdW90ZU1lc3NhZ2VJZBgDIAEoCUgAUg5xdW90ZU1lc3NhZ2'
|
'dGV4dBgCIAEoCVIEdGV4dBIcCgl0aW1lc3RhbXAYAyABKANSCXRpbWVzdGFtcBIrCg5xdW90ZU'
|
||||||
'VJZIgBAUIRCg9fcXVvdGVNZXNzYWdlSWQagQEKCFJlYWN0aW9uEigKD3RhcmdldE1lc3NhZ2VJ'
|
'1lc3NhZ2VJZBgEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUIRCg9fcXVvdGVNZXNzYWdlSWQa'
|
||||||
'ZBgBIAEoCVIPdGFyZ2V0TWVzc2FnZUlkEhkKBWVtb2ppGAIgASgJSABSBWVtb2ppiAEBEhsKBn'
|
'gQEKCFJlYWN0aW9uEigKD3RhcmdldE1lc3NhZ2VJZBgBIAEoCVIPdGFyZ2V0TWVzc2FnZUlkEh'
|
||||||
'JlbW92ZRgDIAEoCEgBUgZyZW1vdmWIAQFCCAoGX2Vtb2ppQgkKB19yZW1vdmUa9QEKDU1lc3Nh'
|
'kKBWVtb2ppGAIgASgJSABSBWVtb2ppiAEBEhsKBnJlbW92ZRgDIAEoCEgBUgZyZW1vdmWIAQFC'
|
||||||
'Z2VVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS'
|
'CAoGX2Vtb2ppQgkKB19yZW1vdmUatwIKDU1lc3NhZ2VVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLk'
|
||||||
'5UeXBlUgR0eXBlEigKD3NlbmRlck1lc3NhZ2VJZBgCIAEoCVIPc2VuZGVyTWVzc2FnZUlkEhcK'
|
'VuY3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS5UeXBlUgR0eXBlEi0KD3NlbmRlck1lc3Nh'
|
||||||
'BHRleHQYAyABKAlIAFIEdGV4dIgBARIhCgl0aW1lc3RhbXAYBCABKANIAVIJdGltZXN0YW1wiA'
|
'Z2VJZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESOgoYbXVsdGlwbGVTZW5kZXJNZXNzYW'
|
||||||
'EBIi0KBFR5cGUSCgoGREVMRVRFEAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCBwoFX3Rl'
|
'dlSWRzGAMgAygJUhhtdWx0aXBsZVNlbmRlck1lc3NhZ2VJZHMSFwoEdGV4dBgEIAEoCUgBUgR0'
|
||||||
'eHRCDAoKX3RpbWVzdGFtcBqgBAoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW'
|
'ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wIi0KBFR5cGUSCgoGREVMRVRFEA'
|
||||||
'5kZXJNZXNzYWdlSWQSMAoEdHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlw'
|
'ASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCEgoQX3NlbmRlck1lc3NhZ2VJZEIHCgVfdGV4'
|
||||||
'ZVIEdHlwZRJDChpkaXNwbGF5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TG'
|
'dBqMBQoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSMA'
|
||||||
'ltaXRJbk1pbGxpc2Vjb25kc4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZy'
|
'oEdHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlwZVIEdHlwZRJDChpkaXNw'
|
||||||
'ZXF1aXJlc0F1dGhlbnRpY2F0aW9uEikKDWRvd25sb2FkVG9rZW4YBSABKAxIAVINZG93bmxvYW'
|
'bGF5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TGltaXRJbk1pbGxpc2Vjb2'
|
||||||
'RUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAYgASgMSAJSDWVuY3J5cHRpb25LZXmIAQESKQoN'
|
'5kc4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZyZXF1aXJlc0F1dGhlbnRp'
|
||||||
'ZW5jcnlwdGlvbk1hYxgHIAEoDEgDUg1lbmNyeXB0aW9uTWFjiAEBEi0KD2VuY3J5cHRpb25Ob2'
|
'Y2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2FnZUlkGA'
|
||||||
'5jZRgIIAEoDEgEUg9lbmNyeXB0aW9uTm9uY2WIAQEiJQoEVHlwZRIJCgVJTUFHRRAAEgkKBVZJ'
|
'YgASgJSAFSDnF1b3RlTWVzc2FnZUlkiAEBEikKDWRvd25sb2FkVG9rZW4YByABKAxIAlINZG93'
|
||||||
'REVPEAESBwoDR0lGEAJCHQobX2Rpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRzQhAKDl9kb3dubG'
|
'bmxvYWRUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAggASgMSANSDWVuY3J5cHRpb25LZXmIAQ'
|
||||||
'9hZFRva2VuQhAKDl9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW9uTWFjQhIKEF9lbmNyeXB0'
|
'ESKQoNZW5jcnlwdGlvbk1hYxgJIAEoDEgEUg1lbmNyeXB0aW9uTWFjiAEBEi0KD2VuY3J5cHRp'
|
||||||
'aW9uTm9uY2UapwEKC01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4yIi5FbmNyeXB0ZWRDb250ZW'
|
'b25Ob25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm9uY2WIAQEiMwoEVHlwZRIMCghSRVVQTE9BRB'
|
||||||
'50Lk1lZGlhVXBkYXRlLlR5cGVSBHR5cGUSKAoPdGFyZ2V0TWVzc2FnZUlkGAIgASgJUg90YXJn'
|
'AAEgkKBUlNQUdFEAESCQoFVklERU8QAhIHCgNHSUYQA0IdChtfZGlzcGxheUxpbWl0SW5NaWxs'
|
||||||
'ZXRNZXNzYWdlSWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JFRBABEhQKEERFQ1JZUF'
|
'aXNlY29uZHNCEQoPX3F1b3RlTWVzc2FnZUlkQhAKDl9kb3dubG9hZFRva2VuQhAKDl9lbmNyeX'
|
||||||
'RJT05fRVJST1IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGAEgASgOMiUuRW5jcnlwdGVk'
|
'B0aW9uS2V5QhAKDl9lbmNyeXB0aW9uTWFjQhIKEF9lbmNyeXB0aW9uTm9uY2UapwEKC01lZGlh'
|
||||||
'Q29udGVudC5Db250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5cGUSCwoHUkVRVUVTVBAAEg'
|
'VXBkYXRlEjYKBHR5cGUYASABKA4yIi5FbmNyeXB0ZWRDb250ZW50Lk1lZGlhVXBkYXRlLlR5cG'
|
||||||
'oKBlJFSkVDVBABEgoKBkFDQ0VQVBACGtIBCg1Db250YWN0VXBkYXRlEjgKBHR5cGUYASABKA4y'
|
'VSBHR5cGUSKAoPdGFyZ2V0TWVzc2FnZUlkGAIgASgJUg90YXJnZXRNZXNzYWdlSWQiNgoEVHlw'
|
||||||
'JC5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlwZRIhCglhdmF0YXJTdm'
|
'ZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JFRBABEhQKEERFQ1JZUFRJT05fRVJST1IQAhp4Cg5Db2'
|
||||||
'cYAiABKAlIAFIJYXZhdGFyU3ZniAEBEiUKC2Rpc3BsYXlOYW1lGAMgASgJSAFSC2Rpc3BsYXlO'
|
'50YWN0UmVxdWVzdBI5CgR0eXBlGAEgASgOMiUuRW5jcnlwdGVkQ29udGVudC5Db250YWN0UmVx'
|
||||||
'YW1liAEBIh8KBFR5cGUSCwoHUkVRVUVTVBAAEgoKBlVQREFURRABQgwKCl9hdmF0YXJTdmdCDg'
|
'dWVzdC5UeXBlUgR0eXBlIisKBFR5cGUSCwoHUkVRVUVTVBAAEgoKBlJFSkVDVBABEgoKBkFDQ0'
|
||||||
'oMX2Rpc3BsYXlOYW1lGqQBCghQdXNoS2V5cxIzCgR0eXBlGAEgASgOMh8uRW5jcnlwdGVkQ29u'
|
'VQVBACGtIBCg1Db250YWN0VXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50'
|
||||||
'dGVudC5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGAIgASgDSABSBWtleUlkiAEBEhUKA2'
|
'LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlwZRIhCglhdmF0YXJTdmcYAiABKAlIAFIJYXZhdGFyU3'
|
||||||
'tleRgDIAEoDEgBUgNrZXmIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCCAoG'
|
'ZniAEBEiUKC2Rpc3BsYXlOYW1lGAMgASgJSAFSC2Rpc3BsYXlOYW1liAEBIh8KBFR5cGUSCwoH'
|
||||||
'X2tleUlkQgYKBF9rZXkahwEKCUZsYW1lU3luYxIiCgxmbGFtZUNvdW50ZXIYASABKANSDGZsYW'
|
'UkVRVUVTVBAAEgoKBlVQREFURRABQgwKCl9hdmF0YXJTdmdCDgoMX2Rpc3BsYXlOYW1lGtUBCg'
|
||||||
'1lQ291bnRlchI2ChZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3Vu'
|
'hQdXNoS2V5cxIzCgR0eXBlGAEgASgOMh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy5UeXBl'
|
||||||
'dGVyQ2hhbmdlEh4KCmJlc3RGcmllbmQYAyABKAhSCmJlc3RGcmllbmRCCgoIX2dyb3VwSWRCFw'
|
'UgR0eXBlEhkKBWtleUlkGAIgASgDSABSBWtleUlkiAEBEhUKA2tleRgDIAEoDEgBUgNrZXmIAQ'
|
||||||
'oVX3NlbmRlclByb2ZpbGVDb3VudGVyQg4KDF90ZXh0TWVzc2FnZUIQCg5fbWVzc2FnZVVwZGF0'
|
'ESIQoJY3JlYXRlZEF0GAQgASgDSAJSCWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUVVFU1QQ'
|
||||||
'ZUIICgZfbWVkaWFCDgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YW'
|
'ABIKCgZVUERBVEUQAUIICgZfa2V5SWRCBgoEX2tleUIMCgpfY3JlYXRlZEF0GocBCglGbGFtZV'
|
||||||
'N0UmVxdWVzdEIMCgpfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb24=');
|
'N5bmMSIgoMZmxhbWVDb3VudGVyGAEgASgDUgxmbGFtZUNvdW50ZXISNgoWbGFzdEZsYW1lQ291'
|
||||||
|
'bnRlckNoYW5nZRgCIAEoA1IWbGFzdEZsYW1lQ291bnRlckNoYW5nZRIeCgpiZXN0RnJpZW5kGA'
|
||||||
|
'MgASgIUgpiZXN0RnJpZW5kQgoKCF9ncm91cElkQhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIO'
|
||||||
|
'CgxfdGV4dE1lc3NhZ2VCEAoOX21lc3NhZ2VVcGRhdGVCCAoGX21lZGlhQg4KDF9tZWRpYVVwZG'
|
||||||
|
'F0ZUIQCg5fY29udGFjdFVwZGF0ZUIRCg9fY29udGFjdFJlcXVlc3RCDAoKX2ZsYW1lU3luY0IL'
|
||||||
|
'CglfcHVzaEtleXNCCwoJX3JlYWN0aW9u');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ class EncryptedPushNotification extends $pb.GeneratedMessage {
|
||||||
class PushNotification extends $pb.GeneratedMessage {
|
class PushNotification extends $pb.GeneratedMessage {
|
||||||
factory PushNotification({
|
factory PushNotification({
|
||||||
PushKind? kind,
|
PushKind? kind,
|
||||||
$fixnum.Int64? messageId,
|
$core.String? messageId,
|
||||||
$core.String? reactionContent,
|
$core.String? reactionContent,
|
||||||
}) {
|
}) {
|
||||||
final $result = create();
|
final $result = create();
|
||||||
|
|
@ -134,7 +134,7 @@ class PushNotification extends $pb.GeneratedMessage {
|
||||||
|
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PushNotification', createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PushNotification', createEmptyInstance: create)
|
||||||
..e<PushKind>(1, _omitFieldNames ? '' : 'kind', $pb.PbFieldType.OE, defaultOrMaker: PushKind.reaction, valueOf: PushKind.valueOf, enumValues: PushKind.values)
|
..e<PushKind>(1, _omitFieldNames ? '' : 'kind', $pb.PbFieldType.OE, defaultOrMaker: PushKind.reaction, valueOf: PushKind.valueOf, enumValues: PushKind.values)
|
||||||
..aInt64(2, _omitFieldNames ? '' : 'messageId', protoName: 'messageId')
|
..aOS(2, _omitFieldNames ? '' : 'messageId', protoName: 'messageId')
|
||||||
..aOS(3, _omitFieldNames ? '' : 'reactionContent', protoName: 'reactionContent')
|
..aOS(3, _omitFieldNames ? '' : 'reactionContent', protoName: 'reactionContent')
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
@ -170,9 +170,9 @@ class PushNotification extends $pb.GeneratedMessage {
|
||||||
void clearKind() => clearField(1);
|
void clearKind() => clearField(1);
|
||||||
|
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$fixnum.Int64 get messageId => $_getI64(1);
|
$core.String get messageId => $_getSZ(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
set messageId($fixnum.Int64 v) { $_setInt64(1, v); }
|
set messageId($core.String v) { $_setString(1, v); }
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$core.bool hasMessageId() => $_has(1);
|
$core.bool hasMessageId() => $_has(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
|
|
@ -237,7 +237,7 @@ class PushUser extends $pb.GeneratedMessage {
|
||||||
$fixnum.Int64? userId,
|
$fixnum.Int64? userId,
|
||||||
$core.String? displayName,
|
$core.String? displayName,
|
||||||
$core.bool? blocked,
|
$core.bool? blocked,
|
||||||
$fixnum.Int64? lastMessageId,
|
$core.String? lastMessageId,
|
||||||
$core.Iterable<PushKey>? pushKeys,
|
$core.Iterable<PushKey>? pushKeys,
|
||||||
}) {
|
}) {
|
||||||
final $result = create();
|
final $result = create();
|
||||||
|
|
@ -266,7 +266,7 @@ class PushUser extends $pb.GeneratedMessage {
|
||||||
..aInt64(1, _omitFieldNames ? '' : 'userId', protoName: 'userId')
|
..aInt64(1, _omitFieldNames ? '' : 'userId', protoName: 'userId')
|
||||||
..aOS(2, _omitFieldNames ? '' : 'displayName', protoName: 'displayName')
|
..aOS(2, _omitFieldNames ? '' : 'displayName', protoName: 'displayName')
|
||||||
..aOB(3, _omitFieldNames ? '' : 'blocked')
|
..aOB(3, _omitFieldNames ? '' : 'blocked')
|
||||||
..aInt64(4, _omitFieldNames ? '' : 'lastMessageId', protoName: 'lastMessageId')
|
..aOS(4, _omitFieldNames ? '' : 'lastMessageId', protoName: 'lastMessageId')
|
||||||
..pc<PushKey>(5, _omitFieldNames ? '' : 'pushKeys', $pb.PbFieldType.PM, protoName: 'pushKeys', subBuilder: PushKey.create)
|
..pc<PushKey>(5, _omitFieldNames ? '' : 'pushKeys', $pb.PbFieldType.PM, protoName: 'pushKeys', subBuilder: PushKey.create)
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
@ -320,9 +320,9 @@ class PushUser extends $pb.GeneratedMessage {
|
||||||
void clearBlocked() => clearField(3);
|
void clearBlocked() => clearField(3);
|
||||||
|
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
$fixnum.Int64 get lastMessageId => $_getI64(3);
|
$core.String get lastMessageId => $_getSZ(3);
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
set lastMessageId($fixnum.Int64 v) { $_setInt64(3, v); }
|
set lastMessageId($core.String v) { $_setString(3, v); }
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
$core.bool hasLastMessageId() => $_has(3);
|
$core.bool hasLastMessageId() => $_has(3);
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ const PushNotification$json = {
|
||||||
'1': 'PushNotification',
|
'1': 'PushNotification',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'kind', '3': 1, '4': 1, '5': 14, '6': '.PushKind', '10': 'kind'},
|
{'1': 'kind', '3': 1, '4': 1, '5': 14, '6': '.PushKind', '10': 'kind'},
|
||||||
{'1': 'messageId', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'messageId', '17': true},
|
{'1': 'messageId', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'messageId', '17': true},
|
||||||
{'1': 'reactionContent', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'reactionContent', '17': true},
|
{'1': 'reactionContent', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'reactionContent', '17': true},
|
||||||
],
|
],
|
||||||
'8': [
|
'8': [
|
||||||
|
|
@ -76,7 +76,7 @@ const PushNotification$json = {
|
||||||
/// Descriptor for `PushNotification`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `PushNotification`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List pushNotificationDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List pushNotificationDescriptor = $convert.base64Decode(
|
||||||
'ChBQdXNoTm90aWZpY2F0aW9uEh0KBGtpbmQYASABKA4yCS5QdXNoS2luZFIEa2luZBIhCgltZX'
|
'ChBQdXNoTm90aWZpY2F0aW9uEh0KBGtpbmQYASABKA4yCS5QdXNoS2luZFIEa2luZBIhCgltZX'
|
||||||
'NzYWdlSWQYAiABKANIAFIJbWVzc2FnZUlkiAEBEi0KD3JlYWN0aW9uQ29udGVudBgDIAEoCUgB'
|
'NzYWdlSWQYAiABKAlIAFIJbWVzc2FnZUlkiAEBEi0KD3JlYWN0aW9uQ29udGVudBgDIAEoCUgB'
|
||||||
'Ug9yZWFjdGlvbkNvbnRlbnSIAQFCDAoKX21lc3NhZ2VJZEISChBfcmVhY3Rpb25Db250ZW50');
|
'Ug9yZWFjdGlvbkNvbnRlbnSIAQFCDAoKX21lc3NhZ2VJZEISChBfcmVhY3Rpb25Db250ZW50');
|
||||||
|
|
||||||
@$core.Deprecated('Use pushUsersDescriptor instead')
|
@$core.Deprecated('Use pushUsersDescriptor instead')
|
||||||
|
|
@ -98,7 +98,7 @@ const PushUser$json = {
|
||||||
{'1': 'userId', '3': 1, '4': 1, '5': 3, '10': 'userId'},
|
{'1': 'userId', '3': 1, '4': 1, '5': 3, '10': 'userId'},
|
||||||
{'1': 'displayName', '3': 2, '4': 1, '5': 9, '10': 'displayName'},
|
{'1': 'displayName', '3': 2, '4': 1, '5': 9, '10': 'displayName'},
|
||||||
{'1': 'blocked', '3': 3, '4': 1, '5': 8, '10': 'blocked'},
|
{'1': 'blocked', '3': 3, '4': 1, '5': 8, '10': 'blocked'},
|
||||||
{'1': 'lastMessageId', '3': 4, '4': 1, '5': 3, '9': 0, '10': 'lastMessageId', '17': true},
|
{'1': 'lastMessageId', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'lastMessageId', '17': true},
|
||||||
{'1': 'pushKeys', '3': 5, '4': 3, '5': 11, '6': '.PushKey', '10': 'pushKeys'},
|
{'1': 'pushKeys', '3': 5, '4': 3, '5': 11, '6': '.PushKey', '10': 'pushKeys'},
|
||||||
],
|
],
|
||||||
'8': [
|
'8': [
|
||||||
|
|
@ -110,7 +110,7 @@ const PushUser$json = {
|
||||||
final $typed_data.Uint8List pushUserDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List pushUserDescriptor = $convert.base64Decode(
|
||||||
'CghQdXNoVXNlchIWCgZ1c2VySWQYASABKANSBnVzZXJJZBIgCgtkaXNwbGF5TmFtZRgCIAEoCV'
|
'CghQdXNoVXNlchIWCgZ1c2VySWQYASABKANSBnVzZXJJZBIgCgtkaXNwbGF5TmFtZRgCIAEoCV'
|
||||||
'ILZGlzcGxheU5hbWUSGAoHYmxvY2tlZBgDIAEoCFIHYmxvY2tlZBIpCg1sYXN0TWVzc2FnZUlk'
|
'ILZGlzcGxheU5hbWUSGAoHYmxvY2tlZBgDIAEoCFIHYmxvY2tlZBIpCg1sYXN0TWVzc2FnZUlk'
|
||||||
'GAQgASgDSABSDWxhc3RNZXNzYWdlSWSIAQESJAoIcHVzaEtleXMYBSADKAsyCC5QdXNoS2V5Ug'
|
'GAQgASgJSABSDWxhc3RNZXNzYWdlSWSIAQESJAoIcHVzaEtleXMYBSADKAsyCC5QdXNoS2V5Ug'
|
||||||
'hwdXNoS2V5c0IQCg5fbGFzdE1lc3NhZ2VJZA==');
|
'hwdXNoS2V5c0IQCg5fbGFzdE1lc3NhZ2VJZA==');
|
||||||
|
|
||||||
@$core.Deprecated('Use pushKeyDescriptor instead')
|
@$core.Deprecated('Use pushKeyDescriptor instead')
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ message Message {
|
||||||
PLAINTEXT_CONTENT = 1;
|
PLAINTEXT_CONTENT = 1;
|
||||||
CIPHERTEXT = 2;
|
CIPHERTEXT = 2;
|
||||||
PREKEY_BUNDLE = 3;
|
PREKEY_BUNDLE = 3;
|
||||||
|
TEST_NOTIFICATION = 4;
|
||||||
}
|
}
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
string receiptId = 2;
|
string receiptId = 2;
|
||||||
|
|
@ -46,7 +47,8 @@ message EncryptedContent {
|
||||||
message TextMessage {
|
message TextMessage {
|
||||||
string senderMessageId = 1;
|
string senderMessageId = 1;
|
||||||
string text = 2;
|
string text = 2;
|
||||||
optional string quoteMessageId = 3;
|
int64 timestamp = 3;
|
||||||
|
optional string quoteMessageId = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Reaction {
|
message Reaction {
|
||||||
|
|
@ -62,27 +64,31 @@ message EncryptedContent {
|
||||||
OPENED = 2;
|
OPENED = 2;
|
||||||
}
|
}
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
string senderMessageId = 2;
|
optional string senderMessageId = 2;
|
||||||
optional string text = 3;
|
repeated string multipleSenderMessageIds = 3;
|
||||||
optional int64 timestamp = 4;
|
optional string text = 4;
|
||||||
|
int64 timestamp = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Media {
|
message Media {
|
||||||
enum Type {
|
enum Type {
|
||||||
IMAGE = 0;
|
REUPLOAD = 0;
|
||||||
VIDEO = 1;
|
IMAGE = 1;
|
||||||
GIF = 2;
|
VIDEO = 2;
|
||||||
|
GIF = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
string senderMessageId = 1;
|
string senderMessageId = 1;
|
||||||
Type type = 2;
|
Type type = 2;
|
||||||
optional int64 displayLimitInMilliseconds = 3;
|
optional int64 displayLimitInMilliseconds = 3;
|
||||||
bool requiresAuthentication = 4;
|
bool requiresAuthentication = 4;
|
||||||
|
int64 timestamp = 5;
|
||||||
|
optional string quoteMessageId = 6;
|
||||||
|
|
||||||
optional bytes downloadToken = 5;
|
optional bytes downloadToken = 7;
|
||||||
optional bytes encryptionKey = 6;
|
optional bytes encryptionKey = 8;
|
||||||
optional bytes encryptionMac = 7;
|
optional bytes encryptionMac = 9;
|
||||||
optional bytes encryptionNonce = 8;
|
optional bytes encryptionNonce = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MediaUpdate {
|
message MediaUpdate {
|
||||||
|
|
@ -124,6 +130,7 @@ message EncryptedContent {
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
optional int64 keyId = 2;
|
optional int64 keyId = 2;
|
||||||
optional bytes key = 3;
|
optional bytes key = 3;
|
||||||
|
optional int64 createdAt = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FlameSync {
|
message FlameSync {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ enum PushKind {
|
||||||
|
|
||||||
message PushNotification {
|
message PushNotification {
|
||||||
PushKind kind = 1;
|
PushKind kind = 1;
|
||||||
optional int64 messageId = 2;
|
optional string messageId = 2;
|
||||||
optional string reactionContent = 3;
|
optional string reactionContent = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ message PushUser {
|
||||||
int64 userId = 1;
|
int64 userId = 1;
|
||||||
string displayName = 2;
|
string displayName = 2;
|
||||||
bool blocked = 3;
|
bool blocked = 3;
|
||||||
optional int64 lastMessageId = 4;
|
optional string lastMessageId = 4;
|
||||||
repeated PushKey pushKeys = 5;
|
repeated PushKey pushKeys = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ class ApiService {
|
||||||
_channel = null;
|
_channel = null;
|
||||||
isAuthenticated = false;
|
isAuthenticated = false;
|
||||||
globalCallbackConnectionState(isConnected: false);
|
globalCallbackConnectionState(isConnected: false);
|
||||||
await twonlyDB.messagesDao.resetPendingDownloadState();
|
await twonlyDB.mediaFilesDao.resetPendingDownloadState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startReconnectionTimer() async {
|
Future<void> startReconnectionTimer() async {
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ Future<void> handleDownloadStatusUpdateInternal(
|
||||||
|
|
||||||
Mutex protectDownload = Mutex();
|
Mutex protectDownload = Mutex();
|
||||||
|
|
||||||
Future<void> startDownloadMedia(Message message, bool force) async {
|
Future<void> startDownloadMedia(MediaFile media, bool force) async {
|
||||||
Log.info(
|
Log.info(
|
||||||
'Download blocked for ${message.messageId} because of network state.',
|
'Download blocked for ${message.messageId} because of network state.',
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,163 +1,153 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/json/message_old.dart';
|
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
||||||
as pb;
|
as pb;
|
||||||
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
|
||||||
import 'package:twonly/src/services/api/server_messages.dart'
|
|
||||||
show messageGetsAck;
|
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
|
|
||||||
final lockRetransmission = Mutex();
|
final lockRetransmission = Mutex();
|
||||||
|
|
||||||
Future<void> tryTransmitMessages() async {
|
Future<void> tryTransmitMessages() async {
|
||||||
return lockRetransmission.protect(() async {
|
return lockRetransmission.protect(() async {
|
||||||
final retransIds =
|
final receipts = await twonlyDB.receiptsDao.getReceiptsNotAckByServer();
|
||||||
await twonlyDB.messageRetransmissionDao.getRetransmitAbleMessages();
|
|
||||||
|
|
||||||
Log.info('Retransmitting ${retransIds.length} text messages');
|
if (receipts.isEmpty) return;
|
||||||
|
|
||||||
if (retransIds.isEmpty) return;
|
Log.info('Reuploading ${receipts.length} messages to the server.');
|
||||||
|
|
||||||
for (final retransId in retransIds) {
|
for (final receipt in receipts) {
|
||||||
await sendRetransmitMessage(retransId);
|
await tryToSendCompleteMessage(receipt: receipt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> tryToSendCompleteMessage(String receiptId) async {
|
Future<void> tryToSendCompleteMessage({
|
||||||
|
String? receiptId,
|
||||||
|
Receipt? receipt,
|
||||||
|
bool reupload = false,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final retrans = await twonlyDB.messageRetransmissionDao
|
if (receiptId == null && receipt == null) return;
|
||||||
.getRetransmissionById(retransId)
|
if (receipt == null) {
|
||||||
.getSingleOrNull();
|
receipt = await twonlyDB.receiptsDao.getReceiptById(receiptId!);
|
||||||
|
if (receipt == null) {
|
||||||
/// SET THE Message().receiptID !!!!!!!
|
Log.error('Receipt $receiptId not found.');
|
||||||
/// ALSO THE encryptedContent is NOT YET ENCRYPTED!
|
|
||||||
|
|
||||||
if (retrans == null) {
|
|
||||||
Log.error('$retransId not found in database');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retrans.acknowledgeByServerAt != null) {
|
|
||||||
Log.error('$retransId message already retransmitted');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
receiptId = receipt.receiptId;
|
||||||
|
|
||||||
final json = MessageJson.fromJson(
|
if (reupload) {
|
||||||
jsonDecode(
|
await twonlyDB.receiptsDao.updateReceipt(
|
||||||
utf8.decode(
|
receiptId,
|
||||||
gzip.decode(retrans.plaintextContent),
|
const ReceiptsCompanion(
|
||||||
|
ackByServerAt: Value(null),
|
||||||
),
|
),
|
||||||
) as Map<String, dynamic>,
|
|
||||||
);
|
|
||||||
|
|
||||||
Log.info('Retransmitting $retransId: ${json.kind} to ${retrans.contactId}');
|
|
||||||
|
|
||||||
final contact = await twonlyDB.contactsDao
|
|
||||||
.getContactByUserId(retrans.contactId)
|
|
||||||
.getSingleOrNull();
|
|
||||||
if (contact == null || contact.deleted) {
|
|
||||||
Log.warn('Contact deleted $retransId or not found in database.');
|
|
||||||
await twonlyDB.messageRetransmissionDao
|
|
||||||
.deleteRetransmissionById(retransId);
|
|
||||||
if (retrans.messageId != null) {
|
|
||||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
|
||||||
retrans.messageId!,
|
|
||||||
const MessagesCompanion(errorWhileSending: Value(true)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (receipt.ackByServerAt != null) {
|
||||||
|
Log.error('$receiptId message already uploaded!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final encryptedBytes = await signalEncryptMessage(
|
Log.info('Uploading $receiptId (Message to ${receipt.contactId})');
|
||||||
retrans.contactId,
|
|
||||||
retrans.plaintextContent,
|
final message = pb.Message.fromBuffer(receipt.message)
|
||||||
|
..receiptId = receiptId;
|
||||||
|
|
||||||
|
final encryptedContent =
|
||||||
|
pb.EncryptedContent.fromBuffer(message.encryptedContent);
|
||||||
|
|
||||||
|
var pushData = await getPushDataFromEncryptedContent(
|
||||||
|
receipt.contactId,
|
||||||
|
receipt.messageId,
|
||||||
|
encryptedContent,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (encryptedBytes == null) {
|
if (message.type == pb.Message_Type.TEST_NOTIFICATION) {
|
||||||
|
pushData = (PushNotification()..kind = PushKind.testNotification)
|
||||||
|
.writeToBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.type == pb.Message_Type.CIPHERTEXT) {
|
||||||
|
final cipherText = await signalEncryptMessage(
|
||||||
|
receipt.contactId,
|
||||||
|
Uint8List.fromList(message.encryptedContent),
|
||||||
|
);
|
||||||
|
if (cipherText == null) {
|
||||||
Log.error('Could not encrypt the message. Aborting and trying again.');
|
Log.error('Could not encrypt the message. Aborting and trying again.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
message.encryptedContent = cipherText.serialize();
|
||||||
final encryptedHash = (await Sha256().hash(encryptedBytes)).bytes;
|
switch (cipherText.getType()) {
|
||||||
|
case CiphertextMessage.prekeyType:
|
||||||
await twonlyDB.messageRetransmissionDao.updateRetransmission(
|
message.type = pb.Message_Type.PREKEY_BUNDLE;
|
||||||
retransId,
|
case CiphertextMessage.whisperType:
|
||||||
MessageRetransmissionsCompanion(
|
message.type = pb.Message_Type.CIPHERTEXT;
|
||||||
encryptedHash: Value(Uint8List.fromList(encryptedHash)),
|
default:
|
||||||
),
|
Log.error('Invalid ciphertext type: ${cipherText.getType()}.');
|
||||||
);
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final resp = await apiService.sendTextMessage(
|
final resp = await apiService.sendTextMessage(
|
||||||
retrans.contactId,
|
receipt.contactId,
|
||||||
encryptedBytes,
|
message.writeToBuffer(),
|
||||||
retrans.pushData,
|
pushData,
|
||||||
);
|
);
|
||||||
|
|
||||||
var retry = true;
|
|
||||||
|
|
||||||
if (resp.isError) {
|
if (resp.isError) {
|
||||||
Log.error('Could not retransmit message.');
|
Log.error('Could not transmit message $receiptId got ${resp.error}.');
|
||||||
if (resp.error == ErrorCode.UserIdNotFound) {
|
if (resp.error == ErrorCode.UserIdNotFound) {
|
||||||
retry = false;
|
await twonlyDB.receiptsDao.deleteReceipt(receiptId);
|
||||||
if (retrans.messageId != null) {
|
|
||||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
|
||||||
retrans.messageId!,
|
|
||||||
const MessagesCompanion(errorWhileSending: Value(true)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await twonlyDB.contactsDao.updateContact(
|
await twonlyDB.contactsDao.updateContact(
|
||||||
retrans.contactId,
|
receipt.contactId,
|
||||||
const ContactsCompanion(deleted: Value(true)),
|
const ContactsCompanion(deleted: Value(true)),
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.isSuccess) {
|
if (resp.isSuccess) {
|
||||||
retry = false;
|
if (receipt.messageId != null) {
|
||||||
if (retrans.messageId != null) {
|
await twonlyDB.messagesDao.updateMessageId(
|
||||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
receipt.messageId!,
|
||||||
retrans.messageId!,
|
|
||||||
const MessagesCompanion(
|
const MessagesCompanion(
|
||||||
acknowledgeByServer: Value(true),
|
ackByServer: Value(true),
|
||||||
errorWhileSending: Value(false),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
if (!receipt.contactWillSendsReceipt) {
|
||||||
|
await twonlyDB.receiptsDao.deleteReceipt(receiptId);
|
||||||
if (!retry) {
|
|
||||||
if (!messageGetsAck(json.kind)) {
|
|
||||||
await twonlyDB.messageRetransmissionDao
|
|
||||||
.deleteRetransmissionById(retransId);
|
|
||||||
} else {
|
} else {
|
||||||
await twonlyDB.messageRetransmissionDao.updateRetransmission(
|
await twonlyDB.receiptsDao.updateReceipt(
|
||||||
retransId,
|
receiptId,
|
||||||
MessageRetransmissionsCompanion(
|
ReceiptsCompanion(
|
||||||
acknowledgeByServerAt: Value(DateTime.now()),
|
ackByServerAt: Value(DateTime.now()),
|
||||||
retryCount: Value(retrans.retryCount + 1),
|
retryCount: Value(receipt.retryCount + 1),
|
||||||
lastRetry: Value(DateTime.now()),
|
lastRetry: Value(DateTime.now()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error('error resending message: $e');
|
Log.error('Unknown Error when sending message: $e');
|
||||||
await twonlyDB.messageRetransmissionDao.deleteRetransmissionById(retransId);
|
if (receiptId != null) {
|
||||||
|
await twonlyDB.receiptsDao.deleteReceipt(receiptId);
|
||||||
|
}
|
||||||
|
if (receipt != null) {
|
||||||
|
await twonlyDB.receiptsDao.deleteReceipt(receipt.receiptId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,78 +167,31 @@ Future<void> sendCipherText(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (receipt != null) {
|
if (receipt != null) {
|
||||||
await tryToSendCompleteMessage(receipt.receiptId);
|
await tryToSendCompleteMessage(receipt: receipt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> sendTextMessage(
|
|
||||||
// int target,
|
|
||||||
// TextMessageContent content,
|
|
||||||
// PushNotification? pushNotification,
|
|
||||||
// ) async {
|
|
||||||
// final messageSendAt = DateTime.now();
|
|
||||||
// DateTime? openedAt;
|
|
||||||
|
|
||||||
// if (pushNotification != null && pushNotification.hasReactionContent()) {
|
|
||||||
// openedAt = DateTime.now();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final messageId = await twonlyDB.messagesDao.insertMessage(
|
|
||||||
// MessagesCompanion(
|
|
||||||
// contactId: Value(target),
|
|
||||||
// kind: const Value(MessageKind.textMessage),
|
|
||||||
// sendAt: Value(messageSendAt),
|
|
||||||
// responseToOtherMessageId: Value(content.responseToMessageId),
|
|
||||||
// responseToMessageId: Value(content.responseToOtherMessageId),
|
|
||||||
// downloadState: const Value(DownloadState.downloaded),
|
|
||||||
// openedAt: Value(openedAt),
|
|
||||||
// contentJson: Value(
|
|
||||||
// jsonEncode(content.toJson()),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (messageId == null) return;
|
|
||||||
|
|
||||||
// if (pushNotification != null && !pushNotification.hasReactionContent()) {
|
|
||||||
// pushNotification.messageId = Int64(messageId);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final msg = MessageJson(
|
|
||||||
// kind: MessageKind.textMessage,
|
|
||||||
// messageSenderId: messageId,
|
|
||||||
// content: content,
|
|
||||||
// timestamp: messageSendAt,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// await encryptAndSendMessageAsync(
|
|
||||||
// messageId,
|
|
||||||
// target,
|
|
||||||
// msg,
|
|
||||||
// pushNotification: pushNotification,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
Future<void> notifyContactAboutOpeningMessage(
|
Future<void> notifyContactAboutOpeningMessage(
|
||||||
int fromUserId,
|
int contactId,
|
||||||
List<int> messageOtherIds,
|
List<String> messageOtherIds,
|
||||||
) async {
|
) async {
|
||||||
var biggestMessageId = messageOtherIds.first;
|
var biggestMessageId = messageOtherIds.first;
|
||||||
|
|
||||||
for (final messageOtherId in messageOtherIds) {
|
for (final messageOtherId in messageOtherIds) {
|
||||||
if (messageOtherId > biggestMessageId) biggestMessageId = messageOtherId;
|
if (isUUIDNewer(messageOtherId, biggestMessageId)) {
|
||||||
await encryptAndSendMessageAsync(
|
biggestMessageId = messageOtherId;
|
||||||
null,
|
}
|
||||||
fromUserId,
|
}
|
||||||
MessageJson(
|
await sendCipherText(
|
||||||
kind: MessageKind.opened,
|
contactId,
|
||||||
messageReceiverId: messageOtherId,
|
pb.EncryptedContent(
|
||||||
content: MessageContent(),
|
messageUpdate: pb.EncryptedContent_MessageUpdate(
|
||||||
timestamp: DateTime.now(),
|
type: pb.EncryptedContent_MessageUpdate_Type.OPENED,
|
||||||
|
multipleSenderMessageIds: messageOtherIds,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
await updateLastMessageId(contactId, biggestMessageId);
|
||||||
await updateLastMessageId(fromUserId, biggestMessageId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> notifyContactsAboutProfileChange({int? onlyToContact}) async {
|
Future<void> notifyContactsAboutProfileChange({int? onlyToContact}) async {
|
||||||
|
|
@ -256,11 +199,13 @@ Future<void> notifyContactsAboutProfileChange({int? onlyToContact}) async {
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
if (user.avatarSvg == null) return;
|
if (user.avatarSvg == null) return;
|
||||||
|
|
||||||
final encryptedContent = pb.EncryptedContent()
|
final encryptedContent = pb.EncryptedContent(
|
||||||
..contactUpdate = (pb.EncryptedContent_ContactUpdate()
|
contactUpdate: pb.EncryptedContent_ContactUpdate(
|
||||||
..type = pb.EncryptedContent_ContactUpdate_Type.UPDATE
|
type: pb.EncryptedContent_ContactUpdate_Type.UPDATE,
|
||||||
..avatarSvg = user.avatarSvg!
|
avatarSvg: user.avatarSvg,
|
||||||
..displayName = user.displayName);
|
displayName: user.displayName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (onlyToContact != null) {
|
if (onlyToContact != null) {
|
||||||
await sendCipherText(onlyToContact, encryptedContent);
|
await sendCipherText(onlyToContact, encryptedContent);
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ Future<void> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
Log.info(
|
Log.info(
|
||||||
'Got decryption error: ${message.plaintextContent.decryptionErrorMessage.type} for $receiptId',
|
'Got decryption error: ${message.plaintextContent.decryptionErrorMessage.type} for $receiptId',
|
||||||
);
|
);
|
||||||
await tryToSendCompleteMessage(receiptId);
|
await tryToSendCompleteMessage(receiptId: receiptId, reupload: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Message_Type.CIPHERTEXT:
|
case Message_Type.CIPHERTEXT:
|
||||||
|
|
@ -97,8 +97,10 @@ Future<void> handleNewMessage(int fromUserId, Uint8List body) async {
|
||||||
contactWillSendsReceipt: const Value(false),
|
contactWillSendsReceipt: const Value(false),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tryToSendCompleteMessage(receiptId);
|
await tryToSendCompleteMessage(receiptId: receiptId);
|
||||||
}
|
}
|
||||||
|
case Message_Type.TEST_NOTIFICATION:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/services/api/utils.dart';
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
Future<void> handleContactRequest(
|
Future<void> handleContactRequest(
|
||||||
|
|
@ -17,6 +18,7 @@ Future<void> handleContactRequest(
|
||||||
) async {
|
) async {
|
||||||
switch (contactRequest.type) {
|
switch (contactRequest.type) {
|
||||||
case EncryptedContent_ContactRequest_Type.REQUEST:
|
case EncryptedContent_ContactRequest_Type.REQUEST:
|
||||||
|
Log.info('Got a contact request from $fromUserId');
|
||||||
// Request the username by the server so an attacker can not
|
// Request the username by the server so an attacker can not
|
||||||
// forge the displayed username in the contact request
|
// forge the displayed username in the contact request
|
||||||
final username = await apiService.getUsername(fromUserId);
|
final username = await apiService.getUsername(fromUserId);
|
||||||
|
|
@ -33,6 +35,7 @@ Future<void> handleContactRequest(
|
||||||
}
|
}
|
||||||
await setupNotificationWithUsers();
|
await setupNotificationWithUsers();
|
||||||
case EncryptedContent_ContactRequest_Type.ACCEPT:
|
case EncryptedContent_ContactRequest_Type.ACCEPT:
|
||||||
|
Log.info('Got a contact accept from $fromUserId');
|
||||||
await twonlyDB.contactsDao.updateContact(
|
await twonlyDB.contactsDao.updateContact(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
const ContactsCompanion(
|
const ContactsCompanion(
|
||||||
|
|
@ -41,6 +44,7 @@ Future<void> handleContactRequest(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case EncryptedContent_ContactRequest_Type.REJECT:
|
case EncryptedContent_ContactRequest_Type.REJECT:
|
||||||
|
Log.info('Got a contact reject from $fromUserId');
|
||||||
await twonlyDB.contactsDao.deleteContactByUserId(fromUserId);
|
await twonlyDB.contactsDao.deleteContactByUserId(fromUserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,12 +52,15 @@ Future<void> handleContactRequest(
|
||||||
Future<void> handleContactUpdate(
|
Future<void> handleContactUpdate(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
EncryptedContent_ContactUpdate contactUpdate,
|
EncryptedContent_ContactUpdate contactUpdate,
|
||||||
int? senderProfileCounter) async {
|
int? senderProfileCounter,
|
||||||
|
) async {
|
||||||
switch (contactUpdate.type) {
|
switch (contactUpdate.type) {
|
||||||
case EncryptedContent_ContactUpdate_Type.REQUEST:
|
case EncryptedContent_ContactUpdate_Type.REQUEST:
|
||||||
|
Log.info('Got a contact update request from $fromUserId');
|
||||||
await notifyContactsAboutProfileChange(onlyToContact: fromUserId);
|
await notifyContactsAboutProfileChange(onlyToContact: fromUserId);
|
||||||
|
|
||||||
case EncryptedContent_ContactUpdate_Type.UPDATE:
|
case EncryptedContent_ContactUpdate_Type.UPDATE:
|
||||||
|
Log.info('Got a contact update $fromUserId');
|
||||||
if (contactUpdate.hasAvatarSvg() &&
|
if (contactUpdate.hasAvatarSvg() &&
|
||||||
contactUpdate.hasDisplayName() &&
|
contactUpdate.hasDisplayName() &&
|
||||||
senderProfileCounter != null) {
|
senderProfileCounter != null) {
|
||||||
|
|
@ -74,6 +81,7 @@ Future<void> handleFlameSync(
|
||||||
int contactId,
|
int contactId,
|
||||||
EncryptedContent_FlameSync flameSync,
|
EncryptedContent_FlameSync flameSync,
|
||||||
) async {
|
) async {
|
||||||
|
Log.info('Got a flameSync from $contactId');
|
||||||
final contact = await twonlyDB.contactsDao
|
final contact = await twonlyDB.contactsDao
|
||||||
.getContactByUserId(contactId)
|
.getContactByUserId(contactId)
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
|
|
|
||||||
|
|
@ -1,92 +1,158 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
|
import 'package:twonly/src/services/api/media_download.dart';
|
||||||
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
|
import 'package:twonly/src/services/mediafile.service.dart';
|
||||||
|
import 'package:twonly/src/services/thumbnail.service.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
Future<void> handleMedia(int fromUserId, String groupId, EncryptedContent_Media media) async {
|
Future<void> handleMedia(
|
||||||
TODO
|
int fromUserId,
|
||||||
}
|
String groupId,
|
||||||
|
EncryptedContent_Media media,
|
||||||
|
) async {
|
||||||
|
Log.info(
|
||||||
|
'Got a media message: ${media.senderMessageId} from $groupId with type ${media.type}',
|
||||||
|
);
|
||||||
|
|
||||||
Future<void> handleMediaUpdate(int fromUserId, String groupId, EncryptedContent_MediaUpdate mediaUpdate) async {
|
late MediaType mediaType;
|
||||||
TODO
|
switch (media.type) {
|
||||||
|
case EncryptedContent_Media_Type.REUPLOAD:
|
||||||
|
final message = await twonlyDB.messagesDao
|
||||||
|
.getMessageById(media.senderMessageId)
|
||||||
|
.getSingleOrNull();
|
||||||
|
if (message == null ||
|
||||||
|
message.senderId != fromUserId ||
|
||||||
|
message.mediaId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case there was already a downloaded file delete it...
|
||||||
|
await removeMediaFile(message.mediaId!);
|
||||||
|
|
||||||
// switch (message.kind) {
|
await twonlyDB.mediaFilesDao.updateMedia(
|
||||||
// case MessageKind.receiveMediaError:
|
message.mediaId!,
|
||||||
// if (message.messageReceiverId != null) {
|
MediaFilesCompanion(
|
||||||
// final openedMessage = await twonlyDB.messagesDao
|
downloadState: const Value(DownloadState.pending),
|
||||||
// .getMessageByIdAndContactId(fromUserId, message.messageReceiverId!)
|
downloadToken: Value(Uint8List.fromList(media.downloadToken)),
|
||||||
// .getSingleOrNull();
|
encryptionKey: Value(Uint8List.fromList(media.encryptionKey)),
|
||||||
|
encryptionMac: Value(Uint8List.fromList(media.encryptionMac)),
|
||||||
// if (openedMessage != null) {
|
encryptionNonce: Value(Uint8List.fromList(media.encryptionNonce)),
|
||||||
// /// message found
|
|
||||||
|
|
||||||
// /// checks if
|
|
||||||
// /// 1. this was a media upload
|
|
||||||
// /// 2. the media was not already retransmitted
|
|
||||||
// /// 3. the media was send in the last two days
|
|
||||||
// if (openedMessage.mediaUploadId != null &&
|
|
||||||
// openedMessage.mediaRetransmissionState ==
|
|
||||||
// MediaRetransmitting.none &&
|
|
||||||
// openedMessage.sendAt
|
|
||||||
// .isAfter(DateTime.now().subtract(const Duration(days: 2)))) {
|
|
||||||
// // reset the media upload state to pending,
|
|
||||||
// // this will cause the media to be re-encrypted again
|
|
||||||
// await twonlyDB.mediaUploadsDao.updateMediaUpload(
|
|
||||||
// openedMessage.mediaUploadId!,
|
|
||||||
// const MediaUploadsCompanion(
|
|
||||||
// state: Value(
|
|
||||||
// UploadState.pending,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// // reset the message upload so the upload will be done again
|
|
||||||
// await twonlyDB.messagesDao.updateMessageByOtherUser(
|
|
||||||
// fromUserId,
|
|
||||||
// message.messageReceiverId!,
|
|
||||||
// const MessagesCompanion(
|
|
||||||
// downloadState: Value(DownloadState.pending),
|
|
||||||
// mediaRetransmissionState:
|
|
||||||
// Value(MediaRetransmitting.retransmitted),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// unawaited(retryMediaUpload(false));
|
|
||||||
// } else {
|
|
||||||
// await twonlyDB.messagesDao.updateMessageByOtherUser(
|
|
||||||
// fromUserId,
|
|
||||||
// message.messageReceiverId!,
|
|
||||||
// const MessagesCompanion(
|
|
||||||
// errorWhileSending: Value(true),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
if (message.kind == MessageKind.storedMediaFile) {
|
|
||||||
if (message.messageReceiverId != null) {
|
|
||||||
/// stored media file just updates the message
|
|
||||||
await twonlyDB.messagesDao.updateMessageByOtherUser(
|
|
||||||
fromUserId,
|
|
||||||
message.messageReceiverId!,
|
|
||||||
const MessagesCompanion(
|
|
||||||
mediaStored: Value(true),
|
|
||||||
errorWhileSending: Value(false),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final msg = await twonlyDB.messagesDao
|
|
||||||
.getMessageByIdAndContactId(
|
final mediaFile =
|
||||||
|
await twonlyDB.mediaFilesDao.getMediaFileById(message.mediaId!);
|
||||||
|
|
||||||
|
if (mediaFile != null) {
|
||||||
|
unawaited(startDownloadMedia(mediaFile, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
case EncryptedContent_Media_Type.IMAGE:
|
||||||
|
mediaType = MediaType.image;
|
||||||
|
case EncryptedContent_Media_Type.VIDEO:
|
||||||
|
mediaType = MediaType.video;
|
||||||
|
case EncryptedContent_Media_Type.GIF:
|
||||||
|
mediaType = MediaType.gif;
|
||||||
|
}
|
||||||
|
|
||||||
|
final mediaFile = await twonlyDB.mediaFilesDao.insertMedia(
|
||||||
|
MediaFilesCompanion(
|
||||||
|
downloadState: const Value(DownloadState.pending),
|
||||||
|
type: Value(mediaType),
|
||||||
|
requiresAuthentication: Value(media.requiresAuthentication),
|
||||||
|
displayLimitInMilliseconds: Value(
|
||||||
|
media.hasDisplayLimitInMilliseconds()
|
||||||
|
? media.displayLimitInMilliseconds.toInt()
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
downloadToken: Value(Uint8List.fromList(media.downloadToken)),
|
||||||
|
encryptionKey: Value(Uint8List.fromList(media.encryptionKey)),
|
||||||
|
encryptionMac: Value(Uint8List.fromList(media.encryptionMac)),
|
||||||
|
encryptionNonce: Value(Uint8List.fromList(media.encryptionNonce)),
|
||||||
|
createdAt: Value(fromTimestamp(media.timestamp)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mediaFile == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final message = await twonlyDB.messagesDao.insertMessage(
|
||||||
|
MessagesCompanion(
|
||||||
|
messageId: Value(media.senderMessageId),
|
||||||
|
senderId: Value(fromUserId),
|
||||||
|
groupId: Value(groupId),
|
||||||
|
mediaId: Value(mediaFile.mediaId),
|
||||||
|
ackByServer: const Value(true),
|
||||||
|
ackByUser: const Value(true),
|
||||||
|
quotesMessageId: Value(
|
||||||
|
media.hasQuoteMessageId() ? media.quoteMessageId : null,
|
||||||
|
),
|
||||||
|
createdAt: Value(fromTimestamp(media.timestamp)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (message != null) {
|
||||||
|
Log.info('Inserted a new media message with ID: ${message.messageId}');
|
||||||
|
await twonlyDB.contactsDao.incFlameCounter(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
message.messageReceiverId!,
|
true,
|
||||||
)
|
fromTimestamp(media.timestamp),
|
||||||
.getSingleOrNull();
|
);
|
||||||
if (msg != null && msg.mediaUploadId != null) {
|
|
||||||
final filePath = await getMediaFilePath(msg.mediaUploadId, 'send');
|
unawaited(startDownloadMedia(mediaFile, false));
|
||||||
if (filePath.contains('mp4')) {
|
}
|
||||||
unawaited(createThumbnailsForVideo(File(filePath)));
|
}
|
||||||
} else {
|
|
||||||
unawaited(createThumbnailsForImage(File(filePath)));
|
Future<void> handleMediaUpdate(
|
||||||
|
int fromUserId,
|
||||||
|
String groupId,
|
||||||
|
EncryptedContent_MediaUpdate mediaUpdate,
|
||||||
|
) async {
|
||||||
|
final message = await twonlyDB.messagesDao
|
||||||
|
.getMessageById(mediaUpdate.targetMessageId)
|
||||||
|
.getSingleOrNull();
|
||||||
|
if (message == null || message.mediaId == null) return;
|
||||||
|
final mediaFile =
|
||||||
|
await twonlyDB.mediaFilesDao.getMediaFileById(message.mediaId!);
|
||||||
|
if (mediaFile == null) return;
|
||||||
|
|
||||||
|
switch (mediaUpdate.type) {
|
||||||
|
case EncryptedContent_MediaUpdate_Type.REOPENED:
|
||||||
|
Log.info('Got media file reopened ${mediaFile.mediaId}');
|
||||||
|
await twonlyDB.mediaFilesDao.updateMedia(
|
||||||
|
mediaFile.mediaId,
|
||||||
|
const MediaFilesCompanion(
|
||||||
|
reopenByContact: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case EncryptedContent_MediaUpdate_Type.STORED:
|
||||||
|
Log.info('Got media file stored ${mediaFile.mediaId}');
|
||||||
|
await twonlyDB.mediaFilesDao.updateMedia(
|
||||||
|
mediaFile.mediaId,
|
||||||
|
const MediaFilesCompanion(
|
||||||
|
storedByContact: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
unawaited(createThumbnailForMediaFile(mediaFile));
|
||||||
|
|
||||||
|
case EncryptedContent_MediaUpdate_Type.DECRYPTION_ERROR:
|
||||||
|
Log.info('Got media file decryption error ${mediaFile.mediaId}');
|
||||||
|
final reuploadRequestedBy = mediaFile.reuploadRequestedBy ?? [];
|
||||||
|
reuploadRequestedBy.add(fromUserId);
|
||||||
|
await twonlyDB.mediaFilesDao.updateMedia(
|
||||||
|
mediaFile.mediaId,
|
||||||
|
MediaFilesCompanion(
|
||||||
|
uploadState: const Value(UploadState.pending),
|
||||||
|
reuploadRequestedBy: Value(reuploadRequestedBy),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (message.content != null) {}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,15 @@ Future<void> handleMessageUpdate(
|
||||||
) async {
|
) async {
|
||||||
switch (messageUpdate.type) {
|
switch (messageUpdate.type) {
|
||||||
case EncryptedContent_MessageUpdate_Type.OPENED:
|
case EncryptedContent_MessageUpdate_Type.OPENED:
|
||||||
Log.info('Opened message ${messageUpdate.senderMessageId}');
|
Log.info(
|
||||||
|
'Opened message ${messageUpdate.multipleSenderMessageIds.length}');
|
||||||
|
for (final senderMessageId in messageUpdate.multipleSenderMessageIds) {
|
||||||
await twonlyDB.messagesDao.handleMessageOpened(
|
await twonlyDB.messagesDao.handleMessageOpened(
|
||||||
groupId,
|
groupId,
|
||||||
messageUpdate.senderMessageId,
|
senderMessageId,
|
||||||
fromTimestamp(messageUpdate.timestamp),
|
fromTimestamp(messageUpdate.timestamp),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
case EncryptedContent_MessageUpdate_Type.DELETE:
|
case EncryptedContent_MessageUpdate_Type.DELETE:
|
||||||
Log.info('Delete message ${messageUpdate.senderMessageId}');
|
Log.info('Delete message ${messageUpdate.senderMessageId}');
|
||||||
await twonlyDB.messagesDao.handleMessageDeletion(
|
await twonlyDB.messagesDao.handleMessageDeletion(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
DateTime lastPushKeyRequest = DateTime.now().subtract(const Duration(hours: 1));
|
DateTime lastPushKeyRequest = DateTime.now().subtract(const Duration(hours: 1));
|
||||||
|
|
||||||
|
|
@ -11,6 +12,7 @@ Future<void> handlePushKey(
|
||||||
) async {
|
) async {
|
||||||
switch (pushKeys.type) {
|
switch (pushKeys.type) {
|
||||||
case EncryptedContent_PushKeys_Type.REQUEST:
|
case EncryptedContent_PushKeys_Type.REQUEST:
|
||||||
|
Log.info('Got a pushkey request from $contactId');
|
||||||
if (lastPushKeyRequest
|
if (lastPushKeyRequest
|
||||||
.isBefore(DateTime.now().subtract(const Duration(seconds: 60)))) {
|
.isBefore(DateTime.now().subtract(const Duration(seconds: 60)))) {
|
||||||
lastPushKeyRequest = DateTime.now();
|
lastPushKeyRequest = DateTime.now();
|
||||||
|
|
@ -18,6 +20,7 @@ Future<void> handlePushKey(
|
||||||
}
|
}
|
||||||
|
|
||||||
case EncryptedContent_PushKeys_Type.UPDATE:
|
case EncryptedContent_PushKeys_Type.UPDATE:
|
||||||
|
Log.info('Got a pushkey update from $contactId');
|
||||||
await handleNewPushKey(contactId, pushKeys.keyId.toInt(), pushKeys.key);
|
await handleNewPushKey(contactId, pushKeys.keyId.toInt(), pushKeys.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
Future<void> handleReaction(
|
Future<void> handleReaction(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
String groupId,
|
String groupId,
|
||||||
EncryptedContent_Reaction reaction,
|
EncryptedContent_Reaction reaction,
|
||||||
) async {
|
) async {
|
||||||
|
Log.info('Got a reaction from $fromUserId');
|
||||||
if (reaction.hasRemove()) {
|
if (reaction.hasRemove()) {
|
||||||
if (reaction.remove) {
|
if (reaction.remove) {
|
||||||
await twonlyDB.reactionsDao
|
await twonlyDB.reactionsDao
|
||||||
|
|
|
||||||
|
|
@ -1,125 +1,34 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
Future<void> handleTextMessage(
|
Future<void> handleTextMessage(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
String groupId,
|
String groupId,
|
||||||
EncryptedContent_TextMessage textMessage,
|
EncryptedContent_TextMessage textMessage,
|
||||||
) async {
|
) async {
|
||||||
TODO
|
Log.info(
|
||||||
// final content = message.content!;
|
'Got a text message: ${textMessage.senderMessageId} from $groupId',
|
||||||
// // when a message is received doubled ignore it...
|
);
|
||||||
|
|
||||||
// final openedMessage = await twonlyDB.messagesDao
|
final message = await twonlyDB.messagesDao.insertMessage(
|
||||||
// .getMessageByOtherMessageId(fromUserId, message.messageSenderId!)
|
MessagesCompanion(
|
||||||
// .getSingleOrNull();
|
messageId: Value(textMessage.senderMessageId),
|
||||||
|
senderId: Value(fromUserId),
|
||||||
// if (openedMessage != null) {
|
groupId: Value(groupId),
|
||||||
// if (openedMessage.errorWhileSending) {
|
content: Value(textMessage.text),
|
||||||
// await twonlyDB.messagesDao
|
ackByServer: const Value(true),
|
||||||
// .deleteMessagesByMessageId(openedMessage.messageId);
|
ackByUser: const Value(true),
|
||||||
// } else {
|
quotesMessageId: Value(
|
||||||
// Log.error(
|
textMessage.hasQuoteMessageId() ? textMessage.quoteMessageId : null,
|
||||||
// 'Got a duplicated message from other user: ${message.messageSenderId!}',
|
),
|
||||||
// );
|
createdAt: Value(fromTimestamp(textMessage.timestamp)),
|
||||||
// final ok = client.Response_Ok()..none = true;
|
),
|
||||||
// return client.Response()..ok = ok;
|
);
|
||||||
// }
|
if (message != null) {
|
||||||
// }
|
Log.info('Inserted a new text message with ID: ${message.messageId}');
|
||||||
|
}
|
||||||
// int? responseToMessageId;
|
|
||||||
// int? responseToOtherMessageId;
|
|
||||||
// int? messageId;
|
|
||||||
|
|
||||||
// var acknowledgeByUser = false;
|
|
||||||
// DateTime? openedAt;
|
|
||||||
|
|
||||||
// if (message.kind == MessageKind.reopenedMedia) {
|
|
||||||
// acknowledgeByUser = true;
|
|
||||||
// openedAt = DateTime.now();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (content is TextMessageContent) {
|
|
||||||
// responseToMessageId = content.responseToMessageId;
|
|
||||||
// responseToOtherMessageId = content.responseToOtherMessageId;
|
|
||||||
|
|
||||||
// if (responseToMessageId != null || responseToOtherMessageId != null) {
|
|
||||||
// // reactions are shown in the notification directly...
|
|
||||||
// if (isEmoji(content.text)) {
|
|
||||||
// openedAt = DateTime.now();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (content is ReopenedMediaFileContent) {
|
|
||||||
// responseToMessageId = content.messageId;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (responseToMessageId != null) {
|
|
||||||
// await twonlyDB.messagesDao.updateMessageByOtherUser(
|
|
||||||
// fromUserId,
|
|
||||||
// responseToMessageId,
|
|
||||||
// MessagesCompanion(
|
|
||||||
// errorWhileSending: const Value(false),
|
|
||||||
// openedAt: Value(
|
|
||||||
// DateTime.now(),
|
|
||||||
// ), // when a user reacted to the media file, it should be marked as opened
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final contentJson = jsonEncode(content.toJson());
|
|
||||||
// final update = MessagesCompanion(
|
|
||||||
// contactId: Value(fromUserId),
|
|
||||||
// kind: Value(message.kind),
|
|
||||||
// messageOtherId: Value(message.messageSenderId),
|
|
||||||
// contentJson: Value(contentJson),
|
|
||||||
// acknowledgeByServer: const Value(true),
|
|
||||||
// acknowledgeByUser: Value(acknowledgeByUser),
|
|
||||||
// responseToMessageId: Value(responseToMessageId),
|
|
||||||
// responseToOtherMessageId: Value(responseToOtherMessageId),
|
|
||||||
// openedAt: Value(openedAt),
|
|
||||||
// downloadState: Value(
|
|
||||||
// message.kind == MessageKind.media
|
|
||||||
// ? DownloadState.pending
|
|
||||||
// : DownloadState.downloaded,
|
|
||||||
// ),
|
|
||||||
// sendAt: Value(message.timestamp),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// messageId = await twonlyDB.messagesDao.insertMessage(
|
|
||||||
// update,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (messageId == null) {
|
|
||||||
// Log.error('could not insert message into db');
|
|
||||||
// return client.Response()..error = ErrorCode.InternalError;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Log.info('Inserted a new message with id: $messageId');
|
|
||||||
|
|
||||||
// if (message.kind == MessageKind.media) {
|
|
||||||
// await twonlyDB.contactsDao.incFlameCounter(
|
|
||||||
// fromUserId,
|
|
||||||
// true,
|
|
||||||
// message.timestamp,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// final msg = await twonlyDB.messagesDao
|
|
||||||
// .getMessageByMessageId(messageId)
|
|
||||||
// .getSingleOrNull();
|
|
||||||
// if (msg != null) {
|
|
||||||
// unawaited(startDownloadMedia(msg, false));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Log.error('Content is not defined $message');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // unarchive contact when receiving a new message
|
|
||||||
// await twonlyDB.contactsDao.updateContact(
|
|
||||||
// fromUserId,
|
|
||||||
// const ContactsCompanion(
|
|
||||||
// archived: Value(false),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// return null;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import 'package:cryptography_plus/cryptography_plus.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||||
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pbenum.dart';
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart'
|
import 'package:twonly/src/views/camera/share_image_editor_view.dart'
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,17 @@ import 'package:cryptography_plus/cryptography_plus.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/json/message_old.dart' as my;
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
/// This function must be called after the database is setup
|
/// This function must be called after the database is setup
|
||||||
Future<void> setupNotificationWithUsers({
|
Future<void> setupNotificationWithUsers({
|
||||||
|
|
@ -104,19 +106,14 @@ Future<void> setupNotificationWithUsers({
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendNewPushKey(int userId, PushKey pushKey) async {
|
Future<void> sendNewPushKey(int userId, PushKey pushKey) async {
|
||||||
await encryptAndSendMessageAsync(
|
await sendCipherText(
|
||||||
null,
|
|
||||||
userId,
|
userId,
|
||||||
my.MessageJson(
|
EncryptedContent()
|
||||||
kind: MessageKind.pushKey,
|
..pushKeys = (EncryptedContent_PushKeys()
|
||||||
content: my.PushKeyContent(
|
..type = EncryptedContent_PushKeys_Type.UPDATE
|
||||||
keyId: pushKey.id.toInt(),
|
..key = pushKey.key
|
||||||
key: pushKey.key,
|
..keyId = pushKey.id
|
||||||
),
|
..createdAt = pushKey.createdAtUnixTimestamp),
|
||||||
timestamp: DateTime.fromMillisecondsSinceEpoch(
|
|
||||||
pushKey.createdAtUnixTimestamp.toInt(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,7 +129,7 @@ Future<void> updatePushUser(Contact contact) async {
|
||||||
displayName: getContactDisplayName(contact),
|
displayName: getContactDisplayName(contact),
|
||||||
pushKeys: [],
|
pushKeys: [],
|
||||||
blocked: contact.blocked,
|
blocked: contact.blocked,
|
||||||
lastMessageId: Int64(),
|
lastMessageId: uuid.v7(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -160,7 +157,7 @@ Future<void> handleNewPushKey(int fromUserId, int keyId, List<int> key) async {
|
||||||
displayName: getContactDisplayName(contact),
|
displayName: getContactDisplayName(contact),
|
||||||
pushKeys: [],
|
pushKeys: [],
|
||||||
blocked: contact.blocked,
|
blocked: contact.blocked,
|
||||||
lastMessageId: Int64(),
|
lastMessageId: uuid.v7(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
pushUser = pushKeys.firstWhereOrNull((x) => x.userId == fromUserId);
|
pushUser = pushKeys.firstWhereOrNull((x) => x.userId == fromUserId);
|
||||||
|
|
@ -174,8 +171,8 @@ Future<void> handleNewPushKey(int fromUserId, int keyId, List<int> key) async {
|
||||||
pushUser!.pushKeys.clear();
|
pushUser!.pushKeys.clear();
|
||||||
pushUser.pushKeys.add(
|
pushUser.pushKeys.add(
|
||||||
PushKey(
|
PushKey(
|
||||||
id: Int64(pushKey.keyId),
|
id: Int64(keyId),
|
||||||
key: pushKey.key,
|
key: key,
|
||||||
createdAtUnixTimestamp: Int64(DateTime.now().millisecondsSinceEpoch),
|
createdAtUnixTimestamp: Int64(DateTime.now().millisecondsSinceEpoch),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -183,7 +180,7 @@ Future<void> handleNewPushKey(int fromUserId, int keyId, List<int> key) async {
|
||||||
await setPushKeys(SecureStorageKeys.sendingPushKeys, pushKeys);
|
await setPushKeys(SecureStorageKeys.sendingPushKeys, pushKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateLastMessageId(int fromUserId, int messageId) async {
|
Future<void> updateLastMessageId(int fromUserId, String messageId) async {
|
||||||
final pushUsers = await getPushKeys(SecureStorageKeys.receivingPushKeys);
|
final pushUsers = await getPushKeys(SecureStorageKeys.receivingPushKeys);
|
||||||
|
|
||||||
final pushUser = pushUsers.firstWhereOrNull((x) => x.userId == fromUserId);
|
final pushUser = pushUsers.firstWhereOrNull((x) => x.userId == fromUserId);
|
||||||
|
|
@ -192,15 +189,103 @@ Future<void> updateLastMessageId(int fromUserId, int messageId) async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pushUser.lastMessageId < Int64(messageId)) {
|
if (isUUIDNewer(messageId, pushUser.lastMessageId)) {
|
||||||
pushUser.lastMessageId = Int64(messageId);
|
pushUser.lastMessageId = messageId;
|
||||||
await setPushKeys(SecureStorageKeys.receivingPushKeys, pushUsers);
|
await setPushKeys(SecureStorageKeys.receivingPushKeys, pushUsers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> getPushDataFromEncryptedContent(
|
||||||
|
int toUserId,
|
||||||
|
String? messageId,
|
||||||
|
EncryptedContent content,
|
||||||
|
) async {
|
||||||
|
late PushKind kind;
|
||||||
|
String? reactionContent;
|
||||||
|
|
||||||
|
if (content.hasReaction()) {
|
||||||
|
if (content.reaction.remove) return null;
|
||||||
|
|
||||||
|
final msg = await twonlyDB.messagesDao
|
||||||
|
.getMessageById(content.reaction.targetMessageId)
|
||||||
|
.getSingleOrNull();
|
||||||
|
if (msg == null) return null;
|
||||||
|
if (msg.content != null) {
|
||||||
|
kind = PushKind.reactionToText;
|
||||||
|
} else if (msg.mediaId != null) {
|
||||||
|
final media = await twonlyDB.mediaFilesDao.getMediaFileById(msg.mediaId!);
|
||||||
|
if (media == null) return null;
|
||||||
|
switch (media.type) {
|
||||||
|
case MediaType.image:
|
||||||
|
kind = PushKind.reactionToImage;
|
||||||
|
case MediaType.video:
|
||||||
|
kind = PushKind.reactionToVideo;
|
||||||
|
case MediaType.gif:
|
||||||
|
kind = PushKind.reaction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reactionContent = content.reaction.emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.hasTextMessage()) {
|
||||||
|
kind = PushKind.text;
|
||||||
|
if (content.textMessage.hasQuoteMessageId()) {
|
||||||
|
kind = PushKind.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (content.hasMedia()) {
|
||||||
|
switch (content.media.type) {
|
||||||
|
case EncryptedContent_Media_Type.IMAGE:
|
||||||
|
kind = PushKind.image;
|
||||||
|
case EncryptedContent_Media_Type.VIDEO:
|
||||||
|
kind = PushKind.video;
|
||||||
|
// ignore: no_default_cases
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (content.media.requiresAuthentication) {
|
||||||
|
kind = PushKind.twonly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.hasContactRequest()) {
|
||||||
|
switch (content.contactRequest.type) {
|
||||||
|
case EncryptedContent_ContactRequest_Type.REQUEST:
|
||||||
|
kind = PushKind.contactRequest;
|
||||||
|
case EncryptedContent_ContactRequest_Type.ACCEPT:
|
||||||
|
kind = PushKind.acceptRequest;
|
||||||
|
case EncryptedContent_ContactRequest_Type.REJECT:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.hasMediaUpdate()) {
|
||||||
|
switch (content.mediaUpdate.type) {
|
||||||
|
case EncryptedContent_MediaUpdate_Type.REOPENED:
|
||||||
|
kind = PushKind.reopenedMedia;
|
||||||
|
case EncryptedContent_MediaUpdate_Type.STORED:
|
||||||
|
kind = PushKind.storedMediaFile;
|
||||||
|
case EncryptedContent_MediaUpdate_Type.DECRYPTION_ERROR:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final pushNotification = PushNotification()..kind = kind;
|
||||||
|
if (reactionContent != null) {
|
||||||
|
pushNotification.reactionContent = reactionContent;
|
||||||
|
}
|
||||||
|
if (messageId != null) {
|
||||||
|
pushNotification.messageId = messageId;
|
||||||
|
}
|
||||||
|
return encryptPushNotification(toUserId, pushNotification);
|
||||||
|
}
|
||||||
|
|
||||||
/// this will trigger a push notification
|
/// this will trigger a push notification
|
||||||
/// push notification only containing the message kind and username
|
/// push notification only containing the message kind and username
|
||||||
Future<Uint8List?> getPushData(int toUserId, PushNotification content) async {
|
Future<Uint8List?> encryptPushNotification(
|
||||||
|
int toUserId,
|
||||||
|
PushNotification content,
|
||||||
|
) async {
|
||||||
final pushKeys = await getPushKeys(SecureStorageKeys.sendingPushKeys);
|
final pushKeys = await getPushKeys(SecureStorageKeys.sendingPushKeys);
|
||||||
|
|
||||||
var key = 'InsecureOnlyUsedForAddingContact'.codeUnits;
|
var key = 'InsecureOnlyUsedForAddingContact'.codeUnits;
|
||||||
|
|
@ -218,14 +303,12 @@ Future<Uint8List?> getPushData(int toUserId, PushNotification content) async {
|
||||||
// this will be enforced after every app uses this system... :/
|
// this will be enforced after every app uses this system... :/
|
||||||
// return null;
|
// return null;
|
||||||
Log.error('Using insecure key as the receiver does not send a push key!');
|
Log.error('Using insecure key as the receiver does not send a push key!');
|
||||||
await encryptAndSendMessageAsync(
|
|
||||||
null,
|
await sendCipherText(
|
||||||
toUserId,
|
toUserId,
|
||||||
my.MessageJson(
|
EncryptedContent()
|
||||||
kind: MessageKind.requestPushKey,
|
..pushKeys = (EncryptedContent_PushKeys()
|
||||||
content: my.MessageContent(),
|
..type = EncryptedContent_PushKeys_Type.REQUEST),
|
||||||
timestamp: DateTime.now(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,15 @@ import 'package:twonly/src/services/signal/consts.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/prekeys.signal.dart';
|
import 'package:twonly/src/services/signal/prekeys.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/utils.signal.dart';
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
|
|
||||||
/// This caused some troubles, so protection the encryption...
|
/// This caused some troubles, so protection the encryption...
|
||||||
final lockingSignalEncryption = Mutex();
|
final lockingSignalEncryption = Mutex();
|
||||||
|
|
||||||
Future<Uint8List?> signalEncryptMessage(
|
Future<CiphertextMessage?> signalEncryptMessage(
|
||||||
int target,
|
int target,
|
||||||
Uint8List plaintextContent,
|
Uint8List plaintextContent,
|
||||||
) async {
|
) async {
|
||||||
return lockingSignalEncryption.protect<Uint8List?>(() async {
|
return lockingSignalEncryption.protect<CiphertextMessage?>(() async {
|
||||||
try {
|
try {
|
||||||
final signalStore = (await getSignalStore())!;
|
final signalStore = (await getSignalStore())!;
|
||||||
final address = SignalProtocolAddress(target.toString(), defaultDeviceId);
|
final address = SignalProtocolAddress(target.toString(), defaultDeviceId);
|
||||||
|
|
@ -75,14 +74,7 @@ Future<Uint8List?> signalEncryptMessage(
|
||||||
Log.error('did not get the identity of the remote address');
|
Log.error('did not get the identity of the remote address');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return await session.encrypt(plaintextContent);
|
||||||
final ciphertext = await session.encrypt(plaintextContent);
|
|
||||||
|
|
||||||
final b = BytesBuilder()
|
|
||||||
..add(ciphertext.serialize())
|
|
||||||
..add(intToBytes(ciphertext.getType()));
|
|
||||||
|
|
||||||
return b.takeBytes();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e.toString());
|
Log.error(e.toString());
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,23 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:video_thumbnail/video_thumbnail.dart';
|
import 'package:video_thumbnail/video_thumbnail.dart';
|
||||||
|
|
||||||
|
|
||||||
|
Future<void> createThumbnailForMediaFile(MediaFile media) async {
|
||||||
|
|
||||||
|
switch (media.type) {
|
||||||
|
case MediaType.image:
|
||||||
|
TODO
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> createThumbnailsForImage(File file) async {
|
Future<void> createThumbnailsForImage(File file) async {
|
||||||
final fileExtension = file.path.split('.').last.toLowerCase();
|
final fileExtension = file.path.split('.').last.toLowerCase();
|
||||||
if (fileExtension != 'png') {
|
if (fileExtension != 'png') {
|
||||||
|
|
|
||||||
|
|
@ -267,3 +267,9 @@ MediaMessageContent? getMediaContent(Message message) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isUUIDNewer(String uuid1, String uuid2) {
|
||||||
|
final timestamp1 = int.parse(uuid1.substring(0, 8), radix: 16);
|
||||||
|
final timestamp2 = int.parse(uuid2.substring(0, 8), radix: 16);
|
||||||
|
return timestamp1 > timestamp2;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:fixnum/fixnum.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||||
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pbserver.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
|
||||||
import 'package:twonly/src/services/fcm.service.dart';
|
import 'package:twonly/src/services/fcm.service.dart';
|
||||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
@ -52,10 +51,10 @@ class NotificationView extends StatelessWidget {
|
||||||
if (run) {
|
if (run) {
|
||||||
final user = await getUser();
|
final user = await getUser();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
final pushData = await getPushData(
|
final pushData = await encryptPushNotification(
|
||||||
user.userId,
|
user.userId,
|
||||||
PushNotification(
|
PushNotification(
|
||||||
messageId: Int64(),
|
messageId: uuid.v4(),
|
||||||
kind: PushKind.testNotification,
|
kind: PushKind.testNotification,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:hashlib/random.dart';
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:twonly/src/services/api/media_upload.dart';
|
import 'package:twonly/src/services/api/media_upload.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/pow.dart';
|
import 'package:twonly/src/utils/pow.dart';
|
||||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
|
|
||||||
|
|
@ -40,5 +42,11 @@ void main() {
|
||||||
final uv4String = utf8.decode(uv4Bytes.cast<int>());
|
final uv4String = utf8.decode(uv4Bytes.cast<int>());
|
||||||
expect(uv4String, uv4);
|
expect(uv4String, uv4);
|
||||||
});
|
});
|
||||||
|
test('comparing uui7', () async {
|
||||||
|
final uv7Old = uuid.v7();
|
||||||
|
sleep(const Duration(milliseconds: 1000));
|
||||||
|
final uv7New = uuid.v7();
|
||||||
|
expect(isUUIDNewer(uv7New, uv7Old), true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue