diff --git a/src/shared/components/Dialogs/Panel.tsx b/src/shared/components/Dialogs/Panel.tsx index e36cc4b9..edac72ef 100644 --- a/src/shared/components/Dialogs/Panel.tsx +++ b/src/shared/components/Dialogs/Panel.tsx @@ -1,7 +1,7 @@ import React, { ReactNode, CSSProperties } from "react" import classNames from "classnames" import { animated } from "@react-spring/web" -import { useRealmPanelTransition } from "shared/hooks" +import { usePanelRealmClose, useRealmPanelTransition } from "shared/hooks" import Portal from "../Interface/Portal" type PortalSectionProps = { @@ -36,12 +36,17 @@ function Container({ style, }: PanelContainerProps) { const containerTransitionStyles = useRealmPanelTransition(position) + const closePanel = usePanelRealmClose() return ( { + if (e.target !== e.currentTarget) return + closePanel() + }} >
{ + const onUnload = () => { + posthog?.capture("$pageleave") + } + window.addEventListener("beforeunload", onUnload) + return () => { + window.removeEventListener("beforeunload", onUnload) + } + }, [posthog]) +} + export function useTrackEvents() { const location = useLocation() const posthog = usePostHog() diff --git a/src/shared/hooks/realm.ts b/src/shared/hooks/realm.ts index d1290e22..9d991a14 100644 --- a/src/shared/hooks/realm.ts +++ b/src/shared/hooks/realm.ts @@ -1,6 +1,12 @@ import { easings, useSpring } from "@react-spring/web" -import { useMemo } from "react" -import { selectRealmPanelVisible, useDappSelector } from "redux-state" +import { useCallback, useMemo } from "react" +import { + selectRealmPanelVisible, + setDisplayedRealmId, + setRealmPanelVisible, + useDappDispatch, + useDappSelector, +} from "redux-state" import { REALM_PANEL_ANIMATION_TIME } from "shared/constants" import { useTabletScreen } from "./helpers" @@ -71,3 +77,20 @@ export function useRealmCloseButtonTransition() { return buttonTransition } + +export function usePanelRealmClose() { + const dispatch = useDappDispatch() + + const handlePanelClose = useCallback(() => { + dispatch(setRealmPanelVisible(false)) + + const timeout = setTimeout( + () => dispatch(setDisplayedRealmId(null)), + REALM_PANEL_ANIMATION_TIME + ) + + return () => clearTimeout(timeout) + }, [dispatch]) + + return handlePanelClose +} diff --git a/src/shared/utils/timers.ts b/src/shared/utils/timers.ts index 303f772f..e2924537 100644 --- a/src/shared/utils/timers.ts +++ b/src/shared/utils/timers.ts @@ -16,16 +16,28 @@ export const formatDate = (date: Date): string => }) .toUpperCase() +export const convertDateToUTCTimezone = () => { + const currentDate = new Date() + currentDate.setUTCHours(currentDate.getUTCHours()) + + return currentDate +} + // This function calculates time remaining in the format: X days Y minutes remaining export const getTimeRemaining = (endDate: Date): string => { - const currentTimestamp = Date.now() + const currentDate = convertDateToUTCTimezone() const endDateTimestamp = endDate.getTime() + const currentTimestamp = currentDate.getTime() + const timeRemainingTimestamp = endDateTimestamp - currentTimestamp const daysLeft = timeRemainingTimestamp / DAY const hoursLeft = timeRemainingTimestamp / HOUR - Math.floor(daysLeft) * 24 + // If 24h hours left, convert it to one day + const adjustedDaysLeft = Math.ceil(hoursLeft) === 24 ? daysLeft + 1 : daysLeft + // If less than 1 hour left, display "coming soon" if (Math.floor(daysLeft) === 0 && hoursLeft < 1) { return XP_COMING_SOON_TEXT @@ -33,41 +45,42 @@ export const getTimeRemaining = (endDate: Date): string => { let timeRemainingText = "" - if (Math.floor(daysLeft) > 0) { + if (Math.floor(adjustedDaysLeft) > 0) { timeRemainingText += - Math.floor(daysLeft) === 1 ? "1 day " : `${Math.floor(daysLeft)} days ` + Math.floor(adjustedDaysLeft) === 1 + ? "1 day " + : `${Math.floor(adjustedDaysLeft)} days ` } - timeRemainingText += - Math.ceil(hoursLeft) === 1 ? "1 hour " : `${Math.ceil(hoursLeft)} hours ` + if (Math.ceil(hoursLeft) !== 24) { + timeRemainingText += `${Math.ceil(hoursLeft)} hours ` // if smaller than 1 hour, we display "XP drop coming soon" + } return `${timeRemainingText}remaining` } /** - This function returns next selected day (f.e. next Thursday) and CET time + This function returns next selected day (f.e. next Thursday) * @param weekDay day of the week (1 = Monday, 2 = Tuesday, etc.) * @param hour desired hour in 24h format * @returns timestamp of next selected day */ export function getNextSelectedWeekDay(weekDay: number, hour: number) { - const currentTimestamp = new Date() - - // Convert time to CET timezone - currentTimestamp.setUTCHours(currentTimestamp.getUTCHours() + 1) + const currentDate = convertDateToUTCTimezone() + const adjustedHour = hour - currentDate.getTimezoneOffset() / 60 - const currentHour = currentTimestamp.getHours() - const daysLeft = (weekDay - currentTimestamp.getUTCDay() + 7) % 7 + const currentHour = currentDate.getHours() + const daysLeft = (weekDay - currentDate.getUTCDay() + 7) % 7 // If the today and selected day are the same and it is passed selected hour, then choose next closest day const daysUntilSelectedDay = - daysLeft === 0 && currentHour >= hour ? 7 : daysLeft + daysLeft === 0 && currentHour >= adjustedHour ? 7 : daysLeft const nextSelectedDay = new Date( - currentTimestamp.getUTCFullYear(), - currentTimestamp.getUTCMonth(), - currentTimestamp.getUTCDate() + daysUntilSelectedDay, - hour, + currentDate.getUTCFullYear(), + currentDate.getUTCMonth(), + currentDate.getUTCDate() + daysUntilSelectedDay, + adjustedHour, 0, 0 ) diff --git a/src/ui/DApps/index.tsx b/src/ui/DApps/index.tsx index bad2729a..2419db15 100644 --- a/src/ui/DApps/index.tsx +++ b/src/ui/DApps/index.tsx @@ -1,5 +1,5 @@ import React from "react" -import { useMobileScreen, useTrackEvents } from "shared/hooks" +import { useMobileScreen, useTrackEvents, useUnLoad } from "shared/hooks" import GlobalStyles from "ui/GlobalStyles" import MobileDApp from "./MobileDApp" import DesktopDApp from "./DesktopDApp" @@ -7,6 +7,7 @@ import DesktopDApp from "./DesktopDApp" export default function DApp() { const isMobileScreen = useMobileScreen() useTrackEvents() + useUnLoad() return ( <> diff --git a/src/ui/Island/RealmPanel/RealmPanelCloseButton.tsx b/src/ui/Island/RealmPanel/RealmPanelCloseButton.tsx index 6949ea45..1b1c3da3 100644 --- a/src/ui/Island/RealmPanel/RealmPanelCloseButton.tsx +++ b/src/ui/Island/RealmPanel/RealmPanelCloseButton.tsx @@ -3,6 +3,7 @@ import closeIcon from "shared/assets/icons/s/close-black.svg" import { animated } from "@react-spring/web" import { useRealmCloseButtonTransition } from "shared/hooks" import Button from "../../../shared/components/Interface/Button" +import Portal from "shared/components/Interface/Portal" export default function RealmPanelCloseButton({ onClose, @@ -12,16 +13,18 @@ export default function RealmPanelCloseButton({ const buttonTransition = useRealmCloseButtonTransition() return ( - - - + + + + + ) } diff --git a/src/ui/Island/RealmPanel/RealmPanelCountdown.tsx b/src/ui/Island/RealmPanel/RealmPanelCountdown.tsx index efbd1b29..69d13632 100644 --- a/src/ui/Island/RealmPanel/RealmPanelCountdown.tsx +++ b/src/ui/Island/RealmPanel/RealmPanelCountdown.tsx @@ -1,6 +1,6 @@ import React from "react" import { - selectSeasonDurationInWeeks, + // selectSeasonDurationInWeeks, selectSeasonWeek, selectWeekEndDate, useDappSelector, @@ -11,12 +11,13 @@ import Icon from "shared/components/Media/Icon" export default function RealmPanelCountdown() { const seasonWeek = useDappSelector(selectSeasonWeek) - const seasonDuration = useDappSelector(selectSeasonDurationInWeeks) + // TODO: for now we are hardcoding the season duration to 7 weeks to get better UX + const seasonDuration = 7 // useDappSelector(selectSeasonDurationInWeeks) const weekEndDate = useDappSelector(selectWeekEndDate) if (!weekEndDate) return null - const nextDropTimestamp = getNextSelectedWeekDay(4, 18) + const nextDropTimestamp = getNextSelectedWeekDay(4, 17) // 17:00 UTC time const timeRemaining = getTimeRemaining(nextDropTimestamp) return ( diff --git a/src/ui/Island/RealmPanel/index.tsx b/src/ui/Island/RealmPanel/index.tsx index 69cfa0a6..6e573615 100644 --- a/src/ui/Island/RealmPanel/index.tsx +++ b/src/ui/Island/RealmPanel/index.tsx @@ -2,9 +2,12 @@ import React, { useEffect } from "react" import { useAssistant, useLocalStorageChange, + usePanelRealmClose, useTabletScreen, } from "shared/hooks" import { LOCAL_STORAGE_VISITED_REALM } from "shared/constants" +import { selectRealmPanelVisible, useDappSelector } from "redux-state" +import ClickableModalOverlay from "shared/components/Dialogs/ClickableModalOverlay" import RealmDetailsPanel from "./RealmDetailsPanel" import RealmLeaderboardPanel from "./RealmLeaderboardPanel" import RealmPanelCountdown from "./RealmPanelCountdown" @@ -18,6 +21,9 @@ export default function RealmPanel({ onClose }: { onClose: () => void }) { LOCAL_STORAGE_VISITED_REALM ) + const realmPanelVisible = useDappSelector(selectRealmPanelVisible) + const handlePanelClose = usePanelRealmClose() + useEffect(() => { if (value) return updateStorage(true) @@ -37,6 +43,7 @@ export default function RealmPanel({ onClose }: { onClose: () => void }) { )} + {realmPanelVisible && } ) } diff --git a/src/ui/Island/index.tsx b/src/ui/Island/index.tsx index 0aa33ed0..b364c509 100644 --- a/src/ui/Island/index.tsx +++ b/src/ui/Island/index.tsx @@ -1,10 +1,11 @@ -import React, { memo, useCallback, useEffect } from "react" +import React, { memo, useEffect } from "react" import backgroundImg from "public/dapp_island_bg.webp" import { useValueRef, IslandContext, useAssistant, useAssets, + usePanelRealmClose, } from "shared/hooks" import { selectDisplayedRealmId, @@ -17,7 +18,6 @@ import { import FullPageLoader from "shared/components/Loaders/FullPageLoader" import { usePostHog } from "posthog-js/react" import RealmPanel from "ui/Island/RealmPanel" -import { REALM_PANEL_ANIMATION_TIME } from "shared/constants" import InteractiveIsland from "./Background/InteractiveIsland" import IslandPresence from "./Reflect/IslandPresence" @@ -52,16 +52,7 @@ function IslandWrapper() { }, })) - const handleClose = useCallback(() => { - dispatch(setRealmPanelVisible(false)) - - const timeout = setTimeout( - () => dispatch(setDisplayedRealmId(null)), - REALM_PANEL_ANIMATION_TIME - ) - - return () => clearTimeout(timeout) - }, [dispatch]) + const handlePanelClose = usePanelRealmClose() return ( <> @@ -80,7 +71,7 @@ function IslandWrapper() { {process.env.DISABLE_REFLECT === "true" ? null : } - +