Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/stream_chat_persistence/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Upcoming Changes

🐞 Fixed

- `PollDao` no longer crashes when reading polls whose creator user is missing from the local cache.

## 9.25.0

🚀 Performance
Expand Down
12 changes: 5 additions & 7 deletions packages/stream_chat_persistence/lib/src/dao/poll_dao.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class PollDao extends DatabaseAccessor<DriftChatDatabase> with _$PollDaoMixin {

Future<Poll> _pollFromJoinRow(TypedResult row) async {
final pollEntity = row.readTable(polls);
final userEntity = row.readTable(users);
final userEntity = row.readTableOrNull(users);
final allVotes = await _db.pollVoteDao.getPollVotes(pollEntity.id);
final latestAnswers = allVotes.where((it) => it.isAnswer);
final ownVotesAndAnswers = allVotes.where((it) => it.userId == _db.userId);
Expand All @@ -38,7 +38,7 @@ class PollDao extends DatabaseAccessor<DriftChatDatabase> with _$PollDaoMixin {
}

return pollEntity.toPoll(
createdBy: userEntity.toUser(),
createdBy: userEntity?.toUser(),
latestAnswers: latestAnswers.toList(),
ownVotesAndAnswers: ownVotesAndAnswers.toList(),
latestVotesByOption: latestVotesByOption,
Expand Down Expand Up @@ -68,9 +68,7 @@ class PollDao extends DatabaseAccessor<DriftChatDatabase> with _$PollDaoMixin {
[leftOuterJoin(users, polls.createdById.equalsExp(users.id))]).get();
for (final row in rows) {
final pollEntity = row.readTable(polls);
// Same as `_pollFromJoinRow` => reads users via `readTable` (not
// `readTableOrNull`) on a LEFT JOIN
final userEntity = row.readTable(users);
final userEntity = row.readTableOrNull(users);
final allVotes = votesByPoll[pollEntity.id] ?? const <PollVote>[];
result[pollEntity.id] = _buildPoll(pollEntity, userEntity, allVotes);
}
Expand Down Expand Up @@ -99,7 +97,7 @@ class PollDao extends DatabaseAccessor<DriftChatDatabase> with _$PollDaoMixin {

Poll _buildPoll(
PollEntity pollEntity,
UserEntity userEntity,
UserEntity? userEntity,
List<PollVote> allVotes,
) {
final latestAnswers = allVotes.where((it) => it.isAnswer);
Expand All @@ -118,7 +116,7 @@ class PollDao extends DatabaseAccessor<DriftChatDatabase> with _$PollDaoMixin {
}

return pollEntity.toPoll(
createdBy: userEntity.toUser(),
createdBy: userEntity?.toUser(),
latestAnswers: latestAnswers.toList(),
ownVotesAndAnswers: ownVotesAndAnswers.toList(),
latestVotesByOption: latestVotesByOption,
Expand Down
3 changes: 3 additions & 0 deletions packages/stream_chat_persistence/lib/src/db/query_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import 'dart:math' as math;
/// chunk size of 900 leaves headroom for any other bound parameters that
/// share the same statement (for example a `AND userId = ?` filter).
Iterable<List<T>> chunked<T>(List<T> input, [int size = 900]) sync* {
if (size <= 0) {
throw ArgumentError.value(size, 'size', 'must be greater than 0');
}
for (var i = 0; i < input.length; i += size) {
yield input.sublist(i, math.min(i + size, input.length));
}
Expand Down
49 changes: 49 additions & 0 deletions packages/stream_chat_persistence/test/src/db/query_utils_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_persistence/src/db/query_utils.dart';

void main() {
group('chunked', () {
test('returns an empty iterable for an empty input', () {
expect(chunked(<int>[]).toList(), isEmpty);
});

test('yields a single chunk when input fits in one chunk', () {
final input = List.generate(5, (i) => i);
expect(chunked(input, 10).toList(), [input]);
});

test('splits input into evenly sized chunks', () {
final input = List.generate(6, (i) => i);
expect(chunked(input, 2).toList(), [
[0, 1],
[2, 3],
[4, 5],
]);
});

test('handles a trailing partial chunk', () {
final input = List.generate(7, (i) => i);
expect(chunked(input, 3).toList(), [
[0, 1, 2],
[3, 4, 5],
[6],
]);
});

test('uses a default size of 900', () {
final input = List.generate(1000, (i) => i);
final chunks = chunked(input).toList();
expect(chunks, hasLength(2));
expect(chunks[0], hasLength(900));
expect(chunks[1], hasLength(100));
});

test('throws ArgumentError when size is zero', () {
expect(() => chunked([1, 2, 3], 0).toList(), throwsArgumentError);
});

test('throws ArgumentError when size is negative', () {
expect(() => chunked([1, 2, 3], -1).toList(), throwsArgumentError);
});
});
}
Loading