Skip to content

Commit 3c555cc

Browse files
Custom collection names (#33)
* Custom collection names * Fixes after PR Co-authored-by: Alex Demchenko <[email protected]>
1 parent 931c8da commit 3c555cc

File tree

4 files changed

+99
-23
lines changed

4 files changed

+99
-23
lines changed

lib/flutter_firebase_chat_core.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
library flutter_firebase_chat_core;
22

33
export 'src/firebase_chat_core.dart';
4+
export 'src/firebase_chat_core_config.dart';
45
export 'src/util.dart';

lib/src/firebase_chat_core.dart

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:cloud_firestore/cloud_firestore.dart';
22
import 'package:firebase_auth/firebase_auth.dart';
33
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
4+
import 'firebase_chat_core_config.dart';
45
import 'util.dart';
56

67
/// Provides access to Firebase chat data. Singleton, use
@@ -12,6 +13,13 @@ class FirebaseChatCore {
1213
});
1314
}
1415

16+
/// Config to set custom names for rooms and users collections. Also
17+
/// see [FirebaseChatCoreConfig].
18+
FirebaseChatCoreConfig config = const FirebaseChatCoreConfig(
19+
'rooms',
20+
'users',
21+
);
22+
1523
/// Current logged in user in Firebase. Does not update automatically.
1624
/// Use [FirebaseAuth.authStateChanges] to listen to the state changes.
1725
User? firebaseUser = FirebaseAuth.instance.currentUser;
@@ -20,6 +28,12 @@ class FirebaseChatCore {
2028
static final FirebaseChatCore instance =
2129
FirebaseChatCore._privateConstructor();
2230

31+
/// Sets custom config to change default names for rooms
32+
/// and users collcetions. Also see [FirebaseChatCoreConfig].
33+
void setConfig(FirebaseChatCoreConfig firebaseChatCoreConfig) {
34+
config = firebaseChatCoreConfig;
35+
}
36+
2337
/// Creates a chat group room with [users]. Creator is automatically
2438
/// added to the group. [name] is required and will be used as
2539
/// a group name. Add an optional [imageUrl] that will be a group avatar
@@ -32,10 +46,16 @@ class FirebaseChatCore {
3246
}) async {
3347
if (firebaseUser == null) return Future.error('User does not exist');
3448

35-
final currentUser = await fetchUser(firebaseUser!.uid);
49+
final currentUser = await fetchUser(
50+
firebaseUser!.uid,
51+
config.usersCollectionName,
52+
);
53+
3654
final roomUsers = [types.User.fromJson(currentUser)] + users;
3755

38-
final room = await FirebaseFirestore.instance.collection('rooms').add({
56+
final room = await FirebaseFirestore.instance
57+
.collection(config.roomsCollectionName)
58+
.add({
3959
'createdAt': FieldValue.serverTimestamp(),
4060
'imageUrl': imageUrl,
4161
'metadata': metadata,
@@ -73,11 +93,12 @@ class FirebaseChatCore {
7393
if (fu == null) return Future.error('User does not exist');
7494

7595
final query = await FirebaseFirestore.instance
76-
.collection('rooms')
96+
.collection(config.roomsCollectionName)
7797
.where('userIds', arrayContains: fu.uid)
7898
.get();
7999

80-
final rooms = await processRoomsQuery(fu, query);
100+
final rooms =
101+
await processRoomsQuery(fu, query, config.usersCollectionName);
81102

82103
try {
83104
return rooms.firstWhere((room) {
@@ -91,10 +112,16 @@ class FirebaseChatCore {
91112
// Create a new room instead
92113
}
93114

94-
final currentUser = await fetchUser(fu.uid);
115+
final currentUser = await fetchUser(
116+
fu.uid,
117+
config.usersCollectionName,
118+
);
119+
95120
final users = [types.User.fromJson(currentUser), otherUser];
96121

97-
final room = await FirebaseFirestore.instance.collection('rooms').add({
122+
final room = await FirebaseFirestore.instance
123+
.collection(config.roomsCollectionName)
124+
.add({
98125
'createdAt': FieldValue.serverTimestamp(),
99126
'imageUrl': null,
100127
'metadata': metadata,
@@ -116,7 +143,10 @@ class FirebaseChatCore {
116143
/// Creates [types.User] in Firebase to store name and avatar used on
117144
/// rooms list
118145
Future<void> createUserInFirestore(types.User user) async {
119-
await FirebaseFirestore.instance.collection('users').doc(user.id).set({
146+
await FirebaseFirestore.instance
147+
.collection(config.usersCollectionName)
148+
.doc(user.id)
149+
.set({
120150
'createdAt': FieldValue.serverTimestamp(),
121151
'firstName': user.firstName,
122152
'imageUrl': user.imageUrl,
@@ -130,13 +160,16 @@ class FirebaseChatCore {
130160

131161
/// Removes [types.User] from `users` collection in Firebase
132162
Future<void> deleteUserFromFirestore(String userId) async {
133-
await FirebaseFirestore.instance.collection('users').doc(userId).delete();
163+
await FirebaseFirestore.instance
164+
.collection(config.usersCollectionName)
165+
.doc(userId)
166+
.delete();
134167
}
135168

136169
/// Returns a stream of messages from Firebase for a given room
137170
Stream<List<types.Message>> messages(types.Room room) {
138171
return FirebaseFirestore.instance
139-
.collection('rooms/${room.id}/messages')
172+
.collection('${config.roomsCollectionName}/${room.id}/messages')
140173
.orderBy('createdAt', descending: true)
141174
.snapshots()
142175
.map(
@@ -169,10 +202,12 @@ class FirebaseChatCore {
169202
if (fu == null) return const Stream.empty();
170203

171204
return FirebaseFirestore.instance
172-
.collection('rooms')
205+
.collection(config.roomsCollectionName)
173206
.doc(roomId)
174207
.snapshots()
175-
.asyncMap((doc) => processRoomDocument(doc, fu));
208+
.asyncMap(
209+
(doc) => processRoomDocument(doc, fu, config.usersCollectionName),
210+
);
176211
}
177212

178213
/// Returns a stream of rooms from Firebase. Only rooms where current
@@ -192,16 +227,20 @@ class FirebaseChatCore {
192227

193228
final collection = orderByUpdatedAt
194229
? FirebaseFirestore.instance
195-
.collection('rooms')
230+
.collection(config.roomsCollectionName)
196231
.where('userIds', arrayContains: fu.uid)
197232
.orderBy('updatedAt', descending: true)
198233
: FirebaseFirestore.instance
199-
.collection('rooms')
234+
.collection(config.roomsCollectionName)
200235
.where('userIds', arrayContains: fu.uid);
201236

202-
return collection
203-
.snapshots()
204-
.asyncMap((query) => processRoomsQuery(fu, query));
237+
return collection.snapshots().asyncMap(
238+
(query) => processRoomsQuery(
239+
fu,
240+
query,
241+
config.usersCollectionName,
242+
),
243+
);
205244
}
206245

207246
/// Sends a message to the Firestore. Accepts any partial message and a
@@ -246,7 +285,7 @@ class FirebaseChatCore {
246285
messageMap['updatedAt'] = FieldValue.serverTimestamp();
247286

248287
await FirebaseFirestore.instance
249-
.collection('rooms/$roomId/messages')
288+
.collection('${config.roomsCollectionName}/$roomId/messages')
250289
.add(messageMap);
251290
}
252291
}
@@ -264,15 +303,18 @@ class FirebaseChatCore {
264303
messageMap['updatedAt'] = FieldValue.serverTimestamp();
265304

266305
await FirebaseFirestore.instance
267-
.collection('rooms/$roomId/messages')
306+
.collection('${config.roomsCollectionName}/$roomId/messages')
268307
.doc(message.id)
269308
.update(messageMap);
270309
}
271310

272311
/// Returns a stream of all users from Firebase
273312
Stream<List<types.User>> users() {
274313
if (firebaseUser == null) return const Stream.empty();
275-
return FirebaseFirestore.instance.collection('users').snapshots().map(
314+
return FirebaseFirestore.instance
315+
.collection(config.usersCollectionName)
316+
.snapshots()
317+
.map(
276318
(snapshot) => snapshot.docs.fold<List<types.User>>(
277319
[],
278320
(previousValue, doc) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'package:meta/meta.dart';
2+
3+
/// Class that represents the chat config. Can be used for setting custom names
4+
/// for rooms and users collections. Call [FirebaseChatCore.instance.setConfig]
5+
/// before doing anything else with [FirebaseChatCore.instance] if you want to
6+
/// change the default collection names. When using custom names don't forget
7+
/// to update your security rules and indexes.
8+
@immutable
9+
class FirebaseChatCoreConfig {
10+
const FirebaseChatCoreConfig(
11+
this.roomsCollectionName,
12+
this.usersCollectionName,
13+
);
14+
15+
/// Property to set rooms collection name
16+
final String roomsCollectionName;
17+
18+
/// Property to set users collection name
19+
final String usersCollectionName;
20+
}

lib/src/util.dart

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@ extension RoomTypeToShortString on types.RoomType {
1919
}
2020

2121
/// Fetches user from Firebase and returns a promise
22-
Future<Map<String, dynamic>> fetchUser(String userId, {String? role}) async {
23-
final doc =
24-
await FirebaseFirestore.instance.collection('users').doc(userId).get();
22+
Future<Map<String, dynamic>> fetchUser(
23+
String userId,
24+
String usersCollectionName, {
25+
String? role,
26+
}) async {
27+
final doc = await FirebaseFirestore.instance
28+
.collection(usersCollectionName)
29+
.doc(userId)
30+
.get();
2531

2632
final data = doc.data()!;
2733

@@ -39,9 +45,14 @@ Future<Map<String, dynamic>> fetchUser(String userId, {String? role}) async {
3945
Future<List<types.Room>> processRoomsQuery(
4046
User firebaseUser,
4147
QuerySnapshot<Map<String, dynamic>> query,
48+
String usersCollectionName,
4249
) async {
4350
final futures = query.docs.map(
44-
(doc) => processRoomDocument(doc, firebaseUser),
51+
(doc) => processRoomDocument(
52+
doc,
53+
firebaseUser,
54+
usersCollectionName,
55+
),
4556
);
4657

4758
return await Future.wait(futures);
@@ -51,6 +62,7 @@ Future<List<types.Room>> processRoomsQuery(
5162
Future<types.Room> processRoomDocument(
5263
DocumentSnapshot<Map<String, dynamic>> doc,
5364
User firebaseUser,
65+
String usersCollectionName,
5466
) async {
5567
final data = doc.data()!;
5668

@@ -68,6 +80,7 @@ Future<types.Room> processRoomDocument(
6880
userIds.map(
6981
(userId) => fetchUser(
7082
userId as String,
83+
usersCollectionName,
7184
role: userRoles?[userId] as String?,
7285
),
7386
),

0 commit comments

Comments
 (0)