Skip to content

Commit

Permalink
feat(frontend): remove fullscreen modal
Browse files Browse the repository at this point in the history
  • Loading branch information
luzzifoss committed Oct 16, 2023
1 parent 2763792 commit 23835f2
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 277 deletions.
134 changes: 8 additions & 126 deletions packages/frontend/src/pages/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,152 +1,34 @@
import React, { useCallback, useEffect, useState } from "react";
import {
Route,
Routes,
useLocation,
matchPath,
type Location,
useNavigate,
} from "react-router-dom";
import React from "react";
import { Route, Routes } from "react-router-dom";
import { Home } from "../home";
import { Page } from "../page";
import { Campaigns } from "../campaigns";
import { CreateWithTemplateId } from "../create-with-template-id";
import { useFathomTrackPageWatch } from "../../hooks/useFathomTrackPageWatch";
import { usePreviousDistinct } from "react-use";
import { useStagingMode } from "@carrot-kpi/react";
import { StagingModeBanner } from "../../components/staging-mode-banner";

const CREATE_ROUTE_PATH = { path: "/create/:templateId", key: "create" };
const PAGE_ROUTE_PATH = { path: "/campaigns/:address", key: "page" };

const MODAL_ROUTE_PATHS = [CREATE_ROUTE_PATH, PAGE_ROUTE_PATH];

const DEFAULT_LOCATION = {
pathname: "/",
state: undefined,
key: "",
search: "",
hash: "",
};

interface AppProps {
templateId?: number;
}

export const App = ({ templateId }: AppProps) => {
const location = useLocation();
const previousLocation = usePreviousDistinct(location);
const navigate = useNavigate();
const stagingMode = useStagingMode();

useFathomTrackPageWatch();

const [modalLocation, setModalLocation] = useState<Location | undefined>();
const [closingModalId, setClosingModalId] = useState("");
const [mainLocation, setMainLocation] = useState(location);

useEffect(() => {
if (
previousLocation &&
location.pathname === previousLocation.pathname &&
location.search === previousLocation.search &&
location.state === previousLocation.state
)
return;

// detect modal opening and setup. If the previous distinct
// location was not a modal route, and the current one is,
// the previous location is set as the main route to prevent
// the modal background from unmounting, while the new location
// is set as the modals one in order to correctly mount the
// component with animations etc etc.
const openingModal = MODAL_ROUTE_PATHS.find(({ path }) =>
matchPath({ path }, location.pathname),
);
if (openingModal) {
// in case previous location is not there (for example when
// coming in through an external link), set the homepage as
// the main location
document.documentElement.classList.add("overflow-hidden");

const isPreviousLocationModal =
previousLocation &&
!!MODAL_ROUTE_PATHS.find(({ path }) =>
matchPath({ path }, previousLocation.pathname),
);
// if the previous location was a modal (i.e. if we're switching
// through modals) set default location as main
setMainLocation(
isPreviousLocationModal
? DEFAULT_LOCATION
: previousLocation || DEFAULT_LOCATION,
);
setModalLocation(location);
return;
}

// detect modal closing and teardown. If the previous distinct
// location was a modal route, and the current one isn't, trigger
// the modal close.
let closingModalId = "";
if (previousLocation && !!modalLocation) {
for (let i = 0; i < MODAL_ROUTE_PATHS.length; i++) {
const { path, key } = MODAL_ROUTE_PATHS[i];
if (matchPath({ path }, previousLocation.pathname)) {
closingModalId = key;
break;
}
}
}
if (closingModalId) {
setModalLocation(previousLocation);
setClosingModalId(closingModalId);
return;
}

// if not coming from a modal or going to one, scroll to top on
// distinct main location changes
if (!location.state?.navigatingAwayFromModal) setMainLocation(location);
}, [location, mainLocation, modalLocation, previousLocation]);

const handleAnimationEnd = useCallback(() => {
document.documentElement.classList.remove("overflow-hidden");
setClosingModalId("");
setModalLocation(undefined);
navigate(mainLocation, { state: { navigatingAwayFromModal: true } });
}, [mainLocation, navigate]);

return (
<>
{stagingMode && <StagingModeBanner />}
<Routes location={mainLocation}>
<Routes>
<Route path="/" element={<Home templateId={templateId} />} />
<Route path="/campaigns" element={<Campaigns />} />
<Route
path="/create/:templateId"
element={<CreateWithTemplateId />}
/>
<Route path="/campaigns/:address" element={<Page />} />
</Routes>
{modalLocation && (
<Routes location={modalLocation}>
<Route
path={CREATE_ROUTE_PATH.path}
element={
<CreateWithTemplateId
closing={
closingModalId === CREATE_ROUTE_PATH.key
}
onOutAnimationEnd={handleAnimationEnd}
/>
}
/>
<Route
path={PAGE_ROUTE_PATH.path}
element={
<Page
closing={closingModalId === PAGE_ROUTE_PATH.key}
onOutAnimationEnd={handleAnimationEnd}
/>
}
/>
</Routes>
)}
</>
);
};
138 changes: 63 additions & 75 deletions packages/frontend/src/pages/create-with-template-id/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,18 @@ import {
useIPFSGatewayURL,
usePreferDecentralization,
} from "@carrot-kpi/react";
import { useTransition, config as springConfig } from "@react-spring/web";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useAccount, useNetwork, usePublicClient } from "wagmi";
import { Fetcher, ResolvedTemplate } from "@carrot-kpi/sdk";
import { ErrorFeedback, Loader } from "@carrot-kpi/ui";
import { AnimatedFullscreenModal } from "../../components/fullscreen-modal";
import { useAddTransaction } from "../../hooks/useAddTransaction";
import { Authenticate } from "../../components/authenticate";
import { useIsPinningProxyAuthenticated } from "../../hooks/useIsPinningProxyAuthenticated";
import { useInvalidateLatestKPITokens } from "../../hooks/useInvalidateLatestKPITokens";
import { Navbar } from "../../components/ui/navbar";

interface CreateWithTemplateIdProps {
closing?: boolean;
onOutAnimationEnd?: () => void;
}

export const CreateWithTemplateId = ({
closing,
onOutAnimationEnd,
}: CreateWithTemplateIdProps) => {
export const CreateWithTemplateId = () => {
const { i18n, t } = useTranslation();
const { state } = useLocation();
const navigate = useNavigate();
Expand All @@ -37,29 +28,13 @@ export const CreateWithTemplateId = ({
const ipfsGatewayURL = useIPFSGatewayURL();
const invalidateLatestKPITokens = useInvalidateLatestKPITokens();

const [loading, setLoading] = useState(false);
const [template, setTemplate] = useState<ResolvedTemplate | null>(
state && "specification" in state.template ? state.template : null,
);
const [show, setShow] = useState(!closing);
const [formKey, setFormKey] = useState(0);
const pinningProxyAuthenticated = useIsPinningProxyAuthenticated();

const transitions = useTransition(show, {
config: { ...springConfig.default, duration: 100 },
from: { opacity: 0, translateY: "0.5%", scale: 0.97 },
enter: { opacity: 1, translateY: "0%", scale: 1 },
leave: {
opacity: 0,
translateY: "0.5%",
scale: 0.97,
},
onDestroyed: onOutAnimationEnd,
});

useEffect(() => {
setShow(!closing);
}, [closing]);

// every time the chain or the connected address changes,
// reset the creation form state
useEffect(() => {
Expand All @@ -82,6 +57,7 @@ export const CreateWithTemplateId = ({
}
let cancelled = false;
const fetchData = async () => {
if (!cancelled) setLoading(true);
try {
const templates = await Fetcher.fetchKPITokenTemplates({
publicClient,
Expand All @@ -92,7 +68,7 @@ export const CreateWithTemplateId = ({
console.warn(
`inconsistent array length while fetching template with id ${templateId} on ${chain?.name}`,
);
if (!cancelled) setShow(false);
if (!cancelled) setTemplate(null);
return;
}
const resolvedTemplates = await Fetcher.resolveTemplates({
Expand All @@ -103,7 +79,7 @@ export const CreateWithTemplateId = ({
console.warn(
`inconsistent array length while resolving template with id ${templateId} on ${chain?.name}`,
);
if (!cancelled) setShow(false);
if (!cancelled) setTemplate(null);
return;
}
if (!cancelled) setTemplate(resolvedTemplates[0]);
Expand All @@ -112,6 +88,8 @@ export const CreateWithTemplateId = ({
`could not fetch template with id ${templateId}`,
error,
);
} finally {
if (!cancelled) setLoading(false);
}
};
void fetchData();
Expand All @@ -132,51 +110,61 @@ export const CreateWithTemplateId = ({
}, [invalidateLatestKPITokens]);

const handleDismiss = useCallback(() => {
setShow(false);
}, []);
navigate(-1);
}, [navigate]);

return transitions((style, show) => {
return (
show && (
<AnimatedFullscreenModal
bgColor="green"
springStyle={style}
onDismiss={handleDismiss}
>
{!pinningProxyAuthenticated ? (
<Authenticate onCancel={handleDismiss} />
) : (
<KPITokenCreationForm
key={formKey}
template={template || undefined}
fallback={
<div className="py-10 text-black flex justify-center">
<Loader />
</div>
}
error={
<div className="py-10 flex justify-center">
<ErrorFeedback
messages={{
title: t(
"error.initializing.creation.title",
),
description: t(
"error.initializing.creation.description",
),
}}
/>
</div>
}
i18n={i18n}
className={{ root: "w-full h-full" }}
onCreate={handleCreate}
navigate={navigate}
onTx={addTransaction}
return (
<div className="flex flex-col w-screen h-screen overflow-y-auto overflow-x-hidden bg-green">
<Navbar />
<div className="flex-grow bg-grid-light bg-left-top">
{!pinningProxyAuthenticated ? (
<Authenticate onCancel={handleDismiss} />
) : loading ? (
<div className="py-10 text-black flex justify-center">
<Loader />
</div>
) : template ? (
<KPITokenCreationForm
key={formKey}
template={template || undefined}
fallback={
<div className="py-10 text-black flex justify-center">
<Loader />
</div>
}
error={
<div className="py-10 flex justify-center">
<ErrorFeedback
messages={{
title: t(
"error.initializing.creation.title",
),
description: t(
"error.initializing.creation.description",
),
}}
/>
</div>
}
i18n={i18n}
className={{ root: "w-full h-full" }}
onCreate={handleCreate}
navigate={navigate}
onTx={addTransaction}
/>
) : (
<div className="py-10 flex justify-center">
<ErrorFeedback
messages={{
title: t("error.initializing.creation.title"),
description: t(
"error.initializing.creation.description",
),
}}
/>
)}
</AnimatedFullscreenModal>
)
);
});
</div>
)}
</div>
</div>
);
};
Loading

0 comments on commit 23835f2

Please sign in to comment.