diff --git a/packages/react-uploads-list/package.json b/packages/react-uploads-list/package.json index a771c7ec..99cde839 100644 --- a/packages/react-uploads-list/package.json +++ b/packages/react-uploads-list/package.json @@ -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": { diff --git a/packages/react-uploads-list/src/UploadsList.tsx b/packages/react-uploads-list/src/UploadsList.tsx index 5bfa792c..9a1afcd6 100644 --- a/packages/react-uploads-list/src/UploadsList.tsx +++ b/packages/react-uploads-list/src/UploadsList.tsx @@ -29,26 +29,29 @@ export type UploadsListComponentContextValue = [ actions: UploadsListComponentContextActions ] -export const UploadsListComponentContext = - createContext([ - { - /** - * 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([ + { + /** + * 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 export type UploadsListRenderProps = Omit< @@ -94,9 +97,26 @@ export const UploadsListRoot = (props: UploadsListRootProps): JSX.Element => { ) } +export type PrevButtonOptions = Options +export type PrevButtonProps = Props> + +/** + * 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 = 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 = Options -export type NextButtonProps = - Props> +export type NextButtonProps = Props> /** * Button that loads the next page of results. @@ -119,9 +139,7 @@ export const NextButton: Component = createComponent( ) export type ReloadButtonOptions = Options -export type ReloadButtonProps = Props< -ReloadButtonOptions -> +export type ReloadButtonProps = Props> /** * Button that reloads an `UploadsList`. @@ -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 }) diff --git a/packages/react-uploads-list/src/providers/UploadsList.tsx b/packages/react-uploads-list/src/providers/UploadsList.tsx index 70827a35..7f9afc4d 100644 --- a/packages/react-uploads-list/src/providers/UploadsList.tsx +++ b/packages/react-uploads-list/src/providers/UploadsList.tsx @@ -19,6 +19,7 @@ export const uploadsListContextDefaultValue: UploadsListContextValue = [ loading: false }, { + prev: async () => {}, next: async () => {}, reload: async () => {} } @@ -46,13 +47,14 @@ export function UploadsListProvider ({ children }: UploadsListProviderProps): JSX.Element { const [{ space, agent }, { getProofs }] = useKeyring() - const [cursor, setCursor] = useState() + const [before, setBefore] = useState() + const [after, setAfter] = useState() const [loading, setLoading] = useState(false) const [error, setError] = useState() const [data, setData] = useState() const [controller, setController] = useState(new AbortController()) - const loadPage = async (cursor?: string): Promise => { + const loadPage = async (cursor?: string, pre?: boolean): Promise => { if (space == null) return if (agent == null) return @@ -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 */ @@ -90,17 +96,20 @@ export function UploadsListProvider ({ const state = { data, loading, error } const actions = { - next: async (): Promise => { - await loadPage(cursor) - }, + next: async (): Promise => { await loadPage(after) }, + prev: async (): Promise => { await loadPage(before, true) }, reload: async (): Promise => { - 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]) diff --git a/packages/react/src/UploadsList.tsx b/packages/react/src/UploadsList.tsx index a1f78196..5190209f 100644 --- a/packages/react/src/UploadsList.tsx +++ b/packages/react/src/UploadsList.tsx @@ -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 ? ( <>
No uploads
-