Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix chats does not sorting by local messages (#701) #765

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions lib/domain/repository/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,6 @@ abstract class RxChat implements Comparable<RxChat> {
/// [MyUser] in this [chat].
Rx<ChatItem>? get firstUnread;

/// Returns the last [ChatItem] of this [RxChat].
ChatItem? get lastItem;

/// Listens to the updates of this [RxChat] while the returned [Stream] is
/// listened to.
Stream<void> get updates;
Expand Down
9 changes: 9 additions & 0 deletions lib/store/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ class ChatRepository extends DisposableInterface
rxChat = await ensureRemoteDialog(chatId);
} catch (_) {
local?.status.value = SendingStatus.error;
return;
}
}

Expand Down Expand Up @@ -1459,6 +1460,14 @@ class ChatRepository extends DisposableInterface
// [Chat.firstItem] is maintained locally only for [Pagination] reasons.
chat.value.firstItem ??= saved?.chat.value.firstItem;

// [Chat.lastItem] and [Chat.updatedAt] are maintained locally if last
// message is local.
if (saved != null &&
saved.chat.value.updatedAt.isAfter(chat.value.updatedAt)) {
chat.value.updatedAt = saved.chat.value.updatedAt;
chat.value.lastItem = saved.chat.value.lastItem;
}

// Check the versions first, if [ignoreVersion] is `false`.
if (saved != null && !ignoreVersion) {
if (saved.ver != null && saved.ver! >= chat.ver) {
Expand Down
59 changes: 46 additions & 13 deletions lib/store/chat_rx.dart
Copy link
Contributor Author

@krida2000 krida2000 Dec 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SleepySquash Нешел небольшой баг, если в чате много непрочитаных cообщений, то локальные сообщения не будут добавляться в пагинацию либо в хайв. Пока что не придумал как можно это пофиксить, но когда реализуется наша идея с хайвом с пробелами, эта проблема уйдет.

Видео
2023-12-26.12-30-50.mp4

Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ class HiveRxChat extends RxChat {
/// [Timer] unmuting the muted [chat] when its [MuteDuration.until] expires.
Timer? _muteTimer;

/// [ChatItemHiveProvider.boxEvents] subscription.
StreamIterator<BoxEvent>? _localSubscription;

/// [ChatRepository.chatEvents] subscription.
///
/// May be uninitialized since connection establishment may fail.
Expand Down Expand Up @@ -245,19 +248,6 @@ class HiveRxChat extends RxChat {
return null;
}

@override
ChatItem? get lastItem {
ChatItem? item = chat.value.lastItem;
if (messages.isNotEmpty) {
final ChatItem last = messages.last.value;
if (item?.at.isBefore(last.at) == true) {
item = last;
}
}

return item;
}

@override
Stream<void> get updates => _controller.stream;

Expand Down Expand Up @@ -380,6 +370,8 @@ class HiveRxChat extends RxChat {

await _local.init(userId: me);

_initLocalSubscription();

HiveChatItem? item;
if (chat.value.lastReadItem != null) {
item = await get(chat.value.lastReadItem!);
Expand All @@ -402,6 +394,7 @@ class HiveRxChat extends RxChat {
_aroundToken.cancel();
_muteTimer?.cancel();
_readTimer?.cancel();
_localSubscription?.cancel();
_remoteSubscription?.close(immediate: true);
_remoteSubscription = null;
_paginationSubscription?.cancel();
Expand Down Expand Up @@ -1063,6 +1056,46 @@ class HiveRxChat extends RxChat {
}
}

/// Initializes [ChatItemHiveProvider.boxEvents] subscription.
Future<void> _initLocalSubscription() async {
_localSubscription = StreamIterator(_local.boxEvents);
while (await _localSubscription!.moveNext()) {
final BoxEvent event = _localSubscription!.current;
final ChatItemKey key = ChatItemKey.fromString(event.key);

Comment on lines +1077 to +1083
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Во всём этом меня смущает только одна вещь - если тут механизм забарахлит и по какой-то причине не будет добавлено сообщение, которое по факту будет последним, то мы банально заблокируем чат в старом состоянии без возможности это сообщение где-нибудь увидеть - счётчик снизу на "чаты" будет "1", по факту сообщение было добавлено, но не рисуется. Эта проблема, #691, иногда воспроизводится - висит последнее сообщение какое-либо, но в списке сообщений оно не рисуется. Помогает несколько рефрешей страницы и прокрутка вверх/вниз, напрягает - это уже неправильное поведение. Боюсь, с заменой lastItemа на хайв мы можем вовсе потерять тогда и отображение в списке чатов последнего айтема корректное.

Кстати, есть мысли, что и это, и #691 связаны с #777 - вероятно, события в чат могут прилетать, но игнорироваться, т.к. страница пагинации уже версию обновила на свежую?

Предлагаю решить эти два issue сначала, а потом merge'ить этот PR, с ним всё остальное LGTM.

if (event.deleted) {
if (chat.value.lastItem?.key == key) {
final HiveChat? chatEntity = await _chatLocal.get(id);

if (chatEntity != null) {
if (_local.keys.isNotEmpty) {
final HiveChatItem? item = await _local.get(_local.keys.last);
if (item != null) {
chatEntity.value.lastItem = item.value;
chatEntity.value.updatedAt = item.value.at;
}
} else {
chatEntity.value.lastItem = null;
}

await _chatRepository.put(chatEntity, ignoreVersion: true);
}
}
} else {
final HiveChatItem item = event.value;
if (item.value.at.isAfter(chat.value.updatedAt)) {
final HiveChat? chatEntity = await _chatLocal.get(id);

if (chatEntity != null) {
chatEntity.value.updatedAt = item.value.at;
chatEntity.value.lastItem = item.value;
await _chatRepository.put(chatEntity, ignoreVersion: true);
}
}
}
}
}

/// Initializes [ChatRepository.chatEvents] subscription.
Future<void> _initRemoteSubscription() async {
if (_disposed) {
Expand Down
7 changes: 4 additions & 3 deletions lib/store/pagination/hive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -250,13 +250,14 @@ class HivePageProvider<T extends Object, C, K>

final T? firstItem = items.firstOrNull;
if (firstItem != null && isFirst != null) {
hasPrevious =
!isFirst!.call(firstItem) || getKey(firstItem) != ordered.first;
hasPrevious = items.none((e) => isFirst!.call(e)) ||
getKey(firstItem) != ordered.first;
}

final T? lastItem = items.lastOrNull;
if (lastItem != null && isLast != null) {
hasNext = !isLast!.call(lastItem) || getKey(lastItem) != ordered.last;
hasNext = items.none((e) => isLast!.call(e)) ||
getKey(lastItem) != ordered.last;
}

final Page<T, C> page = Page(
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/page/home/page/chat/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ class ChatController extends GetxController {

if (!PlatformUtils.isMobile) {
Future.delayed(Duration.zero, send.field.focus.requestFocus);
} else {
send.field.focus.requestFocus();
Comment on lines +441 to +442
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SleepySquash Тут каждый раз при отправке сообщения на телефоне терялся фокус и скрывалась клавитура. Мне кажется что намного удобней чтобы фокус оставался.

}
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/ui/page/home/tab/chats/widget/recent_chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ class RecentChatTile extends StatelessWidget {
final List<Widget> subtitle;

final Chat chat = rxChat.chat.value;
final ChatItem? item = rxChat.lastItem;
final ChatItem? item = rxChat.chat.value.lastItem;
final ChatMessage? draft = rxChat.draft.value;

final Iterable<String> typings = rxChat.typingUsers
Expand Down Expand Up @@ -825,7 +825,7 @@ class RecentChatTile extends StatelessWidget {
return Obx(() {
final Chat chat = rxChat.chat.value;

final ChatItem? item = rxChat.lastItem;
final ChatItem? item = rxChat.chat.value.lastItem;

if (item != null && item.author.id == me && !chat.isMonolog) {
final bool isSent = item.status.value == SendingStatus.sent;
Expand Down
3 changes: 0 additions & 3 deletions lib/ui/page/style/page/widgets/common/dummy_chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ class DummyRxChat extends RxChat {
@override
RxBool get hasPrevious => RxBool(false);

@override
ChatItem? get lastItem => null;

@override
UserId? get me => null;

Expand Down
Loading