-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Paginator2 for paginating through PagedData model, use for notifi…
…cations Add State.id/setId for debugging - Allow custom equality checking for State and State.Generator - Add State.bindManual - Fix Endpoint data search param input object being incorrect
- Loading branch information
1 parent
c69d57a
commit 6235ccb
Showing
12 changed files
with
614 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import type { PaginatedEndpoint, PreparedPaginatedQueryReturning, PreparedQueryOf } from 'endpoint/Endpoint' | ||
import type { StateOr } from 'utility/State' | ||
import State from 'utility/State' | ||
import type { PromiseOr } from 'utility/Type' | ||
|
||
interface PagedData<T> { | ||
readonly pageCount: State<number | undefined> | ||
get pages (): readonly State<T | false | null>[] | ||
/** @deprecated */ | ||
get rawPages (): State.Mutable<PromiseOr<State.Mutable<T | false | null>>[]> | ||
get (page: number): PromiseOr<State<T | false | null>> | ||
set (page: number, data: T, isLastPage?: true): void | ||
setPageCount (count: number): void | ||
clear (): void | ||
} | ||
|
||
export interface PagedDataDefinition<T> { | ||
get (page: number): PromiseOr<StateOr<T | false | null>> | ||
} | ||
|
||
const PagedData = Object.assign( | ||
function <T> (definition: PagedDataDefinition<T>): PagedData<T> { | ||
const pageCount = State<number | undefined>(undefined) | ||
const pages: State.Mutable<PromiseOr<State.Mutable<T | false | null>>[]> = State([], false)// .setId('PagedData pages') | ||
return { | ||
pageCount, | ||
get pages () { | ||
return pages.value.filter((page): page is Exclude<typeof page, Promise<any>> => !(page instanceof Promise)) | ||
}, | ||
rawPages: pages, | ||
get (page): PromiseOr<State<T | false | null>> { | ||
if (pages.value[page] instanceof Promise) | ||
return pages.value[page] | ||
|
||
const existing = pages.value[page]?.value | ||
if (existing === undefined || existing === false) { | ||
pages.value[page] = Promise.resolve(definition.get(page)) | ||
.then(data => { | ||
if (!State.is(pages.value[page])) { // if it's already a State, it's been updated before this, don't overwrite | ||
let newState: State.Mutable<T | false | null> | ||
if (State.is(data)) { | ||
newState = State(null, false)// .setId(`PagedData page ${page} get 1`) | ||
newState.bindManual(data) | ||
} | ||
else | ||
newState = State(data, false)// .setId(`PagedData page ${page} get 2`) | ||
|
||
pages.value[page] = newState | ||
pages.emit() | ||
} | ||
|
||
return pages.value[page] | ||
}) | ||
pages.emit() | ||
} | ||
|
||
return pages.value[page] | ||
}, | ||
set (page, data, isLastPage) { | ||
if (State.is(pages.value[page])) | ||
pages.value[page].value = data | ||
else | ||
pages.value[page] = State(data, false)// .setId(`PagedData page ${page} set`) | ||
|
||
if (isLastPage) | ||
pages.value.length = pageCount.value = page + 1 | ||
else if (pageCount.value !== undefined && page >= pageCount.value) | ||
pageCount.value = undefined | ||
|
||
pages.emit() | ||
}, | ||
setPageCount (count) { | ||
pageCount.value = count | ||
}, | ||
clear () { | ||
pages.value = [] | ||
pageCount.value = undefined | ||
}, | ||
} | ||
}, | ||
{ | ||
fromEndpoint<T> (endpoint: PreparedPaginatedQueryReturning<T>): PagedData<T> { | ||
const e = endpoint as PreparedQueryOf<PaginatedEndpoint> | ||
return PagedData({ | ||
async get (page) { | ||
const response = await e.query(undefined, { page }) | ||
if (toast.handleError(response)) | ||
return false | ||
|
||
if (!Array.isArray(response.data) || response.data.length) | ||
return response.data as T | ||
|
||
return null | ||
}, | ||
}) | ||
}, | ||
} | ||
) | ||
|
||
export default PagedData |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import type { PagedDataDefinition } from 'model/PagedData' | ||
import PagedData from 'model/PagedData' | ||
import State from 'utility/State' | ||
import type { PromiseOr } from 'utility/Type' | ||
|
||
interface PagedListData<T> extends PagedData<T[]> { | ||
readonly pageSize: number | ||
resized (pageSize: number): PagedListData<T> | ||
} | ||
|
||
function PagedListData<T> (pageSize: number, definition: PagedDataDefinition<T[]>): PagedListData<T> { | ||
const list = PagedData<T[]>(definition) | ||
return Object.assign( | ||
list, | ||
{ | ||
pageSize, | ||
resized (resizePageSize: number) { | ||
const newList = PagedListData(resizePageSize, { | ||
get: getPage, | ||
}) | ||
|
||
list.rawPages.subscribeManual(() => { | ||
// go through current pages and update each from the source list | ||
const pages = newList.rawPages.value | ||
for (let i = 0; i < pages.length; i++) { | ||
const page = pages[i] | ||
if (State.is(page)) { | ||
const pageState = page | ||
const value = getPage(i) | ||
if (value instanceof Promise) | ||
void value.then(setPageValue) | ||
else | ||
setPageValue(value) | ||
|
||
function setPageValue (value: false | State<T[] | null>) { | ||
if (State.is(value)) | ||
pageState.bindManual(value) | ||
else | ||
pageState.value = value | ||
} | ||
|
||
continue | ||
} | ||
|
||
const value = getPage(i) | ||
if (value instanceof Promise) | ||
pages[i] = value.then(setPage) | ||
else | ||
pages[i] = setPage(value) | ||
|
||
function setPage (value: false | State<T[] | null>) { | ||
const state = pages[i] = State<T[] | null | false>(null, false)// .setId('PagedListData subscribeManual setPage') | ||
if (State.is(value)) | ||
state.bindManual(value) | ||
else | ||
state.value = value | ||
return state | ||
} | ||
} | ||
}) | ||
|
||
return newList | ||
|
||
type SourcePages = State.Mutable<false | T[] | null>[] | ||
|
||
function getPage (page: number): PromiseOr<State<T[] | null> | false> { | ||
const start = page * resizePageSize | ||
const end = (page + 1) * resizePageSize | ||
const startPageInSource = Math.floor(start / pageSize) | ||
const endPageInSource = Math.ceil(end / pageSize) | ||
const startIndexInFirstSourcePage = start % pageSize | ||
const endIndexInLastSourcePage = (end % pageSize) || pageSize | ||
|
||
const rawPages = list.rawPages.value.slice() | ||
for (let i = 0; i < rawPages.length; i++) { | ||
const rawPage = rawPages[i] | ||
if (i >= startPageInSource && i < endPageInSource && State.is(rawPage) && rawPage.value === false) { | ||
rawPages[i] = list.get(i) as PromiseOr<State.Mutable<T[] | false | null>> | ||
} | ||
} | ||
|
||
const sourcePages = rawPages.slice(startPageInSource, endPageInSource) | ||
if (sourcePages.some(page => page instanceof Promise)) | ||
return Promise.all(sourcePages).then(sourcePages => resolveData(sourcePages, startIndexInFirstSourcePage, endIndexInLastSourcePage)) | ||
|
||
return resolveData(sourcePages as SourcePages, startIndexInFirstSourcePage, endIndexInLastSourcePage) | ||
} | ||
|
||
function resolveData (sourcePages: SourcePages, startIndex: number, endIndex: number): State<T[] | null> | false { | ||
const data: T[] = [] | ||
for (let i = 0; i < sourcePages.length; i++) { | ||
const sourcePage = sourcePages[i] | ||
if (sourcePage.value === false) | ||
return false | ||
|
||
if (sourcePage.value === null) | ||
continue | ||
|
||
if (i === 0 && i === sourcePages.length - 1) | ||
data.push(...sourcePage.value.slice(startIndex, endIndex)) | ||
else if (i === 0) | ||
data.push(...sourcePage.value.slice(startIndex)) | ||
else if (i === sourcePages.length - 1) | ||
data.push(...sourcePage.value.slice(0, endIndex)) | ||
else | ||
data.push(...sourcePage.value) | ||
} | ||
|
||
return State.Generator(() => data, false).observeManual(...sourcePages)// .setId('PagedListData resolveData') | ||
} | ||
}, | ||
} | ||
) | ||
} | ||
|
||
export default PagedListData |
Oops, something went wrong.