mirror of
https://github.com/Iconica-Development/flutter_chat.git
synced 2025-05-19 10:53:51 +02:00
feat: refactor
This commit is contained in:
parent
f55c43653c
commit
4df2adb984
27 changed files with 553 additions and 588 deletions
|
@ -4,29 +4,5 @@
|
||||||
|
|
||||||
library flutter_community_chat;
|
library flutter_community_chat;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_community_chat/service/chat_service.dart';
|
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
|
||||||
import 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
|
||||||
export 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
export 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
||||||
export 'package:flutter_community_chat/service/chat_service.dart';
|
|
||||||
export 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
export 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
|
|
||||||
class CommunityChat extends StatelessWidget {
|
|
||||||
const CommunityChat({
|
|
||||||
required this.chatService,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ChatService chatService;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => ChatScreen(
|
|
||||||
chats: chatService.dataProvider.getChatsStream(),
|
|
||||||
onPressStartChat: () => chatService.onPressStartChat(context),
|
|
||||||
onPressChat: (chat) => chatService.onPressChat(context, chat),
|
|
||||||
onDeleteChat: (ChatModel chat) => chatService.deleteChat(chat),
|
|
||||||
options: chatService.options,
|
|
||||||
translations: chatService.translations(context),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_community_chat/ui/components/image_loading_snackbar.dart';
|
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
|
||||||
import 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
|
||||||
import 'package:flutter_image_picker/flutter_image_picker.dart';
|
|
||||||
|
|
||||||
abstract class ChatService {
|
|
||||||
ChatService({
|
|
||||||
required this.options,
|
|
||||||
required this.imagePickerConfig,
|
|
||||||
required this.dataProvider,
|
|
||||||
});
|
|
||||||
|
|
||||||
final CommunityChatInterface dataProvider;
|
|
||||||
final ChatOptions options;
|
|
||||||
final ImagePickerConfig imagePickerConfig;
|
|
||||||
bool _isFetchingUsers = false;
|
|
||||||
|
|
||||||
ImagePickerTheme imagePickerTheme(BuildContext context) =>
|
|
||||||
const ImagePickerTheme();
|
|
||||||
|
|
||||||
ChatTranslations translations(BuildContext context);
|
|
||||||
|
|
||||||
Future<void> _push(BuildContext context, Widget widget) =>
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(builder: (context) => widget),
|
|
||||||
);
|
|
||||||
|
|
||||||
void _pop(BuildContext context) => Navigator.of(context).pop();
|
|
||||||
|
|
||||||
Future<void> onPressStartChat(BuildContext context) async {
|
|
||||||
if (!_isFetchingUsers) {
|
|
||||||
_isFetchingUsers = true;
|
|
||||||
await dataProvider.getChatUsers().then(
|
|
||||||
(users) {
|
|
||||||
_isFetchingUsers = false;
|
|
||||||
_push(
|
|
||||||
context,
|
|
||||||
buildNewChatScreen(context, users),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildNewChatScreen(
|
|
||||||
BuildContext context,
|
|
||||||
List<ChatUserModel> users,
|
|
||||||
) =>
|
|
||||||
NewChatScreen(
|
|
||||||
options: options,
|
|
||||||
translations: translations(context),
|
|
||||||
onPressCreateChat: (user) => onPressChat(
|
|
||||||
context,
|
|
||||||
PersonalChatModel(user: user),
|
|
||||||
popBeforePush: true,
|
|
||||||
),
|
|
||||||
users: users,
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<void> onPressChat(
|
|
||||||
BuildContext context,
|
|
||||||
ChatModel chat, {
|
|
||||||
bool popBeforePush = false,
|
|
||||||
}) =>
|
|
||||||
dataProvider.setChat(chat).then((_) {
|
|
||||||
if (popBeforePush) {
|
|
||||||
_pop(context);
|
|
||||||
}
|
|
||||||
_push(
|
|
||||||
context,
|
|
||||||
buildChatDetailScreen(context, chat),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Widget buildChatDetailScreen(
|
|
||||||
BuildContext context,
|
|
||||||
ChatModel chat,
|
|
||||||
) =>
|
|
||||||
ChatDetailScreen(
|
|
||||||
options: options,
|
|
||||||
translations: translations(context),
|
|
||||||
chat: chat,
|
|
||||||
chatMessages: dataProvider.getMessagesStream(),
|
|
||||||
onPressSelectImage: (ChatModel chat) => onPressSelectImage(
|
|
||||||
context,
|
|
||||||
chat,
|
|
||||||
),
|
|
||||||
onMessageSubmit: (ChatModel chat, String content) =>
|
|
||||||
dataProvider.sendTextMessage(content),
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<void> onPressSelectImage(
|
|
||||||
BuildContext context,
|
|
||||||
ChatModel chat,
|
|
||||||
) =>
|
|
||||||
showModalBottomSheet<Uint8List?>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) => options.imagePickerContainerBuilder(
|
|
||||||
ImagePicker(
|
|
||||||
customButton: options.closeImagePickerButtonBuilder(
|
|
||||||
context,
|
|
||||||
() => Navigator.of(context).pop(),
|
|
||||||
translations(context),
|
|
||||||
),
|
|
||||||
imagePickerConfig: imagePickerConfig,
|
|
||||||
imagePickerTheme: imagePickerTheme(context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).then(
|
|
||||||
(image) async {
|
|
||||||
var messenger = ScaffoldMessenger.of(context);
|
|
||||||
|
|
||||||
messenger.showSnackBar(
|
|
||||||
getImageLoadingSnackbar(
|
|
||||||
translations(context),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (image != null) {
|
|
||||||
await dataProvider.sendImageMessage(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
messenger.hideCurrentSnackBar();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<void> deleteChat(ChatModel chat) => dataProvider.deleteChat(chat);
|
|
||||||
}
|
|
|
@ -179,7 +179,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "packages/flutter_community_chat_interface"
|
path: "packages/flutter_community_chat_interface"
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: "6a9e88ec8d07118e1543b1a82aa5482d6832cbf8"
|
resolved-ref: c644e1affb1dd4f570bf0e4ae2e950f5e9d83c37
|
||||||
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
@ -188,7 +188,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "packages/flutter_community_chat_view"
|
path: "packages/flutter_community_chat_view"
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: "6a9e88ec8d07118e1543b1a82aa5482d6832cbf8"
|
resolved-ref: c644e1affb1dd4f570bf0e4ae2e950f5e9d83c37
|
||||||
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
@ -202,7 +202,7 @@ packages:
|
||||||
source: git
|
source: git
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
flutter_image_picker:
|
flutter_image_picker:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "1.0.3"
|
ref: "1.0.3"
|
||||||
|
@ -319,7 +319,7 @@ packages:
|
||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.13"
|
version: "0.12.14"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -361,7 +361,7 @@ packages:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.3"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -478,7 +478,7 @@ packages:
|
||||||
name: sqflite
|
name: sqflite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0+3"
|
version: "2.2.2"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -527,7 +527,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.16"
|
version: "0.4.17"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -19,10 +19,6 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_community_chat.git
|
url: https://github.com/Iconica-Development/flutter_community_chat.git
|
||||||
path: packages/flutter_community_chat_interface
|
path: packages/flutter_community_chat_interface
|
||||||
flutter_image_picker:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_image_picker
|
|
||||||
ref: 1.0.3
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
|
|
@ -7,22 +7,9 @@ class FirebaseChatOptions {
|
||||||
this.chatsCollectionName = 'chats',
|
this.chatsCollectionName = 'chats',
|
||||||
this.messagesCollectionName = 'messages',
|
this.messagesCollectionName = 'messages',
|
||||||
this.usersCollectionName = 'users',
|
this.usersCollectionName = 'users',
|
||||||
this.userFilter,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String chatsCollectionName;
|
final String chatsCollectionName;
|
||||||
final String messagesCollectionName;
|
final String messagesCollectionName;
|
||||||
final String usersCollectionName;
|
final String usersCollectionName;
|
||||||
|
|
||||||
final FirebaseUserFilter? userFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FirebaseUserFilter {
|
|
||||||
const FirebaseUserFilter({
|
|
||||||
required this.field,
|
|
||||||
required this.expectedValue,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String field;
|
|
||||||
final Object expectedValue;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,84 +4,4 @@
|
||||||
|
|
||||||
library flutter_community_chat_firebase;
|
library flutter_community_chat_firebase;
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
|
||||||
import 'package:firebase_storage/firebase_storage.dart';
|
|
||||||
import 'package:flutter_community_chat_firebase/config/firebase_chat_options.dart';
|
|
||||||
import 'package:flutter_community_chat_firebase/service/service.dart';
|
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
|
||||||
export 'package:flutter_community_chat_firebase/service/service.dart';
|
export 'package:flutter_community_chat_firebase/service/service.dart';
|
||||||
|
|
||||||
class FirebaseCommunityChatDataProvider extends CommunityChatInterface {
|
|
||||||
late final FirebaseUserService _userService;
|
|
||||||
late final FirebaseMessageService _messageService;
|
|
||||||
late final FirebaseChatService _chatService;
|
|
||||||
final FirebaseChatOptions firebaseChatOptions;
|
|
||||||
|
|
||||||
FirebaseCommunityChatDataProvider({
|
|
||||||
this.firebaseChatOptions = const FirebaseChatOptions(),
|
|
||||||
FirebaseApp? app,
|
|
||||||
FirebaseUserService? firebaseUserService,
|
|
||||||
FirebaseMessageService? firebaseMessageService,
|
|
||||||
FirebaseChatService? firebaseChatService,
|
|
||||||
}) {
|
|
||||||
var appInstance = app ?? Firebase.app();
|
|
||||||
|
|
||||||
var db = FirebaseFirestore.instanceFor(app: appInstance);
|
|
||||||
var storage = FirebaseStorage.instanceFor(app: appInstance);
|
|
||||||
var auth = FirebaseAuth.instanceFor(app: appInstance);
|
|
||||||
|
|
||||||
_userService = firebaseUserService ??
|
|
||||||
FirebaseUserService(
|
|
||||||
db: db,
|
|
||||||
auth: auth,
|
|
||||||
options: firebaseChatOptions,
|
|
||||||
);
|
|
||||||
|
|
||||||
_chatService = firebaseChatService ??
|
|
||||||
FirebaseChatService(
|
|
||||||
db: db,
|
|
||||||
storage: storage,
|
|
||||||
userService: _userService,
|
|
||||||
options: firebaseChatOptions,
|
|
||||||
);
|
|
||||||
|
|
||||||
_messageService = firebaseMessageService ??
|
|
||||||
FirebaseMessageService(
|
|
||||||
db: db,
|
|
||||||
storage: storage,
|
|
||||||
userService: _userService,
|
|
||||||
chatService: _chatService,
|
|
||||||
options: firebaseChatOptions,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<List<ChatMessageModel>> getMessagesStream() =>
|
|
||||||
_messageService.getMessagesStream();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<ChatUserModel>> getChatUsers() => _userService.getAllUsers();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<List<ChatModel>> getChatsStream() => _chatService.getChatsStream();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> sendTextMessage(String text) =>
|
|
||||||
_messageService.sendTextMessage(text);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> sendImageMessage(Uint8List image) =>
|
|
||||||
_messageService.sendImageMessage(image);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> setChat(ChatModel chat) async =>
|
|
||||||
await _messageService.setChat(chat);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteChat(ChatModel chat) async =>
|
|
||||||
await _chatService.deleteChat(chat);
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,31 +4,38 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_storage/firebase_storage.dart';
|
import 'package:firebase_storage/firebase_storage.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_community_chat_firebase/config/firebase_chat_options.dart';
|
import 'package:flutter_community_chat_firebase/config/firebase_chat_options.dart';
|
||||||
import 'package:flutter_community_chat_firebase/dto/firebase_chat_document.dart';
|
import 'package:flutter_community_chat_firebase/dto/firebase_chat_document.dart';
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
import 'firebase_user_service.dart';
|
|
||||||
|
|
||||||
class FirebaseChatService {
|
class FirebaseChatService implements ChatService {
|
||||||
|
late FirebaseFirestore _db;
|
||||||
|
late FirebaseStorage _storage;
|
||||||
|
late ChatUserService _userService;
|
||||||
|
late FirebaseChatOptions _options;
|
||||||
|
|
||||||
FirebaseChatService({
|
FirebaseChatService({
|
||||||
required this.db,
|
required ChatUserService userService,
|
||||||
required this.storage,
|
FirebaseApp? app,
|
||||||
required this.userService,
|
FirebaseChatOptions? options,
|
||||||
required this.options,
|
}) {
|
||||||
});
|
var appInstance = app ?? Firebase.app();
|
||||||
|
|
||||||
FirebaseFirestore db;
|
_db = FirebaseFirestore.instanceFor(app: appInstance);
|
||||||
FirebaseStorage storage;
|
_storage = FirebaseStorage.instanceFor(app: appInstance);
|
||||||
FirebaseUserService userService;
|
_userService = userService;
|
||||||
FirebaseChatOptions options;
|
_options = options ?? const FirebaseChatOptions();
|
||||||
|
}
|
||||||
|
|
||||||
StreamSubscription<QuerySnapshot> _addChatSubscription(
|
StreamSubscription<QuerySnapshot> _addChatSubscription(
|
||||||
List<String> chatIds,
|
List<String> chatIds,
|
||||||
Function(List<ChatModel>) onReceivedChats,
|
Function(List<PersonalChatModel>) onReceivedChats,
|
||||||
) {
|
) {
|
||||||
var snapshots = db
|
var snapshots = _db
|
||||||
.collection(options.chatsCollectionName)
|
.collection(_options.chatsCollectionName)
|
||||||
.where(
|
.where(
|
||||||
FieldPath.documentId,
|
FieldPath.documentId,
|
||||||
whereIn: chatIds,
|
whereIn: chatIds,
|
||||||
|
@ -41,8 +48,8 @@ class FirebaseChatService {
|
||||||
.snapshots();
|
.snapshots();
|
||||||
|
|
||||||
return snapshots.listen((snapshot) async {
|
return snapshots.listen((snapshot) async {
|
||||||
var currentUser = await userService.getCurrentUser();
|
var currentUser = await _userService.getCurrentUser();
|
||||||
List<ChatModel> chats = [];
|
List<PersonalChatModel> chats = [];
|
||||||
|
|
||||||
for (var chatDoc in snapshot.docs) {
|
for (var chatDoc in snapshot.docs) {
|
||||||
var chatData = chatDoc.data();
|
var chatData = chatDoc.data();
|
||||||
|
@ -51,7 +58,7 @@ class FirebaseChatService {
|
||||||
|
|
||||||
if (chatData.lastMessage != null) {
|
if (chatData.lastMessage != null) {
|
||||||
var messageData = chatData.lastMessage!;
|
var messageData = chatData.lastMessage!;
|
||||||
var sender = await userService.getUser(messageData.sender);
|
var sender = await _userService.getUser(messageData.sender);
|
||||||
|
|
||||||
if (sender != null) {
|
if (sender != null) {
|
||||||
var timestamp = DateTime.fromMillisecondsSinceEpoch(
|
var timestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
@ -77,7 +84,7 @@ class FirebaseChatService {
|
||||||
var otherUserId = List<String>.from(chatData.users).firstWhere(
|
var otherUserId = List<String>.from(chatData.users).firstWhere(
|
||||||
(element) => element != currentUser?.id,
|
(element) => element != currentUser?.id,
|
||||||
);
|
);
|
||||||
var otherUser = await userService.getUser(otherUserId);
|
var otherUser = await _userService.getUser(otherUserId);
|
||||||
|
|
||||||
if (otherUser != null) {
|
if (otherUser != null) {
|
||||||
chats.add(
|
chats.add(
|
||||||
|
@ -117,14 +124,15 @@ class FirebaseChatService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<ChatModel>> _getSpecificChatsStream(List<String> chatIds) {
|
Stream<List<PersonalChatModel>> _getSpecificChatsStream(
|
||||||
late StreamController<List<ChatModel>> controller;
|
List<String> chatIds) {
|
||||||
|
late StreamController<List<PersonalChatModel>> controller;
|
||||||
List<StreamSubscription<QuerySnapshot>> subscriptions = [];
|
List<StreamSubscription<QuerySnapshot>> subscriptions = [];
|
||||||
var splittedChatIds = _splitChatIds(chatIds: chatIds);
|
var splittedChatIds = _splitChatIds(chatIds: chatIds);
|
||||||
|
|
||||||
controller = StreamController<List<ChatModel>>(
|
controller = StreamController<List<PersonalChatModel>>(
|
||||||
onListen: () {
|
onListen: () {
|
||||||
var chats = <int, List<ChatModel>>{};
|
var chats = <int, List<PersonalChatModel>>{};
|
||||||
|
|
||||||
for (var chatIdPair in splittedChatIds.asMap().entries) {
|
for (var chatIdPair in splittedChatIds.asMap().entries) {
|
||||||
subscriptions.add(
|
subscriptions.add(
|
||||||
|
@ -133,7 +141,7 @@ class FirebaseChatService {
|
||||||
(data) {
|
(data) {
|
||||||
chats[chatIdPair.key] = data;
|
chats[chatIdPair.key] = data;
|
||||||
|
|
||||||
List<ChatModel> mergedChats = [];
|
List<PersonalChatModel> mergedChats = [];
|
||||||
|
|
||||||
mergedChats.addAll(
|
mergedChats.addAll(
|
||||||
chats.values.expand((element) => element),
|
chats.values.expand((element) => element),
|
||||||
|
@ -160,15 +168,17 @@ class FirebaseChatService {
|
||||||
return controller.stream;
|
return controller.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<ChatModel>> getChatsStream() {
|
@override
|
||||||
late StreamController<List<ChatModel>> controller;
|
Stream<List<PersonalChatModel>> getChatsStream() {
|
||||||
|
late StreamController<List<PersonalChatModel>> controller;
|
||||||
StreamSubscription? userChatsSubscription;
|
StreamSubscription? userChatsSubscription;
|
||||||
StreamSubscription? chatsSubscription;
|
StreamSubscription? chatsSubscription;
|
||||||
controller = StreamController(
|
controller = StreamController(
|
||||||
onListen: () async {
|
onListen: () async {
|
||||||
var currentUser = await userService.getCurrentUser();
|
debugPrint('Start listening to chats');
|
||||||
userChatsSubscription = db
|
var currentUser = await _userService.getCurrentUser();
|
||||||
.collection(options.usersCollectionName)
|
userChatsSubscription = _db
|
||||||
|
.collection(_options.usersCollectionName)
|
||||||
.doc(currentUser?.id)
|
.doc(currentUser?.id)
|
||||||
.collection('chats')
|
.collection('chats')
|
||||||
.snapshots()
|
.snapshots()
|
||||||
|
@ -185,37 +195,34 @@ class FirebaseChatService {
|
||||||
onCancel: () {
|
onCancel: () {
|
||||||
chatsSubscription?.cancel();
|
chatsSubscription?.cancel();
|
||||||
userChatsSubscription?.cancel();
|
userChatsSubscription?.cancel();
|
||||||
|
debugPrint('Stop listening to chats');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return controller.stream;
|
return controller.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ChatModel?> getChatByUser(ChatUserModel user) async {
|
@override
|
||||||
var currentUser = await userService.getCurrentUser();
|
Future<PersonalChatModel> getOrCreateChatByUser(ChatUserModel user) async {
|
||||||
var chatCollection = await db
|
var currentUser = await _userService.getCurrentUser();
|
||||||
.collection(options.usersCollectionName)
|
var collection = await _db
|
||||||
|
.collection(_options.usersCollectionName)
|
||||||
.doc(currentUser?.id)
|
.doc(currentUser?.id)
|
||||||
.collection('chats')
|
.collection('chats')
|
||||||
|
.where('users', arrayContains: user.id)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
for (var element in chatCollection.docs) {
|
var doc = collection.docs.isNotEmpty ? collection.docs.first : null;
|
||||||
var data = element.data();
|
|
||||||
if (data.containsKey('users') && data['users'] is List) {
|
|
||||||
if (data['users'].contains(user.id)) {
|
|
||||||
return PersonalChatModel(
|
return PersonalChatModel(
|
||||||
id: element.id,
|
id: doc?.id,
|
||||||
user: user,
|
user: user,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@override
|
||||||
Future<void> deleteChat(ChatModel chat) async {
|
Future<void> deleteChat(ChatModel chat) async {
|
||||||
var chatCollection = await db
|
var chatCollection = await _db
|
||||||
.collection(options.chatsCollectionName)
|
.collection(_options.chatsCollectionName)
|
||||||
.doc(chat.id)
|
.doc(chat.id)
|
||||||
.withConverter(
|
.withConverter(
|
||||||
fromFirestore: (snapshot, _) =>
|
fromFirestore: (snapshot, _) =>
|
||||||
|
@ -228,8 +235,8 @@ class FirebaseChatService {
|
||||||
|
|
||||||
if (chatData != null) {
|
if (chatData != null) {
|
||||||
for (var userId in chatData.users) {
|
for (var userId in chatData.users) {
|
||||||
db
|
_db
|
||||||
.collection(options.usersCollectionName)
|
.collection(_options.usersCollectionName)
|
||||||
.doc(userId)
|
.doc(userId)
|
||||||
.collection('chats')
|
.collection('chats')
|
||||||
.doc(chat.id)
|
.doc(chat.id)
|
||||||
|
@ -237,9 +244,12 @@ class FirebaseChatService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chat.id != null) {
|
if (chat.id != null) {
|
||||||
await db.collection(options.chatsCollectionName).doc(chat.id).delete();
|
await _db
|
||||||
await storage
|
.collection(_options.chatsCollectionName)
|
||||||
.ref(options.chatsCollectionName)
|
.doc(chat.id)
|
||||||
|
.delete();
|
||||||
|
await _storage
|
||||||
|
.ref(_options.chatsCollectionName)
|
||||||
.child(chat.id!)
|
.child(chat.id!)
|
||||||
.listAll()
|
.listAll()
|
||||||
.then((value) {
|
.then((value) {
|
||||||
|
@ -250,4 +260,48 @@ class FirebaseChatService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PersonalChatModel> storeChatIfNot(PersonalChatModel chat) async {
|
||||||
|
if (chat.id == null) {
|
||||||
|
var currentUser = await _userService.getCurrentUser();
|
||||||
|
|
||||||
|
if (currentUser?.id == null || chat.user.id == null) {
|
||||||
|
return chat;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> userIds = [
|
||||||
|
currentUser!.id!,
|
||||||
|
chat.user.id!,
|
||||||
|
];
|
||||||
|
|
||||||
|
var reference = await _db
|
||||||
|
.collection(_options.chatsCollectionName)
|
||||||
|
.withConverter(
|
||||||
|
fromFirestore: (snapshot, _) =>
|
||||||
|
FirebaseChatDocument.fromJson(snapshot.data()!, snapshot.id),
|
||||||
|
toFirestore: (chat, _) => chat.toJson(),
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
FirebaseChatDocument(
|
||||||
|
personal: true,
|
||||||
|
users: userIds,
|
||||||
|
lastUsed: Timestamp.now(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var userId in userIds) {
|
||||||
|
await _db
|
||||||
|
.collection(_options.usersCollectionName)
|
||||||
|
.doc(userId)
|
||||||
|
.collection('chats')
|
||||||
|
.doc(reference.id)
|
||||||
|
.set({'users': userIds});
|
||||||
|
}
|
||||||
|
|
||||||
|
chat.id = reference.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,56 +5,40 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_storage/firebase_storage.dart';
|
import 'package:firebase_storage/firebase_storage.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_community_chat_firebase/config/firebase_chat_options.dart';
|
import 'package:flutter_community_chat_firebase/config/firebase_chat_options.dart';
|
||||||
import 'package:flutter_community_chat_firebase/dto/firebase_chat_document.dart';
|
|
||||||
import 'package:flutter_community_chat_firebase/dto/firebase_message_document.dart';
|
import 'package:flutter_community_chat_firebase/dto/firebase_message_document.dart';
|
||||||
import 'package:flutter_community_chat_firebase/service/firebase_chat_service.dart';
|
|
||||||
import 'package:flutter_community_chat_firebase/service/firebase_user_service.dart';
|
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class FirebaseMessageService {
|
class FirebaseMessageService implements MessageService {
|
||||||
FirebaseMessageService({
|
late final FirebaseFirestore _db;
|
||||||
required this.db,
|
late final FirebaseStorage _storage;
|
||||||
required this.storage,
|
late final ChatUserService _userService;
|
||||||
required this.userService,
|
late FirebaseChatOptions _options;
|
||||||
required this.chatService,
|
|
||||||
required this.options,
|
|
||||||
});
|
|
||||||
|
|
||||||
final FirebaseFirestore db;
|
|
||||||
final FirebaseStorage storage;
|
|
||||||
final FirebaseUserService userService;
|
|
||||||
final FirebaseChatService chatService;
|
|
||||||
FirebaseChatOptions options;
|
|
||||||
late StreamController<List<ChatMessageModel>> _controller;
|
late StreamController<List<ChatMessageModel>> _controller;
|
||||||
StreamSubscription<QuerySnapshot>? _subscription;
|
StreamSubscription<QuerySnapshot>? _subscription;
|
||||||
ChatModel? _chat;
|
|
||||||
|
|
||||||
Future<void> setChat(ChatModel chat) async {
|
FirebaseMessageService({
|
||||||
if (chat.id == null && chat is PersonalChatModel) {
|
required ChatUserService userService,
|
||||||
var chatWithUser = await chatService.getChatByUser(chat.user);
|
FirebaseApp? app,
|
||||||
|
FirebaseChatOptions? options,
|
||||||
|
}) {
|
||||||
|
var appInstance = app ?? Firebase.app();
|
||||||
|
|
||||||
if (chatWithUser != null) {
|
_db = FirebaseFirestore.instanceFor(app: appInstance);
|
||||||
_chat = chatWithUser;
|
_storage = FirebaseStorage.instanceFor(app: appInstance);
|
||||||
return;
|
_userService = userService;
|
||||||
}
|
_options = options ?? const FirebaseChatOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
_chat = chat;
|
Future<void> _sendMessage(ChatModel chat, Map<String, dynamic> data) async {
|
||||||
}
|
var currentUser = await _userService.getCurrentUser();
|
||||||
|
|
||||||
Future<void> _beforeSendMessage() async {
|
if (chat.id == null || currentUser == null) {
|
||||||
if (_chat != null) {
|
|
||||||
_chat = await createChatIfNotExists(_chat!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _sendMessage(Map<String, dynamic> data) async {
|
|
||||||
var currentUser = await userService.getCurrentUser();
|
|
||||||
|
|
||||||
if (_chat?.id == null || currentUser == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,15 +48,15 @@ class FirebaseMessageService {
|
||||||
...data
|
...data
|
||||||
};
|
};
|
||||||
|
|
||||||
var chatReference = db
|
var chatReference = _db
|
||||||
.collection(
|
.collection(
|
||||||
options.chatsCollectionName,
|
_options.chatsCollectionName,
|
||||||
)
|
)
|
||||||
.doc(_chat!.id);
|
.doc(chat.id);
|
||||||
|
|
||||||
await chatReference
|
await chatReference
|
||||||
.collection(
|
.collection(
|
||||||
options.messagesCollectionName,
|
_options.messagesCollectionName,
|
||||||
)
|
)
|
||||||
.add(message);
|
.add(message);
|
||||||
|
|
||||||
|
@ -80,35 +64,54 @@ class FirebaseMessageService {
|
||||||
'last_used': DateTime.now(),
|
'last_used': DateTime.now(),
|
||||||
'last_message': message,
|
'last_message': message,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (chat.id != null && _controller.hasListener && (_subscription == null)) {
|
||||||
|
_subscription = _startListeningForMessages(chat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendTextMessage(String text) => _beforeSendMessage().then(
|
@override
|
||||||
(_) => _sendMessage({'text': text}),
|
Future<void> sendTextMessage({
|
||||||
|
required String text,
|
||||||
|
required ChatModel chat,
|
||||||
|
}) =>
|
||||||
|
_sendMessage(
|
||||||
|
chat,
|
||||||
|
{
|
||||||
|
'text': text,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> sendImageMessage(Uint8List image) => _beforeSendMessage().then(
|
@override
|
||||||
(_) {
|
Future<void> sendImageMessage({
|
||||||
if (_chat?.id == null) {
|
required ChatModel chat,
|
||||||
return null;
|
required Uint8List image,
|
||||||
|
}) async {
|
||||||
|
if (chat.id == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ref = storage.ref(
|
var ref = _storage
|
||||||
'${options.chatsCollectionName}/${_chat!.id}/${const Uuid().v4()}');
|
.ref('${_options.chatsCollectionName}/${chat.id}/${const Uuid().v4()}');
|
||||||
|
|
||||||
return ref.putData(image).then(
|
return ref.putData(image).then(
|
||||||
(_) => ref.getDownloadURL().then(
|
(_) => ref.getDownloadURL().then(
|
||||||
(url) {
|
(url) {
|
||||||
_sendMessage({'image_url': url});
|
_sendMessage(
|
||||||
|
chat,
|
||||||
|
{
|
||||||
|
'image_url': url,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
Query<FirebaseMessageDocument> _getMessagesQuery(String chatId) => db
|
Query<FirebaseMessageDocument> _getMessagesQuery(ChatModel chat) => _db
|
||||||
.collection(options.chatsCollectionName)
|
.collection(_options.chatsCollectionName)
|
||||||
.doc(chatId)
|
.doc(chat.id)
|
||||||
.collection(options.messagesCollectionName)
|
.collection(_options.messagesCollectionName)
|
||||||
.orderBy('timestamp', descending: false)
|
.orderBy('timestamp', descending: false)
|
||||||
.withConverter<FirebaseMessageDocument>(
|
.withConverter<FirebaseMessageDocument>(
|
||||||
fromFirestore: (snapshot, _) =>
|
fromFirestore: (snapshot, _) =>
|
||||||
|
@ -116,15 +119,18 @@ class FirebaseMessageService {
|
||||||
toFirestore: (user, _) => user.toJson(),
|
toFirestore: (user, _) => user.toJson(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Stream<List<ChatMessageModel>> getMessagesStream() {
|
@override
|
||||||
|
Stream<List<ChatMessageModel>> getMessagesStream(ChatModel chat) {
|
||||||
_controller = StreamController<List<ChatMessageModel>>(
|
_controller = StreamController<List<ChatMessageModel>>(
|
||||||
onListen: () {
|
onListen: () {
|
||||||
if (_chat?.id != null) {
|
if (chat.id != null) {
|
||||||
_subscription = _startListeningForMessages(_chat!);
|
_subscription = _startListeningForMessages(chat);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCancel: () {
|
onCancel: () {
|
||||||
_subscription?.cancel();
|
_subscription?.cancel();
|
||||||
|
_subscription = null;
|
||||||
|
debugPrint('Canceling messages stream');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -132,7 +138,9 @@ class FirebaseMessageService {
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamSubscription<QuerySnapshot> _startListeningForMessages(ChatModel chat) {
|
StreamSubscription<QuerySnapshot> _startListeningForMessages(ChatModel chat) {
|
||||||
var snapshots = _getMessagesQuery(chat.id!).snapshots();
|
debugPrint('Start listening for messages in chat ${chat.id}');
|
||||||
|
|
||||||
|
var snapshots = _getMessagesQuery(chat).snapshots();
|
||||||
|
|
||||||
return snapshots.listen(
|
return snapshots.listen(
|
||||||
(snapshot) async {
|
(snapshot) async {
|
||||||
|
@ -141,7 +149,7 @@ class FirebaseMessageService {
|
||||||
for (var messageDoc in snapshot.docs) {
|
for (var messageDoc in snapshot.docs) {
|
||||||
var messageData = messageDoc.data();
|
var messageData = messageDoc.data();
|
||||||
|
|
||||||
var sender = await userService.getUser(messageData.sender);
|
var sender = await _userService.getUser(messageData.sender);
|
||||||
|
|
||||||
if (sender != null) {
|
if (sender != null) {
|
||||||
var timestamp = DateTime.fromMillisecondsSinceEpoch(
|
var timestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
@ -168,54 +176,4 @@ class FirebaseMessageService {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ChatModel?> createChatIfNotExists(ChatModel chat) async {
|
|
||||||
if (chat.id == null) {
|
|
||||||
if (chat is! PersonalChatModel) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentUser = await userService.getCurrentUser();
|
|
||||||
|
|
||||||
if (currentUser?.id == null || chat.user.id == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> userIds = [
|
|
||||||
currentUser!.id!,
|
|
||||||
chat.user.id!,
|
|
||||||
];
|
|
||||||
|
|
||||||
var reference = await db
|
|
||||||
.collection(options.chatsCollectionName)
|
|
||||||
.withConverter(
|
|
||||||
fromFirestore: (snapshot, _) =>
|
|
||||||
FirebaseChatDocument.fromJson(snapshot.data()!, snapshot.id),
|
|
||||||
toFirestore: (chat, _) => chat.toJson(),
|
|
||||||
)
|
|
||||||
.add(
|
|
||||||
FirebaseChatDocument(
|
|
||||||
personal: true,
|
|
||||||
users: userIds,
|
|
||||||
lastUsed: Timestamp.now(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (var userId in userIds) {
|
|
||||||
await db
|
|
||||||
.collection(options.usersCollectionName)
|
|
||||||
.doc(userId)
|
|
||||||
.collection('chats')
|
|
||||||
.doc(reference.id)
|
|
||||||
.set({'users': userIds});
|
|
||||||
}
|
|
||||||
|
|
||||||
chat.id = reference.id;
|
|
||||||
|
|
||||||
_subscription?.cancel();
|
|
||||||
_subscription = _startListeningForMessages(chat);
|
|
||||||
}
|
|
||||||
|
|
||||||
return chat;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,25 +4,32 @@
|
||||||
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter_community_chat_firebase/config/firebase_chat_options.dart';
|
import 'package:flutter_community_chat_firebase/config/firebase_chat_options.dart';
|
||||||
import 'package:flutter_community_chat_firebase/dto/firebase_user_document.dart';
|
import 'package:flutter_community_chat_firebase/dto/firebase_user_document.dart';
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
|
|
||||||
class FirebaseUserService {
|
class FirebaseUserService implements ChatUserService {
|
||||||
FirebaseUserService({
|
FirebaseUserService({
|
||||||
required this.db,
|
FirebaseApp? app,
|
||||||
required this.auth,
|
FirebaseChatOptions? options,
|
||||||
required this.options,
|
}) {
|
||||||
});
|
var appInstance = app ?? Firebase.app();
|
||||||
|
|
||||||
|
_db = FirebaseFirestore.instanceFor(app: appInstance);
|
||||||
|
_auth = FirebaseAuth.instanceFor(app: appInstance);
|
||||||
|
_options = options ?? const FirebaseChatOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
late FirebaseFirestore _db;
|
||||||
|
late FirebaseAuth _auth;
|
||||||
|
late FirebaseChatOptions _options;
|
||||||
|
|
||||||
FirebaseFirestore db;
|
|
||||||
FirebaseAuth auth;
|
|
||||||
FirebaseChatOptions options;
|
|
||||||
ChatUserModel? _currentUser;
|
ChatUserModel? _currentUser;
|
||||||
final Map<String, ChatUserModel> _users = {};
|
final Map<String, ChatUserModel> _users = {};
|
||||||
|
|
||||||
CollectionReference<FirebaseUserDocument> get _userCollection => db
|
CollectionReference<FirebaseUserDocument> get _userCollection => _db
|
||||||
.collection(options.usersCollectionName)
|
.collection(_options.usersCollectionName)
|
||||||
.withConverter<FirebaseUserDocument>(
|
.withConverter<FirebaseUserDocument>(
|
||||||
fromFirestore: (snapshot, _) => FirebaseUserDocument.fromJson(
|
fromFirestore: (snapshot, _) => FirebaseUserDocument.fromJson(
|
||||||
snapshot.data()!,
|
snapshot.data()!,
|
||||||
|
@ -31,6 +38,7 @@ class FirebaseUserService {
|
||||||
toFirestore: (user, _) => user.toJson(),
|
toFirestore: (user, _) => user.toJson(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
Future<ChatUserModel?> getUser(String id) async {
|
Future<ChatUserModel?> getUser(String id) async {
|
||||||
if (_users.containsKey(id)) {
|
if (_users.containsKey(id)) {
|
||||||
return _users[id]!;
|
return _users[id]!;
|
||||||
|
@ -54,11 +62,13 @@ class FirebaseUserService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
Future<ChatUserModel?> getCurrentUser() async =>
|
Future<ChatUserModel?> getCurrentUser() async =>
|
||||||
_currentUser == null && auth.currentUser?.uid != null
|
_currentUser == null && _auth.currentUser?.uid != null
|
||||||
? _currentUser = await getUser(auth.currentUser!.uid)
|
? _currentUser = await getUser(_auth.currentUser!.uid)
|
||||||
: _currentUser;
|
: _currentUser;
|
||||||
|
|
||||||
|
@override
|
||||||
Future<List<ChatUserModel>> getAllUsers() async {
|
Future<List<ChatUserModel>> getAllUsers() async {
|
||||||
var currentUser = await getCurrentUser();
|
var currentUser = await getCurrentUser();
|
||||||
|
|
||||||
|
@ -67,13 +77,6 @@ class FirebaseUserService {
|
||||||
isNotEqualTo: currentUser?.id,
|
isNotEqualTo: currentUser?.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.userFilter != null) {
|
|
||||||
query = query.where(
|
|
||||||
options.userFilter!.field,
|
|
||||||
isEqualTo: options.userFilter!.expectedValue,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = await query.get();
|
var data = await query.get();
|
||||||
|
|
||||||
return data.docs.map((user) {
|
return data.docs.map((user) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ packages:
|
||||||
name: _flutterfire_internals
|
name: _flutterfire_internals
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.9"
|
version: "1.0.10"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -84,21 +84,21 @@ packages:
|
||||||
name: cloud_firestore
|
name: cloud_firestore
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.2.0"
|
||||||
cloud_firestore_platform_interface:
|
cloud_firestore_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cloud_firestore_platform_interface
|
name: cloud_firestore_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.9.0"
|
version: "5.9.1"
|
||||||
cloud_firestore_web:
|
cloud_firestore_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cloud_firestore_web
|
name: cloud_firestore_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.1"
|
||||||
code_builder:
|
code_builder:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -154,28 +154,28 @@ packages:
|
||||||
name: firebase_auth
|
name: firebase_auth
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.4"
|
version: "4.2.1"
|
||||||
firebase_auth_platform_interface:
|
firebase_auth_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_auth_platform_interface
|
name: firebase_auth_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.11.3"
|
version: "6.11.5"
|
||||||
firebase_auth_web:
|
firebase_auth_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_auth_web
|
name: firebase_auth_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.3"
|
version: "5.2.1"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.4.0"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -189,28 +189,28 @@ packages:
|
||||||
name: firebase_core_web
|
name: firebase_core_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.2"
|
||||||
firebase_storage:
|
firebase_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_storage
|
name: firebase_storage
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.6"
|
version: "11.0.8"
|
||||||
firebase_storage_platform_interface:
|
firebase_storage_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_storage_platform_interface
|
name: firebase_storage_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.24"
|
version: "4.1.25"
|
||||||
firebase_storage_web:
|
firebase_storage_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_storage_web
|
name: firebase_storage_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.16"
|
version: "3.3.17"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -228,7 +228,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "packages/flutter_community_chat_interface"
|
path: "packages/flutter_community_chat_interface"
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: "6a9e88ec8d07118e1543b1a82aa5482d6832cbf8"
|
resolved-ref: c644e1affb1dd4f570bf0e4ae2e950f5e9d83c37
|
||||||
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
|
|
@ -4,11 +4,6 @@
|
||||||
|
|
||||||
library flutter_community_chat_interface;
|
library flutter_community_chat_interface;
|
||||||
|
|
||||||
export 'src/community_chat_interface.dart';
|
export 'package:flutter_community_chat_interface/src/chat_data_provider.dart';
|
||||||
export 'src/model/chat.dart';
|
export 'package:flutter_community_chat_interface/src/model/model.dart';
|
||||||
export 'src/model/chat_image_message.dart';
|
export 'package:flutter_community_chat_interface/src/service/service.dart';
|
||||||
export 'src/model/chat_message.dart';
|
|
||||||
export 'src/model/chat_text_message.dart';
|
|
||||||
export 'src/model/chat_user.dart';
|
|
||||||
export 'src/model/group_chat.dart';
|
|
||||||
export 'src/model/personal_chat.dart';
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
|
import 'package:flutter_data_interface/flutter_data_interface.dart';
|
||||||
|
|
||||||
|
class ChatDataProvider extends DataInterface {
|
||||||
|
ChatDataProvider({
|
||||||
|
required this.chatService,
|
||||||
|
required this.userService,
|
||||||
|
required this.messageService,
|
||||||
|
}) : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
final ChatUserService userService;
|
||||||
|
final ChatService chatService;
|
||||||
|
final MessageService messageService;
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
|
||||||
import 'package:flutter_data_interface/flutter_data_interface.dart';
|
|
||||||
|
|
||||||
abstract class CommunityChatInterface extends DataInterface {
|
|
||||||
CommunityChatInterface() : super(token: _token);
|
|
||||||
|
|
||||||
static final Object _token = Object();
|
|
||||||
|
|
||||||
Future<void> setChat(ChatModel chat);
|
|
||||||
Future<void> sendTextMessage(String text);
|
|
||||||
Future<void> sendImageMessage(Uint8List image);
|
|
||||||
Stream<List<ChatMessageModel>> getMessagesStream();
|
|
||||||
Stream<List<ChatModel>> getChatsStream();
|
|
||||||
Future<List<ChatUserModel>> getChatUsers();
|
|
||||||
Future<void> deleteChat(ChatModel chat);
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export 'chat.dart';
|
||||||
|
export 'chat_image_message.dart';
|
||||||
|
export 'chat_text_message.dart';
|
||||||
|
export 'chat_user.dart';
|
||||||
|
export 'group_chat.dart';
|
||||||
|
export 'personal_chat.dart';
|
||||||
|
export 'chat_message.dart';
|
|
@ -0,0 +1,8 @@
|
||||||
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
|
|
||||||
|
abstract class ChatService {
|
||||||
|
Stream<List<PersonalChatModel>> getChatsStream();
|
||||||
|
Future<PersonalChatModel> getOrCreateChatByUser(ChatUserModel user);
|
||||||
|
Future<void> deleteChat(PersonalChatModel chat);
|
||||||
|
Future<PersonalChatModel> storeChatIfNot(PersonalChatModel chat);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
|
|
||||||
|
abstract class MessageService {
|
||||||
|
Future<void> sendTextMessage({
|
||||||
|
required ChatModel chat,
|
||||||
|
required String text,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> sendImageMessage({
|
||||||
|
required ChatModel chat,
|
||||||
|
required Uint8List image,
|
||||||
|
});
|
||||||
|
|
||||||
|
Stream<List<ChatMessageModel>> getMessagesStream(
|
||||||
|
ChatModel chat,
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export 'chat_service.dart';
|
||||||
|
export 'user_service.dart';
|
||||||
|
export 'message_service.dart';
|
|
@ -0,0 +1,7 @@
|
||||||
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
|
|
||||||
|
abstract class ChatUserService {
|
||||||
|
Future<ChatUserModel?> getUser(String id);
|
||||||
|
Future<ChatUserModel?> getCurrentUser();
|
||||||
|
Future<List<ChatUserModel>> getAllUsers();
|
||||||
|
}
|
|
@ -161,7 +161,7 @@ packages:
|
||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.13"
|
version: "0.12.14"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -196,7 +196,7 @@ packages:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.3"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -257,7 +257,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.16"
|
version: "0.4.17"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -113,6 +113,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
|
cross_file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cross_file
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.3+2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -186,7 +193,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "packages/flutter_community_chat_interface"
|
path: "packages/flutter_community_chat_interface"
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: "6a9e88ec8d07118e1543b1a82aa5482d6832cbf8"
|
resolved-ref: c644e1affb1dd4f570bf0e4ae2e950f5e9d83c37
|
||||||
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
@ -206,6 +213,15 @@ packages:
|
||||||
url: "https://github.com/Iconica-Development/flutter_data_interface.git"
|
url: "https://github.com/Iconica-Development/flutter_data_interface.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
flutter_image_picker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: "1.0.3"
|
||||||
|
resolved-ref: "20814755cca74296600a0ae3e016e46979e66a7e"
|
||||||
|
url: "https://github.com/Iconica-Development/flutter_image_picker"
|
||||||
|
source: git
|
||||||
|
version: "1.0.3"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -213,11 +229,23 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_plugin_android_lifecycle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_plugin_android_lifecycle
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -239,6 +267,41 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
image_picker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.6"
|
||||||
|
image_picker_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.5+3"
|
||||||
|
image_picker_for_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_for_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.10"
|
||||||
|
image_picker_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_ios
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.6+1"
|
||||||
|
image_picker_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -246,6 +309,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.0"
|
version: "0.17.0"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.4"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -425,7 +495,7 @@ packages:
|
||||||
name: sqflite
|
name: sqflite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0+3"
|
version: "2.2.2"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -16,9 +16,9 @@ class ChatBottom extends StatefulWidget {
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Future<void> Function(ChatModel chat, String text) onMessageSubmit;
|
final Future<void> Function(String text) onMessageSubmit;
|
||||||
final TextInputBuilder messageInputBuilder;
|
final TextInputBuilder messageInputBuilder;
|
||||||
final Function(ChatModel)? onPressSelectImage;
|
final VoidCallback? onPressSelectImage;
|
||||||
final ChatModel chat;
|
final ChatModel chat;
|
||||||
final ChatTranslations translations;
|
final ChatTranslations translations;
|
||||||
|
|
||||||
|
@ -44,9 +44,8 @@ class _ChatBottomState extends State<ChatBottom> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (widget.onPressSelectImage != null)
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => widget.onPressSelectImage!(widget.chat),
|
onPressed: widget.onPressSelectImage,
|
||||||
icon: const Icon(Icons.image),
|
icon: const Icon(Icons.image),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -54,7 +53,7 @@ class _ChatBottomState extends State<ChatBottom> {
|
||||||
var value = _textEditingController.text;
|
var value = _textEditingController.text;
|
||||||
|
|
||||||
if (value.isNotEmpty) {
|
if (value.isNotEmpty) {
|
||||||
widget.onMessageSubmit(widget.chat, value);
|
widget.onMessageSubmit(value);
|
||||||
_textEditingController.clear();
|
_textEditingController.clear();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_community_chat/flutter_community_chat.dart';
|
import 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
||||||
|
|
||||||
SnackBar getImageLoadingSnackbar(ChatTranslations translations) => SnackBar(
|
SnackBar getImageLoadingSnackbar(ChatTranslations translations) => SnackBar(
|
||||||
duration: const Duration(minutes: 1),
|
duration: const Duration(minutes: 1),
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
import 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
import 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
||||||
import 'package:flutter_community_chat_view/src/components/chat_image.dart';
|
import 'package:flutter_community_chat_view/src/components/chat_image.dart';
|
||||||
|
import 'package:flutter_image_picker/flutter_image_picker.dart';
|
||||||
|
|
||||||
class ChatOptions {
|
class ChatOptions {
|
||||||
const ChatOptions({
|
const ChatOptions({
|
||||||
|
@ -13,7 +14,6 @@ class ChatOptions {
|
||||||
this.messageInputBuilder = _createMessageInput,
|
this.messageInputBuilder = _createMessageInput,
|
||||||
this.chatRowContainerBuilder = _createChatRowContainer,
|
this.chatRowContainerBuilder = _createChatRowContainer,
|
||||||
this.imagePickerContainerBuilder = _createImagePickerContainer,
|
this.imagePickerContainerBuilder = _createImagePickerContainer,
|
||||||
this.closeImagePickerButtonBuilder = _createCloseImagePickerButton,
|
|
||||||
this.scaffoldBuilder = _createScaffold,
|
this.scaffoldBuilder = _createScaffold,
|
||||||
this.userAvatarBuilder = _createUserAvatar,
|
this.userAvatarBuilder = _createUserAvatar,
|
||||||
this.noChatsPlaceholderBuilder = _createNoChatsPlaceholder,
|
this.noChatsPlaceholderBuilder = _createNoChatsPlaceholder,
|
||||||
|
@ -22,8 +22,7 @@ class ChatOptions {
|
||||||
final ButtonBuilder newChatButtonBuilder;
|
final ButtonBuilder newChatButtonBuilder;
|
||||||
final TextInputBuilder messageInputBuilder;
|
final TextInputBuilder messageInputBuilder;
|
||||||
final ContainerBuilder chatRowContainerBuilder;
|
final ContainerBuilder chatRowContainerBuilder;
|
||||||
final ContainerBuilder imagePickerContainerBuilder;
|
final ImagePickerContainerBuilder imagePickerContainerBuilder;
|
||||||
final ButtonBuilder closeImagePickerButtonBuilder;
|
|
||||||
final ScaffoldBuilder scaffoldBuilder;
|
final ScaffoldBuilder scaffoldBuilder;
|
||||||
final UserAvatarBuilder userAvatarBuilder;
|
final UserAvatarBuilder userAvatarBuilder;
|
||||||
final NoChatsPlaceholderBuilder noChatsPlaceholderBuilder;
|
final NoChatsPlaceholderBuilder noChatsPlaceholderBuilder;
|
||||||
|
@ -70,24 +69,20 @@ Widget _createChatRowContainer(
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _createImagePickerContainer(
|
Widget _createImagePickerContainer(
|
||||||
Widget imagePicker,
|
VoidCallback onClose,
|
||||||
|
ChatTranslations translations,
|
||||||
) =>
|
) =>
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
child: imagePicker,
|
child: ImagePicker(
|
||||||
);
|
customButton: ElevatedButton(
|
||||||
|
onPressed: onClose,
|
||||||
Widget _createCloseImagePickerButton(
|
|
||||||
BuildContext context,
|
|
||||||
VoidCallback onPressed,
|
|
||||||
ChatTranslations translations,
|
|
||||||
) =>
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
child: Text(
|
child: Text(
|
||||||
translations.cancelImagePickerBtn,
|
translations.cancelImagePickerBtn,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Scaffold _createScaffold(
|
Scaffold _createScaffold(
|
||||||
|
@ -138,6 +133,11 @@ typedef ContainerBuilder = Widget Function(
|
||||||
Widget child,
|
Widget child,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
typedef ImagePickerContainerBuilder = Widget Function(
|
||||||
|
VoidCallback onClose,
|
||||||
|
ChatTranslations translations,
|
||||||
|
);
|
||||||
|
|
||||||
typedef ScaffoldBuilder = Scaffold Function(
|
typedef ScaffoldBuilder = Scaffold Function(
|
||||||
AppBar appBar,
|
AppBar appBar,
|
||||||
Widget body,
|
Widget body,
|
||||||
|
|
|
@ -2,52 +2,78 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
import 'package:flutter_community_chat_interface/flutter_community_chat_interface.dart';
|
||||||
import 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
import 'package:flutter_community_chat_view/flutter_community_chat_view.dart';
|
||||||
import 'package:flutter_community_chat_view/src/components/chat_bottom.dart';
|
import 'package:flutter_community_chat_view/src/components/chat_bottom.dart';
|
||||||
import 'package:flutter_community_chat_view/src/components/chat_detail_row.dart';
|
import 'package:flutter_community_chat_view/src/components/chat_detail_row.dart';
|
||||||
|
import 'package:flutter_community_chat_view/src/components/image_loading_snackbar.dart';
|
||||||
|
|
||||||
class ChatDetailScreen extends StatelessWidget {
|
class ChatDetailScreen extends StatelessWidget {
|
||||||
const ChatDetailScreen({
|
const ChatDetailScreen({
|
||||||
required this.options,
|
required this.options,
|
||||||
required this.chat,
|
|
||||||
required this.onMessageSubmit,
|
required this.onMessageSubmit,
|
||||||
|
required this.onUploadImage,
|
||||||
this.translations = const ChatTranslations(),
|
this.translations = const ChatTranslations(),
|
||||||
|
this.chat,
|
||||||
this.chatMessages,
|
this.chatMessages,
|
||||||
this.onPressSelectImage,
|
|
||||||
this.onPressChatTitle,
|
this.onPressChatTitle,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ChatModel chat;
|
final PersonalChatModel? chat;
|
||||||
final ChatOptions options;
|
final ChatOptions options;
|
||||||
final ChatTranslations translations;
|
final ChatTranslations translations;
|
||||||
final Stream<List<ChatMessageModel>>? chatMessages;
|
final Stream<List<ChatMessageModel>>? chatMessages;
|
||||||
final Function(ChatModel)? onPressSelectImage;
|
final Future<void> Function(Uint8List image) onUploadImage;
|
||||||
final Future<void> Function(ChatModel chat, String text) onMessageSubmit;
|
final Future<void> Function(String text) onMessageSubmit;
|
||||||
final Future<void> Function(ChatModel chat)? onPressChatTitle;
|
final VoidCallback? onPressChatTitle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Scaffold(
|
Widget build(BuildContext context) {
|
||||||
|
Future<void> onPressSelectImage() => showModalBottomSheet<Uint8List?>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
options.imagePickerContainerBuilder(
|
||||||
|
() => Navigator.of(context).pop(),
|
||||||
|
translations,
|
||||||
|
),
|
||||||
|
).then(
|
||||||
|
(image) async {
|
||||||
|
var messenger = ScaffoldMessenger.of(context)
|
||||||
|
..showSnackBar(
|
||||||
|
getImageLoadingSnackbar(translations),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (image != null) {
|
||||||
|
await onUploadImage(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
messenger.hideCurrentSnackBar();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: GestureDetector(
|
title: GestureDetector(
|
||||||
onTap: () =>
|
onTap: onPressChatTitle,
|
||||||
onPressChatTitle != null ? onPressChatTitle!(chat) : {},
|
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: chat == null
|
||||||
if (chat is PersonalChatModel)
|
? []
|
||||||
|
: [
|
||||||
options.userAvatarBuilder(
|
options.userAvatarBuilder(
|
||||||
(chat as PersonalChatModel).user,
|
chat!.user,
|
||||||
36.0,
|
36.0,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 15.5),
|
padding: const EdgeInsets.only(left: 15.5),
|
||||||
child: Text(
|
child: Text(
|
||||||
(chat as PersonalChatModel).user.fullName,
|
chat!.user.fullName,
|
||||||
style: const TextStyle(fontSize: 18),
|
style: const TextStyle(fontSize: 18),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -66,7 +92,7 @@ class ChatDetailScreen extends StatelessWidget {
|
||||||
padding: const EdgeInsets.only(top: 24.0),
|
padding: const EdgeInsets.only(top: 24.0),
|
||||||
children: [
|
children: [
|
||||||
for (var message
|
for (var message
|
||||||
in (snapshot.data ?? chat.messages ?? []).reversed)
|
in (snapshot.data ?? chat?.messages ?? []).reversed)
|
||||||
ChatDetailRow(
|
ChatDetailRow(
|
||||||
message: message,
|
message: message,
|
||||||
),
|
),
|
||||||
|
@ -74,8 +100,9 @@ class ChatDetailScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (chat != null)
|
||||||
ChatBottom(
|
ChatBottom(
|
||||||
chat: chat,
|
chat: chat!,
|
||||||
messageInputBuilder: options.messageInputBuilder,
|
messageInputBuilder: options.messageInputBuilder,
|
||||||
onPressSelectImage: onPressSelectImage,
|
onPressSelectImage: onPressSelectImage,
|
||||||
onMessageSubmit: onMessageSubmit,
|
onMessageSubmit: onMessageSubmit,
|
||||||
|
@ -84,4 +111,5 @@ class ChatDetailScreen extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,10 @@ class ChatScreen extends StatefulWidget {
|
||||||
|
|
||||||
final ChatOptions options;
|
final ChatOptions options;
|
||||||
final ChatTranslations translations;
|
final ChatTranslations translations;
|
||||||
final Stream<List<ChatModel>> chats;
|
final Stream<List<PersonalChatModel>> chats;
|
||||||
final VoidCallback? onPressStartChat;
|
final VoidCallback? onPressStartChat;
|
||||||
final void Function(ChatModel chat) onDeleteChat;
|
final void Function(PersonalChatModel chat) onDeleteChat;
|
||||||
final void Function(ChatModel chat) onPressChat;
|
final void Function(PersonalChatModel chat) onPressChat;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChatScreen> createState() => _ChatScreenState();
|
State<ChatScreen> createState() => _ChatScreenState();
|
||||||
|
@ -43,11 +43,11 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.only(top: 15.0),
|
padding: const EdgeInsets.only(top: 15.0),
|
||||||
children: [
|
children: [
|
||||||
StreamBuilder<List<ChatModel>>(
|
StreamBuilder<List<PersonalChatModel>>(
|
||||||
stream: widget.chats,
|
stream: widget.chats,
|
||||||
builder: (BuildContext context, snapshot) => Column(
|
builder: (BuildContext context, snapshot) => Column(
|
||||||
children: [
|
children: [
|
||||||
for (ChatModel chat in snapshot.data ?? [])
|
for (PersonalChatModel chat in snapshot.data ?? [])
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) => Dismissible(
|
builder: (context) => Dismissible(
|
||||||
confirmDismiss: (_) => showDialog(
|
confirmDismiss: (_) => showDialog(
|
||||||
|
@ -100,15 +100,11 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||||
onTap: () => widget.onPressChat(chat),
|
onTap: () => widget.onPressChat(chat),
|
||||||
child: widget.options.chatRowContainerBuilder(
|
child: widget.options.chatRowContainerBuilder(
|
||||||
ChatRow(
|
ChatRow(
|
||||||
avatar: chat is PersonalChatModel
|
avatar: widget.options.userAvatarBuilder(
|
||||||
? widget.options.userAvatarBuilder(
|
|
||||||
chat.user,
|
chat.user,
|
||||||
40.0,
|
40.0,
|
||||||
)
|
),
|
||||||
: Container(),
|
title: chat.user.fullName,
|
||||||
title: chat is PersonalChatModel
|
|
||||||
? chat.user.fullName
|
|
||||||
: (chat as GroupChatModel).title,
|
|
||||||
subTitle: chat.lastMessage != null
|
subTitle: chat.lastMessage != null
|
||||||
? chat.lastMessage
|
? chat.lastMessage
|
||||||
is ChatTextMessageModel
|
is ChatTextMessageModel
|
||||||
|
|
|
@ -113,6 +113,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
|
cross_file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cross_file
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.3+2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -179,7 +186,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "packages/flutter_community_chat_interface"
|
path: "packages/flutter_community_chat_interface"
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: "6a9e88ec8d07118e1543b1a82aa5482d6832cbf8"
|
resolved-ref: c644e1affb1dd4f570bf0e4ae2e950f5e9d83c37
|
||||||
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
url: "https://github.com/Iconica-Development/flutter_community_chat.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
@ -192,6 +199,15 @@ packages:
|
||||||
url: "https://github.com/Iconica-Development/flutter_data_interface.git"
|
url: "https://github.com/Iconica-Development/flutter_data_interface.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
flutter_image_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: "1.0.3"
|
||||||
|
resolved-ref: "20814755cca74296600a0ae3e016e46979e66a7e"
|
||||||
|
url: "https://github.com/Iconica-Development/flutter_image_picker"
|
||||||
|
source: git
|
||||||
|
version: "1.0.3"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -199,11 +215,23 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_plugin_android_lifecycle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_plugin_android_lifecycle
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -225,6 +253,41 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
image_picker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.6"
|
||||||
|
image_picker_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.5+3"
|
||||||
|
image_picker_for_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_for_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.10"
|
||||||
|
image_picker_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_ios
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.6+1"
|
||||||
|
image_picker_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -232,6 +295,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.0"
|
version: "0.17.0"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.4"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -411,7 +481,7 @@ packages:
|
||||||
name: sqflite
|
name: sqflite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0+3"
|
version: "2.2.2"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -21,7 +21,10 @@ dependencies:
|
||||||
url: https://github.com/Iconica-Development/flutter_community_chat.git
|
url: https://github.com/Iconica-Development/flutter_community_chat.git
|
||||||
path: packages/flutter_community_chat_interface
|
path: packages/flutter_community_chat_interface
|
||||||
cached_network_image: ^3.2.2
|
cached_network_image: ^3.2.2
|
||||||
|
flutter_image_picker:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_image_picker
|
||||||
|
ref: 1.0.3
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
Loading…
Reference in a new issue