Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
}
]
],
"plugins": []
"plugins": [
"@babel/plugin-syntax-jsx",
"babel-plugin-macros"
]
}
20 changes: 20 additions & 0 deletions components/Error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
interface ErrorProps {
code?: number
message: string
}

export const Error: React.FC<ErrorProps> = ({ code, message }) => {
return (
<div className="center-vertical-screen space-y-5">
<img
className="h-28 sm:h-48 dark:invert"
alt=""
src="/logo.svg"
/>
<div className="text-center space-y-1">
<h1 className="text-4xl">{code}</h1>
<p>{message}</p>
</div>
</div>
)
}
20 changes: 20 additions & 0 deletions components/baseButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ButtonHTMLAttributes, FC } from 'react'

import classNames from 'clsx'

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant
}

type ButtonVariant = 'outline'

export const Button: FC<ButtonProps> = ({ className, variant, ...props }) => (
<button
{...props}
className={classNames(
variant === 'outline' ? '' : '',
'border-font-light dark:border-font-dark border-x border-y px-5 py-1 rounded transition-colors',
className
)}
/>
)
18 changes: 18 additions & 0 deletions components/navbar/DropdownTransition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Transition } from '@headlessui/react'
import { FC, Fragment, ReactNode } from 'react'

export const DropdownTransition: FC<{ children: ReactNode }> = ({
children,
}) => (
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
{children}
</Transition>
)
95 changes: 95 additions & 0 deletions components/navbar/ProfileDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { faWallet } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { Menu } from '@headlessui/react'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
import Jazzicon, { jsNumberForAddress } from 'react-jazzicon'

import { useMetamaskStore } from 'kindelia/metamask/useMetamaskStore'
import { classNames } from 'kindelia/react/classNames'

import { DropdownTransition } from './DropdownTransition'

export default function ProfileDropdown() {
const router = useRouter()

const [account, login, logout, handleAccountsChanged] = useMetamaskStore(
(store) => [
store.account,
store.login,
store.logout,
store.handleAccountsChanged,
]
)

useEffect(() => {
if (!window.ethereum) return

const handleAccount = async (accounts: string[]) => {
handleAccountsChanged(accounts)
router.reload()
}

window.ethereum.on('chainChanged', router.reload)
window.ethereum.on('accountsChanged', handleAccount as any)

return () => {
window.ethereum?.removeAllListeners()
}
}, [router, handleAccountsChanged])

return (
<Menu as="div" className="ml-3 relative">
<div>
<Menu.Button className="w-8 flex text-sm rounded-full ">
<span className="sr-only">Open user menu</span>
{account ? (
<Jazzicon diameter={32} seed={jsNumberForAddress(account)} />
) : (
<FontAwesomeIcon size="lg" icon={faWallet} />
)}
</Menu.Button>
</div>
<DropdownTransition>
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
{account ? (
<>
<Menu.Item>
<span className="px-4 py-2 w-full block text-center text-gray-700">
{account.substring(0, 5)}...{account.slice(-4)}
</span>
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
onClick={logout}
className={classNames(
active ? 'bg-gray-100' : '',
'block px-4 py-2 text-sm text-gray-700 w-full'
)}
>
Logout
</button>
)}
</Menu.Item>
</>
) : (
<Menu.Item>
{({ active }) => (
<button
onClick={login}
className={classNames(
active ? 'bg-gray-100' : '',
'block px-4 py-2 text-sm text-gray-700 w-full'
)}
>
{window.ethereum ? 'Login Metamask' : 'Metamask not found'}
</button>
)}
</Menu.Item>
)}
</Menu.Items>
</DropdownTransition>
</Menu>
)
}
15 changes: 15 additions & 0 deletions components/navbar/Searchbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useState } from 'react'

import { classNames } from 'kindelia/react/classNames'

export default function Searchbar(props: { className: any }) {
const [search, setSearch] = useState('')
return (
<input
onChange={(e) => setSearch(e.currentTarget.value)}
value={search}
placeholder="🔎 Search"
className={classNames('themeDefault2 px-3 py-2', props.className)}
/>
)
}
61 changes: 61 additions & 0 deletions components/navbar/SelectNode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Menu } from '@headlessui/react'
import { useRouter } from 'next/router'
import { FC, useEffect, useRef } from 'react'

import { classNames } from 'kindelia/react/classNames'
import { useNodeStore } from 'kindelia/store/useNodeStore'

import { DropdownTransition } from './DropdownTransition'

export const SelectNode: FC = () => {
const [nodes, selectedNode, selectNode] = useNodeStore((store) => [
store.nodes,
store.selectedNode,
store.selectNode,
])

const server = useRef(true)

const router = useRouter()

useEffect(() => {
if (server.current) {
server.current = false
return
}

router.replace(router.asPath)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedNode])

return (
<Menu
as="div"
className="border-2 px-1 mx-2 my-2 rounded-md font-medium relative border-x border-y themeBorder"
>
<Menu.Button className="w-28 text-sm ">{selectedNode.name}</Menu.Button>
<DropdownTransition>
<Menu.Items className="z-10 flex flex-col absolute mt-2 w-28 rounded-md shadow-lg py-1 ring-1 focus:outline-none themeDefault">
{nodes.map((node) => (
<Menu.Item key={node.name} disabled={selectedNode === node}>
{({ disabled }) => (
<button
className={classNames(
disabled
? 'font-bold border-8 border-searchbar-light dark:border-searchbar-dark'
: '',
'text-sm py-2 themeHover'
)}
disabled={disabled}
onClick={() => selectNode(node)}
>
{node.name}
</button>
)}
</Menu.Item>
))}
</Menu.Items>
</DropdownTransition>
</Menu>
)
}
34 changes: 34 additions & 0 deletions components/navbar/ToggleTheme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'

import { classNames } from 'kindelia/react/classNames'

export default function ToggleTheme() {
const [mounted, setMounted] = useState(false)
const { theme, setTheme } = useTheme()

useEffect(() => setMounted(true), [])

const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light')
}

return (
<button
onClick={toggleTheme}
id="toggleTheme"
className="self-center flex pr-2"
>
{mounted && (
<div className="w-9 h-5 flex items-center bg-gray-300 rounded-full relative">
<div
className={classNames(
'w-4 h-4 bg-white rounded-full shadow absolute transition-all',
theme === 'light' ? 'right-5' : 'right-0'
)}
/>
</div>
)}
</button>
)
}
12 changes: 12 additions & 0 deletions components/navbar/ViewNotification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { BellIcon } from '@heroicons/react/outline'
export default function ViewNotification() {
return (
<button
type="button"
className="bg-gray-200 p-1 rounded-full text-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
>
<span className="sr-only">View notifications</span>
<BellIcon className="h-6 w-6" aria-hidden="true" />
</button>
)
}
Loading