-
Notifications
You must be signed in to change notification settings - Fork 272
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
740 additions
and
40 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,7 @@ | ||
# See http://help.github.com/ignore-files/ for more about ignoring files. | ||
|
||
# compiled output | ||
dist | ||
|
||
# dependencies | ||
node_modules |
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,54 @@ | ||
# React + TypeScript + Vite | ||
|
||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. | ||
|
||
Currently, two official plugins are available: | ||
|
||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh | ||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh | ||
|
||
## Expanding the ESLint configuration | ||
|
||
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: | ||
|
||
```js | ||
export default tseslint.config({ | ||
extends: [ | ||
// Remove ...tseslint.configs.recommended and replace with this | ||
...tseslint.configs.recommendedTypeChecked, | ||
// Alternatively, use this for stricter rules | ||
...tseslint.configs.strictTypeChecked, | ||
// Optionally, add this for stylistic rules | ||
...tseslint.configs.stylisticTypeChecked, | ||
], | ||
languageOptions: { | ||
// other options... | ||
parserOptions: { | ||
project: ['./tsconfig.node.json', './tsconfig.app.json'], | ||
tsconfigRootDir: import.meta.dirname, | ||
}, | ||
}, | ||
}) | ||
``` | ||
|
||
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: | ||
|
||
```js | ||
// eslint.config.js | ||
import reactX from 'eslint-plugin-react-x' | ||
import reactDom from 'eslint-plugin-react-dom' | ||
|
||
export default tseslint.config({ | ||
plugins: { | ||
// Add the react-x and react-dom plugins | ||
'react-x': reactX, | ||
'react-dom': reactDom, | ||
}, | ||
rules: { | ||
// other rules... | ||
// Enable its recommended typescript rules | ||
...reactX.configs['recommended-typescript'].rules, | ||
...reactDom.configs.recommended.rules, | ||
}, | ||
}) | ||
``` |
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,28 @@ | ||
import js from '@eslint/js' | ||
import globals from 'globals' | ||
import reactHooks from 'eslint-plugin-react-hooks' | ||
import reactRefresh from 'eslint-plugin-react-refresh' | ||
import tseslint from 'typescript-eslint' | ||
|
||
export default tseslint.config( | ||
{ ignores: ['dist'] }, | ||
{ | ||
extends: [js.configs.recommended, ...tseslint.configs.recommended], | ||
files: ['**/*.{ts,tsx}'], | ||
languageOptions: { | ||
ecmaVersion: 2020, | ||
globals: globals.browser, | ||
}, | ||
plugins: { | ||
'react-hooks': reactHooks, | ||
'react-refresh': reactRefresh, | ||
}, | ||
rules: { | ||
...reactHooks.configs.recommended.rules, | ||
'react-refresh/only-export-components': [ | ||
'warn', | ||
{ allowConstantExport: true }, | ||
], | ||
}, | ||
}, | ||
) |
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,13 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="http://docs.base.org/favicon.ico" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Account Manifest Generator</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.tsx"></script> | ||
</body> | ||
</html> |
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,35 @@ | ||
{ | ||
"name": "account-manifest", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "tsc -b && vite build", | ||
"copy": "cp -R ./dist/* ../create-onchain/src/manifest", | ||
"build:copy": "npm run build && npm run copy", | ||
"lint": "eslint .", | ||
"preview": "vite preview" | ||
}, | ||
"dependencies": { | ||
"@coinbase/onchainkit": "^0.37.5", | ||
"react": "^19.0.0", | ||
"react-dom": "^19.0.0", | ||
"wagmi": "^2.14.12" | ||
}, | ||
"devDependencies": { | ||
"@eslint/js": "^9.21.0", | ||
"@tailwindcss/vite": "^4.0.9", | ||
"@types/react": "^19.0.10", | ||
"@types/react-dom": "^19.0.4", | ||
"@vitejs/plugin-react": "^4.3.4", | ||
"eslint": "^9.21.0", | ||
"eslint-plugin-react-hooks": "^5.1.0", | ||
"eslint-plugin-react-refresh": "^0.4.19", | ||
"globals": "^15.15.0", | ||
"tailwindcss": "^4.0.9", | ||
"typescript": "~5.7.2", | ||
"typescript-eslint": "^8.24.1", | ||
"vite": "^6.2.0" | ||
} | ||
} |
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,26 @@ | ||
import { base } from 'wagmi/chains'; | ||
import { OnchainKitProvider } from '@coinbase/onchainkit'; | ||
import Page from './components/Page'; | ||
|
||
function App() { | ||
return ( | ||
<OnchainKitProvider | ||
chain={base} | ||
config={{ | ||
appearance: { | ||
name: 'Account Manifest Generator', | ||
logo: 'https://pbs.twimg.com/media/GkXUnEnaoAIkKvG?format=jpg&name=medium', | ||
mode: 'auto', | ||
theme: 'base', | ||
}, | ||
wallet: { | ||
display: 'modal', | ||
} | ||
}} | ||
> | ||
<Page /> | ||
</OnchainKitProvider> | ||
) | ||
} | ||
|
||
export default App |
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,177 @@ | ||
import { useEffect, useRef, useState } from 'react' | ||
import { useAccount } from 'wagmi'; | ||
import { | ||
ConnectWallet, | ||
Wallet, | ||
WalletDropdown, | ||
WalletDropdownDisconnect, | ||
} from '@coinbase/onchainkit/wallet'; | ||
import { | ||
Address, | ||
Avatar, | ||
Name, | ||
Identity, | ||
EthBalance, | ||
} from '@coinbase/onchainkit/identity'; | ||
import { Step } from './Step'; | ||
import { useGetFid } from '../hooks/useGetFid'; | ||
import { validateUrl } from '../utils'; | ||
import { useSignManifest } from '../hooks/useSignManifest'; | ||
|
||
function Page() { | ||
const wsRef = useRef<WebSocket | null>(null); | ||
const [fid, setFid] = useState<number | null>(null); | ||
const [domain, setDomain] = useState<string>(''); | ||
const [domainError, setDomainError] = useState<string | null>(null); | ||
|
||
const getFid = useGetFid(); | ||
const { address } = useAccount(); | ||
const { isPending, error, generateAccountAssociation } = useSignManifest({ | ||
domain, | ||
fid, | ||
address, | ||
onSigned: (accountAssociation) => { | ||
wsRef.current?.send(JSON.stringify(accountAssociation)); | ||
window.close(); | ||
} | ||
}); | ||
|
||
useEffect(() => { | ||
wsRef.current = new WebSocket('ws://localhost:3333'); | ||
|
||
return () => { | ||
wsRef.current?.close(); | ||
} | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (address) { | ||
getFid(address).then(setFid); | ||
} | ||
}, [address, getFid]) | ||
|
||
useEffect(() => { | ||
// super hacky way to remove the sign up button and 'or continue' div from the wallet modal | ||
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: cause above | ||
const observer = new MutationObserver((mutations) => { | ||
for (const mutation of mutations) { | ||
if (mutation.addedNodes.length) { | ||
const modal = document.querySelector('[data-testid="ockModalOverlay"]'); | ||
if (modal) { | ||
const signUpButton = modal.querySelector<HTMLElement>('div > div.flex.w-full.flex-col.gap-3 > button:first-of-type'); | ||
const orContinueDiv = modal.querySelector<HTMLElement>('div > div.flex.w-full.flex-col.gap-3 > div.relative'); | ||
|
||
if (signUpButton) { | ||
signUpButton.style.display = 'none'; | ||
} | ||
if (orContinueDiv) { | ||
orContinueDiv.style.display = 'none'; | ||
} | ||
} | ||
} | ||
}; | ||
}); | ||
|
||
observer.observe(document.body, { | ||
childList: true, | ||
subtree: true | ||
}); | ||
|
||
return () => observer.disconnect(); | ||
}, []); | ||
|
||
const handleValidateUrl = () => { | ||
const isValid = validateUrl(domain); | ||
if (!isValid) { | ||
setDomainError('Invalid URL'); | ||
} | ||
}; | ||
|
||
const handleDomainChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
setDomain(e.target.value); | ||
setDomainError(null); | ||
} | ||
|
||
return ( | ||
<main className='flex min-h-screen w-[600px] flex-col gap-6 font-sans'> | ||
<Step | ||
label='1' | ||
description={ | ||
<> | ||
<p className='p-4'>Use coinbase smart wallet if you have a passkey farcaster account through TBA</p> | ||
<p className='p-4'>Use MetaMask or Phantom to set up a wallet using your warpcast recovery key</p> | ||
</> | ||
} | ||
> | ||
<Wallet className='w-[206px]'> | ||
<ConnectWallet className='w-full'> | ||
<Avatar className="h-6 w-6" /> | ||
<Name /> | ||
</ConnectWallet> | ||
<WalletDropdown> | ||
<Identity className="px-4 pt-3 pb-2" hasCopyAddressOnClick={true}> | ||
<Avatar /> | ||
<Name /> | ||
<Address /> | ||
<EthBalance /> | ||
</Identity> | ||
<WalletDropdownDisconnect /> | ||
</WalletDropdown> | ||
</Wallet> | ||
</Step> | ||
|
||
<Step | ||
label='2' | ||
disabled={!address} | ||
description={ | ||
<> | ||
<p className='p-4'>Enter the domain your app will be hosted on</p> | ||
<p className='p-4'>This will be used to generate the account manifest and also added to your .env file as the `NEXT_PUBLIC_URL` variable</p> | ||
</> | ||
} | ||
> | ||
<div className='flex flex-col gap-2'> | ||
<input type="text" | ||
placeholder="Enter Domain" | ||
className='rounded border border-gray-300 px-4 py-2' | ||
value={domain} | ||
onChange={handleDomainChange} | ||
onBlur={handleValidateUrl} | ||
/> | ||
{domainError && <p className='text-red-500'>{domainError}</p>} | ||
</div> | ||
</Step> | ||
|
||
<Step | ||
label='3' | ||
disabled={!address || !domain || fid === 0} | ||
description={ | ||
<> | ||
<p className='p-4'>This will generate the account manifest and sign it with your wallet</p> | ||
<p className='p-4'>The account manifest will be saved to your .env file as `FARCASTER_HEADER`, `FARCASTER_PAYLOAD` and `FARCASTER_SIGNATURE` variables</p> | ||
</> | ||
} | ||
> | ||
<div className='flex flex-col gap-2'> | ||
{fid === 0 | ||
? <p className='text-red-500'>There is no FID associated with this account, please connect with your TBA passkey account.</p> | ||
: <p>Your FID is {fid}</p>} | ||
|
||
<button | ||
type="button" | ||
disabled={!address || !domain || fid === 0} | ||
onClick={generateAccountAssociation} | ||
className={`rounded px-4 py-2 text-white ${ | ||
!address || !domain || fid === 0 ? 'bg-blue-200!' : 'bg-blue-800!' | ||
}`} | ||
> | ||
{isPending ? 'Signing...' : 'Sign Account Manifest'} | ||
</button> | ||
{error && <p className='text-red-500'>{error.message.split('\n').map(line => <span key={line}>{line}<br /></span>)}</p>} | ||
</div> | ||
</Step> | ||
</main> | ||
) | ||
} | ||
|
||
export default Page; |
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,20 @@ | ||
type StepProps = { | ||
disabled?: boolean; | ||
label: string; | ||
children: React.ReactNode; | ||
description?: React.ReactNode; | ||
} | ||
|
||
export function Step({disabled, label, description, children}: StepProps) { | ||
return ( | ||
<div className={`flex w-full items-center gap-4 ${disabled ? 'opacity-50' : ''}`}> | ||
<div className='flex h-[50px] w-[50px] shrink-0 items-center justify-center rounded-full border-2 border-gray-300 p-4'> | ||
{label} | ||
</div> | ||
<div className='flex-shrink-0 flex-grow-0 basis-[206px]'> | ||
{children} | ||
</div> | ||
{description && <div className='text-gray-500 text-sm'>{description}</div>} | ||
</div> | ||
) | ||
} |
Oops, something went wrong.