mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-14 20:04:28 +01:00
feat: refactor project structure
Refactored the project structure so that each page has its own tree of components and a global "Components" folder for the components that is used by more than one page. - Added an "assets" directory that exports all static images/icons/fonts/...etc
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import Claim_CopySignatureCard from './Claim_CopySignatureCard';
|
||||
|
||||
import { ModalsDecorator } from '.storybook/helpers'
|
||||
|
||||
export default {
|
||||
title: 'Claim/Copy Signature Card',
|
||||
component: Claim_CopySignatureCard,
|
||||
|
||||
decorators: [ModalsDecorator]
|
||||
} as ComponentMeta<typeof Claim_CopySignatureCard>;
|
||||
|
||||
const Template: ComponentStory<typeof Claim_CopySignatureCard> = (args) => <Claim_CopySignatureCard {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Direction, ModalId, replaceModal } from '../../redux/features/modals.slice';
|
||||
import { useAppDispatch, useAppSelector } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
import CopyToClipboard from 'src/Components/Shared/CopyToClipboard/CopyToClipboard'
|
||||
import { useCallback } from 'react';
|
||||
import { IoClose } from 'react-icons/io5';
|
||||
|
||||
export default function Claim_CopySignatureCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { projectName, image } = useAppSelector(state => ({ projectName: state.project.project?.title, image: state.project.project?.thumbnail_image, }))
|
||||
|
||||
const generatedHash = '0x000330RaaSt302440zxc327jjiaswf19987183345pRReuBNksbaaueee'
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
dispatch(replaceModal({
|
||||
modalId: ModalId.Claim_Submitted,
|
||||
direction: Direction.NEXT
|
||||
}))
|
||||
}, [dispatch])
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
custom={direction}
|
||||
variants={modalCardVariants}
|
||||
initial='initial'
|
||||
animate="animate"
|
||||
exit='exit'
|
||||
className="modal-card max-w-[343px] p-24 rounded-xl relative"
|
||||
>
|
||||
<IoClose className='absolute text-body2 top-24 right-24 hover:cursor-pointer' onClick={onClose} />
|
||||
<h2 className='text-h5 font-bold'>Claim this project</h2>
|
||||
<div className="flex justify-center my-32">
|
||||
<img
|
||||
src={image}
|
||||
className='w-80 h-80 object-cover rounded-2xl'
|
||||
alt="" />
|
||||
</div>
|
||||
<p className="text-body4 text-center px-16">
|
||||
Good job! Now paste this on the webpage
|
||||
<a className="font-bold" href="www.projectname.com/"
|
||||
target='_blank' rel='noreferrer'
|
||||
> www.projectname.com/</a>
|
||||
</p>
|
||||
<div className="input-wrapper mt-32">
|
||||
<input
|
||||
className="input-field overflow-ellipsis"
|
||||
value={generatedHash}
|
||||
/>
|
||||
|
||||
<CopyToClipboard text={generatedHash} />
|
||||
</div>
|
||||
<div className="mt-32">
|
||||
<button className='btn btn-primary w-full' onClick={handleNext}>Submit for review</button>
|
||||
</div>
|
||||
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import Claim_FundWithdrawCard from './Claim_FundWithdrawCard';
|
||||
|
||||
import { ModalsDecorator } from '.storybook/helpers'
|
||||
|
||||
export default {
|
||||
title: 'Claim/Fund Withdraw Card',
|
||||
component: Claim_FundWithdrawCard,
|
||||
|
||||
decorators: [ModalsDecorator]
|
||||
} as ComponentMeta<typeof Claim_FundWithdrawCard>;
|
||||
|
||||
const Template: ComponentStory<typeof Claim_FundWithdrawCard> = (args) => <Claim_FundWithdrawCard {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
// import { useAppDispatch } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
|
||||
export default function Claim_FundWithdrawCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
//const dispatch = useAppDispatch();
|
||||
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
custom={direction}
|
||||
variants={modalCardVariants}
|
||||
initial='initial'
|
||||
animate="animate"
|
||||
exit='exit'
|
||||
className="modal-card max-w-[260px] py-16 px-24 rounded-xl relative"
|
||||
>
|
||||
<div className="flex justify-center my-16">
|
||||
<img
|
||||
src={'assets/icons/lightning-small.svg'}
|
||||
className='w-48 h-48 object-cover rounded-full'
|
||||
alt="" />
|
||||
</div>
|
||||
<p className="text-h4 text-center font-bold">
|
||||
2,220 sats
|
||||
</p>
|
||||
<p className="text-body4 text-center text-gray-400">
|
||||
2.78$
|
||||
</p>
|
||||
<div className="mt-16 flex flex-col gap-8">
|
||||
<button className='btn btn-primary w-full shadow-xs' >Fund</button>
|
||||
<button className='btn border w-full shadow-xs' >Withdraw</button>
|
||||
</div>
|
||||
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import Claim_GenerateSignatureCard from './Claim_GenerateSignatureCard';
|
||||
|
||||
import { ModalsDecorator } from '.storybook/helpers'
|
||||
|
||||
export default {
|
||||
title: 'Claim/Generate Signature Card',
|
||||
component: Claim_GenerateSignatureCard,
|
||||
|
||||
decorators: [ModalsDecorator]
|
||||
} as ComponentMeta<typeof Claim_GenerateSignatureCard>;
|
||||
|
||||
const Template: ComponentStory<typeof Claim_GenerateSignatureCard> = (args) => <Claim_GenerateSignatureCard {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Direction, ModalId, replaceModal } from '../../redux/features/modals.slice';
|
||||
import { useAppDispatch, useAppSelector } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { IoClose } from 'react-icons/io5';
|
||||
|
||||
export default function Claim_GenerateSignatureCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { projectName, image } = useAppSelector(state => ({ projectName: state.project.project?.title, image: state.project.project?.thumbnail_image, }))
|
||||
|
||||
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
dispatch(replaceModal({
|
||||
modalId: ModalId.Claim_CopySignature,
|
||||
direction: Direction.NEXT
|
||||
}))
|
||||
}, [dispatch])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
// const timeout = setTimeout(handleNext, 3000)
|
||||
// return () => clearTimeout(timeout)
|
||||
}, [handleNext])
|
||||
|
||||
//const onCopy = () => {
|
||||
// // Copy to Clipboard
|
||||
// setTimeout(handleNext, 2000)
|
||||
//}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
custom={direction}
|
||||
variants={modalCardVariants}
|
||||
initial='initial'
|
||||
animate="animate"
|
||||
exit='exit'
|
||||
className="modal-card max-w-[343px] p-24 rounded-xl relative"
|
||||
>
|
||||
<IoClose className='absolute text-body2 top-24 right-24 hover:cursor-pointer' onClick={onClose} />
|
||||
<h2 className='text-h5 font-bold'>Claim this project</h2>
|
||||
<div className="flex justify-center my-32">
|
||||
<img
|
||||
src={image}
|
||||
className='w-80 h-80 object-cover rounded-2xl'
|
||||
alt="" />
|
||||
</div>
|
||||
<p className="text-body4 text-center px-16">
|
||||
To claim ownership of <span className="font-bold">{projectName}</span> and its tips, you need to sign a message and paste this on the project website so we can verify you are the real ownership
|
||||
</p>
|
||||
<div className="mt-32">
|
||||
<button className='btn btn-primary w-full' onClick={handleNext}>Generate Signature</button>
|
||||
</div>
|
||||
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import Claim_SubmittedCard from './Claim_SubmittedCard';
|
||||
|
||||
import { ModalsDecorator } from '.storybook/helpers'
|
||||
|
||||
export default {
|
||||
title: 'Claim/Submitted Card',
|
||||
component: Claim_SubmittedCard,
|
||||
|
||||
decorators: [ModalsDecorator]
|
||||
} as ComponentMeta<typeof Claim_SubmittedCard>;
|
||||
|
||||
const Template: ComponentStory<typeof Claim_SubmittedCard> = (args) => <Claim_SubmittedCard {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
import { IoClose } from 'react-icons/io5';
|
||||
|
||||
export default function Claim_SubmittedCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
custom={direction}
|
||||
variants={modalCardVariants}
|
||||
initial='initial'
|
||||
animate="animate"
|
||||
exit='exit'
|
||||
className="modal-card max-w-[343px] p-24 rounded-xl relative"
|
||||
|
||||
>
|
||||
<IoClose
|
||||
className='absolute text-body2 top-24 right-24 hover:cursor-pointer'
|
||||
onClick={onClose} />
|
||||
|
||||
<h2 className='text-h5 font-bold'>Submitted For Review</h2>
|
||||
<div className="flex justify-center my-32">
|
||||
<img
|
||||
src="assets/icons/flag-icon.svg"
|
||||
className='w-80 h-80'
|
||||
alt="success" />
|
||||
</div>
|
||||
<p className="text-body4 text-center">
|
||||
Great work! your claim to <span className="font-bold">Application Name</span> has been submitted for review.
|
||||
<br />
|
||||
Check back soon to see if it was successful.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import Categories from './Categories';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Explore Page/Categories',
|
||||
component: Categories,
|
||||
|
||||
} as ComponentMeta<typeof Categories>;
|
||||
|
||||
const Template: ComponentStory<typeof Categories> = (args) => <Categories />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { ALL_CATEGORIES_QUERY, ALL_CATEGORIES_QUERY_RES } from './query';
|
||||
|
||||
export default function Categories() {
|
||||
|
||||
const { data, loading } = useQuery<ALL_CATEGORIES_QUERY_RES>(ALL_CATEGORIES_QUERY);
|
||||
|
||||
const handleClick = (categoryId: string) => {
|
||||
|
||||
}
|
||||
|
||||
if (loading)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<div className="flex gap-12 flex-wrap">
|
||||
{data?.allCategories.map(category => <span key={category.id} className="chip hover:cursor-pointer hover:bg-gray-200" onClick={() => handleClick(category.id)}>{category.title}</span>)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import { ProjectCategory } from "src/utils/interfaces";
|
||||
|
||||
export const ALL_CATEGORIES_QUERY = gql`
|
||||
query AllCategories {
|
||||
allCategories {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export type ALL_CATEGORIES_QUERY_RES = {
|
||||
allCategories: ProjectCategory[];
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
import Categories from "./Categories/Categories";
|
||||
import Header from "./Header/Header";
|
||||
import ProjectsSection from "./ProjectsSection/ProjectsSection";
|
||||
|
||||
|
||||
export default function ExplorePage() {
|
||||
return (
|
||||
<>
|
||||
<div className="px-32">
|
||||
<Header />
|
||||
</div>
|
||||
<div className="my-40 px-32">
|
||||
<Categories />
|
||||
</div>
|
||||
<ProjectsSection />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import Header from './Header';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Explore Page/Header',
|
||||
component: Header,
|
||||
|
||||
} as ComponentMeta<typeof Header>;
|
||||
|
||||
const Template: ComponentStory<typeof Header> = (args) => <Header />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import Button from 'src/Components/Shared/Button/Button'
|
||||
|
||||
const headerLinks = [
|
||||
{
|
||||
title: "A fun directory of Lightning Enabled Applications on the Open Web",
|
||||
img:
|
||||
"https://s3-alpha-sig.figma.com/img/07b8/5d84/145942255afd215b3da26dbbf1dd03bd?Expires=1638144000&Signature=Cl1DUQJIUsrrFi48M~qU1r3Z0agGdy-uiNUao5g8-nu34QtoyWTFPXvaH3naSZBYqcPyKFq1jaXF6Mw1uj1hdWwGhXhMPLnKNJFFrGViVXhXu-3YeCPY9p4-IcieFJBZPVA~zDY8zxY5b06loWsINAVx4eMHRAhSWl~~Mca5PjlSXloiYrT00W-6c9m8gevfMMX~PsHQedzwYzg0j2DlnhPX8LbRkli1G2OxtCaFwo3~HGHXIlFGuGU1uXRvi1qBWrdjdsuWgIly1ekcFfJWAKmwYXk06EtCmfWRgGYbD7cBK~lwOkFofbf1LW0yqLv0hr4svwToH~3FiHenrCF-1g__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA",
|
||||
link: {
|
||||
content: "Submit App",
|
||||
url: "#",
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
"Join the next wave of the Lightning Network in November’s ‘Shock the Web’ hackathon",
|
||||
img: 'https://s3-alpha-sig.figma.com/img/be1b/cd75/1baa911b3875134c0889d6755c4ba2cb?Expires=1638748800&Signature=DOiLciAA95w8gOvAowjiiR-ZPbmV1oGSRRK8YpE4ALMoe47pL7DifQxZvL1LQn~NRa0aLMoMk61521fbbGJeDAwk~j6fIm~iZAlMzQn7DdWy0wFR0uLQagOgpIiIXO-w8CeC14VoW-hrjIX5mDmOonJzkfoftGqIF1WCOmP2EuswyJpIngFdLb15gCex4Necs3vH2cuD9iSgWG2za97KfdXZP79ROyk2EN9Q3~a7RT4FTBBIlgKDLuFGSVRxReXVNajn~XSxBJh2de9dFVa3tOXkwJXu3jb0G4x-wRCaG-KmBhUOemuGtu5Fumh6goktGh~bIDwoHeUBVKFHAzaYgw__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA', link: {
|
||||
content: "Register Now",
|
||||
url: "#",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-40 justify-center md:justify-between">
|
||||
<div className="rounded-20 h-[280px] relative overflow-hidden p-24 flex flex-col items-start justify-end">
|
||||
<img
|
||||
className="w-full h-full object-cover absolute top-0 left-0 z-[-2]"
|
||||
src={headerLinks[0].img}
|
||||
alt=""
|
||||
/>
|
||||
<div className="w-full h-full object-cover bg-gradient-to-t from-gray-900 absolute top-0 left-0 z-[-1]"></div>
|
||||
<h3 className="text-white text-h3 max-w-[80%]">{headerLinks[0].title}</h3>
|
||||
<Button
|
||||
color='primary'
|
||||
href={headerLinks[0].link.url}
|
||||
className="font-regular mt-36"
|
||||
>
|
||||
{headerLinks[0].link.content}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="hidden md:flex flex-col rounded-20 h-[280px] relative overflow-hidden p-24 items-start justify-end">
|
||||
<img
|
||||
className="w-full h-full object-cover absolute top-0 left-0 z-[-2]"
|
||||
src={headerLinks[1].img}
|
||||
alt=""
|
||||
/>
|
||||
<div className="w-full h-full object-cover bg-gradient-to-t from-gray-900 absolute top-0 left-0 z-[-1]"></div>
|
||||
<h3 className="text-white text-h3 max-w-[80%]">
|
||||
{headerLinks[1].title}
|
||||
</h3>
|
||||
<Button href={headerLinks[1].link.url} className="font-regular mt-36">
|
||||
{headerLinks[1].link.content}
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import mockData from 'src/api/mockData.json'
|
||||
|
||||
import ProjectCardMini from './ProjectCardMini';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Explore Page/Project Card Mini',
|
||||
component: ProjectCardMini,
|
||||
|
||||
} as ComponentMeta<typeof ProjectCardMini>;
|
||||
|
||||
const Template: ComponentStory<typeof ProjectCardMini> = (args) => <ProjectCardMini {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
project: mockData.projectsCards[0]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { MdLocalFireDepartment } from "react-icons/md";
|
||||
import { ProjectCard } from "../../../utils/interfaces";
|
||||
|
||||
|
||||
interface Props {
|
||||
project: ProjectCard
|
||||
onClick: (projectId: string) => void
|
||||
}
|
||||
|
||||
export default function ProjectCardMini({ project, onClick }: Props) {
|
||||
return (
|
||||
<div className="bg-gray-25 select-none px-16 py-16 flex w-[296px] gap-16 border border-gray-200 rounded-10 hover:cursor-pointer hover:bg-gray-100" onClick={() => onClick(project.id)}>
|
||||
<img src={project.thumbnail_image} alt={project.title} draggable="false" className="flex-shrink-0 w-80 h-80 bg-gray-200 border-0 rounded-8"></img>
|
||||
<div className="justify-around items-start min-w-0">
|
||||
<p className="text-body4 w-full font-bold overflow-ellipsis overflow-hidden whitespace-nowrap">{project.title}</p>
|
||||
<p className="text-body5 text-gray-600 font-light my-[5px]">{project.category.title}</p>
|
||||
<span className="chip-small bg-warning-50 text-yellow-700 font-light text-body5 py-[3px] px-10"> <MdLocalFireDepartment className='inline-block text-fire transform text-body4 align-middle' /> {project.votes_count} </span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import mockData from 'src/api/mockData.json'
|
||||
|
||||
import ProjectsRow from './ProjectsRow';
|
||||
import { MdLocalFireDepartment } from 'react-icons/md';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Explore Page/ProjectsRow',
|
||||
component: ProjectsRow,
|
||||
|
||||
} as ComponentMeta<typeof ProjectsRow>;
|
||||
|
||||
const Template: ComponentStory<typeof ProjectsRow> = (args) => <ProjectsRow {...args} />;
|
||||
|
||||
export const Hottest = Template.bind({});
|
||||
Hottest.args = {
|
||||
title: <>
|
||||
Hottest
|
||||
<MdLocalFireDepartment
|
||||
className='inline-block text-fire align-bottom scale-125 ml-4 origin-bottom'
|
||||
/></>,
|
||||
categoryId: '2',
|
||||
projects: mockData.projectsCards
|
||||
}
|
||||
|
||||
export const Defi = Template.bind({});
|
||||
Defi.args = {
|
||||
title: 'DeFi',
|
||||
categoryId: '33',
|
||||
projects: mockData.projectsCards
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { ReactElement, useCallback, useRef, useState } from "react";
|
||||
import { ProjectCard } from "../../../utils/interfaces";
|
||||
import Carousel from 'react-multi-carousel';
|
||||
import { MdDoubleArrow, } from 'react-icons/md';
|
||||
import { useAppDispatch } from "../../../utils/hooks";
|
||||
import { ModalId, openModal } from "../../../redux/features/modals.slice";
|
||||
import ProjectCardMini from "../ProjectCardMini/ProjectCardMini";
|
||||
import { useResizeListener } from 'src/utils/hooks'
|
||||
|
||||
const responsive = {
|
||||
all: {
|
||||
breakpoint: { max: 5000, min: 0 },
|
||||
items: (((window.innerWidth - 64) / (296 + 48))),
|
||||
}
|
||||
}
|
||||
|
||||
const calcNumItems = () => {
|
||||
const items = (((window.innerWidth - 32 - 296) / (296 + 20)));
|
||||
return items;
|
||||
}
|
||||
|
||||
interface Props { title: string | ReactElement, categoryId: string, projects: ProjectCard[] }
|
||||
|
||||
export default function ProjectsRow({ title, categoryId, projects }: Props) {
|
||||
|
||||
const [carouselItmsCnt, setCarouselItmsCnt] = useState(calcNumItems);
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
responsive.all.items = carouselItmsCnt
|
||||
|
||||
let drag = useRef(false);
|
||||
|
||||
document.addEventListener('mousedown', () => drag.current = false);
|
||||
document.addEventListener('mousemove', () => drag.current = true);
|
||||
|
||||
const handleClick = (projectId: string) => {
|
||||
if (!drag.current)
|
||||
dispatch(openModal({ modalId: ModalId.Project, propsToPass: { projectId } }))
|
||||
}
|
||||
|
||||
useResizeListener(() => {
|
||||
setCarouselItmsCnt(calcNumItems());
|
||||
}, [setCarouselItmsCnt])
|
||||
|
||||
|
||||
return (
|
||||
<div className='mb-48'>
|
||||
<h3 className="font-bolder text-body3 mb-24 px-32">{title}
|
||||
<span>
|
||||
<MdDoubleArrow className='text-gray-200 ml-8 hover:cursor-pointer align-bottom transform scale-y-110 scale-x-125 origin-left' onClick={() => {
|
||||
console.log(categoryId);
|
||||
}} />
|
||||
</span>
|
||||
</h3>
|
||||
<Carousel
|
||||
containerClass='pl-32 pr-[-32px]'
|
||||
showDots={false}
|
||||
arrows={false}
|
||||
responsive={responsive}
|
||||
centerMode
|
||||
itemClass='pb-[1px]'
|
||||
>
|
||||
{projects.map((project, idx) =>
|
||||
<ProjectCardMini key={idx} project={project} onClick={handleClick} />
|
||||
)}
|
||||
</Carousel>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import ProjectsSection from './ProjectsSection';
|
||||
|
||||
|
||||
export default {
|
||||
title: 'Explore Page/Projects Section',
|
||||
component: ProjectsSection,
|
||||
|
||||
} as ComponentMeta<typeof ProjectsSection>;
|
||||
|
||||
const Template: ComponentStory<typeof ProjectsSection> = (args) => <ProjectsSection />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
import ProjectsRow from "../ProjectsRow/ProjectsRow";
|
||||
import { MdLocalFireDepartment } from "react-icons/md";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { ALL_CATEGORIES_PROJECTS_QUERY, ALL_CATEGORIES_PROJECTS_RES } from "./query";
|
||||
|
||||
|
||||
export default function ProjectsSection() {
|
||||
|
||||
const { data, loading } = useQuery<ALL_CATEGORIES_PROJECTS_RES>(ALL_CATEGORIES_PROJECTS_QUERY);
|
||||
|
||||
if (loading || !data) return null;
|
||||
|
||||
return (
|
||||
<div className='mt-32 lg:mt-48'>
|
||||
<ProjectsRow title={<>Hottest <MdLocalFireDepartment className='inline-block text-fire align-bottom scale-125 origin-bottom' /></>}
|
||||
categoryId="133123"
|
||||
projects={data.newProjects} />
|
||||
{data.allCategories.map(({ id, title, project, }) => {
|
||||
if (project)
|
||||
return <ProjectsRow
|
||||
key={id}
|
||||
categoryId={id}
|
||||
title={title}
|
||||
projects={project.map(p => ({ ...p, category: { id, title } }))} />
|
||||
else return null
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import { ProjectCard } from "src/utils/interfaces";
|
||||
|
||||
export const ALL_CATEGORIES_PROJECTS_QUERY = gql`
|
||||
query AllCategoriesProjects {
|
||||
allCategories {
|
||||
id
|
||||
title
|
||||
project {
|
||||
id
|
||||
thumbnail_image
|
||||
title
|
||||
votes_count
|
||||
}
|
||||
}
|
||||
newProjects {
|
||||
id
|
||||
title
|
||||
thumbnail_image
|
||||
votes_count
|
||||
category {
|
||||
title
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export type ALL_CATEGORIES_PROJECTS_RES = {
|
||||
newProjects: ProjectCard[];
|
||||
allCategories: {
|
||||
id: string;
|
||||
title: string;
|
||||
project: ProjectCard[];
|
||||
}[];
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Direction, ModalId, replaceModal } from '../../redux/features/modals.slice';
|
||||
import { useAppDispatch } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
import { Direction, ModalId, replaceModal } from 'src/redux/features/modals.slice';
|
||||
import { useAppDispatch } from 'src/utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
|
||||
import { AiFillThunderbolt } from 'react-icons/ai';
|
||||
import CopyToClipboard from 'src/Components/Shared/CopyToClipboard/CopyToClipboard';
|
||||
import CopyToClipboard from 'src/Components/CopyToClipboard/CopyToClipboard';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { IoClose } from 'react-icons/io5';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Direction, ModalId, replaceModal } from '../../redux/features/modals.slice';
|
||||
import { useAppDispatch } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
import { Direction, ModalId, replaceModal } from 'src/redux/features/modals.slice';
|
||||
import { useAppDispatch } from 'src/utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
|
||||
import { IoLockClosed, } from 'react-icons/io5'
|
||||
import Button from 'src/Components/Shared/Button/Button';
|
||||
import Button from 'src/Components/Button/Button';
|
||||
|
||||
export default function Login_NativeWalletCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Direction, ModalId, replaceModal } from '../../redux/features/modals.slice';
|
||||
import { useAppDispatch } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
import { Direction, ModalId, replaceModal } from 'src/redux/features/modals.slice';
|
||||
import { useAppDispatch } from 'src/utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
|
||||
import { AiFillThunderbolt } from 'react-icons/ai';
|
||||
import Loader from 'react-loader-spinner';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
@@ -1,9 +1,9 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { useAppDispatch } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
import { useAppDispatch } from 'src/utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { closeModal, openSceduledModal } from '../../redux/features/modals.slice';
|
||||
import { connectWallet } from '../../redux/features/wallet.slice';
|
||||
import { closeModal, openSceduledModal } from 'src/redux/features/modals.slice';
|
||||
import { connectWallet } from 'src/redux/features/wallet.slice';
|
||||
|
||||
export default function Login_SuccessCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useEffect } from "react";
|
||||
import { closeModal, Direction, ModalId, removeScheduledModal } from "../../../redux/features/modals.slice";
|
||||
import { useAppDispatch, useAppSelector } from "../../../utils/hooks";
|
||||
import Claim_CopySignatureCard from "../../ClaimProject/Claim_CopySignatureCard";
|
||||
import Claim_GenerateSignatureCard from "../../ClaimProject/Claim_GenerateSignatureCard";
|
||||
import Login_ExternalWalletCard from "../../Login/Login_ExternalWalletCard";
|
||||
import Login_NativeWalletCard from "../../Login/Login_NativeWalletCard";
|
||||
import Login_SuccessCard from "../../Login/Login_SuccessCard";
|
||||
import Login_ScanningWalletCard from "../../Login/Login_ScanningWalletCard";
|
||||
import ProjectCard from "../../Project/ProjectCard";
|
||||
import TipCard from "../../Tip/TipCard";
|
||||
import { closeModal, Direction, ModalId, removeScheduledModal } from "src/redux/features/modals.slice";
|
||||
import { useAppDispatch, useAppSelector } from "src/utils/hooks";
|
||||
import Claim_CopySignatureCard from "src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard";
|
||||
import Claim_GenerateSignatureCard from "src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard";
|
||||
import Login_ExternalWalletCard from "src/Components/Modals/Login/Login_ExternalWalletCard";
|
||||
import Login_NativeWalletCard from "src/Components/Modals/Login/Login_NativeWalletCard";
|
||||
import Login_SuccessCard from "src/Components/Modals/Login/Login_SuccessCard";
|
||||
import Login_ScanningWalletCard from "src/Components/Modals/Login/Login_ScanningWalletCard";
|
||||
import ProjectCard from "src/pages/ProjectPage/ProjectCard/ProjectCard";
|
||||
import TipCard from "src/pages/ProjectPage/Tip/TipCard";
|
||||
import Modal from "../Modal/Modal";
|
||||
import { Portal } from "../Portal/Portal";
|
||||
import Claim_SubmittedCard from "../../ClaimProject/Claim_SubmittedCard";
|
||||
import Claim_FundWithdrawCard from "../../ClaimProject/Claim_FundWithdrawCard";
|
||||
import { Portal } from "../../Portal/Portal";
|
||||
import Claim_SubmittedCard from "src/pages/ProjectPage/ClaimProject/Claim_SubmittedCard";
|
||||
import Claim_FundWithdrawCard from "src/pages/ProjectPage/ClaimProject/Claim_FundWithdrawCard";
|
||||
|
||||
export interface ModalCard {
|
||||
onClose?: () => void;
|
||||
@@ -4,16 +4,15 @@ import { MdLocalFireDepartment } from 'react-icons/md';
|
||||
import { IoExtensionPuzzle } from 'react-icons/io5';
|
||||
import { AiFillThunderbolt } from 'react-icons/ai';
|
||||
import { BsSearch } from "react-icons/bs";
|
||||
import { FormEvent, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { FormEvent, useRef, useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { GrClose } from 'react-icons/gr';
|
||||
import { useAppDispatch, useAppSelector } from "../../../utils/hooks";
|
||||
import { ModalId, openModal } from "../../../redux/features/modals.slice";
|
||||
import { useAppDispatch, useAppSelector } from "src/utils/hooks";
|
||||
import { ModalId, openModal } from "src/redux/features/modals.slice";
|
||||
import { Link } from "react-router-dom";
|
||||
import Button from "../Button/Button";
|
||||
import { setNavHeight } from "src/redux/features/theme.slice";
|
||||
import _throttle from 'lodash.throttle'
|
||||
import { useResizeListener } from 'src/utils/hooks'
|
||||
import { useResizeListener } from 'src/utils/hooks'
|
||||
|
||||
export const navLinks = [
|
||||
{ text: "Explore", url: "/", icon: FaHome, color: 'text-primary-600' },
|
||||
@@ -1,17 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import ProjectCard from './ProjectCard';
|
||||
|
||||
import { ModalsDecorator } from '.storybook/helpers'
|
||||
|
||||
export default {
|
||||
title: 'Project/Project Card',
|
||||
component: ProjectCard,
|
||||
|
||||
decorators: [ModalsDecorator]
|
||||
} as ComponentMeta<typeof ProjectCard>;
|
||||
|
||||
const Template: ComponentStory<typeof ProjectCard> = (args) => <ProjectCard {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { BsJoystick } from 'react-icons/bs'
|
||||
import { MdClose, MdLocalFireDepartment } from 'react-icons/md';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer';
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { useAppDispatch, useAppSelector } from '../../utils/hooks';
|
||||
import { ModalId, openModal, scheduleModal } from '../../redux/features/modals.slice';
|
||||
import { setProject } from '../../redux/features/project.slice';
|
||||
import { connectWallet } from '../../redux/features/wallet.slice';
|
||||
import Button from 'src/Components/Shared/Button/Button';
|
||||
import { requestProvider } from 'webln';
|
||||
import { PROJECT_BY_ID_QUERY, PROJECT_BY_ID_RES, PROJECT_BY_ID_VARS } from './query'
|
||||
import { AiFillThunderbolt } from 'react-icons/ai';
|
||||
|
||||
|
||||
export default function ProjectCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { loading } = useQuery<PROJECT_BY_ID_RES, PROJECT_BY_ID_VARS>(
|
||||
PROJECT_BY_ID_QUERY,
|
||||
{
|
||||
variables: { projectId: parseInt(props.projectId) },
|
||||
onCompleted: data => {
|
||||
dispatch(setProject(data.getProject))
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const { isWalletConnected, webln, project, isMobileScreen } = useAppSelector(state => ({
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
webln: state.wallet.provider,
|
||||
project: state.project.project,
|
||||
isMobileScreen: state.theme.isMobileScreen
|
||||
}));
|
||||
|
||||
|
||||
|
||||
if (loading || !project) return <></>;
|
||||
|
||||
const onConnectWallet = async () => {
|
||||
try {
|
||||
const webln = await requestProvider();
|
||||
if (webln) {
|
||||
dispatch(connectWallet(webln));
|
||||
alert("wallet connected!");
|
||||
}
|
||||
// Now you can call all of the webln.* methods
|
||||
}
|
||||
catch (err: any) {
|
||||
// Tell the user what went wrong
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
const onTip = () => {
|
||||
|
||||
if (!isWalletConnected) {
|
||||
dispatch(scheduleModal({ modalId: ModalId.Tip, propsToPass: { projectId: props.projectId } }))
|
||||
dispatch(openModal({
|
||||
modalId: ModalId.Login_ScanWallet
|
||||
}))
|
||||
} else
|
||||
dispatch(openModal({ modalId: ModalId.Tip, propsToPass: { projectId: props.projectId } }))
|
||||
}
|
||||
|
||||
|
||||
const onClaim = () => {
|
||||
if (!isWalletConnected) {
|
||||
dispatch(scheduleModal({
|
||||
modalId: ModalId.Claim_GenerateSignature,
|
||||
propsToPass: { projectId: props.projectId },
|
||||
}))
|
||||
dispatch(openModal({
|
||||
modalId: ModalId.Login_ScanWallet
|
||||
}))
|
||||
} else
|
||||
dispatch(openModal({
|
||||
modalId: ModalId.Claim_GenerateSignature,
|
||||
propsToPass: { projectId: props.projectId },
|
||||
}))
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
custom={direction}
|
||||
variants={modalCardVariants}
|
||||
initial='initial'
|
||||
animate="animate"
|
||||
exit='exit'
|
||||
className={`modal-card max-w-[768px] ${props.isPageModal && isMobileScreen && 'rounded-0 w-full min-h-screen'}`}
|
||||
|
||||
>
|
||||
<div className="relative h-[80px] lg:h-[152px]">
|
||||
<img className="w-full h-full object-cover" src={project.cover_image} alt="" />
|
||||
<button className="w-[48px] h-[48px] bg-white absolute top-1/2 left-32 -translate-y-1/2 rounded-full hover:bg-gray-200 text-center" onClick={onClose}><MdClose className=' inline-block text-body2 lg:text-body1' /></button>
|
||||
</div>
|
||||
<div className="p-24">
|
||||
<div className="flex gap-24 items-center h-[93px]">
|
||||
<div className="flex-shrink-0 w-[93px] h-[93px] rounded-md overflow-hidden">
|
||||
<img className="w-full h-full object-cover" src={project?.thumbnail_image} alt="" />
|
||||
</div>
|
||||
<div className='flex flex-col items-start justify-between self-stretch'>
|
||||
<h3 className="text-h3 font-regular">{project?.title}</h3>
|
||||
<a className="text-blue-400 font-regular text-body4" target='_blank' rel="noreferrer" href={project?.website}>{project?.website?.replace(/(^\w+:|^)\/\//, '')}</a>
|
||||
<div>
|
||||
<span className="chip-small font-light text-body5 py-4 px-12 mr-8"> {project?.category.title}</span>
|
||||
|
||||
<span className="chip-small bg-warning-50 font-light text-body5 py-4 px-12"><MdLocalFireDepartment className='inline-block text-fire transform text-body4 align-middle' /> {project?.votes_count}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-shrink-0 hidden md:flex ml-auto gap-16">
|
||||
<Button color='primary' size='md' className=" my-16">Play <BsJoystick /></Button>
|
||||
{isWalletConnected ?
|
||||
<Button onClick={onTip} size='md' className="border border-warning-100 bg-warning-50 hover:bg-warning-50 active:bg-warning-100 my-16">Tip <MdLocalFireDepartment className='text-fire' /></Button>
|
||||
:
|
||||
<Button onClick={onConnectWallet} size='md' className="border border-gray-200 bg-gray-100 hover:bg-gray-50 active:bg-gray-100 my-16">Connect Wallet to Vote</Button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-40 text-body4 leading-normal">{project?.description}</p>
|
||||
<div className="md:hidden">
|
||||
<Button color='primary' size='md' fullWidth className="w-full mt-24 mb-16">Play <BsJoystick /></Button>
|
||||
{isWalletConnected ?
|
||||
<Button size='md' fullWidth className="bg-yellow-100 hover:bg-yellow-50 mb-24" onClick={onTip}>Vote <MdLocalFireDepartment className='text-fire' /></Button>
|
||||
:
|
||||
<Button size='md' fullWidth className="bg-gray-200 hover:bg-gray-100 mb-24" onClick={onConnectWallet}><AiFillThunderbolt className='inline-block text-thunder transform scale-125' /> Connect Wallet to Vote</Button>
|
||||
}
|
||||
</div>
|
||||
<div className="mt-40">
|
||||
<h3 className="text-h5 font-bold mb-16">Screenshots</h3>
|
||||
<div className="grid grid-cols-2 gap-12 justify-items-center md:gap-24">
|
||||
<div className="w-full relative pt-[56%]">
|
||||
<div className="absolute top-0 left-0 w-full h-full object-cover bg-gray-300 rounded-xl"></div>
|
||||
</div>
|
||||
<div className="w-full relative pt-[56%]">
|
||||
<div className="absolute top-0 left-0 w-full h-full object-cover bg-gray-300 rounded-xl"></div>
|
||||
</div>
|
||||
<div className="w-full relative pt-[56%]">
|
||||
<div className="absolute top-0 left-0 w-full h-full object-cover bg-gray-300 rounded-xl"></div>
|
||||
</div>
|
||||
<div className="w-full relative pt-[56%]">
|
||||
<div className="absolute top-0 left-0 w-full h-full object-cover bg-gray-300 rounded-xl"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="my-40" />
|
||||
<div className="text-center">
|
||||
<h3 className="text-body4 font-regular">Are you the creator of this project?</h3>
|
||||
<Button color='gray' size='md' className="my-16" onClick={onClaim}>Claim 🖐</Button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import { Project } from "src/utils/interfaces";
|
||||
|
||||
export const PROJECT_BY_ID_QUERY = gql`
|
||||
query Project($projectId: Int!) {
|
||||
getProject(id: $projectId) {
|
||||
id
|
||||
cover_image
|
||||
thumbnail_image
|
||||
title
|
||||
website
|
||||
votes_count
|
||||
category {
|
||||
title
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export interface PROJECT_BY_ID_RES {
|
||||
getProject: Project;
|
||||
}
|
||||
|
||||
export interface PROJECT_BY_ID_VARS {
|
||||
projectId: number;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import TipCard from './TipCard';
|
||||
|
||||
import { ModalsDecorator } from '.storybook/helpers'
|
||||
|
||||
export default {
|
||||
title: 'Tip/Tip Card',
|
||||
component: TipCard,
|
||||
|
||||
decorators: [ModalsDecorator]
|
||||
} as ComponentMeta<typeof TipCard>;
|
||||
|
||||
const Template: ComponentStory<typeof TipCard> = (args) => <TipCard {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import React, { useState } from 'react';
|
||||
import { AiFillThunderbolt } from 'react-icons/ai'
|
||||
import { IoClose } from 'react-icons/io5'
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer';
|
||||
import { useAppDispatch, useAppSelector } from '../../utils/hooks';
|
||||
import { gql, useQuery, useMutation } from "@apollo/client";
|
||||
import useWindowSize from "react-use/lib/useWindowSize";
|
||||
import Confetti from "react-confetti";
|
||||
|
||||
const defaultOptions = [
|
||||
{ text: '10 sat', value: 10 },
|
||||
{ text: '100 sats', value: 100 },
|
||||
{ text: '1k sats', value: 1000 },
|
||||
]
|
||||
|
||||
enum PaymentStatus {
|
||||
DEFAULT,
|
||||
FETCHING_PAYMENT_DETAILS,
|
||||
PAID,
|
||||
AWAITING_PAYMENT,
|
||||
PAYMENT_CONFIRMED,
|
||||
NOT_PAID,
|
||||
CANCELED
|
||||
}
|
||||
|
||||
const VOTE = gql`
|
||||
mutation Mutation($projectId: Int!, $amountInSat: Int!) {
|
||||
vote(project_id: $projectId, amount_in_sat: $amountInSat) {
|
||||
id
|
||||
amount_in_sat
|
||||
payment_request
|
||||
payment_hash
|
||||
paid
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CONFIRM_VOTE = gql`
|
||||
mutation Mutation($paymentRequest: String!, $preimage: String!) {
|
||||
confirmVote(payment_request: $paymentRequest, preimage: $preimage) {
|
||||
id
|
||||
amount_in_sat
|
||||
payment_request
|
||||
payment_hash
|
||||
paid
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function TipCard({ onClose, direction, ...props }: ModalCard) {
|
||||
const { width, height } = useWindowSize()
|
||||
|
||||
const { isWalletConnected, webln } = useAppSelector(state => ({
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
webln: state.wallet.provider,
|
||||
}));
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState(10);
|
||||
const [voteAmount, setVoteAmount] = useState<number>(10);
|
||||
const [paymentStatus, setPaymentStatus] = useState<PaymentStatus>(PaymentStatus.DEFAULT);
|
||||
|
||||
const [vote, { data }] = useMutation(VOTE, {
|
||||
onCompleted: (votingData) => {
|
||||
setPaymentStatus(PaymentStatus.AWAITING_PAYMENT);
|
||||
webln.sendPayment(votingData.vote.payment_request).then((res: any) => {
|
||||
console.log("waiting for payment", res);
|
||||
confirmVote({variables: { paymentRequest: votingData.vote.payment_request, preimage: res.preimage }});
|
||||
setPaymentStatus(PaymentStatus.PAID);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.log(err);
|
||||
setPaymentStatus(PaymentStatus.NOT_PAID);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const [confirmVote, { data: confirmedVoteData }] = useMutation(CONFIRM_VOTE, {
|
||||
onCompleted: (votingData) => {
|
||||
setPaymentStatus(PaymentStatus.PAYMENT_CONFIRMED);
|
||||
}
|
||||
});
|
||||
|
||||
const onChangeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSelectedOption(-1);
|
||||
setVoteAmount(Number(event.target.value));
|
||||
};
|
||||
|
||||
const onSelectOption = (idx: number) => {
|
||||
setSelectedOption(idx);
|
||||
setVoteAmount(defaultOptions[idx].value);
|
||||
}
|
||||
|
||||
const requestPayment = () => {
|
||||
setPaymentStatus(PaymentStatus.FETCHING_PAYMENT_DETAILS);
|
||||
vote({variables: { "amountInSat": voteAmount, "projectId": parseInt("1") }});
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
custom={direction}
|
||||
variants={modalCardVariants}
|
||||
initial='initial'
|
||||
animate="animate"
|
||||
exit='exit'
|
||||
className="modal-card max-w-[343px] p-24 rounded-xl relative"
|
||||
>
|
||||
<IoClose className='absolute text-body2 top-24 right-24 hover:cursor-pointer' onClick={onClose} />
|
||||
<h2 className='text-h5 font-bold'>Upvote Project</h2>
|
||||
<div className="mt-32 ">
|
||||
<label className="block text-gray-700 text-body4 mb-2 ">
|
||||
Enter Amount
|
||||
</label>
|
||||
<div className="input-wrapper">
|
||||
<input
|
||||
className="input-field"
|
||||
value={voteAmount} onChange={onChangeInput}
|
||||
type="number"
|
||||
placeholder="e.g 5 sats" />
|
||||
{/* <IoCopy className='input-icon' /> */}
|
||||
</div>
|
||||
<div className="flex mt-16 justify-between">
|
||||
{defaultOptions.map((option, idx) =>
|
||||
<button
|
||||
key={idx}
|
||||
className={`btn border px-12 rounded-md py-8 text-body ${idx === selectedOption && "border-primary-500 bg-primary-100 hover:bg-primary-100 text-primary-600"}`}
|
||||
onClick={() => onSelectOption(idx)}
|
||||
>
|
||||
{option.text}<AiFillThunderbolt className='inline-block text-thunder' />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-body6 mt-12 text-gray-500">1 sat = 1 vote</p>
|
||||
{paymentStatus === PaymentStatus.FETCHING_PAYMENT_DETAILS && <p className="text-body6 mt-12 text-gray-500">Please wait while we the fetch payment details.</p>}
|
||||
{paymentStatus === PaymentStatus.NOT_PAID && <p className="text-body6 mt-12 text-red-500">You did not confirm the payment. Please try again.</p>}
|
||||
{paymentStatus === PaymentStatus.PAID && <p className="text-body6 mt-12 text-green-500">The invoice was paid! Please wait while we confirm it.</p>}
|
||||
{paymentStatus === PaymentStatus.AWAITING_PAYMENT && <p className="text-body6 mt-12 text-yellow-500">Please confirm the payment in the prompt...</p>}
|
||||
{paymentStatus === PaymentStatus.PAYMENT_CONFIRMED && <p className="text-body6 mt-12 text-green-500">Imagine confetti here</p>}
|
||||
<button className="btn btn-primary w-full mt-32" onClick={requestPayment}>
|
||||
Upvote
|
||||
</button>
|
||||
</div>
|
||||
{paymentStatus === PaymentStatus.PAYMENT_CONFIRMED && <Confetti width={width} height={height} />}
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user