diff --git a/examples/react/photo-uploader/.gitignore b/examples/react/photo-uploader/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/examples/react/photo-uploader/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/react/photo-uploader/index.html b/examples/react/photo-uploader/index.html new file mode 100644 index 00000000..e0d1c840 --- /dev/null +++ b/examples/react/photo-uploader/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/examples/react/photo-uploader/package.json b/examples/react/photo-uploader/package.json new file mode 100644 index 00000000..2d3d0531 --- /dev/null +++ b/examples/react/photo-uploader/package.json @@ -0,0 +1,24 @@ +{ + "name": "photo-uploader", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@w3ui/react": "workspace:^1.0.0", + "@w3ui/react-uploads-list": "workspace:^2.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.0.26", + "@types/react-dom": "^18.0.9", + "@vitejs/plugin-react": "^3.0.0", + "typescript": "^4.9.3", + "vite": "^4.0.0" + } +} \ No newline at end of file diff --git a/examples/react/photo-uploader/public/vite.svg b/examples/react/photo-uploader/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/examples/react/photo-uploader/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/react/photo-uploader/src/App.css b/examples/react/photo-uploader/src/App.css new file mode 100644 index 00000000..2c5e2ef5 --- /dev/null +++ b/examples/react/photo-uploader/src/App.css @@ -0,0 +1,41 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/examples/react/photo-uploader/src/App.tsx b/examples/react/photo-uploader/src/App.tsx new file mode 100644 index 00000000..4bfdb76a --- /dev/null +++ b/examples/react/photo-uploader/src/App.tsx @@ -0,0 +1,31 @@ +import { Authenticator, Uploader, UploadsList, W3APIProvider } from '@w3ui/react' +import { useUploadsList } from '@w3ui/react-uploads-list' +import './App.css' + +function PhotosList () { + const [{ data }] = useUploadsList() + return ( +
+ {data?.map(upload => ( + + ))} +
+ ) +} + +function App () { + return ( +
+ + + + + + +
+ ) +} + +export default App diff --git a/examples/react/photo-uploader/src/assets/react.svg b/examples/react/photo-uploader/src/assets/react.svg new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ b/examples/react/photo-uploader/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/react/photo-uploader/src/index.css b/examples/react/photo-uploader/src/index.css new file mode 100644 index 00000000..917888c1 --- /dev/null +++ b/examples/react/photo-uploader/src/index.css @@ -0,0 +1,70 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/react/photo-uploader/src/main.tsx b/examples/react/photo-uploader/src/main.tsx new file mode 100644 index 00000000..791f139e --- /dev/null +++ b/examples/react/photo-uploader/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + , +) diff --git a/examples/react/photo-uploader/src/vite-env.d.ts b/examples/react/photo-uploader/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/examples/react/photo-uploader/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/react/photo-uploader/tsconfig.json b/examples/react/photo-uploader/tsconfig.json new file mode 100644 index 00000000..3d0a51a8 --- /dev/null +++ b/examples/react/photo-uploader/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/react/photo-uploader/tsconfig.node.json b/examples/react/photo-uploader/tsconfig.node.json new file mode 100644 index 00000000..9d31e2ae --- /dev/null +++ b/examples/react/photo-uploader/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/react/photo-uploader/vite.config.ts b/examples/react/photo-uploader/vite.config.ts new file mode 100644 index 00000000..5a33944a --- /dev/null +++ b/examples/react/photo-uploader/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/packages/react-uploads-list/src/UploadsList.tsx b/packages/react-uploads-list/src/UploadsList.tsx index 6a27224c..ef388a3d 100644 --- a/packages/react-uploads-list/src/UploadsList.tsx +++ b/packages/react-uploads-list/src/UploadsList.tsx @@ -1,7 +1,7 @@ import type { As, Component, Props, Options, RenderProp } from 'ariakit-react-utils' import type { UploadsListContextState, UploadsListContextActions } from '@w3ui/uploads-list-core' -import React, { Fragment, createContext, useContext, useMemo, useCallback, useEffect } from 'react' +import React, { Fragment, createContext, useContext, useMemo, useCallback } from 'react' import { createComponent, createElement } from 'ariakit-react-utils' import { useUploadsList } from './providers/UploadsList' @@ -68,10 +68,6 @@ export const UploadsListRoot = (props: UploadsListRootProps): JSX.Element => { } else { renderedChildren = children as React.ReactNode } - useEffect(() => { - // load the first page of results asynchronously - void actions.next() - }, []) return ( {renderedChildren} diff --git a/packages/react-uploads-list/src/providers/UploadsList.tsx b/packages/react-uploads-list/src/providers/UploadsList.tsx index a0c81247..9c075eb8 100644 --- a/packages/react-uploads-list/src/providers/UploadsList.tsx +++ b/packages/react-uploads-list/src/providers/UploadsList.tsx @@ -1,4 +1,4 @@ -import React, { useContext, createContext, useState } from 'react' +import React, { useContext, createContext, useState, useEffect } from 'react' import { UploadListResult, UploadsListContextState, UploadsListContextActions, ServiceConfig, list } from '@w3ui/uploads-list-core' import { useKeyring } from '@w3ui/react-keyring' import { list as uploadList } from '@web3-storage/capabilities/upload' @@ -82,6 +82,9 @@ export function UploadsListProvider ({ size, servicePrincipal, connection, child } } + // we should reload the page any time the space or agent change + useEffect(() => { void loadPage() }, [space, agent]) + return ( {children} @@ -91,7 +94,7 @@ export function UploadsListProvider ({ size, servicePrincipal, connection, child /** * Use the scoped uploads list context state from a parent `UploadsListProvider`. - */ +*/ export function useUploadsList (): UploadsListContextValue { return useContext(UploadsListContext) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44590e41..c50c4f4c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -120,6 +120,29 @@ importers: '@vitejs/plugin-react': 3.0.0_vite@4.0.3 vite: 4.0.3 + examples/react/photo-uploader: + specifiers: + '@types/react': ^18.0.26 + '@types/react-dom': ^18.0.9 + '@vitejs/plugin-react': ^3.0.0 + '@w3ui/react': workspace:^1.0.0 + '@w3ui/react-uploads-list': workspace:^2.0.1 + react: ^18.2.0 + react-dom: ^18.2.0 + typescript: ^4.9.3 + vite: ^4.0.0 + dependencies: + '@w3ui/react': link:../../../packages/react + '@w3ui/react-uploads-list': link:../../../packages/react-uploads-list + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + devDependencies: + '@types/react': 18.0.26 + '@types/react-dom': 18.0.9 + '@vitejs/plugin-react': 3.0.0_vite@4.0.3 + typescript: 4.9.4 + vite: 4.0.3 + examples/react/playground: specifiers: '@storybook/addon-actions': ^6.5.15 @@ -2382,23 +2405,23 @@ packages: '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.20.12 dev: true - /@babel/plugin-transform-react-jsx-self/7.18.6_@babel+core@7.20.5: + /@babel/plugin-transform-react-jsx-self/7.18.6_@babel+core@7.20.12: resolution: {integrity: sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.20.5: + /@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.20.12: resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -5301,9 +5324,9 @@ packages: peerDependencies: vite: ^4.0.0 dependencies: - '@babel/core': 7.20.5 - '@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.20.5 - '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.5 + '@babel/core': 7.20.12 + '@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.12 magic-string: 0.27.0 react-refresh: 0.14.0 vite: 4.0.3