@@ -108,6 +108,11 @@ class TimelineFactory {
108
108
class TimelineService {
109
109
final TimelineAssetSource _assetSource;
110
110
final TimelineBucketSource _bucketSource;
111
+ final AsyncMutex _mutex = AsyncMutex ();
112
+ int _bufferOffset = 0 ;
113
+ List <BaseAsset > _buffer = [];
114
+ StreamSubscription ? _bucketSubscription;
115
+
111
116
int _totalAssets = 0 ;
112
117
int get totalAssets => _totalAssets;
113
118
@@ -117,24 +122,41 @@ class TimelineService {
117
122
}) : _assetSource = assetSource,
118
123
_bucketSource = bucketSource {
119
124
_bucketSubscription = _bucketSource ().listen ((buckets) {
120
- _totalAssets =
121
- buckets.fold <int >(0 , (acc, bucket) => acc + bucket.assetCount);
122
- unawaited (_reloadBucket ());
125
+ _mutex.run (() async {
126
+ final totalAssets =
127
+ buckets.fold <int >(0 , (acc, bucket) => acc + bucket.assetCount);
128
+
129
+ if (totalAssets == 0 ) {
130
+ _bufferOffset = 0 ;
131
+ _buffer.clear ();
132
+ } else {
133
+ final int offset;
134
+ final int count;
135
+ // When the buffer is empty or the old bufferOffset is greater than the new total assets,
136
+ // we need to reset the buffer and load the first batch of assets.
137
+ if (_bufferOffset >= totalAssets || _buffer.isEmpty) {
138
+ offset = 0 ;
139
+ count = kTimelineAssetLoadBatchSize;
140
+ } else {
141
+ offset = _bufferOffset;
142
+ count = math.min (
143
+ _buffer.length,
144
+ totalAssets - _bufferOffset,
145
+ );
146
+ }
147
+ _buffer = await _assetSource (offset, count);
148
+ _bufferOffset = offset;
149
+ }
150
+
151
+ // change the state's total assets count only after the buffer is reloaded
152
+ _totalAssets = totalAssets;
153
+ EventStream .shared.emit (const TimelineReloadEvent ());
154
+ });
123
155
});
124
156
}
125
157
126
- final AsyncMutex _mutex = AsyncMutex ();
127
- int _bufferOffset = 0 ;
128
- List <BaseAsset > _buffer = [];
129
- StreamSubscription ? _bucketSubscription;
130
-
131
158
Stream <List <Bucket >> Function () get watchBuckets => _bucketSource;
132
159
133
- Future <void > _reloadBucket () => _mutex.run (() async {
134
- _buffer = await _assetSource (_bufferOffset, _buffer.length);
135
- EventStream .shared.emit (const TimelineReloadEvent ());
136
- });
137
-
138
160
Future <List <BaseAsset >> loadAssets (int index, int count) =>
139
161
_mutex.run (() => _loadAssets (index, count));
140
162
@@ -163,18 +185,20 @@ class TimelineService {
163
185
: (len > kTimelineAssetLoadBatchSize ? index : index + count - len),
164
186
);
165
187
166
- final assets = await _assetSource (start, len);
167
- _buffer = assets;
188
+ _buffer = await _assetSource (start, len);
168
189
_bufferOffset = start;
169
190
170
191
return getAssets (index, count);
171
192
}
172
193
173
194
bool hasRange (int index, int count) =>
174
- index >= _bufferOffset && index + count <= _bufferOffset + _buffer.length;
195
+ index >= 0 &&
196
+ index < _totalAssets &&
197
+ index >= _bufferOffset &&
198
+ index + count <= _bufferOffset + _buffer.length &&
199
+ index + count <= _totalAssets;
175
200
176
201
List <BaseAsset > getAssets (int index, int count) {
177
- assert (index + count <= totalAssets);
178
202
if (! hasRange (index, count)) {
179
203
throw RangeError ('TimelineService::getAssets Index out of range' );
180
204
}
@@ -184,11 +208,13 @@ class TimelineService {
184
208
185
209
// Pre-cache assets around the given index for asset viewer
186
210
Future <void > preCacheAssets (int index) =>
187
- _mutex.run (() => _loadAssets (index, math.min (5 , totalAssets - index)));
211
+ _mutex.run (() => _loadAssets (index, math.min (5 , _totalAssets - index)));
188
212
189
213
BaseAsset getAsset (int index) {
190
214
if (! hasRange (index, 1 )) {
191
- throw RangeError ('TimelineService::getAsset Index out of range' );
215
+ throw RangeError (
216
+ 'TimelineService::getAsset Index $index not in buffer range [$_bufferOffset , ${_bufferOffset + _buffer .length })' ,
217
+ );
192
218
}
193
219
return _buffer.elementAt (index - _bufferOffset);
194
220
}
0 commit comments