mirror of
https://github.com/BlueBubblesApp/bluebubbles-app.git
synced 2025-08-06 19:44:08 +08:00
Initial web listeners implementation for message updates
This commit is contained in:
@ -280,8 +280,9 @@ class ChatSubtitle extends CustomStateful<ConversationTileController> {
|
|||||||
class _ChatSubtitleState extends CustomState<ChatSubtitle, void, ConversationTileController> {
|
class _ChatSubtitleState extends CustomState<ChatSubtitle, void, ConversationTileController> {
|
||||||
String subtitle = "Unknown";
|
String subtitle = "Unknown";
|
||||||
String fakeText = faker.lorem.words(1).join(" ");
|
String fakeText = faker.lorem.words(1).join(" ");
|
||||||
late final StreamSubscription<Query<Message>> sub;
|
late final StreamSubscription sub;
|
||||||
String? cachedLatestMessageGuid = "";
|
String? cachedLatestMessageGuid = "";
|
||||||
|
DateTime? cachedDateCreated;
|
||||||
bool isDelivered = false;
|
bool isDelivered = false;
|
||||||
bool isFromMe = false;
|
bool isFromMe = false;
|
||||||
|
|
||||||
@ -332,12 +333,36 @@ class _ChatSubtitleState extends CustomState<ChatSubtitle, void, ConversationTil
|
|||||||
cachedLatestMessageGuid = message?.guid;
|
cachedLatestMessageGuid = message?.guid;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
sub = WebListeners.newMessage.listen((tuple) {
|
||||||
|
final message = tuple.item1;
|
||||||
|
if (tuple.item2?.guid == controller.chat.guid && (cachedDateCreated == null || message.dateCreated!.isAfter(cachedDateCreated!))) {
|
||||||
|
isFromMe = message.isFromMe ?? false;
|
||||||
|
isDelivered = controller.chat.isGroup || !isFromMe || message.dateDelivered != null || message.dateRead != null;
|
||||||
|
if (message.guid != cachedLatestMessageGuid) {
|
||||||
|
String newSubtitle = MessageHelper.getNotificationText(message);
|
||||||
|
if (newSubtitle != subtitle) {
|
||||||
|
setState(() {
|
||||||
|
subtitle = newSubtitle;
|
||||||
|
fakeText = faker.lorem.words(subtitle.split(" ").length).join(" ");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (!controller.chat.isGroup
|
||||||
|
&& message.isFromMe!
|
||||||
|
&& (message.dateDelivered != null || message.dateRead != null)) {
|
||||||
|
// update delivered status
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
cachedDateCreated = message.dateCreated;
|
||||||
|
cachedLatestMessageGuid = message.guid;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (!kIsWeb) sub.cancel();
|
sub.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ class CupertinoTrailing extends CustomStateful<ConversationTileController> {
|
|||||||
|
|
||||||
class _CupertinoTrailingState extends CustomState<CupertinoTrailing, void, ConversationTileController> {
|
class _CupertinoTrailingState extends CustomState<CupertinoTrailing, void, ConversationTileController> {
|
||||||
DateTime? dateCreated;
|
DateTime? dateCreated;
|
||||||
late final StreamSubscription<Query<Message>> sub;
|
late final StreamSubscription sub;
|
||||||
String? cachedLatestMessageGuid = "";
|
String? cachedLatestMessageGuid = "";
|
||||||
Message? cachedLatestMessage;
|
Message? cachedLatestMessage;
|
||||||
|
|
||||||
@ -162,12 +162,22 @@ class _CupertinoTrailingState extends CustomState<CupertinoTrailing, void, Conve
|
|||||||
cachedLatestMessageGuid = message?.guid;
|
cachedLatestMessageGuid = message?.guid;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
sub = WebListeners.newMessage.listen((tuple) {
|
||||||
|
if (tuple.item2?.guid == controller.chat.guid && (dateCreated == null || tuple.item1.dateCreated!.isAfter(dateCreated!))) {
|
||||||
|
cachedLatestMessage = tuple.item1;
|
||||||
|
setState(() {
|
||||||
|
dateCreated = tuple.item1.dateCreated;
|
||||||
|
});
|
||||||
|
cachedLatestMessageGuid = tuple.item1.guid;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (!kIsWeb) sub.cancel();
|
sub.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ class _MaterialTrailingState extends CustomState<MaterialTrailing, void, Convers
|
|||||||
DateTime? dateCreated;
|
DateTime? dateCreated;
|
||||||
bool unread = false;
|
bool unread = false;
|
||||||
String muteType = "";
|
String muteType = "";
|
||||||
late final StreamSubscription<Query<Message>> sub;
|
late final StreamSubscription sub;
|
||||||
late final StreamSubscription<Query<Chat>> sub2;
|
late final StreamSubscription<Query<Chat>> sub2;
|
||||||
String? cachedLatestMessageGuid = "";
|
String? cachedLatestMessageGuid = "";
|
||||||
Message? cachedLatestMessage;
|
Message? cachedLatestMessage;
|
||||||
@ -216,13 +216,23 @@ class _MaterialTrailingState extends CustomState<MaterialTrailing, void, Convers
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
sub = WebListeners.newMessage.listen((tuple) {
|
||||||
|
if (tuple.item2?.guid == controller.chat.guid && (dateCreated == null || tuple.item1.dateCreated!.isAfter(dateCreated!))) {
|
||||||
|
cachedLatestMessage = tuple.item1;
|
||||||
|
setState(() {
|
||||||
|
dateCreated = tuple.item1.dateCreated;
|
||||||
|
});
|
||||||
|
cachedLatestMessageGuid = tuple.item1.guid;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
sub.cancel();
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
sub.cancel();
|
|
||||||
sub2.cancel();
|
sub2.cancel();
|
||||||
}
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
@ -32,8 +32,9 @@ class PinnedTileTextBubbleState extends CustomState<PinnedTileTextBubble, void,
|
|||||||
Message? lastMessage;
|
Message? lastMessage;
|
||||||
String subtitle = "Unknown";
|
String subtitle = "Unknown";
|
||||||
String fakeText = faker.lorem.words(1).join(" ");
|
String fakeText = faker.lorem.words(1).join(" ");
|
||||||
late final StreamSubscription<Query<Message>> sub;
|
late final StreamSubscription sub;
|
||||||
String? cachedLatestMessageGuid = "";
|
String? cachedLatestMessageGuid = "";
|
||||||
|
DateTime? cachedDateCreated;
|
||||||
late bool unread = chat.hasUnreadMessage ?? false;
|
late bool unread = chat.hasUnreadMessage ?? false;
|
||||||
late final StreamSubscription<Query<Chat>> sub2;
|
late final StreamSubscription<Query<Chat>> sub2;
|
||||||
|
|
||||||
@ -98,13 +99,30 @@ class PinnedTileTextBubbleState extends CustomState<PinnedTileTextBubble, void,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
sub = WebListeners.newMessage.listen((tuple) {
|
||||||
|
final message = tuple.item1;
|
||||||
|
if (tuple.item2?.guid == controller.chat.guid && (cachedDateCreated == null || message.dateCreated!.isAfter(cachedDateCreated!))) {
|
||||||
|
if (message.guid != cachedLatestMessageGuid) {
|
||||||
|
String newSubtitle = MessageHelper.getNotificationText(message);
|
||||||
|
if (newSubtitle != subtitle) {
|
||||||
|
setState(() {
|
||||||
|
subtitle = newSubtitle;
|
||||||
|
fakeText = faker.lorem.words(subtitle.split(" ").length).join(" ");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedDateCreated = message.dateCreated;
|
||||||
|
cachedLatestMessageGuid = message.guid;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
sub.cancel();
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
sub.cancel();
|
|
||||||
sub2.cancel();
|
sub2.cancel();
|
||||||
}
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
@ -96,7 +96,7 @@ class _SamsungTrailingState extends CustomState<SamsungTrailing, void, Conversat
|
|||||||
DateTime? dateCreated;
|
DateTime? dateCreated;
|
||||||
bool unread = false;
|
bool unread = false;
|
||||||
String muteType = "";
|
String muteType = "";
|
||||||
late final StreamSubscription<Query<Message>> sub;
|
late final StreamSubscription sub;
|
||||||
late final StreamSubscription<Query<Chat>> sub2;
|
late final StreamSubscription<Query<Chat>> sub2;
|
||||||
String? cachedLatestMessageGuid = "";
|
String? cachedLatestMessageGuid = "";
|
||||||
Message? cachedLatestMessage;
|
Message? cachedLatestMessage;
|
||||||
@ -160,6 +160,16 @@ class _SamsungTrailingState extends CustomState<SamsungTrailing, void, Conversat
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
sub = WebListeners.newMessage.listen((tuple) {
|
||||||
|
if (tuple.item2?.guid == controller.chat.guid && (dateCreated == null || tuple.item1.dateCreated!.isAfter(dateCreated!))) {
|
||||||
|
cachedLatestMessage = tuple.item1;
|
||||||
|
setState(() {
|
||||||
|
dateCreated = tuple.item1.dateCreated;
|
||||||
|
});
|
||||||
|
cachedLatestMessageGuid = tuple.item1.guid;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class ReactionWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class ReactionWidgetState extends OptimizedState<ReactionWidget> {
|
class ReactionWidgetState extends OptimizedState<ReactionWidget> {
|
||||||
late Message reaction = widget.reaction;
|
late Message reaction = widget.reaction;
|
||||||
late final StreamSubscription<Query<Message>> sub;
|
late final StreamSubscription sub;
|
||||||
bool hasStream = false;
|
bool hasStream = false;
|
||||||
|
|
||||||
List<Message>? get reactions => widget.reactions;
|
List<Message>? get reactions => widget.reactions;
|
||||||
@ -61,13 +61,22 @@ class ReactionWidgetState extends OptimizedState<ReactionWidget> {
|
|||||||
} else {
|
} else {
|
||||||
reaction = _message;
|
reaction = _message;
|
||||||
}
|
}
|
||||||
if (widget.message != null) {
|
getActiveMwc(widget.message!.guid!)?.updateAssociatedMessage(reaction, updateHolder: false);
|
||||||
getActiveMwc(widget.message!.guid!)?.updateAssociatedMessage(reaction, updateHolder: false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
hasStream = true;
|
hasStream = true;
|
||||||
|
} else if (kIsWeb && widget.message != null) {
|
||||||
|
sub = WebListeners.messageUpdate.listen((tuple) {
|
||||||
|
final _message = tuple.item1;
|
||||||
|
final tempGuid = tuple.item2;
|
||||||
|
if (tempGuid == reaction.guid || _message.guid == reaction.guid) {
|
||||||
|
setState(() {
|
||||||
|
reaction = _message;
|
||||||
|
});
|
||||||
|
getActiveMwc(widget.message!.guid!)?.updateAssociatedMessage(reaction, updateHolder: false);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -171,6 +171,8 @@ class Chat {
|
|||||||
bool updateDisplayName = false,
|
bool updateDisplayName = false,
|
||||||
bool updateDateDeleted = false,
|
bool updateDateDeleted = false,
|
||||||
}) {
|
}) {
|
||||||
|
// ignore: argument_type_not_assignable, return_of_invalid_type, invalid_assignment, for_in_of_invalid_element_type
|
||||||
|
WebListeners.notifyChat(this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,18 +207,30 @@ class Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Message save({Chat? chat}) {
|
Message save({Chat? chat}) {
|
||||||
|
// ignore: argument_type_not_assignable, return_of_invalid_type, invalid_assignment, for_in_of_invalid_element_type
|
||||||
|
WebListeners.notifyMessage(this, chat: chat);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<Message>> bulkSaveNewMessages(Chat chat, List<Message> messages) async {
|
static Future<List<Message>> bulkSaveNewMessages(Chat chat, List<Message> messages) async {
|
||||||
|
for (Message m in messages) {
|
||||||
|
// ignore: argument_type_not_assignable, return_of_invalid_type, invalid_assignment, for_in_of_invalid_element_type
|
||||||
|
WebListeners.notifyMessage(m, chat: chat);
|
||||||
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Message> bulkSave(List<Message> messages) {
|
static List<Message> bulkSave(List<Message> messages) {
|
||||||
|
for (Message m in messages) {
|
||||||
|
// ignore: argument_type_not_assignable, return_of_invalid_type, invalid_assignment, for_in_of_invalid_element_type
|
||||||
|
WebListeners.notifyMessage(m);
|
||||||
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Message> replaceMessage(String? oldGuid, Message newMessage, {bool awaitNewMessageEvent = true, Chat? chat}) async {
|
static Future<Message> replaceMessage(String? oldGuid, Message newMessage, {bool awaitNewMessageEvent = true, Chat? chat}) async {
|
||||||
|
// ignore: argument_type_not_assignable, return_of_invalid_type, invalid_assignment, for_in_of_invalid_element_type
|
||||||
|
WebListeners.notifyMessage(newMessage, tempGuid: oldGuid, chat: chat);
|
||||||
return newMessage;
|
return newMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ class ActionHandler extends GetxService {
|
|||||||
if (tempGuid != null) return;
|
if (tempGuid != null) return;
|
||||||
Logger.info("New message: [${m.text}] - for chat [${c.guid}]", tag: "ActionHandler");
|
Logger.info("New message: [${m.text}] - for chat [${c.guid}]", tag: "ActionHandler");
|
||||||
// Gets the chat from the db or server (if new)
|
// Gets the chat from the db or server (if new)
|
||||||
c = m.isParticipantEvent ? await handleNewOrUpdatedChat(c) : (Chat.findOne(guid: c.guid) ?? await handleNewOrUpdatedChat(c));
|
c = m.isParticipantEvent ? await handleNewOrUpdatedChat(c) : kIsWeb ? c : (Chat.findOne(guid: c.guid) ?? await handleNewOrUpdatedChat(c));
|
||||||
// Get the message handle
|
// Get the message handle
|
||||||
m.handle = c.handles.firstWhereOrNull((e) => e.originalROWID == m.handleId) ?? Handle.findOne(originalROWID: m.handleId);
|
m.handle = c.handles.firstWhereOrNull((e) => e.originalROWID == m.handleId) ?? Handle.findOne(originalROWID: m.handleId);
|
||||||
// Display notification if needed and save everything to DB
|
// Display notification if needed and save everything to DB
|
||||||
|
@ -99,6 +99,13 @@ class NotificationsService extends GetxService {
|
|||||||
}
|
}
|
||||||
currentCount = newCount;
|
currentCount = newCount;
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
countSub = WebListeners.newMessage.listen((tuple) {
|
||||||
|
final activeChatFetching = cm.activeChat != null ? ms(cm.activeChat!.chat.guid).isFetching : false;
|
||||||
|
if (ls.isAlive && !activeChatFetching && tuple.item2 != null) {
|
||||||
|
MessageHelper.handleNotification(tuple.item1, tuple.item2!, findExisting: false);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
lib/services/backend/web/listeners.dart
Normal file
51
lib/services/backend/web/listeners.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:bluebubbles/models/models.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
/// Class to replace objectbox DB listener functionality with an old-fashioned
|
||||||
|
/// stream based listener
|
||||||
|
class WebListeners {
|
||||||
|
static final Set<String> _messageGuids = {};
|
||||||
|
static final Set<String> _chatGuids = {};
|
||||||
|
|
||||||
|
static final StreamController<Tuple3<Message, String?, Chat?>> _messageUpdate = StreamController.broadcast();
|
||||||
|
static final StreamController<Tuple2<Message, Chat?>> _newMessage = StreamController.broadcast();
|
||||||
|
|
||||||
|
static final StreamController<Chat> _chatUpdate = StreamController.broadcast();
|
||||||
|
static final StreamController<Chat> _newChat = StreamController.broadcast();
|
||||||
|
|
||||||
|
static Stream<Tuple3<Message, String?, Chat?>> get messageUpdate => _messageUpdate.stream;
|
||||||
|
static Stream<Tuple2<Message, Chat?>> get newMessage => _newMessage.stream;
|
||||||
|
|
||||||
|
static Stream<Chat> get chatUpdate => _chatUpdate.stream;
|
||||||
|
static Stream<Chat> get newChat => _newChat.stream;
|
||||||
|
|
||||||
|
static void notifyMessage(Message m, {Chat? chat, String? tempGuid}) {
|
||||||
|
if (tempGuid != null) {
|
||||||
|
if (_messageGuids.contains(tempGuid)) {
|
||||||
|
_messageGuids.add(m.guid!);
|
||||||
|
_messageUpdate.add(Tuple3(m, tempGuid, chat));
|
||||||
|
} else {
|
||||||
|
_messageGuids.add(tempGuid);
|
||||||
|
_newMessage.add(Tuple2(m, chat));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_messageGuids.contains(m.guid)) {
|
||||||
|
_messageUpdate.add(Tuple3(m, null, chat));
|
||||||
|
} else {
|
||||||
|
_messageGuids.add(m.guid!);
|
||||||
|
_newMessage.add(Tuple2(m, chat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void notifyChat(Chat c) {
|
||||||
|
if (_chatGuids.contains(c.guid)) {
|
||||||
|
_chatUpdate.add(c);
|
||||||
|
} else {
|
||||||
|
_chatGuids.add(c.guid);
|
||||||
|
_newChat.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ export 'backend/sync/incremental_sync_manager.dart';
|
|||||||
export 'backend/sync/sync_manager_impl.dart' show SyncStatus;
|
export 'backend/sync/sync_manager_impl.dart' show SyncStatus;
|
||||||
export 'backend/sync/sync_service.dart';
|
export 'backend/sync/sync_service.dart';
|
||||||
export 'backend/sync/tasks/sync_tasks.dart';
|
export 'backend/sync/tasks/sync_tasks.dart';
|
||||||
|
export 'backend/web/listeners.dart';
|
||||||
export 'backend/action_handler.dart';
|
export 'backend/action_handler.dart';
|
||||||
export 'backend_ui_interop/event_dispatcher.dart';
|
export 'backend_ui_interop/event_dispatcher.dart';
|
||||||
export 'backend_ui_interop/intents.dart';
|
export 'backend_ui_interop/intents.dart';
|
||||||
|
@ -47,6 +47,11 @@ class ChatsService extends GetxService {
|
|||||||
}
|
}
|
||||||
currentCount = newCount;
|
currentCount = newCount;
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
countSub = WebListeners.newChat.listen((chat) async {
|
||||||
|
if (!ss.settings.finishedSetup.value) return;
|
||||||
|
addChat(chat);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +117,7 @@ class ChatsService extends GetxService {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
if (!kIsWeb) {
|
countSub.cancel();
|
||||||
countSub.cancel();
|
|
||||||
}
|
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ MessageWidgetController? getActiveMwc(String guid) => Get.isRegistered<MessageWi
|
|||||||
|
|
||||||
class MessageWidgetController extends StatefulController with SingleGetTickerProviderMixin {
|
class MessageWidgetController extends StatefulController with SingleGetTickerProviderMixin {
|
||||||
final RxBool showEdits = false.obs;
|
final RxBool showEdits = false.obs;
|
||||||
bool _init = false;
|
|
||||||
|
|
||||||
List<MessagePart> parts = [];
|
List<MessagePart> parts = [];
|
||||||
Message message;
|
Message message;
|
||||||
@ -29,7 +28,7 @@ class MessageWidgetController extends StatefulController with SingleGetTickerPro
|
|||||||
String? newMessageGuid;
|
String? newMessageGuid;
|
||||||
ConversationViewController? cvController;
|
ConversationViewController? cvController;
|
||||||
late final String tag;
|
late final String tag;
|
||||||
late final StreamSubscription<Query<Message>> sub;
|
late final StreamSubscription sub;
|
||||||
|
|
||||||
static const maxBubbleSizeFactor = 0.75;
|
static const maxBubbleSizeFactor = 0.75;
|
||||||
|
|
||||||
@ -45,7 +44,6 @@ class MessageWidgetController extends StatefulController with SingleGetTickerPro
|
|||||||
super.onInit();
|
super.onInit();
|
||||||
buildMessageParts();
|
buildMessageParts();
|
||||||
if (!kIsWeb && message.id != null) {
|
if (!kIsWeb && message.id != null) {
|
||||||
_init = true;
|
|
||||||
final messageQuery = messageBox.query(Message_.id.equals(message.id!)).watch();
|
final messageQuery = messageBox.query(Message_.id.equals(message.id!)).watch();
|
||||||
sub = messageQuery.listen((Query<Message> query) async {
|
sub = messageQuery.listen((Query<Message> query) async {
|
||||||
if (message.id == null) return;
|
if (message.id == null) return;
|
||||||
@ -60,12 +58,20 @@ class MessageWidgetController extends StatefulController with SingleGetTickerPro
|
|||||||
updateMessage(_message);
|
updateMessage(_message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (kIsWeb) {
|
||||||
|
sub = WebListeners.messageUpdate.listen((tuple) {
|
||||||
|
final _message = tuple.item1;
|
||||||
|
final tempGuid = tuple.item2;
|
||||||
|
if (_message.guid == message.guid || tempGuid == message.guid) {
|
||||||
|
updateMessage(_message);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
if (_init) sub.cancel();
|
sub.cancel();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,10 +167,11 @@ class MessageWidgetController extends StatefulController with SingleGetTickerPro
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateMessage(Message newItem) {
|
void updateMessage(Message newItem) {
|
||||||
|
final chat = message.chat.target?.guid ?? cvController?.chat.guid ?? cm.activeChat!.chat.guid;
|
||||||
final oldGuid = message.guid;
|
final oldGuid = message.guid;
|
||||||
if (newItem.guid != oldGuid && oldGuid!.contains("temp")) {
|
if (newItem.guid != oldGuid && oldGuid!.contains("temp")) {
|
||||||
message = Message.merge(newItem, message);
|
message = Message.merge(newItem, message);
|
||||||
ms(message.chat.target!.guid).updateMessage(message, oldGuid: oldGuid);
|
ms(chat).updateMessage(message, oldGuid: oldGuid);
|
||||||
updateWidgets<MessageHolder>(null);
|
updateWidgets<MessageHolder>(null);
|
||||||
if (message.isFromMe! && message.attachments.isNotEmpty) {
|
if (message.isFromMe! && message.attachments.isNotEmpty) {
|
||||||
updateWidgets<AttachmentHolder>(null);
|
updateWidgets<AttachmentHolder>(null);
|
||||||
@ -172,9 +179,9 @@ class MessageWidgetController extends StatefulController with SingleGetTickerPro
|
|||||||
} else if (newItem.dateDelivered != message.dateDelivered || newItem.dateRead != message.dateRead || newItem.didNotifyRecipient != message.didNotifyRecipient) {
|
} else if (newItem.dateDelivered != message.dateDelivered || newItem.dateRead != message.dateRead || newItem.didNotifyRecipient != message.didNotifyRecipient) {
|
||||||
final edited = newItem.dateEdited != message.dateEdited;
|
final edited = newItem.dateEdited != message.dateEdited;
|
||||||
message = Message.merge(newItem, message);
|
message = Message.merge(newItem, message);
|
||||||
ms(message.chat.target!.guid).updateMessage(message);
|
ms(chat).updateMessage(message);
|
||||||
// update the latest 2 messages in case their indicators need to go away
|
// update the latest 2 messages in case their indicators need to go away
|
||||||
final messages = ms(message.chat.target!.guid).struct.messages
|
final messages = ms(chat).struct.messages
|
||||||
.where((e) => e.isFromMe! && (e.dateDelivered != null || e.dateRead != null))
|
.where((e) => e.isFromMe! && (e.dateDelivered != null || e.dateRead != null))
|
||||||
.toList()..sort((a, b) => b.dateCreated!.compareTo(a.dateCreated!));
|
.toList()..sort((a, b) => b.dateCreated!.compareTo(a.dateCreated!));
|
||||||
for (Message m in messages.take(2)) {
|
for (Message m in messages.take(2)) {
|
||||||
@ -190,7 +197,7 @@ class MessageWidgetController extends StatefulController with SingleGetTickerPro
|
|||||||
message = Message.merge(newItem, message);
|
message = Message.merge(newItem, message);
|
||||||
parts.clear();
|
parts.clear();
|
||||||
buildMessageParts();
|
buildMessageParts();
|
||||||
ms(message.chat.target!.guid).updateMessage(message);
|
ms(chat).updateMessage(message);
|
||||||
updateWidgets<MessageHolder>(null);
|
updateWidgets<MessageHolder>(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ class MessagesService extends GetxController {
|
|||||||
|
|
||||||
int currentCount = 0;
|
int currentCount = 0;
|
||||||
bool isFetching = false;
|
bool isFetching = false;
|
||||||
bool _init = false;
|
|
||||||
String? method;
|
String? method;
|
||||||
|
|
||||||
Message? get mostRecentSent => (struct.messages.where((e) => e.isFromMe!).toList()
|
Message? get mostRecentSent => (struct.messages.where((e) => e.isFromMe!).toList()
|
||||||
@ -49,7 +48,6 @@ class MessagesService extends GetxController {
|
|||||||
|
|
||||||
// watch for new messages
|
// watch for new messages
|
||||||
if (chat.id != null) {
|
if (chat.id != null) {
|
||||||
_init = true;
|
|
||||||
final countQuery = (messageBox.query(Message_.dateDeleted.isNull())
|
final countQuery = (messageBox.query(Message_.dateDeleted.isNull())
|
||||||
..link(Message_.chat, Chat_.id.equals(chat.id!))
|
..link(Message_.chat, Chat_.id.equals(chat.id!))
|
||||||
..order(Message_.id, flags: Order.descending)).watch(triggerImmediately: true);
|
..order(Message_.id, flags: Order.descending)).watch(triggerImmediately: true);
|
||||||
@ -61,38 +59,23 @@ class MessagesService extends GetxController {
|
|||||||
final messages = event.find();
|
final messages = event.find();
|
||||||
event.limit = 0;
|
event.limit = 0;
|
||||||
for (Message message in messages) {
|
for (Message message in messages) {
|
||||||
message.handle = message.getHandle();
|
await _handleNewMessage(message);
|
||||||
if (message.hasAttachments) {
|
|
||||||
message.attachments = List<Attachment>.from(message.dbAttachments);
|
|
||||||
// we may need an artificial delay in some cases since the attachment
|
|
||||||
// relation is initialized after message itself is saved
|
|
||||||
if (message.attachments.isEmpty) {
|
|
||||||
await Future.delayed(const Duration(milliseconds: 250));
|
|
||||||
message.attachments = List<Attachment>.from(message.dbAttachments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add this as a reaction if needed, update thread originators and associated messages
|
|
||||||
if (message.associatedMessageGuid != null) {
|
|
||||||
struct.getMessage(message.associatedMessageGuid!)?.associatedMessages.add(message);
|
|
||||||
getActiveMwc(message.associatedMessageGuid!)?.updateAssociatedMessage(message);
|
|
||||||
}
|
|
||||||
if (message.threadOriginatorGuid != null) {
|
|
||||||
getActiveMwc(message.threadOriginatorGuid!)?.updateThreadOriginator(message);
|
|
||||||
}
|
|
||||||
struct.addMessages([message]);
|
|
||||||
if (message.associatedMessageGuid == null) {
|
|
||||||
newFunc.call(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentCount = newCount;
|
currentCount = newCount;
|
||||||
});
|
});
|
||||||
|
} else if (kIsWeb) {
|
||||||
|
countSub = WebListeners.newMessage.listen((tuple) {
|
||||||
|
if (tuple.item2?.guid == chat.guid) {
|
||||||
|
_handleNewMessage(tuple.item1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
if (_init) countSub.cancel();
|
countSub.cancel();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +91,33 @@ class MessagesService extends GetxController {
|
|||||||
Get.reload<MessagesService>(tag: tag);
|
Get.reload<MessagesService>(tag: tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _handleNewMessage(Message message) async {
|
||||||
|
if (!kIsWeb) {
|
||||||
|
message.handle = message.getHandle();
|
||||||
|
}
|
||||||
|
if (message.hasAttachments && !kIsWeb) {
|
||||||
|
message.attachments = List<Attachment>.from(message.dbAttachments);
|
||||||
|
// we may need an artificial delay in some cases since the attachment
|
||||||
|
// relation is initialized after message itself is saved
|
||||||
|
if (message.attachments.isEmpty) {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 250));
|
||||||
|
message.attachments = List<Attachment>.from(message.dbAttachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add this as a reaction if needed, update thread originators and associated messages
|
||||||
|
if (message.associatedMessageGuid != null) {
|
||||||
|
struct.getMessage(message.associatedMessageGuid!)?.associatedMessages.add(message);
|
||||||
|
getActiveMwc(message.associatedMessageGuid!)?.updateAssociatedMessage(message);
|
||||||
|
}
|
||||||
|
if (message.threadOriginatorGuid != null) {
|
||||||
|
getActiveMwc(message.threadOriginatorGuid!)?.updateThreadOriginator(message);
|
||||||
|
}
|
||||||
|
struct.addMessages([message]);
|
||||||
|
if (message.associatedMessageGuid == null) {
|
||||||
|
newFunc.call(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void updateMessage(Message updated, {String? oldGuid}) {
|
void updateMessage(Message updated, {String? oldGuid}) {
|
||||||
final toUpdate = struct.getMessage(oldGuid ?? updated.guid!);
|
final toUpdate = struct.getMessage(oldGuid ?? updated.guid!);
|
||||||
if (toUpdate == null) return;
|
if (toUpdate == null) return;
|
||||||
|
Reference in New Issue
Block a user