Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement reverse paging #381

Merged
merged 9 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react-uploads-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,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
73 changes: 44 additions & 29 deletions packages/react-uploads-list/src/UploadsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,29 @@ export type UploadsListComponentContextValue = [
actions: UploadsListComponentContextActions
]

export const UploadsListComponentContext =
createContext<UploadsListComponentContextValue>([
{
/**
* A boolean indicating whether the uploads list
* is currently loading data from the server.
*/
loading: false
},
{
/**
* A function that will load the next page of results.
*/
next: async () => {},
/**
* A function that will reload the uploads list.
*/
reload: async () => {}
}
])
export const UploadsListComponentContext = createContext<UploadsListComponentContextValue>([
{
/**
* A boolean indicating whether the uploads list
* is currently loading data from the server.
*/
loading: false
},
{
/**
* A function that will load the previous page of results.
*/
prev: async () => {},
/**
* A function that will load the next page of results.
*/
next: async () => {},
/**
* A function that will reload the uploads list.
*/
reload: async () => {}
}
])

export type UploadsListRootOptions = Options<typeof Fragment>
export type UploadsListRenderProps = Omit<
Expand Down Expand Up @@ -94,9 +97,26 @@ 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>>
export type NextButtonProps<T extends As = 'button'> = Props<NextButtonOptions<T>>

/**
* Button that loads the next page of results.
Expand All @@ -119,9 +139,7 @@ export const NextButton: Component<NextButtonProps> = createComponent(
)

export type ReloadButtonOptions<T extends As = 'button'> = Options<T>
export type ReloadButtonProps<T extends As = 'button'> = Props<
ReloadButtonOptions<T>
>
export type ReloadButtonProps<T extends As = 'button'> = Props<ReloadButtonOptions<T>>

/**
* Button that reloads an `UploadsList`.
Expand Down Expand Up @@ -150,7 +168,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 })
25 changes: 17 additions & 8 deletions packages/react-uploads-list/src/providers/UploadsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const uploadsListContextDefaultValue: UploadsListContextValue = [
loading: false
},
{
prev: async () => {},
next: async () => {},
reload: async () => {}
}
Expand Down Expand Up @@ -46,13 +47,14 @@ export function UploadsListProvider ({
children
}: UploadsListProviderProps): JSX.Element {
const [{ space, agent }, { getProofs }] = useKeyring()
const [cursor, setCursor] = useState<string>()
const [before, setBefore] = useState<string>()
const [after, setAfter] = 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 @@ -71,11 +73,15 @@ export function UploadsListProvider ({
const page = await list(conf, {
cursor,
size,
pre,
signal: newController.signal,
connection
})
setCursor(page.cursor)
setData(page.results)
if (page.size > 0) {
setBefore(page.before)
setAfter(page.after)
setData(page.results)
}
} catch (error_: any) {
if (error_.name !== 'AbortError') {
/* eslint-disable no-console */
Expand All @@ -90,17 +96,20 @@ export function UploadsListProvider ({

const state = { data, loading, error }
const actions = {
next: async (): Promise<void> => {
await loadPage(cursor)
},
next: async (): Promise<void> => { await loadPage(after) },
prev: async (): Promise<void> => { await loadPage(before, true) },
reload: async (): Promise<void> => {
setCursor(undefined)
setBefore(undefined)
setAfter(undefined)
await loadPage()
}
}

// we should reload the page any time the space or agent change
useEffect(() => {
setBefore(undefined)
setAfter(undefined)
setData([])
void loadPage()
}, [space, agent])

Expand Down
33 changes: 21 additions & 12 deletions packages/react/src/UploadsList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import type { UploadListResult } from '@w3ui/uploads-list-core'
import React from 'react'
import { ChevronLeftIcon, ChevronRightIcon, ArrowPathIcon } from '@heroicons/react/20/solid'
import type { UploadListResult } from '@w3ui/uploads-list-core'
import { UploadsList as UploadsListCore } from '@w3ui/react-uploads-list'

function Uploads ({ uploads }: { uploads?: UploadListResult[] }): JSX.Element {
interface UploadsProps {
uploads?: UploadListResult[]
loading: boolean
}

function Uploads ({ uploads, loading }: UploadsProps): JSX.Element {
return uploads === undefined || uploads.length === 0
? (
<>
<div className='w3-uploads-list-no-uploads'>No uploads</div>
<nav>
<UploadsListCore.ReloadButton className='reload w3ui-button'>
Reload
<nav className='flex flex-row justify-center'>
<UploadsListCore.ReloadButton className='reload w3ui-button w-auto px-2'>
<ArrowPathIcon className={`h-6 w-6 ${loading ? 'animate-spin' : ''}`}/>
</UploadsListCore.ReloadButton>
</nav>
</>
Expand All @@ -36,13 +42,16 @@ function Uploads ({ uploads }: { uploads?: UploadListResult[] }): JSX.Element {
</tbody>
</table>
</div>
<nav>
<UploadsListCore.NextButton className='next w3ui-button'>
Next
</UploadsListCore.NextButton>
<UploadsListCore.ReloadButton className='reload w3ui-button'>
Reload
<nav className='flex flex-row justify-center'>
<UploadsListCore.PrevButton className='prev w3ui-button w-auto px-2'>
<ChevronLeftIcon className='h-6 w-6'/>
</UploadsListCore.PrevButton>
<UploadsListCore.ReloadButton className='reload w3ui-button w-auto px-2'>
<ArrowPathIcon className={`h-6 w-6 ${loading ? 'animate-spin' : ''}`}/>
</UploadsListCore.ReloadButton>
<UploadsListCore.NextButton className='next w3ui-button w-auto px-2'>
<ChevronRightIcon className='h-6 w-6'/>
</UploadsListCore.NextButton>
</nav>
</>
)
Expand All @@ -53,7 +62,7 @@ export const UploadsList = (): JSX.Element => {
<UploadsListCore>
{(props) => (
<div className='w3-uploads-list'>
<Uploads uploads={props.uploadsList?.[0].data} />
<Uploads uploads={props.uploadsList?.[0].data} loading={props.uploadsList?.[0].loading ?? false}/>
</div>
)}
</UploadsListCore>
Expand Down
2 changes: 1 addition & 1 deletion packages/uploader-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"homepage": "https://github.com/web3-storage/w3ui/tree/main/packages/uploader-core",
"dependencies": {
"@ucanto/interface": "^4.2.3",
"@web3-storage/upload-client": "^5.4.0",
"@web3-storage/upload-client": "^6.0.0",
"multiformats": "^11.0.1"
},
"eslintConfig": {
Expand Down
2 changes: 1 addition & 1 deletion packages/uploads-list-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"homepage": "https://github.com/web3-storage/w3ui/tree/main/packages/uploads-list-core",
"dependencies": {
"@ucanto/interface": "^4.2.3",
"@web3-storage/upload-client": "^5.4.0"
"@web3-storage/upload-client": "^6.0.0"
},
"eslintConfig": {
"extends": [
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 @@ -30,6 +30,10 @@ export interface UploadsListContextState {
}

export interface UploadsListContextActions {
/**
* Load the next page of results.
*/
prev: () => Promise<void>
/**
* Load the next page of results.
*/
Expand Down
18 changes: 9 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.