Skip to content

Commit

Permalink
feat: implement reverse paging
Browse files Browse the repository at this point in the history
Use the new `pre` caveat and `startCursor` and `endCursor` from storacha/w3infra#139 to implement reverse paging.

One unsatisfying issue with this is that paging backwards reverses the
order of items in the uploads list, and that edge conditions
behave fairly confusingly.

Not entirely sure what to do about this yet, so pushing up for some feedback.

This currently only works with a bunch of custom service config and `file:/`
dependencies that I am not pushing up for now.
  • Loading branch information
travis committed Feb 14, 2023
1 parent 0bd21fb commit 97b1ed1
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/react-uploads-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"dependencies": {
"@w3ui/react-keyring": "workspace:^",
"@w3ui/uploads-list-core": "workspace:^",
"@web3-storage/capabilities": "^2.2.0",
"@web3-storage/capabilities": "^2.3.0",
"ariakit-react-utils": "0.17.0-next.27"
},
"peerDependencies": {
Expand Down
24 changes: 23 additions & 1 deletion packages/react-uploads-list/src/UploadsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export const UploadsListComponentContext = createContext<UploadsListComponentCon
loading: false
},
{
/**
* A function that will load the previous page of results.
*/
prev: async () => { },
/**
* A function that will load the next page of results.
*/
Expand Down Expand Up @@ -75,6 +79,24 @@ export const UploadsListRoot = (props: UploadsListRootProps): JSX.Element => {
)
}

export type PrevButtonOptions<T extends As = 'button'> = Options<T>
export type PrevButtonProps<T extends As = 'button'> = Props<PrevButtonOptions<T>>

/**
* Button that loads the next page of results.
*
* A 'button' designed to work with `UploadsList`. Any passed props will
* be passed along to the `button` component.
*/
export const PrevButton: Component<PrevButtonProps> = createComponent((props: any) => {
const [, { prev }] = useContext(UploadsListComponentContext)
const onClick = useCallback((e: React.MouseEvent) => {
e.preventDefault()
void prev()
}, [prev])
return createElement('button', { ...props, onClick })
})

export type NextButtonOptions<T extends As = 'button'> = Options<T>
export type NextButtonProps<T extends As = 'button'> = Props<NextButtonOptions<T>>

Expand Down Expand Up @@ -118,4 +140,4 @@ export function useUploadsListComponent (): UploadsListComponentContextValue {
return useContext(UploadsListComponentContext)
}

export const UploadsList = Object.assign(UploadsListRoot, { NextButton, ReloadButton })
export const UploadsList = Object.assign(UploadsListRoot, { PrevButton, NextButton, ReloadButton })
15 changes: 10 additions & 5 deletions packages/react-uploads-list/src/providers/UploadsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ export interface UploadsListProviderProps extends ServiceConfig {
*/
export function UploadsListProvider ({ size, servicePrincipal, connection, children }: UploadsListProviderProps): JSX.Element {
const [{ space, agent }, { getProofs }] = useKeyring()
const [cursor, setCursor] = useState<string>()
const [startCursor, setStartCursor] = useState<string>()
const [endCursor, setEndCursor] = useState<string>()
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error>()
const [data, setData] = useState<UploadListResult[]>()
const [controller, setController] = useState(new AbortController())

const loadPage = async (cursor?: string): Promise<void> => {
const loadPage = async (cursor?: string, pre?: boolean): Promise<void> => {
if (space == null) return
if (agent == null) return

Expand All @@ -58,10 +59,12 @@ export function UploadsListProvider ({ size, servicePrincipal, connection, child
const page = await list(conf, {
cursor,
size,
pre,
signal: newController.signal,
connection
})
setCursor(page.cursor)
setStartCursor(page.startCursor)
setEndCursor(page.endCursor)
setData(page.results)
} catch (err: any) {
if (err.name !== 'AbortError') {
Expand All @@ -75,9 +78,11 @@ export function UploadsListProvider ({ size, servicePrincipal, connection, child

const state = { data, loading, error }
const actions = {
next: async (): Promise<void> => { await loadPage(cursor) },
next: async (): Promise<void> => { await loadPage(endCursor) },
prev: async (): Promise<void> => { await loadPage(startCursor, true) },
reload: async (): Promise<void> => {
setCursor(undefined)
setStartCursor(undefined)
setEndCursor(undefined)
await loadPage()
}
}
Expand Down
9 changes: 6 additions & 3 deletions packages/react/src/UploadsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ function Uploads ({ uploads }: { uploads?: UploadListResult[] }): JSX.Element {
</table>
</div>
<nav>
<UploadsListCore.NextButton className='next w3ui-button'>
Next
</UploadsListCore.NextButton>
<UploadsListCore.PrevButton className='prev w3ui-button'>
Prev
</UploadsListCore.PrevButton>
<UploadsListCore.ReloadButton className='reload w3ui-button'>
Reload
</UploadsListCore.ReloadButton>
<UploadsListCore.NextButton className='next w3ui-button'>
Next
</UploadsListCore.NextButton>
</nav>
</>
)
Expand Down
4 changes: 4 additions & 0 deletions packages/uploads-list-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export interface UploadsListContextState {
}

export interface UploadsListContextActions {
/**
* Load the next page of results.
*/
prev: () => Promise<void>
/**
* Load the next page of results.
*/
Expand Down

0 comments on commit 97b1ed1

Please sign in to comment.