From f9407c8b095302fd118172c862f2a458a64aa7af Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Wed, 12 Jan 2022 18:48:20 +0200 Subject: [PATCH] chore: refactor the modals opening mechanic Rewrote the logic and functions for dispatching openModal actions so that now it provides proper typing for the available modals and for what props are they expecting to be provided --- .../Modals/Login/Login_ExternalWalletCard.tsx | 4 +- .../Modals/Login/Login_NativeWalletCard.tsx | 4 +- .../Modals/Login/Login_ScanningWalletCard.tsx | 4 +- .../Modals/Login/Login_SuccessCard.tsx | 1 - .../ModalsContainer/ModalsContainer.tsx | 47 ++----- src/Components/Navbar/Navbar.tsx | 8 +- .../ExplorePage/ProjectsRow/ProjectsRow.tsx | 4 +- .../ClaimProject/Claim_CopySignatureCard.tsx | 4 +- .../Claim_GenerateSignatureCard.tsx | 4 +- .../ProjectPage/ProjectCard/ProjectCard.tsx | 23 ++-- src/pages/ProjectPage/Tip/TipCard.tsx | 18 ++- src/redux/features/modals.slice.ts | 122 +++++++++++++----- 12 files changed, 136 insertions(+), 107 deletions(-) diff --git a/src/Components/Modals/Login/Login_ExternalWalletCard.tsx b/src/Components/Modals/Login/Login_ExternalWalletCard.tsx index ff44745..7802aa3 100644 --- a/src/Components/Modals/Login/Login_ExternalWalletCard.tsx +++ b/src/Components/Modals/Login/Login_ExternalWalletCard.tsx @@ -1,5 +1,5 @@ import { motion } from 'framer-motion' -import { Direction, ModalId, replaceModal } from 'src/redux/features/modals.slice'; +import { Direction, 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'; @@ -14,7 +14,7 @@ export default function Login_ExternalWalletCard({ onClose, direction, ...props const handleNext = useCallback(() => { dispatch(replaceModal({ - modalId: ModalId.Login_Success, + Modal: 'Login_SuccessCard', direction: Direction.NEXT })) }, [dispatch]) diff --git a/src/Components/Modals/Login/Login_NativeWalletCard.tsx b/src/Components/Modals/Login/Login_NativeWalletCard.tsx index d799695..8b7f5d7 100644 --- a/src/Components/Modals/Login/Login_NativeWalletCard.tsx +++ b/src/Components/Modals/Login/Login_NativeWalletCard.tsx @@ -1,5 +1,5 @@ import { motion } from 'framer-motion' -import { Direction, ModalId, replaceModal } from 'src/redux/features/modals.slice'; +import { Direction, 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' @@ -11,7 +11,7 @@ export default function Login_NativeWalletCard({ onClose, direction, ...props }: const handleNext = () => { dispatch(replaceModal({ - modalId: ModalId.Login_ExternalWallet, + Modal: 'Login_ExternalWalletCard', direction: Direction.NEXT })) } diff --git a/src/Components/Modals/Login/Login_ScanningWalletCard.tsx b/src/Components/Modals/Login/Login_ScanningWalletCard.tsx index b66ddd7..13168c2 100644 --- a/src/Components/Modals/Login/Login_ScanningWalletCard.tsx +++ b/src/Components/Modals/Login/Login_ScanningWalletCard.tsx @@ -1,5 +1,5 @@ import { motion } from 'framer-motion' -import { Direction, ModalId, replaceModal } from 'src/redux/features/modals.slice'; +import { Direction, 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'; @@ -13,7 +13,7 @@ export default function Login_ScanningWalletCard({ onClose, direction, ...props const handleNext = useCallback(() => { dispatch(replaceModal({ - modalId: ModalId.Login_NativeWallet, + Modal: 'Login_NativeWalletCard', direction: Direction.NEXT })) }, [dispatch]) diff --git a/src/Components/Modals/Login/Login_SuccessCard.tsx b/src/Components/Modals/Login/Login_SuccessCard.tsx index 9b7725e..b65c0e0 100644 --- a/src/Components/Modals/Login/Login_SuccessCard.tsx +++ b/src/Components/Modals/Login/Login_SuccessCard.tsx @@ -3,7 +3,6 @@ import { useAppDispatch } from 'src/utils/hooks'; import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer' import { useCallback, useEffect } from 'react'; 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) { diff --git a/src/Components/Modals/ModalsContainer/ModalsContainer.tsx b/src/Components/Modals/ModalsContainer/ModalsContainer.tsx index 9f54e33..f259b4a 100644 --- a/src/Components/Modals/ModalsContainer/ModalsContainer.tsx +++ b/src/Components/Modals/ModalsContainer/ModalsContainer.tsx @@ -1,6 +1,6 @@ import { AnimatePresence, motion } from "framer-motion"; -import { useEffect } from "react"; -import { closeModal, Direction, ModalId, removeScheduledModal } from "src/redux/features/modals.slice"; +import { ComponentProps, useEffect } from "react"; +import { ALL_MODALS, closeModal, Direction, 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"; @@ -18,9 +18,10 @@ import Claim_FundWithdrawCard from "src/pages/ProjectPage/ClaimProject/Claim_Fun export interface ModalCard { onClose?: () => void; direction: number; - [key: string]: any; + isPageModal?: boolean; } + export const modalCardVariants = { initial: (direction: any) => { if (direction === Direction.START) return { opacity: 0, y: 300 }; @@ -43,38 +44,6 @@ export const modalCardVariants = { -const ModalsMap = (modalId: ModalId) => { - switch (modalId) { - case ModalId.Project: - return ProjectCard; - case ModalId.Login_ScanWallet: - return Login_ScanningWalletCard; - case ModalId.Login_NativeWallet: - return Login_NativeWalletCard; - case ModalId.Login_Success: - return Login_SuccessCard; - case ModalId.Login_ExternalWallet: - return Login_ExternalWalletCard; - case ModalId.Tip: - return TipCard; - case ModalId.Claim_GenerateSignature: - return Claim_GenerateSignatureCard; - case ModalId.Claim_CopySignature: - return Claim_CopySignatureCard; - case ModalId.Claim_Submitted: - return Claim_SubmittedCard; - case ModalId.Claim_FundWithdraw: - return Claim_FundWithdrawCard; - - - - default: - return () => <> - } - - -} - export default function ModalsContainer() { @@ -110,11 +79,11 @@ export default function ModalsContainer() { }} > - {openModals.map(modal => { - const Child = ModalsMap(modal.modalId); + {openModals.map((modal, idx) => { + const Child = ALL_MODALS[modal.Modal]; return ( - - + + ) })} diff --git a/src/Components/Navbar/Navbar.tsx b/src/Components/Navbar/Navbar.tsx index 3ab583e..a070284 100644 --- a/src/Components/Navbar/Navbar.tsx +++ b/src/Components/Navbar/Navbar.tsx @@ -8,11 +8,11 @@ import { FormEvent, useRef, useState } from "react"; import { motion } from "framer-motion"; import { GrClose } from 'react-icons/gr'; import { useAppDispatch, useAppSelector } from "src/utils/hooks"; -import { ModalId, openModal } from "src/redux/features/modals.slice"; +import { 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 { useResizeListener } from 'src/utils/hooks' +import { useResizeListener } from 'src/utils/hooks' export const navLinks = [ { text: "Explore", url: "/", icon: FaHome, color: 'text-primary-600' }, @@ -49,13 +49,13 @@ export default function Navbar() { const onConnectWallet = () => { dispatch(openModal({ - modalId: ModalId.Login_ScanWallet + Modal: 'Login_ScanningWalletCard' })); } const onWithdraw = () => { dispatch(openModal({ - modalId: ModalId.Claim_FundWithdraw + Modal: 'Claim_FundWithdrawCard' })) } diff --git a/src/pages/ExplorePage/ProjectsRow/ProjectsRow.tsx b/src/pages/ExplorePage/ProjectsRow/ProjectsRow.tsx index 8ab2ee7..2adafcc 100644 --- a/src/pages/ExplorePage/ProjectsRow/ProjectsRow.tsx +++ b/src/pages/ExplorePage/ProjectsRow/ProjectsRow.tsx @@ -3,7 +3,7 @@ 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 { openModal } from "../../../redux/features/modals.slice"; import ProjectCardMini from "../ProjectCardMini/ProjectCardMini"; import { useResizeListener } from 'src/utils/hooks' @@ -35,7 +35,7 @@ export default function ProjectsRow({ title, categoryId, projects }: Props) { const handleClick = (projectId: string) => { if (!drag.current) - dispatch(openModal({ modalId: ModalId.Project, propsToPass: { projectId } })) + dispatch(openModal({ Modal: "ProjectCard", props: { projectId } })) } useResizeListener(() => { diff --git a/src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard.tsx b/src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard.tsx index 4603bf1..9522f43 100644 --- a/src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard.tsx +++ b/src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard.tsx @@ -1,5 +1,5 @@ import { motion } from 'framer-motion' -import { Direction, ModalId, replaceModal } from 'src/redux/features/modals.slice'; +import { Direction, replaceModal } from 'src/redux/features/modals.slice'; import { useAppDispatch, useAppSelector } from 'src/utils/hooks'; import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer' import CopyToClipboard from 'src/Components/CopyToClipboard/CopyToClipboard' @@ -15,7 +15,7 @@ export default function Claim_CopySignatureCard({ onClose, direction, ...props } const handleNext = useCallback(() => { dispatch(replaceModal({ - modalId: ModalId.Claim_Submitted, + Modal: 'Claim_SubmittedCard', direction: Direction.NEXT })) }, [dispatch]) diff --git a/src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard.tsx b/src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard.tsx index 3fc8e30..81a131f 100644 --- a/src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard.tsx +++ b/src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard.tsx @@ -1,5 +1,5 @@ import { motion } from 'framer-motion' -import { Direction, ModalId, replaceModal } from 'src/redux/features/modals.slice'; +import { Direction, replaceModal } from 'src/redux/features/modals.slice'; import { useAppDispatch, useAppSelector } from 'src/utils/hooks'; import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer' import { useCallback, useEffect } from 'react'; @@ -14,7 +14,7 @@ export default function Claim_GenerateSignatureCard({ onClose, direction, ...pro const handleNext = useCallback(() => { dispatch(replaceModal({ - modalId: ModalId.Claim_CopySignature, + Modal: 'Claim_CopySignatureCard', direction: Direction.NEXT })) }, [dispatch]) diff --git a/src/pages/ProjectPage/ProjectCard/ProjectCard.tsx b/src/pages/ProjectPage/ProjectCard/ProjectCard.tsx index 26d8dd9..4590ccd 100644 --- a/src/pages/ProjectPage/ProjectCard/ProjectCard.tsx +++ b/src/pages/ProjectPage/ProjectCard/ProjectCard.tsx @@ -4,7 +4,7 @@ import { MdClose, MdLocalFireDepartment } from 'react-icons/md'; import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'; import { useQuery } from "@apollo/client"; import { useAppDispatch, useAppSelector } from 'src/utils/hooks'; -import { ModalId, openModal, scheduleModal } from 'src/redux/features/modals.slice'; +import { openModal, scheduleModal } from 'src/redux/features/modals.slice'; import { setProject } from 'src/redux/features/project.slice'; import { connectWallet } from 'src/redux/features/wallet.slice'; import Button from 'src/Components/Button/Button'; @@ -12,15 +12,18 @@ 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'; +interface Props extends ModalCard { + projectId: string +} -export default function ProjectCard({ onClose, direction, ...props }: ModalCard) { +export default function ProjectCard({ onClose, direction, projectId, ...props }: Props) { const dispatch = useAppDispatch(); const { loading } = useQuery( PROJECT_BY_ID_QUERY, { - variables: { projectId: parseInt(props.projectId) }, + variables: { projectId: parseInt(projectId) }, onCompleted: data => { dispatch(setProject(data.getProject)) }, @@ -56,28 +59,26 @@ export default function ProjectCard({ onClose, direction, ...props }: ModalCard) const onTip = () => { if (!isWalletConnected) { - dispatch(scheduleModal({ modalId: ModalId.Tip, propsToPass: { projectId: props.projectId } })) + dispatch(scheduleModal({ Modal: 'TipCard' })) dispatch(openModal({ - modalId: ModalId.Login_ScanWallet + Modal: 'Login_ScanningWalletCard' })) } else - dispatch(openModal({ modalId: ModalId.Tip, propsToPass: { projectId: props.projectId } })) + dispatch(openModal({ Modal: 'TipCard' })) } const onClaim = () => { if (!isWalletConnected) { dispatch(scheduleModal({ - modalId: ModalId.Claim_GenerateSignature, - propsToPass: { projectId: props.projectId }, + Modal: 'Claim_GenerateSignatureCard', })) dispatch(openModal({ - modalId: ModalId.Login_ScanWallet + Modal: 'Login_ScanningWalletCard' })) } else dispatch(openModal({ - modalId: ModalId.Claim_GenerateSignature, - propsToPass: { projectId: props.projectId }, + Modal: 'Claim_GenerateSignatureCard', })) } diff --git a/src/pages/ProjectPage/Tip/TipCard.tsx b/src/pages/ProjectPage/Tip/TipCard.tsx index e660127..b59d2dc 100644 --- a/src/pages/ProjectPage/Tip/TipCard.tsx +++ b/src/pages/ProjectPage/Tip/TipCard.tsx @@ -48,7 +48,11 @@ mutation Mutation($paymentRequest: String!, $preimage: String!) { } `; -export default function TipCard({ onClose, direction, ...props }: ModalCard) { +interface Props extends ModalCard { + +} + +export default function TipCard({ onClose, direction, ...props }: Props) { const { width, height } = useWindowSize() const { isWalletConnected, webln } = useAppSelector(state => ({ @@ -67,13 +71,13 @@ export default function TipCard({ onClose, direction, ...props }: ModalCard) { 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 }}); + confirmVote({ variables: { paymentRequest: votingData.vote.payment_request, preimage: res.preimage } }); setPaymentStatus(PaymentStatus.PAID); }) - .catch((err: any) => { - console.log(err); - setPaymentStatus(PaymentStatus.NOT_PAID); - }); + .catch((err: any) => { + console.log(err); + setPaymentStatus(PaymentStatus.NOT_PAID); + }); } }); @@ -95,7 +99,7 @@ export default function TipCard({ onClose, direction, ...props }: ModalCard) { const requestPayment = () => { setPaymentStatus(PaymentStatus.FETCHING_PAYMENT_DETAILS); - vote({variables: { "amountInSat": voteAmount, "projectId": parseInt("1") }}); + vote({ variables: { "amountInSat": voteAmount, "projectId": parseInt("1") } }); } return ( diff --git a/src/redux/features/modals.slice.ts b/src/redux/features/modals.slice.ts index ef5084a..b4c8027 100644 --- a/src/redux/features/modals.slice.ts +++ b/src/redux/features/modals.slice.ts @@ -1,4 +1,16 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +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 Claim_SubmittedCard from "src/pages/ProjectPage/ClaimProject/Claim_SubmittedCard"; +import Claim_FundWithdrawCard from "src/pages/ProjectPage/ClaimProject/Claim_FundWithdrawCard"; +import { ModalCard } from "src/Components/Modals/ModalsContainer/ModalsContainer"; +import { ComponentProps } from "react"; export enum Direction { START, @@ -7,31 +19,60 @@ export enum Direction { EXIT, } -export enum ModalId { - None, - Project, - Login_ScanWallet, - Login_NativeWallet, - Login_ExternalWallet, - Login_Success, - Claim_GenerateSignature, - Claim_CopySignature, - Claim_Submitted, - Claim_FundWithdraw, - Tip, + + +export const ALL_MODALS = { + ProjectCard, + Login_ScanningWalletCard, + Login_NativeWalletCard, + Login_SuccessCard, + Login_ExternalWalletCard, + TipCard, + Claim_GenerateSignatureCard, + Claim_CopySignatureCard, + Claim_SubmittedCard, + Claim_FundWithdrawCard, } + +type ExcludeBaseModalProps = Omit + +type ModalProps = ExcludeBaseModalProps> + +type ModalAction = U extends any ? + {} extends ModalProps ? + { Modal: U } + : + { Modal: U, props: ModalProps } + : + never; + + +function dispatch(action: ModalAction) { + +} + +dispatch({ + Modal: 'Login_NativeWalletCard', + + +}) + + + + + + interface OpenModal { - modalId: ModalId; - isPageModal?: boolean; - propsToPass?: any; + Modal: ModalAction['Modal'], + props?: any; } interface StoreState { isOpen: boolean; isLoading: boolean; direction: Direction; - flows: ModalId[]; + flows: keyof typeof ALL_MODALS[]; toOpenLater: OpenModal | null; openModals: OpenModal[]; isMobileScreen?: boolean; @@ -41,7 +82,7 @@ const initialState = { isOpen: false, isLoading: false, direction: Direction.START, - flows: [], + flows: [] as any, toOpenLater: null, openModals: [] as OpenModal[], } as StoreState; @@ -54,8 +95,15 @@ export const modalSlice = createSlice({ state.direction = action.payload; }, - scheduleModal(state, action: PayloadAction) { - state.toOpenLater = action.payload; + scheduleModal(state, action: PayloadAction) { + + let props: any = {}; + if ('props' in action.payload) props = { ...action.payload.props } + + state.toOpenLater = { + Modal: action.payload.Modal, + props, + }; }, openSceduledModal(state) { @@ -73,33 +121,38 @@ export const modalSlice = createSlice({ openModal( state, - action: PayloadAction<{ modalId: ModalId; propsToPass?: any }> + action: PayloadAction ) { state.direction = Direction.START; state.isOpen = true; - const isPageModal = action.payload.modalId === ModalId.Project; + + let props: any = {}; + if ('props' in action.payload) props = { ...action.payload.props } + + props.isPageModal = action.payload.Modal === 'ProjectCard'; + state.openModals.push({ - modalId: action.payload.modalId, - propsToPass: action.payload.propsToPass, - isPageModal, + Modal: action.payload.Modal, + props }); }, replaceModal( state, - action: PayloadAction<{ - modalId: ModalId; - propsToPass?: any; - direction: Direction; - }> + action: PayloadAction ) { state.direction = action.payload.direction; state.openModals.pop(); - const isPageModal = action.payload.modalId === ModalId.Project; + + + let props: any = {}; + if ('props' in action.payload) props = { ...action.payload.props } + + props.isPageModal = action.payload.Modal === 'ProjectCard'; + state.openModals.push({ - modalId: action.payload.modalId, - propsToPass: action.payload.propsToPass || {}, - isPageModal, + Modal: action.payload.Modal, + props, }); }, @@ -111,6 +164,8 @@ export const modalSlice = createSlice({ }, }); + + export const { closeModal, openModal, @@ -121,4 +176,5 @@ export const { removeScheduledModal, } = modalSlice.actions; + export default modalSlice.reducer;