mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-21 06:12:24 +00:00
update popup modal logic
This commit is contained in:
parent
520c32235c
commit
a02ea7c684
4 changed files with 230 additions and 284 deletions
|
|
@ -1,7 +1,12 @@
|
||||||
import { ReactNode, useCallback } from "react";
|
import classNames from "classnames";
|
||||||
|
import { ReactNode, useCallback, useEffect } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
|
|
||||||
|
import { IconPatch } from "@/components/buttons/IconPatch";
|
||||||
|
import { Icons } from "@/components/Icon";
|
||||||
import { OverlayPortal } from "@/components/overlays/OverlayDisplay";
|
import { OverlayPortal } from "@/components/overlays/OverlayDisplay";
|
||||||
|
import { Flare } from "@/components/utils/Flare";
|
||||||
|
import { Heading2 } from "@/components/utils/Text";
|
||||||
import { useQueryParam } from "@/hooks/useQueryParams";
|
import { useQueryParam } from "@/hooks/useQueryParams";
|
||||||
|
|
||||||
export function useModal(id: string) {
|
export function useModal(id: string) {
|
||||||
|
|
@ -40,3 +45,74 @@ export function Modal(props: { id: string; children?: ReactNode }) {
|
||||||
</OverlayPortal>
|
</OverlayPortal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function FancyModal(props: {
|
||||||
|
id: string;
|
||||||
|
children?: ReactNode;
|
||||||
|
title?: string;
|
||||||
|
size?: "md" | "xl";
|
||||||
|
oneTime?: boolean;
|
||||||
|
}) {
|
||||||
|
const modal = useModal(props.id);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.oneTime) {
|
||||||
|
const isDismissed = localStorage.getItem(`modal-${props.id}-dismissed`);
|
||||||
|
if (!isDismissed) {
|
||||||
|
modal.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [modal, props.id, props.oneTime]);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
if (props.oneTime) {
|
||||||
|
localStorage.setItem(`modal-${props.id}-dismissed`, "true");
|
||||||
|
}
|
||||||
|
modal.hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OverlayPortal darken close={handleClose} show={modal.isShown}>
|
||||||
|
<Helmet>
|
||||||
|
<html data-no-scroll />
|
||||||
|
</Helmet>
|
||||||
|
<div className="flex absolute inset-0 items-center justify-center">
|
||||||
|
<Flare.Base
|
||||||
|
className={classNames(
|
||||||
|
"group -m-[0.705em] rounded-3xl bg-background-main transition-colors duration-300 focus:relative focus:z-10",
|
||||||
|
"w-full mx-4 p-6 bg-mediaCard-hoverBackground bg-opacity-60 backdrop-filter backdrop-blur-lg shadow-lg",
|
||||||
|
props.size === "md" ? "max-w-md" : "max-w-2xl",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="transition-transform duration-300 overflow-y-scroll max-h-[90dvh] scrollbar-none">
|
||||||
|
<Flare.Light
|
||||||
|
flareSize={300}
|
||||||
|
cssColorVar="--colors-mediaCard-hoverAccent"
|
||||||
|
backgroundClass="bg-mediaCard-hoverBackground duration-100"
|
||||||
|
className="rounded-3xl bg-background-main group-hover:opacity-100"
|
||||||
|
/>
|
||||||
|
<Flare.Child className="pointer-events-auto relative mb-2p-[0.4em] transition-transform duration-300">
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
{props.title && (
|
||||||
|
<Heading2 className="!mt-0 !mb-0 pr-6">
|
||||||
|
{props.title}
|
||||||
|
</Heading2>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-s font-semibold text-type-secondary hover:text-white transition-transform hover:scale-95"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
<IconPatch icon={Icons.X} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="text-lg text-type-secondary">
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</Flare.Child>
|
||||||
|
</div>
|
||||||
|
</Flare.Base>
|
||||||
|
</div>
|
||||||
|
</OverlayPortal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ export function HomePage() {
|
||||||
const s = useSearch(search);
|
const s = useSearch(search);
|
||||||
const [showBookmarks, setShowBookmarks] = useState(false);
|
const [showBookmarks, setShowBookmarks] = useState(false);
|
||||||
const [showWatching, setShowWatching] = useState(false);
|
const [showWatching, setShowWatching] = useState(false);
|
||||||
|
// const modal = useModal("notice");
|
||||||
|
|
||||||
const handleClick = (path: To) => {
|
const handleClick = (path: To) => {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
|
|
@ -58,48 +59,12 @@ export function HomePage() {
|
||||||
|
|
||||||
const enableDiscover = usePreferencesStore((state) => state.enableDiscover);
|
const enableDiscover = usePreferencesStore((state) => state.enableDiscover);
|
||||||
|
|
||||||
/*
|
|
||||||
// Safari Notice
|
|
||||||
const [showModal, setShowModal] = useState(() => {
|
|
||||||
const isSafari =
|
|
||||||
typeof navigator !== "undefined" &&
|
|
||||||
/Safari/.test(navigator.userAgent) &&
|
|
||||||
!/Chrome/.test(navigator.userAgent);
|
|
||||||
|
|
||||||
const isMac =
|
|
||||||
typeof navigator !== "undefined" && /Mac/.test(navigator.platform);
|
|
||||||
|
|
||||||
const isIOS =
|
|
||||||
typeof navigator !== "undefined" &&
|
|
||||||
/iPhone|iPad|iPod/.test(navigator.userAgent);
|
|
||||||
|
|
||||||
return isSafari && (isMac || isIOS);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* One time notice
|
|
||||||
const [showModal, setShowModal] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const isDismissed = localStorage.getItem("popupDismissed");
|
|
||||||
if (!isDismissed) {
|
|
||||||
setShowModal(true);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
|
||||||
setShowModal(false);
|
|
||||||
localStorage.setItem("popupDismissed", "true");
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// const { loggedIn } = useAuth(); // Adjust padding for popup show button based on logged in state
|
// const { loggedIn } = useAuth(); // Adjust padding for popup show button based on logged in state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HomeLayout showBg={showBg}>
|
<HomeLayout showBg={showBg}>
|
||||||
{/* Popup show button
|
{/* <a
|
||||||
<a
|
onClick={() => modal.show()}
|
||||||
onClick={() => setShowModal(true)}
|
|
||||||
className={` text-white tabbable rounded-full z-50 fixed top-5 ${
|
className={` text-white tabbable rounded-full z-50 fixed top-5 ${
|
||||||
loggedIn
|
loggedIn
|
||||||
? "right-[7.5rem] lg:right-[12.5rem] lg:text-2xl"
|
? "right-[7.5rem] lg:right-[12.5rem] lg:text-2xl"
|
||||||
|
|
@ -110,8 +75,7 @@ export function HomePage() {
|
||||||
<IconPill icon={Icons.WARNING}>
|
<IconPill icon={Icons.WARNING}>
|
||||||
<span className="font-bold select-none">READ</span>
|
<span className="font-bold select-none">READ</span>
|
||||||
</IconPill>
|
</IconPill>
|
||||||
</a>
|
</a> */}
|
||||||
*/}
|
|
||||||
<div className="mb-16 sm:mb-24">
|
<div className="mb-16 sm:mb-24">
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<style type="text/css">{`
|
<style type="text/css">{`
|
||||||
|
|
@ -122,100 +86,96 @@ export function HomePage() {
|
||||||
<title>{t("global.name")}</title>
|
<title>{t("global.name")}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
{/* Popup
|
{/* Popup
|
||||||
{showModal && (
|
<FancyModal
|
||||||
<PopupModal
|
id="notice"
|
||||||
styles="max-w-2xl" // max-w-md for short
|
title="We're changing our backend server!"
|
||||||
title="We’re changing our backend server!"
|
oneTime
|
||||||
message={
|
>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
On <strong>January 8th</strong>, the backend server will
|
On <strong>January 8th</strong>, the backend server will change
|
||||||
change from:
|
from:
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>server.vidbinge.com</strong> →{" "}
|
<strong>server.vidbinge.com</strong> →{" "}
|
||||||
<strong>server.fifthwit.tech</strong>
|
<strong>server.fifthwit.tech</strong>
|
||||||
</p>
|
</p>
|
||||||
<br />
|
<br />
|
||||||
<p>
|
<p>
|
||||||
You will need to <strong>migrate your account </strong> to the
|
You will need to <strong>migrate your account </strong> to the new
|
||||||
new server or choose to continue using the old server by
|
server or choose to continue using the old server by updating your
|
||||||
updating your settings.
|
settings.
|
||||||
</p>
|
</p>
|
||||||
<br />
|
<br />
|
||||||
<p>
|
<p>
|
||||||
<strong>What You Need to Know:</strong>
|
<strong>What You Need to Know:</strong>
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
1. <strong>Migrating Your Account:</strong> Your data (e.g.,
|
1. <strong>Migrating Your Account:</strong> Your data (e.g.,
|
||||||
bookmarks) will not be automatically transferred. You’ll
|
bookmarks) will not be automatically transferred. You'll
|
||||||
need to migrate your account from the settings page. Or from
|
need to migrate your account from the settings page. Or from
|
||||||
below.
|
below.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2. <strong>Staying on the Old Server:</strong> If you don’t
|
2. <strong>Staying on the Old Server:</strong> If you don't
|
||||||
want to change to the new server, your data will remain safe
|
want to change to the new server, your data will remain safe on{" "}
|
||||||
on <strong>server.vidbinge.com</strong>. You can change the
|
<strong>server.vidbinge.com</strong>. You can change the Backend
|
||||||
Backend URL in your settings to
|
URL in your settings to "https://server.vidbinge.com".
|
||||||
"https://server.vidbinge.com".
|
</li>
|
||||||
</li>
|
</ul>
|
||||||
</ul>
|
<br />
|
||||||
<br />
|
<p>
|
||||||
<p>
|
<strong>Steps to Move Your Data:</strong>
|
||||||
<strong>Steps to Move Your Data:</strong>
|
</p>
|
||||||
</p>
|
<ol>
|
||||||
<ol>
|
<li>
|
||||||
<li>
|
1. Log into your account on <strong>server.vidbinge.com</strong>
|
||||||
1. Log into your account on{" "}
|
.
|
||||||
<strong>server.vidbinge.com</strong>.
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
(If you already are logged in, press here:{" "}
|
||||||
(If you already are logged in, press here:{" "}
|
<a href="/migration" className="text-type-link">
|
||||||
<a href="/migration" className="text-type-link">
|
Migrate my data.
|
||||||
Migrate my data.
|
</a>
|
||||||
</a>
|
)
|
||||||
)
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
2. Go to the <strong>Settings</strong> page.
|
||||||
2. Go to the <strong>Settings</strong> page.
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
3. Scroll down to{" "}
|
||||||
3. Scroll down to{" "}
|
<strong>Connections > Custom Server</strong>.
|
||||||
<strong>Connections > Custom Server</strong>.
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
3. Press the "Migrate my data to a new server."
|
||||||
3. Press the "Migrate my data to a new server."
|
button.
|
||||||
button.
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
4. Enter the new server url:{" "}
|
||||||
4. Enter the new server url:{" "}
|
<strong>https://server.fifthwit.tech</strong> and press
|
||||||
<strong>https://server.fifthwit.tech</strong> and press
|
"Migrate".
|
||||||
"Migrate".
|
</li>
|
||||||
</li>
|
<li>5. Login to your account with the same passphrase!</li>
|
||||||
<li>5. Login to your account with the same passphrase!</li>
|
</ol>
|
||||||
</ol>
|
<br />
|
||||||
<br />
|
<p>
|
||||||
<p>
|
Thank you for your understanding and support during this
|
||||||
Thank you for your understanding and support during this
|
transition! If you have questions or need help, feel free to reach
|
||||||
transition! If you have questions or need help, feel free to
|
out on the{" "}
|
||||||
reach out on the{" "}
|
<a
|
||||||
<a
|
href="https://discord.com/invite/7z6znYgrTG"
|
||||||
href="https://discord.com/invite/7z6znYgrTG"
|
target="_blank"
|
||||||
target="_blank"
|
rel="noopener noreferrer"
|
||||||
rel="noopener noreferrer"
|
className="text-type-link"
|
||||||
className="text-type-link"
|
>
|
||||||
>
|
P-Stream Discord
|
||||||
P-Stream Discord
|
</a>
|
||||||
</a>
|
!
|
||||||
!
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</FancyModal>
|
||||||
}
|
|
||||||
onClose={handleCloseModal}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
*/}
|
*/}
|
||||||
|
|
||||||
<HeroPart searchParams={searchParams} setIsSticky={setShowBg} />
|
<HeroPart searchParams={searchParams} setIsSticky={setShowBg} />
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,12 @@ import { SettingsCard } from "@/components/layout/SettingsCard";
|
||||||
import { Stepper } from "@/components/layout/Stepper";
|
import { Stepper } from "@/components/layout/Stepper";
|
||||||
import { BiggerCenterContainer } from "@/components/layout/ThinContainer";
|
import { BiggerCenterContainer } from "@/components/layout/ThinContainer";
|
||||||
import { VerticalLine } from "@/components/layout/VerticalLine";
|
import { VerticalLine } from "@/components/layout/VerticalLine";
|
||||||
import { Modal, ModalCard, useModal } from "@/components/overlays/Modal";
|
import {
|
||||||
|
FancyModal,
|
||||||
|
Modal,
|
||||||
|
ModalCard,
|
||||||
|
useModal,
|
||||||
|
} from "@/components/overlays/Modal";
|
||||||
import {
|
import {
|
||||||
StatusCircle,
|
StatusCircle,
|
||||||
StatusCircleProps,
|
StatusCircleProps,
|
||||||
|
|
@ -33,7 +38,6 @@ import { conf } from "@/setup/config";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
import { getProxyUrls } from "@/utils/proxyUrls";
|
import { getProxyUrls } from "@/utils/proxyUrls";
|
||||||
|
|
||||||
import { PopupModal } from "../parts/home/PopupModal";
|
|
||||||
import { Status, testFebboxToken } from "../parts/settings/SetupPart";
|
import { Status, testFebboxToken } from "../parts/settings/SetupPart";
|
||||||
|
|
||||||
async function getFebboxTokenStatus(febboxToken: string | null) {
|
async function getFebboxTokenStatus(febboxToken: string | null) {
|
||||||
|
|
@ -179,6 +183,7 @@ export function FEDAPISetup() {
|
||||||
export function OnboardingPage() {
|
export function OnboardingPage() {
|
||||||
const navigate = useNavigateOnboarding();
|
const navigate = useNavigateOnboarding();
|
||||||
const skipModal = useModal("skip");
|
const skipModal = useModal("skip");
|
||||||
|
const infoModal = useModal("info");
|
||||||
const { completeAndRedirect } = useRedirectBack();
|
const { completeAndRedirect } = useRedirectBack();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const noProxies = getProxyUrls().length === 0;
|
const noProxies = getProxyUrls().length === 0;
|
||||||
|
|
@ -189,12 +194,6 @@ export function OnboardingPage() {
|
||||||
!/Chrome/.test(navigator.userAgent) &&
|
!/Chrome/.test(navigator.userAgent) &&
|
||||||
!/Edg/.test(navigator.userAgent);
|
!/Edg/.test(navigator.userAgent);
|
||||||
|
|
||||||
const [showModal, setShowModal] = useState(false);
|
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
|
||||||
setShowModal(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MinimalPageLayout>
|
<MinimalPageLayout>
|
||||||
<PageTitle subpage k="global.pages.onboarding" />
|
<PageTitle subpage k="global.pages.onboarding" />
|
||||||
|
|
@ -216,68 +215,59 @@ export function OnboardingPage() {
|
||||||
</div>
|
</div>
|
||||||
</ModalCard>
|
</ModalCard>
|
||||||
</Modal>
|
</Modal>
|
||||||
{showModal && (
|
<FancyModal id={infoModal.id} title="Understanding a setup" size="xl">
|
||||||
<PopupModal
|
<div>
|
||||||
styles="max-w-2xl" // max-w-md for short max-w-2xl long
|
<p>
|
||||||
title="Understanding a setup"
|
P-Stream doesn't host videos. It relies on third-party websites
|
||||||
message={
|
for content, so you need to choose how it connects to those sites.
|
||||||
<div>
|
<br />
|
||||||
<p>
|
<br />
|
||||||
P-Stream doesn't host videos. It relies on third-party
|
<strong>Your Options:</strong>
|
||||||
websites for content, so you need to choose how it connects to
|
<br />
|
||||||
those sites.
|
<strong>1. Extension (Recommended)</strong>
|
||||||
|
<br />
|
||||||
|
The extension gives you access to the most sources. It acts as a
|
||||||
|
local proxy and can handle sites that need special cookies or
|
||||||
|
headers to load.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<strong>2. Proxy</strong>
|
||||||
|
<br />
|
||||||
|
The proxy scrapes media from other websites. It bypasses browser
|
||||||
|
restrictions (like CORS) to allow scraping.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<strong>3. Default Setup</strong>
|
||||||
|
<br />
|
||||||
|
Uses P-Stream's built-in proxy. It's the easiest option
|
||||||
|
but might be slower due to shared bandwidth.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{conf().ALLOW_FEBBOX_KEY && (
|
||||||
|
<>
|
||||||
|
<strong>Optional FED API (Febbox) UI token</strong>
|
||||||
|
<br />
|
||||||
|
Bringing your own Febbox account allows you to unlock FED API,
|
||||||
|
our best source with 4K quality, Dolby Atmos, the most content,
|
||||||
|
and the best (fastest) load times. This the highly recommended!
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<strong>Your Options:</strong>
|
</>
|
||||||
<br />
|
)}
|
||||||
<strong>1. Extension (Recommended)</strong>
|
If you have more questions on how this works, feel free to ask on
|
||||||
<br />
|
the{" "}
|
||||||
The extension gives you access to the most sources. It acts as a
|
<a
|
||||||
local proxy and can handle sites that need special cookies or
|
href="https://discord.com/invite/7z6znYgrTG"
|
||||||
headers to load.
|
target="_blank"
|
||||||
<br />
|
rel="noopener noreferrer"
|
||||||
<br />
|
className="text-type-link"
|
||||||
<strong>2. Proxy</strong>
|
>
|
||||||
<br />
|
P-Stream Discord
|
||||||
The proxy scrapes media from other websites. It bypasses browser
|
</a>{" "}
|
||||||
restrictions (like CORS) to allow scraping.
|
server!
|
||||||
<br />
|
</p>
|
||||||
<br />
|
</div>
|
||||||
<strong>3. Default Setup</strong>
|
</FancyModal>
|
||||||
<br />
|
|
||||||
Uses P-Stream's built-in proxy. It's the easiest
|
|
||||||
option but might be slower due to shared bandwidth.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
{conf().ALLOW_FEBBOX_KEY && (
|
|
||||||
<>
|
|
||||||
<strong>Optional FED API (Febbox) UI token</strong>
|
|
||||||
<br />
|
|
||||||
Bringing your own Febbox account allows you to unlock FED
|
|
||||||
API, our best source with 4K quality, Dolby Atmos, the most
|
|
||||||
content, and the best (fastest) load times. This the highly
|
|
||||||
recommended!
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
If you have more questions on how this works, feel free to ask
|
|
||||||
on the{" "}
|
|
||||||
<a
|
|
||||||
href="https://discord.com/invite/7z6znYgrTG"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-type-link"
|
|
||||||
>
|
|
||||||
P-Stream Discord
|
|
||||||
</a>{" "}
|
|
||||||
server!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
onClose={handleCloseModal}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<BiggerCenterContainer>
|
<BiggerCenterContainer>
|
||||||
<Stepper steps={2} current={1} className="mb-12" />
|
<Stepper steps={2} current={1} className="mb-12" />
|
||||||
<Heading2 className="!mt-0 !text-3xl">
|
<Heading2 className="!mt-0 !text-3xl">
|
||||||
|
|
@ -287,7 +277,7 @@ export function OnboardingPage() {
|
||||||
{t("onboarding.start.explainer")}
|
{t("onboarding.start.explainer")}
|
||||||
<div
|
<div
|
||||||
className="pt-4 flex cursor-pointer items-center text-type-link"
|
className="pt-4 flex cursor-pointer items-center text-type-link"
|
||||||
onClick={() => setShowModal(true)}
|
onClick={() => infoModal.show()}
|
||||||
>
|
>
|
||||||
<p>More info</p>
|
<p>More info</p>
|
||||||
<Icon className="pl-2" icon={Icons.CIRCLE_QUESTION} />
|
<Icon className="pl-2" icon={Icons.CIRCLE_QUESTION} />
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import classNames from "classnames";
|
|
||||||
import { ReactNode, useEffect } from "react";
|
|
||||||
|
|
||||||
import { IconPatch } from "@/components/buttons/IconPatch";
|
|
||||||
import { Icons } from "@/components/Icon";
|
|
||||||
import { Flare } from "@/components/utils/Flare";
|
|
||||||
import { Heading2 } from "@/components/utils/Text";
|
|
||||||
|
|
||||||
export interface PopupModalProps {
|
|
||||||
title: string;
|
|
||||||
message: ReactNode;
|
|
||||||
closable?: boolean;
|
|
||||||
onClose?: () => void;
|
|
||||||
styles?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PopupModal({
|
|
||||||
title,
|
|
||||||
message,
|
|
||||||
closable = true,
|
|
||||||
onClose,
|
|
||||||
styles,
|
|
||||||
}: PopupModalProps) {
|
|
||||||
useEffect(() => {
|
|
||||||
document.body.style.overflow = "hidden";
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.body.style.overflow = "";
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
"fixed inset-0 z-[100] flex items-center justify-center",
|
|
||||||
"bg-background-main bg-opacity-75 backdrop-filter backdrop-blur-sm",
|
|
||||||
"transition-opacity duration-400",
|
|
||||||
"pointer-events-auto",
|
|
||||||
)}
|
|
||||||
onClick={onClose}
|
|
||||||
>
|
|
||||||
<Flare.Base
|
|
||||||
className={classNames(
|
|
||||||
"group -m-[0.705em] rounded-3xl bg-background-main transition-colors duration-300 focus:relative focus:z-10",
|
|
||||||
"fixed top-0 left-0 right-0 z-50 p-6 bg-mediaCard-hoverBackground bg-opacity-60 backdrop-filter backdrop-blur-lg shadow-lg mx-auto",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
"transition-transform duration-300",
|
|
||||||
"overflow-y-scroll max-h-[90dvh] md:max-h-[90dvh] scrollbar-none",
|
|
||||||
styles,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Flare.Light
|
|
||||||
flareSize={300}
|
|
||||||
cssColorVar="--colors-mediaCard-hoverAccent"
|
|
||||||
backgroundClass="bg-mediaCard-hoverBackground duration-100"
|
|
||||||
className="rounded-3xl bg-background-main group-hover:opacity-100"
|
|
||||||
/>
|
|
||||||
<Flare.Child className="pointer-events-auto relative mb-2p-[0.4em] transition-transform duration-300">
|
|
||||||
<div className="flex justify-between items-center mb-4">
|
|
||||||
<Heading2 className="!mt-0 !mb-0 pr-6">{title}</Heading2>
|
|
||||||
{closable && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="fixed right-4 text-s font-semibold text-type-secondary hover:text-white transition-transform hover:scale-110"
|
|
||||||
onClick={onClose}
|
|
||||||
>
|
|
||||||
<IconPatch icon={Icons.X} />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<p className="text-lg text-type-secondary">{message}</p>
|
|
||||||
</Flare.Child>
|
|
||||||
</div>
|
|
||||||
</Flare.Base>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue