-
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.
Merge pull request #15 from ardriveapp/upload-folder-page
feat(upload folder): turbo upload folder MVP PE-4643
Showing
14 changed files
with
1,714 additions
and
369 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
v20.12.2 |
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,75 @@ | ||
.wallet-connector { | ||
padding: 0rem 1.5rem 1.5rem 1.5rem; | ||
} | ||
|
||
.wallet-info { | ||
font-family: Wavehaus-Bold; | ||
font-size: 1rem; | ||
color: var(--white); | ||
background-color: var(--off-gray); | ||
padding: 1.5rem; | ||
border-radius: 8px; | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
margin-bottom: 1rem; | ||
} | ||
|
||
.wallet-info .wallet-type { | ||
font-size: 0.9rem; | ||
font-weight: bold; | ||
color: var(--ardrive-red); | ||
margin-right: 0.5rem; | ||
} | ||
|
||
.wallet-info .wallet-address { | ||
font-family: Wavehaus-Bold; /* Optional: to give the address a monospaced look */ | ||
font-size: 1.1rem; | ||
color: var(--text-white); | ||
margin-right: 1rem; | ||
} | ||
|
||
.wallet-connector-buttons { | ||
display: flex; | ||
flex-direction: row; | ||
justify-content: center; | ||
gap: 1rem; | ||
width: 100%; | ||
} | ||
|
||
.wallet-connector button { | ||
background-color: var(--ardrive-red); | ||
color: var(--white); | ||
padding: 0.75rem 1.25rem; | ||
border: none; | ||
border-radius: 0.5rem; | ||
cursor: pointer; | ||
font-family: Wavehaus-Bold; | ||
width: auto; | ||
} | ||
|
||
.wallet-connector button:hover { | ||
background-color: var(--ardrive-red); | ||
filter: brightness(0.9); | ||
} | ||
|
||
.wallet-connector button:disabled { | ||
background-color: var(--light-gray); | ||
cursor: not-allowed; | ||
} | ||
|
||
.wallet-info button { | ||
font-size: 0.6rem; | ||
background-color: var(--light-gray); | ||
color: var(--black); | ||
border: none; | ||
padding: 0.25rem 0.75rem; | ||
border-radius: 0.25rem; | ||
cursor: pointer; | ||
margin-left: auto; /* Push the button to the right */ | ||
} | ||
|
||
.wallet-info button:hover { | ||
background-color: var(--ardrive-red); | ||
color: var(--white); | ||
} |
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,136 @@ | ||
import { | ||
TurboAuthenticatedClient, | ||
TurboFactory, | ||
SolanaWalletAdapter, | ||
ArconnectSigner, | ||
} from "@ardrive/turbo-sdk/web"; | ||
import { PublicKey } from "@solana/web3.js"; | ||
import { ethers } from "ethers"; | ||
import { useState } from "react"; | ||
import { turboConfig } from "../constants"; | ||
import { getArconnect } from "../utils/arconnect"; | ||
import "./TurboWalletConnector.css"; | ||
|
||
interface TurboWalletConnectorProps { | ||
setTurbo: (turbo: TurboAuthenticatedClient | undefined) => void; | ||
} | ||
|
||
export default function TurboWalletConnector({ | ||
setTurbo, | ||
}: TurboWalletConnectorProps): JSX.Element { | ||
const [currentToken, setCurrentToken] = useState<string | undefined>( | ||
undefined, | ||
); | ||
const [walletAddress, setWalletAddress] = useState<string | undefined>( | ||
undefined, | ||
); | ||
|
||
const setupTurbo = async (turbo: TurboAuthenticatedClient) => { | ||
setWalletAddress(await turbo.signer.getNativeAddress()); | ||
setTurbo(turbo); | ||
}; | ||
|
||
const disconnectWallet = () => { | ||
setTurbo(undefined); | ||
setCurrentToken(undefined); | ||
setWalletAddress(undefined); | ||
}; | ||
|
||
const connectEthWallet = async () => { | ||
try { | ||
// Check if MetaMask is installed | ||
|
||
if (window.ethereum) { | ||
// Find the MetaMask provider in the list | ||
const metaMaskProvider = window.ethereum.providers?.find( | ||
(provider) => provider.isMetaMask, | ||
); | ||
|
||
const provider = new ethers.BrowserProvider( | ||
metaMaskProvider ?? window.ethereum, | ||
); // Use the MetaMask provider | ||
const signer = await provider.getSigner(); | ||
|
||
await setupTurbo( | ||
TurboFactory.authenticated({ | ||
token: "ethereum", | ||
walletAdapter: { | ||
getSigner: () => signer, | ||
}, | ||
}), | ||
); | ||
|
||
setCurrentToken("ethereum"); | ||
} | ||
} catch (err) { | ||
console.error(err); | ||
} | ||
}; | ||
|
||
const connectSolWallet = async () => { | ||
try { | ||
// Check if Phantom is installed | ||
if (window.solana) { | ||
const provider = window.solana; | ||
|
||
const publicKey = new PublicKey((await provider.connect()).publicKey); | ||
|
||
const wallet: SolanaWalletAdapter = { | ||
publicKey, | ||
signMessage: async (message: Uint8Array) => { | ||
// Call Phantom's signMessage method | ||
const { signature } = await provider.signMessage(message); | ||
|
||
return signature; | ||
}, | ||
}; | ||
|
||
await setupTurbo( | ||
TurboFactory.authenticated({ | ||
token: "solana", | ||
walletAdapter: wallet, | ||
}), | ||
); | ||
setCurrentToken("solana"); | ||
} | ||
} catch (err) { | ||
console.error(err); | ||
} | ||
}; | ||
|
||
const connectArWallet = async () => { | ||
try { | ||
await getArconnect(); | ||
const arconnect_signer = new ArconnectSigner(window.arweaveWallet); | ||
|
||
await setupTurbo( | ||
TurboFactory.authenticated({ | ||
token: "arweave", | ||
signer: arconnect_signer, | ||
...turboConfig, | ||
}), | ||
); | ||
setCurrentToken("arweave"); | ||
} catch (err) { | ||
console.error(err); | ||
} | ||
}; | ||
return ( | ||
<div className="form wallet-connector"> | ||
<h3>{currentToken ? "Current" : "Connect"} Wallet</h3> | ||
{walletAddress && currentToken ? ( | ||
<p className="wallet-info"> | ||
<span className="wallet-type">{currentToken}</span>{" "} | ||
<span className="wallet-address">{walletAddress}</span> | ||
<button onClick={disconnectWallet}>Disconnect</button> | ||
</p> | ||
) : ( | ||
<div className="wallet-connector-buttons"> | ||
<button onClick={connectEthWallet}>Connect Ethereum Wallet</button> | ||
<button onClick={connectSolWallet}>Connect Solana Wallet</button> | ||
<button onClick={connectArWallet}>Connect Arweave Wallet</button> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} |
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,32 @@ | ||
import { TurboFactory } from "@ardrive/turbo-sdk/web"; | ||
import { useState, useRef, useEffect } from "react"; | ||
import { turboConfig, wincPerCredit } from "../constants"; | ||
|
||
export function useCreditsForToken( | ||
debouncedTokenAmount: string, | ||
errorCallback: (message: string) => void, | ||
): [string | undefined, string | undefined] { | ||
const [winc, setWinc] = useState<string | undefined>(undefined); | ||
const usdWhenCreditsWereLastUpdatedRef = useRef<string | undefined>( | ||
undefined, | ||
); | ||
|
||
// Get credits for USD amount when USD amount has stopped debouncing | ||
useEffect(() => { | ||
TurboFactory.unauthenticated(turboConfig) | ||
.getWincForToken({ tokenAmount: debouncedTokenAmount }) | ||
.then(({ winc }) => { | ||
usdWhenCreditsWereLastUpdatedRef.current = debouncedTokenAmount; | ||
setWinc(winc); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
errorCallback(`Error getting credits for USD amount: ${err.message}`); | ||
}); | ||
}, [debouncedTokenAmount, errorCallback]); | ||
|
||
return [ | ||
winc ? (+winc / wincPerCredit).toString() : undefined, | ||
usdWhenCreditsWereLastUpdatedRef.current, | ||
]; | ||
} |
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 |
---|---|---|
|
@@ -70,7 +70,8 @@ input[type="number"] { | |
color: var(--ardrive-red); | ||
} | ||
|
||
h1 { | ||
h1, | ||
h3 { | ||
font-family: Wavehaus-Extra; | ||
} | ||
|
||
|
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,108 @@ | ||
import { useState } from "react"; | ||
import { ErrMsgCallbackAsProps, ExtendedFileInputProps } from "../types"; | ||
import { ardriveAppUrl } from "../constants"; | ||
import { Page } from "./Page"; | ||
import { TurboAuthenticatedClient } from "@ardrive/turbo-sdk/web"; | ||
import { NewToArDriveInfo } from "../components/NewToArDriveInfo"; | ||
import TurboWalletConnector from "../components/TurboWalletConnector"; | ||
|
||
function UploadForm({ errorCallback }: ErrMsgCallbackAsProps) { | ||
const [selectedFiles, setSelectedFiles] = useState<FileList | null>(null); | ||
|
||
const [turbo, setTurbo] = useState<undefined | TurboAuthenticatedClient>( | ||
undefined, | ||
); | ||
const [sending, setSending] = useState<boolean>(false); | ||
|
||
// TODO: Query params if needed | ||
// const location = useLocation(); | ||
// useEffect(() => { | ||
|
||
// }, [location.search]); | ||
|
||
const canSubmitForm = !!turbo && !!selectedFiles && !sending; | ||
|
||
const handleSubmit = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { | ||
e.preventDefault(); | ||
|
||
if (!canSubmitForm) { | ||
return; | ||
} | ||
|
||
console.log("turbo", turbo); | ||
setSending(true); | ||
turbo | ||
.uploadFolder({ | ||
files: Array.from(selectedFiles), | ||
maxConcurrentUploads: 1, | ||
}) | ||
.catch((err: unknown) => { | ||
console.error("err", err); | ||
errorCallback( | ||
`Error uploading: ${err instanceof Error ? err.message : err}`, | ||
); | ||
// throw err; | ||
}) | ||
.then((res: unknown) => { | ||
console.log("res", res); | ||
setSending(false); | ||
|
||
// TODO: Success Modal or Page | ||
alert("Great Upload!!\n" + JSON.stringify(res)); | ||
}); | ||
}; | ||
|
||
return ( | ||
<> | ||
<h1>Upload a folder or file with your wallet</h1> | ||
<p> | ||
If you do not have an Arweave wallet, you can create one in{" "} | ||
{/* TODO: Create wallet from turbo sdk/app */} | ||
<a href={ardriveAppUrl}>ArDrive App</a>. | ||
</p> | ||
|
||
<TurboWalletConnector setTurbo={setTurbo} /> | ||
|
||
<form className="form"> | ||
<div className="form-section"> | ||
{/* TODO: Current balance of wallet in AR and Turbo Credits */} | ||
{/* TODO: Inputs for manifest options, concurrent uploads, etc. */} | ||
|
||
<label className="form-label">Upload Folder</label> | ||
<input | ||
type="file" | ||
webkitdirectory="true" | ||
onChange={(e) => setSelectedFiles(e.target.files)} | ||
{...({} as ExtendedFileInputProps)} // This line is a workaround for declaring webkitdirectory in TypeScript | ||
/> | ||
<br /> | ||
<label className="form-label">Upload Files</label> | ||
<input | ||
// webkitdirectory | ||
type="file" | ||
multiple | ||
onChange={(e) => setSelectedFiles(e.target.files)} | ||
/> | ||
</div> | ||
|
||
{sending && <p>Now Uploading...</p>} | ||
|
||
{/* TODO: Estimate price */} | ||
<button | ||
type="submit" | ||
className="proceed-button" | ||
onClick={(e) => handleSubmit(e)} | ||
disabled={!canSubmitForm} | ||
> | ||
Send Upload | ||
</button> | ||
</form> | ||
|
||
<NewToArDriveInfo /> | ||
</> | ||
); | ||
} | ||
|
||
export const UploadPage = () => ( | ||
<Page page={(e) => <UploadForm errorCallback={e} />} /> | ||
); |
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 |
---|---|---|
@@ -1,4 +1,31 @@ | ||
import { PublicKey } from "@solana/web3.js"; | ||
import { Eip1193Provider } from "ethers"; | ||
import { InputHTMLAttributes } from "react"; | ||
|
||
export type ErrMsgCallback = (error: string) => void; | ||
export type ErrMsgCallbackAsProps = { | ||
errorCallback: ErrMsgCallback; | ||
}; | ||
|
||
declare global { | ||
interface Window { | ||
ethereum: { | ||
isMetaMask?: boolean; | ||
providers?: Array<Eip1193Provider & { isMetaMask: boolean }>; | ||
request: (args: { | ||
method: string; | ||
params?: Array<unknown>; | ||
}) => Promise<unknown>; | ||
on: (eventName: string, callback: (...args: unknown[]) => void) => void; | ||
}; | ||
solana: { | ||
connect: () => Promise<{ publicKey: PublicKey }>; | ||
signMessage: (message: Uint8Array) => Promise<{ signature: Uint8Array }>; | ||
}; | ||
} | ||
} | ||
|
||
export interface ExtendedFileInputProps | ||
extends InputHTMLAttributes<HTMLInputElement> { | ||
webkitdirectory?: boolean; | ||
} |