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
4 changes: 2 additions & 2 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
NEXT_PUBLIC_EVENTS_FACTORY_CONTRACT_ADDRESS=
NEXT_PUBLIC_PINATA_GATEWAY=
PINATA_IPFS_ENDPOINT=
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=

LINEA_TEST_RPC_ENDPOINT=
Expand All @@ -8,6 +8,6 @@ PINATA_API_KEY=

INFURA_API_KEY=
INFURA_API_SECRET=

ACCOUNT_PRIVATE_KEY

NETWORK=localhost
3 changes: 3 additions & 0 deletions apps/web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ yarn-error.log*

# vercel
.vercel

# generated contract artifacts
lib/contracts/*
3 changes: 2 additions & 1 deletion apps/web/app/api/gas/route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { NextResponse } from "next/server";
import { env } from "@/env.mjs";

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const chainId = searchParams.get("chainId");

const Auth = Buffer.from(
process.env.INFURA_API_KEY + ":" + process.env.INFURA_API_SECRET
env.INFURA_API_KEY + ":" + env.INFURA_API_SECRET
).toString("base64");

const gasPricesResp = await fetch(
Expand Down
28 changes: 28 additions & 0 deletions apps/web/app/api/ipfs/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { env } from "@/env.mjs";

export async function POST(request: Request) {
const data = await request.formData();
const baseUrl = env.INFURA_IPFS_ENDPOINT;

try {
const res = await fetch(`${baseUrl}/api/v0/add`, {
method: "POST",
headers: {
Authorization:
"Basic " +
Buffer.from(
env.INFURA_API_KEY + ":" + env.INFURA_API_SECRET
).toString("base64"),
},
body: data,
});


const json = await res.json();

return Response.json({ hash: json.Hash });
} catch (error) {
console.error(error);
throw new Error("Error adding file");
}
}
15 changes: 2 additions & 13 deletions apps/web/app/events/[eventId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getEventContract } from "@/lib/getEventContract";
import { ContractPermission } from "@/types";
import { getEventById } from "@/lib/actions";
import Image from "next/image";
import { notFound } from "next/navigation";

Expand All @@ -12,17 +11,7 @@ export async function generateMetadata({
return notFound();
}

const eventContract = await getEventContract({
address: eventId,
permission: ContractPermission.READ,
});

const eventData = await Promise.all([
eventContract.title(),
eventContract.description(),
]);

const [title, description] = eventData;
const { title, description } = await getEventById(eventId);

return {
title: `${title} - Eventsea`,
Expand Down
50 changes: 9 additions & 41 deletions apps/web/app/events/[eventId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { notFound } from "next/navigation";
import GetTickets from "@/components/GetTickets";
import Image from "next/image";
import { getEventContract } from "@/lib/getEventContract";
import { format } from "date-fns";
import { getTicketContract } from "@/lib/getTicketContract";
import { ContractPermission } from "@/types";
import EventLocationMap from "@/components/event-location-map";

import { env } from "@/env.mjs";
import { getEventById, getTicketById } from "@/lib/actions";

type PageProps = {
params: {
eventId: string;
Expand All @@ -18,48 +18,16 @@ const EventPage = async ({ params: { eventId } }: PageProps) => {
return notFound();
}

const eventContract = await getEventContract({
address: eventId,
permission: ContractPermission.READ,
});

const eventData = await Promise.all([
eventContract.title(),
eventContract.description(),
eventContract.location(),
eventContract.eventType(),
eventContract.image(),
eventContract.date(),
eventContract.ticketNFT(),
]);

const [title, description, location, eventType, image, date, ticketNFT] =
eventData;

const ticketContract = await getTicketContract({
address: ticketNFT,
permission: ContractPermission.READ,
});

const ticketPrice = await ticketContract._ticketPrice();

const ticketId = await ticketContract.tokenId();
const { title, eventType, description, location, date, ticketNFT, image } =
await getEventById(eventId);
const { id, price } = await getTicketById(ticketNFT);

const formattedDate = format(new Date(Number(date) * 1000), "MMM. d");

return (
<div className="w-full">
<div className="relative w-full h-[450px] rounded-md mb-4 overflow-hidden">
<Image
src={
image
? `${process.env.NEXT_PUBLIC_PINATA_GATEWAY}/${image}`
: "/images/default.png"
}
alt={title}
layout="fill"
objectFit="cover"
/>
<Image src={image} alt={title} layout="fill" objectFit="cover" />
</div>

<div className="relative grid md:grid-cols-2 justify-items-center w-full gap-10">
Expand Down Expand Up @@ -88,11 +56,11 @@ const EventPage = async ({ params: { eventId } }: PageProps) => {

<div className="w-full md:pl-20">
<GetTickets
ticketPrice={ticketPrice}
ticketPrice={price}
ticketNFT={ticketNFT}
title={title}
date={date}
ticketId={ticketId}
ticketId={id}
/>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/components/CardSkeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const CardSkeleton = ({
onLoad={() => setIsLoading(false)}
onError={() => setIsLoading(false)}
className="opacity-0"
/>
/>
</div>
);
};
7 changes: 2 additions & 5 deletions apps/web/components/EventCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const EventCard = ({ event }: { event: EventSea.Event }) => {

return (
<>
{isLoading ? (
{false ? (
<CardSkeleton image={event.image} setIsLoading={setIsLoading} />
) : (
<article
Expand All @@ -24,10 +24,7 @@ export const EventCard = ({ event }: { event: EventSea.Event }) => {
<div className="h-full overflow-hidden transition-transform duration-300 ease-in-out origin-top rounded-lg group-hover:scale-x-105">
<Image
src={
event.image
? `${process.env.NEXT_PUBLIC_PINATA_GATEWAY}/${event.image}`
: "/images/default.png"
}
event.image}
onLoad={() => setIsLoading(false)}
objectFit="cover"
alt={event.title}
Expand Down
8 changes: 2 additions & 6 deletions apps/web/components/ForYou.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const ForYou: React.FC<Props> = ({ events }) => {
<TableBody className="md:space-y-4">
{events.map((event, index) => {
return (
<TableRow className="md:flex md:flex-col md:border-b-0">
<TableRow key={event.id} className="md:flex md:flex-col md:border-b-0">
<TableCell className="p-0">
<Link
className="grid items-center grid-cols-4 py-1 duration-100 bg-white md:border md:rounded-xl hover:bg-opacity-50"
Expand All @@ -45,11 +45,7 @@ const ForYou: React.FC<Props> = ({ events }) => {
>
<div className="flex items-center gap-3 px-2 md:p-4">
<Image
src={
event.image
? `${process.env.NEXT_PUBLIC_PINATA_GATEWAY}/${event.image}`
: "/images/default-thumb.png"
}
src={event.image}
className="hidden md:block"
alt={event.title}
width={50}
Expand Down
22 changes: 14 additions & 8 deletions apps/web/components/GetTickets.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"use client";

import { ethers, formatEther } from "ethers";
import { useState } from "react";
import { Button } from "./ui/Button";
import { getTicketContract } from "@/lib/getTicketContract";
import { ContractPermission } from "@/types";

import { GasFeeCard } from "./GasFeeCard";
import MetaMaskProvider from "@/providers/MetamaskProvider";
import { addTokenMetadata } from "@/lib/ipfs";
import { formatEther } from "ethers";
import TicketContract from "lib/contracts/artifacts/Ticket.sol/Ticket.json";
import { Ticket } from "lib/contracts/typechain-types/";

interface GetTicketsProps {
ticketPrice: bigint;
Expand Down Expand Up @@ -66,10 +66,16 @@ const GetTickets: React.FC<GetTicketsProps> = ({
};

const handleMinTickets = async () => {
const nftContract = await getTicketContract({
address: ticketNFT,
permission: ContractPermission.WRITE,
});
const provider = new ethers.BrowserProvider(window.ethereum!);

const signer = await provider.getSigner();

const ticketsContract = new ethers.Contract(
ticketNFT,
TicketContract.abi,
signer
) as unknown as Ticket;

if (numberOfTickets <= 0) {
console.log("Please select number of tickets");
return;
Expand All @@ -78,7 +84,7 @@ const GetTickets: React.FC<GetTicketsProps> = ({
const Hash = await handleUploadSVG();
const totalAmount = ticketPrice * BigInt(numberOfTickets);
const token = (
await nftContract.mint(
await ticketsContract.mint(
numberOfTickets,
`https://ipfs.io/ipfs/${Hash}`,
{
Expand Down
26 changes: 9 additions & 17 deletions apps/web/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,37 @@

import Link from "next/link";
import { useSDK } from "@metamask/sdk-react";
import { isHexString } from "ethers";
import EventSeaLogo from "../public/icons/EventSeaLogo";
import WalletIcon from "../public/icons/WalletIcon";
import CreateEvent from "@/components/create-event/create-event-form";
import { Button } from "./ui/Button";
import { SearchBar } from "./SearchBar";
import { formatAddress } from "./../lib/utils";
import { formatAddress, getAppChainId } from "./../lib/utils";
import {
Popover,
PopoverTrigger,
PopoverContent,
} from "@/components/ui/popover";
import MetaMaskProvider from "@/providers/MetamaskProvider";
import { useEffect, useState } from "react";

const LINEA_TESTNET_CHAIN = "0xe704";
import { env } from "@/env.mjs";

const appChainId = getAppChainId();

const switchEthereumChain = async () => {
if (!window.ethereum) return;

await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: LINEA_TESTNET_CHAIN }],
params: [{ chainId: appChainId }],
});

window.location.reload();
};

export const ConnectWalletButton = () => {
const [chainId, setChainId] = useState<string | null>(null);
const { sdk, connected, connecting, account } = useSDK();

useEffect(() => {
if (window?.ethereum?.chainId) {
setChainId(window?.ethereum?.chainId);
}
}, []);

const isOnLineaTestnet = chainId === LINEA_TESTNET_CHAIN;
const isOnLocal = chainId === "0x7a69";
const { sdk, connected, connecting, chainId, account } = useSDK();

const connect = async () => {
try {
Expand All @@ -59,7 +51,7 @@ export const ConnectWalletButton = () => {
return (
<div className="relative">
{connected ? (
isOnLineaTestnet || isOnLocal ? (
chainId === appChainId ? (
<Popover>
<PopoverTrigger>
<Button variant="primary">{formatAddress(account)}</Button>
Expand All @@ -81,7 +73,7 @@ export const ConnectWalletButton = () => {
</Popover>
) : (
<Button variant="destructive" onClick={switchEthereumChain}>
Swith to linea
Switch network
</Button>
)
) : (
Expand Down
Loading