import 'rxjs/add/observable/throw' ; import 'rxjs/add/operator/startWith' ; import 'rxjs/add/operator/withLatestFrom' ; import { Observable } from 'rxjs/Observable' ; import { OperatorFunction } from 'rxjs/interfaces' ; import { Omit } from '../utils' ; export type PageToken = string & { kind : 'PageToken' ; } ; export const emptyPageToken = '' as PageToken ; export enum Kind { PageCount = 'PageCount' , PageToken = 'PageToken' } export type CommonState < T = { } > = { kind : Kind ; urlPath : string ; pageSize : number ; urlQuery ? : { } ; result : T [ ] ; nextPage : number ; hasMore : boolean ; limit : number ; } ; export type PageCountState < T = { } > = CommonState < T > & { kind : Kind . PageCount ; } ; export type PageTokenState < T = { } > = CommonState < T > & { kind : Kind . PageToken ; nextPageToken : PageToken ; totalSize ? : number ; } ; export type State < T = { } > = PageTokenState < T > ; export type PolyState < T = { } > = PageCountState < T > | PageTokenState < T > ; export type StateOptions = { kind ? : Kind ; pageSize ? : number ; urlQuery ? : { } ; } ; export type InitOptions < T extends { } = { } > = Omit < StateOptions , 'kind' > & { urlQuery ? : T ; } ; export type Update < T > = { patch ? : T [ ] ; limit ? : number ; } ; export function defaultState < T > ( urlPath , options ) ; export function defaultState < T > ( urlPath ) ; export function defaultState < T > ( urlPath , options ) ; export function defaultState < T > ( urlPath , options ) ; export function defaultState < T > ( urlPath , options ) ; export function defaultState < T > ( urlPath , options = { } ) { const state = { kind : options . kind || Kind . PageToken , urlPath , result : [ ] , nextPage : 1 , hasMore : true , urlQuery : undefined , pageSize : 50 , limit : 0 } ; if ( options . urlQuery ) { const { pageSize : queryPageSize , ... nonPaginationQuery } = options . urlQuery as any ; state . urlQuery = nonPaginationQuery ; state . pageSize = queryPageSize || state . pageSize ; } state . pageSize = options . pageSize || state . pageSize ; return state . kind === Kind . PageCount ? state as PageCountState < T > : { ... state , nextPageToken : emptyPageToken , totalSize : undefined } as PageTokenState < T > ; } export function queryInfo < T > ( state ) { return { patch : state . result , limit : state . limit } ; } export type OriginalResponse < T > = { nextPageToken : PageToken ; result : T [ ] ; totalSize ? : number ; } ; export function accUpdate < T > ( state , resp ) ; export function accUpdate < T > ( state , resp ) ; export function accUpdate < T > ( state , resp ) ; export function accUpdate < T > ( state , resp ) { const nextPage = state . nextPage + 1 ; const limit = state . nextPage * state . pageSize ; switch ( state . kind ) { case Kind . PageCount : { const r = resp as T [ ] ; const hasMore = r . length === state . pageSize ; const result = r ; return { ... state , nextPage , limit , hasMore , result } ; } case Kind . PageToken : { const r = resp as OriginalResponse < T > ; const hasMore = Boolean ( r . nextPageToken ) && r . result . length === state . pageSize ; const result = r . result ; const nextPageToken = r . nextPageToken ; const totalSize = r . totalSize ; return { ... state , nextPage , limit , hasMore , result , nextPageToken , totalSize } ; } default : return state ; } } export function accConcat < T > ( state , resp ) ; export function accConcat < T > ( state , resp ) ; export function accConcat < T > ( state , resp ) ; export function accConcat < T > ( state , resp ) { const nextState = accUpdate ( state , resp ) ; if ( nextState === state ) { return nextState ; } nextState . result = state . result . concat ( nextState . result ) ; return nextState ; } export function expand < T > ( step , accumulate , initState ) ; export function expand < T > ( step , accumulate , initState ) ; export function expand < T > ( step , accumulator , initState ) ; export function expand < T > ( step , accumulator , initState ) { return ( source$ ) => { const state = { ... initState } ; let isLoading = false ; return new Observable ( ( observer ) => { const subs = source$ . subscribe ( { next : ( _ ) => { if ( ! state . hasMore ) { observer . complete ( ) ; return ; } if ( ! isLoading ) { isLoading = true ; observer . next ( step ( state ) . map ( ( stepResult ) => accumulator ( state , stepResult ) ) . do ( ( expanded ) => Object . assign ( state , expanded ) ) . catch ( ( err ) => Observable . throw ( err ) ) . finally ( ( ) => { isLoading = false ; } ) ) ; } } , error : ( err ) => { isLoading = false ; observer . error ( err ) ; } , complete : ( ) => { observer . complete ( ) ; } } ) ; return ( ) => { subs . unsubscribe ( ) ; } ; } ) ; } ; }