From d59b8602f9eeec1f4b9f353e497fc14d87119e46 Mon Sep 17 00:00:00 2001 From: otsmr Date: Sat, 8 Nov 2025 13:36:54 +0100 Subject: [PATCH] fixed some issues with the migration --- .../camera_preview_controller_view.dart | 1 - .../chat_list_components/group_list_item.dart | 11 +- .../updates/62_database_migration.view.dart | 220 ++++++++---------- 3 files changed, 103 insertions(+), 129 deletions(-) diff --git a/lib/src/views/camera/camera_preview_controller_view.dart b/lib/src/views/camera/camera_preview_controller_view.dart index bab3b3b..3c7019a 100644 --- a/lib/src/views/camera/camera_preview_controller_view.dart +++ b/lib/src/views/camera/camera_preview_controller_view.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:io'; import 'package:camera/camera.dart'; -import 'package:drift/drift.dart' show Value; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_android_volume_keydown/flutter_android_volume_keydown.dart'; diff --git a/lib/src/views/chats/chat_list_components/group_list_item.dart b/lib/src/views/chats/chat_list_components/group_list_item.dart index 6f1939a..e70ebf8 100644 --- a/lib/src/views/chats/chat_list_components/group_list_item.dart +++ b/lib/src/views/chats/chat_list_components/group_list_item.dart @@ -218,7 +218,16 @@ class _UserListItem extends State { subtitle: (_currentMessage == null) ? (widget.group.totalMediaCounter == 0) ? Text(context.lang.chatsTapToSend) - : LastMessageTime(dateTime: widget.group.lastMessageExchange) + : Row( + children: [ + LastMessageTime( + dateTime: widget.group.lastMessageExchange), + FlameCounterWidget( + groupId: widget.group.groupId, + prefix: true, + ), + ], + ) : Row( children: [ MessageSendStateIcon( diff --git a/lib/src/views/updates/62_database_migration.view.dart b/lib/src/views/updates/62_database_migration.view.dart index b04a407..b2d0bff 100644 --- a/lib/src/views/updates/62_database_migration.view.dart +++ b/lib/src/views/updates/62_database_migration.view.dart @@ -1,5 +1,7 @@ +import 'dart:collection' show HashSet; import 'dart:convert'; import 'dart:io'; +import 'package:cryptography_plus/cryptography_plus.dart'; import 'package:drift/drift.dart'; import 'package:flutter/material.dart'; import 'package:path/path.dart'; @@ -8,13 +10,11 @@ import 'package:restart_app/restart_app.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/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly_database_old.dart' show TwonlyDatabaseOld; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/log.dart'; -import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; class DatabaseMigrationView extends StatefulWidget { @@ -37,141 +37,107 @@ class _DatabaseMigrationViewState extends State { final oldDatabase = TwonlyDatabaseOld(); final oldContacts = await oldDatabase.contacts.select().get(); - final oldMessages = await oldDatabase.messages.select().get(); for (final oldContact in oldContacts) { - if (oldContact.deleted) continue; - Uint8List? avatarSvg; - if (oldContact.avatarSvg != null) { - avatarSvg = - Uint8List.fromList(gzip.encode(utf8.encode(oldContact.avatarSvg!))); - } - await twonlyDB.contactsDao.insertContact( - ContactsCompanion( - userId: Value(oldContact.userId), - username: Value(oldContact.username), - displayName: Value(oldContact.displayName), - nickName: Value(oldContact.nickName), - avatarSvgCompressed: Value(avatarSvg), - senderProfileCounter: const Value(0), - accepted: Value(oldContact.accepted), - requested: Value(oldContact.requested), - blocked: Value(oldContact.blocked), - verified: Value(oldContact.verified), - createdAt: Value(oldContact.createdAt), - ), - ); - setState(() { - _contactsMigrated += 1; - }); - final group = await twonlyDB.groupsDao.createNewDirectChat( - oldContact.userId, - GroupsCompanion( - pinned: Value(oldContact.pinned), - archived: Value(oldContact.archived), - groupName: Value(getContactDisplayNameOld(oldContact)), - totalMediaCounter: Value(oldContact.totalMediaCounter), - alsoBestFriend: Value(oldContact.alsoBestFriend), - createdAt: Value(oldContact.createdAt), - lastFlameCounterChange: Value(oldContact.lastFlameCounterChange), - lastFlameSync: Value(oldContact.lastFlameSync), - lastMessageExchange: Value(oldContact.lastMessageExchange), - lastMessageReceived: Value(oldContact.lastMessageReceived), - lastMessageSend: Value(oldContact.lastMessageSend), - flameCounter: Value(oldContact.flameCounter), - ), - ); - if (group == null) continue; - for (final oldMessage in oldMessages) { - if (oldMessage.mediaUploadId == null && - oldMessage.mediaDownloadId == null) { - /// only interested in media files... - continue; + try { + if (oldContact.deleted) continue; + Uint8List? avatarSvg; + if (oldContact.avatarSvg != null) { + avatarSvg = Uint8List.fromList( + gzip.encode(utf8.encode(oldContact.avatarSvg!))); } - if (oldMessage.contactId != oldContact.userId) continue; - if (!oldMessage.mediaStored) continue; - - var storedMediaPath = - join((await getApplicationSupportDirectory()).path, 'media'); - if (oldMessage.mediaDownloadId != null) { - storedMediaPath = - '${join(storedMediaPath, 'received')}/${oldMessage.mediaDownloadId}'; - } else { - storedMediaPath = - '${join(storedMediaPath, 'send')}/${oldMessage.mediaDownloadId}'; - } - - var type = MediaType.image; - if (File('$storedMediaPath.mp4').existsSync()) { - type = MediaType.video; - storedMediaPath = '$storedMediaPath.mp4'; - } else if (File('$storedMediaPath.png').existsSync()) { - type = MediaType.image; - storedMediaPath = '$storedMediaPath.png'; - } else if (File('$storedMediaPath.webp').existsSync()) { - type = MediaType.image; - storedMediaPath = '$storedMediaPath.webp'; - } else { - continue; - } - - final uniqueId = Value( - getUUIDforDirectChat( - oldMessage.messageOtherId ?? oldMessage.messageId, - oldMessage.contactId ^ gUser.userId, + await twonlyDB.contactsDao.insertContact( + ContactsCompanion( + userId: Value(oldContact.userId), + username: Value(oldContact.username), + displayName: Value(oldContact.displayName), + nickName: Value(oldContact.nickName), + avatarSvgCompressed: Value(avatarSvg), + senderProfileCounter: const Value(0), + accepted: Value(oldContact.accepted), + requested: Value(oldContact.requested), + blocked: Value(oldContact.blocked), + verified: Value(oldContact.verified), + createdAt: Value(oldContact.createdAt), ), ); - - final mediaFile = await twonlyDB.mediaFilesDao.insertMedia( - MediaFilesCompanion( - mediaId: uniqueId, - stored: const Value(true), - type: Value(type), - createdAt: Value(oldMessage.sendAt), - ), - ); - if (mediaFile == null) continue; - - final message = await twonlyDB.messagesDao.insertMessage( - MessagesCompanion( - messageId: uniqueId, - groupId: Value(group.groupId), - mediaId: uniqueId, - type: const Value(MessageType.media), - ), - ); - if (message == null) continue; - - final mediaService = await MediaFileService.fromMedia(mediaFile); - File(storedMediaPath).copySync(mediaService.storedPath.path); setState(() { - _storedMediaFiles += 1; + _contactsMigrated += 1; }); + await twonlyDB.groupsDao.createNewDirectChat( + oldContact.userId, + GroupsCompanion( + pinned: Value(oldContact.pinned), + archived: Value(oldContact.archived), + groupName: Value(getContactDisplayNameOld(oldContact)), + totalMediaCounter: Value(oldContact.totalMediaCounter), + alsoBestFriend: Value(oldContact.alsoBestFriend), + createdAt: Value(oldContact.createdAt), + lastFlameCounterChange: Value(oldContact.lastFlameCounterChange), + lastFlameSync: Value(oldContact.lastFlameSync), + lastMessageExchange: Value(oldContact.lastMessageExchange), + lastMessageReceived: Value(oldContact.lastMessageReceived), + lastMessageSend: Value(oldContact.lastMessageSend), + flameCounter: Value(oldContact.flameCounter), + maxFlameCounter: Value(oldContact.flameCounter), + maxFlameCounterFrom: Value(DateTime.now()), + ), + ); + } catch (e) { + Log.error(e); } } - final memoriesPath = Directory( - join((await getApplicationSupportDirectory()).path, 'media', 'memories'), - ); - if (memoriesPath.existsSync()) { - final files = memoriesPath.listSync(); - for (final file in files) { - if (file.path.contains('thumbnail')) continue; - final type = - file.path.contains('mp4') ? MediaType.video : MediaType.image; - final stat = FileStat.statSync(file.path); - final mediaFile = await twonlyDB.mediaFilesDao.insertMedia( - MediaFilesCompanion( - type: Value(type), - createdAt: Value(stat.modified), - stored: const Value(true), - ), - ); - final mediaService = await MediaFileService.fromMedia(mediaFile!); - File(file.path).copySync(mediaService.storedPath.path); - setState(() { - _storedMediaFiles += 1; - }); + final folders = ['memories', 'send', 'received']; + + final alreadyCopied = HashSet(); + + for (final folder in folders) { + final memoriesPath = Directory( + join( + (await getApplicationSupportDirectory()).path, + 'media', + folder, + ), + ); + if (memoriesPath.existsSync()) { + final files = memoriesPath.listSync(); + for (final file in files) { + try { + if (file.path.contains('thumbnail')) continue; + late MediaType type; + if (file.path.contains('mp4')) { + type = MediaType.video; + } else if (file.path.contains('png')) { + type = MediaType.image; + } else { + continue; + } + + final bytes = File(file.path).readAsBytesSync(); + final digest = (await Sha256().hash(bytes)).bytes; + if (alreadyCopied.contains(digest)) { + continue; + } + alreadyCopied.add(digest); + + final stat = FileStat.statSync(file.path); + final mediaFile = await twonlyDB.mediaFilesDao.insertMedia( + MediaFilesCompanion( + type: Value(type), + createdAt: Value(stat.modified), + stored: const Value(true), + ), + ); + final mediaService = await MediaFileService.fromMedia(mediaFile!); + File(file.path).copySync(mediaService.storedPath.path); + setState(() { + _storedMediaFiles += 1; + }); + } catch (e) { + Log.error(e); + } + } } }