@@ -19,6 +19,7 @@ import {
19
19
isMatch ,
20
20
get ,
21
21
isEqual ,
22
+ takeRight ,
22
23
} from 'lodash' ;
23
24
import { actionTypes } from '../constants' ;
24
25
import { getBaseQueryName } from '../utils/query' ;
@@ -178,10 +179,11 @@ const orderTransducer = (order) => {
178
179
const orders = isFlat ? [ order ] : order ;
179
180
const [ fields , direction ] = zip (
180
181
...orders . map ( ( [ field , dir ] ) => [
181
- ( data ) =>
182
- typeof data [ field ] === 'string'
183
- ? data [ field ] . toLowerCase ( )
184
- : data [ field ] ,
182
+ ( data ) => {
183
+ if ( typeof data [ field ] === 'string' ) return data [ field ] . toLowerCase ( ) ;
184
+ if ( isTimestamp ( data [ field ] ) ) return data [ field ] . seconds ;
185
+ return data [ field ] ;
186
+ } ,
185
187
dir || 'asc' ,
186
188
] ) ,
187
189
) ;
@@ -195,7 +197,13 @@ const orderTransducer = (order) => {
195
197
* limit from the firestore query
196
198
* @returns {xFormLimiter } - transducer
197
199
*/
198
- const limitTransducer = ( limit ) => ( [ arr ] = [ ] ) => [ take ( arr , limit ) ] ;
200
+ const limitTransducer = ( { limit, endAt, endBefore } ) => {
201
+ if ( ! limit ) return null ;
202
+ const fromRight = ( endAt || endBefore ) !== undefined ;
203
+ return fromRight
204
+ ? ( [ arr ] = [ ] ) => [ takeRight ( arr , limit ) ]
205
+ : ( [ arr ] = [ ] ) => [ take ( arr , limit ) ] ;
206
+ } ;
199
207
200
208
/**
201
209
* @name filterTransducers
@@ -214,6 +222,7 @@ const filterTransducers = (where) => {
214
222
: PROCESSES [ op ] || ( ( ) => true ) ;
215
223
return partialRight ( map , ( collection ) =>
216
224
filter ( Object . values ( collection || { } ) , ( doc ) => {
225
+ if ( ! doc ) return false ;
217
226
let value ;
218
227
if ( field === '__name__' ) {
219
228
value = doc . id ;
@@ -232,6 +241,72 @@ const filterTransducers = (where) => {
232
241
) ;
233
242
} ) ;
234
243
} ;
244
+
245
+ /**
246
+ * @name paginateTransducers
247
+ * @param {RRFQuery } query - Firestore query
248
+ * @param {Boolean } isOptimisticWrite - includes optimistic data
249
+ * @typedef {Function } xFormFilter - in optimistic reads and overrides
250
+ * the reducer needs to take all documents and make a best effort to
251
+ * filter down the document based on a cursor.
252
+ * @returns {xFormFilter } - transducer
253
+ */
254
+ const paginateTransducers = ( query , isOptimisticWrite = false ) => {
255
+ const { orderBy : order , startAt, startAfter, endAt, endBefore, via } = query ;
256
+ const isOptimisticRead = via === undefined ;
257
+ if ( ! ( isOptimisticRead || isOptimisticWrite ) ) return null ;
258
+
259
+ const start = startAt || startAfter ;
260
+ const end = endAt || endBefore ;
261
+ const isAfter = startAfter !== undefined ;
262
+ const isBefore = endBefore !== undefined ;
263
+ if ( start === undefined && end === undefined ) return null ;
264
+
265
+ const isFlat = typeof order [ 0 ] === 'string' ;
266
+ const orders = isFlat ? [ order ] : order ;
267
+ const isPaginateMatched = ( doc , at , before = false , after = false ) =>
268
+ orders . find ( ( [ field , sort = 'asc' ] , idx ) => {
269
+ const value = Array . isArray ( at ) ? at [ idx ] : at ;
270
+ if ( value === undefined ) return false ;
271
+
272
+ // TODO: add support for document refs
273
+ const proc = isTimestamp ( doc [ field ] ) ? PROCESSES_TIMESTAMP : PROCESSES ;
274
+ let compare = process [ '==' ] ;
275
+ if ( startAt || endAt ) compare = proc [ sort === 'desc' ? '<=' : '>=' ] ;
276
+ if ( startAfter || endBefore ) compare = proc [ sort === 'desc' ? '<' : '>' ] ;
277
+
278
+ const isMatched = compare ( doc [ field ] , value ) ;
279
+ if ( isMatched ) {
280
+ return true ;
281
+ }
282
+ } ) !== undefined ;
283
+
284
+ return partialRight ( map , ( docs ) => {
285
+ const results = [ ] ;
286
+ let started = start === undefined ;
287
+
288
+ docs . forEach ( ( doc ) => {
289
+ if ( ! started && start ) {
290
+ if ( isPaginateMatched ( doc , start , undefined , isAfter ) ) {
291
+ started = true ;
292
+ }
293
+ }
294
+
295
+ if ( started && end ) {
296
+ if ( isPaginateMatched ( doc , end , isBefore , undefined ) ) {
297
+ started = false ;
298
+ }
299
+ }
300
+
301
+ if ( started ) {
302
+ results . push ( doc ) ;
303
+ }
304
+ } ) ;
305
+
306
+ return results ;
307
+ } ) ;
308
+ } ;
309
+
235
310
/**
236
311
* @name populateTransducer
237
312
* @param {string } collection - path to collection in Firestore
@@ -318,13 +393,12 @@ function buildTransducer(overrides, query) {
318
393
collection,
319
394
where,
320
395
orderBy : order ,
321
- limit,
322
396
ordered,
323
397
fields,
324
398
populates,
325
399
} = query ;
326
400
327
- const useOverrides =
401
+ const isOptimistic =
328
402
ordered === undefined ||
329
403
Object . keys ( ( overrides || { } ) [ collection ] || { } ) . length > 0 ;
330
404
@@ -335,17 +409,26 @@ function buildTransducer(overrides, query) {
335
409
const xfGetDoc = getDocumentTransducer ( ( ordered || [ ] ) . map ( ( [ __ , id ] ) => id ) ) ;
336
410
const xfFields = ! fields ? null : fieldsTransducer ( fields ) ;
337
411
338
- const xfApplyOverrides = ! useOverrides
412
+ const xfApplyOverrides = ! isOptimistic
339
413
? null
340
414
: overridesTransducers ( overrides || { [ collection ] : [ ] } , collection ) ;
341
415
const xfFilter =
342
- ! useOverrides || filterTransducers ( ! where ? [ '' , '*' , '' ] : where ) ;
343
- const xfOrder = ! useOverrides || ! order ? null : orderTransducer ( order ) ;
344
- const xfLimit = ! limit ? null : limitTransducer ( limit ) ;
416
+ ! isOptimistic || filterTransducers ( ! where ? [ '' , '*' , '' ] : where ) ;
417
+ const xfOrder = ! isOptimistic || ! order ? null : orderTransducer ( order ) ;
418
+ const xfPaginate = paginateTransducers ( query , isOptimistic ) ;
419
+ const xfLimit = limitTransducer ( query ) ;
345
420
346
- if ( ! useOverrides ) {
421
+ if ( ! isOptimistic ) {
347
422
return flow (
348
- compact ( [ xfPopulate , xfGetCollection , xfGetDoc , xfLimit , xfFields ] ) ,
423
+ compact ( [
424
+ xfPopulate ,
425
+ xfGetCollection ,
426
+ xfGetDoc ,
427
+ xfOrder ,
428
+ xfPaginate ,
429
+ xfLimit ,
430
+ xfFields ,
431
+ ] ) ,
349
432
) ;
350
433
}
351
434
@@ -358,6 +441,7 @@ function buildTransducer(overrides, query) {
358
441
partialRight ( map , ( db ) => finishDraft ( db ) ) ,
359
442
...xfFilter ,
360
443
xfOrder ,
444
+ xfPaginate ,
361
445
xfLimit ,
362
446
xfFields ,
363
447
] ) ,
0 commit comments