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
1 change: 1 addition & 0 deletions example/ios/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ Runner/GeneratedPluginRegistrant.*
!default.perspectivev3

**/GoogleService-Info.plist
Podfile.lock
10 changes: 10 additions & 0 deletions example/lib/modules/chat_detail/chat_detail_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,17 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
size: 30,
),
),
loadMoreData: (direction, message) => chatController.onLoadMoreData(
direction,
message,
batchSize: 8,
),
repliedMessageConfig: RepliedMessageConfiguration(
loadOldReplyMessage: (messageId) =>
chatController.loadOldReplyMessage(
messageId,
batchSize: 8,
),
backgroundColor: Colors.grey.shade300,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 14),
),
Expand Down
3 changes: 2 additions & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ dependencies:
firebase_core: ^3.13.0
cloud_firestore: ^5.6.7
uuid: ^4.5.1
# TODO(YASH): Update to latest version to include Pagination enums & replaceMessageList
chatview:
git:
url: https://github.com/SimformSolutionsPvtLtd/chatview/
ref: feat/use_data_models_from_chatview_utils
ref: feat/chat_list_ui_base

chatview_connect:
path: ..
Expand Down
123 changes: 121 additions & 2 deletions lib/src/database/database_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ abstract interface class DatabaseService {
/// Used to fetch messages associated with a specific chat session.
///
/// - (required): [sortBy] specifies the sorting order of messages
/// by defaults it will be sorted by the dateTime.
/// by defaults it will be sorted by the `createAt` dateTime.
///
/// - (required): [sortOrder] specifies the order of sorting for messages.
/// by defaults it will be ascending sort order.
Expand Down Expand Up @@ -86,7 +86,7 @@ abstract interface class DatabaseService {
/// Used to stream messages for the specified chat session in real time.
///
/// - (required): [sortBy] specifies the sorting order of messages
/// by defaults it will be sorted by the dateTime.
/// by defaults it will be sorted by the `createAt` dateTime.
///
/// - (required): [sortOrder] specifies the order of sorting for messages.
/// by defaults it will be ascending sort order.
Expand Down Expand Up @@ -725,4 +725,123 @@ abstract interface class DatabaseService {
required String chatId,
DeleteChatMediaCallback? deleteMedia,
});

/// Retrieves a list of messages surrounding a specific message in a chat.
/// This method fetches messages before and after the specified message
/// to provide context.
///
/// **Parameters:**
/// - (required): [chatId] A unique identifier for the chat room.
/// Specifies the chat room from which surrounding messages will be fetched.
/// - (required): [retry] The number of times to retry fetching messages
/// in case of a failure.
/// - (required): [messageId] The unique identifier of the message
/// for which surrounding messages are being retrieved.
/// - (required): [batchSize] The number of messages to retrieve
/// before and after the specified message.
/// - (required): [sortBy] specifies the sorting order of messages
/// by defaults it will be sorted by the `createAt` dateTime.
/// - (required): [sortOrder] specifies the order of sorting for messages.
/// by defaults it will be ascending sort order.
///
/// **Returns:**
/// A [Future] that resolves to a [List] of [Message] objects,
/// representing the surrounding messages.
///
/// This method is useful for providing context in chat applications,
/// allowing users to see related messages around a specific point in the chat.
///
/// **Example usage:**
/// ```dart
/// List<Message> surroundingMessages = await getSurroundingMessages(
/// chatId: "chat123",
/// messageId: "msg456",
/// batchSize: 5,
/// );
/// ```
Future<List<Message>> getSurroundingMessages({
required String chatId,
required int retry,
required String messageId,
required int batchSize,
required MessageSortBy sortBy,
required MessageSortOrder sortOrder,
});

/// Retrieves a list of previous messages in a chat room.
///
/// This method fetches messages that were sent before
/// the specified message ID, allowing users to navigate
/// through the chat history.
///
/// **Parameters:**
/// - (required): [chatId] A unique identifier for the chat room.
/// Specifies the chat room from which previous messages will be fetched.
/// - (required): [retry] The number of times to retry fetching messages
/// in case of a failure.
/// - (required): [messageId] The unique identifier of the message
/// before which previous messages are being retrieved.
/// - (required): [batchSize] The number of messages to retrieve
/// before the specified message.
///
/// **Returns:**
/// A [Future] that resolves to a [List] of [Message] objects,
/// representing the previous messages.
///
/// This method is useful for implementing pagination or
/// load-more functionality in chat applications,
/// allowing users to view older messages in the chat history.
///
/// **Example usage:**
/// ```dart
/// List<Message> previousMessages = await getPreviousMessages(
/// chatId: "chat123",
/// messageId: "msg456",
/// batchSize: 10,
/// );
/// ```
Future<List<Message>> getPreviousMessages({
required String chatId,
required int retry,
required String messageId,
required int batchSize,
});

/// Retrieves a list of next messages in a chat room.
/// This method fetches messages that were sent after
/// the specified message ID, allowing users to navigate
/// through the chat history.
///
/// **Parameters:**
/// - (required): [chatId] A unique identifier for the chat room.
/// Specifies the chat room from which next messages will be fetched.
/// - (required): [retry] The number of times to retry fetching messages
/// in case of a failure.
/// - (required): [messageId] The unique identifier of the message
/// after which next messages are being retrieved.
/// - (required): [batchSize] The number of messages to retrieve
/// after the specified message.
///
/// **Returns:**
/// A [Future] that resolves to a [List] of [Message] objects,
/// representing the next messages.
///
/// This method is useful for implementing pagination or
/// load-more functionality in chat applications,
/// allowing users to view newer messages in the chat history.
///
/// **Example usage:**
/// ```dart
/// List<Message> nextMessages = await getNextMessages(
/// chatId: "chat123",
/// messageId: "msg456",
/// batchSize: 10,
/// );
/// ```
Future<List<Message>> getNextMessages({
required String chatId,
required int retry,
required String messageId,
required int batchSize,
});
}
173 changes: 173 additions & 0 deletions lib/src/database/firebase/chatview_firestore_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1725,4 +1725,177 @@ final class ChatViewFireStoreDatabase implements DatabaseService {
),
);
}

@override
Future<List<Message>> getSurroundingMessages({
required String chatId,
required int retry,
required String messageId,
required int batchSize,
required MessageSortBy sortBy,
required MessageSortOrder sortOrder,
}) async {
// -1 to exclude the current message
final surroundingMessagesCount = batchSize - 1;
final firstSideMessageCount = surroundingMessagesCount ~/ 2;
final secondSideMessageCount =
surroundingMessagesCount - firstSideMessageCount;

try {
final messageCollectionRef = _messageCollectionRef(chatId).toMessageQuery(
sortBy: sortBy,
sortOrder: sortOrder,
);

final currentMessageResult =
await _messageCollectionRef(chatId).doc(messageId).get();

final currentMessage = currentMessageResult.data();
if (currentMessage == null) return List<Message>.empty();

final previousMessagesSnapshot = await messageCollectionRef
.endBeforeDocument(currentMessageResult)
.limitToLast(firstSideMessageCount)
.get();

final fetchedPreviousCount = previousMessagesSnapshot.docs.length;

final remainingNextCount = fetchedPreviousCount != firstSideMessageCount
? surroundingMessagesCount - fetchedPreviousCount
: secondSideMessageCount;

final nextMessagesSnapshot = await messageCollectionRef
.startAfterDocument(currentMessageResult)
.limit(remainingNextCount)
.get();

final fetchedNextCount = nextMessagesSnapshot.docs.length;

List<QueryDocumentSnapshot<Message?>>? additionalPreviousMessagesDocs;

final firstPreviousDoc = previousMessagesSnapshot.docs.firstOrNull;

// If next messages are fewer than expected and we still have earlier messages available
if (fetchedNextCount != remainingNextCount && firstPreviousDoc != null) {
// +1 to account for the current message
final totalFetchedCount = fetchedPreviousCount + fetchedNextCount + 1;
final remainingMessagesToFetch = batchSize - totalFetchedCount;

final additionalPreviousSnapshot = await messageCollectionRef
.endBeforeDocument(firstPreviousDoc)
.limitToLast(remainingMessagesToFetch)
.get();

additionalPreviousMessagesDocs = additionalPreviousSnapshot.docs;
}

final allMessageDocs = [
...?additionalPreviousMessagesDocs,
...previousMessagesSnapshot.docs,
currentMessageResult,
...nextMessagesSnapshot.docs,
];

final allMessagesLength = allMessageDocs.length;

return [
for (var i = 0; i < allMessagesLength; i++)
if (allMessageDocs[i].data() case final message?) message,
];
} on FirebaseException catch (_) {
if (retry == 0) return List<Message>.empty();
return getSurroundingMessages(
retry: --retry,
chatId: chatId,
sortBy: sortBy,
messageId: messageId,
sortOrder: sortOrder,
batchSize: batchSize,
);
}
}

@override
Future<List<Message>> getPreviousMessages({
required String chatId,
required int retry,
required String messageId,
required int batchSize,
}) async {
try {
final messageCollectionRef = _messageCollectionRef(chatId).toMessageQuery(
sortBy: MessageSortBy.createAt,
sortOrder: MessageSortOrder.asc,
);

final currentMessageResult =
await _messageCollectionRef(chatId).doc(messageId).get();

final currentMessage = currentMessageResult.data();
if (currentMessage == null) List<Message>.empty();

final previousMessagesSnapshot = await messageCollectionRef
.endBeforeDocument(currentMessageResult)
.limitToLast(batchSize)
.get();

final docs = previousMessagesSnapshot.docs;
final docsLength = docs.length;

return [
for (var i = 0; i < docsLength; i++)
if (docs[i].data() case final message?) message,
];
} on FirebaseException catch (_) {
if (retry == 0) return List<Message>.empty();
return getPreviousMessages(
retry: --retry,
chatId: chatId,
messageId: messageId,
batchSize: batchSize,
);
}
}

@override
Future<List<Message>> getNextMessages({
required String chatId,
required int retry,
required String messageId,
required int batchSize,
}) async {
try {
final messageCollectionRef = _messageCollectionRef(chatId).toMessageQuery(
sortBy: MessageSortBy.createAt,
sortOrder: MessageSortOrder.asc,
);

final currentMessageResult =
await _messageCollectionRef(chatId).doc(messageId).get();

final currentMessage = currentMessageResult.data();
if (currentMessage == null) List<Message>.empty();

final nextMessagesSnapshot = await messageCollectionRef
.startAfterDocument(currentMessageResult)
.limit(batchSize)
.get();

final docs = nextMessagesSnapshot.docs;
final docsLength = docs.length;

return [
for (var i = 0; i < docsLength; i++)
if (docs[i].data() case final message?) message,
];
} on FirebaseException catch (_) {
if (retry == 0) return List<Message>.empty();
return getNextMessages(
retry: --retry,
chatId: chatId,
messageId: messageId,
batchSize: batchSize,
);
}
}
}
Loading
Loading