mirror of
https://github.com/Iconica-Development/flutter_chat.git
synced 2025-05-19 10:53:51 +02:00
feat: use getAllUsersForChat on ChatDetailScreen to avoid call to getUser for each user
This commit is contained in:
parent
990a89199b
commit
77d6f7257e
3 changed files with 70 additions and 65 deletions
|
@ -121,12 +121,13 @@ typedef ContainerBuilder = Widget Function(
|
||||||
/// If null is returned, the default chat message widget will be used so you can
|
/// If null is returned, the default chat message widget will be used so you can
|
||||||
/// override for specific cases
|
/// override for specific cases
|
||||||
/// [previousMessage] is the previous message in the chat
|
/// [previousMessage] is the previous message in the chat
|
||||||
|
/// [sender] is the sender of the message and null if no user sent the message
|
||||||
typedef ChatMessageBuilder = Widget? Function(
|
typedef ChatMessageBuilder = Widget? Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
MessageModel message,
|
MessageModel message,
|
||||||
MessageModel? previousMessage,
|
MessageModel? previousMessage,
|
||||||
UserModel user,
|
UserModel? sender,
|
||||||
Function(UserModel user) onPressUserProfile,
|
Function(UserModel sender) onPressSender,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The group avatar builder
|
/// The group avatar builder
|
||||||
|
|
|
@ -60,23 +60,24 @@ class ChatDetailScreen extends HookWidget {
|
||||||
var chatSnapshot = useStream(chatStream);
|
var chatSnapshot = useStream(chatStream);
|
||||||
var chat = chatSnapshot.data;
|
var chat = chatSnapshot.data;
|
||||||
|
|
||||||
|
var allUsersStream = useMemoized(
|
||||||
|
() => options.userRepository.getAllUsersForChat(chatId: chatId),
|
||||||
|
[chatId],
|
||||||
|
);
|
||||||
|
var usersSnapshot = useStream(allUsersStream);
|
||||||
|
var allUsers = usersSnapshot.data ?? [];
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() {
|
() {
|
||||||
if (chat == null) return;
|
if (chat == null) return;
|
||||||
if (chat.isGroupChat) {
|
chatTitle.value = _getChatTitle(
|
||||||
chatTitle.value = options.translations.groupNameEmpty;
|
|
||||||
} else {
|
|
||||||
unawaited(
|
|
||||||
_computeChatTitle(
|
|
||||||
chatScope: chatScope,
|
chatScope: chatScope,
|
||||||
chat: chat,
|
chat: chat,
|
||||||
onTitleComputed: (title) => chatTitle.value = title,
|
allUsers: allUsers,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
[chat],
|
[chat, allUsers],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
|
@ -98,6 +99,7 @@ class ChatDetailScreen extends HookWidget {
|
||||||
var body = _Body(
|
var body = _Body(
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
chat: chat,
|
chat: chat,
|
||||||
|
chatUsers: allUsers,
|
||||||
onPressUserProfile: onPressUserProfile,
|
onPressUserProfile: onPressUserProfile,
|
||||||
onUploadImage: onUploadImage,
|
onUploadImage: onUploadImage,
|
||||||
onMessageSubmit: onMessageSubmit,
|
onMessageSubmit: onMessageSubmit,
|
||||||
|
@ -120,21 +122,26 @@ class ChatDetailScreen extends HookWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _computeChatTitle({
|
String? _getChatTitle({
|
||||||
required ChatScope chatScope,
|
required ChatScope chatScope,
|
||||||
required ChatModel chat,
|
required ChatModel chat,
|
||||||
required void Function(String?) onTitleComputed,
|
required List<UserModel> allUsers,
|
||||||
}) async {
|
}) {
|
||||||
if (chatScope.options.chatTitleResolver != null) {
|
if (chat.isGroupChat) {
|
||||||
onTitleComputed(chatScope.options.chatTitleResolver!.call(chat));
|
return chatScope.options.translations.groupNameEmpty;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = chat.users.firstWhere((user) => user != chatScope.userId);
|
// For one-to-one, pick the 'other' user from the list
|
||||||
var user = await chatScope.service.getUser(userId: userId).first;
|
var otherUser = allUsers.firstWhere(
|
||||||
onTitleComputed(
|
(u) => u.id != chatScope.userId,
|
||||||
user.fullname ?? chatScope.options.translations.anonymousUser,
|
orElse: () => const UserModel(
|
||||||
|
id: "",
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return otherUser.fullname?.isNotEmpty ?? false
|
||||||
|
? otherUser.fullname
|
||||||
|
: chatScope.options.translations.anonymousUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +205,7 @@ class _Body extends HookWidget {
|
||||||
const _Body({
|
const _Body({
|
||||||
required this.chatId,
|
required this.chatId,
|
||||||
required this.chat,
|
required this.chat,
|
||||||
|
required this.chatUsers,
|
||||||
required this.onPressUserProfile,
|
required this.onPressUserProfile,
|
||||||
required this.onUploadImage,
|
required this.onUploadImage,
|
||||||
required this.onMessageSubmit,
|
required this.onMessageSubmit,
|
||||||
|
@ -206,6 +214,7 @@ class _Body extends HookWidget {
|
||||||
|
|
||||||
final String chatId;
|
final String chatId;
|
||||||
final ChatModel? chat;
|
final ChatModel? chat;
|
||||||
|
final List<UserModel> chatUsers;
|
||||||
final Function(UserModel) onPressUserProfile;
|
final Function(UserModel) onPressUserProfile;
|
||||||
final Function(Uint8List image) onUploadImage;
|
final Function(Uint8List image) onUploadImage;
|
||||||
final Function(String message) onMessageSubmit;
|
final Function(String message) onMessageSubmit;
|
||||||
|
@ -261,15 +270,21 @@ class _Body extends HookWidget {
|
||||||
_ChatNoMessages(isGroupChat: chat!.isGroupChat),
|
_ChatNoMessages(isGroupChat: chat!.isGroupChat),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
for (var (index, message) in messages.indexed)
|
for (var (index, message) in messages.indexed) ...[
|
||||||
if (chat!.id == message.chatId)
|
if (chat!.id == message.chatId)
|
||||||
_ChatBubble(
|
_ChatBubble(
|
||||||
key: ValueKey(message.id),
|
key: ValueKey(message.id),
|
||||||
|
sender: chatUsers
|
||||||
|
.where(
|
||||||
|
(u) => u.id == message.senderId,
|
||||||
|
)
|
||||||
|
.firstOrNull,
|
||||||
message: message,
|
message: message,
|
||||||
previousMessage:
|
previousMessage:
|
||||||
index < messages.length - 1 ? messages[index + 1] : null,
|
index < messages.length - 1 ? messages[index + 1] : null,
|
||||||
onPressUserProfile: onPressUserProfile,
|
onPressSender: onPressUserProfile,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
|
@ -457,7 +472,8 @@ class _ChatBottom extends HookWidget {
|
||||||
class _ChatBubble extends HookWidget {
|
class _ChatBubble extends HookWidget {
|
||||||
const _ChatBubble({
|
const _ChatBubble({
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.onPressUserProfile,
|
required this.sender,
|
||||||
|
required this.onPressSender,
|
||||||
this.previousMessage,
|
this.previousMessage,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
@ -465,45 +481,33 @@ class _ChatBubble extends HookWidget {
|
||||||
/// The message to display.
|
/// The message to display.
|
||||||
final MessageModel message;
|
final MessageModel message;
|
||||||
|
|
||||||
|
/// The user who sent the message. This can be null because some messages are
|
||||||
|
/// not from users
|
||||||
|
final UserModel? sender;
|
||||||
|
|
||||||
/// The previous message in the list, if any.
|
/// The previous message in the list, if any.
|
||||||
final MessageModel? previousMessage;
|
final MessageModel? previousMessage;
|
||||||
|
|
||||||
/// Callback function when the user's profile is pressed.
|
/// Callback function when a message sender is pressed.
|
||||||
final Function(UserModel user) onPressUserProfile;
|
final Function(UserModel user) onPressSender;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var chatScope = ChatScope.of(context);
|
var chatScope = ChatScope.of(context);
|
||||||
var service = chatScope.service;
|
|
||||||
var options = chatScope.options;
|
var options = chatScope.options;
|
||||||
|
|
||||||
var userStream = useMemoized(
|
|
||||||
() => service.getUser(userId: message.senderId),
|
|
||||||
[message.senderId],
|
|
||||||
);
|
|
||||||
var userSnapshot = useStream(userStream);
|
|
||||||
|
|
||||||
if (userSnapshot.connectionState == ConnectionState.waiting) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = userSnapshot.data;
|
|
||||||
if (user == null) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.builders.chatMessageBuilder.call(
|
return options.builders.chatMessageBuilder.call(
|
||||||
context,
|
context,
|
||||||
message,
|
message,
|
||||||
previousMessage,
|
previousMessage,
|
||||||
user,
|
sender,
|
||||||
onPressUserProfile,
|
onPressSender,
|
||||||
) ??
|
) ??
|
||||||
DefaultChatMessageBuilder(
|
DefaultChatMessageBuilder(
|
||||||
message: message,
|
message: message,
|
||||||
previousMessage: previousMessage,
|
previousMessage: previousMessage,
|
||||||
user: user,
|
sender: sender,
|
||||||
onPressUserProfile: onPressUserProfile,
|
onPressSender: onPressSender,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
||||||
const DefaultChatMessageBuilder({
|
const DefaultChatMessageBuilder({
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.previousMessage,
|
required this.previousMessage,
|
||||||
required this.user,
|
required this.sender,
|
||||||
required this.onPressUserProfile,
|
required this.onPressSender,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,25 +25,25 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
||||||
/// is from the same sender as the previous message.
|
/// is from the same sender as the previous message.
|
||||||
final MessageModel? previousMessage;
|
final MessageModel? previousMessage;
|
||||||
|
|
||||||
/// The user that sent the message
|
/// The user that sent the message, can be null if the message is an event
|
||||||
final UserModel user;
|
final UserModel? sender;
|
||||||
|
|
||||||
/// The function that is called when the user profile is pressed
|
/// The function that is called when the sender is clicked
|
||||||
final Function(UserModel user) onPressUserProfile;
|
final Function(UserModel user) onPressSender;
|
||||||
|
|
||||||
/// implements [ChatMessageBuilder]
|
/// implements [ChatMessageBuilder]
|
||||||
static Widget builder(
|
static Widget builder(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
MessageModel message,
|
MessageModel message,
|
||||||
MessageModel? previousMessage,
|
MessageModel? previousMessage,
|
||||||
UserModel user,
|
UserModel? sender,
|
||||||
Function(UserModel user) onPressUserProfile,
|
Function(UserModel sender) onPressSender,
|
||||||
) =>
|
) =>
|
||||||
DefaultChatMessageBuilder(
|
DefaultChatMessageBuilder(
|
||||||
message: message,
|
message: message,
|
||||||
previousMessage: previousMessage,
|
previousMessage: previousMessage,
|
||||||
user: user,
|
sender: sender,
|
||||||
onPressUserProfile: onPressUserProfile,
|
onPressSender: onPressSender,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Merges the [MessageTheme] from the themeresolver with the [MessageTheme]
|
/// Merges the [MessageTheme] from the themeresolver with the [MessageTheme]
|
||||||
|
@ -53,7 +53,7 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required ChatOptions options,
|
required ChatOptions options,
|
||||||
required MessageModel message,
|
required MessageModel message,
|
||||||
required UserModel user,
|
required UserModel? user,
|
||||||
}) =>
|
}) =>
|
||||||
[
|
[
|
||||||
options.messageThemeResolver(context, message, user),
|
options.messageThemeResolver(context, message, user),
|
||||||
|
@ -71,7 +71,7 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
||||||
context: context,
|
context: context,
|
||||||
options: options,
|
options: options,
|
||||||
message: message,
|
message: message,
|
||||||
user: user,
|
user: sender,
|
||||||
);
|
);
|
||||||
|
|
||||||
var isSameSender = previousMessage != null &&
|
var isSameSender = previousMessage != null &&
|
||||||
|
@ -84,7 +84,7 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
||||||
isMessageFromSelf: isMessageFromSelf,
|
isMessageFromSelf: isMessageFromSelf,
|
||||||
message: message,
|
message: message,
|
||||||
messageTheme: messageTheme,
|
messageTheme: messageTheme,
|
||||||
user: user,
|
sender: sender,
|
||||||
);
|
);
|
||||||
|
|
||||||
var messagePadding = messageTheme.messageSidePadding!;
|
var messagePadding = messageTheme.messageSidePadding!;
|
||||||
|
@ -122,14 +122,14 @@ class _ChatMessageBubble extends StatelessWidget {
|
||||||
required this.isMessageFromSelf,
|
required this.isMessageFromSelf,
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.messageTheme,
|
required this.messageTheme,
|
||||||
required this.user,
|
required this.sender,
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool isSameSender;
|
final bool isSameSender;
|
||||||
final bool isMessageFromSelf;
|
final bool isMessageFromSelf;
|
||||||
final MessageModel message;
|
final MessageModel message;
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme messageTheme;
|
||||||
final UserModel user;
|
final UserModel? sender;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -141,7 +141,7 @@ class _ChatMessageBubble extends StatelessWidget {
|
||||||
var messageTime = dateFormatter.format(date: message.timestamp);
|
var messageTime = dateFormatter.format(date: message.timestamp);
|
||||||
|
|
||||||
var senderTitle = Text(
|
var senderTitle = Text(
|
||||||
user.firstName ?? "",
|
sender?.firstName ?? "",
|
||||||
style: theme.textTheme.titleMedium,
|
style: theme.textTheme.titleMedium,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue