mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 10:38:41 +00:00
add message action table
This commit is contained in:
parent
645dfe16da
commit
1c154e6c67
18 changed files with 997 additions and 479 deletions
|
|
@ -97,6 +97,10 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
|
|||
return select(contacts)..where((t) => t.userId.equals(userId));
|
||||
}
|
||||
|
||||
Future<List<Contact>> getContactsByUsername(String username) async {
|
||||
return (select(contacts)..where((t) => t.username.equals(username))).get();
|
||||
}
|
||||
|
||||
Future<void> deleteContactByUserId(int userId) {
|
||||
return (delete(contacts)..where((t) => t.userId.equals(userId))).go();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,4 +31,17 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
|||
return (select(groupMembers)..where((t) => t.groupId.equals(groupId)))
|
||||
.get();
|
||||
}
|
||||
|
||||
Future<List<Group>> getDirectChat(int userId) async {
|
||||
final query = (select(groups).join([
|
||||
leftOuterJoin(
|
||||
groupMembers,
|
||||
groupMembers.groupId.equalsExp(groups.groupId) &
|
||||
groupMembers.contactId.equals(userId),
|
||||
),
|
||||
])
|
||||
..where(groups.isGroupOfTwo.equals(true)));
|
||||
|
||||
return query.map((row) => row.readTable(groups)).get();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ part 'messages.dao.g.dart';
|
|||
Contacts,
|
||||
MediaFiles,
|
||||
MessageHistories,
|
||||
MessageActions,
|
||||
Groups,
|
||||
],
|
||||
)
|
||||
|
|
@ -192,11 +193,10 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
|||
(t) => t.messageId.equals(messageId) & t.senderId.equals(contactId),
|
||||
))
|
||||
.write(
|
||||
MessagesCompanion(
|
||||
isDeletedFromSender: const Value(true),
|
||||
content: const Value(null),
|
||||
modifiedAt: Value(timestamp),
|
||||
mediaId: const Value(null),
|
||||
const MessagesCompanion(
|
||||
isDeletedFromSender: Value(true),
|
||||
content: Value(null),
|
||||
mediaId: Value(null),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -215,6 +215,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
|||
MessageHistoriesCompanion(
|
||||
messageId: Value(messageId),
|
||||
content: Value(msg.content),
|
||||
createdAt: Value(timestamp),
|
||||
),
|
||||
);
|
||||
await (update(messages)
|
||||
|
|
@ -224,29 +225,36 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
|||
.write(
|
||||
MessagesCompanion(
|
||||
content: Value(text),
|
||||
modifiedAt: Value(timestamp),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleMessageOpened(
|
||||
String groupId,
|
||||
int contactId,
|
||||
String messageId,
|
||||
DateTime timestamp,
|
||||
) async {
|
||||
final msg = await getMessageById(messageId).getSingleOrNull();
|
||||
if (msg == null) return;
|
||||
await (update(messages)
|
||||
..where(
|
||||
(t) =>
|
||||
t.groupId.equals(groupId) &
|
||||
t.messageId.equals(messageId) &
|
||||
t.senderId.isNull(),
|
||||
))
|
||||
.write(
|
||||
MessagesCompanion(
|
||||
openedAt: Value(timestamp),
|
||||
openedByCounter: Value(msg.openedByCounter + 1),
|
||||
await into(messageActions).insert(
|
||||
MessageActionsCompanion(
|
||||
messageId: Value(messageId),
|
||||
contactId: Value(contactId),
|
||||
type: const Value(MessageActionType.ackByUserAt),
|
||||
actionAt: Value(timestamp),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleMessageAckByServer(
|
||||
int contactId,
|
||||
String messageId,
|
||||
DateTime timestamp,
|
||||
) async {
|
||||
await into(messageActions).insert(
|
||||
MessageActionsCompanion(
|
||||
messageId: Value(messageId),
|
||||
contactId: Value(contactId),
|
||||
type: const Value(MessageActionType.ackByServerAt),
|
||||
actionAt: Value(timestamp),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,4 +10,5 @@ mixin _$MessagesDaoMixin on DatabaseAccessor<TwonlyDB> {
|
|||
$MessagesTable get messages => attachedDatabase.messages;
|
||||
$MessageHistoriesTable get messageHistories =>
|
||||
attachedDatabase.messageHistories;
|
||||
$MessageActionsTable get messageActions => attachedDatabase.messageActions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import 'package:twonly/src/utils/log.dart';
|
|||
|
||||
part 'receipts.dao.g.dart';
|
||||
|
||||
@DriftAccessor(tables: [Receipts, Messages])
|
||||
@DriftAccessor(tables: [Receipts, Messages, MessageActions])
|
||||
class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
||||
// this constructor is required so that the main database can create an instance
|
||||
// of this object.
|
||||
|
|
@ -24,11 +24,11 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
|||
if (receipt == null) return;
|
||||
|
||||
if (receipt.messageId != null) {
|
||||
await (update(messages)
|
||||
..where((t) => t.messageId.equals(receipt.messageId!)))
|
||||
.write(
|
||||
const MessagesCompanion(
|
||||
ackByUser: Value(true),
|
||||
await into(messageActions).insert(
|
||||
MessageActionsCompanion(
|
||||
messageId: Value(receipt.messageId!),
|
||||
contactId: Value(fromUserId),
|
||||
type: const Value(MessageActionType.ackByUserAt),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -81,6 +81,10 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
|||
.get();
|
||||
}
|
||||
|
||||
Stream<List<Receipt>> watchAll() {
|
||||
return select(receipts).watch();
|
||||
}
|
||||
|
||||
Future<void> updateReceipt(
|
||||
String receiptId,
|
||||
ReceiptsCompanion updates,
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@ mixin _$ReceiptsDaoMixin on DatabaseAccessor<TwonlyDB> {
|
|||
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
|
||||
$MessagesTable get messages => attachedDatabase.messages;
|
||||
$ReceiptsTable get receipts => attachedDatabase.receipts;
|
||||
$MessageActionsTable get messageActions => attachedDatabase.messageActions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,28 +30,50 @@ class Messages extends Table {
|
|||
|
||||
BoolColumn get isEdited => boolean().withDefault(const Constant(false))();
|
||||
|
||||
BoolColumn get ackByUser => boolean().withDefault(const Constant(false))();
|
||||
BoolColumn get ackByServer => boolean().withDefault(const Constant(false))();
|
||||
|
||||
IntColumn get openedByCounter => integer().withDefault(const Constant(0))();
|
||||
DateTimeColumn get openedAt => dateTime().nullable()();
|
||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||
DateTimeColumn get modifiedAt =>
|
||||
dateTime().nullable().withDefault(currentDateAndTime)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {messageId};
|
||||
}
|
||||
|
||||
@DataClassName('MessageHistory')
|
||||
class MessageHistories extends Table {
|
||||
enum MessageActionType {
|
||||
openedAt,
|
||||
modifiedAt,
|
||||
ackByUserAt,
|
||||
ackByServerAt,
|
||||
}
|
||||
|
||||
@DataClassName('MessageAction')
|
||||
class MessageActions extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
|
||||
TextColumn get messageId =>
|
||||
text().references(Messages, #messageId, onDelete: KeyAction.cascade)();
|
||||
|
||||
IntColumn get contactId =>
|
||||
integer().references(Contacts, #contactId, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get type => textEnum<MessageActionType>()();
|
||||
DateTimeColumn get actionAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
@DataClassName('MessageHistory')
|
||||
class MessageHistories extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
|
||||
TextColumn get messageId =>
|
||||
text().references(Messages, #messageId, onDelete: KeyAction.cascade)();
|
||||
|
||||
IntColumn get contactId =>
|
||||
integer().references(Contacts, #contactId, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get content => text().nullable()();
|
||||
|
||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {messageId, createdAt};
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ part 'twonly.db.g.dart';
|
|||
SignalSessionStores,
|
||||
SignalContactPreKeys,
|
||||
SignalContactSignedPreKeys,
|
||||
MessageActions
|
||||
],
|
||||
daos: [
|
||||
MessagesDao,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -76,12 +76,22 @@ Future<void> handleUploadStatusUpdate(TaskStatusUpdate update) async {
|
|||
),
|
||||
);
|
||||
|
||||
await twonlyDB.messagesDao.updateMessagesByMediaId(
|
||||
media.mediaId,
|
||||
const MessagesCompanion(
|
||||
ackByServer: Value(true),
|
||||
),
|
||||
);
|
||||
/// As the messages where send in a bulk acknowledge all messages.
|
||||
|
||||
final messages =
|
||||
await twonlyDB.messagesDao.getMessagesByMediaId(media.mediaId);
|
||||
for (final message in messages) {
|
||||
final contacts =
|
||||
await twonlyDB.groupsDao.getGroupMembers(message.groupId);
|
||||
for (final contact in contacts) {
|
||||
await twonlyDB.messagesDao.handleMessageAckByServer(
|
||||
contact.contactId,
|
||||
message.messageId,
|
||||
DateTime.now(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
Log.error(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
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:twonly/globals.dart';
|
||||
|
|
@ -126,11 +127,10 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
|
|||
|
||||
if (resp.isSuccess) {
|
||||
if (receipt.messageId != null) {
|
||||
await twonlyDB.messagesDao.updateMessageId(
|
||||
await twonlyDB.messagesDao.handleMessageAckByServer(
|
||||
receipt.contactId,
|
||||
receipt.messageId!,
|
||||
const MessagesCompanion(
|
||||
ackByServer: Value(true),
|
||||
),
|
||||
DateTime.now(),
|
||||
);
|
||||
}
|
||||
if (!receipt.contactWillSendsReceipt) {
|
||||
|
|
@ -158,6 +158,37 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
|
|||
return null;
|
||||
}
|
||||
|
||||
Future<void> insertAndSendTextMessage(
|
||||
String groupId,
|
||||
String textMessage,
|
||||
) async {
|
||||
final message = await twonlyDB.messagesDao.insertMessage(
|
||||
MessagesCompanion(
|
||||
groupId: Value(groupId),
|
||||
content: Value(textMessage),
|
||||
),
|
||||
);
|
||||
if (message == null) {
|
||||
Log.error('Could not insert message into database');
|
||||
return;
|
||||
}
|
||||
|
||||
final groupMembers = await twonlyDB.groupsDao.getGroupMembers(groupId);
|
||||
|
||||
for (final groupMember in groupMembers) {
|
||||
unawaited(sendCipherText(
|
||||
groupMember.contactId,
|
||||
pb.EncryptedContent(
|
||||
textMessage: pb.EncryptedContent_TextMessage(
|
||||
senderMessageId: message.messageId,
|
||||
text: textMessage,
|
||||
timestamp: Int64(message.createdAt.millisecondsSinceEpoch),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Future<(Uint8List, Uint8List?)?> sendCipherText(
|
||||
int contactId,
|
||||
pb.EncryptedContent encryptedContent, {
|
||||
|
|
|
|||
|
|
@ -160,7 +160,6 @@ Future<PlaintextContent?> handleEncryptedMessage(
|
|||
if (content.hasMessageUpdate()) {
|
||||
await handleMessageUpdate(
|
||||
fromUserId,
|
||||
content.groupId,
|
||||
content.messageUpdate,
|
||||
);
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -92,8 +92,6 @@ Future<void> handleMedia(
|
|||
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,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import 'package:twonly/src/utils/log.dart';
|
|||
|
||||
Future<void> handleMessageUpdate(
|
||||
int contactId,
|
||||
String groupId,
|
||||
EncryptedContent_MessageUpdate messageUpdate,
|
||||
) async {
|
||||
switch (messageUpdate.type) {
|
||||
|
|
@ -14,7 +13,7 @@ Future<void> handleMessageUpdate(
|
|||
'Opened message ${messageUpdate.multipleSenderMessageIds.length}');
|
||||
for (final senderMessageId in messageUpdate.multipleSenderMessageIds) {
|
||||
await twonlyDB.messagesDao.handleMessageOpened(
|
||||
groupId,
|
||||
contactId,
|
||||
senderMessageId,
|
||||
fromTimestamp(messageUpdate.timestamp),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ Future<void> handleTextMessage(
|
|||
senderId: Value(fromUserId),
|
||||
groupId: Value(groupId),
|
||||
content: Value(textMessage.text),
|
||||
ackByServer: const Value(true),
|
||||
ackByUser: const Value(true),
|
||||
quotesMessageId: Value(
|
||||
textMessage.hasQuoteMessageId() ? textMessage.quoteMessageId : null,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import 'dart:async';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/model/json/message_old.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
class AutomatedTestingView extends StatefulWidget {
|
||||
const AutomatedTestingView({super.key});
|
||||
|
|
@ -36,25 +36,27 @@ class _AutomatedTestingViewState extends State<AutomatedTestingView> {
|
|||
title: const Text('Sending a lot of messages.'),
|
||||
subtitle: Text(lotsOfMessagesStatus),
|
||||
onTap: () async {
|
||||
await twonlyDB.messageRetransmissionDao
|
||||
.clearRetransmissionTable();
|
||||
final username = await showUserNameDialog(context);
|
||||
if (username == null) return;
|
||||
|
||||
final contacts =
|
||||
await twonlyDB.contactsDao.getAllNotBlockedContacts();
|
||||
await twonlyDB.contactsDao.getContactsByUsername(username);
|
||||
|
||||
for (final contact in contacts) {
|
||||
for (var i = 0; i < 200; i++) {
|
||||
setState(() {
|
||||
lotsOfMessagesStatus =
|
||||
'At message $i to ${contact.username}.';
|
||||
});
|
||||
await sendTextMessage(
|
||||
contact.userId,
|
||||
TextMessageContent(
|
||||
text: 'TestMessage $i',
|
||||
),
|
||||
null,
|
||||
);
|
||||
final groups =
|
||||
await twonlyDB.groupsDao.getDirectChat(contact.userId);
|
||||
|
||||
for (final group in groups) {
|
||||
for (var i = 0; i < 200; i++) {
|
||||
setState(() {
|
||||
lotsOfMessagesStatus =
|
||||
'At message $i to ${contact.username}.';
|
||||
});
|
||||
await insertAndSendTextMessage(
|
||||
group.groupId,
|
||||
'Message $i.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -64,3 +66,37 @@ class _AutomatedTestingViewState extends State<AutomatedTestingView> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> showUserNameDialog(
|
||||
BuildContext context,
|
||||
) {
|
||||
final controller = TextEditingController();
|
||||
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Username'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(context.lang.cancel),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // Close the dialog
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(context.lang.ok),
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pop(controller.text); // Return the input text
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift/drift.dart' hide Column;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/model/json/message_old.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
|
||||
class RetransmissionDataView extends StatefulWidget {
|
||||
const RetransmissionDataView({super.key});
|
||||
|
|
@ -19,33 +12,22 @@ class RetransmissionDataView extends StatefulWidget {
|
|||
|
||||
class RetransMsg {
|
||||
RetransMsg({
|
||||
required this.json,
|
||||
required this.retrans,
|
||||
required this.receipt,
|
||||
required this.contact,
|
||||
});
|
||||
final MessageJson json;
|
||||
final MessageRetransmission retrans;
|
||||
final Receipt receipt;
|
||||
final Contact? contact;
|
||||
|
||||
static List<RetransMsg> fromRaw(
|
||||
List<MessageRetransmission> retrans,
|
||||
List<Receipt> receipts,
|
||||
Map<int, Contact> contacts,
|
||||
) {
|
||||
final res = <RetransMsg>[];
|
||||
|
||||
for (final retrans in retrans) {
|
||||
final json = MessageJson.fromJson(
|
||||
jsonDecode(
|
||||
utf8.decode(
|
||||
gzip.decode(retrans.plaintextContent),
|
||||
),
|
||||
) as Map<String, dynamic>,
|
||||
);
|
||||
for (final receipt in receipts) {
|
||||
res.add(
|
||||
RetransMsg(
|
||||
json: json,
|
||||
retrans: retrans,
|
||||
contact: contacts[retrans.contactId],
|
||||
receipt: receipt,
|
||||
contact: contacts[receipt.contactId],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -54,9 +36,9 @@ class RetransMsg {
|
|||
}
|
||||
|
||||
class _RetransmissionDataViewState extends State<RetransmissionDataView> {
|
||||
List<MessageRetransmission> retransmissions = [];
|
||||
List<Receipt> retransmissions = [];
|
||||
Map<int, Contact> contacts = {};
|
||||
StreamSubscription<List<MessageRetransmission>>? subscriptionRetransmission;
|
||||
StreamSubscription<List<Receipt>>? subscriptionRetransmission;
|
||||
StreamSubscription<List<Contact>>? subscriptionContacts;
|
||||
List<RetransMsg> messages = [];
|
||||
|
||||
|
|
@ -85,7 +67,7 @@ class _RetransmissionDataViewState extends State<RetransmissionDataView> {
|
|||
setState(() {});
|
||||
});
|
||||
subscriptionRetransmission =
|
||||
twonlyDB.messageRetransmissionDao.watchAllMessages().listen((updated) {
|
||||
twonlyDB.receiptsDao.watchAll().listen((updated) {
|
||||
retransmissions = updated;
|
||||
if (contacts.isNotEmpty) {
|
||||
messages = RetransMsg.fromRaw(retransmissions, contacts);
|
||||
|
|
@ -108,7 +90,7 @@ class _RetransmissionDataViewState extends State<RetransmissionDataView> {
|
|||
.map(
|
||||
(retrans) => ListTile(
|
||||
title: Text(
|
||||
'${retrans.retrans.retransmissionId}: ${retrans.json.kind}',
|
||||
retrans.receipt.receiptId,
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -117,64 +99,13 @@ class _RetransmissionDataViewState extends State<RetransmissionDataView> {
|
|||
'To ${retrans.contact?.username}',
|
||||
),
|
||||
Text(
|
||||
'Server-Ack: ${retrans.retrans.acknowledgeByServerAt}',
|
||||
'Server-Ack: ${retrans.receipt.ackByServerAt}',
|
||||
),
|
||||
Text(
|
||||
'Retry: ${retrans.retrans.retryCount} : ${retrans.retrans.lastRetry}',
|
||||
'Retry: ${retrans.receipt.retryCount} : ${retrans.receipt.lastRetry}',
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: SizedBox(
|
||||
width: 80,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
width: 40,
|
||||
child: Center(
|
||||
child: GestureDetector(
|
||||
onDoubleTap: () async {
|
||||
await twonlyDB.messageRetransmissionDao
|
||||
.deleteRetransmissionById(
|
||||
retrans.retrans.retransmissionId,
|
||||
);
|
||||
},
|
||||
child: const FaIcon(
|
||||
FontAwesomeIcons.trash,
|
||||
size: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 40,
|
||||
child: OutlinedButton(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all<EdgeInsets>(
|
||||
EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
await twonlyDB.messageRetransmissionDao
|
||||
.updateRetransmission(
|
||||
retrans.retrans.retransmissionId,
|
||||
const MessageRetransmissionsCompanion(
|
||||
acknowledgeByServerAt: Value(null),
|
||||
),
|
||||
);
|
||||
await sendRetransmitMessage(
|
||||
retrans.retrans.retransmissionId,
|
||||
);
|
||||
},
|
||||
child: const FaIcon(
|
||||
FontAwesomeIcons.arrowRotateLeft,
|
||||
size: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ import 'package:package_info_plus/package_info_plus.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/model/protobuf/api/http/http_requests.pb.dart';
|
||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart'
|
||||
show createDownloadToken, uint8ListToHex;
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/settings/help/contact_us/submit_message.view.dart';
|
||||
|
|
@ -32,7 +30,7 @@ class _ContactUsState extends State<ContactUsView> {
|
|||
|
||||
Future<String?> uploadDebugLog() async {
|
||||
if (debugLogDownloadToken != null) return debugLogDownloadToken;
|
||||
final downloadToken = createDownloadToken();
|
||||
final downloadToken = getRandomUint8List(32);
|
||||
|
||||
final debugLog = await loadLogFile();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue