From 85e6149b24951d851dd4fe216ab9e6e136c4be5e Mon Sep 17 00:00:00 2001 From: Magellan Date: Mon, 23 Feb 2026 13:43:33 +0200 Subject: [PATCH 1/9] fix last first message update separate space for messages --- sama_chat_client/lib/src/db/db_service.dart | 14 ++++++++++++-- .../db/local/message_local_datasource.dart | 5 +++-- .../lib/src/db/models/message_model.dart | 3 ++- .../conversation/view/messages_list.dart | 19 +++++++++++++++++-- .../messages/messages_repository.dart | 19 +++++++++++++++---- .../lib/src/shared/utils/list_utils.dart | 5 +++++ 6 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 sama_chat_client/lib/src/shared/utils/list_utils.dart diff --git a/sama_chat_client/lib/src/db/db_service.dart b/sama_chat_client/lib/src/db/db_service.dart index b0f2e7d7..95c4a54c 100644 --- a/sama_chat_client/lib/src/db/db_service.dart +++ b/sama_chat_client/lib/src/db/db_service.dart @@ -6,6 +6,7 @@ import 'package:path/path.dart' as path; import '../../objectbox.g.dart'; import '../features/conversation/models/chat_message.dart'; import 'models/attachment_model.dart'; +import 'models/avatar_model.dart'; import 'models/conversation_model.dart'; import 'models/message_model.dart'; import 'models/user_model.dart'; @@ -187,6 +188,13 @@ class DatabaseService { } if (chatInDb.avatar?.fileId == chat.avatar?.fileId) { chat.avatar?.bid = chatInDb.avatar?.bid; + if (chat.avatar?.imageUrl != chatInDb.avatar?.imageUrl) { + //to check + print('AMBRA chat.avatar putAsync'); + await store! + .box() + .putAsync(chat.avatar!, mode: PutMode.update); + } } if (chatInDb.lastMessage?.id == chat.lastMessage?.id) { chat.lastMessage?.bid = chatInDb.lastMessage?.bid; @@ -275,7 +283,7 @@ class DatabaseService { /// /////////////////////////// Future> getAllMessagesLocal( - String cid, DateTime? ltDate, int? limit) async { + String cid, DateTime? ltDate, DateTime? gtDate, int? limit) async { var condition = MessageModel_.cid .equals(cid) .and(MessageModel_.isTempReplied @@ -285,12 +293,14 @@ class DatabaseService { .or(MessageModel_.rawStatus.isNull())); if (ltDate != null) { condition = condition.and(MessageModel_.createdAt.lessThanDate(ltDate)); + } if (gtDate != null) { + condition = condition.and(MessageModel_.createdAt.greaterThanDate(gtDate)); } final query = store! .box() .query(condition) - .order(MessageModel_.createdAt, flags: Order.descending) + .order(MessageModel_.createdAt, flags: gtDate == null ? Order.descending : 0) .build() ..limit = limit ?? 0; final results = await query.findAsync(); diff --git a/sama_chat_client/lib/src/db/local/message_local_datasource.dart b/sama_chat_client/lib/src/db/local/message_local_datasource.dart index 5dfe4e93..fa6c160d 100644 --- a/sama_chat_client/lib/src/db/local/message_local_datasource.dart +++ b/sama_chat_client/lib/src/db/local/message_local_datasource.dart @@ -6,10 +6,11 @@ class MessageLocalDatasource { final DatabaseService _databaseService = DatabaseService.instance; Future> getAllMessagesLocal(String cid, - {DateTime? ltDate, int? limit}) async { + {DateTime? ltDate, DateTime? gtDate, int? limit}) async { print('getAllMessagesLocal'); try { - return await _databaseService.getAllMessagesLocal(cid, ltDate, limit); + return await _databaseService.getAllMessagesLocal( + cid, ltDate, gtDate, limit); } catch (e) { print('getAllMessagesLocal e ${e.toString()}'); throw DatabaseException(e.toString()); diff --git a/sama_chat_client/lib/src/db/models/message_model.dart b/sama_chat_client/lib/src/db/models/message_model.dart index 4db5a243..0fc2d94b 100644 --- a/sama_chat_client/lib/src/db/models/message_model.dart +++ b/sama_chat_client/lib/src/db/models/message_model.dart @@ -112,7 +112,8 @@ class MessageModel extends Equatable { } @override - List get props => [id, from, rawStatus, body, t]; + List get props => + [id, from, rawStatus, body, t, createdAt?.millisecondsSinceEpoch]; } extension MessageModelExtension on Message { diff --git a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart index bf6e7e20..bafeb958 100644 --- a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart +++ b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart @@ -5,6 +5,7 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../../../db/models/models.dart'; import '../../../shared/ui/colors.dart'; +import '../../../shared/utils/list_utils.dart'; import '../../../shared/utils/screen_factor.dart'; import '../../../shared/utils/string_utils.dart'; import '../bloc/conversation_bloc.dart'; @@ -163,8 +164,9 @@ class _MessagesListState extends State { itemScrollController: _scrollController, itemPositionsListener: itemPositionsListener, padding: EdgeInsets.zero, - separatorBuilder: (context, index) => const SizedBox( - height: 5, + separatorBuilder: (context, index) => SizedBox( + height: separateSpace(state.messages[index], + state.messages.tryGet(index + 1)), ), )); case ConversationStatus.initial: @@ -181,6 +183,19 @@ class _MessagesListState extends State { ])); } + double separateSpace(ChatMessage currentMsg, ChatMessage? prevMsg) { + int diffTime = 30; + + var prevMsgMs = (prevMsg?.createdAt!.millisecondsSinceEpoch ?? 0) ~/ 1000; + var msgMs = currentMsg.createdAt!.millisecondsSinceEpoch ~/ 1000; + var timeGap = (msgMs - prevMsgMs); + + bool isSameOwner = (prevMsg?.isOwn ?? false) && currentMsg.isOwn || + (!(prevMsg?.isOwn ?? false)) && !currentMsg.isOwn; + + return isSameOwner && timeGap < diffTime ? 2 : 15; + } + Widget get scrollFAB => ValueListenableBuilder>( valueListenable: itemPositionsListener.itemPositions, builder: (context, positions, child) { diff --git a/sama_chat_client/lib/src/repository/messages/messages_repository.dart b/sama_chat_client/lib/src/repository/messages/messages_repository.dart index 9c357622..6beff687 100644 --- a/sama_chat_client/lib/src/repository/messages/messages_repository.dart +++ b/sama_chat_client/lib/src/repository/messages/messages_repository.dart @@ -471,17 +471,28 @@ class MessagesRepository { List messages) async { var result = []; + final nextMsg = messages.isNotEmpty + ? (await localDatasource.getAllMessagesLocal(messages[0].cid, + gtDate: messages[0].createdAt, limit: 1)) + .firstOrNull + : null; + for (int i = 0; i < messages.length; i++) { var message = messages[i]; - var chatMessage = message.toChatMessage( - i == 0 || - isServiceMessage(messages[i - 1]) || - messages[i - 1].from != messages[i].from, + i == 0 + ? nextMsg?.from != messages[i].from + : isServiceMessage(messages[i - 1]) || + messages[i - 1].from != messages[i].from, i == messages.length - 1 || isServiceMessage(messages[i + 1]) || messages[i + 1].from != messages[i].from); + if (i == messages.length - 1 && limitMessages == messages.length) { + // do not put last message in result to determine if it's last for user with next pagination + continue; + } + result.add(chatMessage); } return result; diff --git a/sama_chat_client/lib/src/shared/utils/list_utils.dart b/sama_chat_client/lib/src/shared/utils/list_utils.dart new file mode 100644 index 00000000..23a48a50 --- /dev/null +++ b/sama_chat_client/lib/src/shared/utils/list_utils.dart @@ -0,0 +1,5 @@ +extension ListGetExtension on List { + T? tryGet(int index) { + return index >= 0 && index < length ? this[index] : null; + } +} From 68dc1bcdb714bbd00c28e16071b044a90785438d Mon Sep 17 00:00:00 2001 From: Magellan Date: Thu, 26 Feb 2026 15:47:49 +0200 Subject: [PATCH 2/9] add bubble type add update messages after delete adjacent --- .../conversation/bloc/conversation_bloc.dart | 32 +++++++++++ .../conversation/view/messages_list.dart | 53 +++++++++++++++---- .../conversation/widgets/message_bubble.dart | 48 +++++++++++++++-- .../widgets/text_message_item.dart | 3 ++ 4 files changed, 120 insertions(+), 16 deletions(-) diff --git a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart index 466576d5..422a36cd 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart @@ -13,6 +13,7 @@ import '../../../db/resource.dart'; import '../../../repository/conversation/conversation_repository.dart'; import '../../../repository/messages/messages_repository.dart'; import '../../../repository/user/user_repository.dart'; +import '../../../shared/utils/list_utils.dart'; import '../models/models.dart'; part 'conversation_event.dart'; @@ -452,6 +453,37 @@ class ConversationBloc extends Bloc { var messages = [...state.messages]; var messagesMap = {}..addEntries(messages.map((m) => MapEntry(m.id, m))); event.status.msgIds?.forEach((id) { + + int currentIndex = messages.indexOf(messagesMap[id]); + int prevIndex = currentIndex + 1; + int nextIndex = currentIndex - 1; + ChatMessage? prevMsg = messages.tryGet(prevIndex); + ChatMessage? nextMsg = messages.tryGet(nextIndex); + + var prevMsgUpdated = prevMsg?.copyWith( + isLastUserMessage: prevIndex == 0 || + isServiceMessage(messages[prevIndex - 2]) || + messages[prevIndex - 2].from != messages[prevIndex].from, + isFirstUserMessage: prevIndex == messages.length - 1 || + isServiceMessage(messages[prevIndex + 2]) || + messages[prevIndex + 2].from != messages[prevIndex].from); + + var nextMsgUpdated = nextMsg?.copyWith( + isLastUserMessage: nextIndex == 0 || + isServiceMessage(messages[nextIndex - 2]) || + messages[nextIndex - 2].from != messages[nextIndex].from, + isFirstUserMessage: nextIndex == messages.length - 1 || + isServiceMessage(messages[nextIndex + 2]) || + messages[nextIndex + 2].from != messages[nextIndex].from); + + if (prevMsgUpdated != null && prevMsgUpdated != prevMsg) { + messages[prevIndex] = prevMsgUpdated; + } + + if (nextMsgUpdated != null && nextMsgUpdated != nextMsg) { + messages[nextIndex] = nextMsgUpdated; + } + messages.remove(messagesMap[id]); }); emit(state.copyWith(messages: messages)); diff --git a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart index bafeb958..95966683 100644 --- a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart +++ b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart @@ -17,6 +17,7 @@ import '../widgets/focused_popup_menu.dart'; import '../widgets/forward_messages/forward_bubble.dart'; import '../widgets/forward_messages/forward_messages_widget.dart'; import '../widgets/media_attachment.dart'; +import '../widgets/message_bubble.dart'; import '../widgets/reply_bubble.dart'; import '../widgets/service_message_bubble.dart'; import '../widgets/text_message_item.dart'; @@ -142,6 +143,7 @@ class _MessagesListState extends State { ), child: MessageItem( message: msg, + bubbleType: bubbleType(state.messages, index), onTapReply: () { var replyIndex = state.messages.indexWhere( (item) => @@ -165,8 +167,7 @@ class _MessagesListState extends State { itemPositionsListener: itemPositionsListener, padding: EdgeInsets.zero, separatorBuilder: (context, index) => SizedBox( - height: separateSpace(state.messages[index], - state.messages.tryGet(index + 1)), + height: separateSpace(state.messages, index), ), )); case ConversationStatus.initial: @@ -183,17 +184,42 @@ class _MessagesListState extends State { ])); } - double separateSpace(ChatMessage currentMsg, ChatMessage? prevMsg) { + BubbleType bubbleType(List messages, int index) { + ChatMessage currentMsg = messages[index]; + ChatMessage? prevMsg = messages.tryGet(index + 1); + ChatMessage? nextMsg = messages.tryGet(index - 1); + + var prevSame = sameMsgGroup(currentMsg, prevMsg); + var nextSame = sameMsgGroup(currentMsg, nextMsg); + + BubbleType bubbleType = prevSame && nextSame + ? BubbleType.middle + : prevSame + ? BubbleType.upper + : nextSame + ? BubbleType.lower + : BubbleType.common; + + return bubbleType; + } + + bool sameMsgGroup(ChatMessage msg, ChatMessage? other) { int diffTime = 30; - var prevMsgMs = (prevMsg?.createdAt!.millisecondsSinceEpoch ?? 0) ~/ 1000; - var msgMs = currentMsg.createdAt!.millisecondsSinceEpoch ~/ 1000; - var timeGap = (msgMs - prevMsgMs); + bool isSameOwner = (other?.isOwn ?? false) && msg.isOwn || + (!(other?.isOwn ?? false)) && !msg.isOwn; + + var otherMs = (other?.createdAt?.millisecondsSinceEpoch ?? 0) ~/ 1000; + var msgMs = (msg.createdAt?.millisecondsSinceEpoch ?? 0) ~/ 1000; + var timeGap = (msgMs - otherMs).abs(); - bool isSameOwner = (prevMsg?.isOwn ?? false) && currentMsg.isOwn || - (!(prevMsg?.isOwn ?? false)) && !currentMsg.isOwn; + return isSameOwner && timeGap < diffTime; + } - return isSameOwner && timeGap < diffTime ? 2 : 15; + double separateSpace(List messages, int index) { + ChatMessage currentMsg = messages[index]; + ChatMessage? prevMsg = messages.tryGet(index + 1); + return sameMsgGroup(currentMsg, prevMsg) ? 2 : 15; } Widget get scrollFAB => ValueListenableBuilder>( @@ -284,9 +310,14 @@ class MessageItem extends StatelessWidget { final ChatMessage message; final VoidCallback? onTapReply; final VoidCallback? onTapForward; + final BubbleType bubbleType; const MessageItem( - {required this.message, this.onTapReply, this.onTapForward, super.key}); + {required this.message, + required this.bubbleType, + this.onTapReply, + this.onTapForward, + super.key}); @override Widget build(BuildContext context) { @@ -536,6 +567,6 @@ class MessageItem extends StatelessWidget { ); } - return TextMessageItem(message: message); + return TextMessageItem(message: message, bubbleType: bubbleType); } } diff --git a/sama_chat_client/lib/src/features/conversation/widgets/message_bubble.dart b/sama_chat_client/lib/src/features/conversation/widgets/message_bubble.dart index 64e61a5f..4ea72098 100644 --- a/sama_chat_client/lib/src/features/conversation/widgets/message_bubble.dart +++ b/sama_chat_client/lib/src/features/conversation/widgets/message_bubble.dart @@ -7,11 +7,14 @@ import '../../../shared/ui/colors.dart'; import '../../../shared/utils/string_utils.dart'; import '../../conversations_list/widgets/avatar_letter_icon.dart'; +enum BubbleType { common, upper, middle, lower } + class MessageBubble extends StatelessWidget { final UserModel sender; final bool isFirst; final bool isLast; final bool isOwn; + final BubbleType bubbleType; final Widget child; const MessageBubble({ @@ -21,6 +24,7 @@ class MessageBubble extends StatelessWidget { required this.isFirst, required this.isLast, required this.isOwn, + this.bubbleType = BubbleType.common, }); @override @@ -53,7 +57,8 @@ class MessageBubble extends StatelessWidget { painter: CustomChatBubble( color: isOwn ? slateBlue : gainsborough, isOwn: isOwn, - withTail: isLast), + withTail: isLast, + bubbleType: bubbleType), child: Container( constraints: const BoxConstraints(maxWidth: 300.0), margin: const EdgeInsets.symmetric(vertical: 4.0), @@ -91,9 +96,13 @@ class CustomChatBubble extends CustomPainter { final Color color; final bool isOwn; final bool withTail; + final BubbleType bubbleType; CustomChatBubble( - {required this.color, required this.isOwn, required this.withTail}); + {required this.color, + required this.isOwn, + required this.withTail, + required this.bubbleType}); @override void paint(Canvas canvas, Size size) { @@ -102,7 +111,7 @@ class CustomChatBubble extends CustomPainter { Path paintBubbleTail() { if (isOwn) { return Path() - ..moveTo(size.width - 15, size.height - 0) + ..moveTo(size.width - 17, size.height - 0) ..quadraticBezierTo( size.width - 10, size.height, size.width + 6, size.height - 0) ..quadraticBezierTo(size.width - 5, size.height - 5, size.width - 10, @@ -115,9 +124,38 @@ class CustomChatBubble extends CustomPainter { } } - final RRect bubbleBody = RRect.fromRectAndRadius( + Radius topRight = const Radius.circular(10); + Radius topLeft = const Radius.circular(10); + Radius bottomRight = const Radius.circular(10); + Radius bottomLeft = const Radius.circular(10); + + switch (bubbleType) { + case BubbleType.upper: + isOwn + ? topRight = const Radius.circular(2) + : topLeft = const Radius.circular(2); + case BubbleType.middle: + if (isOwn) { + topRight = const Radius.circular(2); + bottomRight = const Radius.circular(2); + } else { + topLeft = const Radius.circular(2); + bottomLeft = const Radius.circular(2); + } + case BubbleType.lower: + isOwn + ? bottomRight = const Radius.circular(2) + : bottomLeft = const Radius.circular(2); + case BubbleType.common: + break; + } + + final RRect bubbleBody = RRect.fromRectAndCorners( Rect.fromLTWH(isOwn ? -8 : 8, 0, size.width, size.height), - const Radius.circular(8)); + topLeft: topLeft, + topRight: topRight, + bottomLeft: bottomLeft, + bottomRight: bottomRight); canvas.drawRRect(bubbleBody, paint); diff --git a/sama_chat_client/lib/src/features/conversation/widgets/text_message_item.dart b/sama_chat_client/lib/src/features/conversation/widgets/text_message_item.dart index e86f2ea9..c9d6eaf9 100644 --- a/sama_chat_client/lib/src/features/conversation/widgets/text_message_item.dart +++ b/sama_chat_client/lib/src/features/conversation/widgets/text_message_item.dart @@ -10,10 +10,12 @@ import 'text_message.dart'; class TextMessageItem extends StatelessWidget { final ChatMessage message; + final BubbleType bubbleType; const TextMessageItem({ super.key, required this.message, + required this.bubbleType, }); @override @@ -23,6 +25,7 @@ class TextMessageItem extends StatelessWidget { isFirst: message.isFirstUserMessage, isLast: message.isLastUserMessage, isOwn: message.isOwn, + bubbleType: bubbleType, child: TextMessage( body: message.body ?? '', style: TextStyle(color: message.isOwn ? white : black, fontSize: 16.0), From 7c503b8e51a7df2854b8f194b3e4f1afc2398fbc Mon Sep 17 00:00:00 2001 From: Magellan Date: Tue, 3 Mar 2026 01:34:56 +0200 Subject: [PATCH 3/9] move message status to message model move building chatMessage to bloc --- sama_chat_client/lib/objectbox-model.json | 51 +- sama_chat_client/lib/objectbox.g.dart | 2221 +++++++++-------- sama_chat_client/lib/src/db/db_service.dart | 31 +- .../db/local/message_local_datasource.dart | 5 +- .../lib/src/db/models/message_model.dart | 46 +- .../conversation/bloc/conversation_bloc.dart | 69 +- .../conversation/bloc/conversation_event.dart | 2 +- .../bloc/send_message/send_message_bloc.dart | 2 +- .../conversation/models/chat_message.dart | 20 +- .../widgets/message_status_widget.dart | 10 +- .../conversation/conversation_repository.dart | 6 +- .../messages/messages_repository.dart | 108 +- .../messages_collector.dart | 10 +- 13 files changed, 1423 insertions(+), 1158 deletions(-) diff --git a/sama_chat_client/lib/objectbox-model.json b/sama_chat_client/lib/objectbox-model.json index afd921e2..7b7ef5f4 100644 --- a/sama_chat_client/lib/objectbox-model.json +++ b/sama_chat_client/lib/objectbox-model.json @@ -17,9 +17,9 @@ { "id": "2:747399038920962484", "name": "fileId", + "indexId": "37:873959491954871870", "type": 9, - "flags": 34848, - "indexId": "37:873959491954871870" + "flags": 34848 }, { "id": "3:768953523524252804", @@ -68,9 +68,9 @@ { "id": "2:5029774360479968412", "name": "fileId", + "indexId": "25:4244316179033347291", "type": 9, - "flags": 34848, - "indexId": "25:4244316179033347291" + "flags": 34848 }, { "id": "3:5415602239263669102", @@ -92,7 +92,7 @@ }, { "id": "11:6168919874786736956", - "lastPropertyId": "16:1698414372814552795", + "lastPropertyId": "17:8330640059358737630", "name": "MessageModel", "properties": [ { @@ -104,9 +104,9 @@ { "id": "2:3431085295353515294", "name": "id", + "indexId": "27:1312181892016791297", "type": 9, - "flags": 2080, - "indexId": "27:1312181892016791297" + "flags": 2080 }, { "id": "3:298933995628152516", @@ -118,11 +118,6 @@ "name": "cid", "type": 9 }, - { - "id": "5:6726139322816044811", - "name": "rawStatus", - "type": 9 - }, { "id": "6:6475269319788621443", "name": "body", @@ -151,17 +146,17 @@ { "id": "11:8502424052705749036", "name": "replyMessageBindId", + "indexId": "39:2280023974172957156", "type": 11, "flags": 520, - "indexId": "39:2280023974172957156", "relationTarget": "MessageModel" }, { "id": "12:6401592278627748216", "name": "senderBindId", + "indexId": "40:5803255922961343502", "type": 11, "flags": 520, - "indexId": "40:5803255922961343502", "relationTarget": "UserModel" }, { @@ -183,6 +178,11 @@ "id": "16:1698414372814552795", "name": "isEdited", "type": 1 + }, + { + "id": "17:8330640059358737630", + "name": "dbStatus", + "type": 6 } ], "relations": [ @@ -207,9 +207,9 @@ { "id": "2:1796029667524301994", "name": "id", + "indexId": "28:8383136850945105628", "type": 9, - "flags": 2080, - "indexId": "28:8383136850945105628" + "flags": 2080 }, { "id": "3:3523188585463807346", @@ -259,9 +259,9 @@ { "id": "12:4610750389367537300", "name": "avatarBindId", + "indexId": "24:2478963062982225581", "type": 11, "flags": 520, - "indexId": "24:2478963062982225581", "relationTarget": "AvatarModel" } ], @@ -281,9 +281,9 @@ { "id": "2:2579398636291900819", "name": "id", + "indexId": "29:4205296519667695880", "type": 9, - "flags": 2080, - "indexId": "29:4205296519667695880" + "flags": 2080 }, { "id": "3:7873950750532297915", @@ -318,33 +318,33 @@ { "id": "9:8543586135917731916", "name": "lastMessageBindId", + "indexId": "30:5882588671767701244", "type": 11, "flags": 520, - "indexId": "30:5882588671767701244", "relationTarget": "MessageModel" }, { "id": "12:8542803209060046040", "name": "avatarBindId", + "indexId": "33:7298741033559918238", "type": 11, "flags": 520, - "indexId": "33:7298741033559918238", "relationTarget": "AvatarModel" }, { "id": "13:6726414800751664493", "name": "opponentBindId", + "indexId": "34:2014479524742516296", "type": 11, "flags": 520, - "indexId": "34:2014479524742516296", "relationTarget": "UserModel" }, { "id": "14:5058682722642913901", "name": "ownerBindId", + "indexId": "35:2642777293408708995", "type": 11, "flags": 520, - "indexId": "35:2642777293408708995", "relationTarget": "UserModel" }, { @@ -355,9 +355,9 @@ { "id": "16:8214829155225431382", "name": "draftMessageBindId", + "indexId": "38:1718185030575189588", "type": 11, "flags": 520, - "indexId": "38:1718185030575189588", "relationTarget": "MessageModel" } ], @@ -475,7 +475,8 @@ 1773146609978416143, 7768713511998184494, 2456344090811030546, - 2261589455151034389 + 2261589455151034389, + 6726139322816044811 ], "retiredRelationUids": [ 4063380833838902563, diff --git a/sama_chat_client/lib/objectbox.g.dart b/sama_chat_client/lib/objectbox.g.dart index 305f71f8..532a3095 100644 --- a/sama_chat_client/lib/objectbox.g.dart +++ b/sama_chat_client/lib/objectbox.g.dart @@ -24,356 +24,418 @@ export 'package:objectbox/objectbox.dart'; // so that callers only have to impor final _entities = [ obx_int.ModelEntity( - id: const obx_int.IdUid(8, 6709400528399140612), - name: 'AttachmentModel', - lastPropertyId: const obx_int.IdUid(8, 4936282859728421093), - flags: 0, - properties: [ - obx_int.ModelProperty( - id: const obx_int.IdUid(1, 7263863558592423196), - name: 'bid', - type: 6, - flags: 1), - obx_int.ModelProperty( - id: const obx_int.IdUid(2, 747399038920962484), - name: 'fileId', - type: 9, - flags: 34848, - indexId: const obx_int.IdUid(37, 873959491954871870)), - obx_int.ModelProperty( - id: const obx_int.IdUid(3, 768953523524252804), - name: 'fileName', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(4, 2743437583742703683), - name: 'fileBlurHash', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(5, 5717160279142175066), - name: 'url', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(6, 4647869943489747714), - name: 'contentType', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(7, 3882295953503253830), - name: 'fileHeight', - type: 6, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(8, 4936282859728421093), - name: 'fileWidth', - type: 6, - flags: 0) - ], - relations: [], - backlinks: []), + id: const obx_int.IdUid(8, 6709400528399140612), + name: 'AttachmentModel', + lastPropertyId: const obx_int.IdUid(8, 4936282859728421093), + flags: 0, + properties: [ + obx_int.ModelProperty( + id: const obx_int.IdUid(1, 7263863558592423196), + name: 'bid', + type: 6, + flags: 1, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(2, 747399038920962484), + name: 'fileId', + type: 9, + flags: 34848, + indexId: const obx_int.IdUid(37, 873959491954871870), + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(3, 768953523524252804), + name: 'fileName', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(4, 2743437583742703683), + name: 'fileBlurHash', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(5, 5717160279142175066), + name: 'url', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(6, 4647869943489747714), + name: 'contentType', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(7, 3882295953503253830), + name: 'fileHeight', + type: 6, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(8, 4936282859728421093), + name: 'fileWidth', + type: 6, + flags: 0, + ), + ], + relations: [], + backlinks: [], + ), obx_int.ModelEntity( - id: const obx_int.IdUid(9, 5352194789380319428), - name: 'AvatarModel', - lastPropertyId: const obx_int.IdUid(5, 7667400744069831045), - flags: 0, - properties: [ - obx_int.ModelProperty( - id: const obx_int.IdUid(1, 7103894213264938615), - name: 'bid', - type: 6, - flags: 1), - obx_int.ModelProperty( - id: const obx_int.IdUid(2, 5029774360479968412), - name: 'fileId', - type: 9, - flags: 34848, - indexId: const obx_int.IdUid(25, 4244316179033347291)), - obx_int.ModelProperty( - id: const obx_int.IdUid(3, 5415602239263669102), - name: 'fileName', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(4, 8603914852688423714), - name: 'fileBlurHash', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(5, 7667400744069831045), - name: 'imageUrl', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), + id: const obx_int.IdUid(9, 5352194789380319428), + name: 'AvatarModel', + lastPropertyId: const obx_int.IdUid(5, 7667400744069831045), + flags: 0, + properties: [ + obx_int.ModelProperty( + id: const obx_int.IdUid(1, 7103894213264938615), + name: 'bid', + type: 6, + flags: 1, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(2, 5029774360479968412), + name: 'fileId', + type: 9, + flags: 34848, + indexId: const obx_int.IdUid(25, 4244316179033347291), + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(3, 5415602239263669102), + name: 'fileName', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(4, 8603914852688423714), + name: 'fileBlurHash', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(5, 7667400744069831045), + name: 'imageUrl', + type: 9, + flags: 0, + ), + ], + relations: [], + backlinks: [], + ), obx_int.ModelEntity( - id: const obx_int.IdUid(11, 6168919874786736956), - name: 'MessageModel', - lastPropertyId: const obx_int.IdUid(16, 1698414372814552795), - flags: 0, - properties: [ - obx_int.ModelProperty( - id: const obx_int.IdUid(1, 849260663934900342), - name: 'bid', - type: 6, - flags: 1), - obx_int.ModelProperty( - id: const obx_int.IdUid(2, 3431085295353515294), - name: 'id', - type: 9, - flags: 2080, - indexId: const obx_int.IdUid(27, 1312181892016791297)), - obx_int.ModelProperty( - id: const obx_int.IdUid(3, 298933995628152516), - name: 'from', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(4, 2201771245741036678), - name: 'cid', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(5, 6726139322816044811), - name: 'rawStatus', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(6, 6475269319788621443), - name: 'body', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(7, 2472667661907244788), - name: 't', - type: 6, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(8, 1010284661967327878), - name: 'createdAt', - type: 10, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(9, 1875750503242122079), - name: 'dbExtension', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(10, 6395861719839579723), - name: 'repliedMessageId', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(11, 8502424052705749036), - name: 'replyMessageBindId', - type: 11, - flags: 520, - indexId: const obx_int.IdUid(39, 2280023974172957156), - relationTarget: 'MessageModel'), - obx_int.ModelProperty( - id: const obx_int.IdUid(12, 6401592278627748216), - name: 'senderBindId', - type: 11, - flags: 520, - indexId: const obx_int.IdUid(40, 5803255922961343502), - relationTarget: 'UserModel'), - obx_int.ModelProperty( - id: const obx_int.IdUid(13, 5213655976270008005), - name: 'isOwn', - type: 1, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(14, 1893283248997063821), - name: 'isTempReplied', - type: 1, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(15, 4095866355783119704), - name: 'forwardedMessageId', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(16, 1698414372814552795), - name: 'isEdited', - type: 1, - flags: 0) - ], - relations: [ - obx_int.ModelRelation( - id: const obx_int.IdUid(2, 1972630443271039517), - name: 'attachments', - targetId: const obx_int.IdUid(8, 6709400528399140612)) - ], - backlinks: []), + id: const obx_int.IdUid(11, 6168919874786736956), + name: 'MessageModel', + lastPropertyId: const obx_int.IdUid(17, 8330640059358737630), + flags: 0, + properties: [ + obx_int.ModelProperty( + id: const obx_int.IdUid(1, 849260663934900342), + name: 'bid', + type: 6, + flags: 1, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(2, 3431085295353515294), + name: 'id', + type: 9, + flags: 2080, + indexId: const obx_int.IdUid(27, 1312181892016791297), + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(3, 298933995628152516), + name: 'from', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(4, 2201771245741036678), + name: 'cid', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(6, 6475269319788621443), + name: 'body', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(7, 2472667661907244788), + name: 't', + type: 6, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(8, 1010284661967327878), + name: 'createdAt', + type: 10, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(9, 1875750503242122079), + name: 'dbExtension', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(10, 6395861719839579723), + name: 'repliedMessageId', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(11, 8502424052705749036), + name: 'replyMessageBindId', + type: 11, + flags: 520, + indexId: const obx_int.IdUid(39, 2280023974172957156), + relationTarget: 'MessageModel', + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(12, 6401592278627748216), + name: 'senderBindId', + type: 11, + flags: 520, + indexId: const obx_int.IdUid(40, 5803255922961343502), + relationTarget: 'UserModel', + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(13, 5213655976270008005), + name: 'isOwn', + type: 1, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(14, 1893283248997063821), + name: 'isTempReplied', + type: 1, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(15, 4095866355783119704), + name: 'forwardedMessageId', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(16, 1698414372814552795), + name: 'isEdited', + type: 1, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(17, 8330640059358737630), + name: 'dbStatus', + type: 6, + flags: 0, + ), + ], + relations: [ + obx_int.ModelRelation( + id: const obx_int.IdUid(2, 1972630443271039517), + name: 'attachments', + targetId: const obx_int.IdUid(8, 6709400528399140612), + ), + ], + backlinks: [], + ), obx_int.ModelEntity( - id: const obx_int.IdUid(12, 6516061588255621718), - name: 'UserModel', - lastPropertyId: const obx_int.IdUid(12, 4610750389367537300), - flags: 0, - properties: [ - obx_int.ModelProperty( - id: const obx_int.IdUid(1, 4596686184514143634), - name: 'bid', - type: 6, - flags: 1), - obx_int.ModelProperty( - id: const obx_int.IdUid(2, 1796029667524301994), - name: 'id', - type: 9, - flags: 2080, - indexId: const obx_int.IdUid(28, 8383136850945105628)), - obx_int.ModelProperty( - id: const obx_int.IdUid(3, 3523188585463807346), - name: 'deviceId', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(4, 3983606319140370548), - name: 'createdAt', - type: 10, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(5, 1544825709604943638), - name: 'updatedAt', - type: 10, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(6, 5527472554947430886), - name: 'recentActivity', - type: 6, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(7, 260065750458389466), - name: 'login', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(8, 3300380321564909619), - name: 'firstName', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(9, 5969495372761363000), - name: 'lastName', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(10, 2323797854769346526), - name: 'phone', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(11, 7157251502045924726), - name: 'email', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(12, 4610750389367537300), - name: 'avatarBindId', - type: 11, - flags: 520, - indexId: const obx_int.IdUid(24, 2478963062982225581), - relationTarget: 'AvatarModel') - ], - relations: [], - backlinks: []), + id: const obx_int.IdUid(12, 6516061588255621718), + name: 'UserModel', + lastPropertyId: const obx_int.IdUid(12, 4610750389367537300), + flags: 0, + properties: [ + obx_int.ModelProperty( + id: const obx_int.IdUid(1, 4596686184514143634), + name: 'bid', + type: 6, + flags: 1, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(2, 1796029667524301994), + name: 'id', + type: 9, + flags: 2080, + indexId: const obx_int.IdUid(28, 8383136850945105628), + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(3, 3523188585463807346), + name: 'deviceId', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(4, 3983606319140370548), + name: 'createdAt', + type: 10, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(5, 1544825709604943638), + name: 'updatedAt', + type: 10, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(6, 5527472554947430886), + name: 'recentActivity', + type: 6, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(7, 260065750458389466), + name: 'login', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(8, 3300380321564909619), + name: 'firstName', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(9, 5969495372761363000), + name: 'lastName', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(10, 2323797854769346526), + name: 'phone', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(11, 7157251502045924726), + name: 'email', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(12, 4610750389367537300), + name: 'avatarBindId', + type: 11, + flags: 520, + indexId: const obx_int.IdUid(24, 2478963062982225581), + relationTarget: 'AvatarModel', + ), + ], + relations: [], + backlinks: [], + ), obx_int.ModelEntity( - id: const obx_int.IdUid(13, 9106374520578572502), - name: 'ConversationModel', - lastPropertyId: const obx_int.IdUid(16, 8214829155225431382), - flags: 0, - properties: [ - obx_int.ModelProperty( - id: const obx_int.IdUid(1, 6717232681942624528), - name: 'bid', - type: 6, - flags: 1), - obx_int.ModelProperty( - id: const obx_int.IdUid(2, 2579398636291900819), - name: 'id', - type: 9, - flags: 2080, - indexId: const obx_int.IdUid(29, 4205296519667695880)), - obx_int.ModelProperty( - id: const obx_int.IdUid(3, 7873950750532297915), - name: 'createdAt', - type: 10, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(4, 9132499605888996505), - name: 'updatedAt', - type: 10, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(5, 7874076721401934636), - name: 'type', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(6, 1911538922432355672), - name: 'name', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(7, 3488711112687952056), - name: 'description', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(8, 4718072041675418821), - name: 'unreadMessagesCount', - type: 6, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(9, 8543586135917731916), - name: 'lastMessageBindId', - type: 11, - flags: 520, - indexId: const obx_int.IdUid(30, 5882588671767701244), - relationTarget: 'MessageModel'), - obx_int.ModelProperty( - id: const obx_int.IdUid(12, 8542803209060046040), - name: 'avatarBindId', - type: 11, - flags: 520, - indexId: const obx_int.IdUid(33, 7298741033559918238), - relationTarget: 'AvatarModel'), - obx_int.ModelProperty( - id: const obx_int.IdUid(13, 6726414800751664493), - name: 'opponentBindId', - type: 11, - flags: 520, - indexId: const obx_int.IdUid(34, 2014479524742516296), - relationTarget: 'UserModel'), - obx_int.ModelProperty( - id: const obx_int.IdUid(14, 5058682722642913901), - name: 'ownerBindId', - type: 11, - flags: 520, - indexId: const obx_int.IdUid(35, 2642777293408708995), - relationTarget: 'UserModel'), - obx_int.ModelProperty( - id: const obx_int.IdUid(15, 8834178211781701775), - name: 'isEncrypted', - type: 1, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(16, 8214829155225431382), - name: 'draftMessageBindId', - type: 11, - flags: 520, - indexId: const obx_int.IdUid(38, 1718185030575189588), - relationTarget: 'MessageModel') - ], - relations: [ - obx_int.ModelRelation( - id: const obx_int.IdUid(5, 7785256265115248397), - name: 'participants', - targetId: const obx_int.IdUid(12, 6516061588255621718)) - ], - backlinks: []) + id: const obx_int.IdUid(13, 9106374520578572502), + name: 'ConversationModel', + lastPropertyId: const obx_int.IdUid(16, 8214829155225431382), + flags: 0, + properties: [ + obx_int.ModelProperty( + id: const obx_int.IdUid(1, 6717232681942624528), + name: 'bid', + type: 6, + flags: 1, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(2, 2579398636291900819), + name: 'id', + type: 9, + flags: 2080, + indexId: const obx_int.IdUid(29, 4205296519667695880), + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(3, 7873950750532297915), + name: 'createdAt', + type: 10, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(4, 9132499605888996505), + name: 'updatedAt', + type: 10, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(5, 7874076721401934636), + name: 'type', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(6, 1911538922432355672), + name: 'name', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(7, 3488711112687952056), + name: 'description', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(8, 4718072041675418821), + name: 'unreadMessagesCount', + type: 6, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(9, 8543586135917731916), + name: 'lastMessageBindId', + type: 11, + flags: 520, + indexId: const obx_int.IdUid(30, 5882588671767701244), + relationTarget: 'MessageModel', + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(12, 8542803209060046040), + name: 'avatarBindId', + type: 11, + flags: 520, + indexId: const obx_int.IdUid(33, 7298741033559918238), + relationTarget: 'AvatarModel', + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(13, 6726414800751664493), + name: 'opponentBindId', + type: 11, + flags: 520, + indexId: const obx_int.IdUid(34, 2014479524742516296), + relationTarget: 'UserModel', + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(14, 5058682722642913901), + name: 'ownerBindId', + type: 11, + flags: 520, + indexId: const obx_int.IdUid(35, 2642777293408708995), + relationTarget: 'UserModel', + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(15, 8834178211781701775), + name: 'isEncrypted', + type: 1, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(16, 8214829155225431382), + name: 'draftMessageBindId', + type: 11, + flags: 520, + indexId: const obx_int.IdUid(38, 1718185030575189588), + relationTarget: 'MessageModel', + ), + ], + relations: [ + obx_int.ModelRelation( + id: const obx_int.IdUid(5, 7785256265115248397), + name: 'participants', + targetId: const obx_int.IdUid(12, 6516061588255621718), + ), + ], + backlinks: [], + ), ]; /// Shortcut for [obx.Store.new] that passes [getObjectBoxModel] and for Flutter @@ -387,557 +449,686 @@ final _entities = [ /// For Flutter apps, also calls `loadObjectBoxLibraryAndroidCompat()` from /// the ObjectBox Flutter library to fix loading the native ObjectBox library /// on Android 6 and older. -Future openStore( - {String? directory, - int? maxDBSizeInKB, - int? maxDataSizeInKB, - int? fileMode, - int? maxReaders, - bool queriesCaseSensitiveDefault = true, - String? macosApplicationGroup}) async { +Future openStore({ + String? directory, + int? maxDBSizeInKB, + int? maxDataSizeInKB, + int? fileMode, + int? maxReaders, + bool queriesCaseSensitiveDefault = true, + String? macosApplicationGroup, +}) async { await loadObjectBoxLibraryAndroidCompat(); - return obx.Store(getObjectBoxModel(), - directory: directory ?? (await defaultStoreDirectory()).path, - maxDBSizeInKB: maxDBSizeInKB, - maxDataSizeInKB: maxDataSizeInKB, - fileMode: fileMode, - maxReaders: maxReaders, - queriesCaseSensitiveDefault: queriesCaseSensitiveDefault, - macosApplicationGroup: macosApplicationGroup); + return obx.Store( + getObjectBoxModel(), + directory: directory ?? (await defaultStoreDirectory()).path, + maxDBSizeInKB: maxDBSizeInKB, + maxDataSizeInKB: maxDataSizeInKB, + fileMode: fileMode, + maxReaders: maxReaders, + queriesCaseSensitiveDefault: queriesCaseSensitiveDefault, + macosApplicationGroup: macosApplicationGroup, + ); } /// Returns the ObjectBox model definition for this project for use with /// [obx.Store.new]. obx_int.ModelDefinition getObjectBoxModel() { final model = obx_int.ModelInfo( - entities: _entities, - lastEntityId: const obx_int.IdUid(13, 9106374520578572502), - lastIndexId: const obx_int.IdUid(40, 5803255922961343502), - lastRelationId: const obx_int.IdUid(5, 7785256265115248397), - lastSequenceId: const obx_int.IdUid(0, 0), - retiredEntityUids: const [ - 8221608220756309685, - 3916919191516272112, - 2083168856616622261, - 6918202109537402906, - 5487033076272012390, - 5012849246139480832, - 6012607412223436967, - 2679282245502448550 - ], - retiredIndexUids: const [ - 2308776673764111186, - 7730326273086017154, - 8590772032330868498, - 8137762055088700512, - 7050184320199163165, - 5813459946923529345, - 626623788693270148, - 6221333517652902864 - ], - retiredPropertyUids: const [ - 5244948909752663107, - 642035060693614009, - 3131801735437468855, - 7717702637457311038, - 6444097050439138616, - 2052978247185038202, - 3759082206816062884, - 4171243892512299453, - 7660760250429865946, - 7994521092493067568, - 2753590088175921176, - 6723871787072869107, - 3672367778271562063, - 6917676645652591844, - 103343067583914471, - 1719497191968112232, - 125456544499571799, - 135999312779228694, - 1224887090371269290, - 5677110412226819126, - 3854621785786133919, - 4183038334923145217, - 9205162562036445098, - 4117982081165635192, - 5813755567765050557, - 2598453969112274346, - 9145044455135425987, - 5590802489791299855, - 6438701760314840495, - 4695719793845453187, - 6314488510877905217, - 3271216996689882758, - 6380047012876766535, - 3456785938009755266, - 3633717856401305859, - 4375214362308418179, - 4477377261154987816, - 8748385958730009606, - 6323590858319605034, - 9168000509218833904, - 5364493506422957153, - 522372722417879397, - 6073599319266890692, - 7578632378595450354, - 5942505151512357888, - 8738776819488037, - 7002687406836241592, - 9127660167613834806, - 4073551582715204272, - 4394882411289672412, - 7519794114385237563, - 5781198741257241343, - 7567633610019030123, - 8740075846196374882, - 5800900320973295407, - 8640554254403841953, - 2172023124059614591, - 811411619103627512, - 3267521444689283243, - 2639370258575167313, - 6792097179376439446, - 5710713795623848612, - 4217675935215597774, - 8971578261290097735, - 34888985269025050, - 4376506821871815231, - 3776672104103537937, - 7972368654429353481, - 8633736652726027619, - 7353937754765402079, - 7048963428701517603, - 7514358998250204160, - 3264904124875647524, - 614204034693092275, - 155656363300645816, - 1773146609978416143, - 7768713511998184494, - 2456344090811030546, - 2261589455151034389 - ], - retiredRelationUids: const [4063380833838902563, 5100230969978387914], - modelVersion: 5, - modelVersionParserMinimum: 5, - version: 1); + entities: _entities, + lastEntityId: const obx_int.IdUid(13, 9106374520578572502), + lastIndexId: const obx_int.IdUid(40, 5803255922961343502), + lastRelationId: const obx_int.IdUid(5, 7785256265115248397), + lastSequenceId: const obx_int.IdUid(0, 0), + retiredEntityUids: const [ + 8221608220756309685, + 3916919191516272112, + 2083168856616622261, + 6918202109537402906, + 5487033076272012390, + 5012849246139480832, + 6012607412223436967, + 2679282245502448550, + ], + retiredIndexUids: const [ + 2308776673764111186, + 7730326273086017154, + 8590772032330868498, + 8137762055088700512, + 7050184320199163165, + 5813459946923529345, + 626623788693270148, + 6221333517652902864, + ], + retiredPropertyUids: const [ + 5244948909752663107, + 642035060693614009, + 3131801735437468855, + 7717702637457311038, + 6444097050439138616, + 2052978247185038202, + 3759082206816062884, + 4171243892512299453, + 7660760250429865946, + 7994521092493067568, + 2753590088175921176, + 6723871787072869107, + 3672367778271562063, + 6917676645652591844, + 103343067583914471, + 1719497191968112232, + 125456544499571799, + 135999312779228694, + 1224887090371269290, + 5677110412226819126, + 3854621785786133919, + 4183038334923145217, + 9205162562036445098, + 4117982081165635192, + 5813755567765050557, + 2598453969112274346, + 9145044455135425987, + 5590802489791299855, + 6438701760314840495, + 4695719793845453187, + 6314488510877905217, + 3271216996689882758, + 6380047012876766535, + 3456785938009755266, + 3633717856401305859, + 4375214362308418179, + 4477377261154987816, + 8748385958730009606, + 6323590858319605034, + 9168000509218833904, + 5364493506422957153, + 522372722417879397, + 6073599319266890692, + 7578632378595450354, + 5942505151512357888, + 8738776819488037, + 7002687406836241592, + 9127660167613834806, + 4073551582715204272, + 4394882411289672412, + 7519794114385237563, + 5781198741257241343, + 7567633610019030123, + 8740075846196374882, + 5800900320973295407, + 8640554254403841953, + 2172023124059614591, + 811411619103627512, + 3267521444689283243, + 2639370258575167313, + 6792097179376439446, + 5710713795623848612, + 4217675935215597774, + 8971578261290097735, + 34888985269025050, + 4376506821871815231, + 3776672104103537937, + 7972368654429353481, + 8633736652726027619, + 7353937754765402079, + 7048963428701517603, + 7514358998250204160, + 3264904124875647524, + 614204034693092275, + 155656363300645816, + 1773146609978416143, + 7768713511998184494, + 2456344090811030546, + 2261589455151034389, + 6726139322816044811, + ], + retiredRelationUids: const [4063380833838902563, 5100230969978387914], + modelVersion: 5, + modelVersionParserMinimum: 5, + version: 1, + ); final bindings = { AttachmentModel: obx_int.EntityDefinition( - model: _entities[0], - toOneRelations: (AttachmentModel object) => [], - toManyRelations: (AttachmentModel object) => {}, - getId: (AttachmentModel object) => object.bid, - setId: (AttachmentModel object, int id) { - object.bid = id; - }, - objectToFB: (AttachmentModel object, fb.Builder fbb) { - final fileIdOffset = - object.fileId == null ? null : fbb.writeString(object.fileId!); - final fileNameOffset = object.fileName == null - ? null - : fbb.writeString(object.fileName!); - final fileBlurHashOffset = object.fileBlurHash == null - ? null - : fbb.writeString(object.fileBlurHash!); - final urlOffset = - object.url == null ? null : fbb.writeString(object.url!); - final contentTypeOffset = object.contentType == null - ? null - : fbb.writeString(object.contentType!); - fbb.startTable(9); - fbb.addInt64(0, object.bid ?? 0); - fbb.addOffset(1, fileIdOffset); - fbb.addOffset(2, fileNameOffset); - fbb.addOffset(3, fileBlurHashOffset); - fbb.addOffset(4, urlOffset); - fbb.addOffset(5, contentTypeOffset); - fbb.addInt64(6, object.fileHeight); - fbb.addInt64(7, object.fileWidth); - fbb.finish(fbb.endTable()); - return object.bid ?? 0; - }, - objectFromFB: (obx.Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final bidParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 4); - final fileIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 6); - final fileNameParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 8); - final fileBlurHashParam = - const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 10); - final urlParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 12); - final contentTypeParam = - const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 14); - final fileHeightParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 16); - final fileWidthParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 18); - final object = AttachmentModel( - bid: bidParam, - fileId: fileIdParam, - fileName: fileNameParam, - fileBlurHash: fileBlurHashParam, - url: urlParam, - contentType: contentTypeParam, - fileHeight: fileHeightParam, - fileWidth: fileWidthParam); - - return object; - }), + model: _entities[0], + toOneRelations: (AttachmentModel object) => [], + toManyRelations: (AttachmentModel object) => {}, + getId: (AttachmentModel object) => object.bid, + setId: (AttachmentModel object, int id) { + object.bid = id; + }, + objectToFB: (AttachmentModel object, fb.Builder fbb) { + final fileIdOffset = object.fileId == null + ? null + : fbb.writeString(object.fileId!); + final fileNameOffset = object.fileName == null + ? null + : fbb.writeString(object.fileName!); + final fileBlurHashOffset = object.fileBlurHash == null + ? null + : fbb.writeString(object.fileBlurHash!); + final urlOffset = object.url == null + ? null + : fbb.writeString(object.url!); + final contentTypeOffset = object.contentType == null + ? null + : fbb.writeString(object.contentType!); + fbb.startTable(9); + fbb.addInt64(0, object.bid ?? 0); + fbb.addOffset(1, fileIdOffset); + fbb.addOffset(2, fileNameOffset); + fbb.addOffset(3, fileBlurHashOffset); + fbb.addOffset(4, urlOffset); + fbb.addOffset(5, contentTypeOffset); + fbb.addInt64(6, object.fileHeight); + fbb.addInt64(7, object.fileWidth); + fbb.finish(fbb.endTable()); + return object.bid ?? 0; + }, + objectFromFB: (obx.Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + final bidParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 4, + ); + final fileIdParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 6); + final fileNameParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 8); + final fileBlurHashParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 10); + final urlParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 12); + final contentTypeParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 14); + final fileHeightParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 16, + ); + final fileWidthParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 18, + ); + final object = AttachmentModel( + bid: bidParam, + fileId: fileIdParam, + fileName: fileNameParam, + fileBlurHash: fileBlurHashParam, + url: urlParam, + contentType: contentTypeParam, + fileHeight: fileHeightParam, + fileWidth: fileWidthParam, + ); + + return object; + }, + ), AvatarModel: obx_int.EntityDefinition( - model: _entities[1], - toOneRelations: (AvatarModel object) => [], - toManyRelations: (AvatarModel object) => {}, - getId: (AvatarModel object) => object.bid, - setId: (AvatarModel object, int id) { - object.bid = id; - }, - objectToFB: (AvatarModel object, fb.Builder fbb) { - final fileIdOffset = - object.fileId == null ? null : fbb.writeString(object.fileId!); - final fileNameOffset = object.fileName == null - ? null - : fbb.writeString(object.fileName!); - final fileBlurHashOffset = object.fileBlurHash == null - ? null - : fbb.writeString(object.fileBlurHash!); - final imageUrlOffset = object.imageUrl == null - ? null - : fbb.writeString(object.imageUrl!); - fbb.startTable(6); - fbb.addInt64(0, object.bid ?? 0); - fbb.addOffset(1, fileIdOffset); - fbb.addOffset(2, fileNameOffset); - fbb.addOffset(3, fileBlurHashOffset); - fbb.addOffset(4, imageUrlOffset); - fbb.finish(fbb.endTable()); - return object.bid ?? 0; - }, - objectFromFB: (obx.Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final bidParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 4); - final fileIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 6); - final fileNameParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 8); - final fileBlurHashParam = - const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 10); - final imageUrlParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 12); - final object = AvatarModel( - bid: bidParam, - fileId: fileIdParam, - fileName: fileNameParam, - fileBlurHash: fileBlurHashParam, - imageUrl: imageUrlParam); - - return object; - }), + model: _entities[1], + toOneRelations: (AvatarModel object) => [], + toManyRelations: (AvatarModel object) => {}, + getId: (AvatarModel object) => object.bid, + setId: (AvatarModel object, int id) { + object.bid = id; + }, + objectToFB: (AvatarModel object, fb.Builder fbb) { + final fileIdOffset = object.fileId == null + ? null + : fbb.writeString(object.fileId!); + final fileNameOffset = object.fileName == null + ? null + : fbb.writeString(object.fileName!); + final fileBlurHashOffset = object.fileBlurHash == null + ? null + : fbb.writeString(object.fileBlurHash!); + final imageUrlOffset = object.imageUrl == null + ? null + : fbb.writeString(object.imageUrl!); + fbb.startTable(6); + fbb.addInt64(0, object.bid ?? 0); + fbb.addOffset(1, fileIdOffset); + fbb.addOffset(2, fileNameOffset); + fbb.addOffset(3, fileBlurHashOffset); + fbb.addOffset(4, imageUrlOffset); + fbb.finish(fbb.endTable()); + return object.bid ?? 0; + }, + objectFromFB: (obx.Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + final bidParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 4, + ); + final fileIdParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 6); + final fileNameParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 8); + final fileBlurHashParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 10); + final imageUrlParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 12); + final object = AvatarModel( + bid: bidParam, + fileId: fileIdParam, + fileName: fileNameParam, + fileBlurHash: fileBlurHashParam, + imageUrl: imageUrlParam, + ); + + return object; + }, + ), MessageModel: obx_int.EntityDefinition( - model: _entities[2], - toOneRelations: (MessageModel object) => - [object.replyMessageBind, object.senderBind], - toManyRelations: (MessageModel object) => { - obx_int.RelInfo.toMany(2, object.bid!): - object.attachments - }, - getId: (MessageModel object) => object.bid, - setId: (MessageModel object, int id) { - object.bid = id; - }, - objectToFB: (MessageModel object, fb.Builder fbb) { - final idOffset = fbb.writeString(object.id); - final fromOffset = fbb.writeString(object.from); - final cidOffset = fbb.writeString(object.cid); - final rawStatusOffset = object.rawStatus == null - ? null - : fbb.writeString(object.rawStatus!); - final bodyOffset = - object.body == null ? null : fbb.writeString(object.body!); - final dbExtensionOffset = object.dbExtension == null - ? null - : fbb.writeString(object.dbExtension!); - final repliedMessageIdOffset = object.repliedMessageId == null - ? null - : fbb.writeString(object.repliedMessageId!); - final forwardedMessageIdOffset = object.forwardedMessageId == null - ? null - : fbb.writeString(object.forwardedMessageId!); - fbb.startTable(17); - fbb.addInt64(0, object.bid ?? 0); - fbb.addOffset(1, idOffset); - fbb.addOffset(2, fromOffset); - fbb.addOffset(3, cidOffset); - fbb.addOffset(4, rawStatusOffset); - fbb.addOffset(5, bodyOffset); - fbb.addInt64(6, object.t); - fbb.addInt64(7, object.createdAt?.millisecondsSinceEpoch); - fbb.addOffset(8, dbExtensionOffset); - fbb.addOffset(9, repliedMessageIdOffset); - fbb.addInt64(10, object.replyMessageBind.targetId); - fbb.addInt64(11, object.senderBind.targetId); - fbb.addBool(12, object.isOwn); - fbb.addBool(13, object.isTempReplied); - fbb.addOffset(14, forwardedMessageIdOffset); - fbb.addBool(15, object.isEdited); - fbb.finish(fbb.endTable()); - return object.bid ?? 0; - }, - objectFromFB: (obx.Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final createdAtValue = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 18); - final bidParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 4); - final idParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final fromParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 8, ''); - final cidParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 10, ''); - final isOwnParam = - const fb.BoolReader().vTableGet(buffer, rootOffset, 28, false); - final repliedMessageIdParam = - const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 22); - final forwardedMessageIdParam = - const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 32); - final rawStatusParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 12); - final bodyParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 14); - final createdAtParam = createdAtValue == null - ? null - : DateTime.fromMillisecondsSinceEpoch(createdAtValue); - final tParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 16); - final isTempRepliedParam = - const fb.BoolReader().vTableGetNullable(buffer, rootOffset, 30); - final isEditedParam = - const fb.BoolReader().vTableGetNullable(buffer, rootOffset, 34); - final object = MessageModel( - bid: bidParam, - id: idParam, - from: fromParam, - cid: cidParam, - isOwn: isOwnParam, - repliedMessageId: repliedMessageIdParam, - forwardedMessageId: forwardedMessageIdParam, - rawStatus: rawStatusParam, - body: bodyParam, - createdAt: createdAtParam, - t: tParam, - isTempReplied: isTempRepliedParam, - isEdited: isEditedParam) - ..dbExtension = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 20); - object.replyMessageBind.targetId = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0); - object.replyMessageBind.attach(store); - object.senderBind.targetId = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 26, 0); - object.senderBind.attach(store); - obx_int.InternalToManyAccess.setRelInfo( - object.attachments, - store, - obx_int.RelInfo.toMany(2, object.bid!)); - return object; - }), + model: _entities[2], + toOneRelations: (MessageModel object) => [ + object.replyMessageBind, + object.senderBind, + ], + toManyRelations: (MessageModel object) => { + obx_int.RelInfo.toMany(2, object.bid!): + object.attachments, + }, + getId: (MessageModel object) => object.bid, + setId: (MessageModel object, int id) { + object.bid = id; + }, + objectToFB: (MessageModel object, fb.Builder fbb) { + final idOffset = fbb.writeString(object.id); + final fromOffset = fbb.writeString(object.from); + final cidOffset = fbb.writeString(object.cid); + final bodyOffset = object.body == null + ? null + : fbb.writeString(object.body!); + final dbExtensionOffset = object.dbExtension == null + ? null + : fbb.writeString(object.dbExtension!); + final repliedMessageIdOffset = object.repliedMessageId == null + ? null + : fbb.writeString(object.repliedMessageId!); + final forwardedMessageIdOffset = object.forwardedMessageId == null + ? null + : fbb.writeString(object.forwardedMessageId!); + fbb.startTable(18); + fbb.addInt64(0, object.bid ?? 0); + fbb.addOffset(1, idOffset); + fbb.addOffset(2, fromOffset); + fbb.addOffset(3, cidOffset); + fbb.addOffset(5, bodyOffset); + fbb.addInt64(6, object.t); + fbb.addInt64(7, object.createdAt?.millisecondsSinceEpoch); + fbb.addOffset(8, dbExtensionOffset); + fbb.addOffset(9, repliedMessageIdOffset); + fbb.addInt64(10, object.replyMessageBind.targetId); + fbb.addInt64(11, object.senderBind.targetId); + fbb.addBool(12, object.isOwn); + fbb.addBool(13, object.isTempReplied); + fbb.addOffset(14, forwardedMessageIdOffset); + fbb.addBool(15, object.isEdited); + fbb.addInt64(16, object.dbStatus); + fbb.finish(fbb.endTable()); + return object.bid ?? 0; + }, + objectFromFB: (obx.Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + final createdAtValue = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 18, + ); + final bidParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 4, + ); + final idParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGet(buffer, rootOffset, 6, ''); + final fromParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGet(buffer, rootOffset, 8, ''); + final cidParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGet(buffer, rootOffset, 10, ''); + final isOwnParam = const fb.BoolReader().vTableGet( + buffer, + rootOffset, + 28, + false, + ); + final repliedMessageIdParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 22); + final forwardedMessageIdParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 32); + final bodyParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 14); + final createdAtParam = createdAtValue == null + ? null + : DateTime.fromMillisecondsSinceEpoch(createdAtValue); + final tParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 16, + ); + final isTempRepliedParam = const fb.BoolReader().vTableGetNullable( + buffer, + rootOffset, + 30, + ); + final isEditedParam = const fb.BoolReader().vTableGetNullable( + buffer, + rootOffset, + 34, + ); + final object = + MessageModel( + bid: bidParam, + id: idParam, + from: fromParam, + cid: cidParam, + isOwn: isOwnParam, + repliedMessageId: repliedMessageIdParam, + forwardedMessageId: forwardedMessageIdParam, + body: bodyParam, + createdAt: createdAtParam, + t: tParam, + isTempReplied: isTempRepliedParam, + isEdited: isEditedParam, + ) + ..dbExtension = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 20) + ..dbStatus = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 36, + ); + object.replyMessageBind.targetId = const fb.Int64Reader().vTableGet( + buffer, + rootOffset, + 24, + 0, + ); + object.replyMessageBind.attach(store); + object.senderBind.targetId = const fb.Int64Reader().vTableGet( + buffer, + rootOffset, + 26, + 0, + ); + object.senderBind.attach(store); + obx_int.InternalToManyAccess.setRelInfo( + object.attachments, + store, + obx_int.RelInfo.toMany(2, object.bid!), + ); + return object; + }, + ), UserModel: obx_int.EntityDefinition( - model: _entities[3], - toOneRelations: (UserModel object) => [object.avatarBind], - toManyRelations: (UserModel object) => {}, - getId: (UserModel object) => object.bid, - setId: (UserModel object, int id) { - object.bid = id; - }, - objectToFB: (UserModel object, fb.Builder fbb) { - final idOffset = - object.id == null ? null : fbb.writeString(object.id!); - final deviceIdOffset = object.deviceId == null - ? null - : fbb.writeString(object.deviceId!); - final loginOffset = - object.login == null ? null : fbb.writeString(object.login!); - final firstNameOffset = object.firstName == null - ? null - : fbb.writeString(object.firstName!); - final lastNameOffset = object.lastName == null - ? null - : fbb.writeString(object.lastName!); - final phoneOffset = - object.phone == null ? null : fbb.writeString(object.phone!); - final emailOffset = - object.email == null ? null : fbb.writeString(object.email!); - fbb.startTable(13); - fbb.addInt64(0, object.bid ?? 0); - fbb.addOffset(1, idOffset); - fbb.addOffset(2, deviceIdOffset); - fbb.addInt64(3, object.createdAt?.millisecondsSinceEpoch); - fbb.addInt64(4, object.updatedAt?.millisecondsSinceEpoch); - fbb.addInt64(5, object.recentActivity); - fbb.addOffset(6, loginOffset); - fbb.addOffset(7, firstNameOffset); - fbb.addOffset(8, lastNameOffset); - fbb.addOffset(9, phoneOffset); - fbb.addOffset(10, emailOffset); - fbb.addInt64(11, object.avatarBind.targetId); - fbb.finish(fbb.endTable()); - return object.bid ?? 0; - }, - objectFromFB: (obx.Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final createdAtValue = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 10); - final updatedAtValue = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 12); - final bidParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 4); - final idParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 6); - final deviceIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 8); - final createdAtParam = createdAtValue == null - ? null - : DateTime.fromMillisecondsSinceEpoch(createdAtValue); - final updatedAtParam = updatedAtValue == null - ? null - : DateTime.fromMillisecondsSinceEpoch(updatedAtValue); - final recentActivityParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 14); - final loginParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 16); - final firstNameParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 18); - final lastNameParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 20); - final phoneParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 22); - final emailParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 24); - final object = UserModel( - bid: bidParam, - id: idParam, - deviceId: deviceIdParam, - createdAt: createdAtParam, - updatedAt: updatedAtParam, - recentActivity: recentActivityParam, - login: loginParam, - firstName: firstNameParam, - lastName: lastNameParam, - phone: phoneParam, - email: emailParam); - object.avatarBind.targetId = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 26, 0); - object.avatarBind.attach(store); - return object; - }), + model: _entities[3], + toOneRelations: (UserModel object) => [object.avatarBind], + toManyRelations: (UserModel object) => {}, + getId: (UserModel object) => object.bid, + setId: (UserModel object, int id) { + object.bid = id; + }, + objectToFB: (UserModel object, fb.Builder fbb) { + final idOffset = object.id == null ? null : fbb.writeString(object.id!); + final deviceIdOffset = object.deviceId == null + ? null + : fbb.writeString(object.deviceId!); + final loginOffset = object.login == null + ? null + : fbb.writeString(object.login!); + final firstNameOffset = object.firstName == null + ? null + : fbb.writeString(object.firstName!); + final lastNameOffset = object.lastName == null + ? null + : fbb.writeString(object.lastName!); + final phoneOffset = object.phone == null + ? null + : fbb.writeString(object.phone!); + final emailOffset = object.email == null + ? null + : fbb.writeString(object.email!); + fbb.startTable(13); + fbb.addInt64(0, object.bid ?? 0); + fbb.addOffset(1, idOffset); + fbb.addOffset(2, deviceIdOffset); + fbb.addInt64(3, object.createdAt?.millisecondsSinceEpoch); + fbb.addInt64(4, object.updatedAt?.millisecondsSinceEpoch); + fbb.addInt64(5, object.recentActivity); + fbb.addOffset(6, loginOffset); + fbb.addOffset(7, firstNameOffset); + fbb.addOffset(8, lastNameOffset); + fbb.addOffset(9, phoneOffset); + fbb.addOffset(10, emailOffset); + fbb.addInt64(11, object.avatarBind.targetId); + fbb.finish(fbb.endTable()); + return object.bid ?? 0; + }, + objectFromFB: (obx.Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + final createdAtValue = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 10, + ); + final updatedAtValue = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 12, + ); + final bidParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 4, + ); + final idParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 6); + final deviceIdParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 8); + final createdAtParam = createdAtValue == null + ? null + : DateTime.fromMillisecondsSinceEpoch(createdAtValue); + final updatedAtParam = updatedAtValue == null + ? null + : DateTime.fromMillisecondsSinceEpoch(updatedAtValue); + final recentActivityParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 14, + ); + final loginParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 16); + final firstNameParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 18); + final lastNameParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 20); + final phoneParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 22); + final emailParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 24); + final object = UserModel( + bid: bidParam, + id: idParam, + deviceId: deviceIdParam, + createdAt: createdAtParam, + updatedAt: updatedAtParam, + recentActivity: recentActivityParam, + login: loginParam, + firstName: firstNameParam, + lastName: lastNameParam, + phone: phoneParam, + email: emailParam, + ); + object.avatarBind.targetId = const fb.Int64Reader().vTableGet( + buffer, + rootOffset, + 26, + 0, + ); + object.avatarBind.attach(store); + return object; + }, + ), ConversationModel: obx_int.EntityDefinition( - model: _entities[4], - toOneRelations: (ConversationModel object) => [ - object.lastMessageBind, - object.avatarBind, - object.opponentBind, - object.ownerBind, - object.draftMessageBind - ], - toManyRelations: (ConversationModel object) => { - obx_int.RelInfo.toMany(5, object.bid!): - object.participants - }, - getId: (ConversationModel object) => object.bid, - setId: (ConversationModel object, int id) { - object.bid = id; - }, - objectToFB: (ConversationModel object, fb.Builder fbb) { - final idOffset = fbb.writeString(object.id); - final typeOffset = fbb.writeString(object.type); - final nameOffset = fbb.writeString(object.name); - final descriptionOffset = object.description == null - ? null - : fbb.writeString(object.description!); - fbb.startTable(17); - fbb.addInt64(0, object.bid ?? 0); - fbb.addOffset(1, idOffset); - fbb.addInt64(2, object.createdAt.millisecondsSinceEpoch); - fbb.addInt64(3, object.updatedAt.millisecondsSinceEpoch); - fbb.addOffset(4, typeOffset); - fbb.addOffset(5, nameOffset); - fbb.addOffset(6, descriptionOffset); - fbb.addInt64(7, object.unreadMessagesCount); - fbb.addInt64(8, object.lastMessageBind.targetId); - fbb.addInt64(11, object.avatarBind.targetId); - fbb.addInt64(12, object.opponentBind.targetId); - fbb.addInt64(13, object.ownerBind.targetId); - fbb.addBool(14, object.isEncrypted); - fbb.addInt64(15, object.draftMessageBind.targetId); - fbb.finish(fbb.endTable()); - return object.bid ?? 0; - }, - objectFromFB: (obx.Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final bidParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 4); - final idParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final createdAtParam = DateTime.fromMillisecondsSinceEpoch( - const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0)); - final updatedAtParam = DateTime.fromMillisecondsSinceEpoch( - const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0)); - final nameParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 14, ''); - final typeParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 12, ''); - final unreadMessagesCountParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 18); - final descriptionParam = - const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 16); - final isEncryptedParam = - const fb.BoolReader().vTableGetNullable(buffer, rootOffset, 32); - final object = ConversationModel( - bid: bidParam, - id: idParam, - createdAt: createdAtParam, - updatedAt: updatedAtParam, - name: nameParam, - type: typeParam, - unreadMessagesCount: unreadMessagesCountParam, - description: descriptionParam, - isEncrypted: isEncryptedParam); - object.lastMessageBind.targetId = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0); - object.lastMessageBind.attach(store); - object.avatarBind.targetId = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 26, 0); - object.avatarBind.attach(store); - object.opponentBind.targetId = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 28, 0); - object.opponentBind.attach(store); - object.ownerBind.targetId = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 30, 0); - object.ownerBind.attach(store); - object.draftMessageBind.targetId = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 34, 0); - object.draftMessageBind.attach(store); - obx_int.InternalToManyAccess.setRelInfo( - object.participants, - store, - obx_int.RelInfo.toMany(5, object.bid!)); - return object; - }) + model: _entities[4], + toOneRelations: (ConversationModel object) => [ + object.lastMessageBind, + object.avatarBind, + object.opponentBind, + object.ownerBind, + object.draftMessageBind, + ], + toManyRelations: (ConversationModel object) => { + obx_int.RelInfo.toMany(5, object.bid!): + object.participants, + }, + getId: (ConversationModel object) => object.bid, + setId: (ConversationModel object, int id) { + object.bid = id; + }, + objectToFB: (ConversationModel object, fb.Builder fbb) { + final idOffset = fbb.writeString(object.id); + final typeOffset = fbb.writeString(object.type); + final nameOffset = fbb.writeString(object.name); + final descriptionOffset = object.description == null + ? null + : fbb.writeString(object.description!); + fbb.startTable(17); + fbb.addInt64(0, object.bid ?? 0); + fbb.addOffset(1, idOffset); + fbb.addInt64(2, object.createdAt.millisecondsSinceEpoch); + fbb.addInt64(3, object.updatedAt.millisecondsSinceEpoch); + fbb.addOffset(4, typeOffset); + fbb.addOffset(5, nameOffset); + fbb.addOffset(6, descriptionOffset); + fbb.addInt64(7, object.unreadMessagesCount); + fbb.addInt64(8, object.lastMessageBind.targetId); + fbb.addInt64(11, object.avatarBind.targetId); + fbb.addInt64(12, object.opponentBind.targetId); + fbb.addInt64(13, object.ownerBind.targetId); + fbb.addBool(14, object.isEncrypted); + fbb.addInt64(15, object.draftMessageBind.targetId); + fbb.finish(fbb.endTable()); + return object.bid ?? 0; + }, + objectFromFB: (obx.Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + final bidParam = const fb.Int64Reader().vTableGetNullable( + buffer, + rootOffset, + 4, + ); + final idParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGet(buffer, rootOffset, 6, ''); + final createdAtParam = DateTime.fromMillisecondsSinceEpoch( + const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0), + ); + final updatedAtParam = DateTime.fromMillisecondsSinceEpoch( + const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0), + ); + final nameParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGet(buffer, rootOffset, 14, ''); + final typeParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGet(buffer, rootOffset, 12, ''); + final unreadMessagesCountParam = const fb.Int64Reader() + .vTableGetNullable(buffer, rootOffset, 18); + final descriptionParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGetNullable(buffer, rootOffset, 16); + final isEncryptedParam = const fb.BoolReader().vTableGetNullable( + buffer, + rootOffset, + 32, + ); + final object = ConversationModel( + bid: bidParam, + id: idParam, + createdAt: createdAtParam, + updatedAt: updatedAtParam, + name: nameParam, + type: typeParam, + unreadMessagesCount: unreadMessagesCountParam, + description: descriptionParam, + isEncrypted: isEncryptedParam, + ); + object.lastMessageBind.targetId = const fb.Int64Reader().vTableGet( + buffer, + rootOffset, + 20, + 0, + ); + object.lastMessageBind.attach(store); + object.avatarBind.targetId = const fb.Int64Reader().vTableGet( + buffer, + rootOffset, + 26, + 0, + ); + object.avatarBind.attach(store); + object.opponentBind.targetId = const fb.Int64Reader().vTableGet( + buffer, + rootOffset, + 28, + 0, + ); + object.opponentBind.attach(store); + object.ownerBind.targetId = const fb.Int64Reader().vTableGet( + buffer, + rootOffset, + 30, + 0, + ); + object.ownerBind.attach(store); + object.draftMessageBind.targetId = const fb.Int64Reader().vTableGet( + buffer, + rootOffset, + 34, + 0, + ); + object.draftMessageBind.attach(store); + obx_int.InternalToManyAccess.setRelInfo( + object.participants, + store, + obx_int.RelInfo.toMany(5, object.bid!), + ); + return object; + }, + ), }; return obx_int.ModelDefinition(model, bindings); @@ -946,214 +1137,263 @@ obx_int.ModelDefinition getObjectBoxModel() { /// [AttachmentModel] entity fields to define ObjectBox queries. class AttachmentModel_ { /// See [AttachmentModel.bid]. - static final bid = - obx.QueryIntegerProperty(_entities[0].properties[0]); + static final bid = obx.QueryIntegerProperty( + _entities[0].properties[0], + ); /// See [AttachmentModel.fileId]. - static final fileId = - obx.QueryStringProperty(_entities[0].properties[1]); + static final fileId = obx.QueryStringProperty( + _entities[0].properties[1], + ); /// See [AttachmentModel.fileName]. - static final fileName = - obx.QueryStringProperty(_entities[0].properties[2]); + static final fileName = obx.QueryStringProperty( + _entities[0].properties[2], + ); /// See [AttachmentModel.fileBlurHash]. - static final fileBlurHash = - obx.QueryStringProperty(_entities[0].properties[3]); + static final fileBlurHash = obx.QueryStringProperty( + _entities[0].properties[3], + ); /// See [AttachmentModel.url]. - static final url = - obx.QueryStringProperty(_entities[0].properties[4]); + static final url = obx.QueryStringProperty( + _entities[0].properties[4], + ); /// See [AttachmentModel.contentType]. - static final contentType = - obx.QueryStringProperty(_entities[0].properties[5]); + static final contentType = obx.QueryStringProperty( + _entities[0].properties[5], + ); /// See [AttachmentModel.fileHeight]. - static final fileHeight = - obx.QueryIntegerProperty(_entities[0].properties[6]); + static final fileHeight = obx.QueryIntegerProperty( + _entities[0].properties[6], + ); /// See [AttachmentModel.fileWidth]. - static final fileWidth = - obx.QueryIntegerProperty(_entities[0].properties[7]); + static final fileWidth = obx.QueryIntegerProperty( + _entities[0].properties[7], + ); } /// [AvatarModel] entity fields to define ObjectBox queries. class AvatarModel_ { /// See [AvatarModel.bid]. - static final bid = - obx.QueryIntegerProperty(_entities[1].properties[0]); + static final bid = obx.QueryIntegerProperty( + _entities[1].properties[0], + ); /// See [AvatarModel.fileId]. - static final fileId = - obx.QueryStringProperty(_entities[1].properties[1]); + static final fileId = obx.QueryStringProperty( + _entities[1].properties[1], + ); /// See [AvatarModel.fileName]. - static final fileName = - obx.QueryStringProperty(_entities[1].properties[2]); + static final fileName = obx.QueryStringProperty( + _entities[1].properties[2], + ); /// See [AvatarModel.fileBlurHash]. - static final fileBlurHash = - obx.QueryStringProperty(_entities[1].properties[3]); + static final fileBlurHash = obx.QueryStringProperty( + _entities[1].properties[3], + ); /// See [AvatarModel.imageUrl]. - static final imageUrl = - obx.QueryStringProperty(_entities[1].properties[4]); + static final imageUrl = obx.QueryStringProperty( + _entities[1].properties[4], + ); } /// [MessageModel] entity fields to define ObjectBox queries. class MessageModel_ { /// See [MessageModel.bid]. - static final bid = - obx.QueryIntegerProperty(_entities[2].properties[0]); + static final bid = obx.QueryIntegerProperty( + _entities[2].properties[0], + ); /// See [MessageModel.id]. - static final id = - obx.QueryStringProperty(_entities[2].properties[1]); + static final id = obx.QueryStringProperty( + _entities[2].properties[1], + ); /// See [MessageModel.from]. - static final from = - obx.QueryStringProperty(_entities[2].properties[2]); + static final from = obx.QueryStringProperty( + _entities[2].properties[2], + ); /// See [MessageModel.cid]. - static final cid = - obx.QueryStringProperty(_entities[2].properties[3]); - - /// See [MessageModel.rawStatus]. - static final rawStatus = - obx.QueryStringProperty(_entities[2].properties[4]); + static final cid = obx.QueryStringProperty( + _entities[2].properties[3], + ); /// See [MessageModel.body]. - static final body = - obx.QueryStringProperty(_entities[2].properties[5]); + static final body = obx.QueryStringProperty( + _entities[2].properties[4], + ); /// See [MessageModel.t]. - static final t = - obx.QueryIntegerProperty(_entities[2].properties[6]); + static final t = obx.QueryIntegerProperty( + _entities[2].properties[5], + ); /// See [MessageModel.createdAt]. - static final createdAt = - obx.QueryDateProperty(_entities[2].properties[7]); + static final createdAt = obx.QueryDateProperty( + _entities[2].properties[6], + ); /// See [MessageModel.dbExtension]. - static final dbExtension = - obx.QueryStringProperty(_entities[2].properties[8]); + static final dbExtension = obx.QueryStringProperty( + _entities[2].properties[7], + ); /// See [MessageModel.repliedMessageId]. - static final repliedMessageId = - obx.QueryStringProperty(_entities[2].properties[9]); + static final repliedMessageId = obx.QueryStringProperty( + _entities[2].properties[8], + ); /// See [MessageModel.replyMessageBind]. static final replyMessageBind = obx.QueryRelationToOne( - _entities[2].properties[10]); + _entities[2].properties[9], + ); /// See [MessageModel.senderBind]. static final senderBind = obx.QueryRelationToOne( - _entities[2].properties[11]); + _entities[2].properties[10], + ); /// See [MessageModel.isOwn]. - static final isOwn = - obx.QueryBooleanProperty(_entities[2].properties[12]); + static final isOwn = obx.QueryBooleanProperty( + _entities[2].properties[11], + ); /// See [MessageModel.isTempReplied]. - static final isTempReplied = - obx.QueryBooleanProperty(_entities[2].properties[13]); + static final isTempReplied = obx.QueryBooleanProperty( + _entities[2].properties[12], + ); /// See [MessageModel.forwardedMessageId]. - static final forwardedMessageId = - obx.QueryStringProperty(_entities[2].properties[14]); + static final forwardedMessageId = obx.QueryStringProperty( + _entities[2].properties[13], + ); /// See [MessageModel.isEdited]. - static final isEdited = - obx.QueryBooleanProperty(_entities[2].properties[15]); + static final isEdited = obx.QueryBooleanProperty( + _entities[2].properties[14], + ); + + /// See [MessageModel.dbStatus]. + static final dbStatus = obx.QueryIntegerProperty( + _entities[2].properties[15], + ); /// see [MessageModel.attachments] static final attachments = obx.QueryRelationToMany( - _entities[2].relations[0]); + _entities[2].relations[0], + ); } /// [UserModel] entity fields to define ObjectBox queries. class UserModel_ { /// See [UserModel.bid]. - static final bid = - obx.QueryIntegerProperty(_entities[3].properties[0]); + static final bid = obx.QueryIntegerProperty( + _entities[3].properties[0], + ); /// See [UserModel.id]. - static final id = - obx.QueryStringProperty(_entities[3].properties[1]); + static final id = obx.QueryStringProperty( + _entities[3].properties[1], + ); /// See [UserModel.deviceId]. - static final deviceId = - obx.QueryStringProperty(_entities[3].properties[2]); + static final deviceId = obx.QueryStringProperty( + _entities[3].properties[2], + ); /// See [UserModel.createdAt]. - static final createdAt = - obx.QueryDateProperty(_entities[3].properties[3]); + static final createdAt = obx.QueryDateProperty( + _entities[3].properties[3], + ); /// See [UserModel.updatedAt]. - static final updatedAt = - obx.QueryDateProperty(_entities[3].properties[4]); + static final updatedAt = obx.QueryDateProperty( + _entities[3].properties[4], + ); /// See [UserModel.recentActivity]. - static final recentActivity = - obx.QueryIntegerProperty(_entities[3].properties[5]); + static final recentActivity = obx.QueryIntegerProperty( + _entities[3].properties[5], + ); /// See [UserModel.login]. - static final login = - obx.QueryStringProperty(_entities[3].properties[6]); + static final login = obx.QueryStringProperty( + _entities[3].properties[6], + ); /// See [UserModel.firstName]. - static final firstName = - obx.QueryStringProperty(_entities[3].properties[7]); + static final firstName = obx.QueryStringProperty( + _entities[3].properties[7], + ); /// See [UserModel.lastName]. - static final lastName = - obx.QueryStringProperty(_entities[3].properties[8]); + static final lastName = obx.QueryStringProperty( + _entities[3].properties[8], + ); /// See [UserModel.phone]. - static final phone = - obx.QueryStringProperty(_entities[3].properties[9]); + static final phone = obx.QueryStringProperty( + _entities[3].properties[9], + ); /// See [UserModel.email]. - static final email = - obx.QueryStringProperty(_entities[3].properties[10]); + static final email = obx.QueryStringProperty( + _entities[3].properties[10], + ); /// See [UserModel.avatarBind]. static final avatarBind = obx.QueryRelationToOne( - _entities[3].properties[11]); + _entities[3].properties[11], + ); } /// [ConversationModel] entity fields to define ObjectBox queries. class ConversationModel_ { /// See [ConversationModel.bid]. - static final bid = - obx.QueryIntegerProperty(_entities[4].properties[0]); + static final bid = obx.QueryIntegerProperty( + _entities[4].properties[0], + ); /// See [ConversationModel.id]. - static final id = - obx.QueryStringProperty(_entities[4].properties[1]); + static final id = obx.QueryStringProperty( + _entities[4].properties[1], + ); /// See [ConversationModel.createdAt]. - static final createdAt = - obx.QueryDateProperty(_entities[4].properties[2]); + static final createdAt = obx.QueryDateProperty( + _entities[4].properties[2], + ); /// See [ConversationModel.updatedAt]. - static final updatedAt = - obx.QueryDateProperty(_entities[4].properties[3]); + static final updatedAt = obx.QueryDateProperty( + _entities[4].properties[3], + ); /// See [ConversationModel.type]. - static final type = - obx.QueryStringProperty(_entities[4].properties[4]); + static final type = obx.QueryStringProperty( + _entities[4].properties[4], + ); /// See [ConversationModel.name]. - static final name = - obx.QueryStringProperty(_entities[4].properties[5]); + static final name = obx.QueryStringProperty( + _entities[4].properties[5], + ); /// See [ConversationModel.description]. - static final description = - obx.QueryStringProperty(_entities[4].properties[6]); + static final description = obx.QueryStringProperty( + _entities[4].properties[6], + ); /// See [ConversationModel.unreadMessagesCount]. static final unreadMessagesCount = @@ -1162,33 +1402,40 @@ class ConversationModel_ { /// See [ConversationModel.lastMessageBind]. static final lastMessageBind = obx.QueryRelationToOne( - _entities[4].properties[8]); + _entities[4].properties[8], + ); /// See [ConversationModel.avatarBind]. static final avatarBind = obx.QueryRelationToOne( - _entities[4].properties[9]); + _entities[4].properties[9], + ); /// See [ConversationModel.opponentBind]. static final opponentBind = obx.QueryRelationToOne( - _entities[4].properties[10]); + _entities[4].properties[10], + ); /// See [ConversationModel.ownerBind]. static final ownerBind = obx.QueryRelationToOne( - _entities[4].properties[11]); + _entities[4].properties[11], + ); /// See [ConversationModel.isEncrypted]. - static final isEncrypted = - obx.QueryBooleanProperty(_entities[4].properties[12]); + static final isEncrypted = obx.QueryBooleanProperty( + _entities[4].properties[12], + ); /// See [ConversationModel.draftMessageBind]. static final draftMessageBind = obx.QueryRelationToOne( - _entities[4].properties[13]); + _entities[4].properties[13], + ); /// see [ConversationModel.participants] static final participants = obx.QueryRelationToMany( - _entities[4].relations[0]); + _entities[4].relations[0], + ); } diff --git a/sama_chat_client/lib/src/db/db_service.dart b/sama_chat_client/lib/src/db/db_service.dart index 95c4a54c..73a45e40 100644 --- a/sama_chat_client/lib/src/db/db_service.dart +++ b/sama_chat_client/lib/src/db/db_service.dart @@ -4,7 +4,6 @@ import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; import '../../objectbox.g.dart'; -import '../features/conversation/models/chat_message.dart'; import 'models/attachment_model.dart'; import 'models/avatar_model.dart'; import 'models/conversation_model.dart'; @@ -190,7 +189,7 @@ class DatabaseService { chat.avatar?.bid = chatInDb.avatar?.bid; if (chat.avatar?.imageUrl != chatInDb.avatar?.imageUrl) { //to check - print('AMBRA chat.avatar putAsync'); + print('chat.avatar putAsync'); await store! .box() .putAsync(chat.avatar!, mode: PutMode.update); @@ -200,8 +199,8 @@ class DatabaseService { chat.lastMessage?.bid = chatInDb.lastMessage?.bid; if (chat.lastMessage != chatInDb.lastMessage) { - var msg = chatInDb.lastMessage - ?.copyWith(rawStatus: chat.lastMessage?.rawStatus); + var msg = + chatInDb.lastMessage?.copyWith(status: chat.lastMessage?.status); await store!.box().putAsync(msg!, mode: PutMode.update); } } else { @@ -288,19 +287,22 @@ class DatabaseService { .equals(cid) .and(MessageModel_.isTempReplied .isNull()) //hide Replied messages, that's not loaded by pagination - .and(MessageModel_.rawStatus - .notEquals(ChatMessageStatus.draft.name) //hide draft messages - .or(MessageModel_.rawStatus.isNull())); + .and(MessageModel_.dbStatus + .notEquals(MessageModelStatus.draft.index) //hide draft messages + .or(MessageModel_.dbStatus.isNull())); if (ltDate != null) { condition = condition.and(MessageModel_.createdAt.lessThanDate(ltDate)); - } if (gtDate != null) { - condition = condition.and(MessageModel_.createdAt.greaterThanDate(gtDate)); + } + if (gtDate != null) { + condition = + condition.and(MessageModel_.createdAt.greaterThanDate(gtDate)); } final query = store! .box() .query(condition) - .order(MessageModel_.createdAt, flags: gtDate == null ? Order.descending : 0) + .order(MessageModel_.createdAt, + flags: gtDate == null ? Order.descending : 0) .build() ..limit = limit ?? 0; final results = await query.findAsync(); @@ -367,10 +369,11 @@ class DatabaseService { return results; } - Future> getMessagesLocalByStatus(String status) async { + Future> getMessagesLocalByStatus( + MessageModelStatus status) async { final query = store ?.box() - .query(MessageModel_.rawStatus.equals(status)) + .query(MessageModel_.dbStatus.equals(status.index)) .build(); final results = query?.findAsync(); query?.close(); @@ -378,12 +381,12 @@ class DatabaseService { } Future getMessageLocalByStatus( - String cid, String status) async { + String cid, MessageModelStatus status) async { final query = store! .box() .query(MessageModel_.cid .equals(cid) - .and(MessageModel_.rawStatus.equals(status))) + .and(MessageModel_.dbStatus.equals(status.index))) .build(); final result = await query.findFirstAsync(); query.close(); diff --git a/sama_chat_client/lib/src/db/local/message_local_datasource.dart b/sama_chat_client/lib/src/db/local/message_local_datasource.dart index fa6c160d..da2f2aa6 100644 --- a/sama_chat_client/lib/src/db/local/message_local_datasource.dart +++ b/sama_chat_client/lib/src/db/local/message_local_datasource.dart @@ -27,7 +27,7 @@ class MessageLocalDatasource { } Future getMessageLocalByStatus( - String cid, String status) async { + String cid, MessageModelStatus status) async { print('getMessageLocalByStatus= $cid'); try { return await _databaseService.getMessageLocalByStatus(cid, status); @@ -44,7 +44,8 @@ class MessageLocalDatasource { } } - Future> getMessagesLocalByStatus(String status) async { + Future> getMessagesLocalByStatus( + MessageModelStatus status) async { try { return await _databaseService.getMessagesLocalByStatus(status); } catch (e) { diff --git a/sama_chat_client/lib/src/db/models/message_model.dart b/sama_chat_client/lib/src/db/models/message_model.dart index 0fc2d94b..75c062f3 100644 --- a/sama_chat_client/lib/src/db/models/message_model.dart +++ b/sama_chat_client/lib/src/db/models/message_model.dart @@ -1,11 +1,14 @@ import 'dart:convert'; +import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; import 'package:objectbox/objectbox.dart'; import 'package:sama_sdk/api/conversations/models/message.dart'; import 'models.dart'; +enum MessageModelStatus { none, pending, draft, sent, read } + @Entity() // ignore: must_be_immutable class MessageModel extends Equatable { @@ -17,7 +20,8 @@ class MessageModel extends Equatable { final String cid; final String? repliedMessageId; final String? forwardedMessageId; - final String? rawStatus; + @Transient() + MessageModelStatus status; final String? body; final bool isOwn; final int? t; @@ -36,6 +40,30 @@ class MessageModel extends Equatable { if (value != null) extension = jsonDecode(value); } + int? get dbStatus { + ensureStableEnumValues(); + return status.index; + } + + set dbStatus(int? value) { + ensureStableEnumValues(); + if (value == null) { + status = MessageModelStatus.none; + } else { + status = value >= 0 && value < MessageModelStatus.values.length + ? MessageModelStatus.values[value] + : MessageModelStatus.none; + } + } + + void ensureStableEnumValues() { + assert(MessageModelStatus.none.index == 0); + assert(MessageModelStatus.pending.index == 1); + assert(MessageModelStatus.draft.index == 2); + assert(MessageModelStatus.sent.index == 3); + assert(MessageModelStatus.read.index == 4); + } + MessageModel({ this.bid, required this.id, @@ -44,7 +72,7 @@ class MessageModel extends Equatable { required this.isOwn, this.repliedMessageId, this.forwardedMessageId, - this.rawStatus, + this.status = MessageModelStatus.none, this.body, this.createdAt, this.t, @@ -74,7 +102,7 @@ class MessageModel extends Equatable { String? cid, String? repliedMessageId, String? forwardedMessageId, - String? rawStatus, + MessageModelStatus? status, String? body, bool? isOwn, DateTime? createdAt, @@ -93,7 +121,7 @@ class MessageModel extends Equatable { cid: cid ?? this.cid, repliedMessageId: repliedMessageId ?? this.repliedMessageId, forwardedMessageId: forwardedMessageId ?? this.forwardedMessageId, - rawStatus: rawStatus ?? this.rawStatus, + status: status ?? this.status, body: body ?? this.body, isOwn: isOwn ?? this.isOwn, createdAt: createdAt ?? this.createdAt, @@ -108,12 +136,12 @@ class MessageModel extends Equatable { @override String toString() { - return 'MessageModel{bid: $bid, id: $id, from: $from, cid: $cid, rawStatus: $rawStatus, body: $body, t: $t, createdAt: $createdAt, extension: $extension, attachments: $attachments}'; + return 'MessageModel{bid: $bid, id: $id, from: $from, cid: $cid, status: $status, body: $body, t: $t, createdAt: $createdAt, extension: $extension, attachments: $attachments}'; } @override List get props => - [id, from, rawStatus, body, t, createdAt?.millisecondsSinceEpoch]; + [id, from, status, body, t, createdAt?.millisecondsSinceEpoch]; } extension MessageModelExtension on Message { @@ -124,7 +152,11 @@ extension MessageModelExtension on Message { cid: cid!, repliedMessageId: repliedMessageId, forwardedMessageId: forwardedMessageId, - rawStatus: isOwn ? rawStatus ?? 'sent' : null, + status: isOwn + ? MessageModelStatus.values + .firstWhereOrNull((i) => i.name == rawStatus) ?? + MessageModelStatus.sent + : MessageModelStatus.none, body: body, isOwn: isOwn, createdAt: createdAt, diff --git a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart index 422a36cd..94e2a82b 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart @@ -48,7 +48,7 @@ class ConversationBloc extends Bloc { final MessagesRepository messagesRepository; final UserRepository userRepository; - StreamSubscription? incomingMessagesSubscription; + StreamSubscription? incomingMessagesSubscription; StreamSubscription? statusMessagesSubscription; StreamSubscription? typingMessageSubscription; StreamSubscription>? lastActivitySubscription; @@ -222,8 +222,8 @@ class ConversationBloc extends Bloc { ) async { try { if (state.status == ConversationStatus.initial) { - final messages = - await messagesRepository.getStoredMessages(currentConversation); + final messages = await buildChatMessageModels( + await messagesRepository.getStoredMessages(currentConversation.id)); emit( state.copyWith( status: ConversationStatus.success, @@ -261,7 +261,8 @@ class ConversationBloc extends Bloc { ltDate: ltDate, gtTime: gtTime); switch (resource.status) { case Status.success: - var messages = resource.data ?? List.empty(); + var messages = + await buildChatMessageModels(resource.data ?? List.empty()); messages.isEmpty ? emit(state.copyWith(hasReachedMax: true, initial: false)) : emit( @@ -401,7 +402,9 @@ class ConversationBloc extends Bloc { if (event.message.extension?['modified'] ?? false) { var indexMsg = messages.indexWhere((m) => m.id == event.message.id); - messages[indexMsg] = event.message; + var msg = messages[indexMsg]; + messages[indexMsg] = event.message + .toChatMessage(msg.isLastUserMessage, msg.isFirstUserMessage); } else { if (messages.isNotEmpty) { messages.first = messages.first.copyWith( @@ -414,14 +417,12 @@ class ConversationBloc extends Bloc { } messages.insert( - 0, - event.message.copyWith( - isFirstUserMessage: messages.isEmpty || - isServiceMessage(messages.first) || - event.message.from != messages.first.from, - isLastUserMessage: true, - ), - ); + 0, + event.message.toChatMessage( + true, + messages.isEmpty || + isServiceMessage(messages.first) || + event.message.from != messages.first.from)); } emit( @@ -433,7 +434,7 @@ class ConversationBloc extends Bloc { var messages = [...state.messages]; var msg = messages.firstWhere((o) => o.id == event.status.messageId); - var msgUpdated = msg.copyWith(status: ChatMessageStatus.pending); + var msgUpdated = msg.copyWith(status: MessageModelStatus.pending); messages[messages.indexOf(msg)] = msgUpdated; emit(state.copyWith(messages: messages)); } @@ -453,7 +454,6 @@ class ConversationBloc extends Bloc { var messages = [...state.messages]; var messagesMap = {}..addEntries(messages.map((m) => MapEntry(m.id, m))); event.status.msgIds?.forEach((id) { - int currentIndex = messages.indexOf(messagesMap[id]); int prevIndex = currentIndex + 1; int nextIndex = currentIndex - 1; @@ -496,7 +496,7 @@ class ConversationBloc extends Bloc { var msg = messages.firstWhereOrNull((o) => o.id == event.status.messageId); if (msg == null) return; var msgUpdated = msg.copyWith( - id: event.status.serverMessageId, status: ChatMessageStatus.sent); + id: event.status.serverMessageId, status: MessageModelStatus.sent); var msgLocal = await messagesRepository.updateMessageLocal(msgUpdated); @@ -514,12 +514,12 @@ class ConversationBloc extends Bloc { FutureOr _onReadStatusReceived( _ReadStatusReceived event, Emitter emit) async { - var messages = {for (var v in state.messages) v.id!: v}; + var messages = {for (var v in state.messages) v.id: v}; var msgListUpdated = []; event.status.msgIds?.forEach((id) { if (messages[id] != null && - messages[id]?.status != ChatMessageStatus.read) { - var msg = messages[id]!.copyWith(status: ChatMessageStatus.read); + messages[id]?.status != MessageModelStatus.read) { + var msg = messages[id]!.copyWith(status: MessageModelStatus.read); messages[id] = msg; msgListUpdated.add(msg); } @@ -540,6 +540,37 @@ class ConversationBloc extends Bloc { emit(state.copyWith(messages: messages)); } + Future> buildChatMessageModels( + List messages) async { + var result = []; + + final nextMsg = messages.isNotEmpty + ? (await messagesRepository.getStoredMessages(messages[0].cid, + gtDate: messages[0].createdAt, limit: 1)) + .firstOrNull + : null; + + for (int i = 0; i < messages.length; i++) { + var message = messages[i]; + var chatMessage = message.toChatMessage( + i == 0 + ? nextMsg?.from != messages[i].from + : isServiceMessage(messages[i - 1]) || + messages[i - 1].from != messages[i].from, + i == messages.length - 1 || + isServiceMessage(messages[i + 1]) || + messages[i + 1].from != messages[i].from); + + if (i == messages.length - 1 && limitMessages == messages.length) { + // do not put last message in result to determine if it's last for user with next pagination + continue; + } + + result.add(chatMessage); + } + return result; + } + @override Future close() { unsubscribeOpponentLastActivity(); diff --git a/sama_chat_client/lib/src/features/conversation/bloc/conversation_event.dart b/sama_chat_client/lib/src/features/conversation/bloc/conversation_event.dart index cc31accd..5c3ee9a6 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/conversation_event.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/conversation_event.dart @@ -21,7 +21,7 @@ final class MessagesMoreRequested extends ConversationEvent { } final class _MessageReceived extends ConversationEvent { - final ChatMessage message; + final MessageModel message; const _MessageReceived(this.message); } diff --git a/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_bloc.dart b/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_bloc.dart index a54e2cfe..66ef5fec 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_bloc.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_bloc.dart @@ -125,7 +125,7 @@ class SendMessageBloc extends Bloc { Future _onDraftMessageReceived( _DraftMessageReceived event, Emitter emit) async { var draftMsg = await messagesRepository.getMessageLocalByStatus( - currentConversation.id, ChatMessageStatus.draft.name); + currentConversation.id, MessageModelStatus.draft); if (draftMsg != null) { emit(state.copyWith( draftMessage: () => draftMsg, diff --git a/sama_chat_client/lib/src/features/conversation/models/chat_message.dart b/sama_chat_client/lib/src/features/conversation/models/chat_message.dart index 9ad69a18..22f5241a 100644 --- a/sama_chat_client/lib/src/features/conversation/models/chat_message.dart +++ b/sama_chat_client/lib/src/features/conversation/models/chat_message.dart @@ -1,12 +1,9 @@ import '../../../db/models/models.dart'; -enum ChatMessageStatus { none, pending, draft, sent, read } - // ignore: must_be_immutable class ChatMessage extends MessageModel { final bool isFirstUserMessage; final bool isLastUserMessage; - final ChatMessageStatus status; ChatMessage({ required this.isFirstUserMessage, @@ -15,11 +12,10 @@ class ChatMessage extends MessageModel { required super.id, required super.from, required super.cid, - this.status = ChatMessageStatus.none, super.bid, super.repliedMessageId, super.forwardedMessageId, - super.rawStatus, + super.status, super.body, super.createdAt, super.t, @@ -32,14 +28,13 @@ class ChatMessage extends MessageModel { ChatMessage copyWith({ bool? isFirstUserMessage, bool? isLastUserMessage, - ChatMessageStatus? status, int? bid, String? id, String? from, String? cid, String? repliedMessageId, String? forwardedMessageId, - String? rawStatus, + MessageModelStatus? status, String? body, bool? isOwn, int? t, @@ -54,7 +49,6 @@ class ChatMessage extends MessageModel { return ChatMessage( isFirstUserMessage: isFirstUserMessage ?? this.isFirstUserMessage, isLastUserMessage: isLastUserMessage ?? this.isLastUserMessage, - status: status ?? this.status, bid: bid ?? this.bid, id: id ?? this.id, from: from ?? this.from, @@ -63,7 +57,7 @@ class ChatMessage extends MessageModel { forwardedMessageId: forwardedMessageId ?? this.forwardedMessageId, body: body ?? this.body, isOwn: isOwn ?? this.isOwn, - rawStatus: rawStatus ?? status?.name ?? this.rawStatus, + status: status ?? this.status, createdAt: createdAt ?? this.createdAt, t: t ?? this.t, isTempReplied: isTempReplied ?? this.isTempReplied, @@ -95,18 +89,12 @@ extension ChatMessageExtension on MessageModel { bid: bid, isLastUserMessage: isLastUserMessage, isFirstUserMessage: isFirstUserMessage, - //consider move ChatMessageStatus enum to model base - status: rawStatus != null - ? ChatMessageStatus.values.byName(rawStatus!) - : isOwn - ? ChatMessageStatus.sent - : ChatMessageStatus.none, id: id, from: from, cid: cid, repliedMessageId: repliedMessageId, forwardedMessageId: forwardedMessageId, - rawStatus: rawStatus, + status: status, body: body, isOwn: isOwn, createdAt: createdAt, diff --git a/sama_chat_client/lib/src/features/conversation/widgets/message_status_widget.dart b/sama_chat_client/lib/src/features/conversation/widgets/message_status_widget.dart index 70d63a90..2c4a2484 100644 --- a/sama_chat_client/lib/src/features/conversation/widgets/message_status_widget.dart +++ b/sama_chat_client/lib/src/features/conversation/widgets/message_status_widget.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; +import '../../../db/models/message_model.dart'; import '../../../shared/ui/colors.dart'; -import '../models/chat_message.dart'; class MessageStatusWidget extends StatelessWidget { - final ChatMessageStatus status; + final MessageModelStatus status; const MessageStatusWidget({ super.key, @@ -13,7 +13,7 @@ class MessageStatusWidget extends StatelessWidget { @override Widget build(BuildContext context) { switch (status) { - case ChatMessageStatus.read: + case MessageModelStatus.read: return const Stack(children: [ Icon( Icons.check_rounded, @@ -27,7 +27,7 @@ class MessageStatusWidget extends StatelessWidget { child: Icon(Icons.check_rounded, size: 15.0, color: lightMallow), ) ]); - case ChatMessageStatus.sent: + case MessageModelStatus.sent: return const Stack(children: [ Icon(Icons.check_rounded, size: 15.0, color: lightMallow), Padding( @@ -37,7 +37,7 @@ class MessageStatusWidget extends StatelessWidget { child: SizedBox.square(dimension: 15.0), ) ]); - case ChatMessageStatus.pending: + case MessageModelStatus.pending: return const Stack(children: [ Icon(Icons.watch_later_outlined, size: 15.0, color: lightMallow), Padding( diff --git a/sama_chat_client/lib/src/repository/conversation/conversation_repository.dart b/sama_chat_client/lib/src/repository/conversation/conversation_repository.dart index 8fb9ac37..65701a4c 100644 --- a/sama_chat_client/lib/src/repository/conversation/conversation_repository.dart +++ b/sama_chat_client/lib/src/repository/conversation/conversation_repository.dart @@ -36,7 +36,7 @@ class ConversationRepository { } StreamSubscription? incomingSystemMessagesSubscription; - StreamSubscription? incomingMessagesSubscription; + StreamSubscription? incomingMessagesSubscription; StreamSubscription? statusMessagesSubscription; StreamSubscription? typingMessageSubscription; @@ -113,7 +113,7 @@ class ConversationRepository { } ConversationModel updatedConversation; - if (message.status == ChatMessageStatus.draft) { + if (message.status == MessageModelStatus.draft) { updatedConversation = conversation.copyWith(draftMessage: () => message); } else { @@ -150,7 +150,7 @@ class ConversationRepository { if (conversationStored != null && conversationStored.lastMessage == null) { var lastMsg = (await messagesRepository - .getStoredMessages(conversationStored, limit: 1)) + .getStoredMessages(conversationStored.id, limit: 1)) .firstOrNull; var updatedChat = conversationStored.copyWith(lastMessage: lastMsg); await localDatasource.updateConversationLocal(updatedChat); diff --git a/sama_chat_client/lib/src/repository/messages/messages_repository.dart b/sama_chat_client/lib/src/repository/messages/messages_repository.dart index 6beff687..2fc22ea3 100644 --- a/sama_chat_client/lib/src/repository/messages/messages_repository.dart +++ b/sama_chat_client/lib/src/repository/messages/messages_repository.dart @@ -8,13 +8,13 @@ import '../../db/local/message_local_datasource.dart'; import '../../db/models/models.dart'; import '../../db/network_bound_resource.dart'; import '../../db/resource.dart'; -import '../../features/conversation/models/models.dart'; import '../user/user_repository.dart'; +const limitMessages = 30; + class MessagesRepository { final MessageLocalDatasource localDatasource; final UserRepository userRepository; - final limitMessages = 30; MessagesRepository( {required this.localDatasource, required this.userRepository}) { @@ -28,10 +28,10 @@ class MessagesRepository { StreamSubscription? deletedMessageSubscription; StreamSubscription? typingMessageSubscription; - final StreamController _incomingMessagesController = + final StreamController _incomingMessagesController = StreamController.broadcast(); - Stream get incomingMessagesStream => + Stream get incomingMessagesStream => _incomingMessagesController.stream; final StreamController _statusMessagesController = @@ -46,9 +46,9 @@ class MessagesRepository { Stream get typingMessageStream => _typingMessageController.stream; - Future>> getAllMessages(ConversationModel chat, + Future>> getAllMessages(ConversationModel chat, {DateTime? ltDate, DateTime? gtTime}) async { - return NetworkBoundResources, List>() + return NetworkBoundResources, List>() .asFuture( loadFromDb: () => localDatasource.getAllMessagesLocal(chat.id, ltDate: ltDate, limit: limitMessages), @@ -63,7 +63,6 @@ class MessagesRepository { localDatasource.removeMessagesLocal(idsToDelete); return localDatasource.saveMessagesLocal(newData); }, - processResponse: buildChatMessageModels, ); } @@ -117,7 +116,7 @@ class MessagesRepository { t: DateTime.now().millisecondsSinceEpoch ~/ 1000, createdAt: DateTime.now()); - _incomingMessagesController.add(messageModel.toChatMessage(true, true)); + _incomingMessagesController.add(messageModel); } Future changeMessageTone(String body, String tone) async { @@ -127,13 +126,12 @@ class MessagesRepository { }); } - Future> getStoredMessagesByIds( + Future> getStoredMessagesByIds( ConversationModel chat, List ids) async { - var messages = await localDatasource.getMessagesLocal(ids); - return buildChatMessageModels(messages); + return await localDatasource.getMessagesLocal(ids); } - Future getReplyMessageById( + Future getReplyMessageById( ConversationModel chat, String id) async { var message = await localDatasource.getMessageLocalById(id); if (message == null) { @@ -141,29 +139,26 @@ class MessagesRepository { message = message?.copyWith(isTempReplied: true); if (message != null) message = await saveMessageLocal(message); } - - if (message != null) { - return (await buildChatMessageModels([message])).firstOrNull; - } - return null; + return message; } - Future> getStoredMessages(ConversationModel chat, - {int? limit}) async { - var messages = await localDatasource.getAllMessagesLocal(chat.id, - limit: limit ?? limitMessages); - return buildChatMessageModels(messages); + Future> getStoredMessages(String cid, + {DateTime? ltDate, DateTime? gtDate, int? limit}) async { + return await localDatasource.getAllMessagesLocal(cid, + ltDate: ltDate, gtDate: gtDate, limit: limit ?? limitMessages); } Future getMessageLocalById(String id) { return localDatasource.getMessageLocalById(id); } - Future getMessageLocalByStatus(String cid, String status) { + Future getMessageLocalByStatus( + String cid, MessageModelStatus status) { return localDatasource.getMessageLocalByStatus(cid, status); } - Future> getMessagesLocalByStatus(String status) { + Future> getMessagesLocalByStatus( + MessageModelStatus status) { return localDatasource.getMessagesLocalByStatus(status); } @@ -189,36 +184,33 @@ class MessagesRepository { repliedMessageId: replyMessage?.id, from: currentUser?.id, id: const Uuid().v1(), - rawStatus: ChatMessageStatus.none.name, + rawStatus: MessageModelStatus.none.name, t: DateTime.now().millisecondsSinceEpoch ~/ 1000, createdAt: DateTime.now()); var msgModel = message .toMessageModel(true, currentUser!) .copyWith(replyMessage: replyMessage); - _incomingMessagesController.add(msgModel.toChatMessage(true, true)); + _incomingMessagesController.add(msgModel); return api.sendMessage(message: message).then((response) async { var (serverMid, msg) = response; if (serverMid == null) { - var msgUpdated = - msgModel.copyWith(rawStatus: ChatMessageStatus.pending.name); + var msgUpdated = msgModel.copyWith(status: MessageModelStatus.pending); saveMessageLocal(msgUpdated); _statusMessagesController .add(api.PendingMessageStatus.fromJson({'mid': message.id})); } if (msg != null) { - ChatMessage chatMessage; + MessageModel msgModel; if (msg.extension?['modified'] ?? false) { - chatMessage = - msg.toMessageModel(true, currentUser).toChatMessage(true, true); + msgModel = msg.toMessageModel(true, currentUser); } else { var sender = await userRepository.getUserById(msg.from ?? ''); sender ??= UserModel(); - chatMessage = - msg.toMessageModel(false, sender).toChatMessage(true, true); + msgModel = msg.toMessageModel(false, sender); } - _incomingMessagesController.add(chatMessage); + _incomingMessagesController.add(msgModel); } }).catchError((onError) { if (onError is api.ResponseException) { @@ -275,7 +267,7 @@ class MessagesRepository { forwardedMessageId: forwardedMessageId, from: currentUser?.id, id: const Uuid().v1(), - rawStatus: ChatMessageStatus.none.name, + rawStatus: MessageModelStatus.none.name, t: DateTime.now().millisecondsSinceEpoch ~/ 1000, createdAt: DateTime.now()); @@ -285,9 +277,9 @@ class MessagesRepository { var msgModel = message.toMessageModel(true, currentUser!).copyWith( id: serverMid, replyMessage: replyMessage, - rawStatus: ChatMessageStatus.sent.name); + status: MessageModelStatus.sent); var msgUpdated = await saveMessageLocal(msgModel); - _incomingMessagesController.add(msgUpdated.toChatMessage(true, true)); + _incomingMessagesController.add(msgUpdated); }, ); } @@ -325,12 +317,12 @@ class MessagesRepository { id: const Uuid().v1(), t: DateTime.now().millisecondsSinceEpoch ~/ 1000, createdAt: DateTime.now(), - rawStatus: ChatMessageStatus.draft.name) + status: MessageModelStatus.draft) ..sender = currentUser ..replyMessage = replyMessage; var msg = await saveMessageLocal(message); - _incomingMessagesController.add(msg.toChatMessage(true, true)); + _incomingMessagesController.add(msg); } Future updateMessageLocal(MessageModel message) async { @@ -363,9 +355,8 @@ class MessagesRepository { message.toMessageModel(currentUser?.id == message.from, sender); msgModel = await saveMessageLocal(msgModel); - var chatMessage = msgModel.toChatMessage(true, true); - _incomingMessagesController.add(chatMessage); + _incomingMessagesController.add(msgModel); }); sentMessageSubscription = api @@ -429,7 +420,7 @@ class MessagesRepository { repliedMessageId: replyMessage?.id, from: currentUser?.id, id: const Uuid().v1(), - rawStatus: ChatMessageStatus.none.name, + rawStatus: MessageModelStatus.none.name, t: DateTime.now().millisecondsSinceEpoch ~/ 1000, createdAt: DateTime.now()); @@ -438,7 +429,7 @@ class MessagesRepository { var msgModel = message .toMessageModel(true, currentUser!) .copyWith(replyMessage: replyMessage); - _incomingMessagesController.add(msgModel.toChatMessage(true, true)); + _incomingMessagesController.add(msgModel); }, ); } @@ -466,37 +457,6 @@ class MessagesRepository { } return result; } - - Future> buildChatMessageModels( - List messages) async { - var result = []; - - final nextMsg = messages.isNotEmpty - ? (await localDatasource.getAllMessagesLocal(messages[0].cid, - gtDate: messages[0].createdAt, limit: 1)) - .firstOrNull - : null; - - for (int i = 0; i < messages.length; i++) { - var message = messages[i]; - var chatMessage = message.toChatMessage( - i == 0 - ? nextMsg?.from != messages[i].from - : isServiceMessage(messages[i - 1]) || - messages[i - 1].from != messages[i].from, - i == messages.length - 1 || - isServiceMessage(messages[i + 1]) || - messages[i + 1].from != messages[i].from); - - if (i == messages.length - 1 && limitMessages == messages.length) { - // do not put last message in result to determine if it's last for user with next pagination - continue; - } - - result.add(chatMessage); - } - return result; - } } bool isServiceMessage(MessageModel message) { diff --git a/sama_chat_client/lib/src/shared/messages_collector/messages_collector.dart b/sama_chat_client/lib/src/shared/messages_collector/messages_collector.dart index 4aeec805..2ae7dba4 100644 --- a/sama_chat_client/lib/src/shared/messages_collector/messages_collector.dart +++ b/sama_chat_client/lib/src/shared/messages_collector/messages_collector.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:sama_sdk/api/api.dart'; import 'package:sama_sdk/api/connection/connection.dart' as conn; +import '../../db/models/message_model.dart'; import '../../repository/conversation/conversation_repository.dart'; import '../../repository/messages/messages_repository.dart'; @@ -42,7 +43,8 @@ class MessagesCollector { } void _collectMessagesPending() async { - var messages = await messagesRepository.getMessagesLocalByStatus('pending'); + var messages = await messagesRepository + .getMessagesLocalByStatus(MessageModelStatus.pending); for (var message in messages) { await messagesRepository.resendTextMessage(message); } @@ -51,11 +53,11 @@ class MessagesCollector { void _onSentStatusReceived(SentMessageStatus status) async { var msg = await messagesRepository.getMessageLocalById(status.messageId); if (msg != null) { - var msgUpdated = - msg.copyWith(id: status.serverMessageId, rawStatus: 'sent'); + var msgUpdated = msg.copyWith( + id: status.serverMessageId, status: MessageModelStatus.sent); var msgLocal = await messagesRepository.updateMessageLocal(msgUpdated); var conversation = - await conversationRepository.getConversationById(msgLocal.cid!); + await conversationRepository.getConversationById(msgLocal.cid); conversationRepository.updateConversationLocal(conversation! .copyWith(lastMessage: msgLocal, updatedAt: msgLocal.createdAt)); } From 1918b24f6db5fa5d3e2491fe560a640e90a2dd44 Mon Sep 17 00:00:00 2001 From: Magellan Date: Thu, 5 Mar 2026 21:34:05 +0200 Subject: [PATCH 4/9] update BubbleType --- .../conversation/bloc/conversation_bloc.dart | 27 +++++++----- .../conversation/models/chat_message.dart | 43 ++++++++++++++++++- .../conversation/view/messages_list.dart | 42 +----------------- .../widgets/media_attachment.dart | 1 + .../conversation/widgets/message_bubble.dart | 3 +- .../widgets/text_message_item.dart | 4 +- 6 files changed, 63 insertions(+), 57 deletions(-) diff --git a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart index 94e2a82b..5c86c76d 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart @@ -403,17 +403,18 @@ class ConversationBloc extends Bloc { if (event.message.extension?['modified'] ?? false) { var indexMsg = messages.indexWhere((m) => m.id == event.message.id); var msg = messages[indexMsg]; - messages[indexMsg] = event.message - .toChatMessage(msg.isLastUserMessage, msg.isFirstUserMessage); + messages[indexMsg] = event.message.toChatMessage( + msg.isLastUserMessage, msg.isFirstUserMessage, msg.bubbleType); } else { if (messages.isNotEmpty) { messages.first = messages.first.copyWith( - isLastUserMessage: isServiceMessage(messages.first) || - event.message.from != messages.first.from, - isFirstUserMessage: messages.length == 1 || - isServiceMessage(messages[1]) || - messages[1].from != messages.first.from, - ); + isLastUserMessage: isServiceMessage(messages.first) || + event.message.from != messages.first.from, + isFirstUserMessage: messages.length == 1 || + isServiceMessage(messages[1]) || + messages[1].from != messages.first.from, + bubbleType: + bubbleType(List.of(messages)..insert(0, event.message), 1)); } messages.insert( @@ -422,7 +423,8 @@ class ConversationBloc extends Bloc { true, messages.isEmpty || isServiceMessage(messages.first) || - event.message.from != messages.first.from)); + event.message.from != messages.first.from, + bubbleType(List.of(messages)..insert(0, event.message), 0))); } emit( @@ -501,7 +503,9 @@ class ConversationBloc extends Bloc { var msgLocal = await messagesRepository.updateMessageLocal(msgUpdated); messages[messages.indexOf(msg)] = msgLocal.toChatMessage( - msgUpdated.isLastUserMessage, msgUpdated.isFirstUserMessage); + msgUpdated.isLastUserMessage, + msgUpdated.isFirstUserMessage, + msgUpdated.bubbleType); emit(state.copyWith(messages: messages)); var chatLocal = await conversationRepository @@ -559,7 +563,8 @@ class ConversationBloc extends Bloc { messages[i - 1].from != messages[i].from, i == messages.length - 1 || isServiceMessage(messages[i + 1]) || - messages[i + 1].from != messages[i].from); + messages[i + 1].from != messages[i].from, + bubbleType(messages, i)); if (i == messages.length - 1 && limitMessages == messages.length) { // do not put last message in result to determine if it's last for user with next pagination diff --git a/sama_chat_client/lib/src/features/conversation/models/chat_message.dart b/sama_chat_client/lib/src/features/conversation/models/chat_message.dart index 22f5241a..a09a3272 100644 --- a/sama_chat_client/lib/src/features/conversation/models/chat_message.dart +++ b/sama_chat_client/lib/src/features/conversation/models/chat_message.dart @@ -1,13 +1,18 @@ import '../../../db/models/models.dart'; +import '../../../shared/utils/list_utils.dart'; + +enum BubbleType { common, upper, middle, lower } // ignore: must_be_immutable class ChatMessage extends MessageModel { final bool isFirstUserMessage; final bool isLastUserMessage; + final BubbleType bubbleType; ChatMessage({ required this.isFirstUserMessage, required this.isLastUserMessage, + required this.bubbleType, required super.isOwn, required super.id, required super.from, @@ -28,6 +33,7 @@ class ChatMessage extends MessageModel { ChatMessage copyWith({ bool? isFirstUserMessage, bool? isLastUserMessage, + BubbleType? bubbleType, int? bid, String? id, String? from, @@ -49,6 +55,7 @@ class ChatMessage extends MessageModel { return ChatMessage( isFirstUserMessage: isFirstUserMessage ?? this.isFirstUserMessage, isLastUserMessage: isLastUserMessage ?? this.isLastUserMessage, + bubbleType: bubbleType ?? this.bubbleType, bid: bid ?? this.bid, id: id ?? this.id, from: from ?? this.from, @@ -84,11 +91,13 @@ class ChatMessage extends MessageModel { } extension ChatMessageExtension on MessageModel { - ChatMessage toChatMessage(bool isLastUserMessage, bool isFirstUserMessage) { + ChatMessage toChatMessage( + bool isLastUserMessage, bool isFirstUserMessage, BubbleType bubbleType) { return ChatMessage( bid: bid, isLastUserMessage: isLastUserMessage, isFirstUserMessage: isFirstUserMessage, + bubbleType: bubbleType, id: id, from: from, cid: cid, @@ -114,3 +123,35 @@ extension ChatMessageExtension on MessageModel { return attachments.isNotEmpty; } } + +bool sameMsgGroup(MessageModel msg, MessageModel? other) { + int diffTime = 30; + + bool isSameOwner = (other?.isOwn ?? false) && msg.isOwn || + (!(other?.isOwn ?? false)) && !msg.isOwn; + + var otherMs = (other?.createdAt?.millisecondsSinceEpoch ?? 0) ~/ 1000; + var msgMs = (msg.createdAt?.millisecondsSinceEpoch ?? 0) ~/ 1000; + var timeGap = (msgMs - otherMs).abs(); + + return isSameOwner && timeGap < diffTime; +} + +BubbleType bubbleType(List messages, int index) { + MessageModel currentMsg = messages[index]; + MessageModel? prevMsg = messages.tryGet(index + 1); + MessageModel? nextMsg = messages.tryGet(index - 1); + + var prevSame = sameMsgGroup(currentMsg, prevMsg); + var nextSame = sameMsgGroup(currentMsg, nextMsg); + + BubbleType bubbleType = prevSame && nextSame + ? BubbleType.middle + : prevSame + ? BubbleType.upper + : nextSame + ? BubbleType.lower + : BubbleType.common; + + return bubbleType; +} diff --git a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart index 95966683..42b42d7c 100644 --- a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart +++ b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart @@ -143,7 +143,6 @@ class _MessagesListState extends State { ), child: MessageItem( message: msg, - bubbleType: bubbleType(state.messages, index), onTapReply: () { var replyIndex = state.messages.indexWhere( (item) => @@ -184,38 +183,6 @@ class _MessagesListState extends State { ])); } - BubbleType bubbleType(List messages, int index) { - ChatMessage currentMsg = messages[index]; - ChatMessage? prevMsg = messages.tryGet(index + 1); - ChatMessage? nextMsg = messages.tryGet(index - 1); - - var prevSame = sameMsgGroup(currentMsg, prevMsg); - var nextSame = sameMsgGroup(currentMsg, nextMsg); - - BubbleType bubbleType = prevSame && nextSame - ? BubbleType.middle - : prevSame - ? BubbleType.upper - : nextSame - ? BubbleType.lower - : BubbleType.common; - - return bubbleType; - } - - bool sameMsgGroup(ChatMessage msg, ChatMessage? other) { - int diffTime = 30; - - bool isSameOwner = (other?.isOwn ?? false) && msg.isOwn || - (!(other?.isOwn ?? false)) && !msg.isOwn; - - var otherMs = (other?.createdAt?.millisecondsSinceEpoch ?? 0) ~/ 1000; - var msgMs = (msg.createdAt?.millisecondsSinceEpoch ?? 0) ~/ 1000; - var timeGap = (msgMs - otherMs).abs(); - - return isSameOwner && timeGap < diffTime; - } - double separateSpace(List messages, int index) { ChatMessage currentMsg = messages[index]; ChatMessage? prevMsg = messages.tryGet(index + 1); @@ -310,14 +277,9 @@ class MessageItem extends StatelessWidget { final ChatMessage message; final VoidCallback? onTapReply; final VoidCallback? onTapForward; - final BubbleType bubbleType; const MessageItem( - {required this.message, - required this.bubbleType, - this.onTapReply, - this.onTapForward, - super.key}); + {required this.message, this.onTapReply, this.onTapForward, super.key}); @override Widget build(BuildContext context) { @@ -567,6 +529,6 @@ class MessageItem extends StatelessWidget { ); } - return TextMessageItem(message: message, bubbleType: bubbleType); + return TextMessageItem(message: message); } } diff --git a/sama_chat_client/lib/src/features/conversation/widgets/media_attachment.dart b/sama_chat_client/lib/src/features/conversation/widgets/media_attachment.dart index e43086ee..03da5238 100644 --- a/sama_chat_client/lib/src/features/conversation/widgets/media_attachment.dart +++ b/sama_chat_client/lib/src/features/conversation/widgets/media_attachment.dart @@ -35,6 +35,7 @@ class MediaAttachment extends StatelessWidget { isFirst: message.isFirstUserMessage, isLast: message.isLastUserMessage, isOwn: message.isOwn, + bubbleType: message.bubbleType, child: BlocBuilder( builder: (context, state) { if (message.attachments.first.url == null) { diff --git a/sama_chat_client/lib/src/features/conversation/widgets/message_bubble.dart b/sama_chat_client/lib/src/features/conversation/widgets/message_bubble.dart index 4ea72098..7da9fe69 100644 --- a/sama_chat_client/lib/src/features/conversation/widgets/message_bubble.dart +++ b/sama_chat_client/lib/src/features/conversation/widgets/message_bubble.dart @@ -6,8 +6,7 @@ import '../../../navigation/constants.dart'; import '../../../shared/ui/colors.dart'; import '../../../shared/utils/string_utils.dart'; import '../../conversations_list/widgets/avatar_letter_icon.dart'; - -enum BubbleType { common, upper, middle, lower } +import '../models/chat_message.dart'; class MessageBubble extends StatelessWidget { final UserModel sender; diff --git a/sama_chat_client/lib/src/features/conversation/widgets/text_message_item.dart b/sama_chat_client/lib/src/features/conversation/widgets/text_message_item.dart index c9d6eaf9..e955b587 100644 --- a/sama_chat_client/lib/src/features/conversation/widgets/text_message_item.dart +++ b/sama_chat_client/lib/src/features/conversation/widgets/text_message_item.dart @@ -10,12 +10,10 @@ import 'text_message.dart'; class TextMessageItem extends StatelessWidget { final ChatMessage message; - final BubbleType bubbleType; const TextMessageItem({ super.key, required this.message, - required this.bubbleType, }); @override @@ -25,7 +23,7 @@ class TextMessageItem extends StatelessWidget { isFirst: message.isFirstUserMessage, isLast: message.isLastUserMessage, isOwn: message.isOwn, - bubbleType: bubbleType, + bubbleType: message.bubbleType, child: TextMessage( body: message.body ?? '', style: TextStyle(color: message.isOwn ? white : black, fontSize: 16.0), From 555973e26714fd24c8f85ed9cfb6d221a9c58113 Mon Sep 17 00:00:00 2001 From: Magellan Date: Fri, 6 Mar 2026 21:00:31 +0200 Subject: [PATCH 5/9] update buildChatMessageModels update MessagesList buildWhen --- .../conversation/bloc/conversation_bloc.dart | 23 +++++++++------- .../conversation/models/chat_message.dart | 26 +++++++++++++------ .../conversation/view/messages_list.dart | 9 ++++--- .../messages/messages_repository.dart | 4 --- 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart index 5c86c76d..3f62d513 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart @@ -464,8 +464,8 @@ class ConversationBloc extends Bloc { var prevMsgUpdated = prevMsg?.copyWith( isLastUserMessage: prevIndex == 0 || - isServiceMessage(messages[prevIndex - 2]) || - messages[prevIndex - 2].from != messages[prevIndex].from, + isServiceMessage(messages.tryGet(prevIndex - 2)) || + messages.tryGet(prevIndex - 2)?.from != messages[prevIndex].from, isFirstUserMessage: prevIndex == messages.length - 1 || isServiceMessage(messages[prevIndex + 2]) || messages[prevIndex + 2].from != messages[prevIndex].from); @@ -548,26 +548,29 @@ class ConversationBloc extends Bloc { List messages) async { var result = []; - final nextMsg = messages.isNotEmpty - ? (await messagesRepository.getStoredMessages(messages[0].cid, - gtDate: messages[0].createdAt, limit: 1)) - .firstOrNull - : null; + var shouldUpdate = state.messages.isNotEmpty && + state.messages.lastOrNull?.id != + messages.tryGet(messages.length - 2)?.id; + + var lastPrevMsg = shouldUpdate ? state.messages.last : null; for (int i = 0; i < messages.length; i++) { var message = messages[i]; var chatMessage = message.toChatMessage( i == 0 - ? nextMsg?.from != messages[i].from + ? lastPrevMsg?.from != messages[i].from : isServiceMessage(messages[i - 1]) || messages[i - 1].from != messages[i].from, i == messages.length - 1 || isServiceMessage(messages[i + 1]) || messages[i + 1].from != messages[i].from, - bubbleType(messages, i)); + i == 0 && shouldUpdate + ? bubbleType( + List.of(messages)..insert(0, state.messages.last), i + 1) + : bubbleType(messages, i)); if (i == messages.length - 1 && limitMessages == messages.length) { - // do not put last message in result to determine if it's last for user with next pagination +// do not put last message in result to determine bubbleType and if it's last for user with next pagination continue; } diff --git a/sama_chat_client/lib/src/features/conversation/models/chat_message.dart b/sama_chat_client/lib/src/features/conversation/models/chat_message.dart index a09a3272..0220b0cc 100644 --- a/sama_chat_client/lib/src/features/conversation/models/chat_message.dart +++ b/sama_chat_client/lib/src/features/conversation/models/chat_message.dart @@ -125,7 +125,7 @@ extension ChatMessageExtension on MessageModel { } bool sameMsgGroup(MessageModel msg, MessageModel? other) { - int diffTime = 30; + int diffTime = 45; bool isSameOwner = (other?.isOwn ?? false) && msg.isOwn || (!(other?.isOwn ?? false)) && !msg.isOwn; @@ -142,16 +142,26 @@ BubbleType bubbleType(List messages, int index) { MessageModel? prevMsg = messages.tryGet(index + 1); MessageModel? nextMsg = messages.tryGet(index - 1); + bool isSimpleCurrent = currentMsg.forwardedMessageId == null && + currentMsg.repliedMessageId == null; + bool isSimpleNext = + nextMsg?.forwardedMessageId == null && nextMsg?.repliedMessageId == null; + var prevSame = sameMsgGroup(currentMsg, prevMsg); var nextSame = sameMsgGroup(currentMsg, nextMsg); - BubbleType bubbleType = prevSame && nextSame - ? BubbleType.middle - : prevSame - ? BubbleType.upper - : nextSame - ? BubbleType.lower - : BubbleType.common; + BubbleType bubbleType = + prevSame && nextSame && isSimpleCurrent && isSimpleNext + ? BubbleType.middle + : prevSame && isSimpleCurrent + ? BubbleType.upper + : nextSame && isSimpleNext + ? BubbleType.lower + : BubbleType.common; return bubbleType; } + +bool isServiceMessage(MessageModel? message) { + return message?.extension != null && message?.extension?['type'] != null; +} diff --git a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart index 42b42d7c..cc8e35f2 100644 --- a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart +++ b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart @@ -17,7 +17,6 @@ import '../widgets/focused_popup_menu.dart'; import '../widgets/forward_messages/forward_bubble.dart'; import '../widgets/forward_messages/forward_messages_widget.dart'; import '../widgets/media_attachment.dart'; -import '../widgets/message_bubble.dart'; import '../widgets/reply_bubble.dart'; import '../widgets/service_message_bubble.dart'; import '../widgets/text_message_item.dart'; @@ -71,6 +70,8 @@ class _MessagesListState extends State { ], child: Stack(children: [ BlocBuilder( + buildWhen: (previous, current) => + current.messages != previous.messages, builder: (context, state) { switch (state.status) { case ConversationStatus.failure: @@ -183,9 +184,9 @@ class _MessagesListState extends State { ])); } - double separateSpace(List messages, int index) { - ChatMessage currentMsg = messages[index]; - ChatMessage? prevMsg = messages.tryGet(index + 1); + double separateSpace(List messages, int index) { + MessageModel currentMsg = messages[index]; + MessageModel? prevMsg = messages.tryGet(index + 1); return sameMsgGroup(currentMsg, prevMsg) ? 2 : 15; } diff --git a/sama_chat_client/lib/src/repository/messages/messages_repository.dart b/sama_chat_client/lib/src/repository/messages/messages_repository.dart index 2fc22ea3..564d141a 100644 --- a/sama_chat_client/lib/src/repository/messages/messages_repository.dart +++ b/sama_chat_client/lib/src/repository/messages/messages_repository.dart @@ -458,7 +458,3 @@ class MessagesRepository { return result; } } - -bool isServiceMessage(MessageModel message) { - return message.extension != null && message.extension?['type'] != null; -} From ef24a9bdc7599240cf683849b6938215f2277ca0 Mon Sep 17 00:00:00 2001 From: Magellan Date: Mon, 16 Mar 2026 00:58:04 +0200 Subject: [PATCH 6/9] update message list bloc update send message bloc scrolling --- .../conversation/bloc/conversation_bloc.dart | 2 +- .../conversation/bloc/conversation_state.dart | 3 +- .../bloc/send_message/send_message_bloc.dart | 27 +-- .../bloc/send_message/send_message_event.dart | 6 - .../bloc/send_message/send_message_state.dart | 5 + .../conversation/view/messages_list.dart | 160 ++++++++++-------- 6 files changed, 102 insertions(+), 101 deletions(-) diff --git a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart index 3f62d513..31203dc2 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart @@ -538,7 +538,7 @@ class ConversationBloc extends Bloc { var msg = messages.firstWhere((o) => o.id == event.status.messageId); //TODO RP or set failed status - // var msgUpdated = msg.copyWith(status: ChatMessageStatus.failed); + // var msgUpdated = msg.copyWith(status: MessageModelStatus.failed); // messages[messages.indexOf(msg)] = msgUpdated; messages.remove(msg); emit(state.copyWith(messages: messages)); diff --git a/sama_chat_client/lib/src/features/conversation/bloc/conversation_state.dart b/sama_chat_client/lib/src/features/conversation/bloc/conversation_state.dart index ea28e1e1..1c614593 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/conversation_state.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/conversation_state.dart @@ -55,7 +55,7 @@ final class ConversationState extends Equatable { hasReachedMax: hasReachedMax ?? this.hasReachedMax, initial: initial ?? this.initial, choose: choose ?? this.choose, - typingStatus: typingStatus, + typingStatus: typingStatus ?? this.typingStatus, replyIdToScroll: replyIdToScroll ?? this.replyIdToScroll, ); } @@ -75,7 +75,6 @@ final class ConversationState extends Equatable { initial, choose, replyIdToScroll, - replyIdToScroll, participants, typingStatus ]; diff --git a/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_bloc.dart b/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_bloc.dart index 66ef5fec..b4caf971 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_bloc.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_bloc.dart @@ -44,9 +44,6 @@ class SendMessageBloc extends Bloc { on( _onSendTextMessage, ); - on( - _onEditTextMessage, - ); on<_DraftMessageReceived>( _onDraftMessageReceived, ); @@ -77,32 +74,22 @@ class SendMessageBloc extends Bloc { Future _onSendTextMessage( SendTextMessage event, Emitter emit) async { try { + bool scroll; emit(state.copyWith( isTextEmpty: true, status: SendMessageStatus.processing)); if (state.editMessage != null) { + scroll = false; await messagesRepository.editMessage(event.message, state.editMessage); } else { + scroll = true; await messagesRepository.sendTextMessage( event.message, currentConversation.id, state.replyMessage); } emit(state.copyWith( - isTextEmpty: true, text: '', status: SendMessageStatus.success)); - } on ResponseException catch (ex) { - emit(state.copyWith( - errorMessage: ex.message, status: SendMessageStatus.failure)); - } - } - - Future _onEditTextMessage( - EditTextMessage event, Emitter emit) async { - try { - emit(state.copyWith( - isTextEmpty: true, status: SendMessageStatus.processing)); - var replyMessage = state.replyMessage; - await messagesRepository.sendTextMessage( - event.message, currentConversation.id, replyMessage); - emit(state.copyWith( - isTextEmpty: true, text: '', status: SendMessageStatus.success)); + isTextEmpty: true, + text: '', + scroll: scroll, + status: SendMessageStatus.success)); } on ResponseException catch (ex) { emit(state.copyWith( errorMessage: ex.message, status: SendMessageStatus.failure)); diff --git a/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_event.dart b/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_event.dart index f8b4059a..059294c2 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_event.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_event.dart @@ -13,12 +13,6 @@ final class SendTextMessage extends SendMessageEvent { const SendTextMessage(this.message); } -final class EditTextMessage extends SendMessageEvent { - final String message; - - const EditTextMessage(this.message); -} - final class TextMessageChanged extends SendMessageEvent { final String text; diff --git a/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_state.dart b/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_state.dart index 454d25c7..03e4d035 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_state.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/send_message/send_message_state.dart @@ -7,6 +7,7 @@ final class SendMessageState extends Equatable { this.status = SendMessageStatus.initial, this.isTextEmpty = true, this.text = '', + this.scroll = false, this.errorMessage, this.draftMessage, this.replyMessage, @@ -16,6 +17,7 @@ final class SendMessageState extends Equatable { final SendMessageStatus status; final bool isTextEmpty; final String text; + final bool scroll; final String? errorMessage; final MessageModel? draftMessage; final MessageModel? replyMessage; @@ -25,6 +27,7 @@ final class SendMessageState extends Equatable { SendMessageStatus? status, bool? isTextEmpty, String? text, + bool? scroll, String? errorMessage, MessageModel? Function()? draftMessage, MessageModel? Function()? replyMessage, @@ -34,6 +37,7 @@ final class SendMessageState extends Equatable { status: status ?? this.status, isTextEmpty: isTextEmpty ?? this.isTextEmpty, text: text ?? this.text, + scroll: scroll ?? this.scroll, errorMessage: errorMessage ?? this.errorMessage, draftMessage: draftMessage != null ? draftMessage() : this.draftMessage, replyMessage: replyMessage != null ? replyMessage() : this.replyMessage, @@ -51,6 +55,7 @@ final class SendMessageState extends Equatable { status, isTextEmpty, text, + scroll, errorMessage, draftMessage, replyMessage, diff --git a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart index cc8e35f2..aeeb8b2f 100644 --- a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart +++ b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart @@ -40,11 +40,18 @@ class _MessagesListState extends State { listeners: [ BlocListener( listener: (context, sendState) { - if (sendState.status == SendMessageStatus.success) { + if (sendState.status == SendMessageStatus.success && + sendState.scroll) { scrollTo(0); } }, ), + BlocListener( + listener: (context, state) { + scrollToReplyIfNeed(state); + markAsReadIfNeed(); + }, + ), BlocListener( listener: (context, state) { switch (state.status) { @@ -69,11 +76,11 @@ class _MessagesListState extends State { }) ], child: Stack(children: [ - BlocBuilder( - buildWhen: (previous, current) => - current.messages != previous.messages, - builder: (context, state) { - switch (state.status) { + BlocSelector( + selector: (state) => state.status, + builder: (context, status) { + var state = context.read().state; + switch (status) { case ConversationStatus.failure: WidgetsBinding.instance .addPostFrameCallback((_) => ScaffoldMessenger.of(context) @@ -103,73 +110,82 @@ class _MessagesListState extends State { ), ); } - markAsReadIfNeed(); - scrollToReplyIfNeed(state); - return NotificationListener( - onNotification: (notification) { - if (notification is ScrollUpdateNotification && - notification.dragDetails != null) { - final keyboardTop = screenHeight - keyboardHeight(); - var shouldClose = keyboardTop < - notification.dragDetails!.globalPosition.dy; - if (notification.scrollDelta! > 0 && shouldClose) { - hideKeyboard(); - } - } else if (notification is ScrollEndNotification) { - _onScroll(notification.metrics.pixels, - notification.metrics.maxScrollExtent); - } - return false; - }, - child: ScrollablePositionedList.separated( - reverse: true, - itemBuilder: (BuildContext context, int index) { - var msg = state.messages[index]; - return SwipeTo( - key: Key(msg.id.toString()), - stickToRight: msg.isOwn, - direction: msg.isOwn - ? DismissDirection.endToStart - : DismissDirection.startToEnd, - onSwipe: () { - print('onSwipe'); - context - .read() - .add(AddReplyMessage(msg)); + return BlocSelector>( + selector: (state) => state.messages, + builder: (context, messages) { + return NotificationListener( + onNotification: (notification) { + if (notification is ScrollUpdateNotification && + notification.dragDetails != null) { + final keyboardTop = + screenHeight - keyboardHeight(); + var shouldClose = keyboardTop < + notification.dragDetails!.globalPosition.dy; + if (notification.scrollDelta! > 0 && + shouldClose) { + hideKeyboard(); + } + } else if (notification + is ScrollEndNotification) { + _onScroll(notification.metrics.pixels, + notification.metrics.maxScrollExtent); + } + return false; }, - actionIcon: const Icon( - Icons.reply_rounded, - color: Colors.black, - size: 25, - ), - child: MessageItem( - message: msg, - onTapReply: () { - var replyIndex = state.messages.indexWhere( - (item) => - item.id == msg.repliedMessageId); - if (replyIndex == -1) { - if (!state.hasReachedMax) { - context.read().add( - MessagesMoreForReply( - msg.repliedMessageId!)); - showProgress(); - } - return; - } - scrollTo(replyIndex); - }, - onTapForward: () => print('onTapForward')), - ); - }, - itemCount: state.messages.length, - itemScrollController: _scrollController, - itemPositionsListener: itemPositionsListener, - padding: EdgeInsets.zero, - separatorBuilder: (context, index) => SizedBox( - height: separateSpace(state.messages, index), - ), - )); + child: ScrollablePositionedList.separated( + reverse: true, + itemBuilder: (BuildContext context, int index) { + var msg = messages[index]; + return SwipeTo( + key: Key(msg.id.toString()), + stickToRight: msg.isOwn, + direction: msg.isOwn + ? DismissDirection.endToStart + : DismissDirection.startToEnd, + onSwipe: () { + print('onSwipe'); + context + .read() + .add(AddReplyMessage(msg)); + }, + actionIcon: const Icon( + Icons.reply_rounded, + color: Colors.black, + size: 25, + ), + child: MessageItem( + message: msg, + onTapReply: () { + var replyIndex = messages.indexWhere( + (item) => + item.id == + msg.repliedMessageId); + if (replyIndex == -1) { + if (!state.hasReachedMax) { + context + .read() + .add(MessagesMoreForReply( + msg.repliedMessageId!)); + showProgress(); + } + return; + } + scrollTo(replyIndex); + }, + onTapForward: () => + print('onTapForward')), + ); + }, + itemCount: messages.length, + itemScrollController: _scrollController, + itemPositionsListener: itemPositionsListener, + padding: EdgeInsets.zero, + separatorBuilder: (context, index) => SizedBox( + height: separateSpace(messages, index), + ), + )); + }); case ConversationStatus.initial: return const Center(child: CircularProgressIndicator()); case ConversationStatus.delete: From 5449379f653996832e385abc34d5e5f3fc3db020 Mon Sep 17 00:00:00 2001 From: Magellan Date: Wed, 18 Mar 2026 00:34:07 +0200 Subject: [PATCH 7/9] fix drop-down menu out of the screen fix buildChatMessageModels remove reply icon on reply remove edit icon on edit update chat menu --- .../conversation/bloc/conversation_bloc.dart | 8 ++++++-- .../conversation/view/conversation_page.dart | 11 +++++++++-- .../conversation/view/message_input.dart | 4 +--- .../conversation/view/messages_list.dart | 16 ++++++++++------ .../conversation/widgets/focused_popup_menu.dart | 8 +++++--- .../conversation/widgets/header_input_box.dart | 6 +++--- .../src/features/profile/view/profile_form.dart | 3 +-- .../lib/src/shared/utils/screen_factor.dart | 2 ++ 8 files changed, 37 insertions(+), 21 deletions(-) diff --git a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart index 31203dc2..85ef5d6e 100644 --- a/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart +++ b/sama_chat_client/lib/src/features/conversation/bloc/conversation_bloc.dart @@ -548,9 +548,13 @@ class ConversationBloc extends Bloc { List messages) async { var result = []; + var lastCurrentMsg = limitMessages == messages.length + ? messages.tryGet(messages.length - 2) + : messages.tryGet(messages.length - 1); + var shouldUpdate = state.messages.isNotEmpty && - state.messages.lastOrNull?.id != - messages.tryGet(messages.length - 2)?.id; + lastCurrentMsg != null && + lastCurrentMsg.id != state.messages.lastOrNull?.id; var lastPrevMsg = shouldUpdate ? state.messages.last : null; diff --git a/sama_chat_client/lib/src/features/conversation/view/conversation_page.dart b/sama_chat_client/lib/src/features/conversation/view/conversation_page.dart index 77b57da4..f7d26066 100644 --- a/sama_chat_client/lib/src/features/conversation/view/conversation_page.dart +++ b/sama_chat_client/lib/src/features/conversation/view/conversation_page.dart @@ -94,6 +94,8 @@ class ConversationPage extends StatelessWidget { toolbarHeight: 64, centerTitle: false, titleSpacing: 0.0, + backgroundColor: smokyBorough, + surfaceTintColor: Colors.transparent, title: BlocBuilder( builder: (BuildContext context, aiState) { return aiState.status == AiMessageStatus.processing @@ -222,6 +224,11 @@ class _PopupMenuButton extends StatelessWidget { Widget build(BuildContext context) { return PopupMenuButton<_Menu>( position: PopupMenuPosition.under, + color: lightMallow, + elevation: 5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), // Rounded corners + ), onSelected: (_Menu item) { switch (item) { case _Menu.info: @@ -273,7 +280,7 @@ class _PopupMenuButton extends StatelessWidget { padding: EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.0), value: _Menu.info, child: ListTile( - leading: Icon(Icons.visibility_outlined), + leading: Icon(Icons.visibility_outlined, size: 25), title: Text('Info'), ), ), @@ -281,7 +288,7 @@ class _PopupMenuButton extends StatelessWidget { padding: EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.0), value: _Menu.deleteAndLeave, child: ListTile( - leading: Icon(Icons.exit_to_app_outlined), + leading: Icon(Icons.exit_to_app_outlined, size: 25), title: Text('Delete and leave'), )) ]; diff --git a/sama_chat_client/lib/src/features/conversation/view/message_input.dart b/sama_chat_client/lib/src/features/conversation/view/message_input.dart index 3b802828..ebcf01b9 100644 --- a/sama_chat_client/lib/src/features/conversation/view/message_input.dart +++ b/sama_chat_client/lib/src/features/conversation/view/message_input.dart @@ -115,8 +115,7 @@ class _MessageInputState extends State { onTap: () { BlocProvider.of(context) .add(const RemoveReplyMessage()); - }, - icon: const Icon(Icons.replay_outlined)), + }), if (showEdit) HeaderInputBox( message: state.editMessage!, @@ -126,7 +125,6 @@ class _MessageInputState extends State { BlocProvider.of(context) .add(const RemoveEditMessage()); }, - icon: const Icon(Icons.edit_outlined), ), Container( constraints: const BoxConstraints(maxHeight: 120.0), diff --git a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart index aeeb8b2f..993deab7 100644 --- a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart +++ b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart @@ -140,9 +140,11 @@ class _MessagesListState extends State { return SwipeTo( key: Key(msg.id.toString()), stickToRight: msg.isOwn, - direction: msg.isOwn - ? DismissDirection.endToStart - : DismissDirection.startToEnd, + direction: msg.isServiceMessage() + ? DismissDirection.none + : msg.isOwn + ? DismissDirection.endToStart + : DismissDirection.startToEnd, onSwipe: () { print('onSwipe'); context @@ -151,7 +153,7 @@ class _MessagesListState extends State { }, actionIcon: const Icon( Icons.reply_rounded, - color: Colors.black, + color: black, size: 25, ), child: MessageItem( @@ -180,7 +182,7 @@ class _MessagesListState extends State { itemCount: messages.length, itemScrollController: _scrollController, itemPositionsListener: itemPositionsListener, - padding: EdgeInsets.zero, + padding: const EdgeInsets.only(top: 5), separatorBuilder: (context, index) => SizedBox( height: separateSpace(messages, index), ), @@ -203,7 +205,9 @@ class _MessagesListState extends State { double separateSpace(List messages, int index) { MessageModel currentMsg = messages[index]; MessageModel? prevMsg = messages.tryGet(index + 1); - return sameMsgGroup(currentMsg, prevMsg) ? 2 : 15; + return sameMsgGroup(currentMsg, prevMsg) && !currentMsg.isServiceMessage() + ? 2 + : 10; } Widget get scrollFAB => ValueListenableBuilder>( diff --git a/sama_chat_client/lib/src/features/conversation/widgets/focused_popup_menu.dart b/sama_chat_client/lib/src/features/conversation/widgets/focused_popup_menu.dart index 79a9b34a..504bd361 100644 --- a/sama_chat_client/lib/src/features/conversation/widgets/focused_popup_menu.dart +++ b/sama_chat_client/lib/src/features/conversation/widgets/focused_popup_menu.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../../shared/ui/colors.dart'; +import '../../../shared/utils/screen_factor.dart'; class FocusedPopupMenuItem { Widget title; @@ -107,7 +108,8 @@ class FocusedMenuDetails extends StatelessWidget { : childOffset.dx + horizontalMenuPadding + leftMenuPadding; final topOffset = needToMove ? topPaddingHeight - topMenuPadding - : (childOffset.dy + menuHeight + childSize.height) < size.height + : (childOffset.dy + menuHeight + childSize.height) < + size.height - navBarHeight(context) ? childOffset.dy + childSize.height + topMenuPadding : childOffset.dy - menuHeight - topMenuPadding; @@ -143,8 +145,8 @@ class FocusedMenuDetails extends StatelessWidget { width: maxMenuWidth, height: menuHeight, decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(5.0)), + color: lightMallow, + borderRadius: BorderRadius.all(Radius.circular(10.0)), boxShadow: [ BoxShadow( color: Colors.black38, diff --git a/sama_chat_client/lib/src/features/conversation/widgets/header_input_box.dart b/sama_chat_client/lib/src/features/conversation/widgets/header_input_box.dart index 453c49b7..d1073abe 100644 --- a/sama_chat_client/lib/src/features/conversation/widgets/header_input_box.dart +++ b/sama_chat_client/lib/src/features/conversation/widgets/header_input_box.dart @@ -8,14 +8,14 @@ class HeaderInputBox extends StatelessWidget { final MessageModel message; final String title; final VoidCallback? onTap; - final Widget icon; + final Widget? icon; const HeaderInputBox( {super.key, required this.message, required this.title, required this.onTap, - required this.icon}); + this.icon}); @override Widget build(BuildContext context) { @@ -30,7 +30,7 @@ class HeaderInputBox extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - icon, + if (icon != null) icon!, const VerticalLine( rightPadding: 10, leftPadding: 10, diff --git a/sama_chat_client/lib/src/features/profile/view/profile_form.dart b/sama_chat_client/lib/src/features/profile/view/profile_form.dart index 120e4e52..d6f14bf2 100644 --- a/sama_chat_client/lib/src/features/profile/view/profile_form.dart +++ b/sama_chat_client/lib/src/features/profile/view/profile_form.dart @@ -662,7 +662,6 @@ class _ChangePasswordInput extends StatelessWidget { } List _formActions(BuildContext context) { - final bottomPadding = MediaQuery.of(context).viewPadding.bottom; return [ TextButton( onPressed: () { @@ -689,7 +688,7 @@ List _formActions(BuildContext context) { behavior: SnackBarBehavior.floating, margin: EdgeInsets.only( bottom: keyboardHeightCtx(context) - - (Platform.isIOS ? bottomPadding : 0.0))) + (Platform.isIOS ? navBarHeight(context) : 0.0))) : SnackBar( content: Text(content), duration: const Duration(seconds: 2), diff --git a/sama_chat_client/lib/src/shared/utils/screen_factor.dart b/sama_chat_client/lib/src/shared/utils/screen_factor.dart index 9169dadb..6a49c177 100644 --- a/sama_chat_client/lib/src/shared/utils/screen_factor.dart +++ b/sama_chat_client/lib/src/shared/utils/screen_factor.dart @@ -12,6 +12,8 @@ final double screenHeight = WidgetsBinding.instance.platformDispatcher.views.first.physicalSize.height / WidgetsBinding.instance.platformDispatcher.views.first.devicePixelRatio; +double navBarHeight(BuildContext ctx) => MediaQuery.of(ctx).viewPadding.bottom; + double keyboardHeightCtx(BuildContext ctx) => MediaQuery.of(ctx).viewInsets.bottom; From 083b277bbbb04abd3964db13fe07de96350eb3e8 Mon Sep 17 00:00:00 2001 From: Magellan Date: Mon, 23 Mar 2026 00:35:04 +0200 Subject: [PATCH 8/9] fix selectedMessages --- .../conversation/view/message_input.dart | 2 +- .../conversation/view/messages_list.dart | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/sama_chat_client/lib/src/features/conversation/view/message_input.dart b/sama_chat_client/lib/src/features/conversation/view/message_input.dart index ebcf01b9..440831ab 100644 --- a/sama_chat_client/lib/src/features/conversation/view/message_input.dart +++ b/sama_chat_client/lib/src/features/conversation/view/message_input.dart @@ -131,7 +131,7 @@ class _MessageInputState extends State { margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0), decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(12.0)), + borderRadius: BorderRadius.all(Radius.circular(15.0)), color: gainsborough, ), child: Row( diff --git a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart index 993deab7..c62f39e6 100644 --- a/sama_chat_client/lib/src/features/conversation/view/messages_list.dart +++ b/sama_chat_client/lib/src/features/conversation/view/messages_list.dart @@ -110,10 +110,11 @@ class _MessagesListState extends State { ), ); } - return BlocSelector>( - selector: (state) => state.messages, - builder: (context, messages) { + return BlocBuilder( + buildWhen: (previous, current) => + previous.messages != current.messages || + previous.selectedMessages != current.selectedMessages, + builder: (context, state) { return NotificationListener( onNotification: (notification) { if (notification is ScrollUpdateNotification && @@ -136,7 +137,7 @@ class _MessagesListState extends State { child: ScrollablePositionedList.separated( reverse: true, itemBuilder: (BuildContext context, int index) { - var msg = messages[index]; + var msg = state.messages[index]; return SwipeTo( key: Key(msg.id.toString()), stickToRight: msg.isOwn, @@ -159,8 +160,8 @@ class _MessagesListState extends State { child: MessageItem( message: msg, onTapReply: () { - var replyIndex = messages.indexWhere( - (item) => + var replyIndex = state.messages + .indexWhere((item) => item.id == msg.repliedMessageId); if (replyIndex == -1) { @@ -179,12 +180,12 @@ class _MessagesListState extends State { print('onTapForward')), ); }, - itemCount: messages.length, + itemCount: state.messages.length, itemScrollController: _scrollController, itemPositionsListener: itemPositionsListener, padding: const EdgeInsets.only(top: 5), separatorBuilder: (context, index) => SizedBox( - height: separateSpace(messages, index), + height: separateSpace(state.messages, index), ), )); }); From 36694216c54c3d106ef708e94a4ed77b49f0111d Mon Sep 17 00:00:00 2001 From: Magellan Date: Tue, 24 Mar 2026 14:53:12 +0200 Subject: [PATCH 9/9] fix logout button moved below navigation bar --- .../lib/src/features/profile/view/profile_form.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sama_chat_client/lib/src/features/profile/view/profile_form.dart b/sama_chat_client/lib/src/features/profile/view/profile_form.dart index d6f14bf2..0618e648 100644 --- a/sama_chat_client/lib/src/features/profile/view/profile_form.dart +++ b/sama_chat_client/lib/src/features/profile/view/profile_form.dart @@ -64,7 +64,8 @@ class ProfileCard extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( + return SafeArea( + child: Padding( padding: EdgeInsets.only(bottom: Platform.isIOS ? 0.0 : 4.0), child: Card( child: Padding( @@ -78,7 +79,7 @@ class ProfileCard extends StatelessWidget { ), ), ), - ); + )); } }