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
This commit is contained in:
MTG2000
2022-01-12 18:48:20 +02:00
parent 43bfab177e
commit f9407c8b09
12 changed files with 136 additions and 107 deletions

View File

@@ -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])

View File

@@ -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
}))
}

View File

@@ -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])

View File

@@ -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) {

View File

@@ -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() {
}}
>
<AnimatePresence>
{openModals.map(modal => {
const Child = ModalsMap(modal.modalId);
{openModals.map((modal, idx) => {
const Child = ALL_MODALS[modal.Modal];
return (
<Modal key={modal.modalId} onClose={onClose} direction={direction} isPageModal={modal.isPageModal}>
<Child onClose={onClose} direction={direction} isPageModal={modal.isPageModal} {...modal.propsToPass} />
<Modal key={idx} onClose={onClose} direction={direction} isPageModal={modal.props?.isPageModal}>
<Child onClose={onClose} direction={direction} isPageModal={modal.props?.isPageModal} {...modal.props} />
</Modal>)
})}
</AnimatePresence>

View File

@@ -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'
}))
}

View File

@@ -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(() => {

View File

@@ -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])

View File

@@ -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])

View File

@@ -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_RES, PROJECT_BY_ID_VARS>(
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',
}))
}

View File

@@ -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 (

View File

@@ -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<U> = Omit<U, keyof ModalCard>
type ModalProps<M extends keyof typeof ALL_MODALS> = ExcludeBaseModalProps<ComponentProps<typeof ALL_MODALS[M]>>
type ModalAction<U extends keyof typeof ALL_MODALS = keyof typeof ALL_MODALS> = U extends any ?
{} extends ModalProps<U> ?
{ Modal: U }
:
{ Modal: U, props: ModalProps<U> }
:
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<OpenModal>) {
state.toOpenLater = action.payload;
scheduleModal(state, action: PayloadAction<ModalAction>) {
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<ModalAction>
) {
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<ModalAction & { direction: Direction }>
) {
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;