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

Pulling refs/heads/staging into test-staging #1867

Merged
merged 18 commits into from
Oct 9, 2023
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
6 changes: 3 additions & 3 deletions components/front-page/HeroBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ export const HeroBanner = ({
className="py-3 px-4 w-full rounded-md flex gap-2"
style={{ backgroundColor: "rgba(28, 100, 242, 0.2)" }}
>
<div className="flex justify-start items-center gap-2 w-1/3">
<div className="flex justify-start items-center gap-3 w-1/3">
<div>
<ZeitgeistIcon variant="blue" height={28} width={28} />
<ZeitgeistIcon variant="blue" height={32} width={32} />
</div>
<div>
<div className="font-bold text-md">Zeitgeist</div>
<div className="text-lg font-medium">Zeitgeist</div>
<div className="text-sm">
{chainProperties.tokenSymbol.toString()}
</div>
Expand Down
232 changes: 232 additions & 0 deletions components/top-bar/Alerts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import { Menu, Transition, Portal } from "@headlessui/react";
import Modal from "components/ui/Modal";
import {
ReadyToReportMarketAlertData,
RedeemableMarketsAlertData,
RelevantMarketDisputeAlertData,
useAlerts,
} from "lib/state/alerts";
import { useWallet } from "lib/state/wallet";
import { useRouter } from "next/router";
import { Fragment, PropsWithChildren, useEffect, useState } from "react";
import { AiOutlineFileAdd } from "react-icons/ai";
import { BiMoneyWithdraw } from "react-icons/bi";
import { IoMdNotificationsOutline } from "react-icons/io";

export const Alerts = () => {
const wallet = useWallet();
const { alerts, setAsRead } = useAlerts(wallet.realAddress);

const hasNotifications = alerts.length > 0;

const [hoveringMenu, setHoveringMenu] = useState(false);

const mouseEnterMenuHandler = () => {
setHoveringMenu(true);
};
const mouseLeaveMenuHandler = () => {
setHoveringMenu(false);
};

return (
<Menu as="div" className="relative z-50">
{({ open, close }) => {
return (
<>
<div className="flex gap-2">
<Menu.Button
disabled={alerts.length === 0}
className="text-white font-light relative flex center gap-2"
>
<div
className={`transition-all ${
hasNotifications
? "text-gray-200 cursor-pointer"
: "text-gray-500"
}`}
>
<IoMdNotificationsOutline
className="transition-all"
size={24}
/>
{hasNotifications && (
<div className="absolute animate-pulse-scale top-0 right-0 w-3 h-3 bg-red-500 rounded-full"></div>
)}
</div>
</Menu.Button>
</div>

<Transition
as={Fragment}
show={open && hoveringMenu}
enter="transition-opacity ease-out duration-100"
enterFrom="transform opacity-0"
enterTo="transform opacity-1"
leave="transition-opacity ease-in opacity-0 duration-75"
leaveFrom="transform opacity-1"
leaveTo="transform opacity-0 "
>
<div
className="fixed z-40 left-0 top-0 h-screen w-screen bg-black/10 backdrop-blur-sm"
aria-hidden="true"
/>
</Transition>

<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform -translate-y-2"
enterTo="transform translate-y-0 "
leave="transition ease-in translate-y-2 duration-75"
leaveFrom="transform translate-y-0"
leaveTo="transform opacity-0 -translate-y-2"
>
<Menu.Items
onMouseEnter={mouseEnterMenuHandler}
onMouseLeave={mouseLeaveMenuHandler}
onWheelCapture={(e) => {
e.stopPropagation();
e.preventDefault();
}}
className={`
fixed md:absolute left-0 md:translate-x-[50%] md:left-auto p-2 md:px-4 md:max-h-[664px]
overflow-y-scroll md:right-0 bottom-0 md:bottom-auto z-50 py-3 top-11
md:top-auto mt-6 md:mt-6 w-full overflow-hidden h-full md:h-auto md:w-96 pb-20 md:pb-0
origin-top-right divide-gray-100 md:rounded-md focus:outline-none
bg-black/20 md:bg-transparent subtle-scroll-bar subtle-scroll-bar-on-hover
`}
>
{alerts.map((alert) => (
<Menu.Item key={alert.id}>
<div className={`${!hoveringMenu && "backdrop-blur-lg"}`}>
{alert.type === "ready-to-report-market" ? (
<ReadyToReportMarketAlertItem alert={alert} />
) : alert.type === "market-dispute" ? (
<RelevantMarketDisputeItem alert={alert} />
) : alert.type === "redeemable-markets" ? (
<RedeemableMarketAlertItem alert={alert} />
) : (
// Including this prevents us from not exhausting the switch on alert type.
// Should never be reached but caught by the type system.
<UnknownAlertItem alert={alert} />
)}
</div>
</Menu.Item>
))}
</Menu.Items>
</Transition>
</>
);
}}
</Menu>
);
};

const AlertCard: React.FC<PropsWithChildren & { onClick?: () => void }> = ({
children,
onClick,
}) => (
<div className="mb-2 md:hover:scale-105 hover:ring-1 ring-[#fa8cce] rounded-md transition-all cursor-pointer">
<div
className={`transition-all bg-white/80 md:bg-white/60 hover:md:bg-white/80 border-1 border-solid border-black/10 py-3 px-4 rounded-md`}
onClick={onClick}
style={{
transform: "translate3d(0,0,0)",
backfaceVisibility: "hidden",
}}
>
{children}
</div>
</div>
);

const ReadyToReportMarketAlertItem = ({
alert,
}: {
alert: ReadyToReportMarketAlertData;
}) => {
const router = useRouter();

useEffect(() => {
router.prefetch(`/markets/${alert.market.marketId}`);
}, [alert]);

return (
<AlertCard
onClick={() => {
router.push(`/markets/${alert.market.marketId}`);
}}
>
<div className="mb-1">
<div
className="rounded-full py-1 px-1.5 inline-flex text-xxs items-center gap-1"
style={{
background:
"linear-gradient(131.15deg, rgba(240, 206, 135, 0.4) 11.02%, rgba(254, 0, 152, 0.4) 93.27%)",
}}
>
<AiOutlineFileAdd size={12} className="text-gray-700" />
Submit Report
</div>
</div>
<div>
<h3 className="text-sm font-medium pl-1">{alert.market.question}</h3>
</div>
</AlertCard>
);
};

const RedeemableMarketAlertItem = ({
alert,
}: {
alert: RedeemableMarketsAlertData;
}) => {
const router = useRouter();
const wallet = useWallet();

useEffect(() => {
router.prefetch(`/portfolio/${wallet.realAddress}`);
}, [alert, wallet.realAddress]);

return (
<AlertCard
onClick={() => {
router.push(`/portfolio/${wallet.realAddress}`);
}}
>
<div className="mb-1">
<div
className="rounded-full py-1 px-1.5 inline-flex text-xxs items-center gap-1"
style={{
background:
"linear-gradient(131.15deg, rgba(50, 255, 157, 0.4) 11.02%, rgb(142 185 231 / 38%) 93.27%)",
}}
>
<BiMoneyWithdraw size={12} className="text-gray-600" />
Redeemable Tokens
</div>
</div>
<div>
<h3 className="text-sm font-medium pl-1">
You have {alert.markets.length} redeemable markets.
</h3>
</div>
</AlertCard>
);
};

const RelevantMarketDisputeItem = ({
alert,
}: {
alert: RelevantMarketDisputeAlertData;
}) => {
return <AlertCard></AlertCard>;
};

/**
* @note Since the param here is `never` it prevents us from forgetting to add a case for a new alert type
* If a case for a alert type is missing in the rendering of the list, the compiler will complain.
*/
const UnknownAlertItem = ({ alert }: { alert: never }) => {
return <></>;
};
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 6 additions & 2 deletions components/menu/index.tsx → components/top-bar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Fragment, useState } from "react";

import { Menu, Transition } from "@headlessui/react";
import { CATEGORIES } from "components/front-page/PopularCategories";
import MenuLogo from "components/menu/MenuLogo";
import MenuLogo from "components/top-bar/MenuLogo";
import dynamic from "next/dynamic";
import Image from "next/image";
import Link from "next/link";
Expand All @@ -17,6 +17,7 @@ import {
FiList,
} from "react-icons/fi";
import { useCategoryCounts } from "lib/hooks/queries/useCategoryCounts";
import { Alerts } from "./Alerts";

const AccountButton = dynamic(() => import("../account/AccountButton"), {
ssr: false,
Expand Down Expand Up @@ -158,7 +159,10 @@ const TopBar = () => {
<div>Leaderboard</div>
</Link>
</div>
<AccountButton />
<div className="center gap-2">
<Alerts />
<AccountButton />
</div>
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion layouts/DefaultLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useResizeDetector } from "react-resize-detector";
import Image from "next/image";
import dynamic from "next/dynamic";

import TopBar from "components/menu";
import TopBar from "components/top-bar";
import Footer from "components/ui/Footer";
import NotificationCenter from "components/ui/NotificationCenter";
import GrillChat from "components/grillchat";
Expand Down
48 changes: 48 additions & 0 deletions lib/hooks/queries/useReadyToReportMarkets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useQuery } from "@tanstack/react-query";
import { MarketStatus } from "@zeitgeistpm/indexer";
import { isFullSdk } from "@zeitgeistpm/sdk-next";
import { isNotNull } from "@zeitgeistpm/utility/dist/null";
import { useSdkv2 } from "../useSdkv2";
import { useChainTime } from "lib/state/chaintime";

export const useReadyToReportMarkets = (account?: string) => {
const [sdk, id] = useSdkv2();
const chainTime = useChainTime();

const enabled = sdk && isFullSdk(sdk) && account && chainTime;

return useQuery(
[id, "ready-to-report-markets", account],
async () => {
if (enabled) {
const closedMarketsForAccount = await sdk.indexer.markets({
where: {
oracle_eq: account,
status_eq: MarketStatus.Closed,
},
});

let readyToReportMarkets = (
await Promise.all(
closedMarketsForAccount.markets.map(async (market) => {
const stage = await sdk.model.markets.getStage(market, chainTime);
if (
stage.type === "OracleReportingPeriod" ||
stage.type === "OpenReportingPeriod"
) {
return market;
}
return null;
}),
)
).filter(isNotNull);

return readyToReportMarkets;
}
},
{
enabled: Boolean(enabled),
refetchInterval: 1000 * 60,
},
);
};
Loading