purge text messages fix multiple bugs

This commit is contained in:
otsmr 2025-10-28 13:23:35 +01:00
parent 4914df5610
commit 071c5c2a0d
25 changed files with 207 additions and 69 deletions

View file

@ -57,13 +57,12 @@ void main() async {
await initFileDownloader(); await initFileDownloader();
unawaited(MediaFileService.purgeTempFolder()); unawaited(MediaFileService.purgeTempFolder());
await twonlyDB.messagesDao.purgeMessageTable();
// await twonlyDB.messagesDao.resetPendingDownloadState(); // await twonlyDB.messagesDao.resetPendingDownloadState();
// await twonlyDB.messageRetransmissionDao.purgeOldRetransmissions(); // await twonlyDB.messageRetransmissionDao.purgeOldRetransmissions();
// await twonlyDB.signalDao.purgeOutDatedPreKeys(); // await twonlyDB.signalDao.purgeOutDatedPreKeys();
// Purge media files in the background
// unawaited(purgeReceivedMediaFiles());
// unawaited(purgeSendMediaFiles()); // unawaited(purgeSendMediaFiles());
// unawaited(performTwonlySafeBackup()); // unawaited(performTwonlySafeBackup());

View file

@ -121,7 +121,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
} }
} }
String getContactDisplayName(Contact user) { String getContactDisplayName(Contact user, {int? maxLength}) {
var name = user.username; var name = user.username;
if (user.nickName != null && user.nickName != '') { if (user.nickName != null && user.nickName != '') {
name = user.nickName!; name = user.nickName!;
@ -131,12 +131,19 @@ String getContactDisplayName(Contact user) {
if (user.accountDeleted) { if (user.accountDeleted) {
name = applyStrikethrough(name); name = applyStrikethrough(name);
} }
if (name.length > 27) { if (maxLength != null) {
return '${name.substring(0, 27 - 3)}...'; name = substringBy(name, maxLength);
} }
return name; return name;
} }
String substringBy(String string, int maxLength) {
if (string.length > maxLength) {
return '${string.substring(0, maxLength - 3)}...';
}
return string;
}
String getContactDisplayNameOld(old.Contact user) { String getContactDisplayNameOld(old.Contact user) {
var name = user.username; var name = user.username;
if (user.nickName != null && user.nickName != '') { if (user.nickName != null && user.nickName != '') {

View file

@ -68,16 +68,20 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
} }
Stream<List<Message>> watchByGroupId(String groupId) { Stream<List<Message>> watchByGroupId(String groupId) {
return ((select(messages)..where((t) => t.groupId.equals(groupId))) return ((select(messages)
..where(
(t) =>
t.groupId.equals(groupId) &
(t.isDeletedFromSender.equals(true) |
((t.type.equals(MessageType.text.name) &
t.content.isNotNull()) |
(t.type.equals(MessageType.media.name) &
t.mediaId.isNotNull()))),
))
..orderBy([(t) => OrderingTerm.asc(t.createdAt)])) ..orderBy([(t) => OrderingTerm.asc(t.createdAt)]))
.watch(); .watch();
} }
// Stream<List<GroupMember>> watchMembersByGroupId(String groupId) {
// return (select(groupMembers)..where((t) => t.groupId.equals(groupId)))
// .watch();
// }
Stream<List<(GroupMember, Contact)>> watchMembersByGroupId(String groupId) { Stream<List<(GroupMember, Contact)>> watchMembersByGroupId(String groupId) {
final query = (select(groupMembers).join([ final query = (select(groupMembers).join([
leftOuterJoin( leftOuterJoin(
@ -101,21 +105,31 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
.watchSingleOrNull(); .watchSingleOrNull();
} }
// Future<void> removeOldMessages() { Future<void> purgeMessageTable() async {
// return (update(messages) final allGroups = await select(groups).get();
// ..where(
// (t) => for (final group in allGroups) {
// (t.openedAt.isSmallerThanValue( final deletionTime = DateTime.now().subtract(
// DateTime.now().subtract(const Duration(days: 1)), Duration(
// ) | milliseconds: group.deleteMessagesAfterMilliseconds,
// (t.sendAt.isSmallerThanValue( ),
// DateTime.now().subtract(const Duration(days: 3)), );
// ) & final affected = await (delete(messages)
// t.errorWhileSending.equals(true))) & ..where(
// t.kind.equals(MessageKind.textMessage.name), (m) =>
// )) m.groupId.equals(group.groupId) &
// .write(const MessagesCompanion(contentJson: Value(null))); // m.messageId.equals(lastMessage.messageId).not() &
// } (m.mediaStored.equals(true) &
m.isDeletedFromSender.equals(true) |
m.mediaStored.equals(false)) &
(m.openedAt.isSmallerThanValue(deletionTime) |
(m.isDeletedFromSender.equals(true) &
m.createdAt.isSmallerThanValue(deletionTime))),
))
.go();
Log.info('Deleted $affected messages.');
}
}
// Future<List<Message>> getAllMessagesPendingDownloading() { // Future<List<Message>> getAllMessagesPendingDownloading() {
// return (select(messages) // return (select(messages)

View file

@ -18,7 +18,7 @@ class Groups extends Table {
boolean().withDefault(const Constant(false))(); boolean().withDefault(const Constant(false))();
IntColumn get deleteMessagesAfterMilliseconds => IntColumn get deleteMessagesAfterMilliseconds =>
integer().withDefault(const Constant(1000 * 60 * 24))(); integer().withDefault(const Constant(1000 * 60 * 60 * 24))();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();

View file

@ -734,7 +734,7 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> {
'delete_messages_after_milliseconds', aliasedName, false, 'delete_messages_after_milliseconds', aliasedName, false,
type: DriftSqlType.int, type: DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultValue: const Constant(1000 * 60 * 24)); defaultValue: const Constant(1000 * 60 * 60 * 24));
static const VerificationMeta _createdAtMeta = static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt'); const VerificationMeta('createdAt');
@override @override

View file

@ -352,5 +352,10 @@
"received": "Empfangen", "received": "Empfangen",
"opened": "Geöffnet", "opened": "Geöffnet",
"waitingForInternet": "Warten auf Internet", "waitingForInternet": "Warten auf Internet",
"editHistory": "Bearbeitungshistorie" "editHistory": "Bearbeitungshistorie",
"archivedChats": "Archivierte Chats",
"durationShortSecond": "Sek.",
"durationShortMinute": "Min.",
"durationShortHour": "Std",
"durationShortDays": "Tagen"
} }

View file

@ -508,5 +508,10 @@
"received": "Received", "received": "Received",
"opened": "Opened", "opened": "Opened",
"waitingForInternet": "Waiting for internet", "waitingForInternet": "Waiting for internet",
"editHistory": "Edit history" "editHistory": "Edit history",
"archivedChats": "Archived chats",
"durationShortSecond": "Sec.",
"durationShortMinute": "Min.",
"durationShortHour": "Hrs.",
"durationShortDays": "Days"
} }

View file

@ -2155,6 +2155,36 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'Edit history'** /// **'Edit history'**
String get editHistory; String get editHistory;
/// No description provided for @archivedChats.
///
/// In en, this message translates to:
/// **'Archived chats'**
String get archivedChats;
/// No description provided for @durationShortSecond.
///
/// In en, this message translates to:
/// **'Sec.'**
String get durationShortSecond;
/// No description provided for @durationShortMinute.
///
/// In en, this message translates to:
/// **'Min.'**
String get durationShortMinute;
/// No description provided for @durationShortHour.
///
/// In en, this message translates to:
/// **'Hrs.'**
String get durationShortHour;
/// No description provided for @durationShortDays.
///
/// In en, this message translates to:
/// **'Days'**
String get durationShortDays;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View file

@ -1143,4 +1143,19 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get editHistory => 'Bearbeitungshistorie'; String get editHistory => 'Bearbeitungshistorie';
@override
String get archivedChats => 'Archivierte Chats';
@override
String get durationShortSecond => 'Sek.';
@override
String get durationShortMinute => 'Min.';
@override
String get durationShortHour => 'Std';
@override
String get durationShortDays => 'Tagen';
} }

View file

@ -1136,4 +1136,19 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get editHistory => 'Edit history'; String get editHistory => 'Edit history';
@override
String get archivedChats => 'Archived chats';
@override
String get durationShortSecond => 'Sec.';
@override
String get durationShortMinute => 'Min.';
@override
String get durationShortHour => 'Hrs.';
@override
String get durationShortDays => 'Days';
} }

View file

@ -71,7 +71,8 @@ Future<void> insertMediaFileInMessagesTable(
} }
Future<void> startBackgroundMediaUpload(MediaFileService mediaService) async { Future<void> startBackgroundMediaUpload(MediaFileService mediaService) async {
if (mediaService.mediaFile.uploadState == UploadState.initialized) { if (mediaService.mediaFile.uploadState == UploadState.initialized ||
mediaService.mediaFile.uploadState == UploadState.preprocessing) {
await mediaService.setUploadState(UploadState.preprocessing); await mediaService.setUploadState(UploadState.preprocessing);
if (!mediaService.tempPath.existsSync()) { if (!mediaService.tempPath.existsSync()) {
await mediaService.compressMedia(); await mediaService.compressMedia();
@ -84,7 +85,9 @@ Future<void> startBackgroundMediaUpload(MediaFileService mediaService) async {
if (!mediaService.uploadRequestPath.existsSync()) { if (!mediaService.uploadRequestPath.existsSync()) {
await _createUploadRequest(mediaService); await _createUploadRequest(mediaService);
} }
await mediaService.setUploadState(UploadState.uploading); if (mediaService.uploadRequestPath.existsSync()) {
await mediaService.setUploadState(UploadState.uploading);
}
} }
if (mediaService.mediaFile.uploadState == UploadState.uploading) { if (mediaService.mediaFile.uploadState == UploadState.uploading) {
@ -109,8 +112,6 @@ Future<void> _encryptMediaFiles(MediaFileService mediaService) async {
mediaService.encryptedPath mediaService.encryptedPath
.writeAsBytesSync(Uint8List.fromList(secretBox.cipherText)); .writeAsBytesSync(Uint8List.fromList(secretBox.cipherText));
await mediaService.setUploadState(UploadState.uploading);
} }
Future<void> _createUploadRequest(MediaFileService media) async { Future<void> _createUploadRequest(MediaFileService media) async {
@ -121,6 +122,11 @@ Future<void> _createUploadRequest(MediaFileService media) async {
final messages = final messages =
await twonlyDB.messagesDao.getMessagesByMediaId(media.mediaFile.mediaId); await twonlyDB.messagesDao.getMessagesByMediaId(media.mediaFile.mediaId);
if (messages.isEmpty) {
// There where no user selected who should receive the image, so waiting with this step...
return;
}
for (final message in messages) { for (final message in messages) {
final groupMembers = final groupMembers =
await twonlyDB.groupsDao.getGroupMembers(message.groupId); await twonlyDB.groupsDao.getGroupMembers(message.groupId);

View file

@ -53,6 +53,8 @@ class MediaFileService {
final messages = final messages =
await twonlyDB.messagesDao.getMessagesByMediaId(mediaId); await twonlyDB.messagesDao.getMessagesByMediaId(mediaId);
// in case messages in empty the file will be deleted, as delete is true by default
for (final message in messages) { for (final message in messages) {
if (message.senderId == null) { if (message.senderId == null) {
// Media was send by me // Media was send by me

View file

@ -89,27 +89,26 @@ String errorCodeToText(BuildContext context, ErrorCode code) {
case ErrorCode.PlanUpgradeNotYearly: case ErrorCode.PlanUpgradeNotYearly:
return context.lang.errorPlanUpgradeNotYearly; return context.lang.errorPlanUpgradeNotYearly;
} }
return code.toString(); // Fallback for unrecognized keys return code.toString();
} }
String formatDuration(int seconds) { String formatDuration(BuildContext context, int seconds) {
if (seconds < 60) { if (seconds < 60) {
return '$seconds Sec.'; return '$seconds ${context.lang.durationShortSecond}';
} else if (seconds < 3600) { } else if (seconds < 3600) {
final minutes = seconds ~/ 60; final minutes = seconds ~/ 60;
return '$minutes Min.'; return '$minutes ${context.lang.durationShortMinute}';
} else if (seconds < 86400) { } else if (seconds < 86400) {
final hours = seconds ~/ 3600; final hours = seconds ~/ 3600;
return '$hours Hrs.'; // Assuming "Stu." is for hours return '$hours ${context.lang.durationShortHour}';
} else { } else {
final days = seconds ~/ 86400; final days = seconds ~/ 86400;
return '$days Days'; return '$days ${context.lang.durationShortDays}';
} }
} }
InputDecoration getInputDecoration(BuildContext context, String hintText) { InputDecoration getInputDecoration(BuildContext context, String hintText) {
final primaryColor = final primaryColor = Theme.of(context).colorScheme.primary;
Theme.of(context).colorScheme.primary; // Get the primary color
return InputDecoration( return InputDecoration(
hintText: hintText, hintText: hintText,
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
class SendToWidget extends StatelessWidget { class SendToWidget extends StatelessWidget {
@ -40,7 +41,7 @@ class SendToWidget extends StatelessWidget {
style: textStyle, style: textStyle,
), ),
Text( Text(
sendTo, substringBy(sendTo, 20),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: boldTextStyle, // Use the bold text style here style: boldTextStyle, // Use the bold text style here
), ),
@ -48,9 +49,4 @@ class SendToWidget extends StatelessWidget {
), ),
); );
} }
String getContactDisplayName(String contact) {
// Replace this with your actual logic to get the contact display name
return contact; // Placeholder implementation
}
} }

View file

@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:hashlib/random.dart'; import 'package:hashlib/random.dart';
import 'package:screenshot/screenshot.dart'; import 'package:screenshot/screenshot.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/mediafiles.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/services/api/mediafiles/upload.service.dart'; import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
@ -75,8 +76,8 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
if (widget.imageBytesFuture != null) { if (widget.imageBytesFuture != null) {
loadImage(widget.imageBytesFuture!); loadImage(widget.imageBytesFuture!);
} else { } else {
if (widget.mediaFileService.storedPath.existsSync()) { if (widget.mediaFileService.tempPath.existsSync()) {
loadImage(widget.mediaFileService.storedPath.readAsBytes()); loadImage(widget.mediaFileService.tempPath.readAsBytes());
} }
} }
} }
@ -482,7 +483,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
label: Text( label: Text(
(widget.sendToGroup == null) (widget.sendToGroup == null)
? context.lang.shareImagedEditorShareWith ? context.lang.shareImagedEditorShareWith
: widget.sendToGroup!.groupName, : substringBy(widget.sendToGroup!.groupName, 15),
style: const TextStyle(fontSize: 17), style: const TextStyle(fontSize: 17),
), ),
), ),

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/chats/chat_list_components/group_list_item.dart'; import 'package:twonly/src/views/chats/chat_list_components/group_list_item.dart';
class ArchivedChatsView extends StatefulWidget { class ArchivedChatsView extends StatefulWidget {
@ -40,7 +41,7 @@ class _ArchivedChatsViewState extends State<ArchivedChatsView> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Archivierte Chats'), title: Text(context.lang.archivedChats),
), ),
body: ListView( body: ListView(
children: _groupsArchived.map((group) { children: _groupsArchived.map((group) {

View file

@ -252,7 +252,7 @@ class _ChatListViewState extends State<ChatListView> {
if (_groupsArchived.isEmpty) return Container(); if (_groupsArchived.isEmpty) return Container();
return ListTile( return ListTile(
title: Text( title: Text(
'Archivierte Chats (${_groupsArchived.length})', '${context.lang.archivedChats} (${_groupsArchived.length})',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle(fontSize: 13), style: const TextStyle(fontSize: 13),
), ),

View file

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts.dao.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';
@ -208,10 +209,12 @@ class _UserListItem extends State<GroupListItem> {
group: widget.group, group: widget.group,
child: ListTile( child: ListTile(
title: Text( title: Text(
widget.group.groupName, substringBy(widget.group.groupName, 30),
), ),
subtitle: (_currentMessage == null) subtitle: (_currentMessage == null)
? Text(context.lang.chatsTapToSend) ? (widget.group.totalMediaCounter == 0)
? Text(context.lang.chatsTapToSend)
: LastMessageTime(dateTime: widget.group.lastMessageExchange)
: Row( : Row(
children: [ children: [
MessageSendStateIcon( MessageSendStateIcon(
@ -222,7 +225,7 @@ class _UserListItem extends State<GroupListItem> {
const Text(''), const Text(''),
const SizedBox(width: 5), const SizedBox(width: 5),
if (_currentMessage != null) if (_currentMessage != null)
LastMessageTime(message: _currentMessage!), LastMessageTime(message: _currentMessage),
FlameCounterWidget( FlameCounterWidget(
groupId: widget.group.groupId, groupId: widget.group.groupId,
prefix: true, prefix: true,

View file

@ -6,9 +6,10 @@ import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
class LastMessageTime extends StatefulWidget { class LastMessageTime extends StatefulWidget {
const LastMessageTime({required this.message, super.key}); const LastMessageTime({this.message, this.dateTime, super.key});
final Message message; final Message? message;
final DateTime? dateTime;
@override @override
State<LastMessageTime> createState() => _LastMessageTimeState(); State<LastMessageTime> createState() => _LastMessageTimeState();
@ -24,12 +25,17 @@ class _LastMessageTimeState extends State<LastMessageTime> {
// Change the color every 200 milliseconds // Change the color every 200 milliseconds
updateTime = updateTime =
Timer.periodic(const Duration(milliseconds: 500), (timer) async { Timer.periodic(const Duration(milliseconds: 500), (timer) async {
final lastAction = await twonlyDB.messagesDao if (widget.message != null) {
.getLastMessageAction(widget.message.messageId); final lastAction = await twonlyDB.messagesDao
setState(() { .getLastMessageAction(widget.message!.messageId);
lastMessageInSeconds = DateTime.now() lastMessageInSeconds = DateTime.now()
.difference(lastAction?.actionAt ?? widget.message.createdAt) .difference(lastAction?.actionAt ?? widget.message!.createdAt)
.inSeconds; .inSeconds;
} else if (widget.dateTime != null) {
lastMessageInSeconds =
DateTime.now().difference(widget.dateTime!).inSeconds;
}
setState(() {
if (lastMessageInSeconds < 0) { if (lastMessageInSeconds < 0) {
lastMessageInSeconds = 0; lastMessageInSeconds = 0;
} }
@ -46,7 +52,7 @@ class _LastMessageTimeState extends State<LastMessageTime> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Text( return Text(
formatDuration(lastMessageInSeconds), formatDuration(context, lastMessageInSeconds),
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
); );
} }

View file

@ -5,6 +5,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.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/messages.table.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/memory_item.model.dart'; import 'package:twonly/src/model/memory_item.model.dart';
@ -241,7 +242,9 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
color: Colors.transparent, color: Colors.transparent,
child: Row( child: Row(
children: [ children: [
Text(group.groupName), Text(
substringBy(widget.group.groupName, 20),
),
const SizedBox(width: 10), const SizedBox(width: 10),
VerifiedShield(key: verifyShieldKey, group: group), VerifiedShield(key: verifyShieldKey, group: group),
const SizedBox(width: 10), const SizedBox(width: 10),

View file

@ -144,6 +144,16 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
case MessageSendState.sending: case MessageSendState.sending:
icon = getLoaderIcon(color); icon = getLoaderIcon(color);
text = context.lang.messageSendState_Sending; text = context.lang.messageSendState_Sending;
if (mediaFile != null) {
if (mediaFile.uploadState == UploadState.uploadLimitReached) {
text = 'Upload Limit erreicht';
}
if (mediaFile.uploadState == UploadState.preprocessing) {
text = 'Wird verarbeitet';
}
}
hasLoader = true; hasLoader = true;
case MessageSendState.receiving: case MessageSendState.receiving:
icon = getLoaderIcon(color); icon = getLoaderIcon(color);

View file

@ -24,7 +24,8 @@ class _ContactViewState extends State<ContactView> {
Future<void> handleUserRemoveRequest(Contact contact) async { Future<void> handleUserRemoveRequest(Contact contact) async {
final remove = await showAlertDialog( final remove = await showAlertDialog(
context, context,
context.lang.contactRemoveTitle(getContactDisplayName(contact)), context.lang
.contactRemoveTitle(getContactDisplayName(contact, maxLength: 20)),
context.lang.contactRemoveBody, context.lang.contactRemoveBody,
); );
if (remove) { if (remove) {
@ -124,7 +125,7 @@ class _ContactViewState extends State<ContactView> {
child: VerifiedShield(key: GlobalKey(), contact: contact), child: VerifiedShield(key: GlobalKey(), contact: contact),
), ),
Text( Text(
getContactDisplayName(contact), getContactDisplayName(contact, maxLength: 20),
style: const TextStyle(fontSize: 20), style: const TextStyle(fontSize: 20),
), ),
// if (flameCounter > 0) // if (flameCounter > 0)

View file

@ -5,6 +5,8 @@ import 'package:photo_view/photo_view_gallery.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/model/memory_item.model.dart'; import 'package:twonly/src/model/memory_item.model.dart';
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/camera/share_image_editor_view.dart'; import 'package:twonly/src/views/camera/share_image_editor_view.dart';
import 'package:twonly/src/views/components/alert_dialog.dart'; import 'package:twonly/src/views/components/alert_dialog.dart';
@ -117,12 +119,28 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
FilledButton.icon( FilledButton.icon(
icon: const FaIcon(FontAwesomeIcons.solidPaperPlane), icon: const FaIcon(FontAwesomeIcons.solidPaperPlane),
onPressed: () async { onPressed: () async {
final orgMediaService =
widget.galleryItems[currentIndex].mediaService;
final newMediaService = await initializeMediaUpload(
orgMediaService.mediaFile.type,
gUser.defaultShowTime,
);
if (newMediaService == null) {
Log.error('Could not create new mediaFIle');
return;
}
orgMediaService.storedPath
.copySync(newMediaService.tempPath.path);
if (!context.mounted) return;
await Navigator.push( await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => ShareImageEditorView( builder: (context) => ShareImageEditorView(
mediaFileService: widget mediaFileService: newMediaService,
.galleryItems[currentIndex].mediaService,
sharedFromGallery: true, sharedFromGallery: true,
), ),
), ),

View file

@ -114,6 +114,7 @@ Future<String?> showDisplayNameChangeDialog(
content: TextField( content: TextField(
controller: controller, controller: controller,
autofocus: true, autofocus: true,
maxLength: 30,
decoration: InputDecoration( decoration: InputDecoration(
hintText: context.lang.settingsProfileEditDisplayNameNew, hintText: context.lang.settingsProfileEditDisplayNameNew,
), ),

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart';
import 'package:twonly/src/views/components/better_list_title.dart'; import 'package:twonly/src/views/components/better_list_title.dart';
@ -63,7 +64,7 @@ class _SettingsMainViewState extends State<SettingsMainView> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
gUser.displayName, substringBy(gUser.displayName, 27),
style: const TextStyle(fontSize: 20), style: const TextStyle(fontSize: 20),
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),