Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate routes to modals #402

Merged
merged 6 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions packages/frontend/src/MobileApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import CONFIG from "./config";
import ToastContainer from "./containers/toasts/ToastContainer";
import "@alphaday/ui-kit/global.scss";
import "./customIonicStyles.scss";
import { ModalContainer } from "./mobile-containers/ModalContainer";
import {
EMobileRoutePaths,
EMobileTabRoutePaths,
mobileRoutes,
modals,
} from "./routes";

const { IS_DEV } = CONFIG;
Expand Down Expand Up @@ -130,15 +132,19 @@ const RouterChild = () => {
);
};

/**
* TODO: Move user-settings (and any other view that should be accessible from multiple tabs)
* to a modal.
* For the MVP it's fine to nest everything within /superfeed
*/
const MobileApp: React.FC = () => {
return (
<IonApp className="theme-dark">
<IonReactRouter>
{modals.map((modal) => (
<ModalContainer
key={modal.id}
modalId={modal.id}
className="!max-w-full min-h-[100vh] rounded-none border-none"
>
<modal.component />
</ModalContainer>
))}
<RouterChild />
</IonReactRouter>
<ToastContainer
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/src/api/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ export * from "./useValueWatcher";
export * from "./useOnScreen";
export * from "./usePullToRefresh";
export * from "./useHistory";
export * from "../store/providers/controlled-modal-provider";
26 changes: 12 additions & 14 deletions packages/frontend/src/api/store/providers/app-context-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FC } from "react";
import { ChatProvider } from "./chat-context";
import ControlledModalProvider from "./controlled-modal-provider";
import { DimensionsProvider } from "./dimensions-context";
import { OauthProvider } from "./oauth-provider";
import { PWAInstallProvider } from "./pwa-install-provider";
Expand All @@ -21,19 +22,16 @@ function Compose(props: Props) {
}, children);
}

const providers = [
PWAInstallProvider,
TutorialProvider,
DimensionsProvider,
ChatProvider,
WalletViewProvider,
OauthProvider,
ControlledModalProvider,
];

export const AppContextProvider: FC<{ children?: React.ReactNode }> = ({
children,
}) => (
<Compose
providers={[
PWAInstallProvider,
TutorialProvider,
DimensionsProvider,
ChatProvider,
WalletViewProvider,
OauthProvider,
]}
>
{children}
</Compose>
);
}) => <Compose providers={providers}>{children}</Compose>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
createContext,
useCallback,
useContext,
useMemo,
useState,
} from "react";

interface Prop {
activeModal: string | null;
setActiveModal: (modalId: string) => void;
closeModal: () => void;
}

export const ControlledModalContext = createContext<Prop>({
activeModal: null,
setActiveModal: () => {},
closeModal: () => {},
});

export const useControlledModal = () => {
const context = useContext(ControlledModalContext);
if (context === undefined) {
throw new Error(
"controlled-modal-context:useControlledModal: not used within a Provider"
);
}
return context;
};

const MODAL_HISTORY = new Set<string>();

const ControlledModalProvider: React.FC<{ children?: React.ReactNode }> = ({
children,
}) => {
const [activeModal, setActiveModal] = useState<string | null>(null);

const closeModal = useCallback(() => {
if (activeModal) {
MODAL_HISTORY.delete(activeModal);
// set active modal to last modal in history
const lastModal = Array.from(MODAL_HISTORY).pop();
setActiveModal(lastModal || null);
}
}, [activeModal]);

const setActiveModalWithHistory = (modalId: string) => {
if (!MODAL_HISTORY.has(modalId)) {
setActiveModal(modalId);
MODAL_HISTORY.add(modalId);
}
};

return (
<ControlledModalContext.Provider
value={useMemo(
() => ({
activeModal,
setActiveModal: setActiveModalWithHistory,
closeModal,
}),
[activeModal, closeModal]
)}
>
{children}
</ControlledModalContext.Provider>
);
};

export default ControlledModalProvider;
6 changes: 3 additions & 3 deletions packages/frontend/src/layout/PagedMobileLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, ReactNode } from "react";
import { Pager, ScrollBar } from "@alphaday/ui-kit";
import { useHistory } from "src/api/hooks";
import { useControlledModal } from "src/api/hooks";

const PagedMobileLayout: FC<{
title: string;
Expand All @@ -9,14 +9,14 @@ const PagedMobileLayout: FC<{
handleClose?: () => void;
handleBack?: () => void;
}> = ({ children, title, onScroll, handleBack, handleClose }) => {
const { backNavigation } = useHistory();
const { closeModal } = useControlledModal();
return (
<ScrollBar className="h-screen flex flex-col" onScroll={onScroll}>
<Pager
title={title}
handleBack={() => {
handleBack?.();
backNavigation();
closeModal();
}}
handleClose={handleClose}
/>
Expand Down
13 changes: 8 additions & 5 deletions packages/frontend/src/mobile-components/navigation/NavHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { FC } from "react";
import { Link } from "react-router-dom";
import { useControlledModal } from "src/api/hooks";
import { ReactComponent as SearchSVG } from "src/assets/svg/search.svg";
import { ReactComponent as UserSVG } from "src/assets/svg/user.svg";
import { EMobileRoutePaths } from "src/routes";
import { EMobileModalIds } from "src/routes";

interface IProps {
avatar: string | undefined;
onSearchHandleClick?: () => void;
}

export const NavHeader: FC<IProps> = ({ avatar, onSearchHandleClick }) => {
const { setActiveModal } = useControlledModal();
return (
<div className="w-full flex justify-between pt-2 px-5">
<Link
to={EMobileRoutePaths.UserSettings}
<span
role="button"
tabIndex={0}
className="relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"
onClick={() => {
setActiveModal(EMobileModalIds.UserSettings);
}}
>
<span className="absolute -inset-1.5" />
<span className="sr-only">Open user menu</span>
Expand All @@ -31,7 +34,7 @@ export const NavHeader: FC<IProps> = ({ avatar, onSearchHandleClick }) => {
<UserSVG className="fill-primary h-7 w-7" />
</div>
)}
</Link>
</span>
<button
type="button"
className="bg-backgroundVariant300 self-center rounded-lg p-2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC } from "react";
import { FormInput, Pager, ScrollBar } from "@alphaday/ui-kit";
import moment from "moment";
import { useHistory } from "src/api/hooks";
import { useControlledModal } from "src/api/hooks";
import { TCoin, THolding } from "src/api/types";

interface IAddHolding {
Expand All @@ -17,7 +17,7 @@ const AddHolding: FC<IAddHolding> = ({
holding,
setHolding,
}) => {
const history = useHistory();
const { closeModal } = useControlledModal();

const defaultHolding: THolding = {
coin: selectedCoin,
Expand Down Expand Up @@ -54,7 +54,7 @@ const AddHolding: FC<IAddHolding> = ({
{selectedCoin.name}
</span>
}
handleClose={history.backNavigation}
handleClose={closeModal}
handleBack={() => setSelectedCoin(undefined)}
/>
<div className="flex flex-col items-center mt-4 mx-4">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC, FormEvent } from "react";
import { Input, Pager, ScrollBar, Spinner } from "@alphaday/ui-kit";
import { useHistory } from "src/api/hooks";
import { useControlledModal } from "src/api/hooks";
import { TCoin } from "src/api/types";
import { ReactComponent as ChevronSVG } from "src/assets/icons/chevron-right.svg";
import { ReactComponent as SearchSVG } from "src/assets/svg/search.svg";
Expand All @@ -22,11 +22,11 @@ const SelectHoldingCoin: FC<ISelectHoldingCoin> = ({
coins,
setSelectedCoin,
}) => {
const history = useHistory();
const { closeModal } = useControlledModal();

return (
<ScrollBar onScroll={onScroll}>
<Pager title="Add Manually" handleClose={history.backNavigation} />
<Pager title="Add Manually" handleClose={closeModal} />
<p className="mx-4 fontGroup-highlight">
Search or select the desired crypto coin that you have in your
portfolio.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { OutlineButton, twMerge } from "@alphaday/ui-kit";
import { ReactComponent as CopySVG } from "src/assets/icons/copy.svg";
import { ReactComponent as HandSVG } from "src/assets/icons/hand.svg";
import { ReactComponent as WalletSVG } from "src/assets/icons/wallet.svg";
import { EMobileRoutePaths } from "src/routes";
import { EMobileModalIds } from "src/routes";

const WalletConnectionOptions: FC<{
isAuthenticated: boolean;
Expand All @@ -16,23 +16,21 @@ const WalletConnectionOptions: FC<{
title="Add Wallet"
subtext="Add your wallet manually to get started"
icon={<WalletSVG className="w-[24px] mr-1" />}
onClick={() => onClick(EMobileRoutePaths.PortfolioAddWallet)}
onClick={() => onClick(EMobileModalIds.PortfolioAddWallet)}
isAuthenticated={isAuthenticated}
/>
<OutlineButton
title="Connect Wallet"
subtext="Connect your wallet to get started"
icon={<CopySVG className="w-[22px] mr-1" />}
onClick={() =>
onClick(EMobileRoutePaths.PortfolioConnectWallet)
}
onClick={() => onClick(EMobileModalIds.PortfolioConnectWallet)}
isAuthenticated={isAuthenticated}
/>
<OutlineButton
title="Add Holdings Manually"
subtext="Add your holdings manually"
icon={<HandSVG className="w-[20px] mr-1" />}
onClick={() => onClick(EMobileRoutePaths.PortfolioAddHolding)}
onClick={() => onClick(EMobileModalIds.PortfolioAddHolding)}
isAuthenticated={isAuthenticated}
/>
</div>
Expand Down
Loading
Loading