Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
paulrouge committed Feb 18, 2023
1 parent b3b5e77 commit e9350bd
Show file tree
Hide file tree
Showing 34 changed files with 2,406 additions and 993 deletions.
57 changes: 17 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,24 @@
# TypeScript Next.js example
# my nextjs web3 template

This is a really simple project that shows the usage of Next.js with TypeScript.
Uses tailwind, typescript and ethers.js to connect to metamask / hana.

## Deploy your own
## GlobalContext
In utils/context/GlobalContext.tsx you can find the global context. The global context is provided
in _app.tsx and can be used in any component.

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or preview live with [StackBlitz](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-typescript)
## Index.tsx | important!
pages/index.tsx is where the global logic regarding the connection to metamask / hana is handled.
It checks if:
- the connected device is a mobile device and if so, if it the hana or metamask app.
- if not mobile, is hana or metamask installed? If so, connect to on of them.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-typescript&project-name=with-typescript&repository-name=with-typescript)
On connecting the users pick is stored in the localStorage. When creating a provider and
signer, the localStorage is checked to see it it should use window.ethereum or window.hanaWallet.

## How to use it?
## Custom Fonts
To import a custom font you have to import it at _document.tsx and add it to the font array in the
in the tailwind config file.

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
## Custom Theme
To add a custom theme you have to add it to the tailwind config file.

```bash
npx create-next-app --example with-typescript with-typescript-app
```

```bash
yarn create next-app --example with-typescript with-typescript-app
```

```bash
pnpm create next-app --example with-typescript with-typescript-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

## Notes

This example shows how to integrate the TypeScript type system into Next.js. Since TypeScript is supported out of the box with Next.js, all we have to do is to install TypeScript.

```
npm install --save-dev typescript
```

To enable TypeScript's features, we install the type declarations for React and Node.

```
npm install --save-dev @types/react @types/react-dom @types/node
```

When we run `next dev` the next time, Next.js will start looking for any `.ts` or `.tsx` files in our project and builds it. It even automatically creates a `tsconfig.json` file for our project with the recommended settings.

Next.js has built-in TypeScript declarations, so we'll get autocompletion for Next.js' modules straight away.

A `type-check` script is also added to `package.json`, which runs TypeScript's `tsc` CLI in `noEmit` mode to run type-checking separately. You can then include this, for example, in your `test` scripts.
94 changes: 94 additions & 0 deletions components/BuyTicketsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React, {useState} from 'react'
import { ethers } from 'ethers'
import lotteryAbi from '../utils/contracts/lottery_abi.json'
import { ContractAddresses } from '../utils/constants/addresses'
import { useGlobalContext } from '../utils/context/globalContext'

type LotteryInstanceProps = {
id: number,
ticketPrice: number,
totalTickets: number,
ticketsSold: number,
closed: boolean,
exists: boolean,
owner: string,
}

type Props = {
modalOpen: boolean,
setModalOpen: React.Dispatch<React.SetStateAction<boolean>>
lottery: LotteryInstanceProps
}

const BuyTicketsModal = (props: Props) => {
const { signer, setTransactionToCheck, lotteryId } = useGlobalContext()
const contractSigner = new ethers.Contract(ContractAddresses.lotteryContract, lotteryAbi.abi, signer)
const [amountOfTickets, setAmountOfTickets] = useState<number>(1)

const handleAmountOfTicketsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setAmountOfTickets(parseInt(e.target.value))
if (parseInt(e.target.value) > props.lottery.totalTickets) {
setAmountOfTickets(props.lottery.totalTickets)
}
}

const handleBuyTickets = async () => {
const options = {
value: ethers.parseEther((amountOfTickets * props.lottery.ticketPrice).toString()),
gasLimit: 10000000
}
try{
const tx:ethers.ContractTransactionResponse = await contractSigner.buyTickets(lotteryId, amountOfTickets, options)
setTransactionToCheck(tx)
} catch (e) {
console.log(e)
}

}


return (
<div className='fixed top-0 left-0 backdrop-blur-sm w-screen h-screen absolute z-20 flex justify-center items-center'
onClick={()=>{props.setModalOpen(false)}}
>
<div className=' bg-funPurple rounded md:p-12 border-8 border-white z-40' onClick={(e) => e.stopPropagation()}>
<div className='font-bold text-5xl text-white'>
get your tickets!
</div>
<div className='mt-4'>
price per ticket {props.lottery.ticketPrice} ICZ
</div>
<div className='mt-2'>
total tickets left: {props.lottery.totalTickets}
</div>
<div className='mt-2'>
total tickets to buy: {isNaN(amountOfTickets) ? 0 : amountOfTickets}
</div>
<div className='mt-2 '>
<input
type='number'
className='w-full bg-funPurple text-white border rounded-md p-2 '
value={amountOfTickets}
onChange={(e) => handleAmountOfTicketsChange(e)}
/>
</div>

<div className='mt-2'>
cost: {isNaN(amountOfTickets) ? 0 : amountOfTickets * props.lottery.ticketPrice} ICZ
</div>
<div className='mt-2'>
<button className='
hover:bg-funBlue hover:text-funPurple border
border-white text-white rounded-md p-2 font-bold
'
onClick={() => handleBuyTickets()}
>
buy tickets
</button>
</div>
</div>
</div>
)
}

export default BuyTicketsModal
16 changes: 16 additions & 0 deletions components/ConnectButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useGlobalContext } from '../utils/context/globalContext';

export default function ConnectButton() {
const { setConnectModalOpen } = useGlobalContext();

return (
<div className='
fixed top-4 right-4 font-bold p-4
bg-funPurple text-white flex justify-center items-center rounded-md cursor-pointer
'
onClick={()=>setConnectModalOpen(true)}
>
QNect
</div>
);
}
19 changes: 19 additions & 0 deletions components/ConnectedAddress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react'
import { useGlobalContext } from '../utils/context/globalContext'

const ConnectedAddress = () => {
const { account } = useGlobalContext()

// format the account address
const formatAddress = (address: string) => {
if (!address) return ''
address = address[0]
return address.slice(0, 6) + '...' + address.slice(-4)
}

return (
<div>Connected: {formatAddress(account)}</div>
)
}

export default ConnectedAddress
16 changes: 2 additions & 14 deletions components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
import React, { ReactNode } from 'react'
import Link from 'next/link'
import Head from 'next/head'

type Props = {
children?: ReactNode
title?: string
}

const Layout = ({ children, title = 'This is the default title' }: Props) => (
<div>
const Layout = ({ children, title = 'Snow Lottery' }: Props) => (
<div className=''>
<Head>
<title>{title}</title>
<meta charSet="utf-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<header>
<nav>
<Link href="/">Home</Link> | <Link href="/about">About</Link> |{' '}
<Link href="/users">Users List</Link> |{' '}
<a href="/api/users">Users API</a>
</nav>
</header>
{children}
<footer>
<hr />
<span>I'm here to stay (Footer)</span>
</footer>
</div>
)

Expand Down
19 changes: 0 additions & 19 deletions components/List.tsx

This file was deleted.

16 changes: 0 additions & 16 deletions components/ListDetail.tsx

This file was deleted.

16 changes: 0 additions & 16 deletions components/ListItem.tsx

This file was deleted.

36 changes: 36 additions & 0 deletions components/ListTicketOwners.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React,{useEffect, useState} from 'react'
import { useGlobalContext } from '../utils/context/globalContext'
import { ethers } from 'ethers'
import lotteryAbi from '../utils/contracts/lottery_abi.json'
import { ContractAddresses } from '../utils/constants/addresses'

type Ticket = {
address: string,
id: number,
}

const TicketOwner = (props:Ticket) => {
return (
<div>
ticket #{props.id} - {props.address.slice(0, 6) + '...' + props.address.slice(-4)}
</div>
)
}

const ListTicketOwners = ({ticketOwners}) => {
const { provider, lotteryId } = useGlobalContext()
// const [ticketOwners, setTicketOwners] = useState<Ticket[]>([])

return (
<div className='mt-4 bg-funBlue p-2 rounded'>
<div className='text-xl mb-2'>Entries:</div>
<div className='h-48 overflow-scroll bg-funRed rounded p-2'>
{ticketOwners.length > 0 && ticketOwners.map((ticket, index) => {
return <TicketOwner key={index} address={ticket.address} id={ticket.id}/>
})}
</div>
</div>
)
}

export default ListTicketOwners
Loading

1 comment on commit e9350bd

@vercel
Copy link

@vercel vercel bot commented on e9350bd Feb 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

snowlottery – ./

snowlottery.vercel.app
snowlottery-paulrouge.vercel.app
snowlottery-git-main-paulrouge.vercel.app

Please sign in to comment.