Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ part 'conversation_event.dart';

part 'conversation_state.dart';

const throttleDuration = Duration(milliseconds: 100);
const messagesThrottleDuration = Duration(milliseconds: 100);
const scrollThrottleDuration = Duration(milliseconds: 1000);
const scrollToReplyTimeout = Duration(seconds: 7);

EventTransformer<E> throttleDroppable<E>(Duration duration) {
Expand Down Expand Up @@ -54,6 +55,8 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
StreamSubscription<Map<String, dynamic>>? lastActivitySubscription;
StreamSubscription<ConversationModel?>? conversationWatcher;

Timer? headerTimer;

ConversationBloc({
required this.currentConversation,
required this.conversationRepository,
Expand All @@ -65,7 +68,7 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
on<MessagesRequested>(_onMessagesRequested);
on<MessagesMoreRequested>(
_onMessagesMoreRequested,
transformer: throttleDroppable(throttleDuration),
transformer: throttleDroppable(messagesThrottleDuration),
);
on<ParticipantsReceived>(
_onParticipantsReceived,
Expand Down Expand Up @@ -123,6 +126,13 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
on<SelectedChatsRemoved>(
onSelectedChatsRemoved,
);
on<ShowHeader>(
onShowHeader,
transformer: throttleDroppable(scrollThrottleDuration),
);
on<HideHeader>(
onHideHeader,
);

add(const ParticipantsReceived());

Expand Down Expand Up @@ -273,6 +283,7 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
: (List.of(state.messages)..addAll(messages)),
hasReachedMax: false,
initial: false,
showHeader: false
),
);
break;
Expand All @@ -284,6 +295,20 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
}
}

Future<void> onShowHeader(event, emit) async {
emit(state.copyWith(showHeader: true));

headerTimer?.cancel();

headerTimer = Timer(const Duration(seconds: 3), () {
add(const HideHeader());
});
}

void onHideHeader(event, emit) {
emit(state.copyWith(showHeader: false));
}

Future<void> _onParticipantsReceived(
ParticipantsReceived event, Emitter<ConversationState> emit) async {
var participants =
Expand Down Expand Up @@ -456,27 +481,31 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
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;
int delMsgIndex = messages.indexOf(messagesMap[id]);
messages.remove(messagesMap[id]);

int prevIndex = delMsgIndex;
int nextIndex = delMsgIndex - 1;
ChatMessage? prevMsg = messages.tryGet(prevIndex);
ChatMessage? nextMsg = messages.tryGet(nextIndex);

var prevMsgUpdated = prevMsg?.copyWith(
isLastUserMessage: prevIndex == 0 ||
isServiceMessage(messages.tryGet(prevIndex - 2)) ||
messages.tryGet(prevIndex - 2)?.from != messages[prevIndex].from,
isServiceMessage(messages.tryGet(prevIndex - 1)) ||
messages.tryGet(prevIndex - 1)?.from != messages[prevIndex].from,
isFirstUserMessage: prevIndex == messages.length - 1 ||
isServiceMessage(messages[prevIndex + 2]) ||
messages[prevIndex + 2].from != messages[prevIndex].from);
isServiceMessage(messages[prevIndex + 1]) ||
messages[prevIndex + 1].from != messages[prevIndex].from,
bubbleType: bubbleType(messages, prevIndex));

var nextMsgUpdated = nextMsg?.copyWith(
isLastUserMessage: nextIndex == 0 ||
isServiceMessage(messages[nextIndex - 2]) ||
messages[nextIndex - 2].from != messages[nextIndex].from,
isServiceMessage(messages[nextIndex - 1]) ||
messages[nextIndex - 1].from != messages[nextIndex].from,
isFirstUserMessage: nextIndex == messages.length - 1 ||
isServiceMessage(messages[nextIndex + 2]) ||
messages[nextIndex + 2].from != messages[nextIndex].from);
isServiceMessage(messages[nextIndex + 1]) ||
messages[nextIndex + 1].from != messages[nextIndex].from,
bubbleType: bubbleType(messages, nextIndex));

if (prevMsgUpdated != null && prevMsgUpdated != prevMsg) {
messages[prevIndex] = prevMsgUpdated;
Expand All @@ -485,8 +514,6 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
if (nextMsgUpdated != null && nextMsgUpdated != nextMsg) {
messages[nextIndex] = nextMsgUpdated;
}

messages.remove(messagesMap[id]);
});
emit(state.copyWith(messages: messages));
}
Expand Down Expand Up @@ -592,6 +619,7 @@ class ConversationBloc extends Bloc<ConversationEvent, ConversationState> {
typingMessageSubscription?.cancel();
lastActivitySubscription?.cancel();
conversationWatcher?.cancel();
headerTimer?.cancel();
return super.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,11 @@ final class SelectedChatsRemoved extends ConversationEvent {
@override
List<Object> get props => [message];
}

final class ShowHeader extends ConversationEvent {
const ShowHeader();
}

final class HideHeader extends ConversationEvent {
const HideHeader();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ final class ConversationState extends Equatable {
this.typingStatus,
this.replyIdToScroll = '',
this.choose = false,
this.showHeader = false,
});

final ConversationModel conversation;
Expand All @@ -31,6 +32,7 @@ final class ConversationState extends Equatable {
final bool hasReachedMax;
final bool initial;
final bool choose;
final bool showHeader;
final TypingMessageStatus? typingStatus;
final String replyIdToScroll;

Expand All @@ -43,6 +45,7 @@ final class ConversationState extends Equatable {
bool? hasReachedMax,
bool? initial,
bool? choose,
bool? showHeader,
String? replyIdToScroll,
TypingMessageStatus? typingStatus,
}) {
Expand All @@ -55,6 +58,7 @@ final class ConversationState extends Equatable {
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
initial: initial ?? this.initial,
choose: choose ?? this.choose,
showHeader: showHeader ?? this.showHeader,
typingStatus: typingStatus ?? this.typingStatus,
replyIdToScroll: replyIdToScroll ?? this.replyIdToScroll,
);
Expand All @@ -74,6 +78,7 @@ final class ConversationState extends Equatable {
hasReachedMax,
initial,
choose,
showHeader,
replyIdToScroll,
participants,
typingStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ extension ChatMessageExtension on MessageModel {
}
}

bool isDifferentDay(MessageModel msg, MessageModel? other) {
final msgCreatedAt =
msg.createdAt ?? DateTime.fromMillisecondsSinceEpoch(msg.t!);
final currentMsgDate =
DateTime(msgCreatedAt.year, msgCreatedAt.month, msgCreatedAt.day);

final nextMsgCreatedAt =
other?.createdAt ?? DateTime.fromMillisecondsSinceEpoch(other?.t ?? 0);
final nextMsgDate = other != null
? DateTime(
nextMsgCreatedAt.year, nextMsgCreatedAt.month, nextMsgCreatedAt.day)
: null;

if (currentMsgDate != nextMsgDate) {
return true;
}

return false;
}

bool sameMsgGroup(MessageModel msg, MessageModel? other) {
int diffTime = 45;

Expand All @@ -134,7 +154,7 @@ bool sameMsgGroup(MessageModel msg, MessageModel? other) {
var msgMs = (msg.createdAt?.millisecondsSinceEpoch ?? 0) ~/ 1000;
var timeGap = (msgMs - otherMs).abs();

return isSameOwner && timeGap < diffTime;
return isSameOwner && !isDifferentDay(msg, other) && timeGap < diffTime;
}

BubbleType bubbleType(List<MessageModel> messages, int index) {
Expand Down
Loading