mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 07:48:40 +00:00
message deletion
This commit is contained in:
parent
c67bd6b464
commit
8f8f2cabe0
12 changed files with 120 additions and 56 deletions
|
|
@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/app.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/providers/connection.provider.dart';
|
||||
|
|
@ -19,8 +20,6 @@ import 'package:twonly/src/services/fcm.service.dart';
|
|||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
|
||||
import 'app.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await initFCMService();
|
||||
|
|
|
|||
|
|
@ -155,12 +155,15 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
|||
}
|
||||
|
||||
Future<void> handleMessageDeletion(
|
||||
int contactId,
|
||||
int? contactId,
|
||||
String messageId,
|
||||
DateTime timestamp,
|
||||
) async {
|
||||
final msg = await getMessageById(messageId).getSingleOrNull();
|
||||
if (msg == null || msg.senderId != contactId) return;
|
||||
if (msg == null || msg.senderId != contactId) {
|
||||
Log.error('Message does not exists or contact is not owner.');
|
||||
return;
|
||||
}
|
||||
if (msg.mediaId != null) {
|
||||
await (delete(mediaFiles)..where((t) => t.mediaId.equals(msg.mediaId!)))
|
||||
.go();
|
||||
|
|
@ -176,7 +179,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
|||
|
||||
await (update(messages)
|
||||
..where(
|
||||
(t) => t.messageId.equals(messageId) & t.senderId.equals(contactId),
|
||||
(t) => t.messageId.equals(messageId),
|
||||
))
|
||||
.write(
|
||||
const MessagesCompanion(
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class Messages extends Table {
|
|||
TextColumn get content => text().nullable()();
|
||||
TextColumn get mediaId => text()
|
||||
.nullable()
|
||||
.references(MediaFiles, #mediaId, onDelete: KeyAction.cascade)();
|
||||
.references(MediaFiles, #mediaId, onDelete: KeyAction.setNull)();
|
||||
|
||||
BoolColumn get mediaStored => boolean().withDefault(const Constant(false))();
|
||||
|
||||
|
|
|
|||
|
|
@ -2243,7 +2243,7 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
|||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES media_files (media_id) ON DELETE CASCADE'));
|
||||
'REFERENCES media_files (media_id) ON DELETE SET NULL'));
|
||||
static const VerificationMeta _mediaStoredMeta =
|
||||
const VerificationMeta('mediaStored');
|
||||
@override
|
||||
|
|
@ -6464,7 +6464,7 @@ abstract class _$TwonlyDB extends GeneratedDatabase {
|
|||
on: TableUpdateQuery.onTableName('media_files',
|
||||
limitUpdateKind: UpdateKind.delete),
|
||||
result: [
|
||||
TableUpdate('messages', kind: UpdateKind.delete),
|
||||
TableUpdate('messages', kind: UpdateKind.update),
|
||||
],
|
||||
),
|
||||
WritePropagation(
|
||||
|
|
|
|||
|
|
@ -291,7 +291,8 @@
|
|||
"tutorialChatMessagesReopenMessageDesc": "Wenn dein Freund dir ein Bild oder Video mit unendlicher Anzeigezeit gesendet hat, kannst du es bis zum Neustart der App jederzeit erneut öffnen. Um dies zu tun, musst du einfach doppelt auf die Nachricht klicken. Dein Freund erhält dann eine Benachrichtigung, dass du das Bild erneut angesehen hast.",
|
||||
"memoriesEmpty": "Sobald du Bilder oder Videos speicherst, landen sie hier in deinen Erinnerungen.",
|
||||
"deleteTitle": "Bist du dir sicher?",
|
||||
"deleteOkBtn": "Für mich löschen",
|
||||
"deleteOkBtnForAll": "Für alle löschen",
|
||||
"deleteOkBtnForMe": "Für mich löschen",
|
||||
"deleteImageTitle": "Bist du dir sicher?",
|
||||
"deleteImageBody": "Das Bild wird unwiderruflich gelöscht.",
|
||||
"backupNoticeTitle": "Kein Backup konfiguriert",
|
||||
|
|
|
|||
|
|
@ -443,7 +443,8 @@
|
|||
"tutorialChatMessagesReopenMessageDesc": "If your friend has sent you a picture or video with infinite display time, you can open it again at any time until you restart the app. To do this, simply double-click on the message. Your friend will then receive a notification that you have viewed the picture again.",
|
||||
"memoriesEmpty": "As soon as you save pictures or videos, they end up here in your memories.",
|
||||
"deleteTitle": "Are you sure?",
|
||||
"deleteOkBtn": "Delete for me",
|
||||
"deleteOkBtnForAll": "Delete for all",
|
||||
"deleteOkBtnForMe": "Delete for me",
|
||||
"deleteImageTitle": "Are you sure?",
|
||||
"deleteImageBody": "The image will be irrevocably deleted.",
|
||||
"settingsBackup": "Backup",
|
||||
|
|
|
|||
|
|
@ -1760,11 +1760,17 @@ abstract class AppLocalizations {
|
|||
/// **'Are you sure?'**
|
||||
String get deleteTitle;
|
||||
|
||||
/// No description provided for @deleteOkBtn.
|
||||
/// No description provided for @deleteOkBtnForAll.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete for all'**
|
||||
String get deleteOkBtnForAll;
|
||||
|
||||
/// No description provided for @deleteOkBtnForMe.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete for me'**
|
||||
String get deleteOkBtn;
|
||||
String get deleteOkBtnForMe;
|
||||
|
||||
/// No description provided for @deleteImageTitle.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -930,7 +930,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get deleteTitle => 'Bist du dir sicher?';
|
||||
|
||||
@override
|
||||
String get deleteOkBtn => 'Für mich löschen';
|
||||
String get deleteOkBtnForAll => 'Für alle löschen';
|
||||
|
||||
@override
|
||||
String get deleteOkBtnForMe => 'Für mich löschen';
|
||||
|
||||
@override
|
||||
String get deleteImageTitle => 'Bist du dir sicher?';
|
||||
|
|
|
|||
|
|
@ -924,7 +924,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get deleteTitle => 'Are you sure?';
|
||||
|
||||
@override
|
||||
String get deleteOkBtn => 'Delete for me';
|
||||
String get deleteOkBtnForAll => 'Delete for all';
|
||||
|
||||
@override
|
||||
String get deleteOkBtnForMe => 'Delete for me';
|
||||
|
||||
@override
|
||||
String get deleteImageTitle => 'Are you sure?';
|
||||
|
|
|
|||
|
|
@ -118,43 +118,52 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
|||
alignment:
|
||||
right ? Alignment.centerRight : Alignment.centerLeft,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
ResponseContainer(
|
||||
msg: widget.message,
|
||||
group: widget.group,
|
||||
mediaService: mediaService,
|
||||
borderRadius: borderRadius,
|
||||
scrollToMessage: widget.scrollToMessage,
|
||||
child: (widget.message.type == MessageType.text)
|
||||
? ChatTextEntry(
|
||||
message: widget.message,
|
||||
nextMessage: widget.nextMessage,
|
||||
borderRadius: borderRadius,
|
||||
minWidth: reactionsForWidth * 43,
|
||||
)
|
||||
: (mediaService == null)
|
||||
? null
|
||||
: ChatMediaEntry(
|
||||
message: widget.message,
|
||||
group: widget.group,
|
||||
mediaService: mediaService!,
|
||||
galleryItems: widget.galleryItems,
|
||||
),
|
||||
),
|
||||
if (reactionsForWidth > 0)
|
||||
const SizedBox(height: 20, width: 10),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
bottom: -20,
|
||||
left: 5,
|
||||
right: 5,
|
||||
child: ReactionRow(
|
||||
if (widget.message.isDeletedFromSender)
|
||||
ChatTextEntry(
|
||||
message: widget.message,
|
||||
reactions: reactions,
|
||||
nextMessage: widget.nextMessage,
|
||||
borderRadius: borderRadius,
|
||||
minWidth: reactionsForWidth * 43,
|
||||
)
|
||||
else
|
||||
Column(
|
||||
children: [
|
||||
ResponseContainer(
|
||||
msg: widget.message,
|
||||
group: widget.group,
|
||||
mediaService: mediaService,
|
||||
borderRadius: borderRadius,
|
||||
scrollToMessage: widget.scrollToMessage,
|
||||
child: (widget.message.type == MessageType.text)
|
||||
? ChatTextEntry(
|
||||
message: widget.message,
|
||||
nextMessage: widget.nextMessage,
|
||||
borderRadius: borderRadius,
|
||||
minWidth: reactionsForWidth * 43,
|
||||
)
|
||||
: (mediaService == null)
|
||||
? null
|
||||
: ChatMediaEntry(
|
||||
message: widget.message,
|
||||
group: widget.group,
|
||||
mediaService: mediaService!,
|
||||
galleryItems: widget.galleryItems,
|
||||
),
|
||||
),
|
||||
if (reactionsForWidth > 0)
|
||||
const SizedBox(height: 20, width: 10),
|
||||
],
|
||||
),
|
||||
if (!widget.message.isDeletedFromSender)
|
||||
Positioned(
|
||||
bottom: -20,
|
||||
left: 5,
|
||||
right: 5,
|
||||
child: ReactionRow(
|
||||
message: widget.message,
|
||||
reactions: reactions,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@ class ChatTextEntry extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final text = message.content ?? '';
|
||||
var text = message.content ?? '';
|
||||
|
||||
if (message.isDeletedFromSender) {
|
||||
text = 'Nachricht wurde gelöscht.';
|
||||
}
|
||||
|
||||
if (EmojiAnimation.supported(text)) {
|
||||
return Container(
|
||||
constraints: const BoxConstraints(
|
||||
|
|
@ -37,11 +42,24 @@ class ChatTextEntry extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
final displayTime = !combineTextMessageWithNext(message, nextMessage);
|
||||
var displayTime = !combineTextMessageWithNext(message, nextMessage);
|
||||
|
||||
var spacerWidth = minWidth - measureTextWidth(text) - 53;
|
||||
if (spacerWidth < 0) spacerWidth = 0;
|
||||
|
||||
Color? color;
|
||||
var expanded = false;
|
||||
if (message.quotesMessageId == null) {
|
||||
color = getMessageColor(message);
|
||||
}
|
||||
if (message.isDeletedFromSender) {
|
||||
color = context.color.surfaceBright;
|
||||
displayTime = false;
|
||||
} else if (measureTextWidth(text) > 270 ||
|
||||
message.quotesMessageId != null) {
|
||||
expanded = true;
|
||||
}
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||
|
|
@ -49,15 +67,14 @@ class ChatTextEntry extends StatelessWidget {
|
|||
),
|
||||
padding: const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
message.quotesMessageId == null ? getMessageColor(message) : null,
|
||||
color: color,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (measureTextWidth(text) > 270 || message.quotesMessageId != null)
|
||||
if (expanded)
|
||||
Expanded(
|
||||
child: BetterText(text: text),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -85,10 +85,32 @@ class MessageContextMenu extends StatelessWidget {
|
|||
context,
|
||||
context.lang.deleteTitle,
|
||||
null,
|
||||
customOk: context.lang.deleteOkBtn,
|
||||
customOk:
|
||||
(message.senderId == null && !message.isDeletedFromSender)
|
||||
? context.lang.deleteOkBtnForAll
|
||||
: context.lang.deleteOkBtnForMe,
|
||||
);
|
||||
if (delete) {
|
||||
await twonlyDB.messagesDao.deleteMessagesById(message.messageId);
|
||||
if (message.senderId == null && !message.isDeletedFromSender) {
|
||||
await twonlyDB.messagesDao.handleMessageDeletion(
|
||||
null,
|
||||
message.messageId,
|
||||
DateTime.now(),
|
||||
);
|
||||
await sendCipherTextToGroup(
|
||||
message.groupId,
|
||||
pb.EncryptedContent(
|
||||
messageUpdate: pb.EncryptedContent_MessageUpdate(
|
||||
type: pb.EncryptedContent_MessageUpdate_Type.DELETE,
|
||||
senderMessageId: message.messageId,
|
||||
),
|
||||
),
|
||||
null,
|
||||
);
|
||||
} else {
|
||||
await twonlyDB.messagesDao
|
||||
.deleteMessagesById(message.messageId);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const FaIcon(FontAwesomeIcons.trash),
|
||||
|
|
|
|||
Loading…
Reference in a new issue