Skip to content

Commit e1a8db7

Browse files
authored
Merge pull request #3688 from IntersectMBO/fix/3681-bug-red-banner-collapse-state-not-preserved-when-wallet-is-not-connected
fix(#3681): preserve maintenance banner expanded state
2 parents 36aa140 + 2238a84 commit e1a8db7

File tree

11 files changed

+171
-30
lines changed

11 files changed

+171
-30
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ changes.
1212

1313
### Added
1414

15+
- Preserve maintenance ending banner state on the wallet connection change [Issue 3681](https://github.com/IntersectMBO/govtool/issues/3681)
16+
1517
### Fixed
1618

1719
### Changed
@@ -20,8 +22,8 @@ changes.
2022

2123
## [v2.0.23](https://github.com/IntersectMBO/govtool/releases/tag/v2.0.23) 2025-05-22
2224

23-
2425
### Added
26+
2527
- Add CIP-129 support for gov_actions hashes in Live Voting (governance actions) [Issue 3619](https://github.com/IntersectMBO/govtool/issues/3619)
2628

2729
- Add maintenance ending banner [Issue 3647](https://github.com/IntersectMBO/govtool/issues/3647)

govtool/frontend/src/components/organisms/Drawer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import { useFeatureFlag } from "@context";
77
import { useGetVoterInfo } from "@hooks";
88
import { WalletInfoCard, DRepInfoCard } from "@molecules";
99
import { openInNewTab } from "@utils";
10+
import { useMaintenanceEndingBannerContext } from "./MaintenanceEndingBanner";
1011

1112
export const Drawer = () => {
1213
const {
1314
isProposalDiscussionForumEnabled,
1415
isGovernanceOutcomesPillarEnabled,
1516
} = useFeatureFlag();
1617
const { voter } = useGetVoterInfo();
18+
const { height: maintenanceEndingBannerHeight } =
19+
useMaintenanceEndingBannerContext();
1720

1821
return (
1922
<Box
@@ -23,7 +26,7 @@ export const Drawer = () => {
2326
flexDirection: "column",
2427
height: "100vh",
2528
position: "sticky",
26-
top: 0,
29+
top: maintenanceEndingBannerHeight,
2730
width: `${DRAWER_WIDTH}px`,
2831

2932
overflowY: "auto",

govtool/frontend/src/components/organisms/MaintenanceEndingBanner.tsx renamed to govtool/frontend/src/components/organisms/MaintenanceEndingBanner/MaintenanceEndingBanner.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import { Box, Typography, IconButton } from "@mui/material";
2-
import { useState } from "react";
32
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
43
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
54
import { Trans, useTranslation } from "react-i18next";
5+
import { useMaintenanceEndingBannerContext } from "./MaintenanceEndingBannerContext";
6+
7+
const EXPANDED_HEIGHT = 135;
8+
const COLLAPSED_HEIGHT = 50;
69

710
export const MaintenanceEndingBanner = () => {
8-
const [isExpanded, setIsExpanded] = useState(true);
11+
const { ref, isExpanded, toggleExpanded } =
12+
useMaintenanceEndingBannerContext();
913
const { t } = useTranslation();
1014

11-
const handleToggle = () => {
12-
setIsExpanded((prev) => !prev);
13-
};
14-
1515
return (
1616
<Box
17+
ref={ref}
1718
sx={{
1819
backgroundColor: "#9c2224",
1920
width: "100%",
@@ -44,7 +45,7 @@ export const MaintenanceEndingBanner = () => {
4445
</Box>
4546
<Box sx={{ display: "flex" }}>
4647
<IconButton
47-
onClick={handleToggle}
48+
onClick={toggleExpanded}
4849
size="small"
4950
data-testid="toggle-maintenance-banner"
5051
sx={{
@@ -60,7 +61,7 @@ export const MaintenanceEndingBanner = () => {
6061
{/* Expandable Content */}
6162
<Box
6263
sx={{
63-
maxHeight: isExpanded ? "500px" : "0px",
64+
height: isExpanded ? EXPANDED_HEIGHT - COLLAPSED_HEIGHT : "0px",
6465
transition: "max-height 0.3s ease-in-out",
6566
overflow: "hidden",
6667
}}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {
2+
createContext,
3+
createRef,
4+
useContext,
5+
useMemo,
6+
useRef,
7+
useState,
8+
useLayoutEffect,
9+
useEffect,
10+
} from "react";
11+
import {
12+
getItemFromLocalStorage,
13+
MAINTENANCE_ENDING_BANNER_EXPANDED_KEY,
14+
setItemToLocalStorage,
15+
} from "@/utils";
16+
17+
interface MaintenanceEndingBannerContextType {
18+
ref: React.RefObject<HTMLDivElement>;
19+
height: number;
20+
isExpanded: boolean;
21+
toggleExpanded: () => void;
22+
}
23+
24+
const MaintenanceEndingBannerContext =
25+
createContext<MaintenanceEndingBannerContextType>({
26+
ref: createRef<HTMLDivElement>(),
27+
height: 0,
28+
isExpanded: true,
29+
toggleExpanded: () => {},
30+
});
31+
32+
export const MaintenanceEndingBannerProvider = ({
33+
children,
34+
}: React.PropsWithChildren) => {
35+
const ref = useRef<HTMLDivElement>(null);
36+
const [height, setHeight] = useState(0);
37+
const [isExpanded, setIsExpanded] = useState<boolean>(true);
38+
39+
useEffect(() => {
40+
const storedValue = getItemFromLocalStorage(
41+
MAINTENANCE_ENDING_BANNER_EXPANDED_KEY,
42+
);
43+
if (storedValue !== null) {
44+
setIsExpanded(JSON.parse(storedValue));
45+
}
46+
}, []);
47+
48+
const toggleExpanded = () => {
49+
setItemToLocalStorage(MAINTENANCE_ENDING_BANNER_EXPANDED_KEY, !isExpanded);
50+
setIsExpanded((prev) => !prev);
51+
};
52+
53+
useLayoutEffect(() => {
54+
let frameId: number | null = null;
55+
56+
const updatePosition = () => {
57+
if (ref.current) {
58+
const rect = ref.current.getBoundingClientRect();
59+
60+
setHeight(rect.height);
61+
}
62+
};
63+
64+
const throttledUpdate = () => {
65+
// Context skipping update - frame already queued
66+
if (frameId) return;
67+
68+
frameId = requestAnimationFrame(() => {
69+
updatePosition();
70+
frameId = null;
71+
});
72+
};
73+
74+
updatePosition();
75+
76+
window.addEventListener("scroll", throttledUpdate, { passive: true });
77+
window.addEventListener("resize", throttledUpdate);
78+
79+
return () => {
80+
window.removeEventListener("scroll", throttledUpdate);
81+
window.removeEventListener("resize", throttledUpdate);
82+
83+
if (frameId) {
84+
cancelAnimationFrame(frameId);
85+
}
86+
};
87+
}, []);
88+
89+
const value = useMemo(
90+
() => ({
91+
ref,
92+
height,
93+
isExpanded,
94+
toggleExpanded,
95+
}),
96+
[ref, height, isExpanded, toggleExpanded],
97+
);
98+
99+
return (
100+
<MaintenanceEndingBannerContext.Provider value={value}>
101+
{children}
102+
</MaintenanceEndingBannerContext.Provider>
103+
);
104+
};
105+
106+
export const useMaintenanceEndingBannerContext = () => {
107+
const context = useContext(MaintenanceEndingBannerContext);
108+
if (!context) {
109+
throw new Error(
110+
"useMaintenanceEndingBannerContext must be used within a MaintenanceEndingBannerProvider",
111+
);
112+
}
113+
return context;
114+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./MaintenanceEndingBanner";
2+
export * from "./MaintenanceEndingBannerContext";

govtool/frontend/src/components/organisms/TopBanners.tsx

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { Box, Link, Typography } from "@mui/material";
22
import { Trans, useTranslation } from "react-i18next";
3-
import { useAppContext, useCardano } from "@/context";
3+
import { useAppContext } from "@/context";
44
import { LINKS } from "@/consts/links";
55
import { MaintenanceEndingBanner } from "./MaintenanceEndingBanner";
66

77
export const TopBanners = () => {
88
const { isMainnet, networkName, isInBootstrapPhase, isAppInitializing } =
99
useAppContext();
10-
const { isEnabled } = useCardano();
1110
const { t } = useTranslation();
1211

1312
if (isAppInitializing) {
@@ -49,18 +48,16 @@ export const TopBanners = () => {
4948
</Box>
5049

5150
{/* GOVTOOL MAINTENANCE ENDING SOON BANNER */}
52-
{isEnabled && (
53-
<Box
54-
sx={{
55-
position: "sticky",
56-
top: 0,
57-
width: "100%",
58-
zIndex: 1200,
59-
}}
60-
>
61-
<MaintenanceEndingBanner />
62-
</Box>
63-
)}
51+
<Box
52+
sx={{
53+
position: "sticky",
54+
top: 0,
55+
width: "100%",
56+
zIndex: 1200,
57+
}}
58+
>
59+
<MaintenanceEndingBanner />
60+
</Box>
6461

6562
{/* BOOTSTRAPPING BANNER */}
6663
<Box>

govtool/frontend/src/components/organisms/TopNav.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { useCardano, useFeatureFlag, useModal } from "@context";
88
import { useScreenDimension, useTranslation } from "@hooks";
99
import { openInNewTab } from "@utils";
1010
import { DrawerMobile } from "./DrawerMobile";
11-
import { MaintenanceEndingBanner } from "./MaintenanceEndingBanner";
11+
import { useMaintenanceEndingBannerContext } from "./MaintenanceEndingBanner";
1212

1313
const POSITION_TO_BLUR = 80;
1414

@@ -26,6 +26,9 @@ export const TopNav = ({ isConnectButton = true }) => {
2626
const navigate = useNavigate();
2727
const { t } = useTranslation();
2828

29+
const { height: maintenanceEndingBannerHeight } =
30+
useMaintenanceEndingBannerContext();
31+
2932
useEffect(() => {
3033
const onScroll = () => {
3134
if (!containerRef.current?.nextElementSibling) return;
@@ -137,9 +140,14 @@ export const TopNav = ({ isConnectButton = true }) => {
137140
);
138141

139142
return (
140-
<Box sx={{ position: "sticky", top: 0, zIndex: 100, width: "100%" }}>
141-
<MaintenanceEndingBanner />
142-
143+
<Box
144+
sx={{
145+
position: "sticky",
146+
top: maintenanceEndingBannerHeight,
147+
zIndex: 100,
148+
width: "100%",
149+
}}
150+
>
143151
<AppBar
144152
ref={containerRef}
145153
position="static"

govtool/frontend/src/components/organisms/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ export * from "./ValidatedGovernanceActionCard";
2929
export * from "./ValidatedGovernanceVotedOnCard";
3030
export * from "./VoteContext";
3131
export * from "./WrongRouteInfo";
32+
export * from "./MaintenanceEndingBanner";

govtool/frontend/src/context/contextProviders.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { GovernanceActionProvider } from "./governanceAction";
88
import { AdaHandleProvider } from "./adaHandle";
99
import { ProposalDiscussionProvider } from "./proposalDiscussion";
1010

11+
import { MaintenanceEndingBannerProvider } from "@/components/organisms/MaintenanceEndingBanner/MaintenanceEndingBannerContext";
12+
1113
interface Props {
1214
children: React.ReactNode;
1315
}
@@ -21,7 +23,11 @@ const ContextProviders = ({ children }: Props) => (
2123
<ModalProvider>
2224
<SnackbarProvider>
2325
<DataActionsBarProvider>
24-
<CardanoProvider>{children}</CardanoProvider>
26+
<CardanoProvider>
27+
<MaintenanceEndingBannerProvider>
28+
{children}
29+
</MaintenanceEndingBannerProvider>
30+
</CardanoProvider>
2531
</DataActionsBarProvider>
2632
</SnackbarProvider>
2733
</ModalProvider>

govtool/frontend/src/pages/Dashboard.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@ export const Dashboard = () => {
8888

8989
return (
9090
<Background opacity={0.7}>
91-
<Box sx={{ display: "flex", position: "relative" }}>
91+
<Box
92+
sx={{
93+
display: "flex",
94+
position: "relative",
95+
}}
96+
>
9297
{isMobile ? null : <Drawer />}
9398
<Box
9499
sx={{

0 commit comments

Comments
 (0)