forked from solana-labs/governance-ui
-
Notifications
You must be signed in to change notification settings - Fork 1
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
1 parent
17d5d98
commit 235874b
Showing
28 changed files
with
1,922 additions
and
259 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,4 @@ | ||
declare module '*.svg' { | ||
const content: any | ||
export default content | ||
} |
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,62 @@ | ||
import { | ||
AccountInfo, | ||
Connection, | ||
PublicKey, | ||
Transaction, | ||
} from '@solana/web3.js' | ||
import Wallet from '@project-serum/sol-wallet-adapter' | ||
|
||
export interface ConnectionContextValues { | ||
endpoint: string | ||
setEndpoint: (newEndpoint: string) => void | ||
connection: Connection | ||
sendConnection: Connection | ||
availableEndpoints: EndpointInfo[] | ||
setCustomEndpoints: (newCustomEndpoints: EndpointInfo[]) => void | ||
} | ||
|
||
export interface EndpointInfo { | ||
name: string | ||
url: string | ||
websocket: string | ||
} | ||
|
||
export interface WalletContextValues { | ||
wallet: Wallet | ||
connected: boolean | ||
providerUrl: string | ||
setProviderUrl: (newProviderUrl: string) => void | ||
providerName: string | ||
} | ||
|
||
export interface TokenAccount { | ||
pubkey: PublicKey | ||
account: AccountInfo<Buffer> | null | ||
effectiveMint: PublicKey | ||
} | ||
|
||
/** | ||
* {tokenMint: preferred token account's base58 encoded public key} | ||
*/ | ||
export interface SelectedTokenAccounts { | ||
[tokenMint: string]: string | ||
} | ||
|
||
// Token infos | ||
export interface KnownToken { | ||
tokenSymbol: string | ||
tokenName: string | ||
icon?: string | ||
mintAddress: string | ||
} | ||
|
||
export interface WalletAdapter { | ||
publicKey: PublicKey | ||
autoApprove: boolean | ||
connected: boolean | ||
signTransaction: (transaction: Transaction) => Promise<Transaction> | ||
signAllTransactions: (transaction: Transaction[]) => Promise<Transaction[]> | ||
connect: () => any | ||
disconnect: () => any | ||
on(event: string, fn: () => void): this | ||
} |
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,45 @@ | ||
import styled from '@emotion/styled' | ||
import useWalletStore from '../stores/useWalletStore' | ||
import { WALLET_PROVIDERS, DEFAULT_PROVIDER } from '../hooks/useWallet' | ||
import useLocalStorageState from '../hooks/useLocalStorageState' | ||
import WalletSelect from './WalletSelect' | ||
import WalletIcon from './WalletIcon' | ||
|
||
const StyledWalletTypeLabel = styled.div` | ||
font-size: 0.6rem; | ||
` | ||
|
||
const ConnectWalletButton = () => { | ||
const wallet = useWalletStore((s) => s.current) | ||
const [savedProviderUrl] = useLocalStorageState( | ||
'walletProvider', | ||
DEFAULT_PROVIDER.url | ||
) | ||
|
||
return ( | ||
<div className="flex justify-between border border-th-primary rounded-md h-11 w-48"> | ||
<button | ||
onClick={() => wallet.connect()} | ||
disabled={!wallet} | ||
className="text-th-primary hover:text-th-fgd-1 focus:outline-none disabled:text-th-fgd-4 disabled:cursor-wait" | ||
> | ||
<div className="flex flex-row items-center px-2 justify-center h-full rounded-l default-transition hover:bg-th-primary hover:text-th-fgd-1"> | ||
<WalletIcon className="w-5 h-5 mr-3 fill-current" /> | ||
<div> | ||
<span className="whitespace-nowrap">Connect Wallet</span> | ||
<StyledWalletTypeLabel className="font-normal text-th-fgd-1 text-left leading-3"> | ||
{WALLET_PROVIDERS.filter((p) => p.url === savedProviderUrl).map( | ||
({ name }) => name | ||
)} | ||
</StyledWalletTypeLabel> | ||
</div> | ||
</div> | ||
</button> | ||
<div className="relative h-full"> | ||
<WalletSelect isPrimary /> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
export default ConnectWalletButton |
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,113 @@ | ||
import { useEffect, useState } from 'react' | ||
import { | ||
CheckCircleIcon, | ||
InformationCircleIcon, | ||
XCircleIcon, | ||
} from '@heroicons/react/outline' | ||
import useNotificationStore from '../stores/useNotificationStore' | ||
|
||
const NotificationList = () => { | ||
const { notifications, set: setNotificationStore } = useNotificationStore( | ||
(s) => s | ||
) | ||
|
||
useEffect(() => { | ||
if (notifications.length > 0) { | ||
const id = setInterval(() => { | ||
setNotificationStore((state) => { | ||
state.notifications = notifications.slice(1, notifications.length) | ||
}) | ||
}, 5000) | ||
|
||
return () => { | ||
clearInterval(id) | ||
} | ||
} | ||
}, [notifications, setNotificationStore]) | ||
|
||
const reversedNotifications = [...notifications].reverse() | ||
|
||
return ( | ||
<div | ||
className={`fixed inset-0 flex items-end px-4 py-6 pointer-events-none sm:p-6`} | ||
> | ||
<div className={`flex flex-col w-full`}> | ||
{reversedNotifications.map((n, idx) => ( | ||
<Notification | ||
key={`${n.message}${idx}`} | ||
type={n.type} | ||
message={n.message} | ||
description={n.description} | ||
txid={n.txid} | ||
/> | ||
))} | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
const Notification = ({ type, message, description, txid }) => { | ||
const [showNotification, setShowNotification] = useState(true) | ||
|
||
if (!showNotification) return null | ||
|
||
return ( | ||
<div | ||
className={`max-w-sm w-full bg-th-bkg-3 shadow-lg rounded-md mt-2 pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden`} | ||
> | ||
<div className={`p-4`}> | ||
<div className={`flex items-center`}> | ||
<div className={`flex-shrink-0`}> | ||
{type === 'success' ? ( | ||
<CheckCircleIcon className={`text-th-green h-9 w-9 mr-1`} /> | ||
) : null} | ||
{type === 'info' && ( | ||
<XCircleIcon className={`text-th-primary h-9 w-9 mr-1`} /> | ||
)} | ||
{type === 'error' && ( | ||
<InformationCircleIcon className={`text-th-red h-9 w-9 mr-1`} /> | ||
)} | ||
</div> | ||
<div className={`ml-2 w-0 flex-1`}> | ||
<div className={`text-lg text-th-fgd-1`}>{message}</div> | ||
{description ? ( | ||
<p className={`mt-0.5 text-base text-th-fgd-2`}>{description}</p> | ||
) : null} | ||
{txid ? ( | ||
<a | ||
href={'https://explorer.solana.com/tx/' + txid} | ||
className="text-th-primary" | ||
> | ||
View transaction {txid.slice(0, 8)}... | ||
{txid.slice(txid.length - 8)} | ||
</a> | ||
) : null} | ||
</div> | ||
<div className={`ml-4 flex-shrink-0 self-start flex`}> | ||
<button | ||
onClick={() => setShowNotification(false)} | ||
className={`bg-th-bkg-3 rounded-md inline-flex text-fgd-3 hover:text-th-fgd-4 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-th-primary`} | ||
> | ||
<span className={`sr-only`}>Close</span> | ||
<svg | ||
className={`h-5 w-5`} | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 20 20" | ||
fill="currentColor" | ||
aria-hidden="true" | ||
> | ||
<path | ||
fillRule="evenodd" | ||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" | ||
clipRule="evenodd" | ||
/> | ||
</svg> | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
export default NotificationList |
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,25 @@ | ||
const WalletIcon = ({ className }) => { | ||
return ( | ||
<svg | ||
className={className} | ||
width="20" | ||
height="17" | ||
viewBox="0 0 20 17" | ||
fill="none" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
fillRule="evenodd" | ||
clipRule="evenodd" | ||
d="M14.625 8.24561C13.7276 8.24561 13 8.97325 13 9.87061C13 10.768 13.7276 11.4956 14.625 11.4956C15.5224 11.4956 16.25 10.768 16.25 9.87061C16.25 8.97325 15.5224 8.24561 14.625 8.24561ZM14 9.87061C14 9.52554 14.2799 9.24561 14.625 9.24561C14.9701 9.24561 15.25 9.52554 15.25 9.87061C15.25 10.2157 14.9701 10.4956 14.625 10.4956C14.2799 10.4956 14 10.2157 14 9.87061Z" | ||
/> | ||
<path | ||
fillRule="evenodd" | ||
clipRule="evenodd" | ||
d="M3.25 0.25C1.59301 0.25 0.25 1.59301 0.25 3.25L0.250676 13.8083C0.250702 15.4652 1.59371 16.8083 3.25068 16.8083H17.2147C18.5735 16.8083 19.7507 15.755 19.7507 14.3708V5.37076C19.7507 4.51126 19.2968 3.77937 18.6275 3.34799C18.6257 2.86554 18.5949 2.24606 18.3863 1.7108C18.2324 1.31604 17.973 0.930835 17.5462 0.652726C17.1244 0.377893 16.6042 0.25 16 0.25H3.25ZM17.6434 4.51627C17.6217 4.50923 17.6004 4.50122 17.5796 4.4923C17.4681 4.45439 17.3457 4.43326 17.2147 4.43326H4.81318C4.39896 4.43326 4.06318 4.09747 4.06318 3.68326C4.06318 3.26904 4.39896 2.93326 4.81318 2.93326H17.1143C17.0993 2.67796 17.0651 2.45157 16.9887 2.2555C16.9238 2.08899 16.8395 1.98262 16.7273 1.90947C16.61 1.83305 16.3958 1.75 16 1.75H3.25C2.42146 1.75 1.75003 2.42141 1.75 3.24995L1.75068 13.8082C1.75068 14.6368 2.42212 15.3083 3.25068 15.3083H17.2147C17.8262 15.3083 18.2507 14.8477 18.2507 14.3708V5.37076C18.2507 5.01586 18.0156 4.67002 17.6434 4.51627Z" | ||
/> | ||
</svg> | ||
) | ||
} | ||
|
||
export default WalletIcon |
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,64 @@ | ||
import { Menu } from '@headlessui/react' | ||
import { | ||
ChevronDownIcon, | ||
ChevronUpIcon, | ||
CheckCircleIcon, | ||
} from '@heroicons/react/outline' | ||
|
||
import useWalletStore from '../stores/useWalletStore' | ||
import { WALLET_PROVIDERS, DEFAULT_PROVIDER } from '../hooks/useWallet' | ||
import useLocalStorageState from '../hooks/useLocalStorageState' | ||
|
||
export default function WalletSelect({ isPrimary = false }) { | ||
const setWalletStore = useWalletStore((s) => s.set) | ||
const [savedProviderUrl] = useLocalStorageState( | ||
'walletProvider', | ||
DEFAULT_PROVIDER.url | ||
) | ||
|
||
const handleSelectProvider = (url) => { | ||
setWalletStore((state) => { | ||
state.providerUrl = url | ||
}) | ||
} | ||
|
||
return ( | ||
<Menu> | ||
{({ open }) => ( | ||
<> | ||
<Menu.Button | ||
className={`flex justify-center items-center h-full rounded-r rounded-l-none focus:outline-none text-th-primary hover:text-th-fgd-1 ${ | ||
isPrimary | ||
? 'px-3 hover:bg-th-primary' | ||
: 'px-2 hover:bg-th-bkg-3 border-l border-th-fgd-4' | ||
} cursor-pointer`} | ||
> | ||
{open ? ( | ||
<ChevronUpIcon className="h-5 w-5" /> | ||
) : ( | ||
<ChevronDownIcon className="h-5 w-5" /> | ||
)} | ||
</Menu.Button> | ||
<Menu.Items className="z-20 p-1 absolute right-0 top-11 bg-th-bkg-1 divide-y divide-th-bkg-3 shadow-lg outline-none rounded-md w-48"> | ||
{WALLET_PROVIDERS.map(({ name, url, icon }) => ( | ||
<Menu.Item key={name}> | ||
<button | ||
className="flex flex-row items-center justify-between w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer font-normal focus:outline-none" | ||
onClick={() => handleSelectProvider(url)} | ||
> | ||
<div className="flex"> | ||
<img src={icon} className="w-5 h-5 mr-2" /> | ||
{name} | ||
</div> | ||
{savedProviderUrl === url ? ( | ||
<CheckCircleIcon className="h-4 w-4 text-th-green" /> | ||
) : null}{' '} | ||
</button> | ||
</Menu.Item> | ||
))} | ||
</Menu.Items> | ||
</> | ||
)} | ||
</Menu> | ||
) | ||
} |
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,39 @@ | ||
import { useState, useRef, useEffect } from 'react' | ||
|
||
export function useEffectAfterTimeout(effect, timeout) { | ||
useEffect(() => { | ||
const handle = setTimeout(effect, timeout) | ||
return () => clearTimeout(handle) | ||
}) | ||
} | ||
|
||
export function useListener(emitter, eventName) { | ||
const [, forceUpdate] = useState(0) | ||
useEffect(() => { | ||
const listener = () => forceUpdate((i) => i + 1) | ||
emitter.on(eventName, listener) | ||
return () => emitter.removeListener(eventName, listener) | ||
}, [emitter, eventName]) | ||
} | ||
|
||
export default function useInterval(callback, delay) { | ||
const savedCallback = useRef<() => void>() | ||
|
||
// Remember the latest callback. | ||
useEffect(() => { | ||
savedCallback.current = callback | ||
}, [callback]) | ||
|
||
// Set up the interval. | ||
useEffect(() => { | ||
function tick() { | ||
savedCallback.current && savedCallback.current() | ||
} | ||
if (delay !== null) { | ||
const id = setInterval(tick, delay) | ||
return () => { | ||
clearInterval(id) | ||
} | ||
} | ||
}, [delay]) | ||
} |
Oops, something went wrong.