Skip to content

Commit 84b2994

Browse files
authored
feat(persistence): add support for location persistence (#2319)
1 parent a32d298 commit 84b2994

22 files changed

+7439
-5524
lines changed

packages/stream_chat/lib/src/db/chat_persistence_client.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:stream_chat/src/core/models/channel_state.dart';
77
import 'package:stream_chat/src/core/models/draft.dart';
88
import 'package:stream_chat/src/core/models/event.dart';
99
import 'package:stream_chat/src/core/models/filter.dart';
10+
import 'package:stream_chat/src/core/models/location.dart';
1011
import 'package:stream_chat/src/core/models/member.dart';
1112
import 'package:stream_chat/src/core/models/message.dart';
1213
import 'package:stream_chat/src/core/models/poll.dart';
@@ -83,6 +84,12 @@ abstract class ChatPersistenceClient {
8384
/// [parentId] for thread messages.
8485
Future<Draft?> getDraftMessageByCid(String cid, {String? parentId});
8586

87+
/// Get stored [Location]s by providing channel [cid]
88+
Future<List<Location>> getLocationsByCid(String cid);
89+
90+
/// Get stored [Location] by providing [messageId]
91+
Future<Location?> getLocationByMessageId(String messageId);
92+
8693
/// Get [ChannelState] data by providing channel [cid]
8794
Future<ChannelState> getChannelStateByCid(
8895
String cid, {
@@ -168,6 +175,12 @@ abstract class ChatPersistenceClient {
168175
/// [DraftMessages.parentId].
169176
Future<void> deleteDraftMessageByCid(String cid, {String? parentId});
170177

178+
/// Removes locations by channel [cid]
179+
Future<void> deleteLocationsByCid(String cid);
180+
181+
/// Removes locations by message [messageIds]
182+
Future<void> deleteLocationsByMessageIds(List<String> messageIds);
183+
171184
/// Updates the message data of a particular channel [cid] with
172185
/// the new [messages] data
173186
Future<void> updateMessages(String cid, List<Message> messages) =>
@@ -228,6 +241,9 @@ abstract class ChatPersistenceClient {
228241
/// Updates the draft messages data with the new [draftMessages] data
229242
Future<void> updateDraftMessages(List<Draft> draftMessages);
230243

244+
/// Updates the locations data with the new [locations] data
245+
Future<void> updateLocations(List<Location> locations);
246+
231247
/// Deletes all the reactions by [messageIds]
232248
Future<void> deleteReactionsByMessageId(List<String> messageIds);
233249

@@ -292,6 +308,8 @@ abstract class ChatPersistenceClient {
292308
final drafts = <Draft>[];
293309
final draftsToDeleteCids = <String>[];
294310

311+
final locations = <Location>[];
312+
295313
for (final state in channelStates) {
296314
final channel = state.channel;
297315
// Continue if channel is not available.
@@ -342,6 +360,11 @@ abstract class ChatPersistenceClient {
342360
...?pinnedMessages?.map((it) => it.draft),
343361
].nonNulls);
344362

363+
locations.addAll([
364+
...?messages?.map((it) => it.sharedLocation),
365+
...?pinnedMessages?.map((it) => it.sharedLocation),
366+
].nonNulls);
367+
345368
users.addAll([
346369
channel.createdBy,
347370
...?messages?.map((it) => it.user),
@@ -386,6 +409,7 @@ abstract class ChatPersistenceClient {
386409
updatePinnedMessageReactions(pinnedReactions),
387410
updatePollVotes(pollVotes),
388411
updateDraftMessages(drafts),
412+
updateLocations(locations),
389413
]);
390414
}
391415

packages/stream_chat/test/src/db/chat_persistence_client_test.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:stream_chat/src/core/models/draft.dart';
66
import 'package:stream_chat/src/core/models/draft_message.dart';
77
import 'package:stream_chat/src/core/models/event.dart';
88
import 'package:stream_chat/src/core/models/filter.dart';
9+
import 'package:stream_chat/src/core/models/location.dart';
910
import 'package:stream_chat/src/core/models/member.dart';
1011
import 'package:stream_chat/src/core/models/message.dart';
1112
import 'package:stream_chat/src/core/models/poll.dart';
@@ -176,6 +177,22 @@ class TestPersistenceClient extends ChatPersistenceClient {
176177

177178
@override
178179
Future<void> updateDraftMessages(List<Draft> draftMessages) => Future.value();
180+
181+
@override
182+
Future<List<Location>> getLocationsByCid(String cid) async => [];
183+
184+
@override
185+
Future<Location?> getLocationByMessageId(String messageId) async => null;
186+
187+
@override
188+
Future<void> updateLocations(List<Location> locations) => Future.value();
189+
190+
@override
191+
Future<void> deleteLocationsByCid(String cid) => Future.value();
192+
193+
@override
194+
Future<void> deleteLocationsByMessageIds(List<String> messageIds) =>
195+
Future.value();
179196
}
180197

181198
void main() {

packages/stream_chat_persistence/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## Upcoming Beta
2+
3+
- Added support for `Location` entity in the database.
4+
15
## Upcoming
26

37
🐞 Fixed

packages/stream_chat_persistence/lib/src/dao/dao.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export 'channel_dao.dart';
22
export 'channel_query_dao.dart';
33
export 'connection_event_dao.dart';
44
export 'draft_message_dao.dart';
5+
export 'location_dao.dart';
56
export 'member_dao.dart';
67
export 'message_dao.dart';
78
export 'pinned_message_dao.dart';

packages/stream_chat_persistence/lib/src/dao/draft_message_dao.dart

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,27 @@ class DraftMessageDao extends DatabaseAccessor<DriftChatDatabase>
1818
final DriftChatDatabase _db;
1919

2020
Future<Draft> _draftFromEntity(DraftMessageEntity entity) async {
21-
// We do not want to fetch the draft message of the parent and quoted
22-
// message because it will create a circular dependency and will
21+
// We do not want to fetch the draft and shared location of the parent and
22+
// quoted message because it will create a circular dependency and will
2323
// result in infinite loop.
2424
const fetchDraft = false;
25+
const fetchSharedLocation = false;
2526

2627
final parentMessage = await switch (entity.parentId) {
27-
final id? => _db.messageDao.getMessageById(id, fetchDraft: fetchDraft),
28+
final id? => _db.messageDao.getMessageById(
29+
id,
30+
fetchDraft: fetchDraft,
31+
fetchSharedLocation: fetchSharedLocation,
32+
),
2833
_ => null,
2934
};
3035

3136
final quotedMessage = await switch (entity.quotedMessageId) {
32-
final id? => _db.messageDao.getMessageById(id, fetchDraft: fetchDraft),
37+
final id? => _db.messageDao.getMessageById(
38+
id,
39+
fetchDraft: fetchDraft,
40+
fetchSharedLocation: fetchSharedLocation,
41+
),
3342
_ => null,
3443
};
3544

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// ignore_for_file: join_return_with_assignment
2+
3+
import 'package:drift/drift.dart';
4+
import 'package:stream_chat/stream_chat.dart';
5+
import 'package:stream_chat_persistence/src/db/drift_chat_database.dart';
6+
import 'package:stream_chat_persistence/src/entity/locations.dart';
7+
import 'package:stream_chat_persistence/src/mapper/mapper.dart';
8+
9+
part 'location_dao.g.dart';
10+
11+
/// The Data Access Object for operations in [Locations] table.
12+
@DriftAccessor(tables: [Locations])
13+
class LocationDao extends DatabaseAccessor<DriftChatDatabase>
14+
with _$LocationDaoMixin {
15+
/// Creates a new location dao instance
16+
LocationDao(this._db) : super(_db);
17+
18+
final DriftChatDatabase _db;
19+
20+
Future<Location> _locationFromEntity(LocationEntity entity) async {
21+
// We do not want to fetch the location of the parent and quoted
22+
// message because it will create a circular dependency and will
23+
// result in infinite loop.
24+
const fetchDraft = false;
25+
const fetchSharedLocation = false;
26+
27+
final channel = await switch (entity.channelCid) {
28+
final cid? => db.channelDao.getChannelByCid(cid),
29+
_ => null,
30+
};
31+
32+
final message = await switch (entity.messageId) {
33+
final id? => _db.messageDao.getMessageById(
34+
id,
35+
fetchDraft: fetchDraft,
36+
fetchSharedLocation: fetchSharedLocation,
37+
),
38+
_ => null,
39+
};
40+
41+
return entity.toLocation(
42+
channel: channel,
43+
message: message,
44+
);
45+
}
46+
47+
/// Get all locations for a channel
48+
Future<List<Location>> getLocationsByCid(String cid) async {
49+
final query = select(locations)..where((tbl) => tbl.channelCid.equals(cid));
50+
51+
final result = await query.map(_locationFromEntity).get();
52+
return Future.wait(result);
53+
}
54+
55+
/// Get location by message ID
56+
Future<Location?> getLocationByMessageId(String messageId) async {
57+
final query = select(locations) //
58+
..where((tbl) => tbl.messageId.equals(messageId));
59+
60+
final result = await query.getSingleOrNull();
61+
if (result == null) return null;
62+
63+
return _locationFromEntity(result);
64+
}
65+
66+
/// Update multiple locations
67+
Future<void> updateLocations(List<Location> locationList) {
68+
return batch(
69+
(it) => it.insertAllOnConflictUpdate(
70+
locations,
71+
locationList.map((it) => it.toEntity()),
72+
),
73+
);
74+
}
75+
76+
/// Delete locations by channel ID
77+
Future<void> deleteLocationsByCid(String cid) =>
78+
(delete(locations)..where((tbl) => tbl.channelCid.equals(cid))).go();
79+
80+
/// Delete locations by message IDs
81+
Future<void> deleteLocationsByMessageIds(List<String> messageIds) =>
82+
(delete(locations)..where((tbl) => tbl.messageId.isIn(messageIds))).go();
83+
}

packages/stream_chat_persistence/lib/src/dao/location_dao.g.dart

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/stream_chat_persistence/lib/src/dao/message_dao.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class MessageDao extends DatabaseAccessor<DriftChatDatabase>
3939
Future<Message> _messageFromJoinRow(
4040
TypedResult rows, {
4141
bool fetchDraft = false,
42+
bool fetchSharedLocation = false,
4243
}) async {
4344
final userEntity = rows.readTableOrNull(_users);
4445
final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers);
@@ -67,6 +68,11 @@ class MessageDao extends DatabaseAccessor<DriftChatDatabase>
6768
_ => null,
6869
};
6970

71+
final sharedLocation = await switch (fetchSharedLocation) {
72+
true => _db.locationDao.getLocationByMessageId(msgEntity.id),
73+
_ => null,
74+
};
75+
7076
return msgEntity.toMessage(
7177
user: userEntity?.toUser(),
7278
pinnedBy: pinnedByEntity?.toUser(),
@@ -75,6 +81,7 @@ class MessageDao extends DatabaseAccessor<DriftChatDatabase>
7581
quotedMessage: quotedMessage,
7682
poll: poll,
7783
draft: draft,
84+
sharedLocation: sharedLocation,
7885
);
7986
}
8087

@@ -85,6 +92,7 @@ class MessageDao extends DatabaseAccessor<DriftChatDatabase>
8592
Future<Message?> getMessageById(
8693
String id, {
8794
bool fetchDraft = true,
95+
bool fetchSharedLocation = true,
8896
}) async {
8997
final query = select(messages).join([
9098
leftOuterJoin(_users, messages.userId.equalsExp(_users.id)),
@@ -101,6 +109,7 @@ class MessageDao extends DatabaseAccessor<DriftChatDatabase>
101109
return _messageFromJoinRow(
102110
result,
103111
fetchDraft: fetchDraft,
112+
fetchSharedLocation: fetchSharedLocation,
104113
);
105114
}
106115

@@ -169,6 +178,7 @@ class MessageDao extends DatabaseAccessor<DriftChatDatabase>
169178
Future<List<Message>> getMessagesByCid(
170179
String cid, {
171180
bool fetchDraft = true,
181+
bool fetchSharedLocation = true,
172182
PaginationParams? messagePagination,
173183
}) async {
174184
final query = select(messages).join([
@@ -190,6 +200,7 @@ class MessageDao extends DatabaseAccessor<DriftChatDatabase>
190200
(row) => _messageFromJoinRow(
191201
row,
192202
fetchDraft: fetchDraft,
203+
fetchSharedLocation: fetchSharedLocation,
193204
),
194205
),
195206
);

packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class PinnedMessageDao extends DatabaseAccessor<DriftChatDatabase>
3838
Future<Message> _messageFromJoinRow(
3939
TypedResult rows, {
4040
bool fetchDraft = false,
41+
bool fetchSharedLocation = false,
4142
}) async {
4243
final userEntity = rows.readTableOrNull(_users);
4344
final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers);
@@ -68,6 +69,11 @@ class PinnedMessageDao extends DatabaseAccessor<DriftChatDatabase>
6869
_ => null,
6970
};
7071

72+
final sharedLocation = await switch (fetchSharedLocation) {
73+
true => _db.locationDao.getLocationByMessageId(msgEntity.id),
74+
_ => null,
75+
};
76+
7177
return msgEntity.toMessage(
7278
user: userEntity?.toUser(),
7379
pinnedBy: pinnedByEntity?.toUser(),
@@ -76,13 +82,15 @@ class PinnedMessageDao extends DatabaseAccessor<DriftChatDatabase>
7682
quotedMessage: quotedMessage,
7783
poll: poll,
7884
draft: draft,
85+
sharedLocation: sharedLocation,
7986
);
8087
}
8188

8289
/// Returns a single message by matching the [PinnedMessages.id] with [id]
8390
Future<Message?> getMessageById(
8491
String id, {
8592
bool fetchDraft = true,
93+
bool fetchSharedLocation = true,
8694
}) async {
8795
final query = select(pinnedMessages).join([
8896
leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)),
@@ -99,6 +107,7 @@ class PinnedMessageDao extends DatabaseAccessor<DriftChatDatabase>
99107
return _messageFromJoinRow(
100108
result,
101109
fetchDraft: fetchDraft,
110+
fetchSharedLocation: fetchSharedLocation,
102111
);
103112
}
104113

@@ -166,6 +175,7 @@ class PinnedMessageDao extends DatabaseAccessor<DriftChatDatabase>
166175
Future<List<Message>> getMessagesByCid(
167176
String cid, {
168177
bool fetchDraft = true,
178+
bool fetchSharedLocation = true,
169179
PaginationParams? messagePagination,
170180
}) async {
171181
final query = select(pinnedMessages).join([
@@ -188,6 +198,7 @@ class PinnedMessageDao extends DatabaseAccessor<DriftChatDatabase>
188198
(row) => _messageFromJoinRow(
189199
row,
190200
fetchDraft: fetchDraft,
201+
fetchSharedLocation: fetchSharedLocation,
191202
),
192203
),
193204
);

packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ part 'drift_chat_database.g.dart';
1313
tables: [
1414
Channels,
1515
DraftMessages,
16+
Locations,
1617
Messages,
1718
PinnedMessages,
1819
Polls,
@@ -30,6 +31,7 @@ part 'drift_chat_database.g.dart';
3031
ChannelDao,
3132
MessageDao,
3233
DraftMessageDao,
34+
LocationDao,
3335
PinnedMessageDao,
3436
PinnedMessageReactionDao,
3537
MemberDao,
@@ -55,7 +57,7 @@ class DriftChatDatabase extends _$DriftChatDatabase {
5557

5658
// you should bump this number whenever you change or add a table definition.
5759
@override
58-
int get schemaVersion => 22;
60+
int get schemaVersion => 1000 + 23;
5961

6062
@override
6163
MigrationStrategy get migration => MigrationStrategy(

0 commit comments

Comments
 (0)