mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-07-18 01:14:07 +00:00
fix performance issue
This commit is contained in:
parent
9faa4fc49a
commit
7a8901cd2d
3 changed files with 103 additions and 51 deletions
|
|
@ -1,12 +1,13 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:hashlib/random.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/utils/log.dart';
|
||||
|
||||
part 'mediafiles.dao.g.dart';
|
||||
|
||||
@DriftAccessor(tables: [MediaFiles])
|
||||
@DriftAccessor(tables: [MediaFiles, Messages])
|
||||
class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
||||
with _$MediaFilesDaoMixin {
|
||||
// this constructor is required so that the main database can create an instance
|
||||
|
|
@ -142,7 +143,9 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
|||
final query =
|
||||
(select(mediaFiles)..where((t) => t.stored.equals(true))).join([])
|
||||
..groupBy([
|
||||
const CustomExpression<Object>('COALESCE(stored_file_hash, media_id)')
|
||||
const CustomExpression<Object>(
|
||||
'COALESCE(stored_file_hash, media_id)',
|
||||
),
|
||||
]);
|
||||
return query.map((row) => row.readTable(mediaFiles)).watch();
|
||||
}
|
||||
|
|
@ -154,6 +157,17 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
|||
.watch();
|
||||
}
|
||||
|
||||
Stream<List<MediaFile>> watchMediaFilesForGroup(String groupId) {
|
||||
final query = select(mediaFiles).join([
|
||||
innerJoin(
|
||||
db.messages,
|
||||
db.messages.mediaId.equalsExp(mediaFiles.mediaId),
|
||||
useColumns: false,
|
||||
),
|
||||
])..where(db.messages.groupId.equals(groupId));
|
||||
return query.map((row) => row.readTable(mediaFiles)).watch();
|
||||
}
|
||||
|
||||
Future<void> updateAllRetransmissionUploadingState() async {
|
||||
await (update(mediaFiles)..where(
|
||||
(t) =>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ part of 'mediafiles.dao.dart';
|
|||
// ignore_for_file: type=lint
|
||||
mixin _$MediaFilesDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||
$MediaFilesTable get mediaFiles => attachedDatabase.mediaFiles;
|
||||
$GroupsTable get groups => attachedDatabase.groups;
|
||||
$ContactsTable get contacts => attachedDatabase.contacts;
|
||||
$MessagesTable get messages => attachedDatabase.messages;
|
||||
MediaFilesDaoManager get managers => MediaFilesDaoManager(this);
|
||||
}
|
||||
|
||||
|
|
@ -13,4 +16,10 @@ class MediaFilesDaoManager {
|
|||
MediaFilesDaoManager(this._db);
|
||||
$$MediaFilesTableTableManager get mediaFiles =>
|
||||
$$MediaFilesTableTableManager(_db.attachedDatabase, _db.mediaFiles);
|
||||
$$GroupsTableTableManager get groups =>
|
||||
$$GroupsTableTableManager(_db.attachedDatabase, _db.groups);
|
||||
$$ContactsTableTableManager get contacts =>
|
||||
$$ContactsTableTableManager(_db.attachedDatabase, _db.contacts);
|
||||
$$MessagesTableTableManager get messages =>
|
||||
$$MessagesTableTableManager(_db.attachedDatabase, _db.messages);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
|
||||
import 'package:twonly/locator.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||
|
|
@ -70,25 +70,27 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
);
|
||||
if (!mounted) return;
|
||||
_lastMessageStream = lastMsgStream.listen((update) {
|
||||
protectUpdateState.protect(() async {
|
||||
await updateState(update, _messagesNotOpened);
|
||||
});
|
||||
_updateState(update, _messagesNotOpened);
|
||||
});
|
||||
|
||||
_lastReactionStream = twonlyDB.reactionsDao.watchLastReactions(widget.group.groupId).listen((update) {
|
||||
_lastReactionStream = twonlyDB.reactionsDao
|
||||
.watchLastReactions(widget.group.groupId)
|
||||
.listen((update) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_lastReaction = update;
|
||||
});
|
||||
});
|
||||
|
||||
_messagesNotOpenedStream = twonlyDB.messagesDao.watchMessageNotOpened(widget.group.groupId).listen((update) {
|
||||
protectUpdateState.protect(() async {
|
||||
await updateState(_lastMessage, update);
|
||||
});
|
||||
_messagesNotOpenedStream = twonlyDB.messagesDao
|
||||
.watchMessageNotOpened(widget.group.groupId)
|
||||
.listen((update) {
|
||||
_updateState(_lastMessage, update);
|
||||
});
|
||||
|
||||
_lastMediaFilesStream = twonlyDB.mediaFilesDao.watchNewestMediaFiles().listen((mediaFiles) {
|
||||
_lastMediaFilesStream = twonlyDB.mediaFilesDao
|
||||
.watchMediaFilesForGroup(widget.group.groupId)
|
||||
.listen((mediaFiles) {
|
||||
if (!mounted) return;
|
||||
for (final mediaFile in mediaFiles) {
|
||||
final index = _previewMediaFiles.indexWhere(
|
||||
|
|
@ -110,19 +112,21 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
}
|
||||
}
|
||||
|
||||
Mutex protectUpdateState = Mutex();
|
||||
|
||||
Future<void> updateState(
|
||||
void _updateState(
|
||||
Message? newLastMessage,
|
||||
List<Message> newMessagesNotOpened,
|
||||
) async {
|
||||
) {
|
||||
if (!mounted) return;
|
||||
if (newLastMessage == null) {
|
||||
// there are no messages at all
|
||||
_currentMessage = null;
|
||||
_previewMessages = [];
|
||||
} else if (newMessagesNotOpened.isNotEmpty) {
|
||||
// Filter for the preview non opened messages. First messages which where send but not yet opened by the other side.
|
||||
final receivedMessages = newMessagesNotOpened.where((x) => x.senderId != null).toList();
|
||||
// Filter for the preview non opened messages. First messages which where
|
||||
// send but not yet opened by the other side.
|
||||
final receivedMessages = newMessagesNotOpened
|
||||
.where((x) => x.senderId != null)
|
||||
.toList();
|
||||
|
||||
if (receivedMessages.isNotEmpty) {
|
||||
_previewMessages = receivedMessages;
|
||||
|
|
@ -133,8 +137,9 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
}
|
||||
} else {
|
||||
// there are no not opened messages show just the last message in the table
|
||||
// only shows the last message in case there was no newer messages which already got deleted
|
||||
// This prevents, that it will show that a images got stored 10 days ago...
|
||||
// only shows the last message in case there was no newer messages which
|
||||
// already got deleted. This prevents showing that an image got stored 10
|
||||
// days ago...
|
||||
if (newLastMessage.createdAt.isAfter(
|
||||
widget.group.lastMessageExchange.subtract(const Duration(days: 2)),
|
||||
)) {
|
||||
|
|
@ -146,7 +151,9 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
}
|
||||
}
|
||||
|
||||
final msgs = _previewMessages.where((x) => x.type == MessageType.media.name).toList();
|
||||
final msgs = _previewMessages
|
||||
.where((x) => x.type == MessageType.media.name)
|
||||
.toList();
|
||||
if (msgs.isNotEmpty &&
|
||||
msgs.first.type == MessageType.media.name &&
|
||||
!msgs.first.isDeletedFromSender &&
|
||||
|
|
@ -157,20 +164,30 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
_hasNonOpenedMediaFile = false;
|
||||
}
|
||||
|
||||
_lastMessage = newLastMessage;
|
||||
_messagesNotOpened = newMessagesNotOpened;
|
||||
setState(() {});
|
||||
|
||||
// Only fetch on first load when a mediaId is not yet cached.
|
||||
_fetchMissingMediaFiles();
|
||||
}
|
||||
|
||||
/// Fetches any media files referenced by preview messages but not yet in the
|
||||
/// local cache. Fire-and-forget; updates state when results arrive.
|
||||
Future<void> _fetchMissingMediaFiles() async {
|
||||
for (final message in _previewMessages) {
|
||||
if (message.mediaId != null && !_previewMediaFiles.any((t) => t.mediaId == message.mediaId)) {
|
||||
if (message.mediaId != null &&
|
||||
!_previewMediaFiles.any((t) => t.mediaId == message.mediaId)) {
|
||||
final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(
|
||||
message.mediaId!,
|
||||
);
|
||||
if (mediaFile != null) {
|
||||
if (mediaFile != null && mounted) {
|
||||
setState(() {
|
||||
_previewMediaFiles.add(mediaFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lastMessage = newLastMessage;
|
||||
_messagesNotOpened = newMessagesNotOpened;
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> onTap() async {
|
||||
|
|
@ -183,7 +200,9 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
}
|
||||
|
||||
if (_hasNonOpenedMediaFile) {
|
||||
final msgs = _previewMessages.where((x) => x.type == MessageType.media.name).toList();
|
||||
final msgs = _previewMessages
|
||||
.where((x) => x.type == MessageType.media.name)
|
||||
.toList();
|
||||
final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(
|
||||
msgs.first.mediaId!,
|
||||
);
|
||||
|
|
@ -223,7 +242,10 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
const SizedBox(width: 3),
|
||||
VerificationBadgeComp(
|
||||
group: widget.group,
|
||||
showOnlyIfVerified: userService.currentUser.securityProfile.showOnlyVerifiedInChatViewList,
|
||||
showOnlyIfVerified: userService
|
||||
.currentUser
|
||||
.securityProfile
|
||||
.showOnlyVerifiedInChatViewList,
|
||||
clickable: false,
|
||||
size: 12,
|
||||
),
|
||||
|
|
@ -258,7 +280,8 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
),
|
||||
const Text('•'),
|
||||
const SizedBox(width: 5),
|
||||
if (_currentMessage != null) LastMessageTimeComp(message: _currentMessage),
|
||||
if (_currentMessage != null)
|
||||
LastMessageTimeComp(message: _currentMessage),
|
||||
FlameCounterWidget(
|
||||
groupId: widget.group.groupId,
|
||||
prefix: true,
|
||||
|
|
@ -272,7 +295,9 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
widget.group.groupId,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
await context.push(Routes.profileContact(contacts.first.userId));
|
||||
await context.push(
|
||||
Routes.profileContact(contacts.first.userId),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
await context.push(Routes.profileGroup(widget.group.groupId));
|
||||
|
|
@ -285,7 +310,9 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
: IconButton(
|
||||
onPressed: () {
|
||||
if (_hasNonOpenedMediaFile) {
|
||||
context.push(Routes.chatsMessages(widget.group.groupId));
|
||||
context.push(
|
||||
Routes.chatsMessages(widget.group.groupId),
|
||||
);
|
||||
} else {
|
||||
context.push(
|
||||
Routes.chatsCameraSendTo,
|
||||
|
|
@ -294,7 +321,9 @@ class _UserListItem extends State<GroupListItemComp> {
|
|||
}
|
||||
},
|
||||
icon: FaIcon(
|
||||
_hasNonOpenedMediaFile ? FontAwesomeIcons.solidComments : FontAwesomeIcons.camera,
|
||||
_hasNonOpenedMediaFile
|
||||
? FontAwesomeIcons.solidComments
|
||||
: FontAwesomeIcons.camera,
|
||||
color: context.color.outline.withAlpha(150),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in a new issue