Skip to content

Commit

Permalink
fix: prevent the unexpected disclaimer modal is popping up on iOS
Browse files Browse the repository at this point in the history
- created `useLocalStorage`.
- deleted `disclaimer` store.
- created `withDisclaimerAgreement` HoC.
- relocated `DisclaimerModal` to `hocs/withDisclaimerAgreement`.
- removed unnecessary state in `DisclaimerModal`.
- updated `Button` to style buttons in invalid forms.
- and more...
  • Loading branch information
honeymaro committed Feb 14, 2024
1 parent 438f8f5 commit 65eee1e
Show file tree
Hide file tree
Showing 9 changed files with 789 additions and 696 deletions.
14 changes: 9 additions & 5 deletions src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ const Button = styled.button<ButtonProps>`
&:hover {
opacity: 0.7;
}
&:disabled {
&:disabled,
form:invalid &[type="submit"] {
background-color: ${theme.colors.disabled};
border-color: ${theme.colors.disabled};
color: ${theme.colors.white};
Expand All @@ -67,7 +68,8 @@ const Button = styled.button<ButtonProps>`
&.active {
background-color: ${theme.colors.selected};
}
&:disabled {
&:disabled,
form:invalid &[type="submit"] {
background-color: ${theme.colors.text.background};
opacity: 0.5;
}
Expand All @@ -90,7 +92,8 @@ const Button = styled.button<ButtonProps>`
background-image: ${theme.colors.gradient};
border: none;
color: ${theme.colors.white};
&:disabled {
&:disabled,
form:invalid &[type="submit"] {
background-image: unset;
background-color: ${theme.colors.disabled};
border: none;
Expand All @@ -104,7 +107,8 @@ const Button = styled.button<ButtonProps>`
&:hover {
opacity: 0.7;
}
&:disabled {
&:disabled,
form:invalid &[type="submit"] {
background-color: ${theme.colors.disabled};
border-color: ${theme.colors.disabled};
color: ${theme.colors.white};
Expand Down Expand Up @@ -132,7 +136,7 @@ const Button = styled.button<ButtonProps>`
display: flex;
`}
&:disabled {
&:disabled, form:invalid &[type="submit"] {
cursor: default;
}
`;
Expand Down
660 changes: 0 additions & 660 deletions src/components/Modal/DisclaimerModal/index.tsx

This file was deleted.

671 changes: 671 additions & 0 deletions src/hocs/withDisclaimerAgreement/DisclaimerModal.tsx

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions src/hocs/withDisclaimerAgreement/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import DisclaimerModal from "./DisclaimerModal";
import useDisclaimerAgreement from "./useDisclaimerAgreement";

function withDisclaimerAgreement<P extends object>(
Component: React.ComponentType<P>,
) {
function WrappedComponent(props: P) {
const { isDisclaimerAgreed, agreeDisclaimer } = useDisclaimerAgreement();
return (
<>
<DisclaimerModal
isOpen={!isDisclaimerAgreed}
onAgree={() => {
agreeDisclaimer();
}}
/>
{isDisclaimerAgreed && <Component {...props} />}
</>
);
}
return WrappedComponent;
}

export default withDisclaimerAgreement;
27 changes: 27 additions & 0 deletions src/hocs/withDisclaimerAgreement/useDisclaimerAgreement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import useLocalStorage from "hooks/useLocalStorage";
import { useMemo } from "react";

const useDisclaimerAgreement = () => {
const [disclaimerLastSeen, setDisclaimerLastSeen] = useLocalStorage(
"disclaimer",
0,
);

const isDisclaimerAgreed = useMemo(() => {
if (!disclaimerLastSeen) return false;
const date = new Date();
date.setDate(date.getDate() - 3);
return new Date(disclaimerLastSeen) > date;
}, [disclaimerLastSeen]);

const agreeDisclaimer = () => {
setDisclaimerLastSeen(Date.now());
};

return {
isDisclaimerAgreed,
agreeDisclaimer,
};
};

export default useDisclaimerAgreement;
50 changes: 50 additions & 0 deletions src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useEffect, useMemo, useState } from "react";

const useLocalStorage = <T = string>(key: string, initialValue: T) => {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item;
} catch {
return null;
}
});

const value = useMemo<T>(() => {
try {
return storedValue ? JSON.parse(storedValue) : initialValue;
} catch {
return storedValue;
}
}, [initialValue, storedValue]);

const setValue: React.Dispatch<React.SetStateAction<T>> = (newValue) => {
try {
const valueToStore =
newValue instanceof Function ? newValue(value) : newValue;
setStoredValue(JSON.stringify(valueToStore));
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch {
// ignore write errors
}
};

useEffect(() => {
const handleStorageChange = () => {
try {
const item = window.localStorage.getItem(key);
setStoredValue(item);
} catch {
setStoredValue(null);
}
};
window.addEventListener("storage", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
};
}, [initialValue, key]);

return [value, setValue] as const;
};

export default useLocalStorage;
16 changes: 3 additions & 13 deletions src/layout/Main/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Fragment, PropsWithChildren, useMemo } from "react";
import { Fragment, PropsWithChildren } from "react";
import styled from "@emotion/styled";
import { useAtomValue } from "jotai";
import disclaimerLastSeenAtom from "stores/disclaimer";
import DisclaimerModal from "components/Modal/DisclaimerModal";
import globalElementsAtom from "stores/globalElements";
import Header, {
DEFAULT_HEADER_HEIGHT,
Expand Down Expand Up @@ -118,18 +116,12 @@ function MainLayout({ children }: PropsWithChildren) {
const network = useNetwork();

const globalElements = useAtomValue(globalElementsAtom);
const disclaimerLastSeen = useAtomValue(disclaimerLastSeenAtom);
const isDisclaimerAgreed = useMemo(() => {
if (!disclaimerLastSeen) return false;
const date = new Date();
date.setDate(date.getDate() - 3);
return disclaimerLastSeen > date;
}, [disclaimerLastSeen]);

return (
<>
<Header />
<Wrapper hasBanner={network.name !== "mainnet"}>
{isDisclaimerAgreed && children}
{children}
<BrowserDelegateButton />
<FooterWrapper>
<Footer />
Expand All @@ -142,8 +134,6 @@ function MainLayout({ children }: PropsWithChildren) {
<NavBarWrapper>{navBar}</NavBarWrapper>
</>
)}

<DisclaimerModal isOpen={!isDisclaimerAgreed} />
{globalElements.map(({ element, id }) => (
<Fragment key={id}>{element}</Fragment>
))}
Expand Down
5 changes: 4 additions & 1 deletion src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import WalletPage from "pages/Wallet";
import TokenDetailPage from "pages/Tokens/TokenDetail";
import PoolDetailPage from "pages/Earn/Pools/PoolDetail";
import MainLayout from "layout/Main";
import withDisclaimerAgreement from "hocs/withDisclaimerAgreement";
// TODO: uncomment when lockdrop is ready
// import LockdropPage from "pages/Earn/Lockdrop";
// import StakePage from "pages/Earn/Lockdrop/Stake";
Expand All @@ -37,12 +38,14 @@ function ReplaceToEarn() {
);
}

const OutletWithDisclaimerAgreement = withDisclaimerAgreement(Outlet);

const routes = createBrowserRouter([
{
path: "/",
element: (
<MainLayout>
<Outlet />
<OutletWithDisclaimerAgreement />
</MainLayout>
),
children: [
Expand Down
17 changes: 0 additions & 17 deletions src/stores/disclaimer.ts

This file was deleted.

0 comments on commit 65eee1e

Please sign in to comment.