Skip to content

Commit

Permalink
refactor(home): modals (#777)
Browse files Browse the repository at this point in the history
* refactor(home): unlock modal

* test(unlockModal): update test and cleanup

* refactor(home): list-channel modal

* refactor(home): list-channel modal - spinner and no channel info

* refactor(home): open-channel modal

* refactor(home): open-channel modal

* refactor(home): send modal

* refactor(home): receive modal

* refactor(home): tx-details modal

* test(modal): update test and remove close test

functionality is covered by next-ui modal

* chore: debugging

* refactor(login): use new components

* fix(home): avoid double effect execution

* refactor(home): modal usage and receive-modal

* refactor(home): onchain receive

* refactor(home): lightning receive

* refactor(home): cleanup receive

* test(receiveModal): update tests to use new tabs

* fix(receiveModal): minor improvements

* refactor(home): send modal

* chore: remove light buttons

* fix(setup): import statement

* refactor(confirmModal): use explicit export

* fix(home): send modal - onchain design

* fix(settings): change pw modal usage

* use vertical tabList on small screen size

---------

Co-authored-by: Christoph Stenglein <[email protected]>
  • Loading branch information
escapedcat and cstenglein authored Oct 5, 2024
1 parent 02728f6 commit efca53d
Show file tree
Hide file tree
Showing 34 changed files with 1,024 additions and 972 deletions.
106 changes: 58 additions & 48 deletions src/components/ConfirmModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,68 +7,78 @@ import {
ModalFooter,
} from "@nextui-org/react";
import type { UseDisclosureReturn } from "@nextui-org/use-disclosure";
import { ReactNode } from "react";
import { type ReactNode } from "react";
import { useTranslation } from "react-i18next";

export type Props = {
headline: string;
body?: ReactNode;
onConfirm?: () => void;
disclosure: UseDisclosureReturn;
headline?: string;
children?: ReactNode;
onConfirm?: () => void;
confirmText?: string;
cancelText?: string;
isLoading?: boolean;
isFormModal?: ReactNode;
} & ({ custom: true } | { custom?: false; body?: ReactNode });

type ConfirmModalComponent = {
(props: Props): JSX.Element;
Header: typeof ModalHeader;
Body: typeof ModalBody;
Footer: typeof ModalFooter;
};

export const ConfirmModal = ({
export const ConfirmModalHeader = ModalHeader;
export const ConfirmModalBody = ModalBody;
export const ConfirmModalFooter = ModalFooter;

export const ConfirmModal: ConfirmModalComponent = ({
disclosure,
headline,
body,
children,
onConfirm,
disclosure,
isLoading,
isFormModal,
}: Props) => {
confirmText,
cancelText,
isLoading = false,
...props
}) => {
const { t } = useTranslation();
const { isOpen, onOpenChange, onClose } = disclosure;

return (
<>
<Modal isOpen={isOpen} onOpenChange={onOpenChange} onClose={onClose}>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">
{headline}
</ModalHeader>
const renderContent = () => {
if ("custom" in props && props.custom) {
return children;
}

return (
<>
{headline && <ConfirmModalHeader>{headline}</ConfirmModalHeader>}

{isFormModal || (
<>
{!!body && <ModalBody>{body}</ModalBody>}
<ConfirmModalBody>{children || props.body}</ConfirmModalBody>

<ModalFooter>
<Button
variant="light"
onClick={onClose}
disabled={isLoading}
>
{t("settings.cancel")}
</Button>
<Button
color="primary"
onClick={onConfirm}
disabled={isLoading}
isLoading={isLoading}
>
{t("settings.confirm")}
</Button>
</ModalFooter>
</>
)}
</>
)}
</ModalContent>
</Modal>
</>
<ConfirmModalFooter>
<Button onClick={onClose} disabled={isLoading}>
{cancelText || t("settings.cancel")}
</Button>
<Button
color="primary"
onClick={onConfirm}
disabled={isLoading || !onConfirm}
isLoading={isLoading}
>
{confirmText || t("settings.confirm")}
</Button>
</ConfirmModalFooter>
</>
);
};

return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange} onClose={onClose}>
<ModalContent>{renderContent()}</ModalContent>
</Modal>
);
};

export default ConfirmModal;
ConfirmModal.Header = ConfirmModalHeader;
ConfirmModal.Body = ConfirmModalBody;
ConfirmModal.Footer = ConfirmModalFooter;
33 changes: 33 additions & 0 deletions src/hooks/use-modalmanager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useDisclosure } from "@nextui-org/use-disclosure";
import { useState } from "react";

export type ModalType =
| "SEND"
| "RECEIVE"
| "DETAIL"
| "OPEN_CHANNEL"
| "LIST_CHANNEL"
| "UNLOCK"
| null;

export function useModalManager() {
const [activeModal, setActiveModal] = useState<ModalType>(null);
const disclosure = useDisclosure();

const openModal = (modalType: ModalType) => {
setActiveModal(modalType);
disclosure.onOpen();
};

const closeModal = () => {
setActiveModal(null);
disclosure.onClose();
};

return {
activeModal,
disclosure,
openModal,
closeModal,
};
}
8 changes: 6 additions & 2 deletions src/hooks/use-sse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ function useSSE() {
const appInstallSuccessHandler = useCallback(
(installData: InstallAppData, appName: string) => {
if (installData.mode === "on") {
toast.success(t("apps.install_success", { appName }));
toast.success(t("apps.install_success", { appName }), {
theme: "dark",
});
} else {
toast.success(t("apps.uninstall_success", { appName }));
toast.success(t("apps.uninstall_success", { appName }), {
theme: "dark",
});
}
},
[t],
Expand Down
1 change: 1 addition & 0 deletions src/i18n/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@
"invoice": "Invoice",
"on_chain": "On-chain",
"receive": "Receive",
"receive_aria_options": "Receive options Lightning or On-chain",
"refresh": "Refresh address",
"scan_qr": "Scan this QR code or copy the address below to receive funds",
"send": "Send",
Expand Down
12 changes: 9 additions & 3 deletions src/pages/Apps/AppCardAlby.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,18 @@ export const AppCardAlby: FC = () => {
});

if (result.success) {
toast.success(t(`appInfo.${id}.action.connection.success`));
toast.success(t(`appInfo.${id}.action.connection.success`), {
theme: "dark",
});
} else {
toast.error(t(`appInfo.${id}.action.connection.error`));
toast.error(t(`appInfo.${id}.action.connection.error`), {
theme: "dark",
});
}
} catch (e) {
toast.error(t(`appInfo.${id}.action.connection.error`));
toast.error(t(`appInfo.${id}.action.connection.error`), {
theme: "dark",
});
}
};

Expand Down
64 changes: 32 additions & 32 deletions src/pages/Home/ListChannelModal/ListChannelModal.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import ChannelList from "./ChannelList";
import LoadingSpinner from "@/components/LoadingSpinner/LoadingSpinner";
import { Alert } from "@/components/Alert";
import {
ConfirmModal,
type Props as ConfirmModalProps,
} from "@/components/ConfirmModal";
import Message from "@/components/Message";
import ModalDialog from "@/layouts/ModalDialog";
import { LightningChannel } from "@/models/lightning-channel";
import { MODAL_ROOT } from "@/utils";
import { checkError } from "@/utils/checkError";
import { instance } from "@/utils/interceptor";
import { Spinner } from "@nextui-org/react";
import { useCallback, useEffect, useState } from "react";
import { createPortal } from "react-dom";
import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";

type Props = {
onClose: () => void;
};

const theme = "dark";
export default function ListChannelModal({ onClose }: Props) {
export default function ListChannelModal({
disclosure,
}: Pick<ConfirmModalProps, "disclosure">) {
const { t } = useTranslation();
const [openChannels, setOpenChannels] = useState<LightningChannel[]>([]);
const [isLoading, setIsLoading] = useState(true);
Expand Down Expand Up @@ -64,28 +64,28 @@ export default function ListChannelModal({ onClose }: Props) {
.finally(() => setIsLoading(false));
};

return createPortal(
<ModalDialog close={onClose}>
<h2 className="mb-2 text-lg font-bold">
{t("home.current_open_channels")}
</h2>
{isLoading && (
<div className="my-2 flex justify-center">
<LoadingSpinner />
</div>
)}
{!isLoading && openChannels.length === 0 && (
<p>{t("home.no_open_channels")}</p>
)}
{openChannels.length > 0 && (
<ChannelList
channel={openChannels}
onDelete={deleteChannelHandler}
isLoading={isLoading}
/>
)}
{error && <Message message={error} />}
</ModalDialog>,
MODAL_ROOT,
return (
<ConfirmModal
headline={t("home.current_open_channels")}
disclosure={disclosure}
custom
>
<ConfirmModal.Body>
{isLoading && <Spinner size="lg" />}

{!isLoading && openChannels.length === 0 && (
<Alert color="info">{t("home.no_open_channels")}</Alert>
)}

{openChannels.length > 0 && (
<ChannelList
channel={openChannels}
onDelete={deleteChannelHandler}
isLoading={isLoading}
/>
)}
{error && <Message message={error} />}
</ConfirmModal.Body>
</ConfirmModal>
);
}
Loading

0 comments on commit efca53d

Please sign in to comment.